moving old trunk out of the way to move bigbang branch into this position

git-svn-id: https://svn.apache.org/repos/asf/directory/apacheds/tags/tags_old@686924 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/old_trunk/LICENSE b/old_trunk/LICENSE
new file mode 100644
index 0000000..467968b
--- /dev/null
+++ b/old_trunk/LICENSE
@@ -0,0 +1,251 @@
+
+                                 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 OpenLDAP Public License
+  Version 2.8, 17 August 2003
+
+Redistribution and use of this software and associated documentation
+("Software"), with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Redistributions in source form must retain copyright statements
+   and notices,
+
+2. Redistributions in binary form must reproduce applicable copyright
+   statements and notices, this list of conditions, and the following
+   disclaimer in the documentation and/or other materials provided
+   with the distribution, and
+
+3. Redistributions must contain a verbatim copy of this document.
+
+The OpenLDAP Foundation may revise this license from time to time.
+Each revision is distinguished by a version number.  You may use
+this Software under terms of this license revision or under the
+terms of any subsequent revision of the license.
+
+THIS SOFTWARE IS PROVIDED BY THE OPENLDAP FOUNDATION AND ITS
+CONTRIBUTORS ``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 THE OPENLDAP FOUNDATION, ITS CONTRIBUTORS, OR THE AUTHOR(S)
+OR OWNER(S) OF THE 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.
+
+The names of the authors and copyright holders must not be used in
+advertising or otherwise to promote the sale, use or other dealing
+in this Software without specific, written prior permission.  Title
+to copyright in this Software shall at all times remain with copyright
+holders.
+
+OpenLDAP is a registered trademark of the OpenLDAP Foundation.
+
+Copyright 1999-2003 The OpenLDAP Foundation, Redwood City,
+California, USA.  All Rights Reserved.  Permission to copy and
+distribute verbatim copies of this document is granted.
diff --git a/old_trunk/NOTICE b/old_trunk/NOTICE
new file mode 100644
index 0000000..6e73dcc
--- /dev/null
+++ b/old_trunk/NOTICE
@@ -0,0 +1,74 @@
+
+ApacheDS
+Copyright 2003-2008 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+
+
+
+
+Copyright 1998-2008 The OpenLDAP Foundation
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted only as authorized by the OpenLDAP
+Public License.
+
+A copy of this license is available in the file LICENSE in the
+top-level directory of the distribution or, alternatively, at
+<http://www.OpenLDAP.org/license.html>.
+
+OpenLDAP is a registered trademark of the OpenLDAP Foundation.
+
+Individual files and/or contributed packages may be copyright by
+other parties and/or subject to additional restrictions.
+
+This work is derived from the University of Michigan LDAP v3.3
+distribution.  Information concerning this software is available
+at <http://www.umich.edu/~dirsvcs/ldap/ldap.html>.
+
+This work also contains materials derived from public sources.
+
+Additional information about OpenLDAP can be obtained at
+<http://www.openldap.org/>.
+
+---
+
+Portions Copyright 1998-2008 Kurt D. Zeilenga.
+Portions Copyright 1998-2006 Net Boolean Incorporated.
+Portions Copyright 2001-2006 IBM Corporation.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted only as authorized by the OpenLDAP
+Public License.
+
+---
+
+Portions Copyright 1999-2007 Howard Y.H. Chu.
+Portions Copyright 1999-2007 Symas Corporation.
+Portions Copyright 1998-2003 Hallvard B. Furuseth.
+Portions Copyright 2007 Gavin Henry
+Portions Copyright 2007 Suretec Systems
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that this notice is preserved.
+The names of the copyright holders may not be used to endorse or
+promote products derived from this software without their specific
+prior written permission.  This software is provided ``as is''
+without express or implied warranty.
+
+---
+
+Portions Copyright (c) 1992-1996 Regents of the University of Michigan.
+All rights reserved.
+
+Redistribution and use in source and binary forms are permitted
+provided that this notice is preserved and that due credit is given
+to the University of Michigan at Ann Arbor.  The name of the
+University may not be used to endorse or promote products derived
+from this software without specific prior written permission.  This
+software is provided ``as is'' without express or implied warranty.
+
diff --git a/old_trunk/README.txt b/old_trunk/README.txt
new file mode 100755
index 0000000..f1b4f83
--- /dev/null
+++ b/old_trunk/README.txt
@@ -0,0 +1,23 @@
+   This distribution includes cryptographic software.  The country in 
+   which you currently reside may have restrictions on the import, 
+   possession, use, and/or re-export to another country, of 
+   encryption software.  BEFORE using any encryption software, please 
+   check your country's laws, regulations and policies concerning the
+   import, possession, or use, and re-export of encryption software, to 
+   see if this is permitted.  See <http://www.wassenaar.org/> for more
+   information.
+
+   The U.S. Government Department of Commerce, Bureau of Industry and
+   Security (BIS), has classified this software as Export Commodity 
+   Control Number (ECCN) 5D002.C.1, which includes information security
+   software using or performing cryptographic functions with asymmetric
+   algorithms.  The form and manner of this Apache Software Foundation
+   distribution makes it eligible for export under the License Exception
+   ENC Technology Software Unrestricted (TSU) exception (see the BIS 
+   Export Administration Regulations, Section 740.13) for both object 
+   code and source code.
+
+   The following provides more details on the included cryptographic
+   software:
+
+   http://www.bouncycastle.org/
diff --git a/old_trunk/apacheds-jdbm/pom.xml b/old_trunk/apacheds-jdbm/pom.xml
new file mode 100644
index 0000000..444bff6
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/pom.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you 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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.directory.server</groupId>
+    <artifactId>apacheds-parent</artifactId>
+    <version>1.5.4-SNAPSHOT</version>
+  </parent>
+  <artifactId>apacheds-jdbm</artifactId>
+  <name>ApacheDS JDBM implementation</name>
+
+  <description>
+    specific JDBM Implementation
+  </description>
+
+  <packaging>jar</packaging>
+
+  <dependencies />
+</project>
+
diff --git a/old_trunk/apacheds-jdbm/src/build.xml b/old_trunk/apacheds-jdbm/src/build.xml
new file mode 100644
index 0000000..f5b7df5
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/build.xml
@@ -0,0 +1,243 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project name="JDBM" default="all" basedir="..">
+
+  <target name="init">
+    <property name="project" value="jdbm" />
+    <property name="source" value="./src" />
+    <property name="main" value="${source}/main" />
+    <property name="etc" value="${source}/etc" />
+    <property name="build" value="build" />
+    <property name="classes" value="${build}/classes" />
+    <property name="test-classes" value="${build}/test-classes" />
+    <property name="dist" value="dist" />
+    <property name="lib" value="lib" />
+    <property name="doc" value="${source}/doc" />
+    <property name="schema" value="${source}/schema" />
+    <property name="test" value="${build}/test" />
+
+    <property name="classpath" value="${classpath}:${classes}" />
+    <property name="build.compiler" value="modern" />
+    <property name="deprecation" value="true" />
+    <property file="VERSION" />
+    <property name="version" value="unspecified" description="default value; should never happen"/>
+    <property name="archive" value="${project}-${version}" />
+  </target>
+
+
+  <!-- Display all the targets -->
+  <target name="targets">
+    <echo message=""/>
+    <echo message="ANT build for ${project} ${version}"/>
+    <echo message=""/>
+    <echo message="The following targets are available:"/>
+    <echo message="  targets  Lists the build targets"/>
+    <echo message="  clean    Cleans the build and distribution directories"/>
+    <echo message="  all      Cleans and builds all the packages and examples"/>
+    <echo message="  release  Cleans and builds all the release packages"/>
+    <echo message="           (JAR, source, doc, tarball) in the dist directory"/>
+    <echo message="  main     Builds the main packages (including debugging info)"/>
+    <echo message="  examples Builds the example packages (including debugging info)"/>
+    <echo message="  tests    Builds the test packages (including debugging info)"/>
+    <echo message="  tests.run Runs unit tests" />
+    <echo message="  jar      Builds the binary JAR in the dist directory"/>
+    <echo message="  doc      Builds the documentation"/>
+    <echo message="  api      Builds the API JavaDocs"/>
+    <echo message="  javadoc  Builds the full JavaDocs"/>
+    <echo message="  source   Builds the source tarball in the dist directory"/>
+    <echo message="  tarball  Builds the binary distribution in the dist directory"/>
+    <echo message="  cvs      Update the sources from the CVS"/>
+    <echo message=""/>
+  </target>
+
+
+  <!-- Prepare target directories -->
+  <target name="prepare" depends="init">
+    <mkdir dir="${build}"/>
+    <mkdir dir="${classes}"/>
+    <mkdir dir="${dist}"/>
+    <mkdir dir="${test}"/>
+  </target>
+
+  <!-- Kill all the directories created in prepare -->
+  <target name="clean" depends="init">
+    <delete dir="${test}"/>
+    <delete dir="${build}"/>
+    <delete dir="${dist}"/>
+  </target>
+
+
+  <!-- Build all the sources with debug and deprecation -->
+  <target name="main" depends="prepare">
+    <javac srcdir="${main}" destdir="${classes}" excludes="**/package.html"
+           classpath="${classpath}" debug="on" deprecation="${deprecation}" source="1.3" target="1.3"/>
+  </target>
+
+  <!-- Same as main, but also cleans and additional targets -->
+  <target name="all" depends="clean,main,examples,jar"/>
+
+  <!-- Run the unit tests -->
+  <target name="tests.run" depends="tests">
+        <!-- showoutput 'yes' to allow outputting debug msgs... -->
+        <junit fork="no" printsummary="yes" haltonfailure="no"
+         showoutput="yes">
+            <batchtest fork="no" todir="${test}">
+                <fileset dir="${test-classes}">
+                    <include name="**/Test*.class"/>
+                </fileset>
+            </batchtest>
+            <formatter type="xml" />
+            <classpath>
+                <pathelement path="${classes}" />
+                <pathelement path="${test-classes}" />
+            </classpath>     
+        </junit>
+
+        <junitreport todir="${build}/test">
+            <fileset dir="${build}/test">
+                <include name="TEST-*.xml" />
+            </fileset>
+            <report todir="${build}/test" />
+        </junitreport>
+   </target>
+
+  <!-- Build the example packages -->
+  <target name="examples" depends="main">
+    <mkdir dir="${build}/examples" />
+    <javac srcdir="${source}/examples" destdir="${build}/examples"
+           classpath="${classpath}" debug="on" deprecation="${deprecation}" />
+  </target>
+
+  <!-- Build the test packages -->
+  <target name="tests" depends="main">
+    <mkdir dir="${test-classes}" />
+    <javac srcdir="${source}/tests" destdir="${test-classes}"
+           classpath="${classpath}" debug="on" deprecation="${deprecation}" />
+  </target>
+
+  <!-- Build the JAR file using main -->
+  <target name="jar" depends="main">
+    <delete   file="${dist}/${archive}.jar" />
+
+    <copy file="${etc}/MANIFEST.MF" tofile="${classes}/MANIFEST.MF" />
+    <replace  file="${classes}/MANIFEST.MF" token="$$VERSION$$" value="${version}" />
+    <copy todir="${classes}">
+        <fileset dir="." includes="LICENSE.txt,CHANGES.txt,README.txt" />
+    </copy>
+
+    <jar      jarfile="${dist}/${archive}.jar" basedir="${classes}"
+              manifest="${classes}/MANIFEST.MF" includes="LICENSE.txt,README.txt,CHANGES.txt,jdbm/**" />
+  </target>
+
+
+  <!-- Build the full JavaDocs -->
+  <target name="javadoc" depends="prepare">
+    <mkdir    dir="${build}/doc" />
+    <mkdir    dir="${build}/doc/javadoc" />
+    <property name="copyright"
+              value="Cees de Groot (C) 2000-2001. All rights reserved  http://jdbm.sourceforge.net" />
+    <javadoc  sourcepath="${main}" destdir="${build}/doc/javadoc"
+              doctitle="JDBM JavaDoc" windowtitle="JDBM JavaDoc" bottom="${copyright}"
+              package="true" author="true" version="true" noindex="true"
+              packagenames="jdbm.*">
+        <link href="http://java.sun.com/products/jdk/1.2/docs/api"/>
+        <link href="http://www.junit.org/junit/javadoc/3.8"/>
+    </javadoc>
+  </target>
+
+  <!-- Build the API JavaDocs -->
+  <target name="api" depends="prepare">
+    <mkdir    dir="${build}/doc" />
+    <mkdir    dir="${build}/doc/api" />
+    <property name="copyright"
+              value="Cees de Groot (C) 2000. All rights reserved  http://jdbm.sourceforge.net" />
+    <javadoc  sourcepath="${main}" destdir="${build}/doc/api"
+              doctitle="JDBM JavaDoc" windowtitle="JDBM JavaDoc" bottom="${copyright}"
+              public="true" author="true"
+              packagenames="jdbm.*">
+        <link href="http://java.sun.com/products/jdk/1.2/docs/api"/>
+    </javadoc>
+  </target>
+
+  <!-- Build the docs, API and full JavaDocs, doc archives -->
+  <!-- Note CdG: Removed doc dependency, we don't have it yet... -->
+  <target name="docs" depends="api,javadoc">
+    <zip    zipfile="${dist}/${archive}-doc.zip" basedir="${build}" includes="doc/**" />
+    <tar    tarfile="${dist}/${archive}-doc.tar" basedir="${build}" includes="doc/**" />
+    <gzip   src="${dist}/${archive}-doc.tar" zipfile="${dist}/${archive}-doc.tgz" />
+    <delete file="${dist}/${archive}-doc.tar" />
+  </target>
+
+
+  <!-- Build the source distribution -->
+  <target name="source" depends="prepare">
+    <delete  file="${dist}/${archive}-src.tgz" />
+    <delete  file="${dist}/${archive}-src.zip" />
+    <delete dir="${build}/${archive}" />
+
+    <mkdir   dir="${build}/${archive}" />
+    <mkdir   dir="${build}/${archive}/src" />
+    <copy    todir="${build}/${archive}/src">
+      <fileset dir="${source}"/>
+    </copy>
+    <mkdir   dir="${build}/${archive}/lib" />
+    <copy    todir="${build}/${archive}/lib">
+      <fileset dir="${lib}"/>
+    </copy>
+    <copy    todir="${build}/${archive}">
+      <fileset dir="${source}" includes="*.sh,*.bat"/>
+    </copy>
+
+    <tar     includes="${archive}/**" basedir="${build}"
+             tarfile="${dist}/${archive}-src.tar" />
+    <gzip    src="${dist}/${archive}-src.tar" zipfile="${dist}/${archive}-src.tgz"/>
+    <delete  file="${dist}/${archive}-src.tar" />
+    <zip     zipfile="${dist}/${archive}-src.zip" basedir="${build}"
+             includes="${archive}/**" />
+
+  </target>
+
+
+  <!-- Build a full release including JAR, zip/tarball, source and documentation -->
+  <target name="release" depends="clean,jar,source,docs,tarball">
+  </target>
+
+  <!-- Build the tarball including JAR and all dependent packages -->
+  <target name="tarball" depends="prepare,jar,docs">
+    <delete  file="${dist}/${archive}.tgz" />
+    <delete  file="${dist}/${archive}.zip" />
+    <delete  dir="${build}/${archive}" />
+
+    <mkdir   dir="${build}/${archive}" />
+    <copy    todir="${build}/${archive}">
+        <fileset dir="." includes="README.txt,LICENSE.txt,CHANGES.txt" />
+    </copy>
+    <copy    todir="${build}/${archive}/lib">
+        <fileset dir="${dist}" includes="${archive}.jar" />
+    </copy>
+    <copy    todir="${build}/${archive}/doc">
+        <fileset dir="${build}/doc/api" includes="**"/>
+    </copy>
+
+    <copy    todir="${build}/${archive}">
+        <fileset dir="${source}" includes="examples/**"/>
+    </copy>
+
+    <tar     tarfile="${dist}/${archive}.tar" basedir="${build}"
+             includes="${archive}/**" excludes="${archive}/*.tar,${archive}/*.zip" />
+    <gzip    src="${dist}/${archive}.tar" zipfile="${dist}/${archive}.tgz" />
+    <delete  file="${dist}/${archive}.tar" />
+
+    <zip     zipfile="${dist}/${archive}.zip" basedir="${build}" 
+             includes="${archive}/**" excludes="${archive}/*.tar,${archive}/*.tgz" />
+  </target>
+
+
+  <!-- Update the sources from the CVS -->
+  <target name="cvs" depends="clean">
+    <cvs cvsRoot=":pserver:anoncvs@cvs.jdbm.sourceforge.net:/cvs/${project}"
+         dest="." package="${project}" />
+  </target>
+
+
+</project>
+
diff --git a/old_trunk/apacheds-jdbm/src/etc/MANIFEST.MF b/old_trunk/apacheds-jdbm/src/etc/MANIFEST.MF
new file mode 100644
index 0000000..d3f94e2
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/etc/MANIFEST.MF
@@ -0,0 +1,8 @@
+Manifest-Version: 1.0
+Specification-Title: JDBM
+Specification-Vendor: JDBM at SourceForge Project
+Specification-Version: 0.09
+Implementation-Title: JDBM
+Implementation-Vendor: JDBM at SourceForge Project
+Implementation-Version: $VERSION$
+
diff --git a/old_trunk/apacheds-jdbm/src/examples/FamousPeople.java b/old_trunk/apacheds-jdbm/src/examples/FamousPeople.java
new file mode 100644
index 0000000..7efa5f1
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/examples/FamousPeople.java
@@ -0,0 +1,157 @@
+
+import java.util.Properties;
+import jdbm.RecordManager;
+import jdbm.RecordManagerFactory;
+
+import jdbm.helper.Tuple;
+import jdbm.helper.TupleBrowser;
+import jdbm.helper.StringComparator;
+
+import jdbm.btree.BTree;
+
+/**
+ * Famous People example.
+ * <p>
+ * Demonstrates the use of B+Tree data structure to manage a list of
+ * people and their occupation.  The example covers insertion,
+ * ordered traversal, reverse traversal and range lookup of records.
+ *
+ * @author <a href="mailto:boisvert@intalio.com">Alex Boisvert</a>
+ * @version $Id: FamousPeople.java,v 1.6 2003/10/21 15:32:02 boisvert Exp $
+ */
+public class FamousPeople {
+
+    static String DATABASE = "people";
+    static String BTREE_NAME = "FamousPeople";
+
+    static String[] people =
+        { "Greenspan, Alan",
+          "Williams-Byrd, Julie",
+          "Picasso, Pablo",
+          "Stallman, Richard",
+          "Fort, Paul",
+          "Søndergaard, Ole",
+          "Schwarzenegger, Arnold",
+          "Dulkinys, Susanna" };
+
+    static String[] occupations =
+        { "Federal Reserve Board Chairman",
+          "Engineer",
+          "Painter",
+          "Programmer",
+          "Poet",
+          "Typographer",
+          "Actor",
+          "Designer" };
+
+    static String PREFIX = "S";
+
+    /**
+     * Example main entrypoint.
+     */
+    public static void main( String[] args ) {
+        RecordManager recman;
+        long          recid;
+        Tuple         tuple = new Tuple();
+        TupleBrowser  browser;
+        BTree         tree;
+        Properties    props;
+
+        props = new Properties();
+
+        try {
+            // open database and setup an object cache
+            recman = RecordManagerFactory.createRecordManager( DATABASE, props );
+
+            // try to reload an existing B+Tree
+            recid = recman.getNamedObject( BTREE_NAME );
+            if ( recid != 0 ) {
+                tree = BTree.load( recman, recid );
+                System.out.println( "Reloaded existing BTree with " + tree.size()
+                                    + " famous people." );
+            } else {
+                // create a new B+Tree data structure and use a StringComparator
+                // to order the records based on people's name.
+                tree = BTree.createInstance( recman, new StringComparator() );
+                recman.setNamedObject( BTREE_NAME, tree.getRecid() );
+                System.out.println( "Created a new empty BTree" );
+            }
+
+            // insert people with their respective occupation
+            System.out.println();
+            for ( int i=0; i<people.length; i++ ) {
+                System.out.println( "Insert: " + people[i] );
+                tree.insert( people[ i ], occupations[ i ], false );
+            }
+
+            // make the data persistent in the database
+            recman.commit();
+
+            // show list of people with their occupation
+            System.out.println();
+            System.out.println( "Person                   Occupation       " );
+            System.out.println( "------------------       ------------------" );
+
+            // traverse people in order
+            browser = tree.browse();
+            while ( browser.getNext( tuple ) ) {
+                print( tuple );
+            }
+
+            // traverse people in reverse order
+            System.out.println();
+            System.out.println( "Reverse order:" );
+            browser = tree.browse( null ); // position browser at end of the list
+
+            while ( browser.getPrevious( tuple ) ) {
+                print( tuple );
+            }
+
+
+
+            // display people whose name start with PREFIX range
+            System.out.println();
+            System.out.println( "All people whose name start with '" + PREFIX + "':" );
+
+            browser = tree.browse( PREFIX );
+            while ( browser.getNext( tuple ) ) {
+                String key = (String) tuple.getKey();
+                if ( key.startsWith( PREFIX ) ) {
+                    print( tuple );
+                } else {
+                    break;
+                }
+            }
+
+        } catch ( Exception except ) {
+            except.printStackTrace();
+        }
+    }
+
+
+    /**
+     * Print a Tuple containing a ( Person, Occupation ) pair.
+     */
+    static void print( Tuple tuple ) {
+        String person = (String) tuple.getKey();
+        String occupation = (String) tuple.getValue();
+        System.out.println( pad( person, 25) + occupation );
+    }
+
+
+    /**
+     * Pad a string with spaces on the right.
+     *
+     * @param str String to add spaces
+     * @param width Width of string after padding
+     */
+    static String pad( String str, int width ) {
+        StringBuffer buf = new StringBuffer( str );
+        int space = width-buf.length();
+        while ( space-- > 0 ) {
+            buf.append( ' ' );
+        }
+        return buf.toString();
+    }
+
+}
diff --git a/old_trunk/apacheds-jdbm/src/examples/FruitBasket.java b/old_trunk/apacheds-jdbm/src/examples/FruitBasket.java
new file mode 100644
index 0000000..90985b6
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/examples/FruitBasket.java
@@ -0,0 +1,122 @@
+

+import jdbm.RecordManager;

+import jdbm.RecordManagerFactory;

+import jdbm.helper.FastIterator;

+import jdbm.htree.HTree;

+

+import java.io.IOException;

+import java.util.Properties;

+

+/**

+ * Sample JDBM application to demonstrate the use of basic JDBM operations.

+ *

+ * @author <a href="mailto:boisvert@intalio.com">Alex Boisvert</a>

+ * @version $Id: FruitBasket.java,v 1.3 2003/08/06 20:10:15 boisvert Exp $

+ */

+public class FruitBasket

+{

+    RecordManager  recman;

+    HTree          hashtable;

+    FastIterator   iter;

+    String         fruit;

+    String         color;

+

+

+    public FruitBasket()

+        throws IOException

+    {

+        // create or open fruits record manager

+        Properties props = new Properties();

+        recman = RecordManagerFactory.createRecordManager( "fruits", props );

+

+        // create or load fruit basket (hashtable of fruits)

+        long recid = recman.getNamedObject( "basket" );

+        if ( recid != 0 ) {

+            System.out.println( "Reloading existing fruit basket..." );

+            hashtable = HTree.load( recman, recid );

+            showBasket();

+        } else {

+            System.out.println( "Creating new fruit basket..." );

+            hashtable = HTree.createInstance( recman );

+            recman.setNamedObject( "basket", hashtable.getRecid() );

+        }

+    }

+

+

+    public void runDemo()

+        throws IOException

+    {

+        // insert keys and values

+        System.out.println();

+        System.out.println( "Adding fruits to the basket..." );

+        hashtable.put( "bananas", "yellow" );

+        hashtable.put( "strawberries", "red" );

+        hashtable.put( "kiwis", "green" );

+

+        showBasket();

+

+

+        // display color of a specific fruit

+        System.out.println();

+        System.out.println( "Get the color of bananas..." );

+        String bananasColor = (String) hashtable.get( "bananas" );

+        System.out.println( "bananas are " + bananasColor );

+        

+        recman.commit();

+        

+        try {

+            // Thread.sleep( 10 * 1000 );

+        } catch ( Exception except ) {

+            // ignore

+        }

+

+        // remove a specific fruit from hashtable

+        System.out.println();

+        System.out.print( "Removing bananas from the basket..." );

+        hashtable.remove( "bananas" );

+        recman.commit();

+        System.out.println( " done." );

+

+        // iterate over remaining objects

+        System.out.println();

+        System.out.println( "Remaining fruit colors:" );

+        iter = hashtable.keys();

+        fruit = (String) iter.next();

+        while ( fruit != null ) {

+            color = (String) hashtable.get( fruit );

+            System.out.println( fruit + " are " + color );

+            fruit = (String) iter.next();

+        }

+        

+        // cleanup

+        recman.close();

+    }

+

+

+    public void showBasket() 

+        throws IOException

+    {

+        // Display content of fruit basket

+        System.out.println();

+        System.out.print( "Fruit basket contains: " );

+        iter = hashtable.keys();

+        fruit = (String) iter.next();

+        while ( fruit != null ) {

+            System.out.print( " " + fruit );

+            fruit = (String) iter.next();

+        }

+        System.out.println();

+    }

+    

+    

+    public static void main( String[] args )

+    {

+        try {

+            FruitBasket basket = new FruitBasket();

+            basket.runDemo();

+        } catch ( IOException ioe ) {

+            ioe.printStackTrace();

+        }

+    }

+

+}

diff --git a/old_trunk/apacheds-jdbm/src/examples/Primes.java b/old_trunk/apacheds-jdbm/src/examples/Primes.java
new file mode 100644
index 0000000..3e4c49d
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/examples/Primes.java
@@ -0,0 +1,276 @@
+
+import jdbm.RecordManager;
+import jdbm.RecordManagerFactory;
+import jdbm.RecordManagerOptions;
+
+import jdbm.btree.BTree;
+
+import jdbm.helper.LongComparator;
+import jdbm.helper.Tuple;
+import jdbm.helper.TupleBrowser;
+
+import java.util.Properties;
+import java.util.Random;
+import java.io.IOException;
+
+/**
+ * Example program dealing with B+Trees and Prime numbers.
+ */
+public class Primes
+{
+
+    /**
+     * Default number of prime number to populate in the database (if not specified on command-line)
+     */
+    public static int DEFAULT_POPULATE = 100;
+
+
+    /**
+     * Default number of random lookups (if not specified on command-line)
+     */
+    public static int DEFAULT_LOOKUPS = 100;
+
+    
+    /**
+     * Record Manager used for persistence.
+     */
+    private RecordManager _recman;
+
+    
+    /**
+     * B+Tree holding prime numbers.
+     */
+    private BTree _primes;
+    
+    
+    /**
+     * Random number generator.
+     */
+    private static Random  _random = new Random();
+
+
+    /**
+     * Main constructor
+     */
+    public Primes( String[] args ) 
+        throws IOException 
+    {
+        long        recid;
+        Properties  props;
+        
+        // open database and setup an object cache
+        props = new Properties();
+        props.put( RecordManagerOptions.CACHE_SIZE, "10000" );
+        _recman = RecordManagerFactory.createRecordManager( "primes", props );
+
+        recid = _recman.getNamedObject( "primes" );
+        if ( recid == 0 ) {
+            System.out.println( "Creating a new primes B+Tree." );
+            _primes = BTree.createInstance( _recman, new LongComparator() );
+            _recman.setNamedObject( "primes", _primes.getRecid() );
+        } else {
+            _primes = BTree.load( _recman, recid );
+            System.out.println( "B+Tree already contains " + _primes.size() + " primes." );
+        }
+        _recman.commit();
+    }
+
+    
+    /**
+     * Get the largest prime number in the database.
+     */
+    public Long getLargestPrime()
+        throws IOException
+     {
+        Tuple         tuple;
+        TupleBrowser  browser;
+        Long          largest = null;
+
+        tuple = new Tuple();
+        browser = _primes.browse( null );
+        if ( browser.getPrevious( tuple ) ) {
+            largest = (Long) tuple.getValue();
+            System.out.println( "Largest prime: " + largest );
+        } else {
+            System.out.println( "No prime number in the database." );
+        }
+        return largest;
+    }
+
+    
+    /**
+     * Populate the database with more prime numbers.
+     *
+     * @param count Number of primes to add to database.
+     */
+    void populate( int count ) 
+        throws IOException 
+    {
+        Long current;
+        Long largest;
+
+        System.out.println( "Populating prime B+Tree..." );
+        
+        // start after the largest known prime
+        largest = getLargestPrime();
+        if ( largest == null ) {
+            largest = new Long( 0 );
+        }
+
+        current = new Long( largest.longValue() + 1L ); 
+        while ( count > 0 ) {
+            if ( isPrime( current ) ) {
+                _primes.insert( current, current, false );
+                System.out.println( "Found prime #" + _primes.size() + ": " + current );
+                count--;
+            }
+            current = new Long( current.longValue() + 1 );
+        }
+        _recman.commit();
+    }
+
+    
+    /**
+     * Returns true if a number is prime.
+     */
+    boolean isPrime( Long number )
+        throws IOException
+    {
+        Tuple         tuple;
+        TupleBrowser  browser;
+        Long          largest;
+        Long          current;
+
+        if ( number.longValue() <= 0L ) {
+            throw new IllegalArgumentException( "Number must be greater than zero" );
+        }
+        if ( number.longValue() == 1 ) {
+            return true;
+        }
+        tuple = new Tuple();
+        browser = _primes.browse();
+        while ( browser.getNext( tuple ) ) {
+            current = (Long) tuple.getValue();
+            if ( current.longValue() != 1 && ( number.longValue() % current.longValue() ) == 0 ) {
+                // not a prime because it is divisibe by a prime
+                return false;
+            }
+        }           
+        // this is a prime
+        return true;
+    }
+
+        
+    /**
+     * Display a number of random prime numbers.
+     */
+    void random( int count ) 
+        throws IOException
+    {
+        Tuple         tuple;
+        TupleBrowser  browser;
+        Long          largest;
+        Long          number;
+
+        tuple = new Tuple();
+        largest = getLargestPrime();
+
+        System.out.println( "Looking up " + count + " random primes...." );
+        long start = System.currentTimeMillis();
+        for ( int i=0; i<count; i++ ) {
+            number = new Long( random( 0, largest.longValue() ) );
+            browser = _primes.browse( number );
+            if ( browser.getNext( tuple ) ) {
+                number = (Long) tuple.getValue();
+                System.out.print( number );
+                System.out.print( ", " );
+            }
+        }
+        long stop = System.currentTimeMillis();
+        System.out.println();
+        System.out.println( "Time: " + (stop-start)/count + " millis/lookup " );
+    }
+
+
+    /**
+     * Return true if number is a prime.
+     */
+    public static boolean isPrimeCompute( long number )
+    {
+        for ( int i=2; i<number/2; i++ ) {
+            if ( ( number % i ) == 0 ) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+
+    /**
+     * Get random number between "low" and "high" (inclusively)
+     */
+    public static long random( long low, long high )
+    {
+        return ( (long) ( _random.nextDouble() * (high-low) ) + low );
+    }
+
+
+    /**
+     * Static program entrypoint.
+     */
+    public static void main( String[] args )
+    {
+        Primes  primes;
+        int     count;
+        Long    number;
+        Long    largest;
+            
+        try {
+            primes = new Primes( args );
+            
+            for ( int i=0; i<args.length; i++ ) {
+                if ( args[i].equalsIgnoreCase( "-populate" ) ) {
+                    if ( ++i < args.length ) {
+                        count = Integer.parseInt( args[i] );
+                    } else {
+                        count = DEFAULT_POPULATE;
+                    }
+                    primes.populate( count );
+                } else if ( args[i].equalsIgnoreCase( "-check" ) ) {
+                    if ( ++i < args.length ) {
+                        number = new Long( Long.parseLong( args[i] ) );
+                    } else {
+                        number = new Long( _random.nextLong() );
+                    }
+                    largest = primes.getLargestPrime();
+                    if ( number.longValue() > primes.getLargestPrime().longValue() ) {
+                        throw new IllegalArgumentException( "Number is larger than largest known prime in database." );
+                    }
+                    if ( primes.isPrime( number ) ) {
+                        System.out.println( "The number " + number + " is a prime." );
+                    } else {
+                        System.out.println( "The number " + number + " is not a prime." );
+                    }
+                } else if ( args[i].equalsIgnoreCase( "-random" ) ) {
+                    if ( ++i < args.length ) {
+                        count = Integer.parseInt( args[i] );
+                    } else {
+                        count = DEFAULT_LOOKUPS;
+                    }
+                    primes.random( count );
+                }
+            }
+            if ( args.length == 0 ) {
+                System.out.println( "Usage:   java Prime [action] [args]" );
+                System.out.println( "" );
+                System.out.println( "Actions:" );
+                System.out.println( "           -populate [number]   Populate database with prime numbers" );
+                System.out.println( "           -check [number]      Check if a number is a prime" );
+                System.out.println( "           -random [number]     Display random prime numbers" );
+                System.out.println( "" );
+            }
+        } catch ( IOException except ) {
+            except.printStackTrace();
+        }
+    }
+}
diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/RecordManager.java b/old_trunk/apacheds-jdbm/src/main/java/jdbm/RecordManager.java
new file mode 100644
index 0000000..dba5e1d
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/RecordManager.java
@@ -0,0 +1,222 @@
+/**

+ * JDBM LICENSE v1.00

+ *

+ * Redistribution and use of this software and associated documentation

+ * ("Software"), with or without modification, are permitted provided

+ * that the following conditions are met:

+ *

+ * 1. Redistributions of source code must retain copyright

+ *    statements and notices.  Redistributions must also contain a

+ *    copy of this document.

+ *

+ * 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 name "JDBM" must not be used to endorse or promote

+ *    products derived from this Software without prior written

+ *    permission of Cees de Groot.  For written permission,

+ *    please contact cg@cdegroot.com.

+ *

+ * 4. Products derived from this Software may not be called "JDBM"

+ *    nor may "JDBM" appear in their names without prior written

+ *    permission of Cees de Groot.

+ *

+ * 5. Due credit should be given to the JDBM Project

+ *    (http://jdbm.sourceforge.net/).

+ *

+ * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS

+ * ``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

+ * CEES DE GROOT OR ANY 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.

+ *

+ * Copyright 2000 (C) Cees de Groot. All Rights Reserved.

+ * Copyright 2000-2001 (C) Alex Boisvert. All Rights Reserved.

+ * Contributions are Copyright (C) 2000 by their associated contributors.

+ *

+ * $Id: RecordManager.java,v 1.3 2005/06/25 23:12:31 doomdark Exp $

+ */

+

+package jdbm;

+

+import java.io.IOException;

+import jdbm.helper.Serializer;

+

+/**

+ *  An interface to manages records, which are uninterpreted blobs of data.

+ *  <p>

+ *  The set of record operations is simple: fetch, insert, update and delete.

+ *  Each record is identified using a "rowid" and contains a byte[] data block.

+ *  Rowids are returned on inserts and you can store them someplace safe

+ *  to be able to get  back to them.  Data blocks can be as long as you wish,

+ *  and may have lengths different from the original when updating.

+ *

+ * @author <a href="mailto:boisvert@intalio.com">Alex Boisvert</a>

+ * @author <a href="cg@cdegroot.com">Cees de Groot</a>

+ * @version $Id: RecordManager.java,v 1.3 2005/06/25 23:12:31 doomdark Exp $

+ */

+public interface RecordManager

+{

+

+    /**

+     * Reserved slot for name directory.

+     */

+    public static final int NAME_DIRECTORY_ROOT = 0;

+

+

+    /**

+     *  Inserts a new record using standard java object serialization.

+     *

+     *  @param obj the object for the new record.

+     *  @return the rowid for the new record.

+     *  @throws IOException when one of the underlying I/O operations fails.

+     */

+    public abstract long insert( Object obj )

+        throws IOException;

+

+    

+    /**

+     *  Inserts a new record using a custom serializer.

+     *

+     *  @param obj the object for the new record.

+     *  @param serializer a custom serializer

+     *  @return the rowid for the new record.

+     *  @throws IOException when one of the underlying I/O operations fails.

+     */

+    public abstract long insert( Object obj, Serializer serializer )

+        throws IOException;

+

+

+    /**

+     *  Deletes a record.

+     *

+     *  @param recid the rowid for the record that should be deleted.

+     *  @throws IOException when one of the underlying I/O operations fails.

+     */

+    public abstract void delete( long recid )

+        throws IOException;

+

+

+    /**

+     *  Updates a record using standard java object serialization.

+     *

+     *  @param recid the recid for the record that is to be updated.

+     *  @param obj the new object for the record.

+     *  @throws IOException when one of the underlying I/O operations fails.

+     */

+    public abstract void update( long recid, Object obj )

+        throws IOException;

+

+

+    /**

+     *  Updates a record using a custom serializer.

+     *

+     *  @param recid the recid for the record that is to be updated.

+     *  @param obj the new object for the record.

+     *  @param serializer a custom serializer

+     *  @throws IOException when one of the underlying I/O operations fails.

+     */

+    public abstract void update( long recid, Object obj, Serializer serializer )

+        throws IOException;

+

+    

+    /**

+     *  Fetches a record using standard java object serialization.

+     *

+     *  @param recid the recid for the record that must be fetched.

+     *  @return the object contained in the record.

+     *  @throws IOException when one of the underlying I/O operations fails.

+     */

+    public abstract Object fetch( long recid )

+        throws IOException;

+

+

+    /**

+     *  Fetches a record using a custom serializer.

+     *

+     *  @param recid the recid for the record that must be fetched.

+     *  @param serializer a custom serializer

+     *  @return the object contained in the record.

+     *  @throws IOException when one of the underlying I/O operations fails.

+     */

+    public abstract Object fetch( long recid, Serializer serializer )

+        throws IOException;

+

+

+    /**

+     *  Closes the record manager.

+     *

+     *  @throws IOException when one of the underlying I/O operations fails.

+     */

+    public abstract void close()

+        throws IOException;

+

+

+    /**

+     *  Returns the number of slots available for "root" rowids. These slots

+     *  can be used to store special rowids, like rowids that point to

+     *  other rowids. Root rowids are useful for bootstrapping access to

+     *  a set of data.

+     */

+    public abstract int getRootCount();

+

+

+    /**

+     *  Returns the indicated root rowid.

+     *

+     *  @see #getRootCount

+     */

+    public abstract long getRoot( int id )

+        throws IOException;

+

+

+    /**

+     *  Sets the indicated root rowid.

+     *

+     *  @see #getRootCount

+     */

+    public abstract void setRoot( int id, long rowid )

+        throws IOException;

+

+

+    /**

+     * Commit (make persistent) all changes since beginning of transaction.

+     */

+    public abstract void commit()

+        throws IOException;

+

+

+    /**

+     * Rollback (cancel) all changes since beginning of transaction.

+     */

+    public abstract void rollback()

+        throws IOException;

+

+

+

+

+    /**

+     * Obtain the record id of a named object. Returns 0 if named object

+     * doesn't exist.

+     */

+    public abstract long getNamedObject( String name )

+        throws IOException;

+

+

+    /**

+     * Set the record id of a named object.

+     */

+    public abstract void setNamedObject( String name, long recid )

+        throws IOException;

+

+}

+

diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/RecordManagerFactory.java b/old_trunk/apacheds-jdbm/src/main/java/jdbm/RecordManagerFactory.java
new file mode 100644
index 0000000..25927ec
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/RecordManagerFactory.java
@@ -0,0 +1,117 @@
+/**

+ * JDBM LICENSE v1.00

+ *

+ * Redistribution and use of this software and associated documentation

+ * ("Software"), with or without modification, are permitted provided

+ * that the following conditions are met:

+ *

+ * 1. Redistributions of source code must retain copyright

+ *    statements and notices.  Redistributions must also contain a

+ *    copy of this document.

+ *

+ * 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 name "JDBM" must not be used to endorse or promote

+ *    products derived from this Software without prior written

+ *    permission of Cees de Groot.  For written permission,

+ *    please contact cg@cdegroot.com.

+ *

+ * 4. Products derived from this Software may not be called "JDBM"

+ *    nor may "JDBM" appear in their names without prior written

+ *    permission of Cees de Groot.

+ *

+ * 5. Due credit should be given to the JDBM Project

+ *    (http://jdbm.sourceforge.net/).

+ *

+ * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS

+ * ``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

+ * CEES DE GROOT OR ANY 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.

+ *

+ * Copyright 2000 (C) Cees de Groot. All Rights Reserved.

+ * Copyright 2000-2001 (C) Alex Boisvert. All Rights Reserved.

+ * Contributions are Copyright (C) 2000 by their associated contributors.

+ *

+ * $Id: RecordManagerFactory.java,v 1.2 2005/06/25 23:12:31 doomdark Exp $

+ */

+

+package jdbm;

+

+import java.io.IOException;

+import java.util.Properties;

+

+/**

+ * This is the factory class to use for instantiating {@link RecordManager}

+ * instances.

+ *

+ * @author <a href="mailto:boisvert@intalio.com">Alex Boisvert</a>

+ * @author <a href="cg@cdegroot.com">Cees de Groot</a>

+ * @version $Id: RecordManagerFactory.java,v 1.2 2005/06/25 23:12:31 doomdark Exp $

+ */

+public final class RecordManagerFactory

+{

+

+    /**

+     * Create a record manager.

+     *

+     * @param name Name of the record file.

+     * @throws IOException if an I/O related exception occurs while creating

+     *                    or opening the record manager.

+     * @throws UnsupportedOperationException if some options are not supported by the

+     *                                      implementation.

+     * @throws IllegalArgumentException if some options are invalid.

+     */

+    public static RecordManager createRecordManager( String name )

+        throws IOException

+    {

+        return createRecordManager( name, new Properties() );

+    }

+

+

+    /**

+     * Create a record manager.

+     *

+     * @param name Name of the record file.

+     * @param options Record manager options.

+     * @throws IOException if an I/O related exception occurs while creating

+     *                    or opening the record manager.

+     * @throws UnsupportedOperationException if some options are not supported by the

+     *                                      implementation.

+     * @throws IllegalArgumentException if some options are invalid.

+     */

+    public static RecordManager createRecordManager( String name,

+                                                     Properties options )

+        throws IOException

+    {

+        String                 provider;

+        Class                  clazz;

+        RecordManagerProvider  factory;

+

+        provider = options.getProperty( RecordManagerOptions.PROVIDER_FACTORY,

+                                        "jdbm.recman.Provider" );

+

+        try {

+            clazz = Thread.currentThread().getContextClassLoader().loadClass( provider );

+            factory = (RecordManagerProvider) clazz.newInstance();

+        } catch ( Exception except ) {

+            throw new IllegalArgumentException( "Invalid record manager provider: "

+                                                + provider

+                                                + "\n[" + except.getClass().getName()

+                                                + ": " + except.getMessage()

+                                                + "]" );

+        }

+        return factory.createRecordManager( name, options );

+    }

+

+}

diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/RecordManagerOptions.java b/old_trunk/apacheds-jdbm/src/main/java/jdbm/RecordManagerOptions.java
new file mode 100644
index 0000000..395a3c8
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/RecordManagerOptions.java
@@ -0,0 +1,123 @@
+/**

+ * JDBM LICENSE v1.00

+ *

+ * Redistribution and use of this software and associated documentation

+ * ("Software"), with or without modification, are permitted provided

+ * that the following conditions are met:

+ *

+ * 1. Redistributions of source code must retain copyright

+ *    statements and notices.  Redistributions must also contain a

+ *    copy of this document.

+ *

+ * 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 name "JDBM" must not be used to endorse or promote

+ *    products derived from this Software without prior written

+ *    permission of Cees de Groot.  For written permission,

+ *    please contact cg@cdegroot.com.

+ *

+ * 4. Products derived from this Software may not be called "JDBM"

+ *    nor may "JDBM" appear in their names without prior written

+ *    permission of Cees de Groot.

+ *

+ * 5. Due credit should be given to the JDBM Project

+ *    (http://jdbm.sourceforge.net/).

+ *

+ * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS

+ * ``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

+ * CEES DE GROOT OR ANY 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.

+ *

+ * Copyright 2000 (C) Cees de Groot. All Rights Reserved.

+ * Copyright 2000-2001 (C) Alex Boisvert. All Rights Reserved.

+ * Contributions are Copyright (C) 2000 by their associated contributors.

+ *

+ * $Id: RecordManagerOptions.java,v 1.1 2002/05/31 06:33:20 boisvert Exp $

+ */

+

+package jdbm;

+

+/**

+ * Standard options for RecordManager.

+ *

+ * @author <a href="mailto:boisvert@intalio.com">Alex Boisvert</a>

+ * @author <a href="cg@cdegroot.com">Cees de Groot</a>

+ * @version $Id: RecordManagerOptions.java,v 1.1 2002/05/31 06:33:20 boisvert Exp $

+ */

+public class RecordManagerOptions

+{

+

+    /**

+     * Option to create a thread-safe record manager.

+     */

+    public static final String PROVIDER_FACTORY = "jdbm.provider";

+

+

+    /**

+     * Option to create a thread-safe record manager.

+     */

+    public static final String THREAD_SAFE = "jdbm.threadSafe";

+

+

+    /**

+     * Option to automatically commit data after each operation.

+     */

+    public static final String AUTO_COMMIT = "jdbm.autoCommit";

+

+

+    /**

+     * Option to disable transaction (to increase performance at the cost of

+     * potential data loss).

+     */

+    public static final String DISABLE_TRANSACTIONS = "jdbm.disableTransactions";

+

+

+    /**

+     * Cache type.

+     */

+    public static final String CACHE_TYPE = "jdbm.cache.type";

+

+

+    /**

+     * Cache size (when applicable)

+     */

+    public static final String CACHE_SIZE = "jdbm.cache.size";

+

+

+    /**

+     * Use normal (strong) object references for the record cache.

+     */

+    public static final String NORMAL_CACHE = "normal";

+

+

+    /**

+     * Use soft references {$link java.lang.ref.SoftReference} for the record

+     * cache instead of the default normal object references.

+     * <p>

+     * Soft references are cleared at the discretion of the garbage collector

+     * in response to memory demand.

+     */

+    public static final String SOFT_REF_CACHE = "soft";

+

+

+    /**

+     * Use weak references {$link java.lang.ref.WeakReference} for the record

+     * cache instead of the default normal object references.

+     * <p>

+     * Weak references do not prevent their referents from being made

+     * finalizable, finalized, and then reclaimed.

+     */

+    public static final String WEAK_REF_CACHE = "weak";

+

+}

diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/RecordManagerProvider.java b/old_trunk/apacheds-jdbm/src/main/java/jdbm/RecordManagerProvider.java
new file mode 100644
index 0000000..14559db
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/RecordManagerProvider.java
@@ -0,0 +1,78 @@
+/**

+ * JDBM LICENSE v1.00

+ *

+ * Redistribution and use of this software and associated documentation

+ * ("Software"), with or without modification, are permitted provided

+ * that the following conditions are met:

+ *

+ * 1. Redistributions of source code must retain copyright

+ *    statements and notices.  Redistributions must also contain a

+ *    copy of this document.

+ *

+ * 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 name "JDBM" must not be used to endorse or promote

+ *    products derived from this Software without prior written

+ *    permission of Cees de Groot.  For written permission,

+ *    please contact cg@cdegroot.com.

+ *

+ * 4. Products derived from this Software may not be called "JDBM"

+ *    nor may "JDBM" appear in their names without prior written

+ *    permission of Cees de Groot.

+ *

+ * 5. Due credit should be given to the JDBM Project

+ *    (http://jdbm.sourceforge.net/).

+ *

+ * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS

+ * ``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

+ * CEES DE GROOT OR ANY 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.

+ *

+ * Copyright 2000 (C) Cees de Groot. All Rights Reserved.

+ * Copyright 2000-2001 (C) Alex Boisvert. All Rights Reserved.

+ * Contributions are Copyright (C) 2000 by their associated contributors.

+ *

+ * $Id: RecordManagerProvider.java,v 1.2 2005/06/25 23:12:31 doomdark Exp $

+ */

+

+package jdbm;

+

+import java.io.IOException;

+import java.util.Properties;

+

+/**

+ *  Provider of RecordManager implementation.  Classes implementing this

+ *  interface act as a factory to provide implementations of RecordManager.

+ *

+ * @author <a href="mailto:boisvert@intalio.com">Alex Boisvert</a>

+ * @version $Id: RecordManagerProvider.java,v 1.2 2005/06/25 23:12:31 doomdark Exp $

+ */

+public interface RecordManagerProvider

+{

+

+    /**

+     * Create a record manager.

+     *

+     * @param filename Base filename of the record file.

+     * @param options Record manager options.

+     * @throws IOException if an I/O related exception occurs while creating

+     *                    or opening the record manager.

+     * @throws UnsupportedOperationException if some options are not supported by the

+     *                                      implementation.

+     * @throws IllegalArgumentException if some options are invalid.

+     */

+    public RecordManager createRecordManager( String filename,

+                                              Properties options )

+        throws IOException;

+}

diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/btree/BPage.java b/old_trunk/apacheds-jdbm/src/main/java/jdbm/btree/BPage.java
new file mode 100644
index 0000000..1e5a3eb
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/btree/BPage.java
@@ -0,0 +1,1197 @@
+/**

+ * JDBM LICENSE v1.00

+ *

+ * Redistribution and use of this software and associated documentation

+ * ("Software"), with or without modification, are permitted provided

+ * that the following conditions are met:

+ *

+ * 1. Redistributions of source code must retain copyright

+ *    statements and notices.  Redistributions must also contain a

+ *    copy of this document.

+ *

+ * 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 name "JDBM" must not be used to endorse or promote

+ *    products derived from this Software without prior written

+ *    permission of Cees de Groot.  For written permission,

+ *    please contact cg@cdegroot.com.

+ *

+ * 4. Products derived from this Software may not be called "JDBM"

+ *    nor may "JDBM" appear in their names without prior written

+ *    permission of Cees de Groot.

+ *

+ * 5. Due credit should be given to the JDBM Project

+ *    (http://jdbm.sourceforge.net/).

+ *

+ * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS

+ * ``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

+ * CEES DE GROOT OR ANY 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.

+ *

+ * Copyright 2001 (C) Alex Boisvert. All Rights Reserved.

+ * Contributions are Copyright (C) 2001 by their associated contributors.

+ *

+ */

+

+package jdbm.btree;

+

+import jdbm.helper.Serializer;

+import jdbm.helper.Tuple;

+import jdbm.helper.TupleBrowser;

+

+import java.io.IOException;

+import java.io.ByteArrayOutputStream;

+import java.io.ByteArrayInputStream;

+import java.io.ObjectInput;

+import java.io.ObjectOutput;

+import java.io.ObjectInputStream;

+import java.io.ObjectOutputStream;

+

+/**

+ * Page of a Btree.

+ * <p>

+ * The page contains a number of key-value pairs.  Keys are ordered to allow

+ * dichotomic search.

+ * <p>

+ * If the page is a leaf page, the keys and values are user-defined and

+ * represent entries inserted by the user.

+ * <p>

+ * If the page is non-leaf, each key represents the greatest key in the

+ * underlying BPages and the values are recids pointing to the children BPages.

+ * The only exception is the rightmost BPage, which is considered to have an

+ * "infinite" key value, meaning that any insert will be to the left of this

+ * pseudo-key

+ *

+ * @author <a href="mailto:boisvert@intalio.com">Alex Boisvert</a>

+ * @version $Id: BPage.java,v 1.6 2003/09/21 15:46:59 boisvert Exp $

+ */

+public final class BPage

+    implements Serializer

+{

+

+    private static final boolean DEBUG = false;

+

+

+    /**

+     * Version id for serialization.

+     */

+    final static long serialVersionUID = 1L;

+

+

+    /**

+     * Parent B+Tree.

+     */

+    transient BTree _btree;

+

+

+    /**

+     * This BPage's record ID in the PageManager.

+     */

+    protected transient long _recid;

+

+

+    /**

+     * Flag indicating if this is a leaf BPage.

+     */

+    protected boolean _isLeaf;

+

+

+    /**

+     * Keys of children nodes

+     */

+    protected Object[] _keys;

+

+

+    /**

+     * Values associated with keys.  (Only valid if leaf BPage)

+     */

+    protected Object[] _values;

+

+

+    /**

+     * Children pages (recids) associated with keys.  (Only valid if non-leaf BPage)

+     */

+    protected long[] _children;

+

+    

+    /**

+     * Index of first used item at the page

+     */

+    protected int _first;

+

+

+    /**

+     * Previous leaf BPage (only if this BPage is a leaf)

+     */

+    protected long _previous;

+

+

+    /**

+     * Next leaf BPage (only if this BPage is a leaf)

+     */

+    protected long _next;

+

+

+    /**

+     * No-argument constructor used by serialization.

+     */

+    public BPage()

+    {

+        // empty

+    }

+

+

+    /**

+     * Root page overflow constructor

+     */

+    BPage( BTree btree, BPage root, BPage overflow )

+        throws IOException

+    {

+        _btree = btree;

+

+        _isLeaf = false;

+

+        _first = _btree._pageSize-2;

+

+        _keys = new Object[ _btree._pageSize ];

+        _keys[ _btree._pageSize-2 ] = overflow.getLargestKey();

+        _keys[ _btree._pageSize-1 ] = root.getLargestKey();

+

+        _children = new long[ _btree._pageSize ];

+        _children[ _btree._pageSize-2 ] = overflow._recid;

+        _children[ _btree._pageSize-1 ] = root._recid;

+

+        _recid = _btree._recman.insert( this, this );

+    }

+

+

+    /**

+     * Root page (first insert) constructor.

+     */

+    BPage( BTree btree, Object key, Object value )

+        throws IOException

+    {

+        _btree = btree;

+

+        _isLeaf = true;

+

+        _first = btree._pageSize-2;

+

+        _keys = new Object[ _btree._pageSize ];

+        _keys[ _btree._pageSize-2 ] = key;

+        _keys[ _btree._pageSize-1 ] = null;  // I am the root BPage for now

+

+        _values = new Object[ _btree._pageSize ];

+        _values[ _btree._pageSize-2 ] = value;

+        _values[ _btree._pageSize-1 ] = null;  // I am the root BPage for now

+

+        _recid = _btree._recman.insert( this, this );

+    }

+

+

+    /**

+     * Overflow page constructor.  Creates an empty BPage.

+     */

+    BPage( BTree btree, boolean isLeaf )

+        throws IOException

+    {

+        _btree = btree;

+

+        _isLeaf = isLeaf;

+

+        // page will initially be half-full

+        _first = _btree._pageSize/2;

+

+        _keys = new Object[ _btree._pageSize ];

+        if ( isLeaf ) {

+            _values = new Object[ _btree._pageSize ];

+        } else {

+            _children = new long[ _btree._pageSize ];

+        }

+

+        _recid = _btree._recman.insert( this, this );

+    }

+

+

+    /**

+     * Get largest key under this BPage.  Null is considered to be the

+     * greatest possible key.

+     */

+    Object getLargestKey()

+    {

+        return _keys[ _btree._pageSize-1 ];

+    }

+

+

+    /**

+     * Return true if BPage is empty.

+     */

+    boolean isEmpty()

+    {

+        if ( _isLeaf ) {

+            return ( _first == _values.length-1 );

+        } else {

+            return ( _first == _children.length-1 );

+        }

+    }

+

+

+    /**

+     * Return true if BPage is full.

+     */

+    boolean isFull() {

+        return ( _first == 0 );

+    }

+

+

+    /**

+     * Find the object associated with the given key.

+     *

+     * @param height Height of the current BPage (zero is leaf page)

+     * @param key The key

+     * @return TupleBrowser positionned just before the given key, or before

+     *                      next greater key if key isn't found.

+     */

+    TupleBrowser find( int height, Object key )

+        throws IOException

+    {

+        int index = findChildren( key );

+

+        /*

+        if ( DEBUG ) {

+            System.out.println( "BPage.find() current: " + this

+                                + " height: " + height);

+        }

+        */

+

+        height -= 1;

+

+        if ( height == 0 ) {

+            // leaf BPage

+            return new Browser( this, index );

+        } else {

+            // non-leaf BPage

+            BPage child = childBPage( index );

+            return child.find( height, key );

+        }

+    }

+

+

+    /**

+     * Find first entry and return a browser positioned before it.

+     *

+     * @return TupleBrowser positionned just before the first entry.

+     */

+    TupleBrowser findFirst()

+        throws IOException

+    {

+        if ( _isLeaf ) {

+            return new Browser( this, _first );

+        } else {

+            BPage child = childBPage( _first );

+            return child.findFirst();

+        }

+    }

+

+

+    /**

+     * Insert the given key and value.

+     * <p>

+     * Since the Btree does not support duplicate entries, the caller must

+     * specify whether to replace the existing value.

+     *

+     * @param height Height of the current BPage (zero is leaf page)

+     * @param key Insert key

+     * @param value Insert value

+     * @param replace Set to true to replace the existing value, if one exists.

+     * @return Insertion result containing existing value OR a BPage if the key

+     *         was inserted and provoked a BPage overflow.

+     */

+    InsertResult insert( int height, Object key, Object value, boolean replace )

+        throws IOException

+    {

+        InsertResult  result;

+        long          overflow;

+

+        int index = findChildren( key );

+

+        height -= 1;

+        if ( height == 0 )  {

+

+            result = new InsertResult();

+

+            // inserting on a leaf BPage

+            overflow = -1;

+            if ( DEBUG ) {

+                System.out.println( "Bpage.insert() Insert on leaf Bpage key=" + key

+                                    + " value=" + value + " index="+index);

+            }

+            if ( compare( key, _keys[ index ] ) == 0 ) {

+                // key already exists

+                if ( DEBUG ) {

+                    System.out.println( "Bpage.insert() Key already exists." ) ;

+                }

+                result._existing = _values[ index ];

+                if ( replace ) {

+                    _values [ index ] = value;

+                    _btree._recman.update( _recid, this, this );

+                }

+                // return the existing key

+                return result;

+            }

+        } else {

+            // non-leaf BPage

+            BPage child = childBPage( index );

+            result = child.insert( height, key, value, replace );

+

+            if ( result._existing != null ) {

+                // return existing key, if any.

+                return result;

+            }

+

+            if ( result._overflow == null ) {

+                // no overflow means we're done with insertion

+                return result;

+            }

+

+            // there was an overflow, we need to insert the overflow page

+            // on this BPage

+            if ( DEBUG ) {

+                System.out.println( "BPage.insert() Overflow page: " + result._overflow._recid );

+            }

+            key = result._overflow.getLargestKey();

+            overflow = result._overflow._recid;

+

+            // update child's largest key

+            _keys[ index ] = child.getLargestKey();

+

+            // clean result so we can reuse it

+            result._overflow = null;

+        }

+

+        // if we get here, we need to insert a new entry on the BPage

+        // before _children[ index ]

+        if ( !isFull() ) {

+            if ( height == 0 ) {

+                insertEntry( this, index-1, key, value );

+            } else {

+                insertChild( this, index-1, key, overflow );

+            }

+            _btree._recman.update( _recid, this, this );

+            return result;

+        }

+

+        // page is full, we must divide the page

+        int half = _btree._pageSize >> 1;

+        BPage newPage = new BPage( _btree, _isLeaf );

+        if ( index < half ) {

+            // move lower-half of entries to overflow BPage,

+            // including new entry

+            if ( DEBUG ) {

+                System.out.println( "Bpage.insert() move lower-half of entries to overflow BPage, including new entry." ) ;

+            }

+            if ( height == 0 ) {

+                copyEntries( this, 0, newPage, half, index );

+                setEntry( newPage, half+index, key, value );

+                copyEntries( this, index, newPage, half+index+1, half-index-1 );

+            } else {

+                copyChildren( this, 0, newPage, half, index );

+                setChild( newPage, half+index, key, overflow );

+                copyChildren( this, index, newPage, half+index+1, half-index-1 );

+            }

+        } else {

+            // move lower-half of entries to overflow BPage,

+            // new entry stays on this BPage

+            if ( DEBUG ) {

+                System.out.println( "Bpage.insert() move lower-half of entries to overflow BPage. New entry stays" ) ;

+            }

+            if ( height == 0 ) {

+                copyEntries( this, 0, newPage, half, half );

+                copyEntries( this, half, this, half-1, index-half );

+                setEntry( this, index-1, key, value );

+            } else {

+                copyChildren( this, 0, newPage, half, half );

+                copyChildren( this, half, this, half-1, index-half );

+                setChild( this, index-1, key, overflow );

+            }

+        }

+

+        _first = half-1;

+

+        // nullify lower half of entries

+        for ( int i=0; i<_first; i++ ) {

+            if ( height == 0 ) {

+                setEntry( this, i, null, null );

+            } else {

+                setChild( this, i, null, -1 );

+            }

+        }

+

+        if ( _isLeaf ) {

+            // link newly created BPage

+            newPage._previous = _previous;

+            newPage._next = _recid;

+            if ( _previous != 0 ) {

+                BPage previous = loadBPage( _previous );

+                previous._next = newPage._recid;

+                _btree._recman.update( _previous, previous, this );

+            }

+            _previous = newPage._recid;

+        }

+

+        _btree._recman.update( _recid, this, this );

+        _btree._recman.update( newPage._recid, newPage, this );

+

+        result._overflow = newPage;

+        return result;

+    }

+

+

+    /**

+     * Remove the entry associated with the given key.

+     *

+     * @param height Height of the current BPage (zero is leaf page)

+     * @param key Removal key

+     * @return Remove result object

+     */

+    RemoveResult remove( int height, Object key )

+        throws IOException

+    {

+        RemoveResult result;

+

+        int half = _btree._pageSize / 2;

+        int index = findChildren( key );

+

+        height -= 1;

+        if ( height == 0 ) {

+            // remove leaf entry

+            if ( compare( _keys[ index ], key ) != 0 ) {

+                throw new IllegalArgumentException( "Key not found: " + key );

+            }

+            result = new RemoveResult();

+            result._value = _values[ index ];

+            removeEntry( this, index );

+

+            // update this BPage

+            _btree._recman.update( _recid, this, this );

+

+        } else {

+            // recurse into Btree to remove entry on a children page

+            BPage child = childBPage( index );

+            result = child.remove( height, key );

+

+            // update children

+            _keys[ index ] = child.getLargestKey();

+            _btree._recman.update( _recid, this, this );

+

+            if ( result._underflow ) {

+                // underflow occured

+                if ( child._first != half+1 ) {

+                    throw new IllegalStateException( "Error during underflow [1]" );

+                }

+                if ( index < _children.length-1 ) {

+                    // exists greater brother page

+                    BPage brother = childBPage( index+1 );

+                    int bfirst = brother._first;

+                    if ( bfirst < half ) {

+                        // steal entries from "brother" page

+                        int steal = ( half - bfirst + 1 ) / 2;

+                        brother._first += steal;

+                        child._first -= steal;

+                        if ( child._isLeaf ) {

+                            copyEntries( child, half+1, child, half+1-steal, half-1 );

+                            copyEntries( brother, bfirst, child, 2*half-steal, steal );

+                        } else {

+                            copyChildren( child, half+1, child, half+1-steal, half-1 );

+                            copyChildren( brother, bfirst, child, 2*half-steal, steal );

+                        }                            

+

+                        for ( int i=bfirst; i<bfirst+steal; i++ ) {

+                            if ( brother._isLeaf ) {

+                                setEntry( brother, i, null, null );

+                            } else {

+                                setChild( brother, i, null, -1 );

+                            }

+                        }

+

+                        // update child's largest key

+                        _keys[ index ] = child.getLargestKey();

+

+                        // no change in previous/next BPage

+

+                        // update BPages

+                        _btree._recman.update( _recid, this, this );

+                        _btree._recman.update( brother._recid, brother, this );

+                        _btree._recman.update( child._recid, child, this );

+

+                    } else {

+                        // move all entries from page "child" to "brother"

+                        if ( brother._first != half ) {

+                            throw new IllegalStateException( "Error during underflow [2]" );

+                        }

+

+                        brother._first = 1;

+                        if ( child._isLeaf ) {

+                            copyEntries( child, half+1, brother, 1, half-1 );

+                        } else {

+                            copyChildren( child, half+1, brother, 1, half-1 );

+                        }

+                        _btree._recman.update( brother._recid, brother, this );

+

+                        // remove "child" from current BPage

+                        if ( _isLeaf ) {

+                            copyEntries( this, _first, this, _first+1, index-_first );

+                            setEntry( this, _first, null, null );

+                        } else {

+                            copyChildren( this, _first, this, _first+1, index-_first );

+                            setChild( this, _first, null, -1 );

+                        }

+                        _first += 1;

+                        _btree._recman.update( _recid, this, this );

+

+                        // re-link previous and next BPages

+                        if ( child._previous != 0 ) {

+                            BPage prev = loadBPage( child._previous );

+                            prev._next = child._next;

+                            _btree._recman.update( prev._recid, prev, this );

+                        }

+                        if ( child._next != 0 ) {

+                            BPage next = loadBPage( child._next );

+                            next._previous = child._previous;

+                            _btree._recman.update( next._recid, next, this );

+                        }

+

+                        // delete "child" BPage

+                        _btree._recman.delete( child._recid );

+                    }

+                } else {

+                    // page "brother" is before "child"

+                    BPage brother = childBPage( index-1 );

+                    int bfirst = brother._first;

+                    if ( bfirst < half ) {

+                        // steal entries from "brother" page

+                        int steal = ( half - bfirst + 1 ) / 2;

+                        brother._first += steal;

+                        child._first -= steal;

+                        if ( child._isLeaf ) {

+                            copyEntries( brother, 2*half-steal, child,

+                                         half+1-steal, steal );

+                            copyEntries( brother, bfirst, brother,

+                                         bfirst+steal, 2*half-bfirst-steal );

+                        } else {

+                            copyChildren( brother, 2*half-steal, child,

+                                          half+1-steal, steal );

+                            copyChildren( brother, bfirst, brother,

+                                          bfirst+steal, 2*half-bfirst-steal );

+                        }

+

+                        for ( int i=bfirst; i<bfirst+steal; i++ ) {

+                            if ( brother._isLeaf ) {

+                                setEntry( brother, i, null, null );

+                            } else {

+                                setChild( brother, i, null, -1 );

+                            }

+                        }

+

+                        // update brother's largest key

+                        _keys[ index-1 ] = brother.getLargestKey();

+

+                        // no change in previous/next BPage

+

+                        // update BPages

+                        _btree._recman.update( _recid, this, this );

+                        _btree._recman.update( brother._recid, brother, this );

+                        _btree._recman.update( child._recid, child, this );

+

+                    } else {

+                        // move all entries from page "brother" to "child"

+                        if ( brother._first != half ) {

+                            throw new IllegalStateException( "Error during underflow [3]" );

+                        }

+

+                        child._first = 1;

+                        if ( child._isLeaf ) {

+                            copyEntries( brother, half, child, 1, half );

+                        } else {

+                            copyChildren( brother, half, child, 1, half );

+                        }

+                        _btree._recman.update( child._recid, child, this );

+

+                        // remove "brother" from current BPage

+                        if ( _isLeaf ) {

+                            copyEntries( this, _first, this, _first+1, index-1-_first );

+                            setEntry( this, _first, null, null );

+                        } else {

+                            copyChildren( this, _first, this, _first+1, index-1-_first );

+                            setChild( this, _first, null, -1 );

+                        }

+                        _first += 1;

+                        _btree._recman.update( _recid, this, this );

+

+                        // re-link previous and next BPages

+                        if ( brother._previous != 0 ) {

+                            BPage prev = loadBPage( brother._previous );

+                            prev._next = brother._next;

+                            _btree._recman.update( prev._recid, prev, this );

+                        }

+                        if ( brother._next != 0 ) {

+                            BPage next = loadBPage( brother._next );

+                            next._previous = brother._previous;

+                            _btree._recman.update( next._recid, next, this );

+                        }

+

+                        // delete "brother" BPage

+                        _btree._recman.delete( brother._recid );

+                    }

+                }

+            }

+        }

+

+        // underflow if page is more than half-empty

+        result._underflow = _first > half;

+

+        return result;

+    }

+

+

+    /**

+     * Find the first children node with a key equal or greater than the given

+     * key.

+     *

+     * @return index of first children with equal or greater key.

+     */

+    private int findChildren( Object key )

+    {

+        int left = _first;

+        int right = _btree._pageSize-1;

+

+        // binary search

+        while ( left < right )  {

+            int middle = ( left + right ) / 2;

+            if ( compare( _keys[ middle ], key ) < 0 ) {

+                left = middle+1;

+            } else {

+                right = middle;

+            }

+        }

+        return right;

+    }

+

+

+    /**

+     * Insert entry at given position.

+     */

+    private static void insertEntry( BPage page, int index,

+                                     Object key, Object value )

+    {

+        Object[] keys = page._keys;

+        Object[] values = page._values;

+        int start = page._first;

+        int count = index-page._first+1;

+

+        // shift entries to the left

+        System.arraycopy( keys, start, keys, start-1, count );

+        System.arraycopy( values, start, values, start-1, count );

+        page._first -= 1;

+        keys[ index ] = key;

+        values[ index ] = value;

+    }

+

+

+    /**

+     * Insert child at given position.

+     */

+    private static void insertChild( BPage page, int index,

+                                     Object key, long child )

+    {

+        Object[] keys = page._keys;

+        long[] children = page._children;

+        int start = page._first;

+        int count = index-page._first+1;

+

+        // shift entries to the left

+        System.arraycopy( keys, start, keys, start-1, count );

+        System.arraycopy( children, start, children, start-1, count );

+        page._first -= 1;

+        keys[ index ] = key;

+        children[ index ] = child;

+    }

+    

+    /**

+     * Remove entry at given position.

+     */

+    private static void removeEntry( BPage page, int index )

+    {

+        Object[] keys = page._keys;

+        Object[] values = page._values;

+        int start = page._first;

+        int count = index-page._first;

+

+        System.arraycopy( keys, start, keys, start+1, count );

+        keys[ start ] = null;

+        System.arraycopy( values, start, values, start+1, count );

+        values[ start ] = null;

+        page._first++;

+    }

+

+

+    /**

+     * Remove child at given position.

+     */

+/*    

+    private static void removeChild( BPage page, int index )

+    {

+        Object[] keys = page._keys;

+        long[] children = page._children;

+        int start = page._first;

+        int count = index-page._first;

+

+        System.arraycopy( keys, start, keys, start+1, count );

+        keys[ start ] = null;

+        System.arraycopy( children, start, children, start+1, count );

+        children[ start ] = (long) -1;

+        page._first++;

+    }

+*/

+    

+    /**

+     * Set the entry at the given index.

+     */

+    private static void setEntry( BPage page, int index, Object key, Object value )

+    {

+        page._keys[ index ] = key;

+        page._values[ index ] = value;

+    }

+

+

+    /**

+     * Set the child BPage recid at the given index.

+     */

+    private static void setChild( BPage page, int index, Object key, long recid )

+    {

+        page._keys[ index ] = key;

+        page._children[ index ] = recid;

+    }

+    

+    

+    /**

+     * Copy entries between two BPages

+     */

+    private static void copyEntries( BPage source, int indexSource,

+                                     BPage dest, int indexDest, int count )

+    {

+        System.arraycopy( source._keys, indexSource, dest._keys, indexDest, count);

+        System.arraycopy( source._values, indexSource, dest._values, indexDest, count);

+    }

+

+

+    /**

+     * Copy child BPage recids between two BPages

+     */

+    private static void copyChildren( BPage source, int indexSource,

+                                      BPage dest, int indexDest, int count )

+    {

+        System.arraycopy( source._keys, indexSource, dest._keys, indexDest, count);

+        System.arraycopy( source._children, indexSource, dest._children, indexDest, count);

+    }

+

+    

+    /**

+     * Return the child BPage at given index.

+     */

+    BPage childBPage( int index )

+        throws IOException

+    {

+        return loadBPage( _children[ index ] );

+    }

+

+

+    /**

+     * Load the BPage at the given recid.

+     */

+    private BPage loadBPage( long recid )

+        throws IOException

+    {

+        BPage child = (BPage) _btree._recman.fetch( recid, this );

+        child._recid = recid;

+        child._btree = _btree;

+        return child;

+    }

+

+    

+    private final int compare( Object value1, Object value2 )

+    {

+        if ( value1 == null ) {

+            return 1;

+        }

+        if ( value2 == null ) {

+            return -1;

+        }

+        return _btree._comparator.compare( value1, value2 );

+    }

+

+    static byte[] readByteArray( ObjectInput in )

+        throws IOException

+    {

+        int len = in.readInt();

+        if ( len < 0 ) {

+            return null;

+        }

+        byte[] buf = new byte[ len ];

+        in.readFully( buf );

+        return buf;

+    }

+

+

+    static void writeByteArray( ObjectOutput out, byte[] buf )

+        throws IOException

+    {

+        if ( buf == null ) {

+            out.writeInt( -1 );

+        } else {

+            out.writeInt( buf.length );

+            out.write( buf );

+        }

+    }

+

+    /**

+     * Dump the structure of the tree on the screen.  This is used for debugging

+     * purposes only.

+     */

+    private void dump( int height )

+    {

+        String prefix = "";

+        for ( int i=0; i<height; i++ ) {

+           prefix += "    ";

+        }

+        System.out.println( prefix + "-------------------------------------- BPage recid=" + _recid);

+        System.out.println( prefix + "first=" + _first );

+        for ( int i=0; i< _btree._pageSize; i++ ) {

+            if ( _isLeaf ) {

+                System.out.println( prefix + "BPage [" + i + "] " + _keys[ i ] + " " + _values[ i ] );

+            } else {

+                System.out.println( prefix + "BPage [" + i + "] " + _keys[ i ] + " " + _children[ i ] );

+            }

+        }

+        System.out.println( prefix + "--------------------------------------" );

+    }

+

+

+    /**

+     * Recursively dump the state of the BTree on screen.  This is used for

+     * debugging purposes only.

+     */

+    void dumpRecursive( int height, int level )

+        throws IOException

+    {

+        height -= 1;

+        level += 1;

+        if ( height > 0 ) {

+            for ( int i=_first; i<_btree._pageSize; i++ ) {

+                if ( _keys[ i ] == null ) break;

+                BPage child = childBPage( i );

+                child.dump( level );

+                child.dumpRecursive( height, level );

+            }

+        }

+    }

+

+

+    /**

+     * Assert the ordering of the keys on the BPage.  This is used for testing

+     * purposes only.

+     */

+    private void assertConsistency()

+    {

+        for ( int i=_first; i<_btree._pageSize-1; i++ ) {

+            if ( compare( (byte[]) _keys[ i ], (byte[]) _keys[ i+1 ] ) >= 0 ) {

+                dump( 0 );

+                throw new Error( "BPage not ordered" );

+            }

+        }

+    }

+

+

+    /**

+     * Recursively assert the ordering of the BPage entries on this page

+     * and sub-pages.  This is used for testing purposes only.

+     */

+    void assertConsistencyRecursive( int height ) 

+        throws IOException 

+    {

+        assertConsistency();

+        if ( --height > 0 ) {

+            for ( int i=_first; i<_btree._pageSize; i++ ) {

+                if ( _keys[ i ] == null ) break;

+                BPage child = childBPage( i );

+                if ( compare( (byte[]) _keys[ i ], child.getLargestKey() ) != 0 ) {

+                    dump( 0 );

+                    child.dump( 0 );

+                    throw new Error( "Invalid child subordinate key" );

+                }

+                child.assertConsistencyRecursive( height );

+            }

+        }

+    }

+

+

+    /**

+     * Deserialize the content of an object from a byte array.

+     *

+     * @param serialized Byte array representation of the object

+     * @return deserialized object

+     *

+     */

+    public Object deserialize( byte[] serialized ) 

+        throws IOException

+    {

+        ByteArrayInputStream  bais;

+        ObjectInputStream     ois;

+        BPage                 bpage;

+

+        bpage = new BPage();

+        bais = new ByteArrayInputStream( serialized );

+        ois = new ObjectInputStream( bais );

+        

+        bpage._isLeaf = ois.readBoolean();

+        if ( bpage._isLeaf ) {

+            bpage._previous = ois.readLong();

+            bpage._next = ois.readLong();

+        }

+

+        bpage._first = ois.readInt();

+

+        bpage._keys = new Object[ _btree._pageSize ];

+        try {

+            for ( int i=bpage._first; i<_btree._pageSize; i++ ) {

+                if ( _btree._keySerializer == null ) {

+                    bpage._keys[ i ] = ois.readObject();

+                } else {

+                    serialized = readByteArray( ois );

+                    if ( serialized != null ) {

+                        bpage._keys[ i ] = _btree._keySerializer.deserialize( serialized );

+                    }

+                }

+            }

+        } catch ( ClassNotFoundException except ) {

+            throw new IOException( except.getMessage() );

+        }

+        

+        if ( bpage._isLeaf ) {

+            bpage._values = new Object[ _btree._pageSize ];

+            try {

+                for ( int i=bpage._first; i<_btree._pageSize; i++ ) {

+                    if ( _btree._valueSerializer == null ) {

+                        bpage._values[ i ] = ois.readObject();

+                    } else {

+                        serialized = readByteArray( ois );

+                        if ( serialized != null ) {

+                            bpage._values[ i ] = _btree._valueSerializer.deserialize( serialized );

+                        }

+                    }

+                }

+            } catch ( ClassNotFoundException except ) {

+                throw new IOException( except.getMessage() );

+            }

+        } else {

+            bpage._children = new long[ _btree._pageSize ];

+            for ( int i=bpage._first; i<_btree._pageSize; i++ ) {

+                bpage._children[ i ] = ois.readLong();

+            }

+        }

+        ois.close();

+        bais.close();

+        

+        return bpage;

+    }

+

+    

+    /** 

+     * Serialize the content of an object into a byte array.

+     *

+     * @param obj Object to serialize

+     * @return a byte array representing the object's state

+     *

+     */

+    public byte[] serialize( Object obj ) 

+        throws IOException

+    {

+        byte[]                 serialized;

+        ByteArrayOutputStream  baos;

+        ObjectOutputStream     oos;

+        BPage                  bpage;

+        byte[]                 data;

+        

+        // note:  It is assumed that BPage instance doing the serialization is the parent

+        // of the BPage object being serialized.

+        

+        bpage = (BPage) obj;

+        baos = new ByteArrayOutputStream();

+        oos = new ObjectOutputStream( baos );        

+        

+        oos.writeBoolean( bpage._isLeaf );

+        if ( bpage._isLeaf ) {

+            oos.writeLong( bpage._previous );

+            oos.writeLong( bpage._next );

+        }

+

+        oos.writeInt( bpage._first );

+        

+        for ( int i=bpage._first; i<_btree._pageSize; i++ ) {

+            if ( _btree._keySerializer == null ) {

+                oos.writeObject( bpage._keys[ i ] );

+            } else {

+                if ( bpage._keys[ i ] != null ) {

+                    serialized = _btree._keySerializer.serialize( bpage._keys[ i ] );

+                    writeByteArray( oos, serialized );

+                } else {

+                    writeByteArray( oos, null );

+                }

+            }

+        }

+

+        if ( bpage._isLeaf ) {

+            for ( int i=bpage._first; i<_btree._pageSize; i++ ) {

+                if ( _btree._valueSerializer == null ) {

+                    oos.writeObject( bpage._values[ i ] );

+                } else {

+                    if ( bpage._values[ i ] != null ) {

+                        serialized = _btree._valueSerializer.serialize( bpage._values[ i ] );

+                        writeByteArray( oos, serialized );

+                    } else {

+                        writeByteArray( oos, null );

+                    }

+                }

+            }

+        } else {

+            for ( int i=bpage._first; i<_btree._pageSize; i++ ) {

+                oos.writeLong( bpage._children[ i ] );

+            }

+        }

+        

+        oos.flush();

+        data = baos.toByteArray();

+        oos.close();

+        baos.close();

+        return data;

+    }

+    

+    

+    /** STATIC INNER CLASS

+     *  Result from insert() method call

+     */

+    static class InsertResult {

+

+        /**

+         * Overflow page.

+         */

+        BPage _overflow;

+

+        /**

+         * Existing value for the insertion key.

+         */

+        Object _existing;

+

+    }

+

+    /** STATIC INNER CLASS

+     *  Result from remove() method call

+     */

+    static class RemoveResult {

+

+        /**

+         * Set to true if underlying pages underflowed

+         */

+        boolean _underflow;

+

+        /**

+         * Removed entry value

+         */

+        Object _value;

+    }

+

+

+    /** PRIVATE INNER CLASS

+     * Browser to traverse leaf BPages.

+     */

+    static class Browser

+        extends TupleBrowser

+    {

+

+        /**

+         * Current page.

+         */

+        private BPage _page;

+

+

+        /**

+         * Current index in the page.  The index positionned on the next

+         * tuple to return.

+         */

+        private int _index;

+

+

+        /**

+         * Create a browser.

+         *

+         * @param page Current page

+         * @param index Position of the next tuple to return.

+         */

+        Browser( BPage page, int index )

+        {

+            _page = page;

+            _index = index;

+        }

+

+        public boolean getNext( Tuple tuple )

+            throws IOException

+        {

+            if ( _index < _page._btree._pageSize ) {

+                if ( _page._keys[ _index ] == null ) {

+                    // reached end of the tree.

+                    return false;

+                }

+            } else if ( _page._next != 0 ) {

+                // move to next page

+                _page = _page.loadBPage( _page._next );

+                _index = _page._first;

+            }

+            tuple.setKey( _page._keys[ _index ] );

+            tuple.setValue( _page._values[ _index ] );

+            _index++;

+            return true;

+        }

+

+        public boolean getPrevious( Tuple tuple )

+            throws IOException

+        {

+            if ( _index == _page._first ) {

+

+                if ( _page._previous != 0 ) {

+                    _page = _page.loadBPage( _page._previous );

+                    _index = _page._btree._pageSize;

+                } else {

+                    // reached beginning of the tree

+                    return false;

+                }

+            }

+            _index--;

+            tuple.setKey( _page._keys[ _index ] );

+            tuple.setValue( _page._values[ _index ] );

+            return true;

+

+        }

+    }

+

+}

diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/btree/BTree.java b/old_trunk/apacheds-jdbm/src/main/java/jdbm/btree/BTree.java
new file mode 100644
index 0000000..e2a0e68
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/btree/BTree.java
@@ -0,0 +1,609 @@
+/**
+ * JDBM LICENSE v1.00
+ *
+ * Redistribution and use of this software and associated documentation
+ * ("Software"), with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain copyright
+ *    statements and notices.  Redistributions must also contain a
+ *    copy of this document.
+ *
+ * 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 name "JDBM" must not be used to endorse or promote
+ *    products derived from this Software without prior written
+ *    permission of Cees de Groot.  For written permission,
+ *    please contact cg@cdegroot.com.
+ *
+ * 4. Products derived from this Software may not be called "JDBM"
+ *    nor may "JDBM" appear in their names without prior written
+ *    permission of Cees de Groot.
+ *
+ * 5. Due credit should be given to the JDBM Project
+ *    (http://jdbm.sourceforge.net/).
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS
+ * ``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
+ * CEES DE GROOT OR ANY 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.
+ *
+ * Copyright 2001 (C) Alex Boisvert. All Rights Reserved.
+ * Contributions are Copyright (C) 2001 by their associated contributors.
+ *
+ */
+
+package jdbm.btree;
+
+import jdbm.RecordManager;
+
+import jdbm.helper.Serializer;
+import jdbm.helper.Tuple;
+import jdbm.helper.TupleBrowser;
+
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.io.Serializable;
+
+import java.util.Comparator;
+
+/**
+ * B+Tree persistent indexing data structure.  B+Trees are optimized for
+ * block-based, random I/O storage because they store multiple keys on
+ * one tree node (called <code>BPage</code>).  In addition, the leaf nodes
+ * directly contain (inline) the values associated with the keys, allowing a
+ * single (or sequential) disk read of all the values on the page.
+ * <p>
+ * B+Trees are n-airy, yeilding log(N) search cost.  They are self-balancing,
+ * preventing search performance degradation when the size of the tree grows.
+ * <p>
+ * Keys and associated values must be <code>Serializable</code> objects. The
+ * user is responsible to supply a serializable <code>Comparator</code> object
+ * to be used for the ordering of entries, which are also called <code>Tuple</code>.
+ * The B+Tree allows traversing the keys in forward and reverse order using a
+ * TupleBrowser obtained from the browse() methods.
+ * <p>
+ * This implementation does not directly support duplicate keys, but it is
+ * possible to handle duplicates by inlining or referencing an object collection
+ * as a value.
+ * <p>
+ * There is no limit on key size or value size, but it is recommended to keep
+ * both as small as possible to reduce disk I/O.   This is especially true for
+ * the key size, which impacts all non-leaf <code>BPage</code> objects.
+ *
+ * @author <a href="mailto:boisvert@intalio.com">Alex Boisvert</a>
+ * @version $Id: BTree.java,v 1.6 2005/06/25 23:12:31 doomdark Exp $
+ */
+public class BTree
+    implements Externalizable
+{
+
+    private static final boolean DEBUG = false;
+
+    /**
+     * Version id for serialization.
+     */
+    final static long serialVersionUID = 1L;
+
+
+    /**
+     * Default page size (number of entries per node)
+     */
+    public static final int DEFAULT_SIZE = 16;
+
+
+    /**
+     * Page manager used to persist changes in BPages
+     */
+    protected transient RecordManager _recman;
+
+
+    /**
+     * This BTree's record ID in the PageManager.
+     */
+    private transient long _recid;
+
+
+    /**
+     * Comparator used to index entries.
+     */
+    protected Comparator _comparator;
+
+
+    /**
+     * Serializer used to serialize index keys (optional)
+     */
+    protected Serializer _keySerializer;
+
+
+    /**
+     * Serializer used to serialize index values (optional)
+     */
+    protected Serializer _valueSerializer;
+
+
+    /**
+     * Height of the B+Tree.  This is the number of BPages you have to traverse
+     * to get to a leaf BPage, starting from the root.
+     */
+    private int _height;
+
+
+    /**
+     * Recid of the root BPage
+     */
+    private transient long _root;
+
+
+    /**
+     * Number of entries in each BPage.
+     */
+    protected int _pageSize;
+
+
+    /**
+     * Total number of entries in the BTree
+     */
+    protected int _entries;
+
+    
+    /**
+     * Serializer used for BPages of this tree
+     */
+    private transient BPage _bpageSerializer;
+    
+
+    /**
+     * No-argument constructor used by serialization.
+     */
+    public BTree()
+    {
+        // empty
+    }
+
+
+    /**
+     * Create a new persistent BTree, with 16 entries per node.
+     *
+     * @param recman Record manager used for persistence.
+     * @param comparator Comparator used to order index entries
+     */
+    public static BTree createInstance( RecordManager recman,
+                                        Comparator comparator )
+        throws IOException
+    {
+        return createInstance( recman, comparator, null, null, DEFAULT_SIZE );
+    }
+
+
+    /**
+     * Create a new persistent BTree, with 16 entries per node.
+     *
+     * @param recman Record manager used for persistence.
+     * @param keySerializer Serializer used to serialize index keys (optional)
+     * @param valueSerializer Serializer used to serialize index values (optional)
+     * @param comparator Comparator used to order index entries
+     */
+    public static BTree createInstance( RecordManager recman,
+                                        Comparator comparator,
+                                        Serializer keySerializer,
+                                        Serializer valueSerializer )
+        throws IOException
+    {
+        return createInstance( recman, comparator, keySerializer, 
+                               valueSerializer, DEFAULT_SIZE );
+    }
+
+
+    /**
+     * Create a new persistent BTree with the given number of entries per node.
+     *
+     * @param recman Record manager used for persistence.
+     * @param comparator Comparator used to order index entries
+     * @param keySerializer Serializer used to serialize index keys (optional)
+     * @param valueSerializer Serializer used to serialize index values (optional)
+     * @param pageSize Number of entries per page (must be even).
+     */
+    public static BTree createInstance( RecordManager recman,
+                                        Comparator comparator,
+                                        Serializer keySerializer,
+                                        Serializer valueSerializer,
+                                        int pageSize )
+        throws IOException
+    {
+        BTree btree;
+
+        if ( recman == null ) {
+            throw new IllegalArgumentException( "Argument 'recman' is null" );
+        }
+
+        if ( comparator == null ) {
+            throw new IllegalArgumentException( "Argument 'comparator' is null" );
+        }
+
+        if ( ! ( comparator instanceof Serializable ) ) {
+            throw new IllegalArgumentException( "Argument 'comparator' must be serializable" );
+        }
+
+        if ( keySerializer != null && ! ( keySerializer instanceof Serializable ) ) {
+            throw new IllegalArgumentException( "Argument 'keySerializer' must be serializable" );
+        }
+
+        if ( valueSerializer != null && ! ( valueSerializer instanceof Serializable ) ) {
+            throw new IllegalArgumentException( "Argument 'valueSerializer' must be serializable" );
+        }
+
+        // make sure there's an even number of entries per BPage
+        if ( ( pageSize & 1 ) != 0 ) {
+            throw new IllegalArgumentException( "Argument 'pageSize' must be even" );
+        }
+
+        btree = new BTree();
+        btree._recman = recman;
+        btree._comparator = comparator;
+        btree._keySerializer = keySerializer;
+        btree._valueSerializer = valueSerializer;
+        btree._pageSize = pageSize;
+        btree._bpageSerializer = new BPage();
+        btree._bpageSerializer._btree = btree;
+        btree._recid = recman.insert( btree );
+        return btree;
+    }
+
+
+    /**
+     * Load a persistent BTree.
+     *
+     * @param recman RecordManager used to store the persistent btree
+     * @param recid Record id of the BTree
+     */
+    public static BTree load( RecordManager recman, long recid )
+        throws IOException
+    {
+        BTree btree = (BTree) recman.fetch( recid );
+        btree._recid = recid;
+        btree._recman = recman;
+        btree._bpageSerializer = new BPage();
+        btree._bpageSerializer._btree = btree;
+        return btree;
+    }
+
+
+    /**
+     * Insert an entry in the BTree.
+     * <p>
+     * The BTree cannot store duplicate entries.  An existing entry can be
+     * replaced using the <code>replace</code> flag.   If an entry with the
+     * same key already exists in the BTree, its value is returned.
+     *
+     * @param key Insert key
+     * @param value Insert value
+     * @param replace Set to true to replace an existing key-value pair.
+     * @return Existing value, if any.
+     */
+    public synchronized Object insert( Object key, Object value,
+                                       boolean replace )
+        throws IOException
+    {
+        if ( key == null ) {
+            throw new IllegalArgumentException( "Argument 'key' is null" );
+        }
+        if ( value == null ) {
+            throw new IllegalArgumentException( "Argument 'value' is null" );
+        }
+
+        BPage rootPage = getRoot();
+
+        if ( rootPage == null ) {
+            // BTree is currently empty, create a new root BPage
+            if (DEBUG) {
+                System.out.println( "BTree.insert() new root BPage" );
+            }
+            rootPage = new BPage( this, key, value );
+            _root = rootPage._recid;
+            _height = 1;
+            _entries = 1;
+            _recman.update( _recid, this );
+            return null;
+        } else {
+            BPage.InsertResult insert = rootPage.insert( _height, key, value, replace );
+            boolean dirty = false;
+            if ( insert._overflow != null ) {
+                // current root page overflowed, we replace with a new root page
+                if ( DEBUG ) {
+                    System.out.println( "BTree.insert() replace root BPage due to overflow" );
+                }
+                rootPage = new BPage( this, rootPage, insert._overflow );
+                _root = rootPage._recid;
+                _height += 1;
+                dirty = true;
+            }
+            if ( insert._existing == null ) {
+                _entries++;
+                dirty = true;
+            }
+            if ( dirty ) {
+                _recman.update( _recid, this );
+            }
+            // insert might have returned an existing value
+            return insert._existing;
+        }
+    }
+
+
+    /**
+     * Remove an entry with the given key from the BTree.
+     *
+     * @param key Removal key
+     * @return Value associated with the key, or null if no entry with given
+     *         key existed in the BTree.
+     */
+    public synchronized Object remove( Object key )
+        throws IOException
+    {
+        if ( key == null ) {
+            throw new IllegalArgumentException( "Argument 'key' is null" );
+        }
+
+        BPage rootPage = getRoot();
+        if ( rootPage == null ) {
+            return null;
+        }
+        boolean dirty = false;
+        BPage.RemoveResult remove = rootPage.remove( _height, key );
+        if ( remove._underflow && rootPage.isEmpty() ) {
+            _height -= 1;
+            dirty = true;
+
+            // TODO:  check contract for BPages to be removed from recman.
+            if ( _height == 0 ) {
+                _root = 0;
+            } else {
+                _root = rootPage.childBPage( _pageSize-1 )._recid;
+            }
+        }
+        if ( remove._value != null ) {
+            _entries--;
+            dirty = true;
+        }
+        if ( dirty ) {
+            _recman.update( _recid, this );
+        }
+        return remove._value;
+    }
+
+
+    /**
+     * Find the value associated with the given key.
+     *
+     * @param key Lookup key.
+     * @return Value associated with the key, or null if not found.
+     */
+    public synchronized Object find( Object key )
+        throws IOException
+    {
+        if ( key == null ) {
+            throw new IllegalArgumentException( "Argument 'key' is null" );
+        }
+        BPage rootPage = getRoot();
+        if ( rootPage == null ) {
+            return null;
+        }
+
+        Tuple tuple = new Tuple( null, null );
+        TupleBrowser browser = rootPage.find( _height, key );
+
+        if ( browser.getNext( tuple ) ) {
+            // find returns the matching key or the next ordered key, so we must
+            // check if we have an exact match
+            if ( _comparator.compare( key, tuple.getKey() ) != 0 ) {
+                return null;
+            } else {
+                return tuple.getValue();
+            }
+        } else {
+            return null;
+        }
+    }
+
+
+    /**
+     * Find the value associated with the given key, or the entry immediately
+     * following this key in the ordered BTree.
+     *
+     * @param key Lookup key.
+     * @return Value associated with the key, or a greater entry, or null if no
+     *         greater entry was found.
+     */
+    public synchronized Tuple findGreaterOrEqual( Object key )
+        throws IOException
+    {
+        Tuple         tuple;
+        TupleBrowser  browser;
+
+        if ( key == null ) {
+            // there can't be a key greater than or equal to "null"
+            // because null is considered an infinite key.
+            return null;
+        }
+
+        tuple = new Tuple( null, null );
+        browser = browse( key );
+        if ( browser.getNext( tuple ) ) {
+            return tuple;
+        } else {
+            return null;
+        }
+    }
+
+
+    /**
+     * Get a browser initially positioned at the beginning of the BTree.
+     * <p><b>
+     * WARNING: �If you make structural modifications to the BTree during
+     * browsing, you will get inconsistent browing results.
+     * </b>
+     *
+     * @return Browser positionned at the beginning of the BTree.
+     */
+    public synchronized TupleBrowser browse()
+        throws IOException
+    {
+        BPage rootPage = getRoot();
+        if ( rootPage == null ) {
+            return EmptyBrowser.INSTANCE;
+        }
+        TupleBrowser browser = rootPage.findFirst();
+        return browser;
+    }
+
+
+    /**
+     * Get a browser initially positioned just before the given key.
+     * <p><b>
+     * WARNING: �If you make structural modifications to the BTree during
+     * browsing, you will get inconsistent browing results.
+     * </b>
+     *
+     * @param key Key used to position the browser.  If null, the browser
+     *            will be positionned after the last entry of the BTree.
+     *            (Null is considered to be an "infinite" key)
+     * @return Browser positionned just before the given key.
+     */
+    public synchronized TupleBrowser browse( Object key )
+        throws IOException
+    {
+        BPage rootPage = getRoot();
+        if ( rootPage == null ) {
+            return EmptyBrowser.INSTANCE;
+        }
+        TupleBrowser browser = rootPage.find( _height, key );
+        return browser;
+    }
+
+
+    /**
+     * Return the number of entries (size) of the BTree.
+     */
+    public synchronized int size()
+    {
+        return _entries;
+    }
+
+
+    /**
+     * Return the persistent record identifier of the BTree.
+     */
+    public long getRecid()
+    {
+        return _recid;
+    }
+
+
+    /**
+     * Return the root BPage, or null if it doesn't exist.
+     */
+    private BPage getRoot()
+        throws IOException
+    {
+        if ( _root == 0 ) {
+            return null;
+        }
+        BPage root = (BPage) _recman.fetch( _root, _bpageSerializer );
+        root._recid = _root;
+        root._btree = this;
+        return root;
+    }
+
+    /**
+     * Implement Externalizable interface.
+     */
+    public void readExternal( ObjectInput in )
+        throws IOException, ClassNotFoundException
+    {
+        _comparator = (Comparator) in.readObject();
+        _keySerializer = (Serializer) in.readObject();
+        _valueSerializer = (Serializer) in.readObject();
+        _height = in.readInt();
+        _root = in.readLong();
+        _pageSize = in.readInt();
+        _entries = in.readInt();
+    }
+
+
+    /**
+     * Implement Externalizable interface.
+     */
+    public void writeExternal( ObjectOutput out )
+        throws IOException
+    {
+        out.writeObject( _comparator );
+        out.writeObject( _keySerializer );
+        out.writeObject( _valueSerializer );
+        out.writeInt( _height );
+        out.writeLong( _root );
+        out.writeInt( _pageSize );
+        out.writeInt( _entries );
+    }
+
+
+    public void setValueSerializer( Serializer valueSerializer )
+    {
+        _valueSerializer = valueSerializer;
+    }
+    
+    
+    /*
+    public void assert() throws IOException {
+        BPage root = getRoot();
+        if ( root != null ) {
+            root.assertRecursive( _height );
+        }
+    }
+    */
+
+
+    /*
+    public void dump() throws IOException {
+        BPage root = getRoot();
+        if ( root != null ) {
+            root.dumpRecursive( _height, 0 );
+        }
+    }
+    */
+
+
+    /** PRIVATE INNER CLASS
+     *  Browser returning no element.
+     */
+    static class EmptyBrowser
+        extends TupleBrowser
+    {
+
+        static TupleBrowser INSTANCE = new EmptyBrowser();
+
+        public boolean getNext( Tuple tuple )
+        {
+            return false;
+        }
+
+        public boolean getPrevious( Tuple tuple )
+        {
+            return false;
+        }
+    }
+}
+
diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/btree/package.html b/old_trunk/apacheds-jdbm/src/main/java/jdbm/btree/package.html
new file mode 100644
index 0000000..4ff1def
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/btree/package.html
@@ -0,0 +1,12 @@
+<!-- $Id: package.html,v 1.1 2001/05/19 16:01:32 boisvert Exp $ -->
+<html>
+  <body>
+    <p>B+Tree (scalable persistent tree) data structure implementation.</p>
+
+    <dl>
+      <dt><b>Version: </b></dt><dd>$Revision: 1.1 $ $Date: 2001/05/19 16:01:32 $</dd>
+      <dt><b>Author: </b></dt><dd><a href="mailto:boisvert@intalio.com">Alex Boisvert</a></dd>
+    </dl>
+
+  </body>
+</html>
diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/ByteArrayComparator.java b/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/ByteArrayComparator.java
new file mode 100644
index 0000000..235cf20
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/ByteArrayComparator.java
@@ -0,0 +1,134 @@
+/**
+ * JDBM LICENSE v1.00
+ *
+ * Redistribution and use of this software and associated documentation
+ * ("Software"), with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain copyright
+ *    statements and notices.  Redistributions must also contain a
+ *    copy of this document.
+ *
+ * 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 name "JDBM" must not be used to endorse or promote
+ *    products derived from this Software without prior written
+ *    permission of Cees de Groot.  For written permission,
+ *    please contact cg@cdegroot.com.
+ *
+ * 4. Products derived from this Software may not be called "JDBM"
+ *    nor may "JDBM" appear in their names without prior written
+ *    permission of Cees de Groot.
+ *
+ * 5. Due credit should be given to the JDBM Project
+ *    (http://jdbm.sourceforge.net/).
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS
+ * ``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
+ * CEES DE GROOT OR ANY 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.
+ *
+ * Copyright 2001 (C) Alex Boisvert. All Rights Reserved.
+ * Contributions are Copyright (C) 2001 by their associated contributors.
+ *
+ */
+
+package jdbm.helper;
+
+import java.util.Comparator;
+import java.io.Serializable;
+
+/**
+ * Comparator for byte arrays.
+ *
+ * @author <a href="mailto:boisvert@intalio.com">Alex Boisvert</a>
+ * @version $Id: ByteArrayComparator.java,v 1.4 2002/05/31 06:33:20 boisvert Exp $
+ */
+public final class ByteArrayComparator
+    implements Comparator, Serializable
+{
+
+    /**
+     * Version id for serialization.
+     */
+    final static long serialVersionUID = 1L;
+
+
+    /**
+     * Compare two objects.
+     *
+     * @param obj1 First object
+     * @param obj2 Second object
+     * @return a positive integer if obj1 > obj2, 0 if obj1 == obj2,
+     *         and a negative integer if obj1 < obj2
+     */
+     public int compare( Object obj1, Object obj2 )
+     {
+        if ( obj1 == null ) {
+            throw new IllegalArgumentException( "Argument 'obj1' is null" );
+        }
+
+        if ( obj2 == null ) {
+            throw new IllegalArgumentException( "Argument 'obj2' is null" );
+        }
+
+        return compareByteArray( (byte[]) obj1, (byte[]) obj2 );
+     }
+
+
+    /**
+     * Compare two byte arrays.
+     */
+    public static int compareByteArray( byte[] thisKey, byte[] otherKey )
+    {
+        int len = Math.min( thisKey.length, otherKey.length );
+
+        // compare the byte arrays
+        for ( int i=0; i<len; i++ ) {
+            if ( thisKey[i] >= 0 ) {
+                if ( otherKey[i] >= 0 ) {
+                    // both positive
+                    if ( thisKey[i] < otherKey[i] ) {
+                        return -1;
+                    } else if ( thisKey[i] > otherKey[i] ) {
+                        return 1;
+                    }
+                } else {
+                    // otherKey is negative => greater (because MSB is 1)
+                    return -1;
+                }
+            } else {
+                if ( otherKey[i] >= 0 ) {
+                    // thisKey is negative => greater (because MSB is 1)
+                    return 1;
+                } else {
+                    // both negative
+                    if ( thisKey[i] < otherKey[i] ) {
+                        return -1;
+                    } else if ( thisKey[i] > otherKey[i] ) {
+                        return 1;
+                    }
+                }
+            }
+        }
+        if ( thisKey.length == otherKey.length) {
+            return 0;
+        }
+        if ( thisKey.length < otherKey.length ) {
+            return -1;
+        }
+        return 1;
+    }
+
+}
diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/ByteArraySerializer.java b/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/ByteArraySerializer.java
new file mode 100644
index 0000000..0d7e884
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/ByteArraySerializer.java
@@ -0,0 +1,102 @@
+/**

+ * JDBM LICENSE v1.00

+ *

+ * Redistribution and use of this software and associated documentation

+ * ("Software"), with or without modification, are permitted provided

+ * that the following conditions are met:

+ *

+ * 1. Redistributions of source code must retain copyright

+ *    statements and notices.  Redistributions must also contain a

+ *    copy of this document.

+ *

+ * 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 name "JDBM" must not be used to endorse or promote

+ *    products derived from this Software without prior written

+ *    permission of Cees de Groot.  For written permission,

+ *    please contact cg@cdegroot.com.

+ *

+ * 4. Products derived from this Software may not be called "JDBM"

+ *    nor may "JDBM" appear in their names without prior written

+ *    permission of Cees de Groot.

+ *

+ * 5. Due credit should be given to the JDBM Project

+ *    (http://jdbm.sourceforge.net/).

+ *

+ * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS

+ * ``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

+ * CEES DE GROOT OR ANY 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.

+ *

+ * Copyright 2001 (C) Alex Boisvert. All Rights Reserved.

+ * Contributions are Copyright (C) 2001 by their associated contributors.

+ *

+ */

+

+package jdbm.helper;

+

+import java.io.IOException;

+

+

+/**

+ * Serializer for byte arrays -- simple returns the byte array itself.  No actual

+ * serialization is performed.

+ *

+ * @author <a href="mailto:boisvert@intalio.com">Alex Boisvert</a>

+ * @version $Id: ByteArraySerializer.java,v 1.1 2003/03/21 02:48:42 boisvert Exp $

+ */

+public final class ByteArraySerializer

+    implements Serializer

+{

+

+    /**

+     * Version id for serialization.

+     */

+    final static long serialVersionUID = 1L;

+

+

+    /**

+     * Static instance.

+     */

+    public static final ByteArraySerializer INSTANCE = new ByteArraySerializer();

+    

+    

+    /** 

+     * Serialize the content of an object into a byte array.

+     *

+     * @param obj Object to serialize

+     * @return a byte array representing the object's state

+     *

+     */

+    public byte[] serialize( Object obj ) 

+        throws IOException

+    {

+        return (byte[]) obj;

+    }

+

+    

+    /**

+     * Deserialize the content of an object from a byte array.

+     *

+     * @param serialized Byte array representation of the object

+     * @return deserialized object

+     *

+     */

+    public Object deserialize( byte[] serialized ) 

+        throws IOException

+    {

+        return serialized;

+    }    

+

+}

diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/CacheEvictionException.java b/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/CacheEvictionException.java
new file mode 100644
index 0000000..ff03a0e
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/CacheEvictionException.java
@@ -0,0 +1,75 @@
+/**
+ * JDBM LICENSE v1.00
+ *
+ * Redistribution and use of this software and associated documentation
+ * ("Software"), with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain copyright
+ *    statements and notices.  Redistributions must also contain a
+ *    copy of this document.
+ *
+ * 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 name "JDBM" must not be used to endorse or promote
+ *    products derived from this Software without prior written
+ *    permission of Cees de Groot.  For written permission,
+ *    please contact cg@cdegroot.com.
+ *
+ * 4. Products derived from this Software may not be called "JDBM"
+ *    nor may "JDBM" appear in their names without prior written
+ *    permission of Cees de Groot.
+ *
+ * 5. Due credit should be given to the JDBM Project
+ *    (http://jdbm.sourceforge.net/).
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS
+ * ``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
+ * CEES DE GROOT OR ANY 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.
+ *
+ * Copyright 2000 (C) Cees de Groot. All Rights Reserved.
+ * Contributions are Copyright (C) 2000 by their associated contributors.
+ *
+ * $Id: CacheEvictionException.java,v 1.4 2003/10/21 15:43:20 boisvert Exp $
+ */
+
+package jdbm.helper;
+
+/**
+ *  Exception that occurs during eviction of an object in the cache.
+ *
+ *  @author <a href="mailto:boisvert@intalio.com">Alex Boisvert</a>
+ *  @version $Id: CacheEvictionException.java,v 1.4 2003/10/21 15:43:20 boisvert Exp $
+ */
+public class CacheEvictionException
+    extends Exception
+{
+
+    /**
+     * Nested exception -- the original exception that occured, if any.
+     */
+    protected Exception _nested;
+
+
+    public CacheEvictionException( Exception nested )
+    {
+        _nested = nested;
+    }
+
+    public Exception getNestedException()
+    {
+        return _nested;
+    }
+}
diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/CachePolicy.java b/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/CachePolicy.java
new file mode 100644
index 0000000..cce2805
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/CachePolicy.java
@@ -0,0 +1,143 @@
+/**
+ * JDBM LICENSE v1.00
+ *
+ * Redistribution and use of this software and associated documentation
+ * ("Software"), with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain copyright
+ *    statements and notices.  Redistributions must also contain a
+ *    copy of this document.
+ *
+ * 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 name "JDBM" must not be used to endorse or promote
+ *    products derived from this Software without prior written
+ *    permission of Cees de Groot.  For written permission,
+ *    please contact cg@cdegroot.com.
+ *
+ * 4. Products derived from this Software may not be called "JDBM"
+ *    nor may "JDBM" appear in their names without prior written
+ *    permission of Cees de Groot.
+ *
+ * 5. Due credit should be given to the JDBM Project
+ *    (http://jdbm.sourceforge.net/).
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS
+ * ``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
+ * CEES DE GROOT OR ANY 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.
+ *
+ * Copyright 2000 (C) Cees de Groot. All Rights Reserved.
+ * Contributions are Copyright (C) 2000 by their associated contributors.
+ *
+ * $Id: CachePolicy.java,v 1.5 2003/11/01 13:25:02 dranatunga Exp $
+ */
+
+package jdbm.helper;
+
+import java.util.Enumeration;
+
+/**
+ *  CachePolicity is an abstraction for different cache policies.
+ *  (ie. MRU, time-based, soft-refs, ...)
+ *
+ * @author <a href="mailto:boisvert@intalio.com">Alex Boisvert</a>
+ * @author <a href="mailto:dranatunga@users.sourceforge.net">Dilum Ranatunga</a>
+ * @version $Id: CachePolicy.java,v 1.5 2003/11/01 13:25:02 dranatunga Exp $
+ */
+public interface CachePolicy
+{
+
+    /**
+     * Place an object in the cache. If the cache does not currently contain
+     * an object for the key specified, this mapping is added. If an object
+     * currently exists under the specified key, the current object is
+     * replaced with the new object.
+     * <p>
+     * If the changes to the cache cause the eviction of any objects
+     * <strong>stored under other key(s)</strong>, events corresponding to
+     * the evictions are fired for each object. If an event listener is
+     * unable to handle the eviction, and throws a cache eviction exception,
+     * that exception is propagated to the caller. If such an exception is
+     * thrown, the cache itself should be left as it was before the
+     * <code>put()</code> operation was invoked: the the object whose
+     * eviction failed is still in the cache, and the new insertion or
+     * modification is reverted.
+     *
+     * @param key key for the cached object
+     * @param value the cached object
+     * @throws CacheEvictionException propagated if, while evicting objects
+     *     to make room for new object, an eviction listener encountered
+     *     this problem.
+     */
+    public void put( Object key, Object value )
+        throws CacheEvictionException;
+
+
+    /**
+     * Obtain the object stored under the key specified.
+     *
+     * @param key key the object was cached under
+     * @return the object if it is still in the cache, null otherwise.
+     */
+    public Object get( Object key );
+
+
+    /**
+     * Remove the object stored under the key specified. Note that since
+     * eviction notices are only fired when objects under <strong>different
+     * keys</strong> are evicted, no event is fired for any object stored
+     * under this key (see {@link #put(Object, Object) put( )}).
+     *
+     * @param key key the object was stored in the cache under.
+     */
+    public void remove( Object key );
+
+
+    /**
+     * Remove all objects from the cache. Consistent with
+     * {@link #remove(Object) remove( )}, no eviction notices are fired.
+     */
+    public void removeAll();
+
+
+    /**
+     * Enumerate through the objects currently in the cache.
+     */
+    public Enumeration elements();
+
+
+    /**
+     * Add a listener to this cache policy.
+     * <p>
+     * If this cache policy already contains a listener that is equal to
+     * the one being added, this call has no effect.
+     *
+     * @param listener the (non-null) listener to add to this policy
+     * @throws IllegalArgumentException if listener is null.
+     */
+    public void addListener( CachePolicyListener listener )
+            throws IllegalArgumentException;
+
+    
+    /**
+     * Remove a listener from this cache policy. The listener is found
+     * using object equality, not identity.
+     *
+     * @param listener the listener to remove from this policy
+     */
+    public void removeListener( CachePolicyListener listener );
+
+}
diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/CachePolicyListener.java b/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/CachePolicyListener.java
new file mode 100644
index 0000000..d521ca1
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/CachePolicyListener.java
@@ -0,0 +1,77 @@
+/**
+ * JDBM LICENSE v1.00
+ *
+ * Redistribution and use of this software and associated documentation
+ * ("Software"), with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain copyright
+ *    statements and notices.  Redistributions must also contain a
+ *    copy of this document.
+ *
+ * 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 name "JDBM" must not be used to endorse or promote
+ *    products derived from this Software without prior written
+ *    permission of Cees de Groot.  For written permission,
+ *    please contact cg@cdegroot.com.
+ *
+ * 4. Products derived from this Software may not be called "JDBM"
+ *    nor may "JDBM" appear in their names without prior written
+ *    permission of Cees de Groot.
+ *
+ * 5. Due credit should be given to the JDBM Project
+ *    (http://jdbm.sourceforge.net/).
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS
+ * ``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
+ * CEES DE GROOT OR ANY 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.
+ *
+ * Copyright 2000 (C) Cees de Groot. All Rights Reserved.
+ * Contributions are Copyright (C) 2000 by their associated contributors.
+ *
+ * $Id: CachePolicyListener.java,v 1.3 2003/11/01 13:25:41 dranatunga Exp $
+ */
+
+package jdbm.helper;
+
+/**
+ * Callback interface between {@link CachePolicy} and a Cache implementation
+ * to notify about cached object eviction.
+ * <p>
+ * Note that <code>CachePolicy</code> implementations typically use
+ * <em>object equality</em> when removing listeners, so concrete
+ * implementations of this interface should also pay attention to
+ * their {@link Object#equals(Object)} and {@link Object#hashCode()}
+ * methods.
+ *
+ * @author <a href="mailto:boisvert@intalio.com">Alex Boisvert</a>
+ * @version $Id: CachePolicyListener.java,v 1.3 2003/11/01 13:25:41 dranatunga Exp $
+ */
+public interface CachePolicyListener {
+
+    /**
+     * Notification that the cache this listener is attached to is evicting
+     * the object indicated.
+     *
+     * @param obj object being evited from cache
+     * @throws CacheEvictionException if this listener encountered problems
+     *     while preparing for the specified object's eviction. For example,
+     *     a listener may try to persist the object to disk, and encounter
+     *     an <code>IOException</code>.
+     */
+    public void cacheObjectEvicted(Object obj) throws CacheEvictionException;
+
+}
diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/Conversion.java b/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/Conversion.java
new file mode 100644
index 0000000..1256c4b
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/Conversion.java
@@ -0,0 +1,223 @@
+/**

+ * JDBM LICENSE v1.00

+ *

+ * Redistribution and use of this software and associated documentation

+ * ("Software"), with or without modification, are permitted provided

+ * that the following conditions are met:

+ *

+ * 1. Redistributions of source code must retain copyright

+ *    statements and notices.  Redistributions must also contain a

+ *    copy of this document.

+ *

+ * 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 name "JDBM" must not be used to endorse or promote

+ *    products derived from this Software without prior written

+ *    permission of Cees de Groot.  For written permission,

+ *    please contact cg@cdegroot.com.

+ *

+ * 4. Products derived from this Software may not be called "JDBM"

+ *    nor may "JDBM" appear in their names without prior written

+ *    permission of Cees de Groot.

+ *

+ * 5. Due credit should be given to the JDBM Project

+ *    (http://jdbm.sourceforge.net/).

+ *

+ * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS

+ * ``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

+ * CEES DE GROOT OR ANY 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.

+ *

+ * Copyright 2001 (C) Alex Boisvert. All Rights Reserved.

+ * Contributions are Copyright (C) 2001 by their associated contributors.

+ *

+ */

+

+package jdbm.helper;

+

+

+/**

+ * Miscelaneous conversion utility methods.

+ *

+ * @author <a href="mailto:boisvert@intalio.com">Alex Boisvert</a>

+ * @version $Id: Conversion.java,v 1.3 2002/05/31 06:33:20 boisvert Exp $

+ */

+public class Conversion

+{

+

+    /**

+     * Convert a string into a byte array.

+     */

+    public static byte[] convertToByteArray( String s )

+    {

+        try {

+            // see the following page for character encoding

+            // http://java.sun.com/products/jdk/1.1/docs/guide/intl/encoding.doc.html

+            return s.getBytes( "UTF8" );

+        } catch ( java.io.UnsupportedEncodingException uee ) {

+            uee.printStackTrace();

+            throw new Error( "Platform doesn't support UTF8 encoding" );

+        }

+    }

+

+

+    /**

+     * Convert a byte into a byte array.

+     */

+    public static byte[] convertToByteArray( byte n )

+    {

+        n = (byte)( n ^ ( (byte) 0x80 ) ); // flip MSB because "byte" is signed

+        return new byte[] { n };

+    }

+

+

+    /**

+     * Convert a short into a byte array.

+     */

+    public static byte[] convertToByteArray( short n )

+    {

+        n = (short) ( n ^ ( (short) 0x8000 ) ); // flip MSB because "short" is signed

+        byte[] key = new byte[ 2 ];

+        pack2( key, 0, n );

+        return key;

+    }

+

+

+    /**

+     * Convert an int into a byte array.

+     */

+    public static byte[] convertToByteArray( int n )

+    {

+        n = (n ^ 0x80000000); // flip MSB because "int" is signed

+        byte[] key = new byte[4];

+        pack4(key, 0, n);

+        return key;

+    }

+

+

+    /**

+     * Convert a long into a byte array.

+     */

+    public static byte[] convertToByteArray( long n )

+    {

+        n = (n ^ 0x8000000000000000L); // flip MSB because "long" is signed

+        byte[] key = new byte[8];

+        pack8( key, 0, n );

+        return key;

+    }

+

+

+    /**

+     * Convert a byte array (encoded as UTF-8) into a String

+     */

+    public static String convertToString( byte[] buf )

+    {

+        try {

+            // see the following page for character encoding

+            // http://java.sun.com/products/jdk/1.1/docs/guide/intl/encoding.doc.html

+            return new String( buf, "UTF8" );

+        } catch ( java.io.UnsupportedEncodingException uee ) {

+            uee.printStackTrace();

+            throw new Error( "Platform doesn't support UTF8 encoding" );

+        }

+    }

+

+

+    /**

+     * Convert a byte array into an integer (signed 32-bit) value.

+     */

+    public static int convertToInt( byte[] buf )

+    {

+        int value = unpack4( buf, 0 );

+        value = ( value ^ 0x80000000 ); // flip MSB because "int" is signed

+        return value;

+    }

+

+

+    /**

+     * Convert a byte array into a long (signed 64-bit) value.

+     */

+    public static long convertToLong( byte[] buf )

+    {

+        long value = ( (long) unpack4( buf, 0 ) << 32  )

+                     + ( unpack4( buf, 4 ) & 0xFFFFFFFFL );

+        value = ( value ^ 0x8000000000000000L ); // flip MSB because "long" is signed

+        return value;

+    }

+

+

+

+

+    static int unpack4( byte[] buf, int offset )

+    {

+        int value = ( buf[ offset ] << 24 )

+            | ( ( buf[ offset+1 ] << 16 ) & 0x00FF0000 )

+            | ( ( buf[ offset+2 ] << 8 ) & 0x0000FF00 )

+            | ( ( buf[ offset+3 ] << 0 ) & 0x000000FF );

+

+        return value;

+    }

+

+

+    static final void pack2( byte[] data, int offs, int val )

+    {

+        data[offs++] = (byte) ( val >> 8 );

+        data[offs++] = (byte) val;

+    }

+

+

+    static final void pack4( byte[] data, int offs, int val )

+    {

+        data[offs++] = (byte) ( val >> 24 );

+        data[offs++] = (byte) ( val >> 16 );

+        data[offs++] = (byte) ( val >> 8 );

+        data[offs++] = (byte) val;

+    }

+

+

+    static final void pack8( byte[] data, int offs, long val )

+    {

+        pack4( data, 0, (int) ( val >> 32 ) );

+        pack4( data, 4, (int) val );

+    }

+

+

+    /**

+     * Test static methods

+     */

+    public static void main( String[] args )

+    {

+        byte[] buf;

+

+        buf = convertToByteArray( (int) 5 );

+        System.out.println( "int value of 5 is: " + convertToInt( buf ) );

+

+        buf = convertToByteArray( (int) -1 );

+        System.out.println( "int value of -1 is: " + convertToInt( buf ) );

+

+        buf = convertToByteArray( (int) 22111000 );

+        System.out.println( "int value of 22111000 is: " + convertToInt( buf ) );

+

+

+        buf = convertToByteArray( (long) 5L );

+        System.out.println( "long value of 5 is: " + convertToLong( buf ) );

+

+        buf = convertToByteArray( (long) -1L );

+        System.out.println( "long value of -1 is: " + convertToLong( buf ) );

+

+        buf = convertToByteArray( (long) 1112223334445556667L );

+        System.out.println( "long value of 1112223334445556667 is: " + convertToLong( buf ) );

+    }

+

+}

diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/DefaultSerializer.java b/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/DefaultSerializer.java
new file mode 100644
index 0000000..6c9c040
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/DefaultSerializer.java
@@ -0,0 +1,103 @@
+/**

+ * JDBM LICENSE v1.00

+ *

+ * Redistribution and use of this software and associated documentation

+ * ("Software"), with or without modification, are permitted provided

+ * that the following conditions are met:

+ *

+ * 1. Redistributions of source code must retain copyright

+ *    statements and notices.  Redistributions must also contain a

+ *    copy of this document.

+ *

+ * 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 name "JDBM" must not be used to endorse or promote

+ *    products derived from this Software without prior written

+ *    permission of Cees de Groot.  For written permission,

+ *    please contact cg@cdegroot.com.

+ *

+ * 4. Products derived from this Software may not be called "JDBM"

+ *    nor may "JDBM" appear in their names without prior written

+ *    permission of Cees de Groot.

+ *

+ * 5. Due credit should be given to the JDBM Project

+ *    (http://jdbm.sourceforge.net/).

+ *

+ * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS

+ * ``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

+ * CEES DE GROOT OR ANY 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.

+ *

+ * Copyright 2001 (C) Alex Boisvert. All Rights Reserved.

+ * Contributions are Copyright (C) 2001 by their associated contributors.

+ *

+ */

+

+package jdbm.helper;

+

+import java.io.IOException;

+

+/**

+ * Default java serializer.

+ *

+ * @author <a href="mailto:boisvert@intalio.com">Alex Boisvert</a>

+ * @version $Id: DefaultSerializer.java,v 1.2 2003/09/21 15:47:00 boisvert Exp $

+ */

+public class DefaultSerializer

+    implements Serializer

+{

+

+    

+    public static final DefaultSerializer INSTANCE = new DefaultSerializer();

+    

+    

+    /**

+     * Construct a DefaultSerializer.

+     */

+    public DefaultSerializer()

+    {

+        // no op

+    }

+

+    

+    /**

+     * Serialize the content of an object into a byte array.

+     *

+     * @param obj Object to serialize

+     * @return a byte array representing the object's state

+     */

+     public byte[] serialize( Object obj )

+        throws IOException

+     {

+         return Serialization.serialize( obj );

+     }

+        

+        

+    /**

+     * Deserialize the content of an object from a byte array.

+     *

+     * @param serialized Byte array representation of the object

+     * @return deserialized object

+     */

+     public Object deserialize( byte[] serialized )

+        throws IOException

+     {

+         try {

+            return Serialization.deserialize( serialized );

+         } catch ( ClassNotFoundException except ) {

+            throw new WrappedRuntimeException( except );

+         }

+     }

+

+}

diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/FastIterator.java b/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/FastIterator.java
new file mode 100644
index 0000000..5433f8b
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/FastIterator.java
@@ -0,0 +1,68 @@
+/**

+ * JDBM LICENSE v1.00

+ *

+ * Redistribution and use of this software and associated documentation

+ * ("Software"), with or without modification, are permitted provided

+ * that the following conditions are met:

+ *

+ * 1. Redistributions of source code must retain copyright

+ *    statements and notices.  Redistributions must also contain a

+ *    copy of this document.

+ *

+ * 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 name "JDBM" must not be used to endorse or promote

+ *    products derived from this Software without prior written

+ *    permission of Cees de Groot.  For written permission,

+ *    please contact cg@cdegroot.com.

+ *

+ * 4. Products derived from this Software may not be called "JDBM"

+ *    nor may "JDBM" appear in their names without prior written

+ *    permission of Cees de Groot.

+ *

+ * 5. Due credit should be given to the JDBM Project

+ *    (http://jdbm.sourceforge.net/).

+ *

+ * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS

+ * ``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

+ * CEES DE GROOT OR ANY 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.

+ *

+ * Copyright 2000 (C) Cees de Groot. All Rights Reserved.

+ * Contributions are Copyright (C) 2000 by their associated contributors.

+ *

+ * $Id: FastIterator.java,v 1.2 2003/10/21 15:43:58 boisvert Exp $

+ */

+

+package jdbm.helper;

+

+

+/**

+ * Fast and simple iterator.

+ *

+ * @version $Revision: 1.2 $

+ * @author <a href="boisvert@intalio.com">Alex Boisvert</a>

+ */

+public abstract class FastIterator

+{

+

+    /**

+     * Returns the next element in the interation.

+     *

+     * @return the next element in the iteration, or null if no more element.

+     */

+    public abstract Object next()

+        throws IterationException;

+

+}

diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/IntegerComparator.java b/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/IntegerComparator.java
new file mode 100644
index 0000000..ff46358
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/IntegerComparator.java
@@ -0,0 +1,105 @@
+/**
+ * JDBM LICENSE v1.00
+ *
+ * Redistribution and use of this software and associated documentation
+ * ("Software"), with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain copyright
+ *    statements and notices.  Redistributions must also contain a
+ *    copy of this document.
+ *
+ * 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 name "JDBM" must not be used to endorse or promote
+ *    products derived from this Software without prior written
+ *    permission of Cees de Groot.  For written permission,
+ *    please contact cg@cdegroot.com.
+ *
+ * 4. Products derived from this Software may not be called "JDBM"
+ *    nor may "JDBM" appear in their names without prior written
+ *    permission of Cees de Groot.
+ *
+ * 5. Due credit should be given to the JDBM Project
+ *    (http://jdbm.sourceforge.net/).
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS
+ * ``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
+ * CEES DE GROOT OR ANY 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.
+ *
+ * Copyright 2001 (C) Alex Boisvert. All Rights Reserved.
+ * Contributions are Copyright (C) 2001 by their associated contributors.
+ *
+ */
+
+package jdbm.helper;
+
+import java.io.Serializable;
+import java.util.Comparator;
+
+/**
+ * Comparator for Integer objects.
+ *
+ * @author <a href="mailto:cdaller@iicm.edu">Christof Dallermassl</a>
+ * @version $Id: IntegerComparator.java,v 1.2 2002/05/31 06:33:20 boisvert Exp $
+ */
+public final class IntegerComparator
+    implements Comparator, Serializable
+{
+
+    /**
+     * Version id for serialization.
+     */
+    final static long serialVersionUID = 1L;
+
+
+    /**
+     * Compare two objects.
+     *
+     * @param obj1 First object
+     * @param obj2 Second object
+     * @return a positive integer if obj1 > obj2, 0 if obj1 == obj2,
+     *         and a negative integer if obj1 < obj2
+     */
+    public int compare( Object obj1, Object obj2 )
+    {
+        if ( obj1 == obj2 ) {
+            return 0;
+        }
+
+        if ( obj1 == null ) {
+            throw new IllegalArgumentException( "Argument 'obj1' is null" );
+        }
+
+        if ( obj2 == null ) {
+            throw new IllegalArgumentException( "Argument 'obj2' is null" );
+        }
+
+        // complicated to avoid usage of Integer.compareTo, as this
+        // method is Java 1.2 only!
+        int int1 = ( (Integer) obj1 ).intValue();
+        int int2 = ( (Integer) obj2 ).intValue();
+        if ( int1 == int2 ) {
+            return 0;
+        }
+
+        if ( int1 < int2 ) {
+          return -1;
+        } else {
+          return 1;
+        }
+    }
+
+}
diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/IntegerSerializer.java b/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/IntegerSerializer.java
new file mode 100644
index 0000000..ead07b8
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/IntegerSerializer.java
@@ -0,0 +1,101 @@
+/**

+ * JDBM LICENSE v1.00

+ *

+ * Redistribution and use of this software and associated documentation

+ * ("Software"), with or without modification, are permitted provided

+ * that the following conditions are met:

+ *

+ * 1. Redistributions of source code must retain copyright

+ *    statements and notices.  Redistributions must also contain a

+ *    copy of this document.

+ *

+ * 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 name "JDBM" must not be used to endorse or promote

+ *    products derived from this Software without prior written

+ *    permission of Cees de Groot.  For written permission,

+ *    please contact cg@cdegroot.com.

+ *

+ * 4. Products derived from this Software may not be called "JDBM"

+ *    nor may "JDBM" appear in their names without prior written

+ *    permission of Cees de Groot.

+ *

+ * 5. Due credit should be given to the JDBM Project

+ *    (http://jdbm.sourceforge.net/).

+ *

+ * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS

+ * ``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

+ * CEES DE GROOT OR ANY 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.

+ *

+ * Copyright 2001 (C) Alex Boisvert. All Rights Reserved.

+ * Contributions are Copyright (C) 2001 by their associated contributors.

+ *

+ */

+

+package jdbm.helper;

+

+import java.io.IOException;

+

+/**

+ * Optimized serializer for integers.

+ *

+ * @author <a href="mailto:boisvert@intalio.com">Alex Boisvert</a>

+ * @version $Id: IntegerSerializer.java,v 1.2 2003/09/21 15:47:00 boisvert Exp $

+ */

+public class IntegerSerializer

+    implements Serializer

+{

+

+    

+    public static final IntegerSerializer INSTANCE = new IntegerSerializer();

+    

+    

+    /**

+     * Construct an IntegerSerializer.

+     */

+    public IntegerSerializer()

+    {

+        // no op

+    }

+

+    

+    /**

+     * Serialize the content of an object into a byte array.

+     *

+     * @param obj Object to serialize

+     * @return a byte array representing the object's state

+     */

+     public byte[] serialize( Object obj )

+        throws IOException

+     {

+         Integer number = (Integer) obj;

+         return Conversion.convertToByteArray( number.intValue() );

+     }

+        

+        

+    /**

+     * Deserialize the content of an object from a byte array.

+     *

+     * @param serialized Byte array representation of the object

+     * @return deserialized object

+     */

+     public Object deserialize( byte[] serialized )

+        throws IOException

+     {

+         int number = Conversion.convertToInt( serialized );

+         return new Integer( number );

+     }

+

+}

diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/IterationException.java b/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/IterationException.java
new file mode 100644
index 0000000..278feb8
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/IterationException.java
@@ -0,0 +1,97 @@
+/**

+ * JDBM LICENSE v1.00

+ *

+ * Redistribution and use of this software and associated documentation

+ * ("Software"), with or without modification, are permitted provided

+ * that the following conditions are met:

+ *

+ * 1. Redistributions of source code must retain copyright

+ *    statements and notices.  Redistributions must also contain a

+ *    copy of this document.

+ *

+ * 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 name "JDBM" must not be used to endorse or promote

+ *    products derived from this Software without prior written

+ *    permission of Cees de Groot.  For written permission,

+ *    please contact cg@cdegroot.com.

+ *

+ * 4. Products derived from this Software may not be called "JDBM"

+ *    nor may "JDBM" appear in their names without prior written

+ *    permission of Cees de Groot.

+ *

+ * 5. Due credit should be given to the JDBM Project

+ *    (http://jdbm.sourceforge.net/).

+ *

+ * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS

+ * ``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

+ * CEES DE GROOT OR ANY 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.

+ *

+ * Copyright 2000 (C) Cees de Groot. All Rights Reserved.

+ * Contributions are Copyright (C) 2000 by their associated contributors.

+ *

+ * $Id: IterationException.java,v 1.2 2003/09/21 15:47:00 boisvert Exp $

+ */

+

+package jdbm.helper;

+

+

+/**

+ * Iteration exception.

+ *

+ * @author <a href="boisvert@intalio.com">Alex Boisvert</a>

+ * @version $Revision: 1.2 $

+ */

+public class IterationException

+    extends WrappedRuntimeException

+{

+

+    /**

+     * Construct a new iteration exception wrapping an underlying exception

+     * and providing a message.

+     *

+     * @param message The exception message

+     * @param except The underlying exception

+     */

+    public IterationException( String message, Exception except )

+    {

+        super( message, except );

+    }

+

+

+    /**

+     * Construct a new iteration exception with a message.

+     *

+     * @param message The exception message

+     */

+    public IterationException( String message )

+    {

+        super( message, null );

+    }

+

+

+    /**

+     * Construct a new iteration exception wrapping an underlying exception.

+     *

+     * @param except The underlying exception

+     */

+    public IterationException( Exception except )

+    {

+        super( except );

+    }

+

+}

+

+

diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/LongComparator.java b/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/LongComparator.java
new file mode 100644
index 0000000..b597a4c
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/LongComparator.java
@@ -0,0 +1,98 @@
+/**
+ * JDBM LICENSE v1.00
+ *
+ * Redistribution and use of this software and associated documentation
+ * ("Software"), with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain copyright
+ *    statements and notices.  Redistributions must also contain a
+ *    copy of this document.
+ *
+ * 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 name "JDBM" must not be used to endorse or promote
+ *    products derived from this Software without prior written
+ *    permission of Cees de Groot.  For written permission,
+ *    please contact cg@cdegroot.com.
+ *
+ * 4. Products derived from this Software may not be called "JDBM"
+ *    nor may "JDBM" appear in their names without prior written
+ *    permission of Cees de Groot.
+ *
+ * 5. Due credit should be given to the JDBM Project
+ *    (http://jdbm.sourceforge.net/).
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS
+ * ``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
+ * CEES DE GROOT OR ANY 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.
+ *
+ * Copyright 2001 (C) Alex Boisvert. All Rights Reserved.
+ * Contributions are Copyright (C) 2001 by their associated contributors.
+ *
+ */
+
+package jdbm.helper;
+
+import java.io.Serializable;
+import java.util.Comparator;
+
+/**
+ * Comparator for java.lang.Long objects.
+ *
+ * @author <a href="mailto:boisvert@intalio.com">Alex Boisvert</a>
+ * @version $Id: LongComparator.java,v 1.4 2002/05/31 06:33:20 boisvert Exp $
+ */
+public final class LongComparator
+    implements Comparator, Serializable
+{
+
+    /**
+     * Version id for serialization.
+     */
+    final static long serialVersionUID = 1L;
+
+
+    /**
+     * Compare two objects.
+     *
+     * @param obj1 First object
+     * @param obj2 Second object
+     * @return a positive integer if obj1 > obj2, 0 if obj1 == obj2,
+     *         and a negative integer if obj1 < obj2
+     */
+     public int compare( Object obj1, Object obj2 )
+     {
+        if ( obj1 == null ) {
+            throw new IllegalArgumentException( "Argument 'obj1' is null" );
+        }
+
+        if ( obj2 == null ) {
+            throw new IllegalArgumentException( "Argument 'obj2' is null" );
+        }
+
+        long l1 = ( (Long) obj1 ).longValue();
+        long l2 = ( (Long) obj2 ).longValue();
+
+        if ( l1 > l2 ) {
+            return 1;
+        } else if ( l1 == l2 ) {
+            return 0;
+        } else {
+            return -1;
+        }
+     }
+
+}
diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/LongSerializer.java b/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/LongSerializer.java
new file mode 100644
index 0000000..7872c9b
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/LongSerializer.java
@@ -0,0 +1,101 @@
+/**

+ * JDBM LICENSE v1.00

+ *

+ * Redistribution and use of this software and associated documentation

+ * ("Software"), with or without modification, are permitted provided

+ * that the following conditions are met:

+ *

+ * 1. Redistributions of source code must retain copyright

+ *    statements and notices.  Redistributions must also contain a

+ *    copy of this document.

+ *

+ * 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 name "JDBM" must not be used to endorse or promote

+ *    products derived from this Software without prior written

+ *    permission of Cees de Groot.  For written permission,

+ *    please contact cg@cdegroot.com.

+ *

+ * 4. Products derived from this Software may not be called "JDBM"

+ *    nor may "JDBM" appear in their names without prior written

+ *    permission of Cees de Groot.

+ *

+ * 5. Due credit should be given to the JDBM Project

+ *    (http://jdbm.sourceforge.net/).

+ *

+ * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS

+ * ``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

+ * CEES DE GROOT OR ANY 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.

+ *

+ * Copyright 2001 (C) Alex Boisvert. All Rights Reserved.

+ * Contributions are Copyright (C) 2001 by their associated contributors.

+ *

+ */

+

+package jdbm.helper;

+

+import java.io.IOException;

+

+/**

+ * Optimized serializer for long integers.

+ *

+ * @author <a href="mailto:boisvert@intalio.com">Alex Boisvert</a>

+ * @version $Id: LongSerializer.java,v 1.2 2003/09/21 15:47:00 boisvert Exp $

+ */

+public class LongSerializer 

+    implements Serializer

+{

+

+    

+    public static final LongSerializer INSTANCE = new LongSerializer();

+    

+    

+    /**

+     * Construct a LongSerializer.

+     */

+    public LongSerializer()

+    {

+        // no op

+    }

+

+    

+    /**

+     * Serialize the content of an object into a byte array.

+     *

+     * @param obj Object to serialize

+     * @return a byte array representing the object's state

+     */

+     public byte[] serialize( Object obj )

+        throws IOException

+     {

+         Long number = (Long) obj;

+         return Conversion.convertToByteArray( number.longValue() );

+     }

+        

+        

+    /**

+     * Deserialize the content of an object from a byte array.

+     *

+     * @param serialized Byte array representation of the object

+     * @return deserialized object

+     */

+     public Object deserialize( byte[] serialized )

+        throws IOException

+     {

+         long number = Conversion.convertToLong( serialized );

+         return new Long( number );

+     }

+

+}

diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/MRU.java b/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/MRU.java
new file mode 100644
index 0000000..27f34a2
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/MRU.java
@@ -0,0 +1,334 @@
+/**
+ * JDBM LICENSE v1.00
+ *
+ * Redistribution and use of this software and associated documentation
+ * ("Software"), with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain copyright
+ *    statements and notices.  Redistributions must also contain a
+ *    copy of this document.
+ *
+ * 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 name "JDBM" must not be used to endorse or promote
+ *    products derived from this Software without prior written
+ *    permission of Cees de Groot.  For written permission,
+ *    please contact cg@cdegroot.com.
+ *
+ * 4. Products derived from this Software may not be called "JDBM"
+ *    nor may "JDBM" appear in their names without prior written
+ *    permission of Cees de Groot.
+ *
+ * 5. Due credit should be given to the JDBM Project
+ *    (http://jdbm.sourceforge.net/).
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS
+ * ``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
+ * CEES DE GROOT OR ANY 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.
+ *
+ * Copyright 2000 (C) Cees de Groot. All Rights Reserved.
+ * Contributions are Copyright (C) 2000 by their associated contributors.
+ *
+ * $Id: MRU.java,v 1.8 2005/06/25 23:12:31 doomdark Exp $
+ */
+
+package jdbm.helper;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+
+
+/**
+ *  MRU - Most Recently Used cache policy.
+ *
+ *  Methods are *not* synchronized, so no concurrent access is allowed.
+ *
+ * @author <a href="mailto:boisvert@intalio.com">Alex Boisvert</a>
+ * @version $Id: MRU.java,v 1.8 2005/06/25 23:12:31 doomdark Exp $
+ */
+public class MRU implements CachePolicy {
+
+    /** Cached object hashtable */
+    Hashtable _hash = new Hashtable();
+
+    /**
+     * Maximum number of objects in the cache.
+     */
+    int _max;
+
+    /**
+     * Beginning of linked-list of cache elements.  First entry is element
+     * which has been used least recently.
+     */
+    CacheEntry _first;
+
+    /**
+     * End of linked-list of cache elements.  Last entry is element
+     * which has been used most recently.
+     */
+    CacheEntry _last;
+
+
+    /**
+     * Cache eviction listeners
+     */
+    Vector listeners = new Vector();
+
+
+    /**
+     * Construct an MRU with a given maximum number of objects.
+     */
+    public MRU(int max) {
+        if (max <= 0) {
+            throw new IllegalArgumentException("MRU cache must contain at least one entry");
+        }
+        _max = max;
+    }
+
+
+    /**
+     * Place an object in the cache.
+     */
+    public void put(Object key, Object value) throws CacheEvictionException {
+        CacheEntry entry = (CacheEntry)_hash.get(key);
+        if (entry != null) {
+            entry.setValue(value);
+            touchEntry(entry);
+        } else {
+
+            if (_hash.size() == _max) {
+                // purge and recycle entry
+                entry = purgeEntry();
+                entry.setKey(key);
+                entry.setValue(value);
+            } else {
+                entry = new CacheEntry(key, value);
+            }
+            addEntry(entry);
+            _hash.put(entry.getKey(), entry);
+        }
+    }
+
+
+    /**
+     * Obtain an object in the cache
+     */
+    public Object get(Object key) {
+        CacheEntry entry = (CacheEntry)_hash.get(key);
+        if (entry != null) {
+            touchEntry(entry);
+            return entry.getValue();
+        } else {
+            return null;
+        }
+    }
+
+
+    /**
+     * Remove an object from the cache
+     */
+    public void remove(Object key) {
+        CacheEntry entry = (CacheEntry)_hash.get(key);
+        if (entry != null) {
+            removeEntry(entry);
+            _hash.remove(entry.getKey());
+        }
+    }
+
+
+    /**
+     * Remove all objects from the cache
+     */
+    public void removeAll() {
+        _hash = new Hashtable();
+        _first = null;
+        _last = null;
+    }
+
+
+    /**
+     * Enumerate elements' values in the cache
+     */
+    public Enumeration elements() {
+        return new MRUEnumeration(_hash.elements());
+    }
+
+    /**
+     * Add a listener to this cache policy
+     *
+     * @param listener Listener to add to this policy
+     */
+    public void addListener(CachePolicyListener listener) {
+        if (listener == null) {
+            throw new IllegalArgumentException("Cannot add null listener.");
+        }
+        if ( ! listeners.contains(listener)) {
+            listeners.addElement(listener);
+        }
+    }
+
+    /**
+     * Remove a listener from this cache policy
+     *
+     * @param listener Listener to remove from this policy
+     */
+    public void removeListener(CachePolicyListener listener) {
+        listeners.removeElement(listener);
+    }
+
+    /**
+     * Add a CacheEntry.  Entry goes at the end of the list.
+     */
+    protected void addEntry(CacheEntry entry) {
+        if (_first == null) {
+            _first = entry;
+            _last = entry;
+        } else {
+            _last.setNext(entry);
+            entry.setPrevious(_last);
+            _last = entry;
+        }
+    }
+
+
+    /**
+     * Remove a CacheEntry from linked list
+     */
+    protected void removeEntry(CacheEntry entry) {
+        if (entry == _first) {
+            _first = entry.getNext();
+        }
+        if (_last == entry) {
+            _last = entry.getPrevious();
+        }
+        CacheEntry previous = entry.getPrevious();
+        CacheEntry next = entry.getNext();
+        if (previous != null) {
+            previous.setNext(next);
+        }
+        if (next != null) {
+            next.setPrevious(previous);
+        }
+        entry.setPrevious(null);
+        entry.setNext(null);
+    }
+
+    /**
+     * Place entry at the end of linked list -- Most Recently Used
+     */
+    protected void touchEntry(CacheEntry entry) {
+        if (_last == entry) {
+            return;
+        }
+        removeEntry(entry);
+        addEntry(entry);
+    }
+
+    /**
+     * Purge least recently used object from the cache
+     *
+     * @return recyclable CacheEntry
+     */
+    protected CacheEntry purgeEntry() throws CacheEvictionException {
+        CacheEntry entry = _first;
+
+        // Notify policy listeners first. if any of them throw an
+        // eviction exception, then the internal data structure
+        // remains untouched.
+        CachePolicyListener listener;
+        for (int i=0; i<listeners.size(); i++) {
+            listener = (CachePolicyListener)listeners.elementAt(i);
+            listener.cacheObjectEvicted(entry.getValue());
+        }
+
+        removeEntry(entry);
+        _hash.remove(entry.getKey());
+
+        entry.setValue(null);
+        return entry;
+    }
+
+}
+
+/**
+ * State information for cache entries.
+ */
+class CacheEntry {
+    private Object _key;
+    private Object _value;
+
+    private CacheEntry _previous;
+    private CacheEntry _next;
+
+    CacheEntry(Object key, Object value) {
+        _key = key;
+        _value = value;
+    }
+
+    Object getKey() {
+        return _key;
+    }
+
+    void setKey(Object obj) {
+        _key = obj;
+    }
+
+    Object getValue() {
+        return _value;
+    }
+
+    void setValue(Object obj) {
+        _value = obj;
+    }
+
+    CacheEntry getPrevious() {
+        return _previous;
+    }
+
+    void setPrevious(CacheEntry entry) {
+        _previous = entry;
+    }
+
+    CacheEntry getNext() {
+        return _next;
+    }
+
+    void setNext(CacheEntry entry) {
+        _next = entry;
+    }
+}
+
+/**
+ * Enumeration wrapper to return actual user objects instead of
+ * CacheEntries.
+ */
+class MRUEnumeration implements Enumeration {
+    Enumeration _enum;
+
+    MRUEnumeration(Enumeration enume) {
+        _enum = enume;
+    }
+
+    public boolean hasMoreElements() {
+        return _enum.hasMoreElements();
+    }
+
+    public Object nextElement() {
+        CacheEntry entry = (CacheEntry)_enum.nextElement();
+        return entry.getValue();
+    }
+}
diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/ObjectBAComparator.java b/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/ObjectBAComparator.java
new file mode 100644
index 0000000..11af373
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/ObjectBAComparator.java
@@ -0,0 +1,166 @@
+/**

+ * JDBM LICENSE v1.00

+ *

+ * Redistribution and use of this software and associated documentation

+ * ("Software"), with or without modification, are permitted provided

+ * that the following conditions are met:

+ *

+ * 1. Redistributions of source code must retain copyright

+ *    statements and notices.  Redistributions must also contain a

+ *    copy of this document.

+ *

+ * 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 name "JDBM" must not be used to endorse or promote

+ *    products derived from this Software without prior written

+ *    permission of Cees de Groot.  For written permission,

+ *    please contact cg@cdegroot.com.

+ *

+ * 4. Products derived from this Software may not be called "JDBM"

+ *    nor may "JDBM" appear in their names without prior written

+ *    permission of Cees de Groot.

+ *

+ * 5. Due credit should be given to the JDBM Project

+ *    (http://jdbm.sourceforge.net/).

+ *

+ * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS

+ * ``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

+ * CEES DE GROOT OR ANY 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.

+ *

+ * Copyright 2001 (C) Alex Boisvert. All Rights Reserved.

+ * Contributions are Copyright (C) 2001 by their associated contributors.

+ *

+ */

+

+package jdbm.helper;

+

+import java.io.IOException;

+import java.io.Serializable;

+import java.util.Comparator;

+

+/**

+ * Comparator for objects which have been serialized into byte arrays.

+ * In effect, it wraps another Comparator which compares object and provides

+ * transparent deserialization from byte array to object.

+ *

+ * @author <a href="mailto:boisvert@intalio.com">Alex Boisvert</a>

+ * @version $Id: ObjectBAComparator.java,v 1.1 2002/05/31 06:33:20 boisvert Exp $

+ */

+public final class ObjectBAComparator

+    implements Comparator, Serializable

+{

+

+    /**

+     * Version id for serialization.

+     */

+    final static long serialVersionUID = 1L;

+

+

+    /**

+     * Wrapped comparator.

+     */

+    private Comparator _comparator;

+

+

+    /**

+     * Construct an ObjectByteArrayComparator which wraps an Object Comparator.

+     *

+     * @param comparator Object comparator.

+     */

+    public ObjectBAComparator( Comparator comparator )

+    {

+        if ( comparator == null ) {

+            throw new IllegalArgumentException( "Argument 'comparator' is null" );

+        }

+

+        _comparator = comparator;

+    }

+

+

+    /**

+     * Compare two objects.

+     *

+     * @param obj1 First object

+     * @param obj2 Second object

+     * @return 1 if obj1 > obj2, 0 if obj1 == obj2, -1 if obj1 < obj2

+     */

+     public int compare( Object obj1, Object obj2 )

+     {

+        if ( obj1 == null ) {

+            throw new IllegalArgumentException( "Argument 'obj1' is null" );

+        }

+

+        if ( obj2 == null ) {

+            throw new IllegalArgumentException( "Argument 'obj2' is null" );

+        }

+

+        try {

+            obj1 = Serialization.deserialize( (byte[]) obj1 );

+            obj2 = Serialization.deserialize( (byte[]) obj2 );

+

+            return _comparator.compare( obj1, obj2 );

+        } catch ( IOException except ) {

+            throw new WrappedRuntimeException( except );

+        } catch ( ClassNotFoundException except ) {

+            throw new WrappedRuntimeException( except );

+        }

+     }

+

+

+    /**

+     * Compare two byte arrays.

+     */

+    public static int compareByteArray( byte[] thisKey, byte[] otherKey )

+    {

+        int len = Math.min( thisKey.length, otherKey.length );

+

+        // compare the byte arrays

+        for ( int i=0; i<len; i++ ) {

+            if ( thisKey[i] >= 0 ) {

+                if ( otherKey[i] >= 0 ) {

+                    // both positive

+                    if ( thisKey[i] < otherKey[i] ) {

+                        return -1;

+                    } else if ( thisKey[i] > otherKey[i] ) {

+                        return 1;

+                    }

+                } else {

+                    // otherKey is negative => greater (because MSB is 1)

+                    return -1;

+                }

+            } else {

+                if ( otherKey[i] >= 0 ) {

+                    // thisKey is negative => greater (because MSB is 1)

+                    return 1;

+                } else {

+                    // both negative

+                    if ( thisKey[i] < otherKey[i] ) {

+                        return -1;

+                    } else if ( thisKey[i] > otherKey[i] ) {

+                        return 1;

+                    }

+                }

+            }

+        }

+        if ( thisKey.length == otherKey.length) {

+            return 0;

+        }

+        if ( thisKey.length < otherKey.length ) {

+            return -1;

+        }

+        return 1;

+    }

+

+}

diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/Serialization.java b/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/Serialization.java
new file mode 100644
index 0000000..13ded7a
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/Serialization.java
@@ -0,0 +1,96 @@
+/**

+ * JDBM LICENSE v1.00

+ *

+ * Redistribution and use of this software and associated documentation

+ * ("Software"), with or without modification, are permitted provided

+ * that the following conditions are met:

+ *

+ * 1. Redistributions of source code must retain copyright

+ *    statements and notices.  Redistributions must also contain a

+ *    copy of this document.

+ *

+ * 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 name "JDBM" must not be used to endorse or promote

+ *    products derived from this Software without prior written

+ *    permission of Cees de Groot.  For written permission,

+ *    please contact cg@cdegroot.com.

+ *

+ * 4. Products derived from this Software may not be called "JDBM"

+ *    nor may "JDBM" appear in their names without prior written

+ *    permission of Cees de Groot.

+ *

+ * 5. Due credit should be given to the JDBM Project

+ *    (http://jdbm.sourceforge.net/).

+ *

+ * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS

+ * ``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

+ * CEES DE GROOT OR ANY 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.

+ *

+ * Copyright 2001 (C) Alex Boisvert. All Rights Reserved.

+ * Contributions are Copyright (C) 2001 by their associated contributors.

+ *

+ */

+

+package jdbm.helper;

+

+import java.io.ByteArrayInputStream;

+import java.io.ByteArrayOutputStream;

+import java.io.IOException;

+import java.io.ObjectInputStream;

+import java.io.ObjectOutputStream;

+

+/**

+ * Serialization-related utility methods.

+ *

+ * @author <a href="mailto:boisvert@intalio.com">Alex Boisvert</a>

+ * @version $Id: Serialization.java,v 1.1 2002/05/31 06:33:20 boisvert Exp $

+ */

+public final class Serialization

+{

+

+    /**

+     * Serialize the object into a byte array.

+     */

+    public static byte[] serialize( Object obj )

+        throws IOException

+    {

+        ByteArrayOutputStream  baos;

+        ObjectOutputStream     oos;

+

+        baos = new ByteArrayOutputStream();

+        oos = new ObjectOutputStream( baos );

+        oos.writeObject( obj );

+        oos.close();

+

+        return baos.toByteArray();

+    }

+

+

+    /**

+     * Deserialize an object from a byte array

+     */

+    public static Object deserialize( byte[] buf )

+        throws ClassNotFoundException, IOException

+    {

+        ByteArrayInputStream  bais;

+        ObjectInputStream     ois;

+

+        bais = new ByteArrayInputStream( buf );

+        ois = new ObjectInputStream( bais );

+        return ois.readObject();

+    }

+

+}

diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/Serializer.java b/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/Serializer.java
new file mode 100644
index 0000000..b0e7883
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/Serializer.java
@@ -0,0 +1,82 @@
+/**

+ * JDBM LICENSE v1.00

+ *

+ * Redistribution and use of this software and associated documentation

+ * ("Software"), with or without modification, are permitted provided

+ * that the following conditions are met:

+ *

+ * 1. Redistributions of source code must retain copyright

+ *    statements and notices.  Redistributions must also contain a

+ *    copy of this document.

+ *

+ * 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 name "JDBM" must not be used to endorse or promote

+ *    products derived from this Software without prior written

+ *    permission of Cees de Groot.  For written permission,

+ *    please contact cg@cdegroot.com.

+ *

+ * 4. Products derived from this Software may not be called "JDBM"

+ *    nor may "JDBM" appear in their names without prior written

+ *    permission of Cees de Groot.

+ *

+ * 5. Due credit should be given to the JDBM Project

+ *    (http://jdbm.sourceforge.net/).

+ *

+ * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS

+ * ``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

+ * CEES DE GROOT OR ANY 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.

+ *

+ * Copyright 2001 (C) Alex Boisvert. All Rights Reserved.

+ * Contributions are Copyright (C) 2001 by their associated contributors.

+ *

+ */

+

+package jdbm.helper;

+

+import java.io.IOException;

+import java.io.Serializable;

+

+/**

+ * Interface used to provide a serialization mechanism other than a class' normal

+ * serialization.

+ *

+ * @author <a href="mailto:boisvert@intalio.com">Alex Boisvert</a>

+ * @version $Id: Serializer.java,v 1.1 2003/03/21 02:48:42 boisvert Exp $

+ */

+public interface Serializer

+    extends Serializable

+{

+

+    /**

+     * Serialize the content of an object into a byte array.

+     *

+     * @param obj Object to serialize

+     * @return a byte array representing the object's state

+     */

+     public byte[] serialize( Object obj )

+        throws IOException;

+        

+        

+    /**

+     * Deserialize the content of an object from a byte array.

+     *

+     * @param serialized Byte array representation of the object

+     * @return deserialized object

+     */

+     public Object deserialize( byte[] serialized )

+        throws IOException;

+

+}

diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/SoftCache.java b/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/SoftCache.java
new file mode 100644
index 0000000..217003c
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/SoftCache.java
@@ -0,0 +1,294 @@
+/**
+ * JDBM LICENSE v1.00
+ *
+ * Redistribution and use of this software and associated documentation
+ * ("Software"), with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain copyright
+ *    statements and notices.  Redistributions must also contain a
+ *    copy of this document.
+ *
+ * 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 name "JDBM" must not be used to endorse or promote
+ *    products derived from this Software without prior written
+ *    permission of Cees de Groot.  For written permission,
+ *    please contact cg@cdegroot.com.
+ *
+ * 4. Products derived from this Software may not be called "JDBM"
+ *    nor may "JDBM" appear in their names without prior written
+ *    permission of Cees de Groot.
+ *
+ * 5. Due credit should be given to the JDBM Project
+ *    (http://jdbm.sourceforge.net/).
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS
+ * ``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
+ * CEES DE GROOT OR ANY 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.
+ *
+ * Copyright 2000 (C) Cees de Groot. All Rights Reserved.
+ * Contributions are Copyright (C) 2000 by their associated contributors.
+ *
+ * $Id
+ */
+package jdbm.helper;
+
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.SoftReference;
+import java.lang.ref.Reference;
+import java.util.Enumeration;
+import java.util.Map;
+import java.util.HashMap;
+
+/**
+ * Wraps a deterministic cache policy with a <q>Level-2</q> cache based on
+ * J2SE's {@link SoftReference soft references}. Soft references allow
+ * this cache to keep references to objects until the memory they occupy
+ * is required elsewhere.
+ * <p>
+ * Since the {@link CachePolicy} interface requires an event be fired
+ * when an object is evicted, and the event contains the actual object,
+ * this class cannot be a stand-alone implementation of
+ * <code>CachePolicy</code>. This limitation arises because Java References
+ * does not support notification before references are cleared; nor do
+ * they support reaching soft referents. Therefore, this wrapper cache
+ * aggressively notifies evictions: events are fired when the objects are
+ * evicted from the internal cache. Consequently, the soft cache may return
+ * a non-null object when <code>get( )</code> is called, even if that
+ * object was said to have been evicted.
+ * <p>
+ * The current implementation uses a hash structure for its internal key
+ * to value mappings.
+ * <p>
+ * Note: this component's publicly exposed methods are not threadsafe;
+ * potentially concurrent code should synchronize on the cache instance.
+ *
+ * @author <a href="mailto:dranatunga@users.sourceforge.net">Dilum Ranatunga</a>
+ * @version $Id: SoftCache.java,v 1.1 2003/11/01 13:29:27 dranatunga Exp $
+ */
+public class SoftCache implements CachePolicy {
+    private static final int INITIAL_CAPACITY = 128;
+    private static final float DEFAULT_LOAD_FACTOR = 1.5f;
+
+    private final ReferenceQueue _clearQueue = new ReferenceQueue();
+    private final CachePolicy _internal;
+    private final Map _cacheMap;
+
+    /**
+     * Creates a soft-reference based L2 cache with a {@link MRU} cache as
+     * the internal (L1) cache. The soft reference cache uses the
+     * default load capacity of 1.5f, which is intended to sacrifice some
+     * performance for space. This compromise is reasonable, since all
+     * {@link #get(Object) get( )s} first try the L1 cache anyway. The
+     * internal MRU is given a capacity of 128 elements.
+     */
+    public SoftCache() {
+        this(new MRU(INITIAL_CAPACITY));
+    }
+
+    /**
+     * Creates a soft-reference based L2 cache wrapping the specified
+     * L1 cache.
+     *
+     * @param internal non null internal cache.
+     * @throws NullPointerException if the internal cache is null.
+     */
+    public SoftCache(CachePolicy internal) throws NullPointerException {
+        this(DEFAULT_LOAD_FACTOR, internal);
+    }
+
+    /**
+     * Creates a soft-reference based L2 cache wrapping the specified
+     * L1 cache. This constructor is somewhat implementation-specific,
+     * so users are encouraged to use {@link #SoftCache(CachePolicy)}
+     * instead.
+     *
+     * @param loadFactor load factor that the soft cache's hash structure
+     *        should use.
+     * @param internal non null internal cache.
+     * @throws IllegalArgumentException if the load factor is nonpositive.
+     * @throws NullPointerException if the internal cache is null.
+     */
+    public SoftCache(float loadFactor, CachePolicy internal) throws IllegalArgumentException, NullPointerException {
+        if (internal == null) {
+            throw new NullPointerException("Internal cache cannot be null.");
+        }
+        _internal = internal;
+        _cacheMap = new HashMap(INITIAL_CAPACITY, loadFactor);
+    }
+
+    /**
+     * Adds the specified value to the cache under the specified key. Note
+     * that the object is added to both this and the internal cache.
+     * @param key the (non-null) key to store the object under
+     * @param value the (non-null) object to place in the cache
+     * @throws CacheEvictionException exception that the internal cache
+     *         would have experienced while evicting an object it currently
+     *         cached.
+     */
+    public void put(Object key, Object value) throws CacheEvictionException {
+        if (key == null) {
+            throw new IllegalArgumentException("key cannot be null.");
+        } else if (value == null) {
+            throw new IllegalArgumentException("value cannot be null.");
+        }
+        _internal.put(key, value);
+        removeClearedEntries();
+        _cacheMap.put(key, new Entry(key, value, _clearQueue));
+    }
+
+    /**
+     * Gets the object cached under the specified key.
+     * <p>
+     * The cache is looked up in the following manner:
+     * <ol>
+     * <li>The internal (L1) cache is checked. If the object is found, it is
+     *     returned.</li>
+     * <li>This (L2) cache is checked. If the object is not found, then
+     *     the caller is informed that the object is inaccessible.</li>
+     * <li>Since the object exists in L2, but not in L1, the object is
+     *     readded to L1 using {@link CachePolicy#put(Object, Object)}.</li>
+     * <li>If the readding succeeds, the value is returned to caller.</li>
+     * <li>If a cache eviction exception is encountered instead, we
+     *     remove the object from L2 and behave as if the object was
+     *     inaccessible.</li>
+     * </ol>
+     * @param key the key that the object was stored under.
+     * @return the object stored under the key specified; null if the
+     *         object is not (nolonger) accessible via this cache.
+     */
+    public Object get(Object key) {
+        // first try the internal cache.
+        Object value = _internal.get(key);
+        if (value != null) {
+            return value;
+        }
+        // poll and remove cleared references.
+        removeClearedEntries();
+        Entry entry = (Entry)_cacheMap.get(key);
+        if (entry == null) { // object is not in cache.
+            return null;
+        }
+        value = entry.getValue();
+        if (value == null) { // object was in cache, but it was cleared.
+            return null;
+        }
+        // we have the object. so we try to re-insert it into internal cache
+        try {
+            _internal.put(key, value);
+        } catch (CacheEvictionException e) {
+            // if the internal cache causes a fuss, we kick the object out.
+            _cacheMap.remove(key);
+            return null;
+        }
+        return value;
+    }
+
+    /**
+     * Removes any object stored under the key specified. Note that the
+     * object is removed from both this (L2) and the internal (L1)
+     * cache.
+     * @param key the key whose object should be removed
+     */
+    public void remove(Object key) {
+        _cacheMap.remove(key);
+        _internal.remove(key);
+    }
+
+    /**
+     * Removes all objects in this (L2) and its internal (L1) cache.
+     */
+    public void removeAll() {
+        _cacheMap.clear();
+        _internal.removeAll();
+    }
+
+    /**
+     * Gets all the objects stored by the internal (L1) cache.
+     * @return an enumeration of objects in internal cache.
+     */
+    public Enumeration elements() {
+        return _internal.elements();
+    }
+
+    /**
+     * Adds the specified listener to this cache. Note that the events
+     * fired by this correspond to the <em>internal</em> cache's events.
+     * @param listener the (non-null) listener to add to this policy
+     * @throws IllegalArgumentException if listener is null.
+     */
+    public void addListener(CachePolicyListener listener)
+            throws IllegalArgumentException {
+        _internal.addListener(listener);
+    }
+
+    /**
+     * Removes a listener that was added earlier.
+     * @param listener the listener to remove.
+     */
+    public void removeListener(CachePolicyListener listener) {
+        _internal.removeListener(listener);
+    }
+
+    /**
+     * Cleans the mapping structure of any obsolete entries. This is usually
+     * called before insertions and lookups on the mapping structure. The
+     * runtime of this is usually very small, but it can be as expensive as
+     * n * log(n) if a large number of soft references were recently cleared.
+     */
+    private final void removeClearedEntries() {
+        for (Reference r = _clearQueue.poll(); r != null; r = _clearQueue.poll()) {
+            Object key = ((Entry)r).getKey();
+            _cacheMap.remove(key);
+        }
+    }
+
+    /**
+     * Value objects we keep in the internal map. This contains the key in
+     * addition to the value, because polling for cleared references
+     * returns these instances, and having access to their corresponding
+     * keys drastically improves the performance of removing the pair
+     * from the map (see {@link SoftCache#removeClearedEntries()}.)
+     */
+    private static class Entry extends SoftReference {
+        private final Object _key;
+
+        /**
+         * Constructor that uses <code>value</code> as the soft
+         * reference's referent.
+         */
+        public Entry(Object key, Object value, ReferenceQueue queue) {
+            super(value, queue);
+            _key = key;
+        }
+
+        /**
+         * Gets the key
+         * @return the key associated with this value.
+         */
+        final Object getKey() {
+            return _key;
+        }
+
+        /**
+         * Gets the value
+         * @return the value; null if it is no longer accessible
+         */
+        final Object getValue() {
+            return this.get();
+        }
+    }
+}
diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/StringComparator.java b/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/StringComparator.java
new file mode 100644
index 0000000..151a108
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/StringComparator.java
@@ -0,0 +1,89 @@
+/**
+ * JDBM LICENSE v1.00
+ *
+ * Redistribution and use of this software and associated documentation
+ * ("Software"), with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain copyright
+ *    statements and notices.  Redistributions must also contain a
+ *    copy of this document.
+ *
+ * 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 name "JDBM" must not be used to endorse or promote
+ *    products derived from this Software without prior written
+ *    permission of Cees de Groot.  For written permission,
+ *    please contact cg@cdegroot.com.
+ *
+ * 4. Products derived from this Software may not be called "JDBM"
+ *    nor may "JDBM" appear in their names without prior written
+ *    permission of Cees de Groot.
+ *
+ * 5. Due credit should be given to the JDBM Project
+ *    (http://jdbm.sourceforge.net/).
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS
+ * ``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
+ * CEES DE GROOT OR ANY 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.
+ *
+ * Copyright 2001 (C) Alex Boisvert. All Rights Reserved.
+ * Contributions are Copyright (C) 2001 by their associated contributors.
+ *
+ */
+
+package jdbm.helper;
+
+import java.io.Serializable;
+import java.util.Comparator;
+
+/**
+ * Comparator for String objects.  Delegates to String.compareTo().
+ *
+ * @author <a href="mailto:boisvert@intalio.com">Alex Boisvert</a>
+ * @version $Id: StringComparator.java,v 1.5 2005/06/25 23:12:31 doomdark Exp $
+ */
+public final class StringComparator
+    implements Comparator, Serializable
+{
+
+    /**
+     * Version id for serialization.
+     */
+    final static long serialVersionUID = 1L;
+
+
+    /**
+     * Compare two objects.
+     *
+     * @param obj1 First object
+     * @param obj2 Second object
+     * @return a positive integer if obj1 > obj2, 0 if obj1 == obj2,
+     *         and a negative integer if obj1 < obj2
+     */
+     public int compare( Object obj1, Object obj2 )
+     {
+        if ( obj1 == null ) {
+            throw new IllegalArgumentException( "Argument 'obj1' is null" );
+        }
+
+        if ( obj2 == null ) {
+            throw new IllegalArgumentException( "Argument 'obj2' is null" );
+        }
+
+        return ( (String) obj1 ).compareTo((String) obj2 );
+     }
+
+}
diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/Tuple.java b/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/Tuple.java
new file mode 100644
index 0000000..1baffdc
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/Tuple.java
@@ -0,0 +1,121 @@
+/**
+ * JDBM LICENSE v1.00
+ *
+ * Redistribution and use of this software and associated documentation
+ * ("Software"), with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain copyright
+ *    statements and notices.  Redistributions must also contain a
+ *    copy of this document.
+ *
+ * 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 name "JDBM" must not be used to endorse or promote
+ *    products derived from this Software without prior written
+ *    permission of Cees de Groot.  For written permission,
+ *    please contact cg@cdegroot.com.
+ *
+ * 4. Products derived from this Software may not be called "JDBM"
+ *    nor may "JDBM" appear in their names without prior written
+ *    permission of Cees de Groot.
+ *
+ * 5. Due credit should be given to the JDBM Project
+ *    (http://jdbm.sourceforge.net/).
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS
+ * ``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
+ * CEES DE GROOT OR ANY 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.
+ *
+ * Copyright 2001 (C) Alex Boisvert. All Rights Reserved.
+ * Contributions are Copyright (C) 2001 by their associated contributors.
+ *
+ */
+
+package jdbm.helper;
+
+
+/**
+ * Tuple consisting of a key-value pair.
+ *
+ * @author <a href="mailto:boisvert@intalio.com">Alex Boisvert</a>
+ * @version $Id: Tuple.java,v 1.2 2001/05/19 14:02:00 boisvert Exp $
+ */
+public final class Tuple {
+
+    /**
+     * Key
+     */
+    private Object _key;
+
+
+    /**
+     * Value
+     */
+    private Object _value;
+
+
+    /**
+     * Construct an empty Tuple.
+     */
+    public Tuple() {
+        // empty
+    }
+
+
+    /**
+     * Construct a Tuple.
+     *
+     * @param key The key.
+     * @param value The value.
+     */
+    public Tuple( Object key, Object value ) {
+        _key = key;
+        _value = value;
+    }
+
+
+    /**
+     * Get the key.
+     */
+    public Object getKey() {
+        return _key;
+    }
+
+
+    /**
+     * Set the key.
+     */
+    public void setKey( Object key ) {
+        _key = key;
+    }
+
+
+    /**
+     * Get the value.
+     */
+    public Object getValue() {
+        return _value;
+    }
+
+
+    /**
+     * Set the value.
+     */
+    public void setValue( Object value ) {
+        _value = value;
+    }
+
+}
diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/TupleBrowser.java b/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/TupleBrowser.java
new file mode 100644
index 0000000..3f34011
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/TupleBrowser.java
@@ -0,0 +1,81 @@
+/**
+ * JDBM LICENSE v1.00
+ *
+ * Redistribution and use of this software and associated documentation
+ * ("Software"), with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain copyright
+ *    statements and notices.  Redistributions must also contain a
+ *    copy of this document.
+ *
+ * 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 name "JDBM" must not be used to endorse or promote
+ *    products derived from this Software without prior written
+ *    permission of Cees de Groot.  For written permission,
+ *    please contact cg@cdegroot.com.
+ *
+ * 4. Products derived from this Software may not be called "JDBM"
+ *    nor may "JDBM" appear in their names without prior written
+ *    permission of Cees de Groot.
+ *
+ * 5. Due credit should be given to the JDBM Project
+ *    (http://jdbm.sourceforge.net/).
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS
+ * ``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
+ * CEES DE GROOT OR ANY 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.
+ *
+ * Copyright 2001 (C) Alex Boisvert. All Rights Reserved.
+ * Contributions are Copyright (C) 2001 by their associated contributors.
+ *
+ */
+
+package jdbm.helper;
+
+import java.io.IOException;
+
+/**
+ * Browser to traverse a collection of tuples.  The browser allows for
+ * forward and reverse order traversal.
+ *
+ * @author <a href="mailto:boisvert@intalio.com">Alex Boisvert</a>
+ * @version $Id: TupleBrowser.java,v 1.2 2001/05/19 14:02:00 boisvert Exp $
+ */
+public abstract class TupleBrowser {
+
+    /**
+     * Get the next tuple.
+     *
+     * @param tuple Tuple into which values are copied.
+     * @return True if values have been copied in tuple, or false if there is
+     *         no next tuple.
+     */
+    public abstract boolean getNext( Tuple tuple )
+        throws IOException;
+
+
+    /**
+     * Get the previous tuple.
+     *
+     * @param tuple Tuple into which values are copied.
+     * @return True if values have been copied in tuple, or false if there is
+     *         no previous tuple.
+     */
+    public abstract boolean getPrevious( Tuple tuple )
+        throws IOException;
+
+}
diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/WrappedRuntimeException.java b/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/WrappedRuntimeException.java
new file mode 100644
index 0000000..01b545e
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/WrappedRuntimeException.java
@@ -0,0 +1,151 @@
+/**

+ * JDBM LICENSE v1.00

+ *

+ * Redistribution and use of this software and associated documentation

+ * ("Software"), with or without modification, are permitted provided

+ * that the following conditions are met:

+ *

+ * 1. Redistributions of source code must retain copyright

+ *    statements and notices.  Redistributions must also contain a

+ *    copy of this document.

+ *

+ * 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 name "JDBM" must not be used to endorse or promote

+ *    products derived from this Software without prior written

+ *    permission of Cees de Groot.  For written permission,

+ *    please contact cg@cdegroot.com.

+ *

+ * 4. Products derived from this Software may not be called "JDBM"

+ *    nor may "JDBM" appear in their names without prior written

+ *    permission of Cees de Groot.

+ *

+ * 5. Due credit should be given to the JDBM Project

+ *    (http://jdbm.sourceforge.net/).

+ *

+ * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS

+ * ``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

+ * CEES DE GROOT OR ANY 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.

+ *

+ * Copyright 2001 (C) Alex Boisvert. All Rights Reserved.

+ * Contributions are Copyright (C) 2001 by their associated contributors.

+ *

+ */

+

+package jdbm.helper;

+

+import java.io.PrintStream;

+import java.io.PrintWriter;

+

+/**

+ * A run-time exception that wraps another exception. The printed stack

+ * trace will be that of the wrapped exception.

+ *

+ * @author <a href="mailto:boisvert@intalio.com">Alex Boisvert</a>

+ * @version $Id: WrappedRuntimeException.java,v 1.1 2002/05/31 06:33:20 boisvert Exp $

+ */

+public class WrappedRuntimeException

+    extends RuntimeException

+{

+

+

+    /**

+     * The underlying exception.

+     */

+    private final Exception _except;

+

+

+    /**

+     * Constructs a new runtime exception based on a checked exception.

+     *

+     * @param message The error message

+     * @param except The checked exception

+     */

+    public WrappedRuntimeException( String message, Exception except )

+    {

+        super( message == null ? "No message available" : message );

+

+        if ( except instanceof WrappedRuntimeException &&

+             ( (WrappedRuntimeException) except )._except != null )

+        {

+            _except = ( (WrappedRuntimeException) except )._except;

+        } else {

+            _except = except;

+        }

+    }

+

+

+    /**

+     * Constructs a new runtime exception based on a checked exception.

+     *

+     * @param except The checked exception

+     */

+    public WrappedRuntimeException( Exception except )

+    {

+        super( except == null || except.getMessage() == null ? "No message available" : except.getMessage() );

+

+        if ( except instanceof WrappedRuntimeException &&

+             ( (WrappedRuntimeException) except )._except != null )

+        {

+            _except = ( (WrappedRuntimeException) except )._except;

+        } else {

+            _except = except;

+        }

+    }

+

+

+    /**

+     * Returns the exception wrapped by this runtime exception.

+     *

+     * @return The exception wrapped by this runtime exception

+     */

+    public Exception getException()

+    {

+        return _except;

+    }

+

+

+    public void printStackTrace()

+    {

+        if ( _except == null ) {

+            super.printStackTrace();

+        } else {

+            _except.printStackTrace();

+        }

+    }

+

+

+    public void printStackTrace( PrintStream stream )

+    {

+        if ( _except == null ) {

+            super.printStackTrace( stream );

+        } else {

+            _except.printStackTrace( stream );

+        }

+    }

+

+

+    public void printStackTrace( PrintWriter writer )

+    {

+        if ( _except == null ) {

+            super.printStackTrace( writer );

+        } else {

+            _except.printStackTrace( writer );

+        }

+    }

+

+}

+

+

diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/package.html b/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/package.html
new file mode 100644
index 0000000..ea48070
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/helper/package.html
@@ -0,0 +1,12 @@
+<!-- $Id: package.html,v 1.1 2001/05/19 16:01:33 boisvert Exp $ -->
+<html>
+  <body>
+    <p>Miscelaneous utility classes and interfaces.</p>
+
+    <dl>
+      <dt><b>Version: </b></dt><dd>$Revision: 1.1 $ $Date: 2001/05/19 16:01:33 $</dd>
+      <dt><b>Author: </b></dt><dd><a href="mailto:boisvert@intalio.com">Alex Boisvert</a></dd>
+    </dl>
+
+  </body>
+</html>
diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/htree/HTree.java b/old_trunk/apacheds-jdbm/src/main/java/jdbm/htree/HTree.java
new file mode 100644
index 0000000..d5eeb33
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/htree/HTree.java
@@ -0,0 +1,188 @@
+/**

+ * JDBM LICENSE v1.00

+ *

+ * Redistribution and use of this software and associated documentation

+ * ("Software"), with or without modification, are permitted provided

+ * that the following conditions are met:

+ *

+ * 1. Redistributions of source code must retain copyright

+ *    statements and notices.  Redistributions must also contain a

+ *    copy of this document.

+ *

+ * 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 name "JDBM" must not be used to endorse or promote

+ *    products derived from this Software without prior written

+ *    permission of Cees de Groot.  For written permission,

+ *    please contact cg@cdegroot.com.

+ *

+ * 4. Products derived from this Software may not be called "JDBM"

+ *    nor may "JDBM" appear in their names without prior written

+ *    permission of Cees de Groot.

+ *

+ * 5. Due credit should be given to the JDBM Project

+ *    (http://jdbm.sourceforge.net/).

+ *

+ * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS

+ * ``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

+ * CEES DE GROOT OR ANY 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.

+ *

+ * Copyright 2000 (C) Cees de Groot. All Rights Reserved.

+ * Contributions are (C) Copyright 2000 by their associated contributors.

+ *

+ */

+

+package jdbm.htree;

+

+import jdbm.RecordManager;

+import jdbm.helper.FastIterator;

+import java.io.IOException;

+

+/**

+ *  Persistent hashtable implementation for PageManager.

+ *  Implemented as an H*Tree structure.

+ *

+ *  WARNING!  If this instance is used in a transactional context, it

+ *            *must* be discarded after a rollback.

+ *

+ *  @author <a href="mailto:boisvert@intalio.com">Alex Boisvert</a>

+ *  @version $Id: HTree.java,v 1.3 2005/06/25 23:12:32 doomdark Exp $

+ */

+public class HTree

+{

+

+    /**

+     * Root hash directory.

+     */

+    private HashDirectory _root;

+

+

+    /**

+     * Private constructor

+     *

+     * @param root Root hash directory.

+     */

+    private HTree( HashDirectory root ) {

+        _root = root;

+    }

+

+

+    /**

+     * Create a persistent hashtable.

+     *

+     * @param recman Record manager used for persistence.

+     */

+    public static HTree createInstance( RecordManager recman )

+        throws IOException

+    {

+        HashDirectory  root;

+        long           recid;

+

+        root = new HashDirectory( (byte) 0 );

+        recid = recman.insert( root );

+        root.setPersistenceContext( recman, recid );

+

+        return new HTree( root );

+    }

+

+

+    /**

+     * Load a persistent hashtable

+     *

+     * @param recman RecordManager used to store the persistent hashtable

+     * @param root_recid Record id of the root directory of the HTree

+     */

+    public static HTree load( RecordManager recman, long root_recid )

+        throws IOException

+    {

+        HTree tree;

+        HashDirectory root;

+

+        root = (HashDirectory) recman.fetch( root_recid );

+        root.setPersistenceContext( recman, root_recid );

+        tree = new HTree( root );

+        return tree;

+    }

+

+

+    /**

+     * Associates the specified value with the specified key.

+     *

+     * @param key key with which the specified value is to be assocated.

+     * @param value value to be associated with the specified key.

+     */

+    public synchronized void put(Object key, Object value)

+        throws IOException

+    {

+        _root.put(key, value);

+    }

+

+

+    /**

+     * Returns the value which is associated with the given key. Returns

+     * <code>null</code> if there is not association for this key.

+     *

+     * @param key key whose associated value is to be returned

+     */

+    public synchronized Object get(Object key)

+        throws IOException

+    {

+        return _root.get(key);

+    }

+

+

+    /**

+     * Remove the value which is associated with the given key.  If the

+     * key does not exist, this method simply ignores the operation.

+     *

+     * @param key key whose associated value is to be removed

+     */

+    public synchronized void remove(Object key)

+        throws IOException

+    {

+        _root.remove(key);

+    }

+

+

+    /**

+     * Returns an enumeration of the keys contained in this

+     */

+    public synchronized FastIterator keys()

+        throws IOException

+    {

+        return _root.keys();

+    }

+

+

+    /**

+     * Returns an enumeration of the values contained in this

+     */

+    public synchronized FastIterator values()

+        throws IOException

+    {

+        return _root.values();

+    }

+

+

+    /**

+     * Get the record identifier used to load this hashtable.

+     */

+    public long getRecid()

+    {

+        return _root.getRecid();

+    }

+

+}

+

diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/htree/HashBucket.java b/old_trunk/apacheds-jdbm/src/main/java/jdbm/htree/HashBucket.java
new file mode 100644
index 0000000..3b4a981
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/htree/HashBucket.java
@@ -0,0 +1,314 @@
+/**

+ * JDBM LICENSE v1.00

+ *

+ * Redistribution and use of this software and associated documentation

+ * ("Software"), with or without modification, are permitted provided

+ * that the following conditions are met:

+ *

+ * 1. Redistributions of source code must retain copyright

+ *    statements and notices.  Redistributions must also contain a

+ *    copy of this document.

+ *

+ * 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 name "JDBM" must not be used to endorse or promote

+ *    products derived from this Software without prior written

+ *    permission of Cees de Groot.  For written permission,

+ *    please contact cg@cdegroot.com.

+ *

+ * 4. Products derived from this Software may not be called "JDBM"

+ *    nor may "JDBM" appear in their names without prior written

+ *    permission of Cees de Groot.

+ *

+ * 5. Due credit should be given to the JDBM Project

+ *    (http://jdbm.sourceforge.net/).

+ *

+ * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS

+ * ``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

+ * CEES DE GROOT OR ANY 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.

+ *

+ * Copyright 2000 (C) Cees de Groot. All Rights Reserved.

+ * Contributions are Copyright (C) 2000 by their associated contributors.

+ *

+ */

+

+package jdbm.htree;

+

+import java.io.Externalizable;

+import java.io.IOException;

+import java.io.ObjectInput;

+import java.io.ObjectOutput;

+

+import java.util.ArrayList;

+

+/**

+ * A bucket is a placeholder for multiple (key, value) pairs.  Buckets

+ * are used to store collisions (same hash value) at all levels of an

+ * H*tree.

+ *

+ * There are two types of buckets: leaf and non-leaf.

+ *

+ * Non-leaf buckets are buckets which hold collisions which happen

+ * when the H*tree is not fully expanded.   Keys in a non-leaf buckets

+ * can have different hash codes.  Non-leaf buckets are limited to an

+ * arbitrary size.  When this limit is reached, the H*tree should create

+ * a new Directory page and distribute keys of the non-leaf buckets into

+ * the newly created Directory.

+ *

+ * A leaf bucket is a bucket which contains keys which all have

+ * the same <code>hashCode()</code>.  Leaf buckets stand at the

+ * bottom of an H*tree because the hashing algorithm cannot further

+ * discriminate between different keys based on their hash code.

+ *

+ *  @author <a href="mailto:boisvert@intalio.com">Alex Boisvert</a>

+ *  @version $Id: HashBucket.java,v 1.2 2005/06/25 23:12:32 doomdark Exp $

+ */

+final class HashBucket

+    extends HashNode

+    implements Externalizable

+{

+

+    final static long serialVersionUID = 1L;

+

+    /**

+     * The maximum number of elements (key, value) a non-leaf bucket

+     * can contain.

+     */

+    public static final int OVERFLOW_SIZE = 8;

+

+

+    /**

+     * Depth of this bucket.

+     */

+    private int _depth;

+

+

+    /**

+     * Keys in this bucket.  Keys are ordered to match their respective

+     * value in <code>_values</code>.

+     */

+    private ArrayList _keys;

+

+

+    /**

+     * Values in this bucket.  Values are ordered to match their respective

+     * key in <code>_keys</code>.

+     */

+    private ArrayList _values;

+

+

+    /**

+     * Public constructor for serialization.

+     */

+    public HashBucket() {

+        // empty

+    }

+

+

+    /**

+     * Construct a bucket with a given depth level.  Depth level is the

+     * number of <code>HashDirectory</code> above this bucket.

+     */

+    public HashBucket( int level )

+    {

+        if ( level > HashDirectory.MAX_DEPTH+1 ) {

+            throw new IllegalArgumentException(

+                            "Cannot create bucket with depth > MAX_DEPTH+1. "

+                            + "Depth=" + level );

+        }

+        _depth = level;

+        _keys = new ArrayList( OVERFLOW_SIZE );

+        _values = new ArrayList( OVERFLOW_SIZE );

+    }

+

+

+    /**

+     * Returns the number of elements contained in this bucket.

+     */

+    public int getElementCount()

+    {

+        return _keys.size();

+    }

+

+

+    /**

+     * Returns whether or not this bucket is a "leaf bucket".

+     */

+    public boolean isLeaf()

+    {

+        return ( _depth > HashDirectory.MAX_DEPTH );

+    }

+

+

+    /**

+     * Returns true if bucket can accept at least one more element.

+     */

+    public boolean hasRoom()

+    {

+        if ( isLeaf() ) {

+            return true;  // leaf buckets are never full

+        } else {

+            // non-leaf bucket

+            return ( _keys.size() < OVERFLOW_SIZE );

+        }

+    }

+

+

+    /**

+     * Add an element (key, value) to this bucket.  If an existing element

+     * has the same key, it is replaced silently.

+     *

+     * @return Object which was previously associated with the given key

+     *          or <code>null</code> if no association existed.

+     */

+    public Object addElement( Object key, Object value )

+    {

+        int existing = _keys.indexOf(key);

+        if ( existing != -1 ) {

+            // replace existing element

+            Object before = _values.get( existing );

+            _values.set( existing, value );

+            return before;

+        } else {

+            // add new (key, value) pair

+            _keys.add( key );

+            _values.add( value );

+            return null;

+        }

+    }

+

+

+    /**

+     * Remove an element, given a specific key.

+     *

+     * @param key Key of the element to remove

+     *

+     * @return Removed element value, or <code>null</code> if not found

+     */

+    public Object removeElement( Object key )

+    {

+        int existing = _keys.indexOf(key);

+        if ( existing != -1 ) {

+            Object obj = _values.get( existing );

+            _keys.remove( existing );

+            _values.remove( existing );

+            return obj;

+        } else {

+            // not found

+            return null;

+        }

+    }

+

+

+    /**

+     * Returns the value associated with a given key.  If the given key

+     * is not found in this bucket, returns <code>null</code>.

+     */

+    public Object getValue( Object key )

+    {

+        int existing = _keys.indexOf(key);

+        if ( existing != -1 ) {

+            return _values.get( existing );

+        } else {

+            // key not found

+            return null;

+        }

+    }

+

+

+    /**

+     * Obtain keys contained in this buckets.  Keys are ordered to match

+     * their values, which be be obtained by calling <code>getValues()</code>.

+     *

+     * As an optimization, the Vector returned is the instance member

+     * of this class.  Please don't modify outside the scope of this class.

+     */

+    ArrayList getKeys()

+    {

+        return this._keys;

+    }

+

+

+    /**

+     * Obtain values contained in this buckets.  Values are ordered to match

+     * their keys, which be be obtained by calling <code>getKeys()</code>.

+     *

+     * As an optimization, the Vector returned is the instance member

+     * of this class.  Please don't modify outside the scope of this class.

+     */

+    ArrayList getValues()

+    {

+        return this._values;

+    }

+

+

+    /**

+     * Implement Externalizable interface.

+     */

+    public void writeExternal( ObjectOutput out )

+        throws IOException

+    {

+        out.writeInt( _depth );

+

+        int entries = _keys.size();

+        out.writeInt( entries );

+

+        // write keys

+        for (int i=0; i<entries; i++) {

+            out.writeObject( _keys.get( i ) );

+        }

+        // write values

+        for (int i=0; i<entries; i++) {

+            out.writeObject( _values.get( i ) );

+        }

+    }

+

+

+    /**

+     * Implement Externalizable interface.

+     */

+    public void readExternal(ObjectInput in)

+    throws IOException, ClassNotFoundException {

+        _depth = in.readInt();

+

+        int entries = in.readInt();

+

+        // prepare array lists

+        int size = Math.max( entries, OVERFLOW_SIZE );

+        _keys = new ArrayList( size );

+        _values = new ArrayList( size );

+

+        // read keys

+        for ( int i=0; i<entries; i++ ) {

+            _keys.add( in.readObject() );

+        }

+        // read values

+        for ( int i=0; i<entries; i++ ) {

+            _values.add( in.readObject() );

+        }

+    }

+

+    public String toString() {

+        StringBuffer buf = new StringBuffer();

+        buf.append("HashBucket {depth=");

+        buf.append(_depth);

+        buf.append(", keys=");

+        buf.append(_keys);

+        buf.append(", values=");

+        buf.append(_values);

+        buf.append("}");

+        return buf.toString();

+    }

+}

diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/htree/HashDirectory.java b/old_trunk/apacheds-jdbm/src/main/java/jdbm/htree/HashDirectory.java
new file mode 100644
index 0000000..99c176d
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/htree/HashDirectory.java
@@ -0,0 +1,548 @@
+/**

+ * JDBM LICENSE v1.00

+ *

+ * Redistribution and use of this software and associated documentation

+ * ("Software"), with or without modification, are permitted provided

+ * that the following conditions are met:

+ *

+ * 1. Redistributions of source code must retain copyright

+ *    statements and notices.  Redistributions must also contain a

+ *    copy of this document.

+ *

+ * 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 name "JDBM" must not be used to endorse or promote

+ *    products derived from this Software without prior written

+ *    permission of Cees de Groot.  For written permission,

+ *    please contact cg@cdegroot.com.

+ *

+ * 4. Products derived from this Software may not be called "JDBM"

+ *    nor may "JDBM" appear in their names without prior written

+ *    permission of Cees de Groot.

+ *

+ * 5. Due credit should be given to the JDBM Project

+ *    (http://jdbm.sourceforge.net/).

+ *

+ * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS

+ * ``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

+ * CEES DE GROOT OR ANY 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.

+ *

+ * Copyright 2000 (C) Cees de Groot. All Rights Reserved.

+ * Contributions are Copyright (C) 2000 by their associated contributors.

+ *

+ */

+

+package jdbm.htree;

+

+import jdbm.RecordManager;

+

+import jdbm.helper.FastIterator;

+import jdbm.helper.IterationException;

+

+import java.io.Externalizable;

+import java.io.IOException;

+import java.io.ObjectInput;

+import java.io.ObjectOutput;

+

+import java.util.ArrayList;

+import java.util.Iterator;

+

+/**

+ *  Hashtable directory page.

+ *

+ *  @author <a href="mailto:boisvert@exoffice.com">Alex Boisvert</a>

+ *  @version $Id: HashDirectory.java,v 1.5 2005/06/25 23:12:32 doomdark Exp $

+ */

+final class HashDirectory

+    extends HashNode

+    implements Externalizable

+{

+

+    static final long serialVersionUID = 1L;

+

+

+    /**

+     * Maximum number of children in a directory.

+     *

+     * (Must be a power of 2 -- if you update this value, you must also

+     *  update BIT_SIZE and MAX_DEPTH.)

+     */

+    static final int MAX_CHILDREN = 256;

+

+

+    /**

+     * Number of significant bits per directory level.

+     */

+    static final int BIT_SIZE = 8; // log2(256) = 8

+

+

+    /**

+     * Maximum number of levels (zero-based)

+     *

+     * (4 * 8 bits = 32 bits, which is the size of an "int", and as

+     *  you know, hashcodes in Java are "ints")

+     */

+    static final int MAX_DEPTH = 3; // 4 levels

+

+

+    /**

+     * Record ids of children pages.

+     */

+    private long[] _children;

+

+

+    /**

+     * Depth of this directory page, zero-based

+     */

+    private byte _depth;

+

+

+    /**

+     * PageManager used to persist changes in directory and buckets

+     */

+    private transient RecordManager _recman;

+

+

+    /**

+     * This directory's record ID in the PageManager.  (transient)

+     */

+    private transient long _recid;

+

+

+    /**

+     * Public constructor used by serialization

+     */

+    public HashDirectory() {

+        // empty

+    }

+

+    /**

+     * Construct a HashDirectory

+     *

+     * @param depth Depth of this directory page.

+     */

+    HashDirectory(byte depth) {

+        _depth = depth;

+        _children = new long[MAX_CHILDREN];

+    }

+

+

+    /**

+     * Sets persistence context.  This method must be called before any

+     * persistence-related operation.

+     *

+     * @param recman RecordManager which stores this directory

+     * @param recid Record id of this directory.

+     */

+    void setPersistenceContext( RecordManager recman, long recid )

+    {

+        this._recman = recman;

+        this._recid = recid;

+    }

+

+

+    /**

+     * Get the record identifier used to load this hashtable.

+     */

+    long getRecid() {

+        return _recid;

+    }

+

+

+    /**

+     * Returns whether or not this directory is empty.  A directory

+     * is empty when it no longer contains buckets or sub-directories.

+     */

+    boolean isEmpty() {

+        for (int i=0; i<_children.length; i++) {

+            if (_children[i] != 0) {

+                return false;

+            }

+        }

+        return true;

+    }

+

+    /**

+     * Returns the value which is associated with the given key. Returns

+     * <code>null</code> if there is not association for this key.

+     *

+     * @param key key whose associated value is to be returned

+     */

+    Object get(Object key)

+        throws IOException

+    {

+        int hash = hashCode( key );

+        long child_recid = _children[ hash ];

+        if ( child_recid == 0 ) {

+            // not bucket/page --> not found

+            return null;

+        } else {

+            HashNode node = (HashNode) _recman.fetch( child_recid );

+            // System.out.println("HashDirectory.get() child is : "+node);

+

+            if ( node instanceof HashDirectory ) {

+                // recurse into next directory level

+                HashDirectory dir = (HashDirectory) node;

+                dir.setPersistenceContext( _recman, child_recid );

+                return dir.get( key );

+            } else {

+                // node is a bucket

+                HashBucket bucket = (HashBucket) node;

+                return bucket.getValue( key );

+            }

+        }

+    }

+

+

+    /**

+     * Associates the specified value with the specified key.

+     *

+     * @param key key with which the specified value is to be assocated.

+     * @param value value to be associated with the specified key.

+     * @return object which was previously associated with the given key,

+     *          or <code>null</code> if no association existed.

+     */

+    Object put(Object key, Object value)

+    throws IOException {

+        if (value == null) {

+            return remove(key);

+        }

+        int hash = hashCode(key);

+        long child_recid = _children[hash];

+        if (child_recid == 0) {

+            // no bucket/page here yet, let's create a bucket

+            HashBucket bucket = new HashBucket(_depth+1);

+

+            // insert (key,value) pair in bucket

+            Object existing = bucket.addElement(key, value);

+

+            long b_recid = _recman.insert(bucket);

+            _children[hash] = b_recid;

+

+            _recman.update(_recid, this);

+

+            // System.out.println("Added: "+bucket);

+            return existing;

+        } else {

+            HashNode node = (HashNode) _recman.fetch( child_recid );

+

+            if ( node instanceof HashDirectory ) {

+                // recursive insert in next directory level

+                HashDirectory dir = (HashDirectory) node;

+                dir.setPersistenceContext( _recman, child_recid );

+                return dir.put( key, value );

+            } else {

+                // node is a bucket

+                HashBucket bucket = (HashBucket)node;

+                if (bucket.hasRoom()) {

+                    Object existing = bucket.addElement(key, value);

+                    _recman.update(child_recid, bucket);

+                    // System.out.println("Added: "+bucket);

+                    return existing;

+                } else {

+                    // overflow, so create a new directory

+                    if (_depth == MAX_DEPTH) {

+                        throw new RuntimeException( "Cannot create deeper directory. "

+                                                    + "Depth=" + _depth );

+                    }

+                    HashDirectory dir = new HashDirectory( (byte) (_depth+1) );

+                    long dir_recid = _recman.insert( dir );

+                    dir.setPersistenceContext( _recman, dir_recid );

+

+                    _children[hash] = dir_recid;

+                    _recman.update( _recid, this );

+

+                    // discard overflown bucket

+                    _recman.delete( child_recid );

+

+                    // migrate existing bucket elements

+                    ArrayList keys = bucket.getKeys();

+                    ArrayList values = bucket.getValues();

+                    int entries = keys.size();

+                    for ( int i=0; i<entries; i++ ) {

+                        dir.put( keys.get( i ), values.get( i ) );

+                    }

+

+                    // (finally!) insert new element

+                    return dir.put( key, value );

+                }

+            }

+        }

+    }

+

+

+    /**

+     * Remove the value which is associated with the given key.  If the

+     * key does not exist, this method simply ignores the operation.

+     *

+     * @param key key whose associated value is to be removed

+     * @return object which was associated with the given key, or

+     *          <code>null</code> if no association existed with given key.

+     */

+    Object remove(Object key) throws IOException {

+        int hash = hashCode(key);

+        long child_recid = _children[hash];

+        if (child_recid == 0) {

+            // not bucket/page --> not found

+            return null;

+        } else {

+            HashNode node = (HashNode) _recman.fetch( child_recid );

+            // System.out.println("HashDirectory.remove() child is : "+node);

+

+            if (node instanceof HashDirectory) {

+                // recurse into next directory level

+                HashDirectory dir = (HashDirectory)node;

+                dir.setPersistenceContext( _recman, child_recid );

+                Object existing = dir.remove(key);

+                if (existing != null) {

+                    if (dir.isEmpty()) {

+                        // delete empty directory

+                        _recman.delete(child_recid);

+                        _children[hash] = 0;

+                        _recman.update(_recid, this);

+                    }

+                }

+                return existing;

+            } else {

+                // node is a bucket

+                HashBucket bucket = (HashBucket)node;

+                Object existing = bucket.removeElement(key);

+                if (existing != null) {

+                    if (bucket.getElementCount() >= 1) {

+                        _recman.update(child_recid, bucket);

+                    } else {

+                        // delete bucket, it's empty

+                        _recman.delete(child_recid);

+                        _children[hash] = 0;

+                        _recman.update(_recid, this);

+                    }

+                }

+                return existing;

+            }

+        }

+    }

+

+    /**

+     * Calculates the hashcode of a key, based on the current directory

+     * depth.

+     */

+    private int hashCode(Object key) {

+        int hashMask = hashMask();

+        int hash = key.hashCode();

+        hash = hash & hashMask;

+        hash = hash >>> ((MAX_DEPTH - _depth) * BIT_SIZE);

+        hash = hash % MAX_CHILDREN;

+        /*

+        System.out.println("HashDirectory.hashCode() is: 0x"

+                           +Integer.toHexString(hash)

+                           +" for object hashCode() 0x"

+                           +Integer.toHexString(key.hashCode()));

+        */

+        return hash;

+    }

+

+    /**

+     * Calculates the hashmask of this directory.  The hashmask is the

+     * bit mask applied to a hashcode to retain only bits that are

+     * relevant to this directory level.

+     */

+    int hashMask() {

+        int bits = MAX_CHILDREN-1;

+        int hashMask = bits << ((MAX_DEPTH - _depth) * BIT_SIZE);

+        /*

+        System.out.println("HashDirectory.hashMask() is: 0x"

+                           +Integer.toHexString(hashMask));

+        */

+        return hashMask;

+    }

+

+    /**

+     * Returns an enumeration of the keys contained in this

+     */

+    FastIterator keys()

+        throws IOException

+    {

+        return new HDIterator( true );

+    }

+

+    /**

+     * Returns an enumeration of the values contained in this

+     */

+    FastIterator values()

+        throws IOException

+    {

+        return new HDIterator( false );

+    }

+

+

+    /**

+     * Implement Externalizable interface

+     */

+    public void writeExternal(ObjectOutput out)

+    throws IOException {

+        out.writeByte(_depth);

+        out.writeObject(_children);

+    }

+

+

+    /**

+     * Implement Externalizable interface

+     */

+    public synchronized void readExternal(ObjectInput in)

+    throws IOException, ClassNotFoundException {

+        _depth = in.readByte();

+        _children = (long[])in.readObject();

+    }

+

+

+    ////////////////////////////////////////////////////////////////////////

+    // INNER CLASS

+    ////////////////////////////////////////////////////////////////////////

+

+    /**

+     * Utility class to enumerate keys/values in a HTree

+     */

+    public class HDIterator

+        extends FastIterator

+    {

+

+        /**

+         * True if we're iterating on keys, False if enumerating on values.

+         */

+        private boolean _iterateKeys;

+

+        /**

+         * Stacks of directories & last enumerated child position

+         */

+        private ArrayList _dirStack;

+        private ArrayList _childStack;

+

+        /**

+         * Current HashDirectory in the hierarchy

+         */

+        private HashDirectory _dir;

+

+        /**

+         * Current child position

+         */

+        private int _child;

+

+        /**

+         * Current bucket iterator

+         */

+        private Iterator _iter;

+

+

+        /**

+         * Construct an iterator on this directory.

+         *

+         * @param iterateKeys True if iteration supplies keys, False

+         *                  if iterateKeys supplies values.

+         */

+        HDIterator( boolean iterateKeys )

+            throws IOException

+        {

+            _dirStack = new ArrayList();

+            _childStack = new ArrayList();

+            _dir = HashDirectory.this;

+            _child = -1;

+            _iterateKeys = iterateKeys;

+

+            prepareNext();

+        }

+

+

+        /**

+         * Returns the next object.

+         */

+        public Object next()

+        {   

+            Object next = null;      

+            if( _iter != null && _iter.hasNext() ) {

+              next = _iter.next();

+            } else {

+              try {

+                prepareNext();

+              } catch ( IOException except ) {

+                throw new IterationException( except );

+              }

+              if ( _iter != null && _iter.hasNext() ) {

+                return next();

+              }

+            }

+            return next;         

+        }

+

+

+        /**

+         * Prepare internal state so we can answer <code>hasMoreElements</code>

+         *

+         * Actually, this code prepares an Enumeration on the next

+         * Bucket to enumerate.   If no following bucket is found,

+         * the next Enumeration is set to <code>null</code>.

+         */

+        private void prepareNext() throws IOException {

+            long child_recid = 0;

+

+            // find next bucket/directory to enumerate

+            do {

+                _child++;

+                if (_child >= MAX_CHILDREN) {

+

+                    if (_dirStack.isEmpty()) {

+                        // no more directory in the stack, we're finished

+                        return;

+                    }

+

+                    // try next page

+                    _dir = (HashDirectory) _dirStack.remove( _dirStack.size()-1 );

+                    _child = ( (Integer) _childStack.remove( _childStack.size()-1 ) ).intValue();

+                    continue;

+                }

+                child_recid = _dir._children[_child];

+            } while (child_recid == 0);

+

+            if (child_recid == 0) {

+                throw new Error("child_recid cannot be 0");

+            }

+

+            HashNode node = (HashNode) _recman.fetch( child_recid );

+            // System.out.println("HDEnumeration.get() child is : "+node);

+ 

+            if ( node instanceof HashDirectory ) {

+                // save current position

+                _dirStack.add( _dir );

+                _childStack.add( new Integer( _child ) );

+

+                _dir = (HashDirectory)node;

+                _child = -1;

+

+                // recurse into

+                _dir.setPersistenceContext( _recman, child_recid );

+                prepareNext();

+            } else {

+                // node is a bucket

+                HashBucket bucket = (HashBucket)node;

+                if ( _iterateKeys ) {

+                    _iter = bucket.getKeys().iterator();

+                } else {

+                    _iter = bucket.getValues().iterator();

+                }

+            }

+        }

+    }

+

+}

+

diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/htree/HashNode.java b/old_trunk/apacheds-jdbm/src/main/java/jdbm/htree/HashNode.java
new file mode 100644
index 0000000..5e95845
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/htree/HashNode.java
Binary files differ
diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/htree/package.html b/old_trunk/apacheds-jdbm/src/main/java/jdbm/htree/package.html
new file mode 100644
index 0000000..47cf975
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/htree/package.html
@@ -0,0 +1,12 @@
+<!-- $Id: package.html,v 1.1 2002/05/31 06:33:20 boisvert Exp $ -->

+<html>

+  <body>

+    <p>HTree (scalable persistent hashtable) data structure implementation.</p>

+

+    <dl>

+      <dt><b>Version: </b></dt><dd>$Revision: 1.1 $ $Date: 2002/05/31 06:33:20 $</dd>

+      <dt><b>Author: </b></dt><dd><a href="mailto:boisvert@intalio.com">Alex Boisvert</a></dd>

+    </dl>

+

+  </body>

+</html>

diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/package.html b/old_trunk/apacheds-jdbm/src/main/java/jdbm/package.html
new file mode 100644
index 0000000..7ee730d
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/package.html
@@ -0,0 +1,21 @@
+<!-- $Id: package.html,v 1.1 2001/05/19 16:01:32 boisvert Exp $ -->
+<html>
+  <body>
+    <p>Simplified public API corresponding to GDBM APIs.</p>
+    
+    <h1><b>JDBM</b></h1>
+
+    <p>JDBM is a simple transactional persistent engine for Java.
+       It aims to be for Java what GDBM is for C/C++, Perl, Python, etc: 
+       a fast, simple persistence engine. </p>
+
+    <p>You can use it to store a mix of hashtables and blobs, and all updates are 
+       done in a transactionally safe manner.</p>
+
+    <dl>
+      <dt><b>Version: </b></dt><dd>$Revision: 1.1 $ $Date: 2001/05/19 16:01:32 $</dd>
+      <dt><b>Author: </b></dt><dd><a href="mailto:boisvert@intalio.com">Alex Boisvert</a></dd>
+    </dl>
+
+  </body>
+</html>
diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/BaseRecordManager.java b/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/BaseRecordManager.java
new file mode 100644
index 0000000..db18340
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/BaseRecordManager.java
@@ -0,0 +1,485 @@
+/**

+ * JDBM LICENSE v1.00

+ *

+ * Redistribution and use of this software and associated documentation

+ * ("Software"), with or without modification, are permitted provided

+ * that the following conditions are met:

+ *

+ * 1. Redistributions of source code must retain copyright

+ *    statements and notices.  Redistributions must also contain a

+ *    copy of this document.

+ *

+ * 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 name "JDBM" must not be used to endorse or promote

+ *    products derived from this Software without prior written

+ *    permission of Cees de Groot.  For written permission,

+ *    please contact cg@cdegroot.com.

+ *

+ * 4. Products derived from this Software may not be called "JDBM"

+ *    nor may "JDBM" appear in their names without prior written

+ *    permission of Cees de Groot.

+ *

+ * 5. Due credit should be given to the JDBM Project

+ *    (http://jdbm.sourceforge.net/).

+ *

+ * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS

+ * ``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

+ * CEES DE GROOT OR ANY 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.

+ *

+ * Copyright 2000 (C) Cees de Groot. All Rights Reserved.

+ * Copyright 2000-2001 (C) Alex Boisvert. All Rights Reserved.

+ * Contributions are Copyright (C) 2000 by their associated contributors.

+ *

+ * $Id: BaseRecordManager.java,v 1.8 2005/06/25 23:12:32 doomdark Exp $

+ */

+

+package jdbm.recman;

+

+import java.io.IOException;

+

+import java.util.HashMap;

+import java.util.Map;

+

+import jdbm.RecordManager;

+import jdbm.helper.Serializer;

+import jdbm.helper.DefaultSerializer;

+

+/**

+ *  This class manages records, which are uninterpreted blobs of data. The

+ *  set of operations is simple and straightforward: you communicate with

+ *  the class using long "rowids" and byte[] data blocks. Rowids are returned

+ *  on inserts and you can stash them away someplace safe to be able to get

+ *  back to them. Data blocks can be as long as you wish, and may have

+ *  lengths different from the original when updating.

+ *  <p>

+ *  Operations are synchronized, so that only one of them will happen

+ *  concurrently even if you hammer away from multiple threads. Operations

+ *  are made atomic by keeping a transaction log which is recovered after

+ *  a crash, so the operations specified by this interface all have ACID

+ *  properties.

+ *  <p>

+ *  You identify a file by just the name. The package attaches <tt>.db</tt>

+ *  for the database file, and <tt>.lg</tt> for the transaction log. The

+ *  transaction log is synchronized regularly and then restarted, so don't

+ *  worry if you see the size going up and down.

+ *

+ * @author <a href="mailto:boisvert@intalio.com">Alex Boisvert</a>

+ * @author <a href="cg@cdegroot.com">Cees de Groot</a>

+ * @version $Id: BaseRecordManager.java,v 1.8 2005/06/25 23:12:32 doomdark Exp $

+ */

+public final class BaseRecordManager

+    implements RecordManager

+{

+

+    /**

+     * Underlying record file.

+     */

+    private RecordFile _file;

+

+

+    /**

+     * Physical row identifier manager.

+     */

+    private PhysicalRowIdManager _physMgr;

+

+

+    /**

+     * Logigal to Physical row identifier manager.

+     */

+    private LogicalRowIdManager _logMgr;

+

+

+    /**

+     * Page manager.

+     */

+    private PageManager _pageman;

+

+

+    /**

+     * Reserved slot for name directory.

+     */

+    public static final int NAME_DIRECTORY_ROOT = 0;

+

+

+    /**

+     * Static debugging flag

+     */

+    public static final boolean DEBUG = false;

+

+    

+    /**

+     * Directory of named JDBMHashtables.  This directory is a persistent

+     * directory, stored as a Hashtable.  It can be retrived by using

+     * the NAME_DIRECTORY_ROOT.

+     */

+    private Map _nameDirectory;

+

+

+    /**

+     *  Creates a record manager for the indicated file

+     *

+     *  @throws IOException when the file cannot be opened or is not

+     *          a valid file content-wise.

+     */

+    public BaseRecordManager( String filename )

+        throws IOException

+    {

+        _file = new RecordFile( filename );

+        _pageman = new PageManager( _file );

+        _physMgr = new PhysicalRowIdManager( _file, _pageman );

+        _logMgr = new LogicalRowIdManager( _file, _pageman );

+    }

+

+

+    /**

+     *  Get the underlying Transaction Manager

+     */

+    public synchronized TransactionManager getTransactionManager()

+    {

+        checkIfClosed();

+

+        return _file.txnMgr;

+    }

+

+

+    /**

+     *  Switches off transactioning for the record manager. This means

+     *  that a) a transaction log is not kept, and b) writes aren't

+     *  synch'ed after every update. This is useful when batch inserting

+     *  into a new database.

+     *  <p>

+     *  Only call this method directly after opening the file, otherwise

+     *  the results will be undefined.

+     */

+    public synchronized void disableTransactions()

+    {

+        checkIfClosed();

+

+        _file.disableTransactions();

+    }

+

+    

+    /**

+     *  Closes the record manager.

+     *

+     *  @throws IOException when one of the underlying I/O operations fails.

+     */

+    public synchronized void close()

+        throws IOException

+    {

+        checkIfClosed();

+

+        _pageman.close();

+        _pageman = null;

+

+        _file.close();

+        _file = null;

+    }

+

+

+    /**

+     *  Inserts a new record using standard java object serialization.

+     *

+     *  @param obj the object for the new record.

+     *  @return the rowid for the new record.

+     *  @throws IOException when one of the underlying I/O operations fails.

+     */

+    public long insert( Object obj )

+        throws IOException

+    {

+        return insert( obj, DefaultSerializer.INSTANCE );

+    }

+

+    

+    /**

+     *  Inserts a new record using a custom serializer.

+     *

+     *  @param obj the object for the new record.

+     *  @param serializer a custom serializer

+     *  @return the rowid for the new record.

+     *  @throws IOException when one of the underlying I/O operations fails.

+     */

+    public synchronized long insert( Object obj, Serializer serializer )

+        throws IOException

+    {

+        byte[]    data;

+        long      recid;

+        Location  physRowId;

+        

+        checkIfClosed();

+

+        data = serializer.serialize( obj );

+        physRowId = _physMgr.insert( data, 0, data.length );

+        recid = _logMgr.insert( physRowId ).toLong();

+        if ( DEBUG ) {

+            System.out.println( "BaseRecordManager.insert() recid " + recid + " length " + data.length ) ;

+        }

+        return recid;

+    }

+

+    /**

+     *  Deletes a record.

+     *

+     *  @param recid the rowid for the record that should be deleted.

+     *  @throws IOException when one of the underlying I/O operations fails.

+     */

+    public synchronized void delete( long recid )

+        throws IOException

+    {

+        checkIfClosed();

+        if ( recid <= 0 ) {

+            throw new IllegalArgumentException( "Argument 'recid' is invalid: "

+                                                + recid );

+        }

+

+        if ( DEBUG ) {

+            System.out.println( "BaseRecordManager.delete() recid " + recid ) ;

+        }

+

+        Location logRowId = new Location( recid );

+        Location physRowId = _logMgr.fetch( logRowId );

+        _physMgr.delete( physRowId );

+        _logMgr.delete( logRowId );

+    }

+

+

+    /**

+     *  Updates a record using standard java object serialization.

+     *

+     *  @param recid the recid for the record that is to be updated.

+     *  @param obj the new object for the record.

+     *  @throws IOException when one of the underlying I/O operations fails.

+     */

+    public void update( long recid, Object obj )

+        throws IOException

+    {

+        update( recid, obj, DefaultSerializer.INSTANCE );

+    }

+

+    

+    /**

+     *  Updates a record using a custom serializer.

+     *

+     *  @param recid the recid for the record that is to be updated.

+     *  @param obj the new object for the record.

+     *  @param serializer a custom serializer

+     *  @throws IOException when one of the underlying I/O operations fails.

+     */

+    public synchronized void update( long recid, Object obj, Serializer serializer )

+        throws IOException

+    {

+        checkIfClosed();

+        if ( recid <= 0 ) {

+            throw new IllegalArgumentException( "Argument 'recid' is invalid: "

+                                                + recid );

+        }

+

+        Location logRecid = new Location( recid );

+        Location physRecid = _logMgr.fetch( logRecid );

+        

+        byte[] data = serializer.serialize( obj );

+        if ( DEBUG ) {

+            System.out.println( "BaseRecordManager.update() recid " + recid + " length " + data.length ) ;

+        }

+        

+        Location newRecid = _physMgr.update( physRecid, data, 0, data.length );

+        if ( ! newRecid.equals( physRecid ) ) {

+            _logMgr.update( logRecid, newRecid );

+        }

+    }

+

+

+    /**

+     *  Fetches a record using standard java object serialization.

+     *

+     *  @param recid the recid for the record that must be fetched.

+     *  @return the object contained in the record.

+     *  @throws IOException when one of the underlying I/O operations fails.

+     */

+    public Object fetch( long recid )

+        throws IOException

+    {

+        return fetch( recid, DefaultSerializer.INSTANCE );

+    }

+

+

+    /**

+     *  Fetches a record using a custom serializer.

+     *

+     *  @param recid the recid for the record that must be fetched.

+     *  @param serializer a custom serializer

+     *  @return the object contained in the record.

+     *  @throws IOException when one of the underlying I/O operations fails.

+     */

+    public synchronized Object fetch( long recid, Serializer serializer )

+        throws IOException

+    {

+        byte[] data;

+

+        checkIfClosed();

+        if ( recid <= 0 ) {

+            throw new IllegalArgumentException( "Argument 'recid' is invalid: "

+                                                + recid );

+        }

+        data = _physMgr.fetch( _logMgr.fetch( new Location( recid ) ) );

+        if ( DEBUG ) {

+            System.out.println( "BaseRecordManager.fetch() recid " + recid + " length " + data.length ) ;

+        }

+        return serializer.deserialize( data );

+    }

+

+

+    /**

+     *  Returns the number of slots available for "root" rowids. These slots

+     *  can be used to store special rowids, like rowids that point to

+     *  other rowids. Root rowids are useful for bootstrapping access to

+     *  a set of data.

+     */

+    public int getRootCount()

+    {

+        return FileHeader.NROOTS;

+    }

+

+    /**

+     *  Returns the indicated root rowid.

+     *

+     *  @see #getRootCount

+     */

+    public synchronized long getRoot( int id )

+        throws IOException

+    {

+        checkIfClosed();

+

+        return _pageman.getFileHeader().getRoot( id );

+    }

+

+

+    /**

+     *  Sets the indicated root rowid.

+     *

+     *  @see #getRootCount

+     */

+    public synchronized void setRoot( int id, long rowid )

+        throws IOException

+    {

+        checkIfClosed();

+

+        _pageman.getFileHeader().setRoot( id, rowid );

+    }

+

+

+    /**

+     * Obtain the record id of a named object. Returns 0 if named object

+     * doesn't exist.

+     */

+    public long getNamedObject( String name )

+        throws IOException

+    {

+        checkIfClosed();

+

+        Map nameDirectory = getNameDirectory();

+        Long recid = (Long) nameDirectory.get( name );

+        if ( recid == null ) {

+            return 0;

+        }

+        return recid.longValue();

+    }

+

+    /**

+     * Set the record id of a named object.

+     */

+    public void setNamedObject( String name, long recid )

+        throws IOException

+    {

+        checkIfClosed();

+

+        Map nameDirectory = getNameDirectory();

+        if ( recid == 0 ) {

+            // remove from hashtable

+            nameDirectory.remove( name );

+        } else {

+            nameDirectory.put( name, new Long( recid ) );

+        }

+        saveNameDirectory( nameDirectory );

+    }

+

+

+    /**

+     * Commit (make persistent) all changes since beginning of transaction.

+     */

+    public synchronized void commit()

+        throws IOException

+    {

+        checkIfClosed();

+

+        _pageman.commit();

+    }

+

+

+    /**

+     * Rollback (cancel) all changes since beginning of transaction.

+     */

+    public synchronized void rollback()

+        throws IOException

+    {

+        checkIfClosed();

+

+        _pageman.rollback();

+    }

+

+

+    /**

+     * Load name directory

+     */

+    private Map getNameDirectory()

+        throws IOException

+    {

+        // retrieve directory of named hashtable

+        long nameDirectory_recid = getRoot( NAME_DIRECTORY_ROOT );

+        if ( nameDirectory_recid == 0 ) {

+            _nameDirectory = new HashMap();

+            nameDirectory_recid = insert( _nameDirectory );

+            setRoot( NAME_DIRECTORY_ROOT, nameDirectory_recid );

+        } else {

+            _nameDirectory = (Map) fetch( nameDirectory_recid );

+        }

+        return _nameDirectory;

+    }

+

+

+    private void saveNameDirectory( Map directory )

+        throws IOException

+    {

+        long recid = getRoot( NAME_DIRECTORY_ROOT );

+        if ( recid == 0 ) {

+            throw new IOException( "Name directory must exist" );

+        }

+        update( recid, _nameDirectory );

+    }

+

+

+    /**

+     * Check if RecordManager has been closed.  If so, throw an

+     * IllegalStateException.

+     */

+    private void checkIfClosed()

+        throws IllegalStateException

+    {

+        if ( _file == null ) {

+            throw new IllegalStateException( "RecordManager has been closed" );

+        }

+    }

+}

diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/BlockIo.java b/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/BlockIo.java
new file mode 100644
index 0000000..990436e
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/BlockIo.java
@@ -0,0 +1,307 @@
+/**
+ * JDBM LICENSE v1.00
+ *
+ * Redistribution and use of this software and associated documentation
+ * ("Software"), with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain copyright
+ *    statements and notices.  Redistributions must also contain a
+ *    copy of this document.
+ *
+ * 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 name "JDBM" must not be used to endorse or promote
+ *    products derived from this Software without prior written
+ *    permission of Cees de Groot.  For written permission,
+ *    please contact cg@cdegroot.com.
+ *
+ * 4. Products derived from this Software may not be called "JDBM"
+ *    nor may "JDBM" appear in their names without prior written
+ *    permission of Cees de Groot.
+ *
+ * 5. Due credit should be given to the JDBM Project
+ *    (http://jdbm.sourceforge.net/).
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS
+ * ``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
+ * CEES DE GROOT OR ANY 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.
+ *
+ * Copyright 2000 (C) Cees de Groot. All Rights Reserved.
+ * Contributions are Copyright (C) 2000 by their associated contributors.
+ *
+ * $Id: BlockIo.java,v 1.2 2002/08/06 05:18:36 boisvert Exp $
+ */
+
+package jdbm.recman;
+
+import java.io.*;
+
+/**
+ *  This class wraps a page-sized byte array and provides methods
+ *  to read and write data to and from it. The readers and writers
+ *  are just the ones that the rest of the toolkit needs, nothing else.
+ *  Values written are compatible with java.io routines.
+ *
+ *  @see java.io.DataInput
+ *  @see java.io.DataOutput
+ */
+public final class BlockIo implements java.io.Externalizable {
+
+    public final static long serialVersionUID = 2L;
+
+    private long blockId;
+
+    private transient byte[] data; // work area
+    private transient BlockView view = null;
+    private transient boolean dirty = false;
+    private transient int transactionCount = 0;
+
+    /**
+     * Default constructor for serialization
+     */
+    public BlockIo() {
+        // empty
+    }
+
+    /**
+     *  Constructs a new BlockIo instance working on the indicated
+     *  buffer.
+     */
+    BlockIo(long blockId, byte[] data) {
+        // removeme for production version
+        if (blockId > 10000000000L)
+            throw new Error("bogus block id " + blockId);
+        this.blockId = blockId;
+        this.data = data;
+    }
+
+    /**
+     *  Returns the underlying array
+     */
+    byte[] getData() {
+        return data;
+    }
+
+    /**
+     *  Sets the block number. Should only be called by RecordFile.
+     */
+    void setBlockId(long id) {
+        if (isInTransaction())
+            throw new Error("BlockId assigned for transaction block");
+        // removeme for production version
+        if (id > 10000000000L)
+            throw new Error("bogus block id " + id);
+        blockId = id;
+    }
+
+    /**
+     *  Returns the block number.
+     */
+    long getBlockId() {
+        return blockId;
+    }
+
+    /**
+     *  Returns the current view of the block.
+     */
+    public BlockView getView() {
+        return view;
+    }
+
+    /**
+     *  Sets the current view of the block.
+     */
+    public void setView(BlockView view) {
+        this.view = view;
+    }
+
+    /**
+     *  Sets the dirty flag
+     */
+    void setDirty() {
+        dirty = true;
+    }
+
+    /**
+     *  Clears the dirty flag
+     */
+    void setClean() {
+        dirty = false;
+    }
+
+    /**
+     *  Returns true if the dirty flag is set.
+     */
+    boolean isDirty() {
+        return dirty;
+    }
+
+    /**
+     *  Returns true if the block is still dirty with respect to the
+     *  transaction log.
+     */
+    boolean isInTransaction() {
+        return transactionCount != 0;
+    }
+
+    /**
+     *  Increments transaction count for this block, to signal that this
+     *  block is in the log but not yet in the data file. The method also
+     *  takes a snapshot so that the data may be modified in new transactions.
+     */
+    synchronized void incrementTransactionCount() {
+        transactionCount++;
+        // @fixme(alex)
+        setClean();
+    }
+
+    /**
+     *  Decrements transaction count for this block, to signal that this
+     *  block has been written from the log to the data file.
+     */
+    synchronized void decrementTransactionCount() {
+        transactionCount--;
+        if (transactionCount < 0)
+            throw new Error("transaction count on block "
+                            + getBlockId() + " below zero!");
+
+    }
+
+    /**
+     *  Reads a byte from the indicated position
+     */
+    public byte readByte(int pos) {
+        return data[pos];
+    }
+
+    /**
+     *  Writes a byte to the indicated position
+     */
+    public void writeByte(int pos, byte value) {
+        data[pos] = value;
+        setDirty();
+    }
+
+    /**
+     *  Reads a short from the indicated position
+     */
+    public short readShort(int pos) {
+        return (short)
+            (((short) (data[pos+0] & 0xff) << 8) |
+             ((short) (data[pos+1] & 0xff) << 0));
+    }
+
+    /**
+     *  Writes a short to the indicated position
+     */
+    public void writeShort(int pos, short value) {
+        data[pos+0] = (byte)(0xff & (value >> 8));
+        data[pos+1] = (byte)(0xff & (value >> 0));
+        setDirty();
+    }
+
+    /**
+     *  Reads an int from the indicated position
+     */
+    public int readInt(int pos) {
+        return
+            (((int)(data[pos+0] & 0xff) << 24) |
+             ((int)(data[pos+1] & 0xff) << 16) |
+             ((int)(data[pos+2] & 0xff) <<  8) |
+             ((int)(data[pos+3] & 0xff) <<  0));
+    }
+
+    /**
+     *  Writes an int to the indicated position
+     */
+    public void writeInt(int pos, int value) {
+        data[pos+0] = (byte)(0xff & (value >> 24));
+        data[pos+1] = (byte)(0xff & (value >> 16));
+        data[pos+2] = (byte)(0xff & (value >>  8));
+        data[pos+3] = (byte)(0xff & (value >>  0));
+        setDirty();
+    }
+
+    /**
+     *  Reads a long from the indicated position
+     */
+    public long readLong( int pos )
+    {
+        // Contributed by Erwin Bolwidt <ejb@klomp.org>
+        // Gives about 15% performance improvement
+        return
+            ( (long)( ((data[pos+0] & 0xff) << 24) |
+                      ((data[pos+1] & 0xff) << 16) |
+                      ((data[pos+2] & 0xff) <<  8) |
+                      ((data[pos+3] & 0xff)      ) ) << 32 ) |
+            ( (long)( ((data[pos+4] & 0xff) << 24) |
+                      ((data[pos+5] & 0xff) << 16) |
+                      ((data[pos+6] & 0xff) <<  8) |
+                      ((data[pos+7] & 0xff)      ) ) & 0xffffffff );
+        /* Original version by Alex Boisvert.  Might be faster on 64-bit JVMs.
+        return
+            (((long)(data[pos+0] & 0xff) << 56) |
+             ((long)(data[pos+1] & 0xff) << 48) |
+             ((long)(data[pos+2] & 0xff) << 40) |
+             ((long)(data[pos+3] & 0xff) << 32) |
+             ((long)(data[pos+4] & 0xff) << 24) |
+             ((long)(data[pos+5] & 0xff) << 16) |
+             ((long)(data[pos+6] & 0xff) <<  8) |
+             ((long)(data[pos+7] & 0xff) <<  0));
+        */
+    }
+
+    /**
+     *  Writes a long to the indicated position
+     */
+    public void writeLong(int pos, long value) {
+        data[pos+0] = (byte)(0xff & (value >> 56));
+        data[pos+1] = (byte)(0xff & (value >> 48));
+        data[pos+2] = (byte)(0xff & (value >> 40));
+        data[pos+3] = (byte)(0xff & (value >> 32));
+        data[pos+4] = (byte)(0xff & (value >> 24));
+        data[pos+5] = (byte)(0xff & (value >> 16));
+        data[pos+6] = (byte)(0xff & (value >>  8));
+        data[pos+7] = (byte)(0xff & (value >>  0));
+        setDirty();
+    }
+
+    // overrides java.lang.Object
+
+    public String toString() {
+        return "BlockIO("
+            + blockId + ","
+            + dirty + ","
+            + view + ")";
+    }
+
+    // implement externalizable interface
+    public void readExternal(ObjectInput in)
+    throws IOException, ClassNotFoundException {
+        blockId = in.readLong();
+        int length = in.readInt();
+        data = new byte[length];
+        in.readFully(data);
+    }
+
+    // implement externalizable interface
+    public void writeExternal(ObjectOutput out) throws IOException {
+        out.writeLong(blockId);
+        out.writeInt(data.length);
+        out.write(data);
+    }
+
+}
diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/BlockView.java b/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/BlockView.java
new file mode 100644
index 0000000..404970f
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/BlockView.java
@@ -0,0 +1,57 @@
+/**
+ * JDBM LICENSE v1.00
+ *
+ * Redistribution and use of this software and associated documentation
+ * ("Software"), with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain copyright
+ *    statements and notices.  Redistributions must also contain a
+ *    copy of this document.
+ *
+ * 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 name "JDBM" must not be used to endorse or promote
+ *    products derived from this Software without prior written
+ *    permission of Cees de Groot.  For written permission,
+ *    please contact cg@cdegroot.com.
+ *
+ * 4. Products derived from this Software may not be called "JDBM"
+ *    nor may "JDBM" appear in their names without prior written
+ *    permission of Cees de Groot. 
+ *
+ * 5. Due credit should be given to the JDBM Project
+ *    (http://jdbm.sourceforge.net/).
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS
+ * ``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
+ * CEES DE GROOT OR ANY 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.
+ *
+ * Copyright 2000 (C) Cees de Groot. All Rights Reserved.
+ * Contributions are Copyright (C) 2000 by their associated contributors.
+ *
+ * $Id: BlockView.java,v 1.2 2005/06/25 23:12:32 doomdark Exp $
+ */
+
+package jdbm.recman;
+
+/**
+ *  This is a marker interface that is implemented by classes that
+ *  interpret blocks of data by pretending to be an overlay.
+ *
+ *  @see BlockIo#setView
+ */
+public interface BlockView {
+}
diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/CacheRecordManager.java b/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/CacheRecordManager.java
new file mode 100644
index 0000000..e3030fb
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/CacheRecordManager.java
@@ -0,0 +1,457 @@
+/**
+ * JDBM LICENSE v1.00
+ *
+ * Redistribution and use of this software and associated documentation
+ * ("Software"), with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain copyright
+ *    statements and notices.  Redistributions must also contain a
+ *    copy of this document.
+ *
+ * 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 name "JDBM" must not be used to endorse or promote
+ *    products derived from this Software without prior written
+ *    permission of Cees de Groot.  For written permission,
+ *    please contact cg@cdegroot.com.
+ *
+ * 4. Products derived from this Software may not be called "JDBM"
+ *    nor may "JDBM" appear in their names without prior written
+ *    permission of Cees de Groot.
+ *
+ * 5. Due credit should be given to the JDBM Project
+ *    (http://jdbm.sourceforge.net/).
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS
+ * ``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
+ * CEES DE GROOT OR ANY 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.
+ *
+ * Copyright 2000 (C) Cees de Groot. All Rights Reserved.
+ * Copyright 2000-2001 (C) Alex Boisvert. All Rights Reserved.
+ * Contributions are Copyright (C) 2000 by their associated contributors.
+ *
+ * $Id: CacheRecordManager.java,v 1.9 2005/06/25 23:12:32 doomdark Exp $
+ */
+
+package jdbm.recman;
+
+import jdbm.RecordManager;
+import jdbm.helper.CacheEvictionException;
+import jdbm.helper.CachePolicy;
+import jdbm.helper.CachePolicyListener;
+import jdbm.helper.DefaultSerializer;
+import jdbm.helper.Serializer;
+import jdbm.helper.WrappedRuntimeException;
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+/**
+ *  A RecordManager wrapping and caching another RecordManager.
+ *
+ * @author <a href="mailto:boisvert@intalio.com">Alex Boisvert</a>
+ * @author <a href="cg@cdegroot.com">Cees de Groot</a>
+ * @version $Id: CacheRecordManager.java,v 1.9 2005/06/25 23:12:32 doomdark Exp $
+ */
+public class CacheRecordManager
+    implements RecordManager
+{
+
+    /**
+     * Wrapped RecordManager
+     */
+    protected RecordManager _recman;
+
+
+    /**
+     * Cache for underlying RecordManager
+     */
+    protected CachePolicy _cache;
+
+
+    /**
+     * Construct a CacheRecordManager wrapping another RecordManager and
+     * using a given cache policy.
+     *
+     * @param recman Wrapped RecordManager
+     * @param cache Cache policy
+     */
+    public CacheRecordManager( RecordManager recman, CachePolicy cache )
+    {
+        if ( recman == null ) {
+            throw new IllegalArgumentException( "Argument 'recman' is null" );
+        }
+        if ( cache == null ) {
+            throw new IllegalArgumentException( "Argument 'cache' is null" );
+        }
+        _recman = recman;
+        _cache = cache;
+        
+        _cache.addListener( new CacheListener() );
+    }
+
+    
+    /**
+     * Get the underlying Record Manager.
+     *
+     * @return underlying RecordManager or null if CacheRecordManager has
+     *         been closed. 
+     */
+    public RecordManager getRecordManager()
+    {
+        return _recman;
+    }
+
+    
+    /**
+     * Get the underlying cache policy
+     *
+     * @return underlying CachePolicy or null if CacheRecordManager has
+     *         been closed. 
+     */
+    public CachePolicy getCachePolicy()
+    {
+        return _cache;
+    }
+
+    
+    /**
+     *  Inserts a new record using a custom serializer.
+     *
+     *  @param obj the object for the new record.
+     *  @return the rowid for the new record.
+     *  @throws IOException when one of the underlying I/O operations fails.
+     */
+    public long insert( Object obj )
+        throws IOException
+    {
+        return insert( obj, DefaultSerializer.INSTANCE );
+    }
+        
+        
+    /**
+     *  Inserts a new record using a custom serializer.
+     *
+     *  @param obj the object for the new record.
+     *  @param serializer a custom serializer
+     *  @return the rowid for the new record.
+     *  @throws IOException when one of the underlying I/O operations fails.
+     */
+    public synchronized long insert( Object obj, Serializer serializer )
+        throws IOException
+    {
+        checkIfClosed();
+
+        long recid = _recman.insert( obj, serializer );
+        try {
+            _cache.put( new Long( recid ), new CacheEntry( recid, obj, serializer, false ) );
+        } catch ( CacheEvictionException except ) {
+            throw new WrappedRuntimeException( except );
+        }
+        return recid;
+    }
+
+
+    /**
+     *  Deletes a record.
+     *
+     *  @param recid the rowid for the record that should be deleted.
+     *  @throws IOException when one of the underlying I/O operations fails.
+     */
+    public synchronized void delete( long recid )
+        throws IOException
+    {
+        checkIfClosed();
+
+        _recman.delete( recid );
+        _cache.remove( new Long( recid ) );
+    }
+
+
+    /**
+     *  Updates a record using standard Java serialization.
+     *
+     *  @param recid the recid for the record that is to be updated.
+     *  @param obj the new object for the record.
+     *  @throws IOException when one of the underlying I/O operations fails.
+     */
+    public void update( long recid, Object obj )
+        throws IOException
+    {
+        update( recid, obj, DefaultSerializer.INSTANCE );
+    }
+    
+
+    /**
+     *  Updates a record using a custom serializer.
+     *
+     *  @param recid the recid for the record that is to be updated.
+     *  @param obj the new object for the record.
+     *  @param serializer a custom serializer
+     *  @throws IOException when one of the underlying I/O operations fails.
+     */
+    public synchronized void update( long recid, Object obj, 
+                                     Serializer serializer )
+        throws IOException
+    {
+        CacheEntry  entry;
+        Long        id;
+        
+        checkIfClosed();
+
+        id = new Long( recid );
+        try {
+            entry = (CacheEntry) _cache.get( id );
+            if ( entry != null ) {
+                // reuse existing cache entry
+                entry._obj = obj;
+                entry._serializer = serializer;
+                entry._isDirty = true;
+            } else {
+                _cache.put( id, new CacheEntry( recid, obj, serializer, true ) );
+            }
+        } catch ( CacheEvictionException except ) {
+            throw new IOException( except.getMessage() );
+        }
+    }
+
+
+    /**
+     *  Fetches a record using standard Java serialization.
+     *
+     *  @param recid the recid for the record that must be fetched.
+     *  @return the object contained in the record.
+     *  @throws IOException when one of the underlying I/O operations fails.
+     */
+    public Object fetch( long recid )
+        throws IOException
+    {
+        return fetch( recid, DefaultSerializer.INSTANCE );
+    }
+
+        
+    /**
+     *  Fetches a record using a custom serializer.
+     *
+     *  @param recid the recid for the record that must be fetched.
+     *  @param serializer a custom serializer
+     *  @return the object contained in the record.
+     *  @throws IOException when one of the underlying I/O operations fails.
+     */
+    public synchronized Object fetch( long recid, Serializer serializer )
+        throws IOException
+    {
+        checkIfClosed();
+
+        Long id = new Long( recid );
+        CacheEntry entry = (CacheEntry) _cache.get( id );
+        if ( entry == null ) {
+            entry = new CacheEntry( recid, null, serializer, false );
+            entry._obj = _recman.fetch( recid, serializer );
+            try {
+                _cache.put( id, entry );
+            } catch ( CacheEvictionException except ) {
+                throw new WrappedRuntimeException( except );
+            }
+        }
+        return entry._obj;
+    }
+
+
+    /**
+     *  Closes the record manager.
+     *
+     *  @throws IOException when one of the underlying I/O operations fails.
+     */
+    public synchronized void close()
+        throws IOException
+    {
+        checkIfClosed();
+
+        updateCacheEntries();
+        _recman.close();
+        _recman = null;
+        _cache = null;
+    }
+
+
+    /**
+     *  Returns the number of slots available for "root" rowids. These slots
+     *  can be used to store special rowids, like rowids that point to
+     *  other rowids. Root rowids are useful for bootstrapping access to
+     *  a set of data.
+     */
+    public synchronized int getRootCount()
+    {
+        checkIfClosed();
+
+        return _recman.getRootCount();
+    }
+
+
+    /**
+     *  Returns the indicated root rowid.
+     *
+     *  @see #getRootCount
+     */
+    public synchronized long getRoot( int id )
+        throws IOException
+    {
+        checkIfClosed();
+
+        return _recman.getRoot( id );
+    }
+
+
+    /**
+     *  Sets the indicated root rowid.
+     *
+     *  @see #getRootCount
+     */
+    public synchronized void setRoot( int id, long rowid )
+        throws IOException
+    {
+        checkIfClosed();
+
+        _recman.setRoot( id, rowid );
+    }
+
+
+    /**
+     * Commit (make persistent) all changes since beginning of transaction.
+     */
+    public synchronized void commit()
+        throws IOException
+    {
+        checkIfClosed();
+        updateCacheEntries();
+        _recman.commit();
+    }
+
+
+    /**
+     * Rollback (cancel) all changes since beginning of transaction.
+     */
+    public synchronized void rollback()
+        throws IOException
+    {
+        checkIfClosed();
+
+        _recman.rollback();
+
+        // discard all cache entries since we don't know which entries
+        // where part of the transaction
+        _cache.removeAll();
+    }
+
+
+    /**
+     * Obtain the record id of a named object. Returns 0 if named object
+     * doesn't exist.
+     */
+    public synchronized long getNamedObject( String name )
+        throws IOException
+    {
+        checkIfClosed();
+
+        return _recman.getNamedObject( name );
+    }
+
+
+    /**
+     * Set the record id of a named object.
+     */
+    public synchronized void setNamedObject( String name, long recid )
+        throws IOException
+    {
+        checkIfClosed();
+
+        _recman.setNamedObject( name, recid );
+    }
+
+
+    /**
+     * Check if RecordManager has been closed.  If so, throw an
+     * IllegalStateException
+     */
+    private void checkIfClosed()
+        throws IllegalStateException
+    {
+        if ( _recman == null ) {
+            throw new IllegalStateException( "RecordManager has been closed" );
+        }
+    }
+
+    
+    /**
+     * Update all dirty cache objects to the underlying RecordManager.
+     */
+    protected void updateCacheEntries()
+        throws IOException
+    {
+        Enumeration enume = _cache.elements();
+        while ( enume.hasMoreElements() ) {
+            CacheEntry entry = (CacheEntry) enume.nextElement();
+            if ( entry._isDirty ) {
+                _recman.update( entry._recid, entry._obj, entry._serializer );
+                entry._isDirty = false;
+            }
+        }
+    }
+
+    
+    private class CacheEntry
+    {
+
+        long _recid;
+        Object _obj;
+        Serializer _serializer;
+        boolean _isDirty;
+        
+        CacheEntry( long recid, Object obj, Serializer serializer, boolean isDirty )
+        {
+            _recid = recid;
+            _obj = obj;
+            _serializer = serializer;
+            _isDirty = isDirty;
+        }
+        
+    } // class CacheEntry
+
+    private class CacheListener
+        implements CachePolicyListener
+    {
+        
+        /** Notification that cache is evicting an object
+         *
+         * @arg obj object evited from cache
+         *
+         */
+        public void cacheObjectEvicted( Object obj ) 
+            throws CacheEvictionException
+        {
+            CacheEntry entry = (CacheEntry) obj;
+            if ( entry._isDirty ) {
+                try {
+                    _recman.update( entry._recid, entry._obj, entry._serializer );
+                } catch ( IOException except ) {
+                    throw new CacheEvictionException( except );
+                }
+            }
+        }
+        
+    }
+}
diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/DataPage.java b/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/DataPage.java
new file mode 100644
index 0000000..e766714
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/DataPage.java
@@ -0,0 +1,91 @@
+/**
+ * JDBM LICENSE v1.00
+ *
+ * Redistribution and use of this software and associated documentation
+ * ("Software"), with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain copyright
+ *    statements and notices.  Redistributions must also contain a
+ *    copy of this document.
+ *
+ * 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 name "JDBM" must not be used to endorse or promote
+ *    products derived from this Software without prior written
+ *    permission of Cees de Groot.  For written permission,
+ *    please contact cg@cdegroot.com.
+ *
+ * 4. Products derived from this Software may not be called "JDBM"
+ *    nor may "JDBM" appear in their names without prior written
+ *    permission of Cees de Groot. 
+ *
+ * 5. Due credit should be given to the JDBM Project
+ *    (http://jdbm.sourceforge.net/).
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS
+ * ``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
+ * CEES DE GROOT OR ANY 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.
+ *
+ * Copyright 2000 (C) Cees de Groot. All Rights Reserved.
+ * Contributions are Copyright (C) 2000 by their associated contributors.
+ *
+ * $Id: DataPage.java,v 1.1 2000/05/06 00:00:31 boisvert Exp $
+ */
+
+package jdbm.recman;
+
+/**
+ *  Class describing a page that holds data.
+ */
+final class DataPage extends PageHeader {
+    // offsets
+    private static final short O_FIRST = PageHeader.SIZE; // short firstrowid
+    static final short O_DATA = (short)(O_FIRST + Magic.SZ_SHORT);
+    static final short DATA_PER_PAGE = (short)(RecordFile.BLOCK_SIZE - O_DATA);
+
+    /**
+     *  Constructs a data page view from the indicated block.
+     */
+    DataPage(BlockIo block) {
+  super(block);
+    }
+
+    /**
+     *  Factory method to create or return a data page for the
+     *  indicated block.
+     */
+    static DataPage getDataPageView(BlockIo block) {
+  BlockView view = block.getView();
+  if (view != null && view instanceof DataPage)
+      return (DataPage) view;
+  else
+      return new DataPage(block);
+    }
+
+    /** Returns the first rowid's offset */
+    short getFirst() {
+  return block.readShort(O_FIRST);
+    }
+    
+    /** Sets the first rowid's offset */
+    void setFirst(short value) {
+  paranoiaMagicOk();
+  if (value > 0 && value < O_DATA)
+      throw new Error("DataPage.setFirst: offset " + value 
+          + " too small");
+  block.writeShort(O_FIRST, value);
+    }
+}
diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/FileHeader.java b/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/FileHeader.java
new file mode 100644
index 0000000..b3e4446
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/FileHeader.java
@@ -0,0 +1,156 @@
+/**
+ * JDBM LICENSE v1.00
+ *
+ * Redistribution and use of this software and associated documentation
+ * ("Software"), with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain copyright
+ *    statements and notices.  Redistributions must also contain a
+ *    copy of this document.
+ *
+ * 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 name "JDBM" must not be used to endorse or promote
+ *    products derived from this Software without prior written
+ *    permission of Cees de Groot.  For written permission,
+ *    please contact cg@cdegroot.com.
+ *
+ * 4. Products derived from this Software may not be called "JDBM"
+ *    nor may "JDBM" appear in their names without prior written
+ *    permission of Cees de Groot. 
+ *
+ * 5. Due credit should be given to the JDBM Project
+ *    (http://jdbm.sourceforge.net/).
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS
+ * ``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
+ * CEES DE GROOT OR ANY 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.
+ *
+ * Copyright 2000 (C) Cees de Groot. All Rights Reserved.
+ * Contributions are Copyright (C) 2000 by their associated contributors.
+ *
+ * $Id: FileHeader.java,v 1.3 2005/06/25 23:12:32 doomdark Exp $
+ */
+
+package jdbm.recman;
+
+/**
+ *  This class represents a file header. It is a 1:1 representation of
+ *  the data that appears in block 0 of a file.
+ */
+class FileHeader implements BlockView {
+    // offsets
+    private static final short O_MAGIC = 0; // short magic
+    private static final short O_LISTS = Magic.SZ_SHORT; // long[2*NLISTS]
+    private static final int O_ROOTS = 
+        O_LISTS + (Magic.NLISTS * 2 * Magic.SZ_LONG);
+
+    // my block
+    private BlockIo block;
+
+    /** The number of "root" rowids available in the file. */
+    static final int NROOTS = 
+        (RecordFile.BLOCK_SIZE - O_ROOTS) / Magic.SZ_LONG;
+
+    /**
+     *  Constructs a FileHeader object from a block.
+     *
+     *  @param block The block that contains the file header
+     *  @param isNew If true, the file header is for a new file.
+     *  @throws IOException if the block is too short to keep the file
+     *          header.
+     */
+    FileHeader(BlockIo block, boolean isNew) {
+        this.block = block;
+        if (isNew)
+            block.writeShort(O_MAGIC, Magic.FILE_HEADER);
+        else if (!magicOk())
+            throw new Error("CRITICAL: file header magic not OK " 
+                            + block.readShort(O_MAGIC));
+    }
+
+    /** Returns true if the magic corresponds with the fileHeader magic.  */
+    private boolean magicOk() {
+        return block.readShort(O_MAGIC) == Magic.FILE_HEADER;
+    }
+
+
+    /** Returns the offset of the "first" block of the indicated list */
+    private short offsetOfFirst(int list) {
+        return (short) (O_LISTS + (2 * Magic.SZ_LONG * list));
+    }
+
+    /** Returns the offset of the "last" block of the indicated list */
+    private short offsetOfLast(int list) {
+        return (short) (offsetOfFirst(list) + Magic.SZ_LONG);
+    }
+
+    /** Returns the offset of the indicated root */
+    private short offsetOfRoot(int root) {
+        return (short) (O_ROOTS + (root * Magic.SZ_LONG));
+    }
+
+    /**
+     *  Returns the first block of the indicated list
+     */
+    long getFirstOf(int list) {
+        return block.readLong(offsetOfFirst(list));
+    }
+    
+    /**
+     *  Sets the first block of the indicated list
+     */
+    void setFirstOf(int list, long value) {
+        block.writeLong(offsetOfFirst(list), value);
+    }
+    
+    /**
+     *  Returns the last block of the indicated list
+     */
+    long getLastOf(int list) {
+        return block.readLong(offsetOfLast(list));
+    }
+    
+    /**
+     *  Sets the last block of the indicated list
+     */
+    void setLastOf(int list, long value) {
+        block.writeLong(offsetOfLast(list), value);
+    }
+
+    /**
+     *  Returns the indicated root rowid. A root rowid is a special rowid
+     *  that needs to be kept between sessions. It could conceivably be
+     *  stored in a special file, but as a large amount of space in the
+     *  block header is wasted anyway, it's more useful to store it where
+     *  it belongs.
+     *
+     *  @see #NROOTS
+     */
+    long getRoot(int root) {
+        return block.readLong(offsetOfRoot(root));
+    }
+
+    /**
+     *  Sets the indicated root rowid.
+     *
+     *  @see #getRoot
+     *  @see #NROOTS
+     */
+    void setRoot(int root, long rowid) {
+        block.writeLong(offsetOfRoot(root), rowid);
+    }
+}
diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/FreeLogicalRowIdPage.java b/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/FreeLogicalRowIdPage.java
new file mode 100644
index 0000000..ca65b08
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/FreeLogicalRowIdPage.java
@@ -0,0 +1,154 @@
+/**
+ * JDBM LICENSE v1.00
+ *
+ * Redistribution and use of this software and associated documentation
+ * ("Software"), with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain copyright
+ *    statements and notices.  Redistributions must also contain a
+ *    copy of this document.
+ *
+ * 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 name "JDBM" must not be used to endorse or promote
+ *    products derived from this Software without prior written
+ *    permission of Cees de Groot.  For written permission,
+ *    please contact cg@cdegroot.com.
+ *
+ * 4. Products derived from this Software may not be called "JDBM"
+ *    nor may "JDBM" appear in their names without prior written
+ *    permission of Cees de Groot. 
+ *
+ * 5. Due credit should be given to the JDBM Project
+ *    (http://jdbm.sourceforge.net/).
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS
+ * ``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
+ * CEES DE GROOT OR ANY 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.
+ *
+ * Copyright 2000 (C) Cees de Groot. All Rights Reserved.
+ * Contributions are Copyright (C) 2000 by their associated contributors.
+ *
+ * $Id: FreeLogicalRowIdPage.java,v 1.1 2000/05/06 00:00:31 boisvert Exp $
+ */
+
+package jdbm.recman;
+
+/**
+ *  Class describing a page that holds logical rowids that were freed. Note
+ *  that the methods have *physical* rowids in their signatures - this is
+ *  because logical and physical rowids are internally the same, only their
+ *  external representation (i.e. in the client API) differs.
+ */
+class FreeLogicalRowIdPage extends PageHeader {
+    // offsets
+    private static final short O_COUNT = PageHeader.SIZE; // short count
+    static final short O_FREE = (short)(O_COUNT + Magic.SZ_SHORT);
+    static final short ELEMS_PER_PAGE = (short)
+        ((RecordFile.BLOCK_SIZE - O_FREE) / PhysicalRowId.SIZE);
+
+    // slots we returned.
+    final PhysicalRowId[] slots = new PhysicalRowId[ELEMS_PER_PAGE];
+
+    /**
+     *  Constructs a data page view from the indicated block.
+     */
+    FreeLogicalRowIdPage(BlockIo block) {
+        super(block);
+    }
+
+    /**
+     *  Factory method to create or return a data page for the
+     *  indicated block.
+     */
+    static FreeLogicalRowIdPage getFreeLogicalRowIdPageView(BlockIo block) {
+
+        BlockView view = block.getView();
+        if (view != null && view instanceof FreeLogicalRowIdPage)
+            return (FreeLogicalRowIdPage) view;
+        else
+            return new FreeLogicalRowIdPage(block);
+    }
+
+    /** Returns the number of free rowids */
+    short getCount() {
+        return block.readShort(O_COUNT);
+    }
+
+    /** Sets the number of free rowids */
+    private void setCount(short i) {
+        block.writeShort(O_COUNT, i);
+    }
+
+    /** Frees a slot */
+    void free(int slot) {
+        get(slot).setBlock(0);
+        setCount((short) (getCount() - 1));
+    }
+
+    /** Allocates a slot */
+    PhysicalRowId alloc(int slot) {
+        setCount((short) (getCount() + 1));
+        get(slot).setBlock(-1);
+        return get(slot);
+    }
+
+    /** Returns true if a slot is allocated */
+    boolean isAllocated(int slot) {
+        return get(slot).getBlock() > 0;
+    }
+
+    /** Returns true if a slot is free */
+    boolean isFree(int slot) {
+        return !isAllocated(slot);
+    }
+
+
+    /** Returns the value of the indicated slot */
+    PhysicalRowId get(int slot) {
+        if (slots[slot] == null)
+            slots[slot] = new PhysicalRowId(block, slotToOffset(slot));;
+        return slots[slot];
+    }
+
+    /** Converts slot to offset */
+    private short slotToOffset(int slot) {
+        return (short) (O_FREE +
+                        (slot * PhysicalRowId.SIZE));
+    }
+
+    /**
+     *  Returns first free slot, -1 if no slots are available
+     */
+    int getFirstFree() {
+        for (int i = 0; i < ELEMS_PER_PAGE; i++) {
+            if (isFree(i))
+                return i;
+        }
+        return -1;
+    }
+
+    /**
+     *  Returns first allocated slot, -1 if no slots are available.
+     */
+    int getFirstAllocated() {
+        for (int i = 0; i < ELEMS_PER_PAGE; i++) {
+            if (isAllocated(i))
+                return i;
+        }
+        return -1;
+    }
+}
diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/FreeLogicalRowIdPageManager.java b/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/FreeLogicalRowIdPageManager.java
new file mode 100644
index 0000000..342246d
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/FreeLogicalRowIdPageManager.java
@@ -0,0 +1,143 @@
+/**
+ * JDBM LICENSE v1.00
+ *
+ * Redistribution and use of this software and associated documentation
+ * ("Software"), with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain copyright
+ *    statements and notices.  Redistributions must also contain a
+ *    copy of this document.
+ *
+ * 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 name "JDBM" must not be used to endorse or promote
+ *    products derived from this Software without prior written
+ *    permission of Cees de Groot.  For written permission,
+ *    please contact cg@cdegroot.com.
+ *
+ * 4. Products derived from this Software may not be called "JDBM"
+ *    nor may "JDBM" appear in their names without prior written
+ *    permission of Cees de Groot. 
+ *
+ * 5. Due credit should be given to the JDBM Project
+ *    (http://jdbm.sourceforge.net/).
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS
+ * ``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
+ * CEES DE GROOT OR ANY 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.
+ *
+ * Copyright 2000 (C) Cees de Groot. All Rights Reserved.
+ * Contributions are Copyright (C) 2000 by their associated contributors.
+ *
+ * $Id: FreeLogicalRowIdPageManager.java,v 1.1 2000/05/06 00:00:31 boisvert Exp $
+ */
+
+package jdbm.recman;
+
+import java.io.IOException;
+
+/**
+ *  This class manages free Logical rowid pages and provides methods
+ *  to free and allocate Logical rowids on a high level.
+ */
+final class FreeLogicalRowIdPageManager {
+    // our record file
+    private RecordFile file;
+    // our page manager
+    private PageManager pageman;
+
+    /**
+     *  Creates a new instance using the indicated record file and
+     *  page manager.
+     */
+    FreeLogicalRowIdPageManager(RecordFile file,
+                                PageManager pageman) throws IOException {
+        this.file = file;
+        this.pageman = pageman;
+    }
+
+    /**
+     *  Returns a free Logical rowid, or
+     *  null if nothing was found.
+     */
+    Location get() throws IOException {
+  
+        // Loop through the free Logical rowid list until we find
+        // the first rowid.
+        Location retval = null;
+        PageCursor curs = new PageCursor(pageman, Magic.FREELOGIDS_PAGE);
+        while (curs.next() != 0) {
+            FreeLogicalRowIdPage fp = FreeLogicalRowIdPage
+                .getFreeLogicalRowIdPageView(file.get(curs.getCurrent()));
+            int slot = fp.getFirstAllocated();
+            if (slot != -1) {
+                // got one!
+                retval =
+                    new Location(fp.get(slot));
+                fp.free(slot);
+                if (fp.getCount() == 0) {
+                    // page became empty - free it
+                    file.release(curs.getCurrent(), false);
+                    pageman.free(Magic.FREELOGIDS_PAGE, curs.getCurrent());
+                }
+                else
+                    file.release(curs.getCurrent(), true);
+                
+                return retval;
+            }
+            else {
+                // no luck, go to next page
+                file.release(curs.getCurrent(), false);
+            }     
+        }
+        return null;
+    }
+
+    /**
+     *  Puts the indicated rowid on the free list
+     */
+    void put(Location rowid)
+    throws IOException {
+        
+        PhysicalRowId free = null;
+        PageCursor curs = new PageCursor(pageman, Magic.FREELOGIDS_PAGE);
+        long freePage = 0;
+        while (curs.next() != 0) {
+            freePage = curs.getCurrent();
+            BlockIo curBlock = file.get(freePage);
+            FreeLogicalRowIdPage fp = FreeLogicalRowIdPage
+                .getFreeLogicalRowIdPageView(curBlock);
+            int slot = fp.getFirstFree();
+            if (slot != -1) {
+                free = fp.alloc(slot);
+                break;
+            }
+            
+            file.release(curBlock);
+        }
+        if (free == null) {
+            // No more space on the free list, add a page.
+            freePage = pageman.allocate(Magic.FREELOGIDS_PAGE);
+            BlockIo curBlock = file.get(freePage);
+            FreeLogicalRowIdPage fp = 
+                FreeLogicalRowIdPage.getFreeLogicalRowIdPageView(curBlock);
+            free = fp.alloc(0);
+        }
+        free.setBlock(rowid.getBlock());
+        free.setOffset(rowid.getOffset());
+        file.release(freePage, true);
+    }
+}
diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/FreePhysicalRowId.java b/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/FreePhysicalRowId.java
new file mode 100644
index 0000000..c604029
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/FreePhysicalRowId.java
@@ -0,0 +1,77 @@
+/**
+ * JDBM LICENSE v1.00
+ *
+ * Redistribution and use of this software and associated documentation
+ * ("Software"), with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain copyright
+ *    statements and notices.  Redistributions must also contain a
+ *    copy of this document.
+ *
+ * 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 name "JDBM" must not be used to endorse or promote
+ *    products derived from this Software without prior written
+ *    permission of Cees de Groot.  For written permission,
+ *    please contact cg@cdegroot.com.
+ *
+ * 4. Products derived from this Software may not be called "JDBM"
+ *    nor may "JDBM" appear in their names without prior written
+ *    permission of Cees de Groot. 
+ *
+ * 5. Due credit should be given to the JDBM Project
+ *    (http://jdbm.sourceforge.net/).
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS
+ * ``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
+ * CEES DE GROOT OR ANY 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.
+ *
+ * Copyright 2000 (C) Cees de Groot. All Rights Reserved.
+ * Contributions are Copyright (C) 2000 by their associated contributors.
+ *
+ * $Id: FreePhysicalRowId.java,v 1.1 2000/05/06 00:00:31 boisvert Exp $
+ */
+
+package jdbm.recman;
+
+/**
+ *  This class extends the physical rowid with a size value to indicated
+ *  the size of a free rowid on the free rowid list.
+ */
+final class FreePhysicalRowId extends PhysicalRowId {
+    // offsets
+    private static final short O_SIZE = PhysicalRowId.SIZE; // int size
+    static final short SIZE = O_SIZE + Magic.SZ_INT;
+
+    /**
+     *  Constructs a physical rowid from the indicated data starting at
+     *  the indicated position.
+     */
+    FreePhysicalRowId(BlockIo block, short pos) {
+  super(block, pos);
+    }
+
+    /** Returns the size */
+    int getSize() {
+  return block.readInt(pos + O_SIZE);
+    }
+
+    /** Sets the size */
+    void setSize(int value) {
+  block.writeInt(pos + O_SIZE, value);
+    }
+
+}
diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/FreePhysicalRowIdPage.java b/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/FreePhysicalRowIdPage.java
new file mode 100644
index 0000000..8a9b860
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/FreePhysicalRowIdPage.java
@@ -0,0 +1,150 @@
+/**
+ * JDBM LICENSE v1.00
+ *
+ * Redistribution and use of this software and associated documentation
+ * ("Software"), with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain copyright
+ *    statements and notices.  Redistributions must also contain a
+ *    copy of this document.
+ *
+ * 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 name "JDBM" must not be used to endorse or promote
+ *    products derived from this Software without prior written
+ *    permission of Cees de Groot.  For written permission,
+ *    please contact cg@cdegroot.com.
+ *
+ * 4. Products derived from this Software may not be called "JDBM"
+ *    nor may "JDBM" appear in their names without prior written
+ *    permission of Cees de Groot. 
+ *
+ * 5. Due credit should be given to the JDBM Project
+ *    (http://jdbm.sourceforge.net/).
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS
+ * ``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
+ * CEES DE GROOT OR ANY 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.
+ *
+ * Copyright 2000 (C) Cees de Groot. All Rights Reserved.
+ * Contributions are Copyright (C) 2000 by their associated contributors.
+ *
+ * $Id: FreePhysicalRowIdPage.java,v 1.1 2000/05/06 00:00:31 boisvert Exp $
+ */
+
+package jdbm.recman;
+
+/**
+ *  Class describing a page that holds physical rowids that were freed.
+ */
+final class FreePhysicalRowIdPage extends PageHeader {
+    // offsets
+    private static final short O_COUNT = PageHeader.SIZE; // short count
+    static final short O_FREE = O_COUNT + Magic.SZ_SHORT;
+    static final short ELEMS_PER_PAGE = 
+  (RecordFile.BLOCK_SIZE - O_FREE) / FreePhysicalRowId.SIZE;
+    
+    // slots we returned.
+    FreePhysicalRowId[] slots = new FreePhysicalRowId[ELEMS_PER_PAGE];
+
+    /**
+     *  Constructs a data page view from the indicated block.
+     */
+    FreePhysicalRowIdPage(BlockIo block) {
+  super(block);
+    }
+
+    /**
+     *  Factory method to create or return a data page for the
+     *  indicated block.
+     */
+    static FreePhysicalRowIdPage getFreePhysicalRowIdPageView(BlockIo block) {
+  BlockView view = block.getView();
+  if (view != null && view instanceof FreePhysicalRowIdPage)
+      return (FreePhysicalRowIdPage) view;
+  else
+      return new FreePhysicalRowIdPage(block);
+    }
+
+    /** Returns the number of free rowids */
+    short getCount() {
+  return block.readShort(O_COUNT);
+    }
+
+    /** Sets the number of free rowids */
+    private void setCount(short i) {
+  block.writeShort(O_COUNT, i);
+    }
+
+    /** Frees a slot */
+    void free(int slot) {
+  get(slot).setSize(0);
+  setCount((short) (getCount() - 1));
+    }
+
+    /** Allocates a slot */
+    FreePhysicalRowId alloc(int slot) {
+  setCount((short) (getCount() + 1));
+  return get(slot);
+    }
+
+    /** Returns true if a slot is allocated */
+    boolean isAllocated(int slot) {
+  return get(slot).getSize() != 0;
+    }
+
+    /** Returns true if a slot is free */
+    boolean isFree(int slot) {
+  return !isAllocated(slot);
+    }
+    
+    
+    /** Returns the value of the indicated slot */
+    FreePhysicalRowId get(int slot) {
+  if (slots[slot] == null) 
+      slots[slot] = new FreePhysicalRowId(block, slotToOffset(slot));;
+  return slots[slot];
+    }
+
+    /** Converts slot to offset */
+    short slotToOffset(int slot) {
+  return (short) (O_FREE +
+      (slot * FreePhysicalRowId.SIZE));
+    }
+    
+    /** 
+     *  Returns first free slot, -1 if no slots are available
+     */
+    int getFirstFree() {
+  for (int i = 0; i < ELEMS_PER_PAGE; i++) {
+      if (isFree(i))
+    return i;
+  }
+  return -1;
+    }
+
+    /** 
+     *  Returns first slot with available size >= indicated size,  
+     *  or -1 if no slots are available.
+     **/
+    int getFirstLargerThan(int size) {
+  for (int i = 0; i < ELEMS_PER_PAGE; i++) {
+      if (isAllocated(i) && get(i).getSize() >= size)
+    return i;
+  }
+  return -1;
+    }
+}
diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/FreePhysicalRowIdPageManager.java b/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/FreePhysicalRowIdPageManager.java
new file mode 100644
index 0000000..879149f
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/FreePhysicalRowIdPageManager.java
@@ -0,0 +1,152 @@
+/**
+ * JDBM LICENSE v1.00
+ *
+ * Redistribution and use of this software and associated documentation
+ * ("Software"), with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain copyright
+ *    statements and notices.  Redistributions must also contain a
+ *    copy of this document.
+ *
+ * 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 name "JDBM" must not be used to endorse or promote
+ *    products derived from this Software without prior written
+ *    permission of Cees de Groot.  For written permission,
+ *    please contact cg@cdegroot.com.
+ *
+ * 4. Products derived from this Software may not be called "JDBM"
+ *    nor may "JDBM" appear in their names without prior written
+ *    permission of Cees de Groot.
+ *
+ * 5. Due credit should be given to the JDBM Project
+ *    (http://jdbm.sourceforge.net/).
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS
+ * ``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
+ * CEES DE GROOT OR ANY 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.
+ *
+ * Copyright 2000 (C) Cees de Groot. All Rights Reserved.
+ * Contributions are Copyright (C) 2000 by their associated contributors.
+ *
+ * $Id: FreePhysicalRowIdPageManager.java,v 1.2 2001/11/17 16:14:25 boisvert Exp $
+ */
+
+package jdbm.recman;
+
+import java.io.IOException;
+
+/**
+ *  This class manages free physical rowid pages and provides methods
+ *  to free and allocate physical rowids on a high level.
+ */
+final class FreePhysicalRowIdPageManager
+{
+    // our record file
+    protected RecordFile _file;
+
+    // our page manager
+    protected PageManager _pageman;
+
+    /**
+     *  Creates a new instance using the indicated record file and
+     *  page manager.
+     */
+    FreePhysicalRowIdPageManager( RecordFile file, PageManager pageman )
+        throws IOException
+    {
+        _file = file;
+        _pageman = pageman;
+    }
+
+
+    /**
+     *  Returns a free physical rowid of the indicated size, or
+     *  null if nothing was found.
+     */
+    Location get( int size )
+        throws IOException
+    {
+        // Loop through the free physical rowid list until we find
+        // a rowid that's large enough.
+        Location retval = null;
+        PageCursor curs = new PageCursor( _pageman, Magic.FREEPHYSIDS_PAGE );
+
+        while (curs.next() != 0) {
+            FreePhysicalRowIdPage fp = FreePhysicalRowIdPage
+                .getFreePhysicalRowIdPageView( _file.get( curs.getCurrent() ) );
+            int slot = fp.getFirstLargerThan( size );
+            if ( slot != -1 ) {
+                // got one!
+                retval = new Location( fp.get( slot ) );
+
+                int slotsize = fp.get( slot ).getSize();
+                fp.free( slot );
+                if ( fp.getCount() == 0 ) {
+                    // page became empty - free it
+                    _file.release( curs.getCurrent(), false );
+                    _pageman.free( Magic.FREEPHYSIDS_PAGE, curs.getCurrent() );
+                } else {
+                    _file.release( curs.getCurrent(), true );
+                }
+
+                return retval;
+            } else {
+                // no luck, go to next page
+                _file.release( curs.getCurrent(), false );
+            }
+
+        }
+        return null;
+    }
+
+    /**
+     *  Puts the indicated rowid on the free list
+     */
+    void put(Location rowid, int size)
+  throws IOException {
+
+  FreePhysicalRowId free = null;
+  PageCursor curs = new PageCursor(_pageman, Magic.FREEPHYSIDS_PAGE);
+  long freePage = 0;
+  while (curs.next() != 0) {
+      freePage = curs.getCurrent();
+      BlockIo curBlock = _file.get(freePage);
+      FreePhysicalRowIdPage fp = FreePhysicalRowIdPage
+    .getFreePhysicalRowIdPageView(curBlock);
+      int slot = fp.getFirstFree();
+      if (slot != -1) {
+    free = fp.alloc(slot);
+    break;
+      }
+
+      _file.release(curBlock);
+  }
+  if (free == null) {
+      // No more space on the free list, add a page.
+      freePage = _pageman.allocate(Magic.FREEPHYSIDS_PAGE);
+      BlockIo curBlock = _file.get(freePage);
+      FreePhysicalRowIdPage fp =
+    FreePhysicalRowIdPage.getFreePhysicalRowIdPageView(curBlock);
+      free = fp.alloc(0);
+  }
+
+  free.setBlock(rowid.getBlock());
+  free.setOffset(rowid.getOffset());
+  free.setSize(size);
+  _file.release(freePage, true);
+    }
+}
diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/Location.java b/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/Location.java
new file mode 100644
index 0000000..abc578e
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/Location.java
@@ -0,0 +1,121 @@
+/**
+ * JDBM LICENSE v1.00
+ *
+ * Redistribution and use of this software and associated documentation
+ * ("Software"), with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain copyright
+ *    statements and notices.  Redistributions must also contain a
+ *    copy of this document.
+ *
+ * 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 name "JDBM" must not be used to endorse or promote
+ *    products derived from this Software without prior written
+ *    permission of Cees de Groot.  For written permission,
+ *    please contact cg@cdegroot.com.
+ *
+ * 4. Products derived from this Software may not be called "JDBM"
+ *    nor may "JDBM" appear in their names without prior written
+ *    permission of Cees de Groot. 
+ *
+ * 5. Due credit should be given to the JDBM Project
+ *    (http://jdbm.sourceforge.net/).
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS
+ * ``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
+ * CEES DE GROOT OR ANY 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.
+ *
+ * Copyright 2000 (C) Cees de Groot. All Rights Reserved.
+ * Contributions are Copyright (C) 2000 by their associated contributors.
+ *
+ * $Id: Location.java,v 1.2 2003/11/01 14:17:21 dranatunga Exp $
+ */
+
+package jdbm.recman;
+
+/**
+ * This class represents a location within a file. Both physical and
+ * logical rowids are based on locations internally - this version is
+ * used when there is no file block to back the location's data.
+ */
+final class Location {
+    private long block;
+    private short offset;
+
+    /**
+     * Creates a location from a (block, offset) tuple.
+     */
+    Location(long block, short offset) {
+        this.block = block;
+        this.offset = offset;
+    }
+
+    /**
+     * Creates a location from a combined block/offset long, as
+     * used in the external representation of logical rowids.
+     * 
+     * @see #toLong()
+     */
+    Location(long blockOffset) {
+        this.offset = (short) (blockOffset & 0xffff);
+        this.block = blockOffset >> 16;
+    }
+
+    /**
+     * Creates a location based on the data of the physical rowid.
+     */
+    Location(PhysicalRowId src) {
+        block = src.getBlock();
+        offset = src.getOffset();
+    }
+
+    /**
+     * Returns the file block of the location
+     */
+    long getBlock() {
+        return block;
+    }
+
+    /**
+     * Returns the offset within the block of the location
+     */
+    short getOffset() {
+        return offset;
+    }
+
+    /**
+     * Returns the external representation of a location when used
+     * as a logical rowid, which combines the block and the offset
+     * in a single long.
+     */
+    long toLong() {
+        return (block << 16) + (long) offset;
+    }
+
+    // overrides of java.lang.Object
+
+    public boolean equals(Object o) {
+        if (o == null || !(o instanceof Location))
+            return false;
+        Location ol = (Location) o;
+        return ol.block == block && ol.offset == offset;
+    }
+
+    public String toString() {
+        return "PL(" + block + ":" + offset + ")";
+    }
+}
diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/LogicalRowIdManager.java b/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/LogicalRowIdManager.java
new file mode 100644
index 0000000..7d16490
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/LogicalRowIdManager.java
@@ -0,0 +1,145 @@
+/**
+ * JDBM LICENSE v1.00
+ *
+ * Redistribution and use of this software and associated documentation
+ * ("Software"), with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain copyright
+ *    statements and notices.  Redistributions must also contain a
+ *    copy of this document.
+ *
+ * 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 name "JDBM" must not be used to endorse or promote
+ *    products derived from this Software without prior written
+ *    permission of Cees de Groot.  For written permission,
+ *    please contact cg@cdegroot.com.
+ *
+ * 4. Products derived from this Software may not be called "JDBM"
+ *    nor may "JDBM" appear in their names without prior written
+ *    permission of Cees de Groot.
+ *
+ * 5. Due credit should be given to the JDBM Project
+ *    (http://jdbm.sourceforge.net/).
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS
+ * ``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
+ * CEES DE GROOT OR ANY 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.
+ *
+ * Copyright 2000 (C) Cees de Groot. All Rights Reserved.
+ * Contributions are Copyright (C) 2000 by their associated contributors.
+ *
+ * $Id: LogicalRowIdManager.java,v 1.3 2005/06/25 23:12:32 doomdark Exp $
+ */
+
+package jdbm.recman;
+
+import java.io.IOException;
+
+/**
+ *  This class manages the linked lists of logical rowid pages.
+ */
+final class LogicalRowIdManager {
+    // our record file and associated page manager
+    private RecordFile file;
+    private PageManager pageman;
+    private FreeLogicalRowIdPageManager freeman;
+
+    /**
+     *  Creates a log rowid manager using the indicated record file and
+     *  page manager
+     */
+    LogicalRowIdManager(RecordFile file, PageManager pageman)
+  throws IOException {
+  this.file = file;
+  this.pageman = pageman;
+  this.freeman = new FreeLogicalRowIdPageManager(file, pageman);
+
+    }
+
+    /**
+     *  Creates a new logical rowid pointing to the indicated physical
+     *  id
+     */
+    Location insert(Location loc)
+    throws IOException {
+  // check whether there's a free rowid to reuse
+  Location retval = freeman.get();
+  if (retval == null) {
+      // no. This means that we bootstrap things by allocating
+      // a new translation page and freeing all the rowids on it.
+      long firstPage = pageman.allocate(Magic.TRANSLATION_PAGE);
+      short curOffset = TranslationPage.O_TRANS;
+      for (int i = 0; i < TranslationPage.ELEMS_PER_PAGE; i++) {
+    freeman.put(new Location(firstPage, curOffset));
+    curOffset += PhysicalRowId.SIZE;
+      }
+      retval = freeman.get();
+      if (retval == null) {
+    throw new Error("couldn't obtain free translation");
+      }
+  }
+  // write the translation.
+  update(retval, loc);
+  return retval;
+    }
+
+    /**
+     *  Releases the indicated logical rowid.
+     */
+    void delete(Location rowid)
+  throws IOException {
+
+  freeman.put(rowid);
+    }
+
+    /**
+     *  Updates the mapping
+     *
+     *  @param rowid The logical rowid
+     *  @param loc The physical rowid
+     */
+    void update(Location rowid, Location loc)
+    throws IOException {
+
+        TranslationPage xlatPage = TranslationPage.getTranslationPageView(
+                                       file.get(rowid.getBlock()));
+        PhysicalRowId physid = xlatPage.get(rowid.getOffset());
+        physid.setBlock(loc.getBlock());
+        physid.setOffset(loc.getOffset());
+        file.release(rowid.getBlock(), true);
+    }
+
+    /**
+     *  Returns a mapping
+     *
+     *  @param rowid The logical rowid
+     *  @return The physical rowid
+     */
+    Location fetch(Location rowid)
+    throws IOException {
+
+        TranslationPage xlatPage = TranslationPage.getTranslationPageView(
+                                       file.get(rowid.getBlock()));
+        try {
+            Location retval = new Location(xlatPage.get(rowid.getOffset()));
+            return retval;
+        } finally {
+            file.release(rowid.getBlock(), false);
+        }
+    }
+
+}
diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/Magic.java b/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/Magic.java
new file mode 100644
index 0000000..f26c6a9
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/Magic.java
@@ -0,0 +1,87 @@
+/**
+ * JDBM LICENSE v1.00
+ *
+ * Redistribution and use of this software and associated documentation
+ * ("Software"), with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain copyright
+ *    statements and notices.  Redistributions must also contain a
+ *    copy of this document.
+ *
+ * 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 name "JDBM" must not be used to endorse or promote
+ *    products derived from this Software without prior written
+ *    permission of Cees de Groot.  For written permission,
+ *    please contact cg@cdegroot.com.
+ *
+ * 4. Products derived from this Software may not be called "JDBM"
+ *    nor may "JDBM" appear in their names without prior written
+ *    permission of Cees de Groot. 
+ *
+ * 5. Due credit should be given to the JDBM Project
+ *    (http://jdbm.sourceforge.net/).
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS
+ * ``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
+ * CEES DE GROOT OR ANY 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.
+ *
+ * Copyright 2000 (C) Cees de Groot. All Rights Reserved.
+ * Contributions are Copyright (C) 2000 by their associated contributors.
+ *
+ * $Id: Magic.java,v 1.1 2000/05/06 00:00:31 boisvert Exp $
+ */
+
+package jdbm.recman;
+
+/**
+ *  This interface contains magic cookies.
+ */
+public interface Magic {
+    /** Magic cookie at start of file */
+    public short FILE_HEADER = 0x1350;
+
+    /** Magic for blocks. They're offset by the block type magic codes. */
+    public short BLOCK = 0x1351;
+
+    /** Magics for blocks in certain lists. Offset by baseBlockMagic */
+    short FREE_PAGE = 0;
+    short USED_PAGE = 1;
+    short TRANSLATION_PAGE = 2;
+    short FREELOGIDS_PAGE = 3;
+    short FREEPHYSIDS_PAGE = 4;
+
+    /** Number of lists in a file */
+    public short NLISTS = 5;
+
+    /**
+     *  Maximum number of blocks in a file, leaving room for a 16 bit
+     *  offset encoded within a long.
+     */
+    long MAX_BLOCKS = 0x7FFFFFFFFFFFL;
+
+    /** Magic for transaction file */
+    short LOGFILE_HEADER = 0x1360;
+
+    /** Size of an externalized byte */
+    public short SZ_BYTE = 1;
+    /** Size of an externalized short */
+    public short SZ_SHORT = 2;
+    /** Size of an externalized int */
+    public short SZ_INT = 4;
+    /** Size of an externalized long */
+    public short SZ_LONG = 8;
+}
diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/PageCursor.java b/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/PageCursor.java
new file mode 100644
index 0000000..d335aae
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/PageCursor.java
@@ -0,0 +1,103 @@
+/**
+ * JDBM LICENSE v1.00
+ *
+ * Redistribution and use of this software and associated documentation
+ * ("Software"), with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain copyright
+ *    statements and notices.  Redistributions must also contain a
+ *    copy of this document.
+ *
+ * 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 name "JDBM" must not be used to endorse or promote
+ *    products derived from this Software without prior written
+ *    permission of Cees de Groot.  For written permission,
+ *    please contact cg@cdegroot.com.
+ *
+ * 4. Products derived from this Software may not be called "JDBM"
+ *    nor may "JDBM" appear in their names without prior written
+ *    permission of Cees de Groot. 
+ *
+ * 5. Due credit should be given to the JDBM Project
+ *    (http://jdbm.sourceforge.net/).
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS
+ * ``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
+ * CEES DE GROOT OR ANY 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.
+ *
+ * Copyright 2000 (C) Cees de Groot. All Rights Reserved.
+ * Contributions are Copyright (C) 2000 by their associated contributors.
+ *
+ * $Id: PageCursor.java,v 1.1 2000/05/06 00:00:31 boisvert Exp $
+ */
+
+package jdbm.recman;
+
+import java.io.IOException;
+
+/**
+ *  This class provides a cursor that can follow lists of pages
+ *  bi-directionally.
+ */
+final class PageCursor {
+    PageManager pageman;
+    long current;
+    short type;
+    
+    /**
+     *  Constructs a page cursor that starts at the indicated block.
+     */
+    PageCursor(PageManager pageman, long current) {
+        this.pageman = pageman;
+        this.current = current;
+    }
+    
+    /**
+     *  Constructs a page cursor that starts at the first block
+     *  of the indicated list.
+     */
+    PageCursor(PageManager pageman, short type) throws IOException {
+        this.pageman = pageman;
+        this.type = type;
+    }
+    
+    /**
+     *  Returns the current value of the cursor.
+     */
+    long getCurrent() throws IOException {
+        return current;
+    }
+    
+    /**
+     *  Returns the next value of the cursor
+     */
+    long next() throws IOException {
+        if (current == 0)
+            current = pageman.getFirst(type);
+        else
+            current = pageman.getNext(current);
+        return current;
+    } 
+    
+    /**
+     *  Returns the previous value of the cursor
+     */
+    long prev() throws IOException {
+        current = pageman.getPrev(current);
+        return current;
+    }
+}
diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/PageHeader.java b/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/PageHeader.java
new file mode 100644
index 0000000..eab2453
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/PageHeader.java
@@ -0,0 +1,156 @@
+/**
+ * JDBM LICENSE v1.00
+ *
+ * Redistribution and use of this software and associated documentation
+ * ("Software"), with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain copyright
+ *    statements and notices.  Redistributions must also contain a
+ *    copy of this document.
+ *
+ * 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 name "JDBM" must not be used to endorse or promote
+ *    products derived from this Software without prior written
+ *    permission of Cees de Groot.  For written permission,
+ *    please contact cg@cdegroot.com.
+ *
+ * 4. Products derived from this Software may not be called "JDBM"
+ *    nor may "JDBM" appear in their names without prior written
+ *    permission of Cees de Groot. 
+ *
+ * 5. Due credit should be given to the JDBM Project
+ *    (http://jdbm.sourceforge.net/).
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS
+ * ``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
+ * CEES DE GROOT OR ANY 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.
+ *
+ * Copyright 2000 (C) Cees de Groot. All Rights Reserved.
+ * Contributions are Copyright (C) 2000 by their associated contributors.
+ *
+ * $Id: PageHeader.java,v 1.2 2003/09/21 15:47:01 boisvert Exp $
+ */
+
+package jdbm.recman;
+
+/**
+ *  This class represents a page header. It is the common superclass for
+ *  all different page views.
+ */
+public class PageHeader implements BlockView {
+    // offsets
+    private static final short O_MAGIC = 0; // short magic
+    private static final short O_NEXT = Magic.SZ_SHORT;  // long next
+    private static final short O_PREV = O_NEXT + Magic.SZ_LONG; // long prev
+    protected static final short SIZE = O_PREV + Magic.SZ_LONG;
+
+    // my block
+    protected BlockIo block;
+
+    /**
+     *  Constructs a PageHeader object from a block
+     *
+     *  @param block The block that contains the file header
+     *  @throws IOException if the block is too short to keep the file
+     *          header.
+     */
+    protected PageHeader(BlockIo block) {
+        initialize(block);
+        if (!magicOk())
+            throw new Error("CRITICAL: page header magic for block "
+                            + block.getBlockId() + " not OK "
+                            + getMagic());
+    }
+    
+    /**
+     *  Constructs a new PageHeader of the indicated type. Used for newly
+     *  created pages.
+     */
+    PageHeader(BlockIo block, short type) {
+        initialize(block);
+        setType(type);
+    }
+    
+    /**
+     *  Factory method to create or return a page header for the
+     *  indicated block.
+     */
+    static PageHeader getView(BlockIo block) {
+        BlockView view = block.getView();
+        if (view != null && view instanceof PageHeader)
+            return (PageHeader) view;
+        else
+            return new PageHeader(block);
+    }
+    
+    private void initialize(BlockIo block) {
+        this.block = block;
+        block.setView(this);
+    }
+    
+    /**
+     *  Returns true if the magic corresponds with the fileHeader magic.
+     */
+    private boolean magicOk() {
+        int magic = getMagic();
+        return magic >= Magic.BLOCK
+            && magic <= (Magic.BLOCK + Magic.FREEPHYSIDS_PAGE);
+    }
+    
+    /**
+     *  For paranoia mode
+     */
+    protected void paranoiaMagicOk() {
+        if (!magicOk())
+            throw new Error("CRITICAL: page header magic not OK "
+                            + getMagic());
+    }
+    
+    /** Returns the magic code */
+    short getMagic() {
+        return block.readShort(O_MAGIC);
+    }
+
+    /** Returns the next block. */
+    long getNext() {
+        paranoiaMagicOk();
+        return block.readLong(O_NEXT);
+    }
+    
+    /** Sets the next block. */
+    void setNext(long next) {
+        paranoiaMagicOk();
+        block.writeLong(O_NEXT, next);
+    }
+    
+    /** Returns the previous block. */
+    long getPrev() {
+        paranoiaMagicOk();
+        return block.readLong(O_PREV);
+    }
+    
+    /** Sets the previous block. */
+    void setPrev(long prev) {
+        paranoiaMagicOk();
+        block.writeLong(O_PREV, prev);
+    }
+    
+    /** Sets the type of the page header */
+    void setType(short type) {
+        block.writeShort(O_MAGIC, (short) (Magic.BLOCK + type));
+    }
+}
diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/PageManager.java b/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/PageManager.java
new file mode 100644
index 0000000..c43a40f
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/PageManager.java
@@ -0,0 +1,272 @@
+/**
+ * JDBM LICENSE v1.00
+ *
+ * Redistribution and use of this software and associated documentation
+ * ("Software"), with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain copyright
+ *    statements and notices.  Redistributions must also contain a
+ *    copy of this document.
+ *
+ * 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 name "JDBM" must not be used to endorse or promote
+ *    products derived from this Software without prior written
+ *    permission of Cees de Groot.  For written permission,
+ *    please contact cg@cdegroot.com.
+ *
+ * 4. Products derived from this Software may not be called "JDBM"
+ *    nor may "JDBM" appear in their names without prior written
+ *    permission of Cees de Groot. 
+ *
+ * 5. Due credit should be given to the JDBM Project
+ *    (http://jdbm.sourceforge.net/).
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS
+ * ``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
+ * CEES DE GROOT OR ANY 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.
+ *
+ * Copyright 2000 (C) Cees de Groot. All Rights Reserved.
+ * Contributions are Copyright (C) 2000 by their associated contributors.
+ *
+ * $Id: PageManager.java,v 1.3 2005/06/25 23:12:32 doomdark Exp $
+ */
+
+package jdbm.recman;
+
+import java.io.*;
+
+/**
+ *  This class manages the linked lists of pages that make up a file.
+ */
+final class PageManager {
+    // our record file
+    private RecordFile file;
+    // header data
+    private FileHeader header;
+    private BlockIo headerBuf;
+    
+    /**
+     *  Creates a new page manager using the indicated record file.
+     */
+    PageManager(RecordFile file) throws IOException {
+        this.file = file;
+        
+        // check the file header. If the magic is 0, we assume a new
+        // file. Note that we hold on to the file header node.
+        headerBuf = file.get(0);
+        if (headerBuf.readShort(0) == 0)
+            header = new FileHeader(headerBuf, true);
+        else
+            header = new FileHeader(headerBuf, false);
+    }
+    
+    /**
+     *  Allocates a page of the indicated type. Returns recid of the
+     *  page.
+     */
+    long allocate(short type) throws IOException {
+        
+        if (type == Magic.FREE_PAGE)
+            throw new Error("allocate of free page?");
+        
+        // do we have something on the free list?
+        long retval = header.getFirstOf(Magic.FREE_PAGE);
+        boolean isNew = false;
+        if (retval != 0) {
+            // yes. Point to it and make the next of that page the
+            // new first free page.
+            header.setFirstOf(Magic.FREE_PAGE, getNext(retval));
+        }
+        else {
+            // nope. make a new record
+            retval = header.getLastOf(Magic.FREE_PAGE);
+            if (retval == 0)
+                // very new file - allocate record #1
+                retval = 1;
+            header.setLastOf(Magic.FREE_PAGE, retval + 1);
+            isNew = true;
+        }
+        
+        // Cool. We have a record, add it to the correct list
+        BlockIo buf = file.get(retval);
+        PageHeader pageHdr = isNew ? new PageHeader(buf, type) 
+            : PageHeader.getView(buf);
+        long oldLast = header.getLastOf(type);
+        
+        // Clean data.
+        System.arraycopy(RecordFile.cleanData, 0, 
+                         buf.getData(), 0, 
+                         RecordFile.BLOCK_SIZE);
+        pageHdr.setType(type);
+        pageHdr.setPrev(oldLast);
+        pageHdr.setNext(0);
+        
+        
+        if (oldLast == 0)
+            // This was the first one of this type
+            header.setFirstOf(type, retval);
+        header.setLastOf(type, retval);
+        file.release(retval, true);
+        
+        // If there's a previous, fix up its pointer
+        if (oldLast != 0) {
+            buf = file.get(oldLast);
+            pageHdr = PageHeader.getView(buf);
+            pageHdr.setNext(retval);
+            file.release(oldLast, true);
+        }
+        
+        // remove the view, we have modified the type.
+        buf.setView(null);
+        
+        return retval;
+    }
+    
+    /**
+     *  Frees a page of the indicated type.
+     */
+    void free(short type, long recid) throws IOException {
+        if (type == Magic.FREE_PAGE)
+            throw new Error("free free page?");
+        if (recid == 0)
+            throw new Error("free header page?");
+        
+        // get the page and read next and previous pointers
+        BlockIo buf = file.get(recid);
+        PageHeader pageHdr = PageHeader.getView(buf);
+        long prev = pageHdr.getPrev();
+        long next = pageHdr.getNext();
+        
+        // put the page at the front of the free list.
+        pageHdr.setType(Magic.FREE_PAGE);
+        pageHdr.setNext(header.getFirstOf(Magic.FREE_PAGE));
+        pageHdr.setPrev(0);
+        
+        header.setFirstOf(Magic.FREE_PAGE, recid);
+        file.release(recid, true);
+        
+        // remove the page from its old list
+        if (prev != 0) {
+            buf = file.get(prev);
+            pageHdr = PageHeader.getView(buf);
+            pageHdr.setNext(next);
+            file.release(prev, true);
+        }
+        else {
+            header.setFirstOf(type, next);
+        }
+        if (next != 0) {
+            buf = file.get(next);
+            pageHdr = PageHeader.getView(buf);
+            pageHdr.setPrev(prev);
+            file.release(next, true);
+        }
+        else {
+            header.setLastOf(type, prev);
+        }
+        
+    }
+    
+    
+    /**
+     *  Returns the page following the indicated block
+     */
+    long getNext(long block) throws IOException {
+        try {
+            return PageHeader.getView(file.get(block)).getNext();
+        } finally {
+            file.release(block, false);
+        }
+    }
+    
+    /**
+     *  Returns the page before the indicated block
+     */
+    long getPrev(long block) throws IOException {
+        try {
+            return PageHeader.getView(file.get(block)).getPrev();
+        } finally {
+            file.release(block, false);
+        }
+    }
+    
+    /**
+     *  Returns the first page on the indicated list.
+     */
+    long getFirst(short type) throws IOException {
+        return header.getFirstOf(type);
+    }
+
+    /**
+     *  Returns the last page on the indicated list.
+     */
+    long getLast(short type) throws IOException {
+        return header.getLastOf(type);
+    }
+    
+    
+    /**
+     *  Commit all pending (in-memory) data by flushing the page manager.
+     *  This forces a flush of all outstanding blocks (this it's an implicit
+     *  {@link RecordFile#commit} as well).
+     */
+    void commit() throws IOException {
+        // write the header out
+        file.release(headerBuf);
+        file.commit();
+
+        // and obtain it again
+        headerBuf = file.get(0);
+        header = new FileHeader(headerBuf, false);
+    }
+
+    /**
+     *  Flushes the page manager. This forces a flush of all outstanding
+     *  blocks (this it's an implicit {@link RecordFile#commit} as well).
+     */
+    void rollback() throws IOException {
+        // release header
+        file.discard(headerBuf);
+        file.rollback();
+        // and obtain it again
+        headerBuf = file.get(0);
+        if (headerBuf.readShort(0) == 0)
+            header = new FileHeader(headerBuf, true);
+        else
+            header = new FileHeader(headerBuf, false);
+    }
+    
+    /**
+     *  Closes the page manager. This flushes the page manager and releases
+     *  the lock on the header.
+     */
+    void close() throws IOException {   
+        file.release(headerBuf);
+        file.commit();
+        headerBuf = null;
+        header = null;
+        file = null;
+    }
+    
+    /**
+     *  Returns the file header.
+     */
+    FileHeader getFileHeader() {
+        return header;
+    }
+    
+}
diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/PhysicalRowId.java b/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/PhysicalRowId.java
new file mode 100644
index 0000000..5b3432d
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/PhysicalRowId.java
@@ -0,0 +1,95 @@
+/**
+ * JDBM LICENSE v1.00
+ *
+ * Redistribution and use of this software and associated documentation
+ * ("Software"), with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain copyright
+ *    statements and notices.  Redistributions must also contain a
+ *    copy of this document.
+ *
+ * 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 name "JDBM" must not be used to endorse or promote
+ *    products derived from this Software without prior written
+ *    permission of Cees de Groot.  For written permission,
+ *    please contact cg@cdegroot.com.
+ *
+ * 4. Products derived from this Software may not be called "JDBM"
+ *    nor may "JDBM" appear in their names without prior written
+ *    permission of Cees de Groot. 
+ *
+ * 5. Due credit should be given to the JDBM Project
+ *    (http://jdbm.sourceforge.net/).
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS
+ * ``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
+ * CEES DE GROOT OR ANY 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.
+ *
+ * Copyright 2000 (C) Cees de Groot. All Rights Reserved.
+ * Contributions are Copyright (C) 2000 by their associated contributors.
+ *
+ * $Id: PhysicalRowId.java,v 1.1 2000/05/06 00:00:31 boisvert Exp $
+ */
+
+package jdbm.recman;
+
+/**
+ *  A physical rowid is nothing else than a pointer to a physical location
+ *  in a file - a (block, offset) tuple.
+ *  <P>
+ *  <B>Note</B>: The fact that the offset is modelled as a short limits 
+ *  the block size to 32k.
+ */
+class PhysicalRowId {
+    // offsets
+    private static final short O_BLOCK = 0; // long block
+    private static final short O_OFFSET = Magic.SZ_LONG; // short offset
+    static final int SIZE = O_OFFSET + Magic.SZ_SHORT;
+    
+    // my block and the position within the block
+    BlockIo block;
+    short pos;
+
+    /**
+     *  Constructs a physical rowid from the indicated data starting at
+     *  the indicated position.
+     */
+    PhysicalRowId(BlockIo block, short pos) {
+        this.block = block;
+        this.pos = pos;
+    }
+    
+    /** Returns the block number */
+    long getBlock() {
+        return block.readLong(pos + O_BLOCK);
+    }
+    
+    /** Sets the block number */
+    void setBlock(long value) {
+        block.writeLong(pos + O_BLOCK, value);
+    }
+    
+    /** Returns the offset */
+    short getOffset() {
+        return block.readShort(pos + O_OFFSET);
+    }
+    
+    /** Sets the offset */
+    void setOffset(short value) {
+        block.writeShort(pos + O_OFFSET, value);
+    }
+}
diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/PhysicalRowIdManager.java b/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/PhysicalRowIdManager.java
new file mode 100644
index 0000000..eb2539b
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/PhysicalRowIdManager.java
@@ -0,0 +1,338 @@
+/**

+ * JDBM LICENSE v1.00

+ *

+ * Redistribution and use of this software and associated documentation

+ * ("Software"), with or without modification, are permitted provided

+ * that the following conditions are met:

+ *

+ * 1. Redistributions of source code must retain copyright

+ *    statements and notices.  Redistributions must also contain a

+ *    copy of this document.

+ *

+ * 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 name "JDBM" must not be used to endorse or promote

+ *    products derived from this Software without prior written

+ *    permission of Cees de Groot.  For written permission,

+ *    please contact cg@cdegroot.com.

+ *

+ * 4. Products derived from this Software may not be called "JDBM"

+ *    nor may "JDBM" appear in their names without prior written

+ *    permission of Cees de Groot.

+ *

+ * 5. Due credit should be given to the JDBM Project

+ *    (http://jdbm.sourceforge.net/).

+ *

+ * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS

+ * ``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

+ * CEES DE GROOT OR ANY 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.

+ *

+ * Copyright 2000 (C) Cees de Groot. All Rights Reserved.

+ * Contributions are Copyright (C) 2000 by their associated contributors.

+ *

+ * $Id: PhysicalRowIdManager.java,v 1.3 2003/03/21 03:00:09 boisvert Exp $

+ */

+

+package jdbm.recman;

+

+import java.io.IOException;

+

+/**

+ *  This class manages physical row ids, and their data.

+ */

+final class PhysicalRowIdManager

+{

+

+    // The file we're talking to and the associated page manager.

+    private RecordFile file;

+    private PageManager pageman;

+    private FreePhysicalRowIdPageManager freeman;

+

+    /**

+     *  Creates a new rowid manager using the indicated record file.

+     *  and page manager.

+     */

+    PhysicalRowIdManager( RecordFile file, PageManager pageManager )

+        throws IOException

+    {

+        this.file = file;

+        this.pageman = pageManager;

+        this.freeman = new FreePhysicalRowIdPageManager(file, pageman);

+    }

+

+    /**

+     *  Inserts a new record. Returns the new physical rowid.

+     */

+    Location insert( byte[] data, int start, int length )

+        throws IOException

+    {

+        Location retval = alloc( length );

+        write( retval, data, start, length );

+        return retval;

+    }

+

+    /**

+     *  Updates an existing record. Returns the possibly changed

+     *  physical rowid.

+     */

+    Location update( Location rowid, byte[] data, int start, int length )

+        throws IOException

+    {

+        // fetch the record header

+        BlockIo block = file.get( rowid.getBlock() );

+        RecordHeader head = new RecordHeader( block, rowid.getOffset() );

+        if ( length > head.getAvailableSize() ) {

+            // not enough space - we need to copy to a new rowid.

+            file.release( block );

+            free( rowid );

+            rowid = alloc( length );

+        } else {

+            file.release( block );

+        }

+

+        // 'nuff space, write it in and return the rowid.

+        write( rowid, data, start, length );

+        return rowid;

+    }

+

+    /**

+     *  Deletes a record.

+     */

+    void delete( Location rowid )

+        throws IOException

+    {

+        free( rowid );

+    }

+

+    /**

+     *  Retrieves a record.

+     */

+    byte[] fetch( Location rowid )

+        throws IOException 

+    {

+        // fetch the record header

+        PageCursor curs = new PageCursor( pageman, rowid.getBlock() );

+        BlockIo block = file.get( curs.getCurrent() );

+        RecordHeader head = new RecordHeader( block, rowid.getOffset() );

+

+        // allocate a return buffer

+        byte[] retval = new byte[ head.getCurrentSize() ];

+        if ( retval.length == 0 ) {

+            file.release( curs.getCurrent(), false );

+            return retval;

+        }

+

+        // copy bytes in

+        int offsetInBuffer = 0;

+        int leftToRead = retval.length;

+        short dataOffset = (short) (rowid.getOffset() + RecordHeader.SIZE);

+        while ( leftToRead > 0 ) {

+            // copy current page's data to return buffer

+            int toCopy = RecordFile.BLOCK_SIZE - dataOffset;

+            if ( leftToRead < toCopy ) {

+                toCopy = leftToRead;

+            }

+            System.arraycopy( block.getData(), dataOffset,

+                              retval, offsetInBuffer,

+                              toCopy );

+

+            // Go to the next block

+            leftToRead -= toCopy;

+            offsetInBuffer += toCopy;

+

+            file.release( block );

+

+            if ( leftToRead > 0 ) {

+                block = file.get( curs.next() );

+                dataOffset = DataPage.O_DATA;

+            }

+

+        }

+

+        return retval;

+    }

+

+    /**

+     *  Allocate a new rowid with the indicated size.

+     */

+    private Location alloc( int size )

+        throws IOException

+    {

+        Location retval = freeman.get( size );

+        if ( retval == null ) {

+            retval = allocNew( size, pageman.getLast( Magic.USED_PAGE ) );

+        }

+        return retval;

+    }

+

+    /**

+     *  Allocates a new rowid. The second parameter is there to

+     *  allow for a recursive call - it indicates where the search

+     *  should start.

+     */

+    private Location allocNew( int size, long start )

+        throws IOException

+    {

+        BlockIo curBlock;

+        DataPage curPage;

+        if ( start == 0 ) {

+            // we need to create a new page.

+            start = pageman.allocate( Magic.USED_PAGE );

+            curBlock = file.get( start );

+            curPage = DataPage.getDataPageView( curBlock );

+            curPage.setFirst( DataPage.O_DATA );

+            RecordHeader hdr = new RecordHeader( curBlock, DataPage.O_DATA );

+            hdr.setAvailableSize( 0 );

+            hdr.setCurrentSize( 0 );

+        } else {

+            curBlock = file.get( start );

+            curPage = DataPage.getDataPageView( curBlock );

+        }

+

+        // follow the rowids on this page to get to the last one. We don't

+        // fall off, because this is the last page, remember?

+        short pos = curPage.getFirst();

+        if ( pos == 0 ) {

+            // page is exactly filled by the last block of a record

+            file.release( curBlock );

+            return allocNew( size, 0 );

+        }

+

+        RecordHeader hdr = new RecordHeader( curBlock, pos );

+        while ( hdr.getAvailableSize() != 0 && pos < RecordFile.BLOCK_SIZE ) {

+            pos += hdr.getAvailableSize() + RecordHeader.SIZE;

+            if ( pos == RecordFile.BLOCK_SIZE ) {

+                // Again, a filled page.

+                file.release( curBlock );

+                return allocNew( size, 0 );

+            }

+

+            hdr = new RecordHeader( curBlock, pos );

+        }

+

+        if ( pos == RecordHeader.SIZE ) {

+            // the last record exactly filled the page. Restart forcing

+            // a new page.

+            file.release( curBlock );

+        }

+

+        // we have the position, now tack on extra pages until we've got

+        // enough space.

+        Location retval = new Location( start, pos );

+        int freeHere = RecordFile.BLOCK_SIZE - pos - RecordHeader.SIZE;

+        if ( freeHere < size ) {

+            // check whether the last page would have only a small bit left.

+            // if yes, increase the allocation. A small bit is a record

+            // header plus 16 bytes.

+            int lastSize = (size - freeHere) % DataPage.DATA_PER_PAGE;

+            if (( DataPage.DATA_PER_PAGE - lastSize ) < (RecordHeader.SIZE + 16) ) {

+                size += (DataPage.DATA_PER_PAGE - lastSize);

+            }

+

+            // write out the header now so we don't have to come back.

+            hdr.setAvailableSize( size );

+            file.release( start, true );

+

+            int neededLeft = size - freeHere;

+            // Refactor these two blocks!

+            while ( neededLeft >= DataPage.DATA_PER_PAGE ) {

+                start = pageman.allocate( Magic.USED_PAGE );

+                curBlock = file.get( start );

+                curPage = DataPage.getDataPageView( curBlock );

+                curPage.setFirst( (short) 0 ); // no rowids, just data

+                file.release( start, true );

+                neededLeft -= DataPage.DATA_PER_PAGE;

+            }

+            if ( neededLeft > 0 ) {

+                // done with whole chunks, allocate last fragment.

+                start = pageman.allocate( Magic.USED_PAGE );

+                curBlock = file.get( start );

+                curPage = DataPage.getDataPageView( curBlock );

+                curPage.setFirst( (short) (DataPage.O_DATA + neededLeft) );

+                file.release( start, true );

+            }

+        } else {

+            // just update the current page. If there's less than 16 bytes

+            // left, we increase the allocation (16 bytes is an arbitrary

+            // number).

+            if ( freeHere - size <= (16 + RecordHeader.SIZE) ) {

+                size = freeHere;

+            }

+            hdr.setAvailableSize( size );

+            file.release( start, true );

+        }

+        return retval;

+

+    }

+

+

+    private void free( Location id )

+        throws IOException

+    {

+        // get the rowid, and write a zero current size into it.

+        BlockIo curBlock = file.get( id.getBlock() );

+        DataPage curPage = DataPage.getDataPageView( curBlock );

+        RecordHeader hdr = new RecordHeader( curBlock, id.getOffset() );

+        hdr.setCurrentSize( 0 );

+        file.release( id.getBlock(), true );

+

+        // write the rowid to the free list

+        freeman.put( id, hdr.getAvailableSize() );

+    }

+

+    /**

+     *  Writes out data to a rowid. Assumes that any resizing has been

+     *  done.

+     */

+    private void write(Location rowid, byte[] data, int start, int length )

+        throws IOException

+    {

+        PageCursor curs = new PageCursor( pageman, rowid.getBlock() );

+        BlockIo block = file.get( curs.getCurrent() );

+        RecordHeader hdr = new RecordHeader( block, rowid.getOffset() );

+        hdr.setCurrentSize( length );

+        if ( length == 0 ) {

+            file.release( curs.getCurrent(), true );

+            return;

+        }

+

+        // copy bytes in

+        int offsetInBuffer = start;

+        int leftToWrite = length;

+        short dataOffset = (short) (rowid.getOffset() + RecordHeader.SIZE);

+        while ( leftToWrite > 0 ) {

+            // copy current page's data to return buffer

+            int toCopy = RecordFile.BLOCK_SIZE - dataOffset;

+

+            if ( leftToWrite < toCopy ) {

+                toCopy = leftToWrite;

+            }

+            System.arraycopy( data, offsetInBuffer, block.getData(), 

+                              dataOffset, toCopy );

+

+            // Go to the next block

+            leftToWrite -= toCopy;

+            offsetInBuffer += toCopy;

+

+            file.release( curs.getCurrent(), true );

+

+            if ( leftToWrite > 0 ) {

+                block = file.get( curs.next() );

+                dataOffset = DataPage.O_DATA;

+            }

+        }

+    }

+}

+

diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/Provider.java b/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/Provider.java
new file mode 100644
index 0000000..d45746a
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/Provider.java
@@ -0,0 +1,116 @@
+/**

+ * JDBM LICENSE v1.00

+ *

+ * Redistribution and use of this software and associated documentation

+ * ("Software"), with or without modification, are permitted provided

+ * that the following conditions are met:

+ *

+ * 1. Redistributions of source code must retain copyright

+ *    statements and notices.  Redistributions must also contain a

+ *    copy of this document.

+ *

+ * 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 name "JDBM" must not be used to endorse or promote

+ *    products derived from this Software without prior written

+ *    permission of Cees de Groot.  For written permission,

+ *    please contact cg@cdegroot.com.

+ *

+ * 4. Products derived from this Software may not be called "JDBM"

+ *    nor may "JDBM" appear in their names without prior written

+ *    permission of Cees de Groot.

+ *

+ * 5. Due credit should be given to the JDBM Project

+ *    (http://jdbm.sourceforge.net/).

+ *

+ * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS

+ * ``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

+ * CEES DE GROOT OR ANY 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.

+ *

+ * Copyright 2000 (C) Cees de Groot. All Rights Reserved.

+ * Copyright 2000-2001 (C) Alex Boisvert. All Rights Reserved.

+ * Contributions are Copyright (C) 2000 by their associated contributors.

+ *

+ * $Id: Provider.java,v 1.3 2005/06/25 23:12:32 doomdark Exp $

+ */

+

+package jdbm.recman;

+

+import java.io.IOException;

+import java.util.Properties;

+

+import jdbm.RecordManager;

+import jdbm.RecordManagerOptions;

+import jdbm.RecordManagerProvider;

+

+import jdbm.helper.MRU;

+

+/**

+ * Provider of the default RecordManager implementation.

+ *

+ * @author <a href="mailto:boisvert@intalio.com">Alex Boisvert</a>

+ * @version $Id: Provider.java,v 1.3 2005/06/25 23:12:32 doomdark Exp $

+ */

+public final class Provider

+    implements RecordManagerProvider

+{

+

+    /**

+     * Create a default implementation record manager.

+     *

+     * @param name Name of the record file.

+     * @param options Record manager options.

+     * @throws IOException if an I/O related exception occurs while creating

+     *                    or opening the record manager.

+     * @throws UnsupportedOperationException if some options are not supported by the

+     *                                      implementation.

+     * @throws IllegalArgumentException if some options are invalid.

+     */

+    public RecordManager createRecordManager( String name,

+                                              Properties options )

+        throws IOException

+    {

+        RecordManager  recman;

+        String         value;

+        int            cacheSize;

+

+        recman = new BaseRecordManager( name );

+

+        value = options.getProperty( RecordManagerOptions.DISABLE_TRANSACTIONS, "false" );

+        if ( value.equalsIgnoreCase( "TRUE" ) ) {

+            ( (BaseRecordManager) recman ).disableTransactions();

+        }

+

+        value = options.getProperty( RecordManagerOptions.CACHE_SIZE, "1000" );

+        cacheSize = Integer.parseInt( value );

+

+        value = options.getProperty( RecordManagerOptions.CACHE_TYPE,

+                                     RecordManagerOptions.NORMAL_CACHE );

+        if ( value.equalsIgnoreCase( RecordManagerOptions.NORMAL_CACHE ) ) {

+            MRU cache = new MRU( cacheSize );

+            recman = new CacheRecordManager( recman, cache );

+        } else if ( value.equalsIgnoreCase( RecordManagerOptions.SOFT_REF_CACHE ) ) {

+            throw new IllegalArgumentException( "Soft reference cache not implemented" );

+        } else if ( value.equalsIgnoreCase( RecordManagerOptions.WEAK_REF_CACHE ) ) {

+            throw new IllegalArgumentException( "Weak reference cache not implemented" );

+        } else {

+            throw new IllegalArgumentException( "Invalid cache type: " + value );

+        }

+

+        return recman;

+    }

+

+

+}

diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/RecordCache.java b/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/RecordCache.java
new file mode 100644
index 0000000..7ce2108
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/RecordCache.java
@@ -0,0 +1,80 @@
+/**
+ * JDBM LICENSE v1.00
+ *
+ * Redistribution and use of this software and associated documentation
+ * ("Software"), with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain copyright
+ *    statements and notices.  Redistributions must also contain a
+ *    copy of this document.
+ *
+ * 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 name "JDBM" must not be used to endorse or promote
+ *    products derived from this Software without prior written
+ *    permission of Cees de Groot.  For written permission,
+ *    please contact cg@cdegroot.com.
+ *
+ * 4. Products derived from this Software may not be called "JDBM"
+ *    nor may "JDBM" appear in their names without prior written
+ *    permission of Cees de Groot. 
+ *
+ * 5. Due credit should be given to the JDBM Project
+ *    (http://jdbm.sourceforge.net/).
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS
+ * ``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
+ * CEES DE GROOT OR ANY 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.
+ *
+ * Copyright 2000 (C) Cees de Groot. All Rights Reserved.
+ * Contributions are Copyright (C) 2000 by their associated contributors.
+ *
+ * $Id: RecordCache.java,v 1.2 2005/06/25 23:12:32 doomdark Exp $
+ */
+
+package jdbm.recman;
+
+import java.io.IOException;
+
+/**
+ *  This interface is used for synchronization.
+ *  <p>
+ *  RecordManager ensures that the cache has the up-to-date information
+ *  by way of an invalidation protocol.
+ */
+public interface RecordCache {
+
+    /**
+     * Notification to flush content related to a given record.
+     */
+    public void flush(long recid) throws IOException;
+
+    /**
+     * Notification to flush data all of records.
+     */
+    public void flushAll() throws IOException;
+
+    /**
+     * Notification to invalidate content related to given record.
+     */
+    public void invalidate(long recid) throws IOException;
+
+    /**
+     * Notification to invalidate content of all records.
+     */
+    public void invalidateAll() throws IOException;
+
+}
diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/RecordFile.java b/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/RecordFile.java
new file mode 100644
index 0000000..6707a02
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/RecordFile.java
@@ -0,0 +1,412 @@
+/**
+ * JDBM LICENSE v1.00
+ *
+ * Redistribution and use of this software and associated documentation
+ * ("Software"), with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain copyright
+ *    statements and notices.  Redistributions must also contain a
+ *    copy of this document.
+ *
+ * 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 name "JDBM" must not be used to endorse or promote
+ *    products derived from this Software without prior written
+ *    permission of Cees de Groot.  For written permission,
+ *    please contact cg@cdegroot.com.
+ *
+ * 4. Products derived from this Software may not be called "JDBM"
+ *    nor may "JDBM" appear in their names without prior written
+ *    permission of Cees de Groot.
+ *
+ * 5. Due credit should be given to the JDBM Project
+ *    (http://jdbm.sourceforge.net/).
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS
+ * ``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
+ * CEES DE GROOT OR ANY 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.
+ *
+ * Copyright 2000 (C) Cees de Groot. All Rights Reserved.
+ * Contributions are Copyright (C) 2000 by their associated contributors.
+ *
+ * $Id: RecordFile.java,v 1.6 2005/06/25 23:12:32 doomdark Exp $
+ */
+
+package jdbm.recman;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ *  This class represents a random access file as a set of fixed size
+ *  records. Each record has a physical record number, and records are
+ *  cached in order to improve access.
+ *<p>
+ *  The set of dirty records on the in-use list constitutes a transaction.
+ *  Later on, we will send these records to some recovery thingy.
+ */
+public final class RecordFile {
+    final TransactionManager txnMgr;
+
+    // Todo: reorganize in hashes and fifos as necessary.
+    // free -> inUse -> dirty -> inTxn -> free
+    // free is a cache, thus a FIFO. The rest are hashes.
+    private final LinkedList free = new LinkedList();
+    private final HashMap inUse = new HashMap();
+    private final HashMap dirty = new HashMap();
+    private final HashMap inTxn = new HashMap();
+
+    // transactions disabled?
+    private boolean transactionsDisabled = false;
+
+    /** The length of a single block. */
+    public final static int BLOCK_SIZE = 8192;//4096;
+
+    /** The extension of a record file */
+    final static String extension = ".db";
+
+    /** A block of clean data to wipe clean pages. */
+    final static byte[] cleanData = new byte[BLOCK_SIZE];
+
+    private RandomAccessFile file;
+    private final String fileName;
+
+    /**
+     *  Creates a new object on the indicated filename. The file is
+     *  opened in read/write mode.
+     *
+     *  @param fileName the name of the file to open or create, without
+     *         an extension.
+     *  @throws IOException whenever the creation of the underlying
+     *          RandomAccessFile throws it.
+     */
+    RecordFile(String fileName) throws IOException {
+        this.fileName = fileName;
+        file = new RandomAccessFile(fileName + extension, "rw");
+        txnMgr = new TransactionManager(this);
+    }
+
+    /**
+     *  Returns the file name.
+     */
+    String getFileName() {
+        return fileName;
+    }
+
+    /**
+     *  Disables transactions: doesn't sync and doesn't use the
+     *  transaction manager.
+     */
+    void disableTransactions() {
+        transactionsDisabled = true;
+    }
+
+    /**
+     *  Gets a block from the file. The returned byte array is
+     *  the in-memory copy of the record, and thus can be written
+     *  (and subsequently released with a dirty flag in order to
+     *  write the block back).
+     *
+     *  @param blockid The record number to retrieve.
+     */
+     BlockIo get(long blockid) throws IOException {
+         Long key = new Long(blockid);
+
+         // try in transaction list, dirty list, free list
+         BlockIo node = (BlockIo) inTxn.get(key);
+         if (node != null) {
+             inTxn.remove(key);
+             inUse.put(key, node);
+             return node;
+         }
+         node = (BlockIo) dirty.get(key);
+         if (node != null) {
+             dirty.remove(key);
+             inUse.put(key, node);
+             return node;
+         }
+         for (Iterator i = free.iterator(); i.hasNext(); ) {
+             BlockIo cur = (BlockIo) i.next();
+             if (cur.getBlockId() == blockid) {
+                 node = cur;
+                 i.remove();
+                 inUse.put(key, node);
+                 return node;
+             }
+         }
+
+         // sanity check: can't be on in use list
+         if (inUse.get(key) != null) {
+             throw new Error("double get for block " + blockid);
+         }
+
+         // get a new node and read it from the file
+         node = getNewNode(blockid);
+         long offset = blockid * BLOCK_SIZE;
+         if (file.length() > 0 && offset <= file.length()) {
+             read(file, offset, node.getData(), BLOCK_SIZE);
+         } else {
+             System.arraycopy(cleanData, 0, node.getData(), 0, BLOCK_SIZE);
+         }
+         inUse.put(key, node);
+         node.setClean();
+         return node;
+     }
+
+
+    /**
+     *  Releases a block.
+     *
+     *  @param blockid The record number to release.
+     *  @param isDirty If true, the block was modified since the get().
+     */
+    void release(long blockid, boolean isDirty)
+    throws IOException {
+        BlockIo node = (BlockIo) inUse.get(new Long(blockid));
+        if (node == null)
+            throw new IOException("bad blockid " + blockid + " on release");
+        if (!node.isDirty() && isDirty)
+            node.setDirty();
+        release(node);
+    }
+
+    /**
+     *  Releases a block.
+     *
+     *  @param block The block to release.
+     */
+    void release(BlockIo block) {
+        Long key = new Long(block.getBlockId());
+        inUse.remove(key);
+        if (block.isDirty()) {
+            // System.out.println( "Dirty: " + key + block );
+            dirty.put(key, block);
+        } else {
+            if (!transactionsDisabled && block.isInTransaction()) {
+                inTxn.put(key, block);
+            } else {
+                free.add(block);
+            }
+        }
+    }
+
+    /**
+     *  Discards a block (will not write the block even if it's dirty)
+     *
+     *  @param block The block to discard.
+     */
+    void discard(BlockIo block) {
+        Long key = new Long(block.getBlockId());
+        inUse.remove(key);
+
+        // note: block not added to free list on purpose, because
+        //       it's considered invalid
+    }
+
+    /**
+     *  Commits the current transaction by flushing all dirty buffers
+     *  to disk.
+     */
+    void commit() throws IOException {
+        // debugging...
+        if (!inUse.isEmpty() && inUse.size() > 1) {
+            showList(inUse.values().iterator());
+            throw new Error("in use list not empty at commit time ("
+                            + inUse.size() + ")");
+        }
+
+        //  System.out.println("committing...");
+
+        if ( dirty.size() == 0 ) {
+            // if no dirty blocks, skip commit process
+            return;
+        }
+
+        if (!transactionsDisabled) {
+            txnMgr.start();
+        }
+
+        for (Iterator i = dirty.values().iterator(); i.hasNext(); ) {
+            BlockIo node = (BlockIo) i.next();
+            i.remove();
+            // System.out.println("node " + node + " map size now " + dirty.size());
+            if (transactionsDisabled) {
+                long offset = node.getBlockId() * BLOCK_SIZE;
+                file.seek(offset);
+                file.write(node.getData());
+                node.setClean();
+                free.add(node);
+            }
+            else {
+                txnMgr.add(node);
+                inTxn.put(new Long(node.getBlockId()), node);
+            }
+        }
+        if (!transactionsDisabled) {
+            txnMgr.commit();
+        }
+    }
+
+    /**
+     *  Rollback the current transaction by discarding all dirty buffers
+     */
+    void rollback() throws IOException {
+        // debugging...
+        if (!inUse.isEmpty()) {
+            showList(inUse.values().iterator());
+            throw new Error("in use list not empty at rollback time ("
+                            + inUse.size() + ")");
+        }
+        //  System.out.println("rollback...");
+        dirty.clear();
+
+        txnMgr.synchronizeLogFromDisk();
+
+        if (!inTxn.isEmpty()) {
+            showList(inTxn.values().iterator());
+            throw new Error("in txn list not empty at rollback time ("
+                            + inTxn.size() + ")");
+        };
+    }
+
+    /**
+     *  Commits and closes file.
+     */
+    void close() throws IOException {
+        if (!dirty.isEmpty()) {
+            commit();
+        }
+        txnMgr.shutdown();
+
+        if (!inTxn.isEmpty()) {
+            showList(inTxn.values().iterator());
+            throw new Error("In transaction not empty");
+        }
+
+        // these actually ain't that bad in a production release
+        if (!dirty.isEmpty()) {
+            System.out.println("ERROR: dirty blocks at close time");
+            showList(dirty.values().iterator());
+            throw new Error("Dirty blocks at close time");
+        }
+        if (!inUse.isEmpty()) {
+            System.out.println("ERROR: inUse blocks at close time");
+            showList(inUse.values().iterator());
+            throw new Error("inUse blocks at close time");
+        }
+
+        // debugging stuff to keep an eye on the free list
+        // System.out.println("Free list size:" + free.size());
+        file.close();
+        file = null;
+    }
+
+
+    /**
+     * Force closing the file and underlying transaction manager.
+     * Used for testing purposed only.
+     */
+    void forceClose() throws IOException {
+      txnMgr.forceClose();
+      file.close();
+    }
+
+    /**
+     *  Prints contents of a list
+     */
+    private void showList(Iterator i) {
+        int cnt = 0;
+        while (i.hasNext()) {
+            System.out.println("elem " + cnt + ": " + i.next());
+            cnt++;
+        }
+    }
+
+
+    /**
+     *  Returns a new node. The node is retrieved (and removed)
+     *  from the released list or created new.
+     */
+    private BlockIo getNewNode(long blockid)
+    throws IOException {
+
+        BlockIo retval = null;
+        if (!free.isEmpty()) {
+            retval = (BlockIo) free.removeFirst();
+        }
+        if (retval == null)
+            retval = new BlockIo(0, new byte[BLOCK_SIZE]);
+
+        retval.setBlockId(blockid);
+        retval.setView(null);
+        return retval;
+    }
+
+    /**
+     *  Synchs a node to disk. This is called by the transaction manager's
+     *  synchronization code.
+     */
+    void synch(BlockIo node) throws IOException {
+        byte[] data = node.getData();
+        if (data != null) {
+            long offset = node.getBlockId() * BLOCK_SIZE;
+            file.seek(offset);
+            file.write(data);
+        }
+    }
+
+    /**
+     *  Releases a node from the transaction list, if it was sitting
+     *  there.
+     *
+     *  @param recycle true if block data can be reused
+     */
+    void releaseFromTransaction(BlockIo node, boolean recycle)
+    throws IOException {
+        Long key = new Long(node.getBlockId());
+        if ((inTxn.remove(key) != null) && recycle) {
+            free.add(node);
+        }
+    }
+
+    /**
+     *  Synchronizes the file.
+     */
+    void sync() throws IOException {
+        file.getFD().sync();
+    }
+
+
+    /**
+     * Utility method: Read a block from a RandomAccessFile
+     */
+    private static void read(RandomAccessFile file, long offset,
+                             byte[] buffer, int nBytes) throws IOException {
+        file.seek(offset);
+        int remaining = nBytes;
+        int pos = 0;
+        while (remaining > 0) {
+            int read = file.read(buffer, pos, remaining);
+            if (read == -1) {
+                System.arraycopy(cleanData, 0, buffer, pos, remaining);
+                break;
+            }
+            remaining -= read;
+            pos += read;
+        }
+    }
+
+}
diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/RecordHeader.java b/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/RecordHeader.java
new file mode 100644
index 0000000..d81759b
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/RecordHeader.java
@@ -0,0 +1,107 @@
+/**
+ * JDBM LICENSE v1.00
+ *
+ * Redistribution and use of this software and associated documentation
+ * ("Software"), with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain copyright
+ *    statements and notices.  Redistributions must also contain a
+ *    copy of this document.
+ *
+ * 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 name "JDBM" must not be used to endorse or promote
+ *    products derived from this Software without prior written
+ *    permission of Cees de Groot.  For written permission,
+ *    please contact cg@cdegroot.com.
+ *
+ * 4. Products derived from this Software may not be called "JDBM"
+ *    nor may "JDBM" appear in their names without prior written
+ *    permission of Cees de Groot. 
+ *
+ * 5. Due credit should be given to the JDBM Project
+ *    (http://jdbm.sourceforge.net/).
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS
+ * ``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
+ * CEES DE GROOT OR ANY 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.
+ *
+ * Copyright 2000 (C) Cees de Groot. All Rights Reserved.
+ * Contributions are Copyright (C) 2000 by their associated contributors.
+ *
+ * $Id: RecordHeader.java,v 1.1 2000/05/06 00:00:31 boisvert Exp $
+ */
+
+package jdbm.recman;
+
+/**
+ *  The data that comes at the start of a record of data. It stores 
+ *  both the current size and the avaliable size for the record - the latter
+ *  can be bigger than the former, which allows the record to grow without
+ *  needing to be moved and which allows the system to put small records
+ *  in larger free spots.
+ */
+class RecordHeader {
+    // offsets
+    private static final short O_CURRENTSIZE = 0; // int currentSize
+    private static final short O_AVAILABLESIZE = Magic.SZ_INT; // int availableSize
+    static final int SIZE = O_AVAILABLESIZE + Magic.SZ_INT;
+    
+    // my block and the position within the block
+    private BlockIo block;
+    private short pos;
+
+    /**
+     *  Constructs a record header from the indicated data starting at
+     *  the indicated position.
+     */
+    RecordHeader(BlockIo block, short pos) {
+        this.block = block;
+        this.pos = pos;
+        if (pos > (RecordFile.BLOCK_SIZE - SIZE))
+            throw new Error("Offset too large for record header (" 
+                            + block.getBlockId() + ":" 
+                            + pos + ")");
+    }
+
+    /** Returns the current size */
+    int getCurrentSize() {
+        return block.readInt(pos + O_CURRENTSIZE);
+    }
+    
+    /** Sets the current size */
+    void setCurrentSize(int value) {
+        block.writeInt(pos + O_CURRENTSIZE, value);
+    }
+    
+    /** Returns the available size */
+    int getAvailableSize() {
+        return block.readInt(pos + O_AVAILABLESIZE);
+    }
+    
+    /** Sets the available size */
+    void setAvailableSize(int value) {
+        block.writeInt(pos + O_AVAILABLESIZE, value);
+    }
+
+    // overrides java.lang.Object
+    public String toString() {
+        return "RH(" + block.getBlockId() + ":" + pos 
+            + ", avl=" + getAvailableSize()
+            + ", cur=" + getCurrentSize() 
+            + ")";
+    }
+}
diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/TransactionManager.java b/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/TransactionManager.java
new file mode 100644
index 0000000..7ab220e
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/TransactionManager.java
@@ -0,0 +1,409 @@
+/**
+ * JDBM LICENSE v1.00
+ *
+ * Redistribution and use of this software and associated documentation
+ * ("Software"), with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain copyright
+ *    statements and notices.  Redistributions must also contain a
+ *    copy of this document.
+ *
+ * 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 name "JDBM" must not be used to endorse or promote
+ *    products derived from this Software without prior written
+ *    permission of Cees de Groot.  For written permission,
+ *    please contact cg@cdegroot.com.
+ *
+ * 4. Products derived from this Software may not be called "JDBM"
+ *    nor may "JDBM" appear in their names without prior written
+ *    permission of Cees de Groot.
+ *
+ * 5. Due credit should be given to the JDBM Project
+ *    (http://jdbm.sourceforge.net/).
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS
+ * ``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
+ * CEES DE GROOT OR ANY 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.
+ *
+ * Copyright 2000 (C) Cees de Groot. All Rights Reserved.
+ * Contributions are Copyright (C) 2000 by their associated contributors.
+ *
+ * $Id: TransactionManager.java,v 1.7 2005/06/25 23:12:32 doomdark Exp $
+ */
+
+package jdbm.recman;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ *  This class manages the transaction log that belongs to every
+ *  {@link RecordFile}. The transaction log is either clean, or
+ *  in progress. In the latter case, the transaction manager
+ *  takes care of a roll forward.
+ *<p>
+ *  Implementation note: this is a proof-of-concept implementation
+ *  which hasn't been optimized for speed. For instance, all sorts
+ *  of streams are created for every transaction.
+ */
+// TODO: Handle the case where we are recovering lg9 and lg0, were we
+// should start with lg9 instead of lg0!
+
+public final class TransactionManager {
+    private RecordFile owner;
+
+    // streams for transaction log.
+    private FileOutputStream fos;
+    private ObjectOutputStream oos;
+
+    /** 
+     * By default, we keep 10 transactions in the log file before
+     * synchronizing it with the main database file.
+     */
+    static final int DEFAULT_TXNS_IN_LOG = 10;
+
+    /** 
+     * Maximum number of transactions before the log file is
+     * synchronized with the main database file.
+     */
+    private int _maxTxns = DEFAULT_TXNS_IN_LOG;
+
+    /**
+     * In-core copy of transactions. We could read everything back from
+     * the log file, but the RecordFile needs to keep the dirty blocks in
+     * core anyway, so we might as well point to them and spare us a lot
+     * of hassle.
+     */
+    private ArrayList[] txns = new ArrayList[DEFAULT_TXNS_IN_LOG];
+    private int curTxn = -1;
+
+    /** Extension of a log file. */
+    static final String extension = ".lg";
+
+    /**
+     *  Instantiates a transaction manager instance. If recovery
+     *  needs to be performed, it is done.
+     *
+     *  @param owner the RecordFile instance that owns this transaction mgr.
+     */
+    TransactionManager(RecordFile owner) throws IOException {
+        this.owner = owner;
+        recover();
+        open();
+    }
+
+    
+    /**
+     * Synchronize log file data with the main database file.
+     * <p>
+     * After this call, the main database file is guaranteed to be 
+     * consistent and guaranteed to be the only file needed for 
+     * backup purposes.
+     */
+    public void synchronizeLog()
+        throws IOException
+    {
+        synchronizeLogFromMemory();
+    }
+
+    
+    /**
+     * Set the maximum number of transactions to record in
+     * the log (and keep in memory) before the log is
+     * synchronized with the main database file.
+     * <p>
+     * This method must be called while there are no
+     * pending transactions in the log.
+     */
+    public void setMaximumTransactionsInLog( int maxTxns )
+        throws IOException
+    {
+        if ( maxTxns <= 0 ) {
+            throw new IllegalArgumentException( 
+                "Argument 'maxTxns' must be greater than 0." );
+        }
+        if ( curTxn != -1 ) {
+            throw new IllegalStateException( 
+                "Cannot change setting while transactions are pending in the log" );
+        }
+        _maxTxns = maxTxns;
+        txns = new ArrayList[ maxTxns ];
+    }
+
+    
+    /** Builds logfile name  */
+    private String makeLogName() {
+        return owner.getFileName() + extension;
+    }
+
+
+    /** Synchs in-core transactions to data file and opens a fresh log */
+    private void synchronizeLogFromMemory() throws IOException {
+        close();
+
+        TreeSet blockList = new TreeSet( new BlockIoComparator() );
+
+        int numBlocks = 0;
+        int writtenBlocks = 0;
+        for (int i = 0; i < _maxTxns; i++) {
+            if (txns[i] == null)
+                continue;
+            // Add each block to the blockList, replacing the old copy of this
+            // block if necessary, thus avoiding writing the same block twice
+            for (Iterator k = txns[i].iterator(); k.hasNext(); ) {
+                BlockIo block = (BlockIo)k.next();
+                if ( blockList.contains( block ) ) {
+                    block.decrementTransactionCount();
+                }
+                else {
+                    writtenBlocks++;
+                    boolean result = blockList.add( block );
+                }
+                numBlocks++;
+            }
+
+            txns[i] = null;
+        }
+        // Write the blocks from the blockList to disk
+        synchronizeBlocks(blockList.iterator(), true);
+
+        owner.sync();
+        open();
+    }
+
+
+    /** Opens the log file */
+    private void open() throws IOException {
+        fos = new FileOutputStream(makeLogName());
+        oos = new ObjectOutputStream(fos);
+        oos.writeShort(Magic.LOGFILE_HEADER);
+        oos.flush();
+        curTxn = -1;
+    }
+
+    /** Startup recovery on all files */
+    private void recover() throws IOException {
+        String logName = makeLogName();
+        File logFile = new File(logName);
+        if (!logFile.exists())
+            return;
+        if (logFile.length() == 0) {
+            logFile.delete();
+            return;
+        }
+
+        FileInputStream fis = new FileInputStream(logFile);
+        ObjectInputStream ois = new ObjectInputStream(fis);
+
+        try {
+            if (ois.readShort() != Magic.LOGFILE_HEADER)
+                throw new Error("Bad magic on log file");
+        } catch (IOException e) {
+            // corrupted/empty logfile
+            logFile.delete();
+            return;
+        }
+
+        while (true) {
+            ArrayList blocks = null;
+            try {
+                blocks = (ArrayList) ois.readObject();
+            } catch (ClassNotFoundException e) {
+                throw new Error("Unexcepted exception: " + e);
+            } catch (IOException e) {
+                // corrupted logfile, ignore rest of transactions
+                break;
+            }
+            synchronizeBlocks(blocks.iterator(), false);
+
+            // ObjectInputStream must match exactly each
+            // ObjectOutputStream created during writes
+            try {
+                ois = new ObjectInputStream(fis);
+            } catch (IOException e) {
+                // corrupted logfile, ignore rest of transactions
+                break;
+            }
+        }
+        owner.sync();
+        logFile.delete();
+    }
+
+    /** Synchronizes the indicated blocks with the owner. */
+    private void synchronizeBlocks(Iterator blockIterator, boolean fromCore)
+    throws IOException {
+        // write block vector elements to the data file.
+        while ( blockIterator.hasNext() ) {
+            BlockIo cur = (BlockIo)blockIterator.next();
+            owner.synch(cur);
+            if (fromCore) {
+                cur.decrementTransactionCount();
+                if (!cur.isInTransaction()) {
+                    owner.releaseFromTransaction(cur, true);
+                }
+            }
+        }
+    }
+
+
+    /** Set clean flag on the blocks. */
+    private void setClean(ArrayList blocks)
+    throws IOException {
+        for (Iterator k = blocks.iterator(); k.hasNext(); ) {
+            BlockIo cur = (BlockIo) k.next();
+            cur.setClean();
+        }
+    }
+
+    /** Discards the indicated blocks and notify the owner. */
+    private void discardBlocks(ArrayList blocks)
+    throws IOException {
+        for (Iterator k = blocks.iterator(); k.hasNext(); ) {
+            BlockIo cur = (BlockIo) k.next();
+            cur.decrementTransactionCount();
+            if (!cur.isInTransaction()) {
+                owner.releaseFromTransaction(cur, false);
+            }
+        }
+    }
+
+    /**
+     *  Starts a transaction. This can block if all slots have been filled
+     *  with full transactions, waiting for the synchronization thread to
+     *  clean out slots.
+     */
+    void start() throws IOException {
+        curTxn++;
+        if (curTxn == _maxTxns) {
+            synchronizeLogFromMemory();
+            curTxn = 0;
+        }
+        txns[curTxn] = new ArrayList();
+    }
+
+    /**
+     *  Indicates the block is part of the transaction.
+     */
+    void add(BlockIo block) throws IOException {
+        block.incrementTransactionCount();
+        txns[curTxn].add(block);
+    }
+
+    /**
+     *  Commits the transaction to the log file.
+     */
+    void commit() throws IOException {
+        oos.writeObject(txns[curTxn]);
+        sync();
+
+        // set clean flag to indicate blocks have been written to log
+        setClean(txns[curTxn]);
+
+        // open a new ObjectOutputStream in order to store
+        // newer states of BlockIo
+        oos = new ObjectOutputStream(fos);
+    }
+
+    /** Flushes and syncs */
+    private void sync() throws IOException {
+        oos.flush();
+        fos.flush();
+        fos.getFD().sync();
+    }
+
+    /**
+     *  Shutdowns the transaction manager. Resynchronizes outstanding
+     *  logs.
+     */
+    void shutdown() throws IOException {
+        synchronizeLogFromMemory();
+        close();
+    }
+
+    /**
+     *  Closes open files.
+     */
+    private void close() throws IOException {
+        sync();
+        oos.close();
+        fos.close();
+        oos = null;
+        fos = null;
+    }
+
+    /**
+     * Force closing the file without synchronizing pending transaction data.
+     * Used for testing purposes only.
+     */
+    void forceClose() throws IOException {
+        oos.close();
+        fos.close();
+        oos = null;
+        fos = null;
+    }
+
+    /**
+     * Use the disk-based transaction log to synchronize the data file.
+     * Outstanding memory logs are discarded because they are believed
+     * to be inconsistent.
+     */
+    void synchronizeLogFromDisk() throws IOException {
+        close();
+
+        for ( int i=0; i < _maxTxns; i++ ) {
+            if (txns[i] == null)
+                continue;
+            discardBlocks(txns[i]);
+            txns[i] = null;
+        }
+
+        recover();
+        open();
+    }
+
+
+    /** INNER CLASS.
+     *  Comparator class for use by the tree set used to store the blocks
+     *  to write for this transaction.  The BlockIo objects are ordered by
+     *  their blockIds.
+     */
+    public static class BlockIoComparator
+        implements Comparator
+    {
+
+        public int compare( Object o1, Object o2 ) {
+            BlockIo block1 = (BlockIo)o1;
+            BlockIo block2 = (BlockIo)o2;
+            int result = 0;
+            if ( block1.getBlockId() == block2.getBlockId() ) {
+                result = 0;
+            }
+            else if ( block1.getBlockId() < block2.getBlockId() ) {
+                result = -1;
+            }
+            else {
+                result = 1;
+            }
+            return result;
+        }
+
+        public boolean equals(Object obj) {
+            return super.equals(obj);
+        }
+    } // class BlockIOComparator
+
+}
diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/TranslationPage.java b/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/TranslationPage.java
new file mode 100644
index 0000000..5d2d8fc
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/TranslationPage.java
@@ -0,0 +1,91 @@
+/**
+ * JDBM LICENSE v1.00
+ *
+ * Redistribution and use of this software and associated documentation
+ * ("Software"), with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain copyright
+ *    statements and notices.  Redistributions must also contain a
+ *    copy of this document.
+ *
+ * 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 name "JDBM" must not be used to endorse or promote
+ *    products derived from this Software without prior written
+ *    permission of Cees de Groot.  For written permission,
+ *    please contact cg@cdegroot.com.
+ *
+ * 4. Products derived from this Software may not be called "JDBM"
+ *    nor may "JDBM" appear in their names without prior written
+ *    permission of Cees de Groot. 
+ *
+ * 5. Due credit should be given to the JDBM Project
+ *    (http://jdbm.sourceforge.net/).
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS
+ * ``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
+ * CEES DE GROOT OR ANY 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.
+ *
+ * Copyright 2000 (C) Cees de Groot. All Rights Reserved.
+ * Contributions are Copyright (C) 2000 by their associated contributors.
+ *
+ * $Id: TranslationPage.java,v 1.1 2000/05/06 00:00:31 boisvert Exp $
+ */
+
+package jdbm.recman;
+
+/**
+ *  Class describing a page that holds translations from physical rowids
+ *  to logical rowids. In fact, the page just holds physical rowids - the
+ *  page's block is the block for the logical rowid, the offset serve
+ *  as offset for the rowids.
+ */
+final class TranslationPage extends PageHeader {
+    // offsets
+    static final short O_TRANS = PageHeader.SIZE; // short count
+    static final short ELEMS_PER_PAGE = 
+        (RecordFile.BLOCK_SIZE - O_TRANS) / PhysicalRowId.SIZE;
+    
+    // slots we returned.
+    final PhysicalRowId[] slots = new PhysicalRowId[ELEMS_PER_PAGE];
+
+    /**
+     *  Constructs a data page view from the indicated block.
+     */
+    TranslationPage(BlockIo block) {
+        super(block);
+    }
+
+    /**
+     *  Factory method to create or return a data page for the
+     *  indicated block.
+     */
+    static TranslationPage getTranslationPageView(BlockIo block) {
+        BlockView view = block.getView();
+        if (view != null && view instanceof TranslationPage)
+            return (TranslationPage) view;
+        else
+            return new TranslationPage(block);
+    }
+
+    /** Returns the value of the indicated rowid on the page */
+    PhysicalRowId get(short offset) {
+        int slot = (offset - O_TRANS) / PhysicalRowId.SIZE;
+        if (slots[slot] == null) 
+            slots[slot] = new PhysicalRowId(block, offset);
+        return slots[slot];
+    }
+}
diff --git a/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/package.html b/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/package.html
new file mode 100644
index 0000000..a0ba996
--- /dev/null
+++ b/old_trunk/apacheds-jdbm/src/main/java/jdbm/recman/package.html
@@ -0,0 +1,12 @@
+<!-- $Id: package.html,v 1.1 2001/05/19 16:01:33 boisvert Exp $ -->
+<html>
+  <body>
+    <p>Core classes for managing persistent objects and processing transactions.</p>
+
+    <dl>
+      <dt><b>Version: </b></dt><dd>$Revision: 1.1 $ $Date: 2001/05/19 16:01:33 $</dd>
+      <dt><b>Author: </b></dt><dd><a href="mailto:boisvert@intalio.com">Alex Boisvert</a></dd>
+    </dl>
+
+  </body>
+</html>
diff --git a/old_trunk/benchmarks/pom.xml b/old_trunk/benchmarks/pom.xml
new file mode 100644
index 0000000..55430f8
--- /dev/null
+++ b/old_trunk/benchmarks/pom.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you 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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.directory.server</groupId>
+    <artifactId>apacheds-parent</artifactId>
+    <version>1.5.3-SNAPSHOT</version>
+  </parent>
+  <artifactId>apacheds-benchmarks</artifactId>
+  <name>ApacheDS Benchmarks</name>
+  
+  <description>
+    Set of programs to run to benchmark the performance of the server.
+  </description>
+
+  <packaging>jar</packaging>
+  
+  <dependencies>
+    <dependency>
+      <groupId>ldapsdk</groupId>
+      <artifactId>ldapsdk</artifactId>
+      <scope>test</scope>
+    </dependency>  
+  </dependencies>
+  
+
+</project>
+
diff --git a/old_trunk/benchmarks/src/main/java/org/apache/directory/server/benchmarks/BindBenchmark.java b/old_trunk/benchmarks/src/main/java/org/apache/directory/server/benchmarks/BindBenchmark.java
new file mode 100644
index 0000000..69935e0
--- /dev/null
+++ b/old_trunk/benchmarks/src/main/java/org/apache/directory/server/benchmarks/BindBenchmark.java
@@ -0,0 +1,681 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.benchmarks;
+
+
+import java.util.ArrayList;
+import java.util.Date;
+
+import netscape.ldap.LDAPConnection;
+import netscape.ldap.LDAPConstraints;
+import netscape.ldap.LDAPException;
+
+import com.sun.slamd.job.JobClass;
+import com.sun.slamd.job.UnableToRunException;
+import com.sun.slamd.parameter.BooleanParameter;
+import com.sun.slamd.parameter.IntegerParameter;
+import com.sun.slamd.parameter.InvalidValueException;
+import com.sun.slamd.parameter.Parameter;
+import com.sun.slamd.parameter.ParameterList;
+import com.sun.slamd.parameter.PasswordParameter;
+import com.sun.slamd.parameter.PlaceholderParameter;
+import com.sun.slamd.parameter.StringParameter;
+import com.sun.slamd.stat.IncrementalTracker;
+import com.sun.slamd.stat.RealTimeStatReporter;
+import com.sun.slamd.stat.StatTracker;
+import com.sun.slamd.stat.TimeTracker;
+
+
+/**
+ * A simple bind benchmark.  Here the same bindDn and password is 
+ * used to bind to the directory.  The connection to the directory is
+ * by default shared across iterations of a thread but this can be 
+ * changed.  This is not a real world experiment but a way for us to
+ * stress test the server, profile it, optimize it and regression test
+ * our results under stress for more reliable feedback.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class BindBenchmark extends JobClass
+{
+    /**
+     * The name of the stat tracker that will be used to count the number of
+     * authentication attempts.
+     */
+    public static final String STAT_TRACKER_AUTHENTICATION_ATTEMPTS = "Authentication Attempts";
+
+    /**
+     * The name of the stat tracker that will be used to keep track of the time 
+     * required to perform each authentication.
+     */
+    public static final String STAT_TRACKER_AUTHENTICATION_TIME = "Authentication Time";
+
+    /**
+     * The name of the stat tracker that will be used to count the number of
+     * failed authentications.
+     */
+    public static final String STAT_TRACKER_FAILED_AUTHENTICATIONS = "Failed Authentications";
+
+    /**
+     * The name of the stat tracker that will be used to count the number of
+     * successful authentications.
+     */
+    public static final String STAT_TRACKER_SUCCESSFUL_AUTHENTICATIONS = "Successful Authentications";
+
+    // -----------------------------------------------------------------------
+    // Extracted paramter values for the whole Job
+    // -----------------------------------------------------------------------
+
+    // The connection is estabilished anew every time for each bind request
+    // if this option is not selected instead of reusing the existing connection
+    // for each iteration.  The default is to share the connection.
+    static boolean shareConnections;
+    
+    // Indicates whether bind failures because of invalid credentials will be
+    // ignored (so we can optimize for authn failures as well).
+    static boolean ignoreInvalidCredentials;
+
+    // The maximum number of iterations per thread.
+    static int iterations;
+
+    // The maximum length of time that any single LDAP operation will be allowed
+    // to take before it is cancelled.
+    static int timeLimit;
+
+    // The time to start working before beginning statistics collection.
+    static int warmUpTime;
+
+    // The delay in milliseconds between authentication attempts.
+    static long delay;
+
+    // The port number of the directory server.
+    static int directoryPort;
+
+    // The address of the directory server.
+    static String directoryHost;
+
+    // The DN to use to bind to the directory 
+    static String bindDN;
+
+    // The password for the bind DN.
+    static String bindPW;
+
+    // -----------------------------------------------------------------------
+    // Paramters definitions
+    // -----------------------------------------------------------------------
+
+    // The parameter controlling where or not connections are reused
+    BooleanParameter shareConnectionsParameter = new BooleanParameter( "shareConnectionsParameter",
+        "Share Connections", 
+        "Specifies whether or not the connection to the LDAP server is shared or " +
+        "re-estabilished every time for each iteration.  If true the iteration creates" +
+        "and destroys a new connection before issueing a bind request.", true );
+    
+    // The parameter that indicates the delay that should be used between each
+    // authentication attempt.
+    IntegerParameter delayParameter = new IntegerParameter( "delay", "Time Between Authentications (ms)",
+        "Specifies the length of time in milliseconds " + "each thread should wait between authentication "
+            + "attempts.  Note that this delay will be " + "between the starts of consecutive attempts and "
+            + "not between the end of one attempt and the " + "beginning of the next.  If an authentication "
+            + "takes longer than this length of time, then " + "there will be no delay.", true, 0, true, 0, false, 0 );
+
+    // The parameter that indicates the number of iterations to perform.
+    IntegerParameter iterationsParameter = new IntegerParameter( "num_iterations", "Number of Iterations",
+        "The number of authentications that should be " + "performed by each thread", false, -1 );
+
+    // The parameter used to indicate the maximum length of time that any single
+    // LDAP operation will be allowed to take.
+    IntegerParameter timeLimitParameter = new IntegerParameter( "time_limit", "Operation Time Limit",
+        "The maximum length of time in seconds that any " + "single LDAP operation will be allowed to take "
+            + "before it is cancelled.", true, 0, true, 0, false, 0 );
+
+    // The parmeter that specifies the cool-down time in seconds.
+    IntegerParameter warmUpParameter = new IntegerParameter( "warm_up", "Warm Up Time",
+        "The time in seconds that the job should " + "search before beginning statistics collection.", true, 0, true,
+        0, false, 0 );
+
+    // The placeholder parameter used as a spacer in the admin interface.
+    PlaceholderParameter placeholder = new PlaceholderParameter();
+
+    // The parameter used to indicate the port number for the directory server.
+    IntegerParameter portParameter = new IntegerParameter( "ldap_port", "Directory Server Port",
+        "The port number for the directory server.", true, 389, true, 1, true, 65535 );
+
+    // The parameter used to indicate the address of the directory server.
+    StringParameter hostParameter = new StringParameter( "ldap_host", "Directory Server Address",
+        "The address for the directory server.", true, "" );
+
+    // The parameter used to indicate the bind DN.
+    StringParameter bindDNParameter = new StringParameter( "binddn", "Directory Bind DN",
+        "The DN to use when binding to the directory server.", false, "" );
+
+    // The parameter used to indicate the bind DN.
+    PasswordParameter bindPWParameter = new PasswordParameter( "bindpw", "Bind Password",
+        "The password to use when binding.", false, "" );
+
+    // -----------------------------------------------------------------------
+    // Stat trakers for each thread.
+    // -----------------------------------------------------------------------
+
+    // The stat tracker that will count the # of authentication attempts.
+    IncrementalTracker attemptCounter;
+
+    // The stat tracker that will count the # of failed authentications.
+    IncrementalTracker failureCounter;
+
+    // The stat tracker that will count the # of successful authentications.
+    IncrementalTracker successCounter;
+
+    // The stat tracker that will time each authentication.
+    TimeTracker authTimer;
+
+    // -----------------------------------------------------------------------
+    // Connection and other parameters for each thread
+    // -----------------------------------------------------------------------
+
+    // The LDAP connection that will be used for bind operations by this thread.
+    LDAPConnection bindConnection;
+
+    // The set of constraints that will be used for bind operations.
+    LDAPConstraints bindConstraints;
+
+
+    public String getJobDescription()
+    {
+        return "Does a bind using a single user name then immediately unbinds.";
+    }
+
+
+    public String getJobName()
+    {
+        return "Bind/Unbind Optimization Test";
+    }
+
+
+    public String getJobCategoryName()
+    {
+        return "ApacheDS Optimization Tests";
+    }
+
+
+    /**
+     * Returns the set of parameters whose value may be specified by the end user.
+     *
+     * @return  The set of configurable parameters for this job class.
+     */
+    public ParameterList getParameterStubs()
+    {
+        Parameter[] parameterArray = new Parameter[]
+            { placeholder, hostParameter, portParameter, bindDNParameter, bindPWParameter, placeholder,
+                warmUpParameter, timeLimitParameter, delayParameter, placeholder,
+                iterationsParameter, shareConnectionsParameter };
+
+        return new ParameterList( parameterArray );
+    }
+
+
+    public StatTracker[] getStatTrackerStubs( String clientID, String threadID, int collectionInterval )
+    {
+        return new StatTracker[]
+            {
+                new IncrementalTracker( clientID, threadID, STAT_TRACKER_AUTHENTICATION_ATTEMPTS, collectionInterval ),
+                new IncrementalTracker( clientID, threadID, STAT_TRACKER_SUCCESSFUL_AUTHENTICATIONS, collectionInterval ),
+                new IncrementalTracker( clientID, threadID, STAT_TRACKER_FAILED_AUTHENTICATIONS, collectionInterval ),
+                new TimeTracker( clientID, threadID, STAT_TRACKER_AUTHENTICATION_TIME, collectionInterval ) };
+    }
+
+
+    public StatTracker[] getStatTrackers()
+    {
+        return new StatTracker[]
+            { attemptCounter, successCounter, failureCounter, authTimer };
+    }
+
+
+    public void validateJobInfo( int numClients, int threadsPerClient, int threadStartupDelay, Date startTime,
+        Date stopTime, int duration, int collectionInterval, ParameterList parameters ) throws InvalidValueException
+    {
+        // might want to add something here later
+    }
+
+
+    public boolean providesParameterTest()
+    {
+        return true;
+    }
+
+
+    /**
+     * Provides a means of testing the provided job parameters to determine
+     * whether they are valid (e.g., to see if the server is reachable) before
+     * scheduling the job for execution.  This method will be executed by the
+     * SLAMD server system itself and not by any of the clients.
+     *
+     * @param  parameters      The job parameters to be tested.
+     * @param  outputMessages  The lines of output that were generated as part of
+     *                         the testing process.  Each line of output should
+     *                         be added to this list as a separate string, and
+     *                         empty strings (but not <CODE>null</CODE> values)
+     *                         are allowed to provide separation between
+     *                         different messages.  No formatting should be
+     *                         provided for these messages, however, since they
+     *                         may be displayed in either an HTML or plain text
+     *                         interface.
+     *
+     * @return  <CODE>true</CODE> if the test completed successfully, or
+     *          <CODE>false</CODE> if not. 
+     */
+    public boolean testJobParameters( ParameterList parameters, ArrayList outputMessages )
+    {
+        // Get all the parameters that we might need to perform the test.
+        StringParameter hostParam = parameters.getStringParameter( hostParameter.getName() );
+        if ( ( hostParam == null ) || ( !hostParam.hasValue() ) )
+        {
+            outputMessages.add( "ERROR:  No directory server address was provided." );
+            return false;
+        }
+        String host = hostParam.getStringValue();
+
+        IntegerParameter portParam = parameters.getIntegerParameter( portParameter.getName() );
+        if ( ( portParam == null ) || ( !hostParam.hasValue() ) )
+        {
+            outputMessages.add( "ERROR:  No directory server port was provided." );
+            return false;
+        }
+        int port = portParam.getIntValue();
+
+        String bindDN = "";
+        StringParameter bindDNParam = parameters.getStringParameter( bindDNParameter.getName() );
+        if ( ( bindDNParam != null ) && bindDNParam.hasValue() )
+        {
+            bindDN = bindDNParam.getStringValue();
+        }
+
+        String bindPassword = "";
+        PasswordParameter bindPWParam = parameters.getPasswordParameter( bindPWParameter.getName() );
+        if ( ( bindPWParam != null ) && bindPWParam.hasValue() )
+        {
+            bindPassword = bindPWParam.getStringValue();
+        }
+
+        // Create the LDAPConnection object that we will use to communicate with the directory server.
+        LDAPConnection conn = new LDAPConnection();
+
+        // Attempt to establish a connection to the directory server.
+        try
+        {
+            outputMessages.add( "Attempting to establish a connection to " + host + ":" + port + "...." );
+            conn.connect( host, port );
+            outputMessages.add( "Connected successfully." );
+            outputMessages.add( "" );
+        }
+        catch ( Exception e )
+        {
+            outputMessages.add( "ERROR:  Unable to connect to the directory " + "server:  " + stackTraceToString( e ) );
+            return false;
+        }
+
+        // Attempt to bind to the directory server using the bind DN and password.
+        try
+        {
+            outputMessages.add( "Attempting to perform an LDAPv3 bind to the " + "directory server with a DN of '"
+                + bindDN + "'...." );
+            conn.bind( 3, bindDN, bindPassword );
+            outputMessages.add( "Bound successfully." );
+            outputMessages.add( "" );
+        }
+        catch ( Exception e )
+        {
+            try
+            {
+                conn.disconnect();
+            }
+            catch ( Exception e2 )
+            {
+            }
+
+            outputMessages.add( "ERROR:  Unable to bind to the directory server:  " + stackTraceToString( e ) );
+            return false;
+        }
+
+        // At this point, all tests have passed.  Close the connection and return true.
+        try
+        {
+            conn.disconnect();
+        }
+        catch ( Exception e )
+        {
+        }
+
+        outputMessages.add( "All tests completed successfully." );
+        return true;
+    }
+
+
+    /**
+     * Performs initialization for this job on each client immediately before each
+     * thread is created to actually run the job.
+     *
+     * @param  clientID    The ID assigned to the client running this job.
+     * @param  parameters  The set of parameters provided to this job that can be
+     *                     used to customize its behavior.
+     *
+     * @throws  UnableToRunException  If the client initialization could not be
+     *                                completed successfully and the job is unable
+     *                                to run.
+     */
+    public void initializeClient( String clientID, ParameterList parameters ) throws UnableToRunException
+    {
+        // Get the shareConnections boolean parameter
+        shareConnectionsParameter = parameters.getBooleanParameter( shareConnectionsParameter.getName() );
+        if ( hostParameter == null )
+        {
+            shareConnections = true; // the default
+        }
+        else
+        {
+            shareConnections = shareConnectionsParameter.getBooleanValue();
+        }
+        
+        
+        // Get the directory server address
+        hostParameter = parameters.getStringParameter( hostParameter.getName() );
+        if ( hostParameter == null )
+        {
+            throw new UnableToRunException( "No directory server host provided." );
+        }
+        else
+        {
+            directoryHost = hostParameter.getStringValue();
+        }
+
+        // Get the directory server port
+        portParameter = parameters.getIntegerParameter( portParameter.getName() );
+        if ( portParameter != null )
+        {
+            directoryPort = portParameter.getIntValue();
+        }
+
+        // Get the DN to use to bind to the directory server.
+        bindDNParameter = parameters.getStringParameter( bindDNParameter.getName() );
+        if ( bindDNParameter == null )
+        {
+            bindDN = "";
+        }
+        else
+        {
+            bindDN = bindDNParameter.getStringValue();
+        }
+
+        // Get the password to use to bind to the directory server.
+        bindPWParameter = parameters.getPasswordParameter( bindPWParameter.getName() );
+        if ( bindPWParameter == null )
+        {
+            bindPW = "";
+        }
+        else
+        {
+            bindPW = bindPWParameter.getStringValue();
+        }
+
+        // Get the warm up time.
+        warmUpTime = 0;
+        warmUpParameter = parameters.getIntegerParameter( warmUpParameter.getName() );
+        if ( warmUpParameter != null )
+        {
+            warmUpTime = warmUpParameter.getIntValue();
+        }
+
+        // Get the max operation time limit.
+        timeLimitParameter = parameters.getIntegerParameter( timeLimitParameter.getName() );
+        if ( timeLimitParameter != null )
+        {
+            timeLimit = timeLimitParameter.getIntValue();
+        }
+
+        // Get the delay between authentication attempts.
+        delay = 0;
+        delayParameter = parameters.getIntegerParameter( delayParameter.getName() );
+        if ( delayParameter != null )
+        {
+            delay = delayParameter.getIntValue();
+        }
+
+        // Get the number of iterations to perform.
+        iterations = -1;
+        iterationsParameter = parameters.getIntegerParameter( iterationsParameter.getName() );
+        if ( ( iterationsParameter != null ) && ( iterationsParameter.hasValue() ) )
+        {
+            iterations = iterationsParameter.getIntValue();
+        }
+    }
+
+
+    public void initializeThread( String clientID, String threadID, int collectionInterval, ParameterList parameters )
+        throws UnableToRunException
+    {
+        if ( shareConnections )
+        {
+            bindConnection = new LDAPConnection();
+    
+            try
+            {
+                bindConnection.connect( 3, directoryHost, directoryPort, "", "" );
+            }
+            catch ( Exception e )
+            {
+                throw new UnableToRunException( "Unable to establish the connections " + "to the directory server:  " + e,
+                    e );
+            }
+    
+            // Initialize the constraints.
+            bindConstraints = bindConnection.getConstraints();
+            bindConstraints.setTimeLimit( 1000 * timeLimit );
+        }
+    
+        // Create the stat trackers.
+        attemptCounter = new IncrementalTracker( clientID, threadID, STAT_TRACKER_AUTHENTICATION_ATTEMPTS,
+            collectionInterval );
+        successCounter = new IncrementalTracker( clientID, threadID, STAT_TRACKER_SUCCESSFUL_AUTHENTICATIONS,
+            collectionInterval );
+
+        failureCounter = new IncrementalTracker( clientID, threadID, STAT_TRACKER_FAILED_AUTHENTICATIONS,
+            collectionInterval );
+        authTimer = new TimeTracker( clientID, threadID, STAT_TRACKER_AUTHENTICATION_TIME, collectionInterval );
+
+        // Enable real-time reporting of the data for these stat trackers.
+        RealTimeStatReporter statReporter = getStatReporter();
+        if ( statReporter != null )
+        {
+            String jobID = getJobID();
+            attemptCounter.enableRealTimeStats( statReporter, jobID );
+            successCounter.enableRealTimeStats( statReporter, jobID );
+            failureCounter.enableRealTimeStats( statReporter, jobID );
+            authTimer.enableRealTimeStats( statReporter, jobID );
+        }
+    }
+
+
+    /**
+     * Performs the work of actually running the job.  When this method completes,
+     * the job will be done.
+     */
+    public void runJob()
+    {
+        // Determine the range of time for which we should collect statistics.
+        long currentTime = System.currentTimeMillis();
+        boolean collectingStats = false;
+        long startCollectingTime = currentTime + ( 1000 * warmUpTime );
+        long stopCollectingTime = Long.MAX_VALUE;
+
+        // See if this thread should operate "infinitely" (i.e., not a fixed number of iterations)
+        boolean infinite = ( iterations <= 0 );
+
+        // Loop until it is time to stop.
+        for ( int ii = 0; !shouldStop() && ( infinite || ii < iterations ); ii++ )
+        {
+            currentTime = System.currentTimeMillis();
+
+            if ( ( !collectingStats ) && ( currentTime >= startCollectingTime ) && ( currentTime < stopCollectingTime ) )
+            {
+                // Start all the stat trackers.
+                attemptCounter.startTracker();
+                successCounter.startTracker();
+                failureCounter.startTracker();
+                authTimer.startTracker();
+                collectingStats = true;
+            }
+            else if ( ( collectingStats ) && ( currentTime >= stopCollectingTime ) )
+            {
+                // Stop all the stat trackers.
+                attemptCounter.stopTracker();
+                successCounter.stopTracker();
+                failureCounter.stopTracker();
+                authTimer.stopTracker();
+                collectingStats = false;
+            }
+
+            // See if we need to sleep before the next attempt
+            if ( delay > 0 )
+            {
+                long now = System.currentTimeMillis();
+                long sleepTime = delay - now;
+
+                if ( sleepTime > 0 )
+                {
+                    try
+                    {
+                        Thread.sleep( sleepTime );
+                    }
+                    catch ( InterruptedException ie )
+                    {
+                    }
+
+                    if ( shouldStop() )
+                    {
+                        break;
+                    }
+                }
+            }
+
+            if ( ! shareConnections )
+            {
+                bindConnection = new LDAPConnection();
+                
+                try
+                {
+                    bindConnection.connect( 3, directoryHost, directoryPort, "", "" );
+                }
+                catch ( Exception e )
+                {
+                    throw new IllegalStateException( "Unable to establish the connections " 
+                        + "to the directory server:  " + e, e );
+                }
+        
+                // Initialize the constraints.
+                bindConstraints = bindConnection.getConstraints();
+                bindConstraints.setTimeLimit( 1000 * timeLimit );
+            }
+
+            if ( collectingStats )
+            {
+                attemptCounter.increment();
+                authTimer.startTimer();
+            }
+
+            // Increment the number of authentication attempts and start the timer
+            try
+            {
+                // Perform a bind as the user to verify that the provided password is
+                // valid.
+                bindConnection.authenticate( 3, bindDN, bindPW );
+                if ( collectingStats )
+                {
+                    successCounter.increment();
+                    authTimer.stopTimer();
+                }
+            }
+            catch ( LDAPException le )
+            {
+                if ( !( ignoreInvalidCredentials && ( le.getLDAPResultCode() == LDAPException.INVALID_CREDENTIALS ) ) )
+                {
+                    if ( collectingStats )
+                    {
+                        failureCounter.increment();
+                        authTimer.stopTimer();
+                    }
+                }
+                
+                StringBuffer buf = new StringBuffer();
+                buf.append( "LDAPException: " ).append( le.getMessage() )
+                    .append( " - " ).append( le.getLDAPErrorMessage() );
+                writeVerbose( buf.toString() );
+            }
+            finally
+            {
+                if ( ! shareConnections )
+                {
+                    if ( bindConnection != null )
+                    {
+                        try
+                        {
+                            bindConnection.disconnect();
+                        }
+                        catch ( Exception e )
+                        {
+                        }
+            
+                        bindConnection = null;
+                    }
+                }
+            }
+        }
+
+        attemptCounter.stopTracker();
+        successCounter.stopTracker();
+        failureCounter.stopTracker();
+        authTimer.stopTracker();
+    }
+
+
+    /**
+     * Attempts to force this thread to exit by closing the connections to the
+     * directory server and setting them to <CODE>null</CODE>.
+     */
+    public void destroy()
+    {
+        if ( shareConnections )
+        {
+            if ( bindConnection != null )
+            {
+                try
+                {
+                    bindConnection.disconnect();
+                }
+                catch ( Exception e )
+                {
+                }
+    
+                bindConnection = null;
+            }
+        }
+    }
+}
diff --git a/old_trunk/benchmarks/src/main/java/org/apache/directory/server/benchmarks/BogusBenchmark.java b/old_trunk/benchmarks/src/main/java/org/apache/directory/server/benchmarks/BogusBenchmark.java
new file mode 100644
index 0000000..9410782
--- /dev/null
+++ b/old_trunk/benchmarks/src/main/java/org/apache/directory/server/benchmarks/BogusBenchmark.java
@@ -0,0 +1,99 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.benchmarks;
+
+
+import com.sun.slamd.job.JobClass;
+import com.sun.slamd.job.UnableToRunException;
+import com.sun.slamd.parameter.BooleanParameter;
+import com.sun.slamd.parameter.ParameterList;
+import com.sun.slamd.stat.IncrementalTracker;
+import com.sun.slamd.stat.StatTracker;
+
+
+public class BogusBenchmark extends JobClass
+{
+    IncrementalTracker incremental = null;
+    
+    
+    public String getJobDescription()
+    {
+        return "Does a bind using a single user name then unbinds.";
+    }
+
+    
+    public String getJobName()
+    {
+        return "Simple Bind";
+    }
+    
+    
+    public String getJobCategoryName()
+    {
+        return "ApacheDS";
+    }
+
+    
+    public ParameterList getParameterStubs()
+    {
+        ParameterList list = new ParameterList();
+        list.addParameter( new BooleanParameter( "TestParam", "Test Parameter", "just for testing", true ) );
+        return list;
+    }
+
+
+    public StatTracker[] getStatTrackerStubs( String clientId, String threadId, int interval )
+    {
+        return new StatTracker[] { new IncrementalTracker( clientId, threadId, "test tracker", interval ) };
+    }
+
+    
+    public StatTracker[] getStatTrackers()
+    {
+        return new StatTracker[] { incremental };
+    }
+
+    
+    public void initializeThread( String clientId, String threadId, int interval, ParameterList params ) 
+        throws UnableToRunException
+    {
+        super.logMessage( "initializeThread() called" );
+        incremental = new IncrementalTracker( clientId, threadId, "test tracker", interval );
+    }
+
+
+    public void runJob()
+    {
+        incremental.startTracker();
+        while ( !shouldStop() )
+        {
+            try
+            {
+                Thread.sleep( 200 );
+                incremental.increment();
+            }
+            catch ( InterruptedException e )
+            {
+                e.printStackTrace();
+            }
+        }
+        incremental.stopTracker();
+    }
+}
diff --git a/old_trunk/benchmarks/src/site/site.xml b/old_trunk/benchmarks/src/site/site.xml
new file mode 100644
index 0000000..9ec7c16
--- /dev/null
+++ b/old_trunk/benchmarks/src/site/site.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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.
+-->
+<!--
+  @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+-->
+<project name="${project.name}">
+    <bannerLeft>
+        <name>${project.name}</name>
+    </bannerLeft>
+    <publishDate position="navigation-bottom" format="dd-MM-yyyy HH:mm" />
+    <body>
+        <menu name="Parent">
+            <item name="Apache Directory ApacheDS" href="../index.html" />
+        </menu>
+        <menu ref="reports" />
+    </body>
+</project>
diff --git a/old_trunk/bootstrap-extract/pom.xml b/old_trunk/bootstrap-extract/pom.xml
new file mode 100644
index 0000000..873a431
--- /dev/null
+++ b/old_trunk/bootstrap-extract/pom.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you 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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.directory.server</groupId>
+    <artifactId>apacheds-parent</artifactId>
+    <version>1.5.4-SNAPSHOT</version>
+  </parent>
+  <artifactId>apacheds-bootstrap-extract</artifactId>
+  <name>ApacheDS Bootstrap Partition File Extractor</name>
+  <packaging>jar</packaging>
+
+  <description>
+    This artifact contains the classes needed to extract db files into the proper position
+      for the schema partition.
+  </description>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-io</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-jdbm-store</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <configuration> 
+          <systemProperties>
+            <property>
+              <name>jarFilePath</name>
+              <value>${basedir}/src/test/resources/test.jar</value>
+            </property>
+            <property>
+              <name>destDirPath</name>
+              <value>${basedir}/target/extracted</value>
+            </property>
+          </systemProperties>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/old_trunk/bootstrap-extract/src/main/java/org/apache/directory/server/schema/bootstrap/partition/DbFileListing.java b/old_trunk/bootstrap-extract/src/main/java/org/apache/directory/server/schema/bootstrap/partition/DbFileListing.java
new file mode 100644
index 0000000..d38d257
--- /dev/null
+++ b/old_trunk/bootstrap-extract/src/main/java/org/apache/directory/server/schema/bootstrap/partition/DbFileListing.java
@@ -0,0 +1,199 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.bootstrap.partition;
+
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+
+/**
+ * Parses the dbfile listing file within this jar.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class DbFileListing
+{
+    Map<String, DbFileType> name2type = new HashMap<String, DbFileType>();
+    private static final String BASE_PATH = DbFileListing.class.getName()
+        .substring( 0, DbFileListing.class.getName().lastIndexOf( "." ) + 1 ).replace( '.', '/' );
+
+
+    public DbFileListing() throws IOException
+    {
+        init();
+    }
+
+
+    /**
+     * Reads the DBFILES resource within some jar on the classpath that 
+     * has this file and loaded it's db files into the name2type map with
+     * something like the following entries ( key => value ):
+     * <pre>
+     * schema/master.db => MASTER_FILE
+     * schema/apacheOnealias.db => SYSTEM_INDEX
+     * schema/apacheSubalias.db => SYSTEM_INDEX
+     * schema/apacheNdn.db => SYSTEM_INDEX
+     * schema/apacheExistance.db => SYSTEM_INDEX
+     * schema/apacheAlias.db => SYSTEM_INDEX
+     * schema/apacheHierarchy.db => SYSTEM_INDEX
+     * schema/apacheUpdn.db => SYSTEM_INDEX
+     * schema/objectClass.db => USER_INDEX
+     * schema/ou.db => USER_INDEX
+     * schema/cn.db => USER_INDEX
+     * schema/m-oid.db => USER_INDEX
+     * schema/m-disabled.db => USER_INDEX
+     * </pre>
+     *
+     * @throws IOException
+     */
+    private void init() throws IOException
+    {
+
+        boolean userIndexMode = false;
+        String line = null;
+        BufferedReader in = new BufferedReader( 
+            new InputStreamReader( 
+                getUniqueResourceAsStream( 
+                    "DBFILES", 
+                    "bootstrap partition database file list. " + 
+                    "Be sure there is exactly one bootstrap partition jar in your classpath." ) ) );
+        try
+        {
+            while ( ( line = in.readLine() ) != null )
+            {
+                if ( line.indexOf( "master.db" ) != -1 )
+                {
+                    name2type.put( line.trim(), DbFileType.MASTER_FILE );
+                    continue;
+                }
+
+                if ( line.indexOf( "USER INDICES" ) != -1 )
+                {
+                    userIndexMode = true;
+                    continue;
+                }
+
+                if ( userIndexMode )
+                {
+                    name2type.put( line.trim(), DbFileType.USER_INDEX );
+                } 
+                else
+                {
+                    name2type.put( line.trim(), DbFileType.SYSTEM_INDEX );
+                }
+            }
+        }
+        finally
+        {
+            in.close();
+        }
+    }
+
+    
+    /**
+     * Gets the DBFILE resource from within a jar off the base path.  If another jar
+     * with such a DBFILE resource exists then an error will result since the resource
+     * is not unique across all the jars.
+     *
+     * @param resourceName the file name of the resource to load
+     * @param resourceDescription
+     * @return the InputStream to read the contents of the resource
+     * @throws IOException if there are problems reading or finding a unique copy of the resource
+     */                                                                                                
+    public static InputStream getUniqueResourceAsStream( String resourceName, String resourceDescription ) throws IOException
+    {
+        resourceName = BASE_PATH + resourceName;
+        URL result = getUniqueResource( resourceName, resourceDescription );
+        return result.openStream();
+    }
+
+    static URL getUniqueResource( String resourceName, String resourceDescription )
+            throws IOException
+    {
+        Enumeration<URL> resources = DbFileListing.class.getClassLoader().getResources( resourceName );
+        if ( !resources.hasMoreElements() )
+        {
+            throw new UniqueResourceException( resourceName, resourceDescription );
+        }
+        URL result = resources.nextElement();
+        if ( resources.hasMoreElements() )
+        {
+            throw new UniqueResourceException( resourceName, result, resources, resourceDescription);
+        }
+        return result;
+    }
+
+
+    public DbFileType getType( String dbfile )
+    {
+        return name2type.get( dbfile );
+    }
+
+
+    public Iterator<String> iterator()
+    {
+        return name2type.keySet().iterator();
+    }
+
+
+    public String getIndexAttributeName( String dbfile )
+    {
+        if ( dbfile.length() < 10 )
+        {
+            throw new IllegalArgumentException( "db file must have a relative jar path name of over 10 characters" );
+        }
+
+        // remove 'schema/'
+        String dbfileName = dbfile.substring( 7 );
+        return dbfileName.substring( 0, dbfileName.length() - 3 );
+    }
+
+
+    /**
+     * Gets the user indices WITHOUT the system indices.
+     *
+     * @return set of user index names
+     */
+    public Set<String> getIndexedAttributes()
+    {
+        Set<String> attributes = new HashSet<String>();
+        Iterator<String> ii = iterator();
+        while( ii.hasNext() )
+        {
+            String name = ii.next();
+            if ( name2type.get( name ) == DbFileType.USER_INDEX )
+            {
+                attributes.add( getIndexAttributeName( name ) );
+            }
+        }
+        return attributes;
+    }
+}
diff --git a/old_trunk/bootstrap-extract/src/main/java/org/apache/directory/server/schema/bootstrap/partition/DbFileType.java b/old_trunk/bootstrap-extract/src/main/java/org/apache/directory/server/schema/bootstrap/partition/DbFileType.java
new file mode 100644
index 0000000..a9e6401
--- /dev/null
+++ b/old_trunk/bootstrap-extract/src/main/java/org/apache/directory/server/schema/bootstrap/partition/DbFileType.java
@@ -0,0 +1,34 @@
+/* 
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.bootstrap.partition;
+
+
+/**
+ * The type of the dbfile.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public enum DbFileType
+{
+    MASTER_FILE,
+    SYSTEM_INDEX,
+    USER_INDEX
+}
diff --git a/old_trunk/bootstrap-extract/src/main/java/org/apache/directory/server/schema/bootstrap/partition/SchemaPartitionExtractor.java b/old_trunk/bootstrap-extract/src/main/java/org/apache/directory/server/schema/bootstrap/partition/SchemaPartitionExtractor.java
new file mode 100644
index 0000000..d4c5319
--- /dev/null
+++ b/old_trunk/bootstrap-extract/src/main/java/org/apache/directory/server/schema/bootstrap/partition/SchemaPartitionExtractor.java
@@ -0,0 +1,101 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.bootstrap.partition;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Iterator;
+
+/**
+ * Extracts dbfiles for the schema partition onto a destination directory.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class SchemaPartitionExtractor
+{
+    private DbFileListing listing;
+    private File outputDirectory;
+
+
+    public SchemaPartitionExtractor( File outputDirectory ) throws IOException
+    {
+        this.outputDirectory = outputDirectory;
+        this.listing = new DbFileListing();
+    }
+
+
+    public void extract() throws IOException
+    {
+        if ( ! outputDirectory.exists() )
+        {
+            outputDirectory.mkdirs();
+        }
+
+        File schemaDirectory = new File( outputDirectory, "schema" );
+        if ( ! schemaDirectory.exists() )
+        {
+            schemaDirectory.mkdirs();
+        }
+
+        Iterator<String> ii = listing.iterator();
+        
+        while ( ii.hasNext() )
+        {
+            extract( ii.next() );
+        }
+    }
+
+
+    public DbFileListing getDbFileListing()
+    {
+        return listing;
+    }
+
+
+    private void extract( String resource ) throws IOException
+    {
+        byte[] buf = new byte[512];
+        InputStream in = DbFileListing.getUniqueResourceAsStream( resource, "database file in bootstrap partition" );
+
+        try
+        {
+            FileOutputStream out = new FileOutputStream( new File( outputDirectory, resource ) );
+            try
+            {
+                while ( in.available() > 0 )
+                {
+                    int readCount = in.read( buf );
+                    out.write( buf, 0, readCount );
+                }
+                out.flush();
+            } finally
+            {
+                out.close();
+            }
+        }
+        finally
+        {
+            in.close();
+        }
+    }
+}
diff --git a/old_trunk/bootstrap-extract/src/main/java/org/apache/directory/server/schema/bootstrap/partition/UniqueResourceException.java b/old_trunk/bootstrap-extract/src/main/java/org/apache/directory/server/schema/bootstrap/partition/UniqueResourceException.java
new file mode 100644
index 0000000..c4fc733
--- /dev/null
+++ b/old_trunk/bootstrap-extract/src/main/java/org/apache/directory/server/schema/bootstrap/partition/UniqueResourceException.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+
+package org.apache.directory.server.schema.bootstrap.partition;
+
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$ $Date$
+ */
+public class UniqueResourceException extends RuntimeException
+{
+    private static final long serialVersionUID = 1L;
+
+    private final String resourceName;
+    private final List<URL> urls;
+    private final String resourceDescription;
+
+    public UniqueResourceException( String resourceName, String resourceDescription )
+    {
+        this( resourceName, null, resourceDescription );
+    }
+
+    public UniqueResourceException( String resourceName, List<URL> urls, String resourceDescription )
+    {
+        this.resourceName = resourceName;
+        this.urls = urls;
+        this.resourceDescription = resourceDescription;
+    }
+
+    public UniqueResourceException( String resourceName, URL first, Enumeration<URL> urlEnum, String resourceDescription )
+    {
+        this( resourceName, toList( first, urlEnum ), resourceDescription );
+    }
+
+    private static List<URL> toList( URL first, Enumeration<URL> urlEnum )
+    {
+        ArrayList<URL> urls = new ArrayList<URL>();
+        urls.add( first );
+        while( urlEnum.hasMoreElements() )
+        {
+            urls.add( urlEnum.nextElement() );
+        }
+        return urls;
+    }
+
+    public String getMessage()
+    {
+        StringBuffer buf = new StringBuffer( "Problem locating " ).append( resourceDescription ).append( "\n" );
+        if ( urls == null )
+        {
+            buf.append( "No resources named '" ).append( resourceName ).append( "' located on classpath" );
+        } else
+        {
+            buf.append( "Multiple copies of resource named '" ).append( resourceName ).append(
+                    "' located on classpath at urls" );
+            for ( URL url : urls )
+            {
+                buf.append( "\n    " ).append( url );
+            }
+        }
+        return buf.toString();
+    }
+
+
+    public String getResourceName()
+    {
+        return resourceName;
+    }
+
+    public List<URL> getUrls()
+    {
+        return Collections.unmodifiableList( urls );
+    }
+}
diff --git a/old_trunk/bootstrap-extract/src/site/site.xml b/old_trunk/bootstrap-extract/src/site/site.xml
new file mode 100644
index 0000000..9ec7c16
--- /dev/null
+++ b/old_trunk/bootstrap-extract/src/site/site.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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.
+-->
+<!--
+  @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+-->
+<project name="${project.name}">
+    <bannerLeft>
+        <name>${project.name}</name>
+    </bannerLeft>
+    <publishDate position="navigation-bottom" format="dd-MM-yyyy HH:mm" />
+    <body>
+        <menu name="Parent">
+            <item name="Apache Directory ApacheDS" href="../index.html" />
+        </menu>
+        <menu ref="reports" />
+    </body>
+</project>
diff --git a/old_trunk/bootstrap-extract/src/test/java/org/apache/directory/server/schema/bootstrap/partition/UniqueResourceTest.java b/old_trunk/bootstrap-extract/src/test/java/org/apache/directory/server/schema/bootstrap/partition/UniqueResourceTest.java
new file mode 100644
index 0000000..d6ea9b0
--- /dev/null
+++ b/old_trunk/bootstrap-extract/src/test/java/org/apache/directory/server/schema/bootstrap/partition/UniqueResourceTest.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+
+package org.apache.directory.server.schema.bootstrap.partition;
+
+import junit.framework.TestCase;
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$ $Date$
+ */
+public class UniqueResourceTest extends TestCase
+{
+
+    public void testUniqueResource() throws Exception
+    {
+        //look for META-INF/LICENSE.txt which should be in at least two jars
+        try
+        {
+            DbFileListing.getUniqueResource( "META-INF/LICENSE", "foo" );
+            fail( "There are at least 2 license files on the classpath, this should have failed" );
+        } catch ( UniqueResourceException e )
+        {
+            assertNotNull("There should be at least 2 LICENSE files on the classpath", e.getUrls());
+        }
+
+    }
+}
diff --git a/old_trunk/bootstrap-partition/pom.xml b/old_trunk/bootstrap-partition/pom.xml
new file mode 100644
index 0000000..e54c996
--- /dev/null
+++ b/old_trunk/bootstrap-partition/pom.xml
@@ -0,0 +1,132 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you 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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.directory.server</groupId>
+    <artifactId>apacheds-parent</artifactId>
+    <version>1.5.4-SNAPSHOT</version>
+  </parent>
+  <artifactId>apacheds-bootstrap-partition</artifactId>
+  <name>ApacheDS Bootstrap Partition</name>
+  <packaging>jar</packaging>
+
+  <description>
+    A special jar file that contains a pre-loaded partition with schema 
+    information.  This schema partition will mount off of the ou=schema 
+    namingContext.  This artifact contains the db files for this partition.
+    It must be used with the apacheds-bootstrap-extract jar which contains
+    the classes to install these files.
+  </description>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.commons</groupId> 
+      <artifactId>commons-io</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-jdbm-store</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-bootstrap-extract</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-schema-extras</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <configuration> 
+          <systemProperties>
+            <property>
+              <name>jarFilePath</name>
+              <value>${basedir}/src/test/resources/test.jar</value>
+            </property>
+            <property>
+              <name>destDirPath</name>
+              <value>${basedir}/target/extracted</value>
+            </property>
+          </systemProperties>
+        </configuration>
+      </plugin>
+
+      <plugin>
+        <groupId>org.apache.directory.server</groupId>
+        <artifactId>apacheds-bootstrap-plugin</artifactId>
+        <configuration>
+          <disabledSchemas>
+            <disabledSchema>nis</disabledSchema>
+            <disabledSchema>krb5kdc</disabledSchema>
+            <disabledSchema>samba</disabledSchema>
+            <disabledSchema>autofs</disabledSchema>
+            <disabledSchema>apachedns</disabledSchema>
+            <disabledSchema>corba</disabledSchema>
+            <disabledSchema>dhcp</disabledSchema>
+            <disabledSchema>mozilla</disabledSchema>
+          </disabledSchemas>
+          <indexedAttributes>
+            <indexedAttribute>objectClass</indexedAttribute>
+            <indexedAttribute>ou</indexedAttribute>
+            <indexedAttribute>cn</indexedAttribute>
+            <indexedAttribute>m-oid</indexedAttribute>
+            <indexedAttribute>m-disabled</indexedAttribute>
+          </indexedAttributes>
+          <bootstrapSchemaClasses>
+            <bootstrapSchemaClass>org.apache.directory.server.schema.bootstrap.ApachednsSchema</bootstrapSchemaClass>
+            <bootstrapSchemaClass>org.apache.directory.server.schema.bootstrap.AutofsSchema</bootstrapSchemaClass>
+            <bootstrapSchemaClass>org.apache.directory.server.schema.bootstrap.CollectiveSchema</bootstrapSchemaClass>
+            <bootstrapSchemaClass>org.apache.directory.server.schema.bootstrap.CorbaSchema</bootstrapSchemaClass>
+            <bootstrapSchemaClass>org.apache.directory.server.schema.bootstrap.CosineSchema</bootstrapSchemaClass>
+            <bootstrapSchemaClass>org.apache.directory.server.schema.bootstrap.DhcpSchema</bootstrapSchemaClass>
+            <bootstrapSchemaClass>org.apache.directory.server.schema.bootstrap.InetorgpersonSchema</bootstrapSchemaClass>
+            <bootstrapSchemaClass>org.apache.directory.server.schema.bootstrap.JavaSchema</bootstrapSchemaClass>
+            <bootstrapSchemaClass>org.apache.directory.server.schema.bootstrap.Krb5kdcSchema</bootstrapSchemaClass>
+            <bootstrapSchemaClass>org.apache.directory.server.schema.bootstrap.MozillaSchema</bootstrapSchemaClass>
+            <bootstrapSchemaClass>org.apache.directory.server.schema.bootstrap.NisSchema</bootstrapSchemaClass>
+            <bootstrapSchemaClass>org.apache.directory.server.schema.bootstrap.SambaSchema</bootstrapSchemaClass>
+          </bootstrapSchemaClasses>
+        </configuration>
+        <executions>
+          <execution>
+            <goals>
+              <goal>load</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/old_trunk/bootstrap-partition/src/site/site.xml b/old_trunk/bootstrap-partition/src/site/site.xml
new file mode 100644
index 0000000..9ec7c16
--- /dev/null
+++ b/old_trunk/bootstrap-partition/src/site/site.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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.
+-->
+<!--
+  @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+-->
+<project name="${project.name}">
+    <bannerLeft>
+        <name>${project.name}</name>
+    </bannerLeft>
+    <publishDate position="navigation-bottom" format="dd-MM-yyyy HH:mm" />
+    <body>
+        <menu name="Parent">
+            <item name="Apache Directory ApacheDS" href="../index.html" />
+        </menu>
+        <menu ref="reports" />
+    </body>
+</project>
diff --git a/old_trunk/bootstrap-plugin/pom.xml b/old_trunk/bootstrap-plugin/pom.xml
new file mode 100644
index 0000000..59e5e14
--- /dev/null
+++ b/old_trunk/bootstrap-plugin/pom.xml
@@ -0,0 +1,98 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you 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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.directory.server</groupId>
+    <artifactId>apacheds-parent</artifactId>
+    <version>1.5.4-SNAPSHOT</version>
+  </parent>
+  <artifactId>apacheds-bootstrap-plugin</artifactId>
+  <name>ApacheDS Bootstrap Plugin</name>
+  <packaging>maven-plugin</packaging>
+
+  <description>
+    This plugin pre-loads a set of schema objects into a special fixed schema
+    partition within the server.  It uses the bootstrap schema objects to do
+    this at build time.  The schema partition files created are then packaged
+    using the jar plugin to become what is known as the bootstrap partition jar.
+  </description>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.maven</groupId>
+      <artifactId>maven-plugin-api</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.maven</groupId>
+      <artifactId>maven-project</artifactId>
+    </dependency>
+
+    <!-- plugin needs a logging implementation -->
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-log4j12</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-jdbm-store</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-core-constants</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-core-entry</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-schema-bootstrap</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-utils</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
+  </dependencies>
+
+  <reporting>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-plugin-plugin</artifactId>
+      </plugin>
+    </plugins>
+  </reporting>
+
+</project>
+
diff --git a/old_trunk/bootstrap-plugin/src/main/java/org/apache/directory/server/core/bootstrap/plugin/BootstrapPlugin.java b/old_trunk/bootstrap-plugin/src/main/java/org/apache/directory/server/core/bootstrap/plugin/BootstrapPlugin.java
new file mode 100644
index 0000000..b7f3d94
--- /dev/null
+++ b/old_trunk/bootstrap-plugin/src/main/java/org/apache/directory/server/core/bootstrap/plugin/BootstrapPlugin.java
@@ -0,0 +1,910 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.bootstrap.plugin;
+
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.constants.ApacheSchemaConstants;
+import org.apache.directory.server.constants.MetaSchemaConstants;
+import org.apache.directory.server.constants.ServerDNConstants;
+import org.apache.directory.server.core.entry.DefaultServerAttribute;
+import org.apache.directory.server.core.entry.DefaultServerEntry;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerModification;
+import org.apache.directory.server.core.partition.impl.btree.Index;
+import org.apache.directory.server.core.partition.impl.btree.IndexNotFoundException;
+import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmIndex;
+import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmStore;
+import org.apache.directory.server.schema.SerializableComparator;
+import org.apache.directory.server.schema.bootstrap.ApacheSchema;
+import org.apache.directory.server.schema.bootstrap.ApachemetaSchema;
+import org.apache.directory.server.schema.bootstrap.BootstrapSchema;
+import org.apache.directory.server.schema.bootstrap.BootstrapSchemaLoader;
+import org.apache.directory.server.schema.bootstrap.CoreSchema;
+import org.apache.directory.server.schema.bootstrap.Schema;
+import org.apache.directory.server.schema.bootstrap.SystemSchema;
+import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
+import org.apache.directory.server.schema.registries.ComparatorRegistry;
+import org.apache.directory.server.schema.registries.DefaultOidRegistry;
+import org.apache.directory.server.schema.registries.DefaultRegistries;
+import org.apache.directory.server.schema.registries.MatchingRuleRegistry;
+import org.apache.directory.server.schema.registries.NormalizerRegistry;
+import org.apache.directory.server.schema.registries.ObjectClassRegistry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.server.schema.registries.SyntaxCheckerRegistry;
+import org.apache.directory.server.schema.registries.SyntaxRegistry;
+import org.apache.directory.server.utils.AttributesFactory;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.entry.Modification;
+import org.apache.directory.shared.ldap.entry.ModificationOperation;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.schema.MatchingRule;
+import org.apache.directory.shared.ldap.schema.ObjectClass;
+import org.apache.directory.shared.ldap.schema.SchemaObject;
+import org.apache.directory.shared.ldap.schema.Syntax;
+import org.apache.directory.shared.ldap.schema.syntax.SyntaxChecker;
+import org.apache.directory.shared.ldap.util.DateUtils;
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.MojoFailureException;
+import org.codehaus.plexus.util.FileUtils;
+
+
+/**
+ * A plugin used to pre-load meta schema entries into the schema partition.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ * @goal load
+ * @description creates and pre-loads ApacheDS schema partition
+ * @phase compile
+ * @requiresDependencyResolution compile
+ */
+public class BootstrapPlugin extends AbstractMojo
+{
+    private static final String ADMIN_NORM_NAME = "0.9.2342.19200300.100.1.1=admin,2.5.4.11=system";
+
+    /**
+     * The classpath elements of the project being tested.
+     *
+     * @parameter expression="${project.compileClasspathElements}"
+     * @required
+     * @readonly
+     */
+    private List<String> classpathElements;
+
+    /**
+     * The package to put the db file entry listing info as well as the partition.
+     *
+     * @parameter expression="org.apache.directory.server.schema.bootstrap.partition"
+     */
+    private String outputPackage;
+
+    /**
+     * The file name to use for the package listing.
+     *
+     * @parameter expression="DBFILES"
+     */
+    private String listingFileName;
+
+    /**
+     * The target directory into which the plugin generates schema partion files
+     * within the specified outputPackage.
+     *
+     * @parameter expression="target/classes"
+     */
+    private File outputDirectory;
+
+    /**
+     * The name of the set of bootstrap schemas to load into the registries
+     * and ultimately into the schema partition being built.
+     *
+     * @parameter
+     */
+    private String[] bootstrapSchemaClasses;
+
+    /**
+     * The set of disabled schema names.
+     *
+     * @parameter
+     */
+    private String[] disabledSchemas;
+
+    /**
+     * The names of Attributes to index.
+     *
+     * @parameter
+     */
+    private String[] indexedAttributes;
+
+    /**
+     * Factory used to create attributes objects from schema entities.
+     */
+    private AttributesFactory attributesFactory = new AttributesFactory();
+
+    /**
+     * Registries of objects used to load the schema partition.
+     */
+    private Registries registries;
+
+    /**
+     * The store to load schema entities into.
+     */
+    private JdbmStore store = new JdbmStore();
+
+    /**
+     * Map of schemas by name
+     */
+    private Map<String, Schema> schemas = new HashMap<String, Schema>();
+
+
+    /**
+     * Loads a bunch of bootstrap classes into memory then adds them to a new
+     * schema partition within the target area.  The db files for this partition
+     * are then packaged into the jar by the jar plugin.
+     */
+    public void execute() throws MojoExecutionException, MojoFailureException
+    {
+        File packageDirectory = new File( outputDirectory, outputPackage.replace( '.', File.separatorChar ) );
+
+        if ( !packageDirectory.exists() )
+        {
+            packageDirectory.mkdirs();
+        }
+
+        // delete output directory if it exists
+        File schemaDirectory = new File( packageDirectory, "schema" );
+
+        if ( schemaDirectory.exists() )
+        {
+            try
+            {
+                FileUtils.forceDelete( schemaDirectory );
+            }
+            catch ( IOException e )
+            {
+                throw new MojoFailureException( "Failed to delete old schema partition folder "
+                    + schemaDirectory.getAbsolutePath() + ": " + e.getMessage() );
+            }
+        }
+
+        initializeSchemas();
+
+        try
+        {
+            initializePartition( schemaDirectory );
+        }
+        catch ( NamingException ne )
+        {
+            throw new MojoFailureException( "Failed to initialize the root partition :" + ne.getMessage() );
+        }
+
+        try
+        {
+            LdapDN dn = new LdapDN( SchemaConstants.OU_AT + "=schema" );
+            dn.normalize( registries.getAttributeTypeRegistry().getNormalizerMapping() );
+
+            if ( !hasEntry( dn ) )
+            {
+                ServerEntry entry = new DefaultServerEntry( registries, dn );
+                entry.put( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC );
+                entry.get( SchemaConstants.OBJECT_CLASS_AT ).add( SchemaConstants.ORGANIZATIONAL_UNIT_OC );
+                entry.put( SchemaConstants.OU_AT, "schema" );
+                store.add( dn, entry );
+            }
+
+            createSchemasAndContainers();
+
+            addSyntaxCheckers();
+            addSyntaxes();
+            addNormalizers();
+            addComparators();
+            addMatchingRules();
+            addAttributeTypes();
+            addObjectClasses();
+
+            if ( disabledSchemas != null && disabledSchemas.length > 0 )
+            {
+                getLog().info( "------------------------------------------------------------------------" );
+                getLog().info( "Disabling schemas:" );
+                getLog().info( "------------------------------------------------------------------------" );
+                getLog().info( "" );
+
+                for ( String disabledSchema : disabledSchemas )
+                {
+                    disableSchema( disabledSchema );
+                    getLog().info( "\t\t o " + disabledSchema );
+                }
+
+                getLog().info( "" );
+                getLog().info( "------------------------------------------------------------------------" );
+            }
+
+            createSchemaModificationAttributesEntry();
+        }
+        catch ( NamingException e )
+        {
+            throw new MojoFailureException( "Failed to add syntaxCheckers to partition: " + e.getMessage() );
+        }
+
+        try
+        {
+            store.sync();
+        }
+        catch ( NamingException e )
+        {
+            e.printStackTrace();
+        }
+
+        // ------------------------------------------------------------------
+        // Create db file listing and place it into the right package on disk
+        // ------------------------------------------------------------------
+
+        File listingFile = new File( packageDirectory, listingFileName );
+        PrintWriter out = null;
+        try
+        {
+            out = new PrintWriter( new FileWriter( listingFile ) );
+            out.print( getDbFileListing().toString() );
+            out.flush();
+        }
+        catch ( IOException e )
+        {
+            throw new MojoFailureException( "Failed to write file: " + e.getMessage() );
+        }
+        catch ( IndexNotFoundException e )
+        {
+            // never really thrown
+            e.printStackTrace();
+        }
+        finally
+        {
+            if ( out != null )
+            {
+                out.close();
+            }
+        }
+    }
+
+    private static final String[] OTHER_SCHEMA_DEPENDENCIES = new String[]
+        { "system", "core", "apache", "apachemeta" };
+
+
+    private void createSchemasAndContainers() throws NamingException
+    {
+        Map<String, Schema> schemaMap = this.registries.getLoadedSchemas();
+
+        for ( Schema schema : schemaMap.values() )
+        {
+            createSchemaAndContainers( schema );
+        }
+
+        Schema other = new Schema()
+        {
+            public String[] getDependencies()
+            {
+                return OTHER_SCHEMA_DEPENDENCIES;
+            }
+
+
+            public String getOwner()
+            {
+                return "uid=admin,ou=system";
+            }
+
+
+            public String getSchemaName()
+            {
+                return "other";
+            }
+
+
+            public boolean isDisabled()
+            {
+                return false;
+            }
+        };
+
+        createSchemaAndContainers( other );
+    }
+
+
+    private void createSchemaAndContainers( Schema schema ) throws NamingException
+    {
+        LdapDN dn = new LdapDN( SchemaConstants.CN_AT + "=" + schema.getSchemaName() + "," + SchemaConstants.OU_AT
+            + "=schema" );
+        dn.normalize( registries.getAttributeTypeRegistry().getNormalizerMapping() );
+
+        if ( hasEntry( dn ) )
+        {
+            return;
+        }
+
+        ServerEntry entry = attributesFactory.getAttributes( schema, registries );
+        entry.setDn( dn );
+        store.add( dn, entry );
+
+        dn = ( LdapDN ) dn.clone();
+
+        dn.add( SchemaConstants.OU_AT + "=comparators" );
+        dn.normalize( registries.getAttributeTypeRegistry().getNormalizerMapping() );
+        checkCreateContainer( dn );
+
+        dn.remove( dn.size() - 1 );
+        dn.add( SchemaConstants.OU_AT + "=normalizers" );
+        dn.normalize( registries.getAttributeTypeRegistry().getNormalizerMapping() );
+        checkCreateContainer( dn );
+
+        dn.remove( dn.size() - 1 );
+        dn.add( SchemaConstants.OU_AT + "=syntaxCheckers" );
+        dn.normalize( registries.getAttributeTypeRegistry().getNormalizerMapping() );
+        checkCreateContainer( dn );
+
+        dn.remove( dn.size() - 1 );
+        dn.add( SchemaConstants.OU_AT + "=syntaxes" );
+        dn.normalize( registries.getAttributeTypeRegistry().getNormalizerMapping() );
+        checkCreateContainer( dn );
+
+        dn.remove( dn.size() - 1 );
+        dn.add( SchemaConstants.OU_AT + "=matchingRules" );
+        dn.normalize( registries.getAttributeTypeRegistry().getNormalizerMapping() );
+        checkCreateContainer( dn );
+
+        dn.remove( dn.size() - 1 );
+        dn.add( SchemaConstants.OU_AT + "=attributeTypes" );
+        dn.normalize( registries.getAttributeTypeRegistry().getNormalizerMapping() );
+        checkCreateContainer( dn );
+
+        dn.remove( dn.size() - 1 );
+        dn.add( SchemaConstants.OU_AT + "=objectClasses" );
+        dn.normalize( registries.getAttributeTypeRegistry().getNormalizerMapping() );
+        checkCreateContainer( dn );
+
+        dn.remove( dn.size() - 1 );
+        dn.add( SchemaConstants.OU_AT + "=nameForms" );
+        dn.normalize( registries.getAttributeTypeRegistry().getNormalizerMapping() );
+        checkCreateContainer( dn );
+
+        dn.remove( dn.size() - 1 );
+        dn.add( SchemaConstants.OU_AT + "=ditStructureRules" );
+        dn.normalize( registries.getAttributeTypeRegistry().getNormalizerMapping() );
+        checkCreateContainer( dn );
+
+        dn.remove( dn.size() - 1 );
+        dn.add( SchemaConstants.OU_AT + "=ditContentRules" );
+        dn.normalize( registries.getAttributeTypeRegistry().getNormalizerMapping() );
+        checkCreateContainer( dn );
+
+        dn.remove( dn.size() - 1 );
+        dn.add( SchemaConstants.OU_AT + "=matchingRuleUse" );
+        dn.normalize( registries.getAttributeTypeRegistry().getNormalizerMapping() );
+        checkCreateContainer( dn );
+    }
+
+
+    private void addAttributeTypes() throws NamingException
+    {
+        getLog().info( "------------------------------------------------------------------------" );
+        getLog().info( " Adding attributeTypes:" );
+        getLog().info( "------------------------------------------------------------------------" );
+        getLog().info( "" );
+
+        AttributeTypeRegistry attributeTypeRegistry = registries.getAttributeTypeRegistry();
+
+        Iterator<AttributeType> ii = attributeTypeRegistry.iterator();
+
+        while ( ii.hasNext() )
+        {
+            AttributeType at = ii.next();
+            String schemaName = attributeTypeRegistry.getSchemaName( at.getOid() );
+            Schema schema = registries.getLoadedSchemas().get( schemaName );
+            getLog().info( "\t\t o [" + schemaName + "] - " + getNameOrNumericoid( at ) );
+            LdapDN dn = checkCreateSchema( schemaName );
+            dn.add( SchemaConstants.OU_AT + "=attributeTypes" );
+            dn.normalize( registries.getAttributeTypeRegistry().getNormalizerMapping() );
+            checkCreateContainer( dn );
+            ServerEntry entry = attributesFactory.getAttributes( at, schema, registries );
+            dn.add( MetaSchemaConstants.M_OID_AT + "=" + at.getOid() );
+            dn.normalize( registries.getAttributeTypeRegistry().getNormalizerMapping() );
+            entry.setDn( dn );
+            store.add( dn, entry );
+        }
+
+        getLog().info( "" );
+    }
+
+
+    private void addObjectClasses() throws NamingException
+    {
+        getLog().info( "------------------------------------------------------------------------" );
+        getLog().info( " Adding objectClasses:" );
+        getLog().info( "------------------------------------------------------------------------" );
+        getLog().info( "" );
+
+        ObjectClassRegistry objectClassRegistry = registries.getObjectClassRegistry();
+        Iterator<ObjectClass> ii = objectClassRegistry.iterator();
+
+        while ( ii.hasNext() )
+        {
+            ObjectClass oc = ii.next();
+            String schemaName = objectClassRegistry.getSchemaName( oc.getOid() );
+            Schema schema = registries.getLoadedSchemas().get( schemaName );
+            getLog().info( "\t\t o [" + schemaName + "] - " + getNameOrNumericoid( oc ) );
+            LdapDN dn = checkCreateSchema( schemaName );
+            dn.add( SchemaConstants.OU_AT + "=objectClasses" );
+            dn.normalize( registries.getAttributeTypeRegistry().getNormalizerMapping() );
+            checkCreateContainer( dn );
+            ServerEntry entry = attributesFactory.getAttributes( oc, schema, registries );
+            dn.add( MetaSchemaConstants.M_OID_AT + "=" + oc.getOid() );
+            dn.normalize( registries.getAttributeTypeRegistry().getNormalizerMapping() );
+            entry.setDn( dn );
+            store.add( dn, entry );
+        }
+
+        getLog().info( "" );
+    }
+
+
+    private void addMatchingRules() throws NamingException
+    {
+        getLog().info( "------------------------------------------------------------------------" );
+        getLog().info( " Adding matchingRules:" );
+        getLog().info( "------------------------------------------------------------------------" );
+        getLog().info( "" );
+
+        MatchingRuleRegistry matchingRuleRegistry = registries.getMatchingRuleRegistry();
+        Iterator<MatchingRule> ii = matchingRuleRegistry.iterator();
+
+        while ( ii.hasNext() )
+        {
+            MatchingRule mr = ii.next();
+            String schemaName = matchingRuleRegistry.getSchemaName( mr.getOid() );
+            Schema schema = registries.getLoadedSchemas().get( schemaName );
+            getLog().info( "\t\t o [" + schemaName + "] - " + getNameOrNumericoid( mr ) );
+            LdapDN dn = checkCreateSchema( schemaName );
+            dn.add( SchemaConstants.OU_AT + "=matchingRules" );
+            dn.normalize( registries.getAttributeTypeRegistry().getNormalizerMapping() );
+            checkCreateContainer( dn );
+            ServerEntry entry = attributesFactory.getAttributes( mr, schema, registries );
+            dn.add( MetaSchemaConstants.M_OID_AT + "=" + mr.getOid() );
+            dn.normalize( registries.getAttributeTypeRegistry().getNormalizerMapping() );
+            entry.setDn( dn );
+            store.add( dn, entry );
+        }
+
+        getLog().info( "" );
+    }
+
+
+    private void addComparators() throws NamingException
+    {
+        getLog().info( "------------------------------------------------------------------------" );
+        getLog().info( " Adding comparators:" );
+        getLog().info( "------------------------------------------------------------------------" );
+        getLog().info( "" );
+
+        ComparatorRegistry comparatorRegistry = registries.getComparatorRegistry();
+        Iterator<String> ii = comparatorRegistry.oidIterator();
+
+        while ( ii.hasNext() )
+        {
+            String oid = ii.next();
+            String schemaName = comparatorRegistry.getSchemaName( oid );
+            Schema schema = registries.getLoadedSchemas().get( schemaName );
+            getLog().info( "\t\t o [" + schemaName + "] - " + oid );
+            LdapDN dn = checkCreateSchema( schemaName );
+            dn.add( SchemaConstants.OU_AT + "=comparators" );
+            dn.normalize( registries.getAttributeTypeRegistry().getNormalizerMapping() );
+            checkCreateContainer( dn );
+            ServerEntry entry = attributesFactory.getAttributes( oid, comparatorRegistry.lookup( oid ), schema,
+                registries );
+            dn.add( MetaSchemaConstants.M_OID_AT + "=" + oid );
+            dn.normalize( registries.getAttributeTypeRegistry().getNormalizerMapping() );
+            entry.setDn( dn );
+            store.add( dn, entry );
+        }
+        getLog().info( "" );
+    }
+
+
+    private void addNormalizers() throws NamingException
+    {
+        getLog().info( "------------------------------------------------------------------------" );
+        getLog().info( " Adding normalizers:" );
+        getLog().info( "------------------------------------------------------------------------" );
+        getLog().info( "" );
+
+        NormalizerRegistry normalizerRegistry = registries.getNormalizerRegistry();
+        Iterator<String> ii = normalizerRegistry.oidIterator();
+
+        while ( ii.hasNext() )
+        {
+            String oid = ii.next();
+            String schemaName = normalizerRegistry.getSchemaName( oid );
+            Schema schema = registries.getLoadedSchemas().get( schemaName );
+            getLog().info( "\t\t o [" + schemaName + "] - " + oid );
+            LdapDN dn = checkCreateSchema( schemaName );
+            dn.add( SchemaConstants.OU_AT + "=normalizers" );
+            dn.normalize( registries.getAttributeTypeRegistry().getNormalizerMapping() );
+            checkCreateContainer( dn );
+            ServerEntry entry = attributesFactory.getAttributes( oid, normalizerRegistry.lookup( oid ), schema,
+                registries );
+            dn.add( MetaSchemaConstants.M_OID_AT + "=" + oid );
+            dn.normalize( registries.getAttributeTypeRegistry().getNormalizerMapping() );
+            entry.setDn( dn );
+            store.add( dn, entry );
+        }
+
+        getLog().info( "" );
+    }
+
+
+    private void addSyntaxes() throws NamingException
+    {
+        getLog().info( "------------------------------------------------------------------------" );
+        getLog().info( " Adding syntaxes:" );
+        getLog().info( "------------------------------------------------------------------------" );
+        getLog().info( "" );
+
+        SyntaxRegistry syntaxRegistry = registries.getSyntaxRegistry();
+        Iterator<Syntax> ii = syntaxRegistry.iterator();
+
+        while ( ii.hasNext() )
+        {
+            Syntax syntax = ii.next();
+            getLog().info( "\t\t o [" + syntax.getSchema() + "] - " + getNameOrNumericoid( syntax ) );
+            LdapDN dn = checkCreateSchema( syntax.getSchema() );
+            Schema schema = registries.getLoadedSchemas().get( syntax.getSchema() );
+            dn.add( SchemaConstants.OU_AT + "=syntaxes" );
+            dn.normalize( registries.getAttributeTypeRegistry().getNormalizerMapping() );
+            checkCreateContainer( dn );
+            ServerEntry entry = attributesFactory.getAttributes( syntax, schema, registries );
+            dn.add( MetaSchemaConstants.M_OID_AT + "=" + syntax.getOid() );
+            dn.normalize( registries.getAttributeTypeRegistry().getNormalizerMapping() );
+            entry.setDn( dn );
+            store.add( dn, entry );
+        }
+        getLog().info( "" );
+    }
+
+
+    private void addSyntaxCheckers() throws NamingException
+    {
+        getLog().info( "------------------------------------------------------------------------" );
+        getLog().info( " Adding syntaxCheckers:" );
+        getLog().info( "------------------------------------------------------------------------" );
+        getLog().info( "" );
+
+        SyntaxCheckerRegistry syntaxCheckerRegistry = registries.getSyntaxCheckerRegistry();
+        Iterator<SyntaxChecker> ii = syntaxCheckerRegistry.iterator();
+
+        while ( ii.hasNext() )
+        {
+            SyntaxChecker syntaxChecker = ii.next();
+            String schemaName = syntaxCheckerRegistry.getSchemaName( syntaxChecker.getSyntaxOid() );
+            Schema schema = registries.getLoadedSchemas().get( schemaName );
+            getLog().info( "\t\t o [" + schemaName + "] - " + syntaxChecker.getSyntaxOid() );
+            LdapDN dn = checkCreateSchema( schemaName );
+            dn.add( SchemaConstants.OU_AT + "=syntaxCheckers" );
+            dn.normalize( registries.getAttributeTypeRegistry().getNormalizerMapping() );
+            checkCreateContainer( dn );
+            ServerEntry entry = attributesFactory.getAttributes( syntaxChecker, schema, registries );
+            dn.add( MetaSchemaConstants.M_OID_AT + "=" + syntaxChecker.getSyntaxOid() );
+            dn.normalize( registries.getAttributeTypeRegistry().getNormalizerMapping() );
+            entry.setDn( dn );
+            store.add( dn, entry );
+        }
+
+        getLog().info( "" );
+    }
+
+
+    /**
+     * Creates the configuration and initializes the partition so we can start
+     * adding entries into it.
+     *
+     * @throws MojoFailureException
+     */
+    private void initializePartition( File workingDirectory ) throws MojoFailureException, NamingException
+    {
+        store.setCacheSize( 1000 );
+        store.setEnableOptimizer( false );
+        store.setName( "schema" );
+        store.setSuffixDn( SchemaConstants.OU_AT + "=schema" );
+        store.setSyncOnWrite( false );
+        store.setWorkingDirectory( workingDirectory );
+
+        // add the indices
+        Set<JdbmIndex> userIndices = new HashSet<JdbmIndex>();
+
+        for ( String indexedAttribute : indexedAttributes )
+        {
+            JdbmIndex index = new JdbmIndex();
+            index.setAttributeId( indexedAttribute );
+            userIndices.add( index );
+        }
+
+        store.setUserIndices( userIndices );
+
+        ServerEntry rootEntry = new DefaultServerEntry( registries, new LdapDN( ServerDNConstants.OU_SCHEMA_DN ) );
+        rootEntry.put( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC, SchemaConstants.ORGANIZATIONAL_UNIT_OC );
+        rootEntry.put( SchemaConstants.OU_AT, "schema" );
+        store.setContextEntry( rootEntry );
+
+        try
+        {
+            store.init( this.registries );
+        }
+        catch ( NamingException e )
+        {
+            throw new MojoFailureException( "Failed to initialize parition: " + e.getMessage() );
+        }
+    }
+
+
+    /**
+     * Creates the special schemaModificationsAttribute entry used to
+     * store the modification attributes for the schema.  The current
+     * time is used to create the initial values for the attributes in
+     * this entry.
+     * 
+     * @throws NamingException if there is a failure to add the entry 
+     */
+    private void createSchemaModificationAttributesEntry() throws NamingException
+    {
+        ServerEntry entry = new DefaultServerEntry( registries );
+        entry.put( SchemaConstants.OBJECT_CLASS_AT, ApacheSchemaConstants.SCHEMA_MODIFICATION_ATTRIBUTES_OC,
+            SchemaConstants.TOP_OC );
+
+        entry.put( ApacheSchemaConstants.SCHEMA_MODIFIERS_NAME_AT, ADMIN_NORM_NAME );
+        entry.put( SchemaConstants.MODIFIERS_NAME_AT, ADMIN_NORM_NAME );
+        entry.put( SchemaConstants.CREATORS_NAME_AT, ADMIN_NORM_NAME );
+
+        entry.put( ApacheSchemaConstants.SCHEMA_MODIFY_TIMESTAMP_AT, DateUtils.getGeneralizedTime() );
+        entry.put( SchemaConstants.MODIFY_TIMESTAMP_AT, DateUtils.getGeneralizedTime() );
+        entry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() );
+
+        entry.put( SchemaConstants.CN_AT, "schemaModifications" );
+        entry.put( ApacheSchemaConstants.SUBSCHEMA_SUBENTRY_NAME_AT, "cn=schema" );
+
+        LdapDN normName = new LdapDN( "cn=schemaModifications,ou=schema" );
+        normName.normalize( registries.getAttributeTypeRegistry().getNormalizerMapping() );
+        entry.setDn( normName );
+        store.add( normName, entry );
+    }
+
+
+    /**
+     * Loads all the bootstrap schemas into the registries in preparation for
+     * loading them into the schema partition.
+     *
+     * @throws MojoFailureException
+     */
+    private void initializeSchemas() throws MojoFailureException
+    {
+        // -------------------------------------------------------------------
+        // load the bootstrap schemas to pre-load into the partition
+        // -------------------------------------------------------------------
+
+        // always include these core bootstrap schemas
+        BootstrapSchema schema = new SystemSchema();
+        schemas.put( schema.getSchemaName(), schema );
+
+        schema = new ApacheSchema();
+        schemas.put( schema.getSchemaName(), schema );
+
+        schema = new ApachemetaSchema();
+        schemas.put( schema.getSchemaName(), schema );
+
+        schema = new CoreSchema();
+        schemas.put( schema.getSchemaName(), schema );
+
+        getLog().info( "------------------------------------------------------------------------" );
+        getLog().info( "Found bootstrap schemas: " );
+        getLog().info( "------------------------------------------------------------------------" );
+        getLog().info( "" );
+
+        // start loading other schemas from the plugin's configuration section
+        ClassLoader parent = getClass().getClassLoader();
+        URL[] urls = new URL[classpathElements.size()];
+        int i = 0;
+
+        for ( String classpathElement : classpathElements )
+        {
+            try
+            {
+                urls[i++] = new File( classpathElement ).toURI().toURL();
+            }
+            catch ( MalformedURLException e )
+            {
+                throw ( MojoFailureException ) new MojoFailureException( "Could not construct classloader: " )
+                    .initCause( e );
+            }
+        }
+
+        ClassLoader cl = new URLClassLoader( urls, parent );
+
+        for ( int ii = 0; ii < bootstrapSchemaClasses.length; ii++ )
+        {
+            try
+            {
+                Class<?> schemaClass = cl.loadClass( bootstrapSchemaClasses[ii] );
+                schema = ( BootstrapSchema ) schemaClass.newInstance();
+                schemas.put( schema.getSchemaName(), schema );
+            }
+            catch ( ClassNotFoundException e )
+            {
+                getLog().info( "ClassLoader " + getClass().getClassLoader() );
+                getLog()
+                    .info(
+                        "ClassLoader URLs: "
+                            + Arrays.asList( ( ( URLClassLoader ) getClass().getClassLoader() ).getURLs() ) );
+                e.printStackTrace();
+                throw new MojoFailureException( "Could not find BootstrapSchema class: " + bootstrapSchemaClasses[ii] );
+            }
+            catch ( InstantiationException e )
+            {
+                e.printStackTrace();
+                throw new MojoFailureException( "Could not instantiate BootstrapSchema class: "
+                    + bootstrapSchemaClasses[ii] );
+            }
+            catch ( IllegalAccessException e )
+            {
+                e.printStackTrace();
+                throw new MojoFailureException( "Could not instantiate BootstrapSchema class due to security: "
+                    + bootstrapSchemaClasses[ii] );
+            }
+
+            getLog().info( "\t" + bootstrapSchemaClasses[ii] );
+        }
+
+        getLog().info( "" );
+
+        BootstrapSchemaLoader loader = new BootstrapSchemaLoader( cl );
+        registries = new DefaultRegistries( "bootstrap", loader, new DefaultOidRegistry() );
+
+        try
+        {
+            loader.loadWithDependencies( schemas.values(), registries );
+        }
+        catch ( NamingException e )
+        {
+            e.printStackTrace();
+            throw new MojoFailureException( "Failed to load bootstrap registries with schemas: " + e.getMessage() );
+        }
+
+        SerializableComparator.setRegistry( registries.getComparatorRegistry() );
+    }
+
+
+    private void checkCreateContainer( LdapDN dn ) throws NamingException
+    {
+        LdapDN clonedDn = ( LdapDN ) dn.clone();
+
+        if ( hasEntry( clonedDn ) )
+        {
+            return;
+        }
+
+        ServerEntry entry = new DefaultServerEntry( registries, clonedDn );
+        entry.put( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC, SchemaConstants.ORGANIZATIONAL_UNIT_OC );
+        entry.put( SchemaConstants.OU_AT, ( String ) clonedDn.getRdn().getValue() );
+        store.add( clonedDn, entry );
+    }
+
+
+    private LdapDN checkCreateSchema( String schemaName ) throws NamingException
+    {
+        Schema schema = schemas.get( schemaName );
+        LdapDN dn = new LdapDN( SchemaConstants.CN_AT + "=" + schemaName + "," + SchemaConstants.OU_AT + "=schema" );
+        dn.normalize( registries.getAttributeTypeRegistry().getNormalizerMapping() );
+
+        if ( hasEntry( dn ) )
+        {
+            return dn;
+        }
+
+        ServerEntry entry = attributesFactory.getAttributes( schema, registries );
+        entry.setDn( dn );
+        store.add( dn, entry );
+        return dn;
+    }
+
+
+    private void disableSchema( String schemaName ) throws NamingException
+    {
+        LdapDN dn = new LdapDN( SchemaConstants.CN_AT + "=" + schemaName + "," + SchemaConstants.OU_AT + "=schema" );
+        dn.normalize( registries.getAttributeTypeRegistry().getNormalizerMapping() );
+
+        Modification mod = new ServerModification( ModificationOperation.ADD_ATTRIBUTE, new DefaultServerAttribute(
+            MetaSchemaConstants.M_DISABLED_AT, registries.getAttributeTypeRegistry().lookup(
+                MetaSchemaConstants.M_DISABLED_AT ), "TRUE" ) );
+
+        List<Modification> mods = new ArrayList<Modification>();
+        mods.add( mod );
+        store.modify( dn, mods );
+    }
+
+
+    private final String getNameOrNumericoid( SchemaObject object )
+    {
+        // first try to use userfriendly name if we can
+        if ( object.getName() != null )
+        {
+            return object.getName();
+        }
+
+        return object.getOid();
+    }
+
+
+    private final boolean hasEntry( LdapDN dn ) throws NamingException
+    {
+        Long id = store.getEntryId( dn.toNormName() );
+
+        return ( id != null );
+    }
+
+
+    private final StringBuffer getDbFileListing() throws IndexNotFoundException
+    {
+        StringBuffer buf = new StringBuffer();
+        buf.append( "schema/master.db\n" );
+
+        Iterator<String> systemIndices = store.systemIndices();
+
+        while ( systemIndices.hasNext() )
+        {
+            Index index = store.getSystemIndex( systemIndices.next() );
+            buf.append( "schema/" );
+            buf.append( index.getAttribute().getName() );
+            buf.append( ".db\n" );
+        }
+
+        buf.append( "[USER INDICES]\n" );
+
+        for ( String indexedAttribute : indexedAttributes )
+        {
+            buf.append( "schema/" );
+            buf.append( indexedAttribute );
+            buf.append( ".db\n" );
+        }
+
+        return buf;
+    }
+}
diff --git a/old_trunk/bootstrap-plugin/src/main/resources/log4j.properties b/old_trunk/bootstrap-plugin/src/main/resources/log4j.properties
new file mode 100644
index 0000000..b05742f
--- /dev/null
+++ b/old_trunk/bootstrap-plugin/src/main/resources/log4j.properties
@@ -0,0 +1,22 @@
+#############################################################################
+#    Licensed to the Apache Software Foundation (ASF) under one or more
+#    contributor license agreements.  See the NOTICE file distributed with
+#    this work for additional information regarding copyright ownership.
+#    The ASF licenses this file to You 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.
+#############################################################################
+log4j.rootCategory=ERROR, stdout
+
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=[%d{HH:mm:ss}] %p [%c] - %m%n
+
diff --git a/old_trunk/bootstrap-plugin/src/site/site.xml b/old_trunk/bootstrap-plugin/src/site/site.xml
new file mode 100644
index 0000000..15d43f9
--- /dev/null
+++ b/old_trunk/bootstrap-plugin/src/site/site.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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.
+-->
+<!--
+  @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+-->
+<project name="${project.name}">
+  <bannerLeft>
+    <name>${project.name}</name>
+  </bannerLeft>
+  <publishDate position="navigation-bottom" format="dd-MM-yyyy HH:mm" />
+  <body>
+    <menu name="Parent">
+      <item name="Apache Directory ApacheDS" href="../index.html" />
+    </menu>
+    <menu name="Overview">
+      <item name="Goals" href="plugin-info.html" />
+    </menu>
+    <menu ref="reports" />
+  </body>
+</project>
diff --git a/old_trunk/btree-base/pom.xml b/old_trunk/btree-base/pom.xml
new file mode 100644
index 0000000..b020a84
--- /dev/null
+++ b/old_trunk/btree-base/pom.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you 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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.directory.server</groupId>
+    <artifactId>apacheds-parent</artifactId>
+    <version>1.5.4-SNAPSHOT</version>
+  </parent>
+  <artifactId>apacheds-btree-base</artifactId>
+  <name>ApacheDS BTree Base</name>
+  <packaging>jar</packaging>
+
+  <description>
+    Base BTree interfaces used by the BTreePartition and JdbmPartitions as well
+    as by the JdbmStore.  Used to break cyclic dependencies.
+  </description>
+
+  <dependencies>
+    <dependency>
+      <groupId>${pom.groupId}</groupId>
+      <artifactId>apacheds-schema-registries</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>${pom.groupId}</groupId>
+      <artifactId>apacheds-core-entry</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
+  </dependencies>
+</project>
+
diff --git a/old_trunk/btree-base/src/main/java/org/apache/directory/server/core/partition/impl/btree/Index.java b/old_trunk/btree-base/src/main/java/org/apache/directory/server/core/partition/impl/btree/Index.java
new file mode 100644
index 0000000..17b22b7
--- /dev/null
+++ b/old_trunk/btree-base/src/main/java/org/apache/directory/server/core/partition/impl/btree/Index.java
@@ -0,0 +1,212 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree;
+
+
+import java.io.File;
+import java.util.regex.Pattern;
+
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+
+import org.apache.directory.shared.ldap.schema.AttributeType;
+
+
+/**
+ * Required interfaces for an index.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public interface Index
+{
+    int DEFAULT_INDEX_CACHE_SIZE = 100;
+    
+    // -----------------------------------------------------------------------
+    // C O N F I G U R A T I O N   M E T H O D S
+    // -----------------------------------------------------------------------
+
+
+    /**
+     * Gets the attribute identifier set at configuration time for this index which may not
+     * be the OID but an alias name for the attributeType associated with this Index
+     *
+     * @return configured attribute oid or alias name
+     */
+    String getAttributeId();
+
+
+    /**
+     * Sets the attribute identifier set at configuration time for this index which may not
+     * be the OID but an alias name for the attributeType associated with this Index
+     *
+     * @param attributeId configured attribute oid or alias name
+     */
+    void setAttributeId( String attributeId );
+
+
+    /**
+     * Gets the size of the index cache in terms of the number of index entries to be cached.
+     *
+     * @return the size of the index cache
+     */
+    int getCacheSize();
+
+
+    /**
+     * Sets the size of the index cache in terms of the number of index entries to be cached.
+     *
+     * @param cacheSize the size of the index cache
+     */
+    void setCacheSize( int cacheSize );
+
+
+    /**
+     * Sets the working directory path to something other than the default. Sometimes more
+     * performance is gained by locating indices on separate disk spindles.
+     *
+     * @param wkDirPath optional working directory path
+     */
+    void setWkDirPath( File wkDirPath );
+
+
+    /**
+     * Gets the working directory path to something other than the default. Sometimes more
+     * performance is gained by locating indices on separate disk spindles.
+     *
+     * @return optional working directory path
+     */
+    File getWkDirPath();
+
+
+    // -----------------------------------------------------------------------
+    // E N D   C O N F I G U R A T I O N   M E T H O D S
+    // -----------------------------------------------------------------------
+
+
+    /**
+     * Gets the attribute this Index is built upon.
+     *
+     * @return the id of the Index's attribute
+     */
+    AttributeType getAttribute();
+
+
+    /**
+     * Gets the normalized value for an attribute.
+     *
+     * @param attrVal the user provided value to normalize
+     * @return the normalized value.
+     * @throws NamingException if something goes wrong.
+     */
+    Object getNormalized( Object attrVal ) throws NamingException;
+
+
+    /**
+     * Gets the total scan count for this index.
+     *
+     * @return the number of key/value pairs in this index
+     * @throws NamingException if their is a failure accessing the index
+     */
+    int count() throws NamingException;
+
+
+    /**
+     * Gets the scan count for the occurance of a specific attribute value 
+     * within the index.
+     *
+     * @param attrVal the value of the attribute to get a scan count for
+     * @return the number of key/value pairs in this index with the value value
+     * @throws NamingException if their is a failure accessing the index
+     */
+    int count( Object attrVal ) throws NamingException;
+
+
+    int count( Object attrVal, boolean isGreaterThan ) throws NamingException;
+
+
+    Object forwardLookup( Object attrVal ) throws NamingException;
+
+
+    Object reverseLookup( Object id ) throws NamingException;
+
+
+    void add( Object attrVal, Object id ) throws NamingException;
+
+
+    void add( Attribute attr, Object id ) throws NamingException;
+
+
+    void add( Attributes attrs, Object id ) throws NamingException;
+
+
+    void drop( Object entryId ) throws NamingException;
+
+
+    void drop( Object attrVal, Object id ) throws NamingException;
+
+
+    /**
+     * If the Attribute does not have any values then this reduces to a 
+     * drop(BigInteger) call.
+     */
+    void drop( Attribute attr, Object id ) throws NamingException;
+
+
+    /**
+     * If the Attribute for this index within the Attributes does not have any 
+     * values then this reduces to a drop(BigInteger) call.
+     */
+    void drop( Attributes attrs, Object id ) throws NamingException;
+
+
+    IndexEnumeration listReverseIndices( Object id ) throws NamingException;
+
+
+    IndexEnumeration<IndexRecord> listIndices() throws NamingException;
+
+
+    IndexEnumeration<IndexRecord> listIndices( Object attrVal ) throws NamingException;
+
+
+    IndexEnumeration<Tuple> listIndices( Object attrVal, boolean isGreaterThan ) throws NamingException;
+
+
+    IndexEnumeration<Tuple> listIndices( Pattern regex ) throws NamingException;
+
+
+    IndexEnumeration<Tuple> listIndices( Pattern regex, String prefix ) throws NamingException;
+
+
+    boolean hasValue( Object attrVal, Object id ) throws NamingException;
+
+
+    boolean hasValue( Object attrVal, Object id, boolean isGreaterThan ) throws NamingException;
+
+
+    boolean hasValue( Pattern regex, Object id ) throws NamingException;
+
+
+    void close() throws NamingException;
+
+
+    void sync() throws NamingException;
+}
diff --git a/old_trunk/btree-base/src/main/java/org/apache/directory/server/core/partition/impl/btree/IndexAssertion.java b/old_trunk/btree-base/src/main/java/org/apache/directory/server/core/partition/impl/btree/IndexAssertion.java
new file mode 100644
index 0000000..bb949c3
--- /dev/null
+++ b/old_trunk/btree-base/src/main/java/org/apache/directory/server/core/partition/impl/btree/IndexAssertion.java
@@ -0,0 +1,49 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree;
+
+
+import javax.naming.NamingException;
+
+
+/**
+ * Asserts whether or not a candidate should be returned in searching based on
+ * hard coded logic.  This interface is not related to the filter AssertionNode.
+ * It is strictly used for purposes internal to the search engine.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public interface IndexAssertion
+{
+    /**
+     * Tests to see if a perspective candidate should be returned based on 
+     * the evaluation of hard coded logic.  If the entry has not been 
+     * resusitated then the getAttributes member of the record will be null.  As
+     * a side-effect an index assertion may populate the entry attribute after
+     * resusitating an entry from the master table.
+     * 
+     * @param record an index record of the entry
+     * @return true if the entry should be returned, false otherwise
+     * @throws NamingException if their are failures while asserting the 
+     * condition
+     */
+    boolean assertCandidate( IndexRecord record ) throws NamingException;
+}
diff --git a/old_trunk/btree-base/src/main/java/org/apache/directory/server/core/partition/impl/btree/IndexAssertionEnumeration.java b/old_trunk/btree-base/src/main/java/org/apache/directory/server/core/partition/impl/btree/IndexAssertionEnumeration.java
new file mode 100644
index 0000000..f7472a0
--- /dev/null
+++ b/old_trunk/btree-base/src/main/java/org/apache/directory/server/core/partition/impl/btree/IndexAssertionEnumeration.java
@@ -0,0 +1,199 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree;
+
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.NoSuchElementException;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+
+
+/**
+ * A prefetching NamingEnumeration over an underlying NamingEnumeration which 
+ * determines if a element should be returned based on a Assertion.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class IndexAssertionEnumeration implements NamingEnumeration<IndexRecord>
+{
+    /** The prefetched candidate */
+    private final IndexRecord prefetched = new IndexRecord();
+    /** The returned candidate */
+    private final IndexRecord candidate = new IndexRecord();
+    /** The iteration cursor */
+    private final NamingEnumeration<IndexRecord> underlying;
+    /** LUT used to avoid returning duplicates */
+    private final Map<Object,Object> candidates;
+    /** */
+    private final IndexAssertion assertion;
+    /** */
+    private final boolean checkDups;
+    /** */
+    private boolean hasMore = true;
+
+
+    // ------------------------------------------------------------------------
+    // C O N S T R U C T O R S
+    // ------------------------------------------------------------------------
+
+
+    public IndexAssertionEnumeration( NamingEnumeration<IndexRecord> underlying, IndexAssertion assertion ) 
+        throws NamingException
+    {
+        this.underlying = underlying;
+        candidates = null;
+        this.assertion = assertion;
+        checkDups = false;
+        prefetch();
+    }
+
+
+    public IndexAssertionEnumeration( NamingEnumeration<IndexRecord> underlying, IndexAssertion assertion, 
+        boolean enableDupCheck ) throws NamingException
+    {
+        this.underlying = underlying;
+        candidates = new HashMap<Object,Object>();
+        this.assertion = assertion;
+        checkDups = enableDupCheck;
+        prefetch();
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Enumeration Method Implementations
+    // ------------------------------------------------------------------------
+
+    /**
+     * @see java.util.Enumeration#nextElement()
+     */
+    public IndexRecord nextElement()
+    {
+        try
+        {
+            return next();
+        }
+        catch ( NamingException e )
+        {
+            throw new NoSuchElementException();
+        }
+    }
+
+
+    /**
+     * @see java.util.Enumeration#hasMoreElements()
+     */
+    public boolean hasMoreElements()
+    {
+        return hasMore;
+    }
+
+
+    // ------------------------------------------------------------------------
+    // NamingEnumeration Method Implementations
+    // ------------------------------------------------------------------------
+
+    /**
+     * @see javax.naming.NamingEnumeration#next()
+     */
+    public IndexRecord next() throws NamingException
+    {
+        candidate.copy( prefetched );
+        prefetch();
+        return candidate;
+    }
+
+
+    /**
+     * @see javax.naming.NamingEnumeration#hasMore()
+     */
+    public boolean hasMore()
+    {
+        return hasMore;
+    }
+
+
+    /**
+     * @see javax.naming.NamingEnumeration#close()
+     */
+    public void close() throws NamingException
+    {
+        hasMore = false;
+        underlying.close();
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Private and Protected Methods
+    // ------------------------------------------------------------------------
+
+    private void prefetch() throws NamingException
+    {
+        IndexRecord rec = null;
+
+        /*
+         * Scan underlying Cursor until we arrive at the next valid candidate
+         * if the cursor is exhuasted we clean up after completing the loop
+         */
+        while ( underlying.hasMore() )
+        {
+            rec = underlying.next();
+
+            // If value is valid then we set it as the next candidate to return
+            if ( assertion.assertCandidate( rec ) )
+            {
+                if ( checkDups )
+                {
+                    boolean dup = candidates.containsKey( rec.getEntryId() );
+
+                    if ( dup )
+                    {
+                        /*
+                         * Dup checking is on and candidate is a duplicate that
+                         * has already been seen so we need to skip it.
+                         */
+                        continue;
+                    }
+                    else
+                    {
+                        /*
+                         * Dup checking is on and the candidate is not in the 
+                         * dup LUT so we need to set it as the next to return 
+                         * and add it to the LUT in case we encounter it another
+                         * time.
+                         */
+                        prefetched.copy( rec );
+                        candidates.put( rec.getEntryId(), rec.getEntryId() );
+                        return;
+                    }
+                }
+
+                prefetched.copy( rec );
+                return;
+            }
+        }
+
+        // At this pt the underlying Cursor has been exhausted so we close up
+        close();
+    }
+}
diff --git a/old_trunk/btree-base/src/main/java/org/apache/directory/server/core/partition/impl/btree/IndexComparator.java b/old_trunk/btree-base/src/main/java/org/apache/directory/server/core/partition/impl/btree/IndexComparator.java
new file mode 100644
index 0000000..f29c4ce
--- /dev/null
+++ b/old_trunk/btree-base/src/main/java/org/apache/directory/server/core/partition/impl/btree/IndexComparator.java
@@ -0,0 +1,177 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree;
+
+
+import org.apache.directory.server.schema.SerializableComparator;
+
+/**
+ * TupleComparator for index records.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class IndexComparator implements TupleComparator
+{
+    private static final long serialVersionUID = 3257283621751633459L;
+
+    private static final SerializableComparator LONG_COMPARATOR = new SerializableComparator(
+        "1.3.6.1.4.1.18060.0.4.1.1.2" )
+    {
+        private static final long serialVersionUID = 3690478030414165816L;
+
+
+        public int compare( Object o1, Object o2 )
+        {
+            try
+            {
+                long thisVal = ( Long ) o1;
+                long anotherVal = ( Long ) o2;
+                
+                if ( thisVal == anotherVal )
+                {
+                    return 0;
+                }
+                
+                if ( thisVal == anotherVal )
+                {
+                    return 0;
+                }
+                
+                if ( thisVal >= 0 )
+                {
+                    if ( anotherVal >= 0 )
+                    {
+                        return ( thisVal > anotherVal ) ? 1 : -1;
+                    }
+                    else
+                    {
+                        return -1;
+                    }
+                }
+                else if ( anotherVal >= 0 )
+                {
+                    return 1;
+                }
+                else
+                {
+                    return ( thisVal < anotherVal ) ? -1 : 1;
+                }
+            }
+            catch ( NullPointerException npe )
+            {
+                if ( o1 == null )
+                {
+                    throw new IllegalArgumentException( "Argument 'obj1' is null" );
+                }
+                else
+                {
+                    throw new IllegalArgumentException( "Argument 'obj2' is null" );
+                }
+            }
+        }
+    };
+    
+    /** Whether or not the key/value is swapped */
+    private final boolean isForwardMap;
+    
+    /** The key comparison to use */
+    private final SerializableComparator keyComp;
+
+
+    /**
+     * Creates an IndexComparator.
+     *
+     * @param keyComp the table comparator to use for keys
+     * @param isForwardMap whether or not the comparator should swap the 
+     * key value pair while conducting comparisons.
+     */
+    public IndexComparator(SerializableComparator keyComp, boolean isForwardMap)
+    {
+        this.keyComp = keyComp;
+        this.isForwardMap = isForwardMap;
+    }
+
+
+    /**
+     * Gets the comparator used to compare keys.  May be null in which
+     * case the compareKey method will throw an UnsupportedOperationException.
+     *
+     * @return the comparator for comparing keys.
+     */
+    public SerializableComparator getKeyComparator()
+    {
+        if ( isForwardMap )
+        {
+            return keyComp;
+        }
+
+        return LONG_COMPARATOR;
+    }
+
+
+    /**
+     * Gets the binary comparator used to compare valuess.  May be null in which
+     * case the compareValue method will throw an UnsupportedOperationException.
+     *
+     * @return the binary comparator for comparing values.
+     */
+    public SerializableComparator getValueComparator()
+    {
+        if ( isForwardMap )
+        {
+            return LONG_COMPARATOR;
+        }
+
+        return keyComp;
+    }
+
+
+    /**
+     * Compares key Object to determine their sorting order returning a
+     * value = to, < or > than 0.
+     *
+     * @param key1 the first key to compare
+     * @param key2 the other key to compare to the first
+     * @return 0 if both are equal, a negative value less than 0 if the first
+     * is less than the second, or a postive value if the first is greater than
+     * the second byte array.
+     */
+    public int compareKey( Object key1, Object key2 )
+    {
+        return getKeyComparator().compare( key1, key2 );
+    }
+
+
+    /**
+     * Comparse value Objects to determine their sorting order returning a
+     * value = to, < or > than 0.
+     *
+     * @param value1 the first value to compare
+     * @param value2 the other value to compare to the first
+     * @return 0 if both are equal, a negative value less than 0 if the first
+     * is less than the second, or a postive value if the first is greater than
+     * the second Object.
+     */
+    public int compareValue( Object value1, Object value2 )
+    {
+        return getValueComparator().compare( value1, value2 );
+    }
+}
diff --git a/old_trunk/btree-base/src/main/java/org/apache/directory/server/core/partition/impl/btree/IndexEnumeration.java b/old_trunk/btree-base/src/main/java/org/apache/directory/server/core/partition/impl/btree/IndexEnumeration.java
new file mode 100644
index 0000000..f52f609
--- /dev/null
+++ b/old_trunk/btree-base/src/main/java/org/apache/directory/server/core/partition/impl/btree/IndexEnumeration.java
@@ -0,0 +1,180 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree;
+
+
+import java.util.NoSuchElementException;
+import java.util.regex.Pattern;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+
+
+/**
+ * A NamingEnumeration over an Index which returns IndexRecords.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class IndexEnumeration<T> implements NamingEnumeration<IndexRecord>
+{
+    /** */
+    private final Pattern re;
+    /** */
+    private final IndexRecord tmp = new IndexRecord();
+    /** */
+    private final IndexRecord returned = new IndexRecord();
+    /** */
+    private final IndexRecord prefetched = new IndexRecord();
+    /** */
+    private final boolean swapKeyVal;
+    /** */
+    private final NamingEnumeration<Tuple> underlying;
+
+    /** */
+    private boolean hasMore = true;
+
+
+    // ------------------------------------------------------------------------
+    // C O N S T R U C T O R S
+    // ------------------------------------------------------------------------
+
+    
+    public IndexEnumeration( NamingEnumeration<Tuple> list ) throws NamingException
+    {
+        this( list, false, null );
+    }
+
+
+    public IndexEnumeration( NamingEnumeration<Tuple> list, boolean swapKeyVal ) throws NamingException
+    {
+        this( list, swapKeyVal, null );
+    }
+
+
+    public IndexEnumeration( NamingEnumeration<Tuple> list, boolean swapKeyVal, Pattern regex ) 
+        throws NamingException
+    {
+        re = regex;
+        underlying = list;
+        this.swapKeyVal = swapKeyVal;
+
+        if ( !underlying.hasMore() )
+        {
+            hasMore = false;
+            return;
+        }
+
+        prefetch();
+    }
+
+
+    // ------------------------------------------------------------------------
+    // NamingEnumeration Interface Methods 
+    // ------------------------------------------------------------------------
+
+    /**
+     * @see javax.naming.NamingEnumeration#next()
+     */
+    public IndexRecord next() throws NamingException
+    {
+        returned.copy( prefetched );
+        prefetch();
+        return returned;
+    }
+
+
+    /**
+     * @see java.util.Enumeration#nextElement()
+     */
+    public IndexRecord nextElement()
+    {
+        try
+        {
+            return next();
+        }
+        catch ( NamingException ne )
+        {
+            throw new NoSuchElementException();
+        }
+    }
+
+
+    /**
+     * @see javax.naming.NamingEnumeration#hasMore()
+     */
+    public boolean hasMore()
+    {
+        return hasMore;
+    }
+
+
+    /**
+     * @see javax.naming.NamingEnumeration#hasMoreElements()
+     */
+    public boolean hasMoreElements()
+    {
+        return hasMore;
+    }
+
+
+    /**
+     * @see javax.naming.NamingEnumeration#close()
+     */
+    public void close() throws NamingException
+    {
+        hasMore = false;
+        underlying.close();
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Private Methods 
+    // ------------------------------------------------------------------------
+
+    private void prefetch() throws NamingException
+    {
+        while ( underlying.hasMore() )
+        {
+            Tuple tuple = underlying.next();
+
+            if ( swapKeyVal )
+            {
+                tmp.setSwapped( tuple, null );
+            }
+            else
+            {
+                tmp.setTuple( tuple, null );
+            }
+
+            // If regex is null just transfer into prefetched from tmp record
+            // but if it is not then use it to match.  Successful match shorts
+            // while loop.
+            if ( null == re || re.matcher( ( String ) tmp.getIndexKey() ).matches() )
+            {
+                prefetched.copy( tmp );
+                return;
+            }
+        }
+
+        // If we got down here then cursor has been consumed without a match!
+        hasMore = false;
+    }
+}
diff --git a/old_trunk/btree-base/src/main/java/org/apache/directory/server/core/partition/impl/btree/IndexNotFoundException.java b/old_trunk/btree-base/src/main/java/org/apache/directory/server/core/partition/impl/btree/IndexNotFoundException.java
new file mode 100755
index 0000000..77ed947
--- /dev/null
+++ b/old_trunk/btree-base/src/main/java/org/apache/directory/server/core/partition/impl/btree/IndexNotFoundException.java
@@ -0,0 +1,92 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree;
+
+
+import javax.naming.NamingException;
+
+
+/**
+ * NamingException for missing indicies if full table scans are disallowed.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class IndexNotFoundException extends NamingException
+{
+    private static final long serialVersionUID = 3906088970608981815L;
+
+    /** the name of the index that was not found */
+    private final String indexName;
+
+
+    /**
+     * Constructs an Exception with a detailed message.
+     * 
+     * @param indexName the name of the index that was not found 
+     */
+    public IndexNotFoundException(String indexName)
+    {
+        super( "Cannot efficiently search the DIB w/o an index on attribute " + indexName
+            + "\n. To allow such searches please contact the "
+            + "directory\nadministrator to create the index or to enable "
+            + "referrals on searches using these\nattributes to a replica with " + "the required set of indices." );
+        this.indexName = indexName;
+    }
+
+
+    /**
+     * Constructs an Exception with a detailed message.
+     * 
+     * @param message the message associated with the exception.
+     * @param indexName the name of the index that was not found 
+     */
+    public IndexNotFoundException(String message, String indexName)
+    {
+        super( message );
+        this.indexName = indexName;
+    }
+
+
+    /**
+     * Constructs an Exception with a detailed message and a root cause 
+     * exception.
+     * 
+     * @param message the message associated with the exception.
+     * @param indexName the name of the index that was not found 
+     * @param rootCause the root cause of this exception 
+     */
+    public IndexNotFoundException(String message, String indexName, Throwable rootCause)
+    {
+        this( message, indexName );
+        setRootCause( rootCause );
+    }
+
+
+    /**
+     * Gets the name of the attribute the index was missing for.
+     *
+     * @return the name of the attribute the index was missing for.
+     */
+    public String getIndexName()
+    {
+        return indexName;
+    }
+}
diff --git a/old_trunk/btree-base/src/main/java/org/apache/directory/server/core/partition/impl/btree/IndexRecord.java b/old_trunk/btree-base/src/main/java/org/apache/directory/server/core/partition/impl/btree/IndexRecord.java
new file mode 100644
index 0000000..ab6268b
--- /dev/null
+++ b/old_trunk/btree-base/src/main/java/org/apache/directory/server/core/partition/impl/btree/IndexRecord.java
@@ -0,0 +1,166 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree;
+
+import org.apache.directory.server.core.entry.ServerEntry;
+
+
+/**
+ * An index key value pair based on a tuple which can optionally reference the
+ * indexed entry if one has been resusitated.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class IndexRecord
+{
+    /** The underlying BTree Tuple */
+    private final Tuple tuple = new Tuple();
+    
+    /** The referenced entry if ressucitated */
+    private ServerEntry entry = null;
+
+
+    /**
+     * Sets the key value tuple represented by this IndexRecord optionally 
+     * setting the entry if one was resusitated from the master table.
+     *
+     * @param tuple the tuple for the IndexRecord
+     * @param entry the resusitated entry if any
+     */
+    public void setTuple( Tuple tuple, ServerEntry entry )
+    {
+        this.tuple.setKey( tuple.getKey() );
+        this.tuple.setValue( tuple.getValue() );
+        this.entry = entry;
+    }
+
+
+    /**
+     * Sets the key value tuple but swapping the key and the value and 
+     * optionally adding the entry if one was resusitated.
+     *
+     * @param tuple the tuple for the IndexRecord
+     * @param entry the resusitated entry if any
+     */
+    public void setSwapped( Tuple tuple, ServerEntry entry )
+    {
+        this.tuple.setKey( tuple.getValue() );
+        this.tuple.setValue( tuple.getKey() );
+        this.entry = entry;
+    }
+
+
+    /**
+     * Gets the entry id for this IndexRecord. 
+     *
+     * @return the id of the entry indexed
+     */
+    public Object getEntryId()
+    {
+        return tuple.getValue();
+    }
+
+
+    /**
+     * Gets the index key ( the attribute's value ) for this IndexRecord.
+     *
+     * @return the key of the entry indexed
+     */
+    public Object getIndexKey()
+    {
+        return tuple.getKey();
+    }
+
+
+    /**
+     * Sets the entry id.
+     *
+     * @param id the id of the entry
+     */
+    public void setEntryId( Object id )
+    {
+        tuple.setValue( id );
+    }
+
+
+    /**
+     * Sets the index key.
+     *
+     * @param key the key of the IndexRecord
+     */
+    public void setIndexKey( Object key )
+    {
+        tuple.setKey( key );
+    }
+
+
+    /**
+     * Gets the entry of this IndexRecord if one was resusitated form the master
+     * table.
+     * 
+     * @return the entry's attributes
+     */
+    public ServerEntry getEntry()
+    {
+        if ( entry == null )
+        {
+            return null;
+        }
+
+        return ( ServerEntry ) entry.clone();
+    }
+
+
+    /**
+     * Sets the entry's attributes.
+     * 
+     * @param entry the entry's attributes
+     */
+    public void setEntry( ServerEntry entry )
+    {
+        this.entry = entry;
+    }
+
+
+    /**
+     * Clears the tuple key, the tuple value and the entry fields.
+     */
+    public void clear()
+    {
+        entry = null;
+        tuple.setKey( null );
+        tuple.setValue( null );
+    }
+
+
+    /**
+     * Utility method used to copy the contents of one IndexRecord into this
+     * IndexRecord.
+     * 
+     * @param record the record whose contents we copy
+     */
+    public void copy( IndexRecord record )
+    {
+        entry = record.getEntry();
+        tuple.setKey( record.getIndexKey() );
+        tuple.setValue( record.getEntryId() );
+    }
+}
diff --git a/old_trunk/btree-base/src/main/java/org/apache/directory/server/core/partition/impl/btree/KeyOnlyComparator.java b/old_trunk/btree-base/src/main/java/org/apache/directory/server/core/partition/impl/btree/KeyOnlyComparator.java
new file mode 100644
index 0000000..2cbd2d7
--- /dev/null
+++ b/old_trunk/btree-base/src/main/java/org/apache/directory/server/core/partition/impl/btree/KeyOnlyComparator.java
@@ -0,0 +1,98 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree;
+
+
+import java.io.Serializable;
+
+import org.apache.directory.server.schema.SerializableComparator;
+
+
+/**
+ * A TupleComparator that compares keys only.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class KeyOnlyComparator implements TupleComparator, Serializable
+{
+    private static final long serialVersionUID = 3544956549803161397L;
+
+    private SerializableComparator keyComparator = null;
+
+
+    public KeyOnlyComparator(SerializableComparator comparator)
+    {
+        keyComparator = comparator;
+    }
+
+
+    /**
+     * Gets the comparator used to compare keys.  May be null in which
+     * case the compareKey method will throw an UnsupportedOperationException.
+     *
+     * @return the comparator for comparing keys.
+     */
+    public SerializableComparator getKeyComparator()
+    {
+        return keyComparator;
+    }
+
+
+    /**
+     * Will throw an UnsupportedOperationException every time.
+     */
+    public SerializableComparator getValueComparator()
+    {
+        throw new UnsupportedOperationException();
+    }
+
+
+    /**
+     * Compares key Object to determine their sorting order returning a
+     * value = to, < or > than 0.
+     *
+     * @param key1 the first key to compare
+     * @param key2 the other key to compare to the first
+     * @return 0 if both are equal, a negative value less than 0 if the first
+     * is less than the second, or a postive value if the first is greater than
+     * the second byte array.
+     */
+    public int compareKey( Object key1, Object key2 )
+    {
+        return keyComparator.compare( key1, key2 );
+    }
+
+
+    /**
+     * Comparse value Objects to determine their sorting order returning a
+     * value = to, < or > than 0.
+     *
+     * @param value1 the first value to compare
+     * @param value2 the other value to compare to the first
+     * @return 0 if both are equal, a negative value less than 0 if the first
+     * is less than the second, or a postive value if the first is greater than
+     * the second Object.
+     */
+    public int compareValue( Object value1, Object value2 )
+    {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/old_trunk/btree-base/src/main/java/org/apache/directory/server/core/partition/impl/btree/MasterTable.java b/old_trunk/btree-base/src/main/java/org/apache/directory/server/core/partition/impl/btree/MasterTable.java
new file mode 100644
index 0000000..5c5c2f8
--- /dev/null
+++ b/old_trunk/btree-base/src/main/java/org/apache/directory/server/core/partition/impl/btree/MasterTable.java
@@ -0,0 +1,116 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree;
+
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.core.entry.ServerEntry;
+
+
+/**
+ * The master table used to store the ServerEntry of entries.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public interface MasterTable extends Table
+{
+    /** the name of the dbf file for this table */
+    String DBF = "master";
+
+    /** the sequence key - stores last sequence value in the admin table */
+    String SEQPROP_KEY = "__sequence__";
+
+
+    /**
+     * Gets the ServerEntry from this MasterTable.
+     *
+     * @param id the Long id of the entry to retrieve.
+     * @return the ServerEntry with operational attributes and all.
+     * @throws NamingException if there is a read error on the underlying Db.
+     */
+    ServerEntry get( Object id ) throws NamingException;
+
+
+    /**
+     * Puts the ServerEntry into this master table at an index 
+     * specified by id.  Used both to create new entries and update existing 
+     * ones.
+     *
+     * @param entry the ServerEntry w/ operational attributes
+     * @param id the Long id of the entry to put
+     * @return the newly created ServerEntry
+     * @throws NamingException if there is a write error on the underlying Db.
+     */
+    ServerEntry put( ServerEntry entry, Object id ) throws NamingException;
+
+
+    /**
+     * Deletes a ServerEntry from the master table at an index specified by id.
+     *
+     * @param id the Long id of the entry to delete
+     * @return the deleted ServerEntry
+     * @throws NamingException if there is a write error on the underlying Db
+     */
+    ServerEntry delete( Object id ) throws NamingException;
+
+
+    /**
+     * Get's the current id value from this master database's sequence without
+     * affecting the seq.
+     *
+     * @return the current value.
+     * @throws NamingException if the admin table storing sequences cannot be
+     * read.
+     */
+    Object getCurrentId() throws NamingException;
+
+
+    /**
+     * Get's the next value from this SequenceBDb.  This has the side-effect of
+     * changing the current sequence values permanently in memory and on disk.
+     *
+     * @return the current value incremented by one.
+     * @throws NamingException if the admin table storing sequences cannot be
+     * read and writen to.
+     */
+    Object getNextId() throws NamingException;
+
+
+    /**
+     * Gets a persistent property stored in the admin table of this MasterTable.
+     *
+     * @param property the key of the property to get the value of
+     * @return the value of the property
+     * @throws NamingException when the underlying admin table cannot be read
+     */
+    String getProperty( String property ) throws NamingException;
+
+
+    /**
+     * Sets a persistent property stored in the admin table of this MasterTable.
+     *
+     * @param property the key of the property to set the value of
+     * @param value the value of the property
+     * @throws NamingException when the underlying admin table cannot be writen
+     */
+    void setProperty( String property, String value ) throws NamingException;
+}
\ No newline at end of file
diff --git a/old_trunk/btree-base/src/main/java/org/apache/directory/server/core/partition/impl/btree/NoDupsEnumeration.java b/old_trunk/btree-base/src/main/java/org/apache/directory/server/core/partition/impl/btree/NoDupsEnumeration.java
new file mode 100644
index 0000000..992d13c
--- /dev/null
+++ b/old_trunk/btree-base/src/main/java/org/apache/directory/server/core/partition/impl/btree/NoDupsEnumeration.java
@@ -0,0 +1,181 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree;
+
+
+import java.util.NoSuchElementException;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+
+
+/**
+ * A simple NamingEnumeration over a TupleBrowser on a table that does not allow
+ * duplicates.
+ * 
+ * <p> WARNING: The Tuple returned by this listing is always the same instance 
+ * object returned every time. It is reused to for the sake of efficency rather 
+ * than creating a new tuple for each hasMore() call.
+ * </p>
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class NoDupsEnumeration implements NamingEnumeration<Tuple>
+{
+    /** Temporary Tuple used to return results */
+    private final Tuple returned = new Tuple();
+    /** Temporary Tuple used to store prefetched values */
+    private final Tuple prefetched = new Tuple();
+    /** The JDBM TupleBrowser this NamingEnumeration wraps */
+    private final TupleBrowser browser;
+    /** The direction of this NamingEnumeration */
+    private final boolean doAscendingScan;
+    /** Whether or not this NamingEnumeration can advance */
+    private boolean hasNext = true;
+
+
+    // ------------------------------------------------------------------------
+    // C O N S T R U C T O R
+    // ------------------------------------------------------------------------
+
+    
+    /**
+     * Creates a cursor over a TupleBrowser where duplicates are not expected.
+     */
+    public NoDupsEnumeration( TupleBrowser browser, boolean doAscendingScan ) throws NamingException
+    {
+        this.browser = browser;
+        this.doAscendingScan = doAscendingScan;
+        prefetch();
+    }
+
+
+    // ------------------------------------------------------------------------
+    // NamingEnumeration Interface Method Implementations
+    // ------------------------------------------------------------------------
+
+    
+    /**
+     * Returns the same Tuple every time but with different key/value pairs.
+     * 
+     * @see javax.naming.NamingEnumeration#next()
+     */
+    public Tuple next() throws NamingException
+    {
+        // Load values into the Tuple to return
+        returned.setKey( prefetched.getKey() );
+        returned.setValue( prefetched.getValue() );
+
+        // Prefetch next set of values to return if and return last prefetched
+        prefetch();
+        return returned;
+    }
+
+
+    /**
+     * Returns the same Tuple every time but with different key/value pairs.
+     * 
+     * @see java.util.Enumeration#nextElement()
+     */
+    public Tuple nextElement()
+    {
+        try
+        {
+            return next();
+        }
+        catch ( NamingException e )
+        {
+            throw new NoSuchElementException();
+        }
+    }
+
+
+    /**
+     * @see javax.naming.NamingEnumeration#hasMore()
+     */
+    public boolean hasMore()
+    {
+        return hasNext;
+    }
+
+
+    /**
+     * Calls hasMore.
+     *
+     * @see java.util.Enumeration#hasMoreElements()
+     */
+    public boolean hasMoreElements()
+    {
+        return hasNext;
+    }
+
+
+    /**
+     * Sets hasNext to false.
+     *
+     * @see javax.naming.NamingEnumeration#close()
+     */
+    public void close()
+    {
+        hasNext = false;
+    }
+
+
+    /**
+     * Gets the direction of this NamingEnumeration.
+     *
+     * @return true if this NamingEnumeration is ascending on keys, false 
+     * otherwise.
+     */
+    public boolean doAscendingScan()
+    {
+        return doAscendingScan;
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Private/Package Friendly Methods
+    // ------------------------------------------------------------------------
+
+    
+    /**
+     * Prefetches a value into prefetched over writing whatever values were 
+     * contained in the Tuple.
+     *
+     * @throws NamingException if the TupleBrowser browser could not advance
+     */
+    private void prefetch() throws NamingException
+    {
+        // Prefetch into tuple!
+        boolean isSuccess = false;
+
+        if ( doAscendingScan )
+        {
+            isSuccess = browser.getNext( prefetched );
+        }
+        else
+        {
+            isSuccess = browser.getPrevious( prefetched );
+        }
+
+        hasNext = isSuccess;
+    }
+}
diff --git a/old_trunk/btree-base/src/main/java/org/apache/directory/server/core/partition/impl/btree/Table.java b/old_trunk/btree-base/src/main/java/org/apache/directory/server/core/partition/impl/btree/Table.java
new file mode 100644
index 0000000..bb69e50
--- /dev/null
+++ b/old_trunk/btree-base/src/main/java/org/apache/directory/server/core/partition/impl/btree/Table.java
@@ -0,0 +1,363 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree;
+
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+
+
+/**
+ * A wrapper interface around a BTree (that does not support duplicate keys) which 
+ * transparently enables duplicate keys and translates underlying exceptions to
+ * NamingExceptions.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public interface Table
+{
+    /**
+     * Gets the comparator used by this Table: may be null if this Table was
+     * not initialized with one.
+     *
+     * @return the final comparator instance or null if this Table was not
+     * created with one.
+     */
+    TupleComparator getComparator();
+
+
+    /**
+     * Gets the data renderer used by this Table to display or log records keys
+     * and values.
+     *
+     * @return the renderer used
+     */
+    TupleRenderer getRenderer();
+
+
+    /**
+     * Sets the data renderer to by used by this Table to display or log record
+     * keys and values.
+     *
+     * @param renderer the DataRenderer instance to used as the renderer.
+     */
+    void setRenderer( TupleRenderer renderer );
+
+
+    /**
+     * Gets the name of this Table.
+     *
+     * @return the name
+     */
+    String getName();
+
+
+    /**
+     * Checks to see if this Table has enabled the use of duplicate keys.
+     *
+     * @return true if duplicate keys are enabled, false otherwise.
+     */
+    boolean isDupsEnabled();
+
+
+    /**
+     * Checks to see if this Table has enabled sorting on the values of
+     * duplicate keys.
+     *
+     * @return true if duplicate key values are sorted, false otherwise.
+     */
+    boolean isSortedDupsEnabled();
+
+
+    // ------------------------------------------------------------------------
+    // Simple Table Key/Value Assertions 
+    // ------------------------------------------------------------------------
+
+    
+    /**
+     * Checks to see if this table has a key: same as a get call with a check to
+     * see if the returned value is null or not.
+     *
+     * @param key the Object of the key to check for
+     * @return true if the key exists, false otherwise.
+     * @throws NamingException if there is a failure to read the underlying Db
+     */
+    boolean has( Object key ) throws NamingException;
+
+
+    /**
+     * Checks to see if this table has a key with a specific value.
+     *
+     * @param key the key Object to check for
+     * @param value the value Object to check for
+     * @return true if a record with the key and value exists, false otherwise.
+     * @throws NamingException if there is a failure to read the underlying Db
+     */
+    boolean has( Object key, Object value ) throws NamingException;
+
+
+    /**
+     * Checks to see if this table has a record with a key greater/less than or
+     * equal to the key argument.  The key argument need not exist for this
+     * call to return true.  The underlying database must be a BTree because
+     * this method depends on the use of sorted keys.
+     *
+     * @param key the key Object to compare keys to
+     * @param isGreaterThan boolean for greater than or less then comparison
+     * @return true if a record with a key greater/less than the key argument
+     * exists, false otherwise
+     * @throws NamingException if there is a failure to read the underlying Db,
+     * or if the underlying Db is not a Btree.
+     */
+    boolean has( Object key, boolean isGreaterThan ) throws NamingException;
+
+
+    /**
+     * Checks to see if this table has a record with a key equal to the
+     * argument key with a value greater/less than or equal to the value
+     * argument provided.  The key argument <strong>MUST</strong> exist for
+     * this call to return true and the underlying Db must be a Btree that
+     * allows for sorted duplicate values.  The entire basis to this method
+     * depends on the fact that duplicate key values are sorted according to
+     * a valid value comparator function.
+     *
+     * If the table does not support duplicates then an 
+     * UnsupportedOperationException is thrown.
+     *
+     * @param key the key Object
+     * @param val the value Object to compare values to
+     * @param isGreaterThan boolean for greater than or less then comparison
+     * @return true if a record with a key greater/less than the key argument
+     * exists, false otherwise
+     * @throws NamingException if there is a failure to read the underlying Db
+     * or if the underlying Db is not of the Btree type that allows sorted
+     * duplicate values.
+     */
+    boolean has( Object key, Object val, boolean isGreaterThan ) throws NamingException;
+
+
+    // ------------------------------------------------------------------------
+    // Table Value Accessors/Mutators
+    // ------------------------------------------------------------------------
+
+    /**
+     * Gets the value of a record by key if the key exists.  If this Table
+     * allows duplicate keys then the first key will be returned.  If this
+     * Table is also a Btree that first key will be the smallest key in the
+     * Table as specificed by this Table's comparator or the default berkeley
+     * bytewise lexical comparator.
+     *
+     * @param key the key of the record
+     * @return the value of the record with key if key exists or null if
+     * no such record exists.
+     * @throws NamingException if there is a failure to read the underlying Db
+     */
+    Object get( Object key ) throws NamingException;
+
+
+    /**
+     * Puts a record into this Table.
+     *
+     * @param key the key of the record
+     * @param value the value of the record.
+     * @return the last value present for key or null if this the key did not
+     * exist before.
+     * @throws NamingException if there is a failure to read or write to
+     * the underlying Db
+     */
+    Object put( Object key, Object value ) throws NamingException;
+
+
+    /**
+     * Efficiently puts a set of values into the Table.  If the Table does not 
+     * support duplicate keys then only the first key within the enumeration is
+     * added.  If there are more elements left after this single addition an
+     * UnsupportedOperationException is thrown.  Nothing is added if the table
+     * does not support duplicates and there is more than one element in the
+     * enumeration.
+     *
+     * @param key the key to use for the values
+     * @param values the values supplied as an enumeration
+     * @throws NamingException if something goes wrong
+     */
+    Object put( Object key, NamingEnumeration<? extends Object> values ) throws NamingException;
+
+
+    /**
+     * Removes all records with key from this Table.
+     *
+     * @param key the key of the records to remove
+     * @throws NamingException if there is a failure to read or write to
+     * the underlying Db
+     */
+    Object remove( Object key ) throws NamingException;
+
+
+    /**
+     * Removes a single specific record with key and value from this Table.
+     *
+     * @param key the key of the record to remove
+     * @param value the value of the record to remove
+     * @throws NamingException if there is a failure to read or write to
+     * the underlying Db
+     */
+    Object remove( Object key, Object value ) throws NamingException;
+
+
+    /**
+     * Removes a set of values with the same key from this Table.  If this 
+     * table does not allow duplicates the method will attempt to remove the 
+     * first value in the enumeration if one exists.  If there is more than one 
+     * value within the enumeration after the first drop an 
+     * UnsupportedOperationException is thrown.  Nothing is removed if there is 
+     * more than one element on the enumeration and the table does not support
+     * duplicates.
+     *
+     * @param key the key of the records to remove
+     * @return the first value removed
+     * @throws NamingException if there is a failure to read or write to
+     * the underlying Db
+     */
+    Object remove( Object key, NamingEnumeration<? extends Object> values ) throws NamingException;
+
+
+    /**
+     * Sets a enumeration to the first record in the Table with a key value of
+     * key and enables single next steps across all duplicate records with
+     * this key.  This enumeration will only iterate over duplicates of the key.
+     * Unlike listTuples(Object) which returns Tuples from the enumerations 
+     * this just returns the values of the key.
+     * 
+     * @param key the key to iterate over
+     * @throws NamingException if the underlying browser could not be set
+     */
+    NamingEnumeration<Object> listValues( Object key ) throws NamingException;
+
+
+    // ------------------------------------------------------------------------
+    // listTuples overloads
+    // ------------------------------------------------------------------------
+
+    
+    /**
+     * Sets a cursor to the first record in the Table and enables single
+     * next steps across all records.
+     *
+     * @throws NamingException if the underlying cursor could not be set.
+     */
+    NamingEnumeration<Tuple> listTuples() throws NamingException;
+
+
+    /**
+     * Sets a cursor to the first record in the Table with a key value of
+     * key and enables single next steps across all duplicate records with
+     * this key.  This cursor will only iterate over duplicates of the key.
+     *
+     * @param key the key to iterate over
+     * @throws NamingException if the underlying cursor could not be set
+     */
+    NamingEnumeration<Tuple> listTuples( Object key ) throws NamingException;
+
+
+    /**
+     * Sets a cursor to the first record in the Table with a key value
+     * greater/less than or equal to key and enables single next steps across
+     * all records with key values equal to or less/greater than key.
+     *
+     * @param key the key to use to position this cursor to record with a key
+     * greater/less than or equal to it
+     * @param isGreaterThan if true the cursor iterates up over ascending keys
+     * greater than or equal to the key argument, but if false this cursor
+     * iterates down over descending keys less than or equal to key argument
+     * @throws NamingException if the underlying cursor could not be set
+     */
+    NamingEnumeration<Tuple> listTuples( Object key, boolean isGreaterThan ) throws NamingException;
+
+
+    /**
+     * Sets a cursor to the first record in the Table with a key equal to
+     * the key argument whose value is greater/less than or equal to val and
+     * enables single next steps across all records with key equal to key.
+     * Hence this cursor will only iterate over duplicate keys where values are
+     * less than or greater than or equal to val.
+     *
+     * If the table does not support duplicates then an 
+     * UnsupportedOperationException is thrown.
+     *
+     * @param key the key to use to position this cursor to record with a key
+     * equal to it.
+     * @param val the value to use to position this cursor to record with a
+     * value greater/less than or equal to it.
+     * @param isGreaterThan if true the cursor iterates up over ascending 
+     * values greater than or equal to the val argument, but if false this 
+     * cursor iterates down over descending values less than or equal to val 
+     * argument starting from the largest value going down
+     * @throws NamingException if the underlying cursor could not be set or
+     * this method is called over a cursor on a table that does not have sorted
+     * duplicates enabled.
+     */
+    NamingEnumeration<Tuple> listTuples( Object key, Object val, boolean isGreaterThan ) throws NamingException;
+
+
+    // ------------------------------------------------------------------------
+    // Table Record Count Methods
+    // ------------------------------------------------------------------------
+
+    /**
+     * Gets the count of the number of records in this Table.
+     *
+     * @return the number of records
+     * @throws NamingException if there is a failure to read the underlying Db
+     */
+    int count() throws NamingException;
+
+
+    /**
+     * Gets the count of the number of records in this Table with a specific
+     * key: returns the number of duplicates for a key.
+     *
+     * @param key the Object key to count.
+     * @return the number of duplicate records for a key.
+     * @throws NamingException if there is a failure to read the underlying Db
+     */
+    int count( Object key ) throws NamingException;
+
+
+    /**
+     * Returns the number of records greater than or less than a key value.  The
+     * key need not exist for this call to return a non-zero value.
+     *
+     * @param key the Object key to count.
+     * @param isGreaterThan boolean set to true to count for greater than and
+     * equal to record keys, or false for less than or equal to keys.
+     * @return the number of keys greater or less than key.
+     * @throws NamingException if there is a failure to read the underlying Db
+     */
+    int count( Object key, boolean isGreaterThan ) throws NamingException;
+
+
+    /**
+     * Closes the underlying Db of this Table.
+     *
+     * @throws NamingException on any failures
+     */
+    void close() throws NamingException;
+}
diff --git a/old_trunk/btree-base/src/main/java/org/apache/directory/server/core/partition/impl/btree/Tuple.java b/old_trunk/btree-base/src/main/java/org/apache/directory/server/core/partition/impl/btree/Tuple.java
new file mode 100644
index 0000000..bc14e97
--- /dev/null
+++ b/old_trunk/btree-base/src/main/java/org/apache/directory/server/core/partition/impl/btree/Tuple.java
@@ -0,0 +1,103 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree;
+
+
+/**
+ * A key/value tuple for simple two column Tables.  Implemented to provide 
+ * independence from the Jdbm Tuple class.  Key and value copying should be 
+ * performed to transpose jdbm.helper.Tuple data into our generic Tuple.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class Tuple
+{
+    /** the key for this Tuple */
+    private Object key;
+    /** the value for this Tuple */
+    private Object value;
+
+
+    /**
+     * Do nothing default that has a null key and null value.
+     */
+    public Tuple()
+    {
+        // does nothing!
+    }
+
+
+    /**
+     * Creates a Tuple using a key and a value.
+     * 
+     * @param key the key to set
+     * @param value the value to set
+     */
+    public Tuple(Object key, Object value)
+    {
+        this.key = key;
+        this.value = value;
+    }
+
+
+    /**
+     * Gets the key for this Tuple.
+     *
+     * @return the Tuple's key
+     */
+    public Object getKey()
+    {
+        return key;
+    }
+
+
+    /**
+     * Sets the key for this Tuple.
+     *
+     * @param key the new key to set
+     */
+    public void setKey( Object key )
+    {
+        this.key = key;
+    }
+
+
+    /**
+     * Gets the value for this Tuple.
+     *
+     * @return the Tuple's value
+     */
+    public Object getValue()
+    {
+        return value;
+    }
+
+
+    /**
+     * Sets the value for this Tuple.
+     *
+     * @param value the new value to set
+     */
+    public void setValue( Object value )
+    {
+        this.value = value;
+    }
+}
diff --git a/old_trunk/btree-base/src/main/java/org/apache/directory/server/core/partition/impl/btree/TupleBrowser.java b/old_trunk/btree-base/src/main/java/org/apache/directory/server/core/partition/impl/btree/TupleBrowser.java
new file mode 100644
index 0000000..ab9455f
--- /dev/null
+++ b/old_trunk/btree-base/src/main/java/org/apache/directory/server/core/partition/impl/btree/TupleBrowser.java
@@ -0,0 +1,54 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree;
+
+
+import javax.naming.NamingException;
+
+
+/**
+ * TupleBrowser interface used to abstract 
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public interface TupleBrowser
+{
+    /**
+     * Gets the next value deemed greater than the last using the key 
+     * comparator.
+     *
+     * @param tuple the tuple to populate with a key/value pair
+     * @return true if there was a next that was populated or false otherwise
+     * @throws NamingException @todo
+     */
+    boolean getNext( Tuple tuple ) throws NamingException;
+
+
+    /**
+     * Gets the previous value deemed greater than the last using the key 
+     * comparator.
+     *
+     * @param tuple the tuple to populate with a key/value pair
+     * @return true if there was a previous value populated or false otherwise
+     * @throws NamingException @todo
+     */
+    boolean getPrevious( Tuple tuple ) throws NamingException;
+}
diff --git a/old_trunk/btree-base/src/main/java/org/apache/directory/server/core/partition/impl/btree/TupleComparator.java b/old_trunk/btree-base/src/main/java/org/apache/directory/server/core/partition/impl/btree/TupleComparator.java
new file mode 100644
index 0000000..2954358
--- /dev/null
+++ b/old_trunk/btree-base/src/main/java/org/apache/directory/server/core/partition/impl/btree/TupleComparator.java
@@ -0,0 +1,78 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree;
+
+
+import java.io.Serializable;
+
+import org.apache.directory.server.schema.SerializableComparator;
+
+
+/**
+ * Used to compare the sorting order of binary data.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public interface TupleComparator extends Serializable
+{
+    /**
+     * Gets the comparator used to compare keys.  May be null in which
+     * case the compareKey method will throw an UnsupportedOperationException.
+     *
+     * @return the comparator for comparing keys.
+     */
+    SerializableComparator getKeyComparator();
+
+
+    /**
+     * Gets the binary comparator used to compare valuess.  May be null in which
+     * case the compareValue method will throw an UnsupportedOperationException.
+     *
+     * @return the binary comparator for comparing values.
+     */
+    SerializableComparator getValueComparator();
+
+
+    /**
+     * Compares key Object to determine their sorting order returning a
+     * value = to, < or > than 0.
+     *
+     * @param key1 the first key to compare
+     * @param key2 the other key to compare to the first
+     * @return 0 if both are equal, a negative value less than 0 if the first
+     * is less than the second, or a postive value if the first is greater than
+     * the second byte array.
+     */
+    int compareKey( Object key1, Object key2 );
+
+
+    /**
+     * Comparse value Objects to determine their sorting order returning a
+     * value = to, < or > than 0.
+     *
+     * @param value1 the first value to compare
+     * @param value2 the other value to compare to the first
+     * @return 0 if both are equal, a negative value less than 0 if the first
+     * is less than the second, or a postive value if the first is greater than
+     * the second Object.
+     */
+    int compareValue( Object value1, Object value2 );
+}
diff --git a/old_trunk/btree-base/src/main/java/org/apache/directory/server/core/partition/impl/btree/TupleEnumeration.java b/old_trunk/btree-base/src/main/java/org/apache/directory/server/core/partition/impl/btree/TupleEnumeration.java
new file mode 100644
index 0000000..9dcab9c
--- /dev/null
+++ b/old_trunk/btree-base/src/main/java/org/apache/directory/server/core/partition/impl/btree/TupleEnumeration.java
@@ -0,0 +1,115 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree;
+
+
+import java.util.Iterator;
+
+import javax.naming.NamingEnumeration;
+
+
+/**
+ * A NamingEnumeration that returns underlying Iterator values for a single key
+ * as Tuples.
+ * 
+ * <p>
+ * WARNING: The tuple returned is reused every time for efficiency and populated
+ * a over and over again with the new value.  The key never changes.
+ * </p>
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class TupleEnumeration implements NamingEnumeration<Tuple>
+{
+    private final Object key;
+    private final Iterator<? extends Object> iterator;
+    private final Tuple tuple = new Tuple();
+
+
+    /**
+     * Creates a cursor over an Iterator of single key's values
+     * 
+     * @param key the keys whose duplicate values are to be returned
+     * @param iterator the underlying iterator this cursor uses
+     */
+    public TupleEnumeration( Object key, Iterator<? extends Object> iterator )
+    {
+        this.key = key;
+        tuple.setKey( key );
+        this.iterator = iterator;
+    }
+
+
+    /**
+     * Gets the next value as a Tuple.
+     *
+     * @see javax.naming.NamingEnumeration#next()
+     */
+    public Tuple next()
+    {
+        tuple.setKey( key );
+        tuple.setValue( iterator.next() );
+        return tuple;
+    }
+
+
+    /**
+     * Gets the next value as a Tuple.
+     *
+     * @see javax.naming.NamingEnumeration#nextElement()
+     */
+    public Tuple nextElement()
+    {
+        tuple.setKey( key );
+        tuple.setValue( iterator.next() );
+        return tuple;
+    }
+
+
+    /**
+     * Checks if another value is available.
+     *
+     * @see javax.naming.NamingEnumeration#hasMore()
+     */
+    public boolean hasMore()
+    {
+        return iterator.hasNext();
+    }
+
+
+    /**
+     * Checks if another value is available.
+     *
+     * @see javax.naming.NamingEnumeration#hasMoreElements()
+     */
+    public boolean hasMoreElements()
+    {
+        return iterator.hasNext();
+    }
+
+
+    /**
+     * @see javax.naming.NamingEnumeration#close()
+     */
+    public void close()
+    {
+    }
+}
diff --git a/old_trunk/btree-base/src/main/java/org/apache/directory/server/core/partition/impl/btree/TupleRenderer.java b/old_trunk/btree-base/src/main/java/org/apache/directory/server/core/partition/impl/btree/TupleRenderer.java
new file mode 100644
index 0000000..427f455
--- /dev/null
+++ b/old_trunk/btree-base/src/main/java/org/apache/directory/server/core/partition/impl/btree/TupleRenderer.java
@@ -0,0 +1,48 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree;
+
+
+/**
+ * A table key/value String renderer for the display or logging of
+ * human readable potentially binary data.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public interface TupleRenderer
+{
+    /**
+     * Gets the key Object rendered as a String.
+     *
+     * @param key the key Object
+     * @return the String representation of the key Object
+     */
+    String getKeyString( Object key );
+
+
+    /**
+     * Gets the value Object rendered as a String.
+     *
+     * @param value the value Object
+     * @return the String representation of the value Object
+     */
+    String getValueString( Object value );
+}
diff --git a/old_trunk/btree-base/src/site/site.xml b/old_trunk/btree-base/src/site/site.xml
new file mode 100644
index 0000000..9ec7c16
--- /dev/null
+++ b/old_trunk/btree-base/src/site/site.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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.
+-->
+<!--
+  @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+-->
+<project name="${project.name}">
+    <bannerLeft>
+        <name>${project.name}</name>
+    </bannerLeft>
+    <publishDate position="navigation-bottom" format="dd-MM-yyyy HH:mm" />
+    <body>
+        <menu name="Parent">
+            <item name="Apache Directory ApacheDS" href="../index.html" />
+        </menu>
+        <menu ref="reports" />
+    </body>
+</project>
diff --git a/old_trunk/core-constants/pom.xml b/old_trunk/core-constants/pom.xml
new file mode 100644
index 0000000..98b5efd
--- /dev/null
+++ b/old_trunk/core-constants/pom.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you 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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.directory.server</groupId>
+    <artifactId>apacheds-parent</artifactId>
+    <version>1.5.4-SNAPSHOT</version>
+  </parent>
+  <artifactId>apacheds-core-constants</artifactId>
+  <name>ApacheDS Core Constants</name>
+  <packaging>jar</packaging>
+
+  <description>
+    Contains classes that store interfaces with various constants in ApacheDS.
+  </description>
+</project>
+
diff --git a/old_trunk/core-constants/src/main/java/org/apache/directory/server/constants/ApacheSchemaConstants.java b/old_trunk/core-constants/src/main/java/org/apache/directory/server/constants/ApacheSchemaConstants.java
new file mode 100644
index 0000000..69c237b
--- /dev/null
+++ b/old_trunk/core-constants/src/main/java/org/apache/directory/server/constants/ApacheSchemaConstants.java
@@ -0,0 +1,138 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.constants;
+
+
+/**
+ * Constants from the Apache schema.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public interface ApacheSchemaConstants
+{
+    String SCHEMA_NAME = "apache";
+
+    // ---- ObjectClasses -----------------------------------------------------
+    // ApacheCatalogEntry
+    String APACHE_CATALOG_ENTRY_OC                  = "apacheCatalogEntry";
+    String APACHE_CATALOG_ENTRY_OC_OID              = "1.3.6.1.4.1.18060.0.4.1.3.5";
+
+    // apacheFactoryConfiguration
+    String APACHE_FACTORY_CONFIGURATION_OC          = "apacheFactoryConfiguration";
+    String APACHE_FACTORY_CONFIGURATION_OC_OID      = "1.3.6.1.4.1.18060.0.4.1.3.4";
+
+    // ApacheServiceConfiguration
+    String APACHE_SERVICE_CONFIGURATION_OC          = "apacheServiceConfiguration";
+    String APACHE_SERVICE_CONFIGURATION_OC_OID      = "1.3.6.1.4.1.18060.0.4.1.3.3";
+
+    // ApacheSubschema
+    String APACHE_SUBSCHEMA_OC                      = "apacheSubschema";
+    String APACHE_SUBSCHEMA_OC_OID                  = "1.3.6.1.4.1.18060.0.4.1.3.9";
+
+    // JavaClass
+    String JAVA_CLASS_OC                            = "javaClass";
+    String JAVA_CLASS_OC_OID                        = "1.3.6.1.4.1.18060.0.4.1.3.8";
+
+    // JavaStoredProcUnit
+    String JAVA_STORED_PROC_UNIT_OC                 = "javaStoredProcUnit";
+    String JAVA_STORED_PROC_UNIT_OC_OID             = "1.3.6.1.4.1.18060.0.4.1.5.5";
+
+    // JavaxScriptStoredProcUnit
+    String JAVAX_SCRIPT_STORED_PROC_UNIT_OC         = "javaxScriptStoredProcUnit";
+    String JAVAX_SCRIPT_STORED_PROC_UNIT_OC_OID     = "1.3.6.1.4.1.18060.0.4.1.5.8";
+
+    // PrefNode
+    String PREF_NODE_OC                             = "prefNode";
+    String PREF_NODE_OC_OID                         = "1.3.6.1.4.1.18060.0.4.1.3.1";
+
+    // SchemaModificationAttributes
+    String SCHEMA_MODIFICATION_ATTRIBUTES_OC        = "schemaModificationAttributes";
+    String SCHEMA_MODIFICATION_ATTRIBUTES_OC_OID    = "1.3.6.1.4.1.18060.0.4.1.3.10";
+
+    // StoredProcUnit
+    String STORED_PROC_UNIT_OC                      = "storedProcUnit";
+    String STORED_PROC_UNIT_OC_OID                  = "1.3.6.1.4.1.18060.0.4.1.5.3";
+
+    // TriggerExecutionSubentry
+    String TRIGGER_EXECUTION_SUBENTRY_OC            = "triggerExecutionSubentry";
+    String TRIGGER_EXECUTION_SUBENTRY_OC_OID        = "1.3.6.1.4.1.18060.0.4.1.2.28";
+
+    // UnixFile
+    String UNIX_FILE_OC                             = "unixFile";
+    String UNIX_FILE_OC_OID                         = "1.3.6.1.4.1.18060.0.4.1.3.7";
+
+    // WindowsFile
+    String WINDOWS_FILE_OC                          = "windowsFile";
+    String WINDOWS_FILE_OC_OID                      = "1.3.6.1.4.1.18060.0.4.1.3.6";
+
+    // ---- AttributeType ----------------------------------------------------------
+    // ApacheNdn
+    String APACHE_N_DN_AT                           = "apacheNdn";
+    String APACHE_N_DN_OID                          = "1.3.6.1.4.1.18060.0.4.1.2.1";
+    
+    // ApacheNdn
+    String APACHE_UP_DN_AT                          = "apacheUpdn";
+    String APACHE_UP_DN_OID                         = "1.3.6.1.4.1.18060.0.4.1.2.2";
+    
+    // ApacheNdn
+    String APACHE_EXISTANCE_AT                      = "apacheExistance";
+    String APACHE_EXISTANCE_OID                     = "1.3.6.1.4.1.18060.0.4.1.2.3";
+    
+    // ApacheNdn
+    String APACHE_HIERARCHY_AT                      = "apacheHierarchy";
+    String APACHE_HIERARCHY_OID                     = "1.3.6.1.4.1.18060.0.4.1.2.4";
+    
+    // ApacheNdn
+    String APACHE_ONE_ALIAS_AT                      = "apacheOneAlias";
+    String APACHE_ONE_ALIAS_OID                     = "1.3.6.1.4.1.18060.0.4.1.2.5";
+    
+    // ApacheNdn
+    String APACHE_SUB_ALIAS_AT                      = "apacheSubAlias";
+    String APACHE_SUB_ALIAS_OID                     = "1.3.6.1.4.1.18060.0.4.1.2.6";
+
+    // ApacheNdn
+    String APACHE_ALIAS_AT                          = "apacheAlias";
+    String APACHE_ALIAS_OID                         = "1.3.6.1.4.1.18060.0.4.1.2.7";
+
+    // entryDeleted
+    String ENTRY_DELETED_AT                         = "entryDeleted";
+    String ENTRY_DELETED_OID                        = "1.3.6.1.4.1.18060.0.4.1.2.31";
+
+    // SchemaModifiersName
+    String SCHEMA_MODIFIERS_NAME_AT         = "schemaModifiersName";
+    String SCHEMA_MODIFIERS_NAME_AT_OID     = "";
+    
+    // SchemaModifyTimestamp
+    String SCHEMA_MODIFY_TIMESTAMP_AT = "schemaModifyTimestamp";
+    String SCHEMA_MODIFY_TIMESTAMP_AT_OID = "";
+    
+    // SubschemaSubentryName
+    String SUBSCHEMA_SUBENTRY_NAME_AT = "subschemaSubentryName";
+    String SUBSCHEMA_SUBENTRY_NAME_AT_OID = "";
+    
+    // WindowsFilePath
+    String WINDOWS_FILE_AT                          = "windowsFilePath";
+    String WINDOWS_FILE_AT_OID                      = "1.3.6.1.4.1.18060.0.4.1.2.19";
+
+    // WindowsFilePath
+    String UNIX_FILE_AT                             = "unixFilePath";
+    String UNIX_FILE_AT_OID                         = "1.3.6.1.4.1.18060.0.4.1.2.20";
+}
diff --git a/old_trunk/core-constants/src/main/java/org/apache/directory/server/constants/CoreSchemaConstants.java b/old_trunk/core-constants/src/main/java/org/apache/directory/server/constants/CoreSchemaConstants.java
new file mode 100644
index 0000000..52ffe31
--- /dev/null
+++ b/old_trunk/core-constants/src/main/java/org/apache/directory/server/constants/CoreSchemaConstants.java
@@ -0,0 +1,32 @@
+/*
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *   or more contributor license agreements.  See the NOTICE file
+ *   distributed with this work for additional information
+ *   regarding copyright ownership.  The ASF licenses this file
+ *   to you 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.
+ *
+ */
+
+package org.apache.directory.server.constants;
+
+/**
+ * Core schema constants used throughout the server.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public interface CoreSchemaConstants
+{
+    String SCHEMA_NAME = "core";
+}
diff --git a/old_trunk/core-constants/src/main/java/org/apache/directory/server/constants/MetaSchemaConstants.java b/old_trunk/core-constants/src/main/java/org/apache/directory/server/constants/MetaSchemaConstants.java
new file mode 100644
index 0000000..8a1d423
--- /dev/null
+++ b/old_trunk/core-constants/src/main/java/org/apache/directory/server/constants/MetaSchemaConstants.java
@@ -0,0 +1,174 @@
+/*
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *   or more contributor license agreements.  See the NOTICE file
+ *   distributed with this work for additional information
+ *   regarding copyright ownership.  The ASF licenses this file
+ *   to you 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.
+ *
+ */
+package org.apache.directory.server.constants;
+
+
+/**
+ * Apache meta schema specific constants used throughout the server.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public interface MetaSchemaConstants
+{
+    String SCHEMA_NAME = "apachemeta";
+
+    // -- objectClass names --
+    
+    String META_TOP_OC                      = "metaTop";
+    String META_TOP_OC_OID                  = "1.3.6.1.4.1.18060.0.4.0.3.1";
+    
+    String META_OBJECT_CLASS_OC             = "metaObjectClass";
+    String META_OBJECT_CLASS_OC_OID         = "1.3.6.1.4.1.18060.0.4.0.3.2";
+    
+    String META_ATTRIBUTE_TYPE_OC           = "metaAttributeType";
+    String META_ATTRIBUTE_TYPE_OC_OID       = "1.3.6.1.4.1.18060.0.4.0.3.3";
+    
+    String META_SYNTAX_OC                   = "metaSyntax";
+    String META_SYNTAX_OC_OID               = "1.3.6.1.4.1.18060.0.4.0.3.4";
+
+    String META_MATCHING_RULE_OC            = "metaMatchingRule";
+    String META_MATCHING_RULE_OC_OID        = "1.3.6.1.4.1.18060.0.4.0.3.5";
+    
+    String META_DIT_STRUCTURE_RULE_OC       = "metaDITStructureRule";
+    String META_DIT_STRUCTURE_RULE_OC_OID   = "1.3.6.1.4.1.18060.0.4.0.3.6";
+    
+    String META_NAME_FORM_OC                = "metaNameForm";
+    String META_NAME_FORM_OC_OID            = "1.3.6.1.4.1.18060.0.4.0.3.7";
+
+    String META_MATCHING_RULE_USE_OC        = "metaMatchingRuleUse";
+    String META_MATCHING_RULE_USE_OC_OID    = "1.3.6.1.4.1.18060.0.4.0.3.8";
+
+    String META_DIT_CONTENT_RULE_OC         = "metaDITContentRule";
+    String META_DIT_CONTENT_RULE_OC_OID     = "1.3.6.1.4.1.18060.0.4.0.3.9";
+    
+    String META_SYNTAX_CHECKER_OC           = "metaSyntaxChecker";
+    String META_SYNTAX_CHECKER_OC_OID       = "1.3.6.1.4.1.18060.0.4.0.3.10";
+    
+    String META_SCHEMA_OC                   = "metaSchema";
+    String META_SCHEMA_OC_OID               = "1.3.6.1.4.1.18060.0.4.0.3.11";
+    
+    String META_NORMALIZER_OC               = "metaNormalizer";
+    String META_NORMALIZER_OC_OID           = "1.3.6.1.4.1.18060.0.4.0.3.12";
+    
+    String META_COMPARATOR_OC               = "metaComparator";
+    String META_COMPARATOR_OC_OID           = "1.3.6.1.4.1.18060.0.4.0.3.13";
+
+
+    // -- attributeType names --
+    String M_OID_AT                         = "m-oid";
+    String M_OID_AT_OID                     = "1.3.6.1.4.1.18060.0.4.0.2.1 ";
+
+    String M_NAME_AT                        = "m-name";
+    String M_NAME_AT_OID                    = "1.3.6.1.4.1.18060.0.4.0.2.2 ";
+
+    String M_DESCRIPTION_AT                 = "m-description";
+    String M_DESCRIPTION_AT_OID             = "1.3.6.1.4.1.18060.0.4.0.2.3 ";
+    
+    String M_OBSOLETE_AT                    = "m-obsolete";
+    String M_OBSOLETE_AT_OID                = "1.3.6.1.4.1.18060.0.4.0.2.4 ";
+    
+    String M_SUP_OBJECT_CLASS_AT            = "m-supObjectClass";
+    String M_SUP_OBJECT_CLASS_AT_OID        = "1.3.6.1.4.1.18060.0.4.0.2.5 ";
+    
+    String M_MUST_AT                        = "m-must";
+    String M_MUST_AT_OID                    = "1.3.6.1.4.1.18060.0.4.0.2.6 ";
+
+    String M_MAY_AT                         = "m-may";
+    String M_MAY_AT_OID                     = "1.3.6.1.4.1.18060.0.4.0.2.7 ";
+
+    String M_TYPE_OBJECT_CLASS_AT           = "m-typeObjectClass";
+    String M_TYPE_OBJECT_CLASS_AT_OID       = "1.3.6.1.4.1.18060.0.4.0.2.8 ";
+    
+    String M_SUP_ATTRIBUTE_TYPE_AT          = "m-supAttributeType";
+    String M_SUP_ATTRIBUTE_TYPE_AT_OID      = "1.3.6.1.4.1.18060.0.4.0.2.10";
+
+    String M_EQUALITY_AT                    = "m-equality";
+    String M_EQUALITY_AT_OID                = "1.3.6.1.4.1.18060.0.4.0.2.11";
+
+    String M_ORDERING_AT                    = "m-ordering";
+    String M_ORDERING_AT_OID                = "1.3.6.1.4.1.18060.0.4.0.2.12";
+
+    String M_SUBSTR_AT                      = "m-substr";
+    String M_SUBSTR_AT_OID                  = "1.3.6.1.4.1.18060.0.4.0.2.13";
+
+    String M_SYNTAX_AT                      = "m-syntax";
+    String M_SYNTAX_AT_OID                  = "1.3.6.1.4.1.18060.0.4.0.2.14";
+
+    String M_SINGLE_VALUE_AT                = "m-singleValue";
+    String M_SINGLE_VALUE_AT_OID            = "1.3.6.1.4.1.18060.0.4.0.2.15";
+    
+    String M_COLLECTIVE_AT                  = "m-collective";
+    String M_COLLECTIVE_AT_OID              = "1.3.6.1.4.1.18060.0.4.0.2.16";
+
+    String M_NO_USER_MODIFICATION_AT        = "m-noUserModification";
+    String M_NO_USER_MODIFICATION_AT_OID    = "1.3.6.1.4.1.18060.0.4.0.2.17";
+
+    String M_USAGE_AT                       = "m-usage";
+    String M_USAGE_AT_OID                   = "1.3.6.1.4.1.18060.0.4.0.2.18";
+    
+    String M_RULE_ID_AT                     = "m-ruleId";
+    String M_RULE_ID_AT_OID                 = "1.3.6.1.4.1.18060.0.4.0.2.20";
+    
+    String M_FORM_AT                        = "m-form";
+    String M_FORM_AT_OID                    = "1.3.6.1.4.1.18060.0.4.0.2.21";
+    
+    String M_SUP_DIT_STRUCTURE_RULE_AT      = "m-supDITStructureRule";
+    String M_SUP_DIT_STRUCTURE_RULE_AT_OID  = "1.3.6.1.4.1.18060.0.4.0.2.22";
+
+    String M_OC_AT                          = "m-oc";
+    String M_OC_AT_OID                      = "1.3.6.1.4.1.18060.0.4.0.2.24";
+    
+    String M_AUX_AT                         = "m-aux";
+    String M_AUX_AT_OID                     = "1.3.6.1.4.1.18060.0.4.0.2.26";
+
+    String M_NOT_AT                         = "m-not";
+    String M_NOT_AT_OID                     = "1.3.6.1.4.1.18060.0.4.0.2.27";
+    
+    String M_APPLIES_AT                     = "m-applies";
+    String M_APPLIES_AT_OID                 = "1.3.6.1.4.1.18060.0.4.0.2.29";
+    
+    String M_MATCHING_RULE_SYNTAX_AT        = "m-matchingRuleSyntax";
+    String M_MATCHING_RULE_SYNTAX_AT_OID    = "1.3.6.1.4.1.18060.0.4.0.2.31";
+
+    String M_FQCN_AT                        = "m-fqcn";
+    String M_FQCN_AT_OID                    = "1.3.6.1.4.1.18060.0.4.0.2.32";
+
+    String M_BYTECODE_AT                    = "m-bytecode";
+    String M_BYTECODE_AT_OID                = "1.3.6.1.4.1.18060.0.4.0.2.33";
+    
+    String X_HUMAN_READABLE_AT              = "x-humanReadable";
+    String X_HUMAN_READABLE_AT_OID          = "1.3.6.1.4.1.18060.0.4.0.2.34";
+
+    String M_DISABLED_AT                    = "m-disabled";
+    String M_DISABLED_AT_OID                = "1.3.6.1.4.1.18060.0.4.0.2.37";
+
+    String M_DEPENDENCIES_AT                = "m-dependencies";
+    String M_DEPENDENCIES_AT_OID            = "1.3.6.1.4.1.18060.0.4.0.2.38";
+    
+    String M_LENGTH_AT                      = "m-length";
+    String M_LENGTH_AT_OID                  = "1.3.6.1.4.1.18060.0.4.0.2.39";
+    
+    // -- schema extensions & values --
+    
+    String X_SCHEMA = "X-SCHEMA";
+    String X_IS_HUMAN_READABLE = "X-IS-HUMAN-READABLE";
+    String SCHEMA_OTHER = "other";
+}
diff --git a/old_trunk/core-constants/src/main/java/org/apache/directory/server/constants/ServerDNConstants.java b/old_trunk/core-constants/src/main/java/org/apache/directory/server/constants/ServerDNConstants.java
new file mode 100644
index 0000000..39058d2
--- /dev/null
+++ b/old_trunk/core-constants/src/main/java/org/apache/directory/server/constants/ServerDNConstants.java
@@ -0,0 +1,75 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.constants;
+
+/**
+ * A utility class where we declare all the statically defined DN used in the server.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev:$
+ */
+public class ServerDNConstants
+{
+    /**
+     * A private constructor to protect this class containing only constants
+     */
+    private ServerDNConstants()
+    {
+        
+    }
+    
+    /** The administrators group DN */
+    public static final String ADMINISTRATORS_GROUP_DN = "cn=Administrators,ou=groups,ou=system";
+
+    /** The system DN */
+    public static final String SYSTEM_DN = "ou=system";
+    
+    /** the default user principal or DN */
+    public static final String ADMIN_SYSTEM_DN = "uid=admin,ou=system";
+    
+    /** the normalized user principal or DN */
+    public static final String ADMIN_SYSTEM_DN_NORMALIZED = "0.9.2342.19200300.100.1.1=admin,2.5.4.11=system";
+
+    /** the DN for the global schema subentry */
+    public static final String CN_SCHEMA_DN = "cn=schema";
+    
+    /** The DN for the gloval schema subentry normalized */
+    public static final String CN_SCHEMA_DN_NORMALIZED = "2.5.4.3=schema";
+   
+    /** the DN for the global schema subentry */
+    public static final String OU_SCHEMA_DN = "ou=schema";
+    
+    /** the base dn under which all users reside */
+    public static final String USERS_SYSTEM_DN = "ou=users,ou=system";
+    
+    /** The default change password base DN. */
+    public static final String USER_EXAMPLE_COM_DN = "ou=users,dc=example,dc=com";
+    
+    
+    /** the base dn under which all groups reside */
+    public static final String GROUPS_SYSTEM_DN = "ou=groups,ou=system";
+    
+    /** the dn base of the system preference hierarchy */
+    public static final String SYSPREFROOT_SYSTEM_DN = "prefNodeName=sysPrefRoot,ou=system";
+    
+    /** The ldifDile base which stores the name of the loaded ldif files */
+    public static final String LDIF_FILES_DN = "ou=loadedLdifFiles,ou=configuration,ou=system";
+
+}
diff --git a/old_trunk/core-constants/src/main/java/org/apache/directory/server/constants/SystemSchemaConstants.java b/old_trunk/core-constants/src/main/java/org/apache/directory/server/constants/SystemSchemaConstants.java
new file mode 100644
index 0000000..9f54f48
--- /dev/null
+++ b/old_trunk/core-constants/src/main/java/org/apache/directory/server/constants/SystemSchemaConstants.java
@@ -0,0 +1,32 @@
+/*
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *   or more contributor license agreements.  See the NOTICE file
+ *   distributed with this work for additional information
+ *   regarding copyright ownership.  The ASF licenses this file
+ *   to you 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.
+ *
+ */
+package org.apache.directory.server.constants;
+
+
+/**
+ * Constants for the System schema.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public interface SystemSchemaConstants
+{
+    String SCHEMA_NAME = "system";
+}
diff --git a/old_trunk/core-constants/src/main/java/org/apache/directory/server/core/partition/Oid.java b/old_trunk/core-constants/src/main/java/org/apache/directory/server/core/partition/Oid.java
new file mode 100644
index 0000000..3db3418
--- /dev/null
+++ b/old_trunk/core-constants/src/main/java/org/apache/directory/server/core/partition/Oid.java
@@ -0,0 +1,50 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition;
+
+
+/**
+ * Provides constants of private OIDs.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class Oid
+{
+    /** Private OID (1.3.6.1.4.1.18060.0.4.1.2.1) for _ndn op attrib */
+    public static final String NDN = "1.3.6.1.4.1.18060.0.4.1.2.1";
+    /** Private OID (1.3.6.1.4.1.18060.0.4.1.2.2) for _updn op attrib */
+    public static final String UPDN = "1.3.6.1.4.1.18060.0.4.1.2.2";
+    /** Private OID (1.3.6.1.4.1.18060.0.4.1.2.3) for _existance op attrib */
+    public static final String EXISTANCE = "1.3.6.1.4.1.18060.0.4.1.2.3";
+    /** Private OID (1.3.6.1.4.1.18060.0.4.1.2.4) for _hierarchy op attrib */
+    public static final String HIERARCHY = "1.3.6.1.4.1.18060.0.4.1.2.4";
+    /** Private OID (1.3.6.1.4.1.18060.0.4.1.2.5) for _oneAlias index */
+    public static final String ONEALIAS = "1.3.6.1.4.1.18060.0.4.1.2.5";
+    /** Private OID (1.3.6.1.4.1.18060.0.4.1.2.6) for _subAlias index */
+    public static final String SUBALIAS = "1.3.6.1.4.1.18060.0.4.1.2.6";
+    /** Private OID (1.3.6.1.4.1.18060.0.4.1.2.7) for _alias index */
+    public static final String ALIAS = "1.3.6.1.4.1.18060.0.4.1.2.7";
+
+
+    private Oid()
+    {
+    }
+}
diff --git a/old_trunk/core-constants/src/site/site.xml b/old_trunk/core-constants/src/site/site.xml
new file mode 100644
index 0000000..9ec7c16
--- /dev/null
+++ b/old_trunk/core-constants/src/site/site.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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.
+-->
+<!--
+  @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+-->
+<project name="${project.name}">
+    <bannerLeft>
+        <name>${project.name}</name>
+    </bannerLeft>
+    <publishDate position="navigation-bottom" format="dd-MM-yyyy HH:mm" />
+    <body>
+        <menu name="Parent">
+            <item name="Apache Directory ApacheDS" href="../index.html" />
+        </menu>
+        <menu ref="reports" />
+    </body>
+</project>
diff --git a/old_trunk/core-entry/pom.xml b/old_trunk/core-entry/pom.xml
new file mode 100644
index 0000000..5102c66
--- /dev/null
+++ b/old_trunk/core-entry/pom.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you 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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.directory.server</groupId>
+    <artifactId>apacheds-parent</artifactId>
+    <version>1.5.4-SNAPSHOT</version>
+  </parent>
+  <artifactId>apacheds-core-entry</artifactId>
+  <name>ApacheDS Core Entry</name>
+
+  <description>
+    Server side LDAP entry classes.
+  </description>
+
+  <packaging>jar</packaging>
+
+  <dependencies>
+    <dependency>
+      <groupId>${pom.groupId}</groupId>
+      <version>${pom.version}</version>
+      <artifactId>apacheds-schema-registries</artifactId>
+    </dependency>
+    
+    <dependency>
+      <groupId>${pom.groupId}</groupId>
+      <version>${pom.version}</version>
+      <artifactId>apacheds-schema-bootstrap</artifactId>
+      <scope>test</scope>
+    </dependency>
+    
+    <dependency>
+      <groupId>${pom.groupId}</groupId>
+      <version>${pom.version}</version>
+      <artifactId>apacheds-schema-extras</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>${pom.groupId}</groupId>
+      <version>${pom.version}</version>
+      <artifactId>apacheds-jdbm</artifactId>
+    </dependency>
+    
+  </dependencies>
+  
+</project>
+
diff --git a/old_trunk/core-entry/src/main/java/org/apache/directory/server/core/entry/DefaultServerAttribute.java b/old_trunk/core-entry/src/main/java/org/apache/directory/server/core/entry/DefaultServerAttribute.java
new file mode 100644
index 0000000..5e55703
--- /dev/null
+++ b/old_trunk/core-entry/src/main/java/org/apache/directory/server/core/entry/DefaultServerAttribute.java
@@ -0,0 +1,1145 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements.  See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership.  The ASF licenses this file

+ * to you 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.

+ */

+package org.apache.directory.server.core.entry;

+

+

+import javax.naming.NamingException;

+import javax.naming.directory.InvalidAttributeValueException;

+

+import org.apache.directory.shared.asn1.primitives.OID;

+import org.apache.directory.shared.ldap.entry.EntryAttribute;

+import org.apache.directory.shared.ldap.entry.Value;

+import org.apache.directory.shared.ldap.entry.client.ClientBinaryValue;

+import org.apache.directory.shared.ldap.entry.client.ClientStringValue;

+import org.apache.directory.shared.ldap.entry.client.DefaultClientAttribute;

+import org.apache.directory.shared.ldap.schema.AttributeType;

+import org.apache.directory.shared.ldap.util.StringTools;

+import org.slf4j.Logger;

+import org.slf4j.LoggerFactory;

+

+

+/**

+ * A server side entry attribute aware of schema.

+ *

+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>

+ * @version $Rev$, $Date$

+ */

+public final class DefaultServerAttribute extends DefaultClientAttribute implements ServerAttribute

+{

+    /** logger for reporting errors that might not be handled properly upstream */

+    private static final Logger LOG = LoggerFactory.getLogger( DefaultServerAttribute.class );

+    

+    /** The associated AttributeType */

+    private AttributeType attributeType;

+    

+    

+    // -----------------------------------------------------------------------

+    // utility methods

+    // -----------------------------------------------------------------------

+    /**

+     * Private helper method used to set an UpId from an attributeType

+     * 

+     * @param at The attributeType for which we want the upID

+     * @return the ID of the given attributeType

+     */

+    private String getUpId( AttributeType at )

+    {

+        String atUpId = at.getName();

+        

+        if ( atUpId == null )

+        {

+            atUpId = at.getOid();

+        }

+        

+        return atUpId;

+    }

+    

+    

+    /**

+     * <p>

+     * Check if the current attribute type is of the expected attributeType

+     * </p>

+     * <p>

+     * This method won't tell if the current attribute is a descendant of 

+     * the attributeType. For instance, the "CN" serverAttribute will return

+     * false if we ask if it's an instance of "Name". 

+     * </p> 

+     *

+     * @param attributeId The AttributeType ID to check

+     * @return True if the current attribute is of the expected attributeType

+     * @throws InvalidAttributeValueException If there is no AttributeType

+     */

+    public boolean instanceOf( String attributeId ) throws InvalidAttributeValueException

+    {

+        String trimmedId = StringTools.trim( attributeId );

+        

+        if ( StringTools.isEmpty( trimmedId ) )

+        {

+            return false;

+        }

+        

+        String normId = StringTools.lowerCaseAscii( trimmedId );

+        

+        for ( String name:attributeType.getNamesRef() )

+        {

+            if ( normId.equalsIgnoreCase( name ) )

+            {

+                return true;

+            }

+        }

+        

+        return normId.equalsIgnoreCase( attributeType.getOid() );

+    }

+    

+

+    /**

+     * <p>

+     * Overload the {@link DefaultClientAttribute#setId(String)} method.

+     * </p>

+     * <p>

+     * As the attributeType has already been set, we have to be sure that the 

+     * argument is compatible with the attributeType's name. 

+     * </p>

+     * <p>

+     * If the given ID is not compatible with the attributeType's possible

+     * names, the previously loaded ID will be kept.

+     * </p>

+     *

+     * @param id The attribute ID

+     */

+    public void setId( String id )

+    {

+        if ( !StringTools.isEmpty( StringTools.trim( id  ) ) )

+        {

+            if ( attributeType.getName() == null )

+            {

+                // If the name is null, then we may have to store an OID

+                if ( OID.isOID( id )  && attributeType.getOid().equals( id ) )

+                {

+                    // Everything is fine, store the upId.

+                    // This should not happen...

+                    super.setId( id );

+                }

+            }

+            else

+            {

+                // We have at least one name. Check that the normalized upId

+                // is one of those names. Otherwise, the upId may be an OID too.

+                // In this case, it must be equals to the attributeType OID.

+                String normId = StringTools.lowerCaseAscii( StringTools.trim( id ) );

+                

+                for ( String atName:attributeType.getNamesRef() )

+                {

+                    if ( atName.equalsIgnoreCase( normId ) )

+                    {

+                        // Found ! We can store the upId and get out

+                        super.setId( normId );

+                        return;

+                    }

+                }

+                

+                // Last case, the UpId is an OID

+                if ( OID.isOID( normId ) && attributeType.getOid().equals( normId ) )

+                {

+                    // We have an OID : stores it

+                    super.setUpId( normId );

+                }

+                else

+                {

+                    // The id is incorrect : this is not allowed 

+                    throw new IllegalArgumentException( "The ID '" + id + "'is incompatible with the AttributeType's id '" + 

+                        attributeType.getName() + "'" );

+                }

+            }

+        }

+        else

+        {

+            throw new IllegalArgumentException( "An ID cannnot be null, empty, or resolved to an emtpy" +

+            " value when trimmed" );

+        }

+    }

+    

+    

+    /**

+     * <p>

+     * Overload the {@link DefaultClientAttribute#setUpId(String)} method.

+     * </p>

+     * <p>

+     * As the attributeType has already been set, we have to be sure that the 

+     * argument is compatible with the attributeType's name. 

+     * </p>

+     * <p>

+     * If the given ID is not compatible with the attributeType's possible

+     * names, the previously loaded ID will be kept.

+     * </p>

+     *

+     * @param upId The attribute ID

+     */

+    public void setUpId( String upId )

+    {

+        if ( !StringTools.isEmpty( StringTools.trim( upId  ) ) )

+        {

+            if ( attributeType.getName() == null )

+            {

+                // If the name is null, then we may have to store an OID

+                if ( OID.isOID( upId )  && attributeType.getOid().equals( upId ) )

+                {

+                    // Everything is fine, store the upId.

+                    // This should not happen...

+                    super.setUpId( upId );

+                    

+                }

+            }

+            else

+            {

+                // We have at least one name. Check that the normalized upId

+                // is one of those names. Otherwise, the upId may be an OID too.

+                // In this case, it must be equals to the attributeType OID.

+                String normUpId = StringTools.lowerCaseAscii( StringTools.trim( upId ) );

+                

+                for ( String atId:attributeType.getNamesRef() )

+                {

+                    if ( atId.equalsIgnoreCase( normUpId ) )

+                    {

+                        // Found ! We can store the upId and get out

+                        super.setUpId( upId );

+                        return;

+                    }

+                }

+                

+                // Last case, the UpId is an OID

+                if ( OID.isOID( normUpId ) && attributeType.getOid().equals( normUpId ) )

+                {

+                    // We have an OID : stores it

+                    super.setUpId( upId );

+                }

+            }

+        }

+    }

+    

+    

+    /**

+     * <p>

+     * Set the user provided ID. If we have none, the upId is assigned

+     * the attributetype's name. If it does not have any name, we will

+     * use the OID.

+     * </p>

+     * <p>

+     * If we have an upId and an AttributeType, they must be compatible. :

+     *  - if the upId is an OID, it must be the AttributeType's OID

+     *  - otherwise, its normalized form must be equals to ones of

+     *  the attributeType's names.

+     * </p>

+     * <p>

+     * In any case, the ATtributeType will be changed. The caller is responsible for

+     * the present values to be compatoble with the new AttributeType.

+     * </p>

+     *

+     * @param upId The attribute ID

+     * @param attributeType The associated attributeType

+     */

+    public void setUpId( String upId, AttributeType attributeType )

+    {

+        if ( StringTools.isEmpty( StringTools.trim( upId  ) ) )

+        {

+            super.setUpId( getUpId( attributeType ) );

+            this.attributeType = attributeType;

+        }

+        else

+        {

+            String name = attributeType.getName();

+            

+            if ( name == null )

+            {

+                // If the name is null, then we may have to store an OID

+                if ( OID.isOID( upId )  && attributeType.getOid().equals( upId ) )

+                {

+                    //  Everything is fine, store the upId. 

+                    super.setUpId( upId );

+                    this.attributeType = attributeType;

+                }

+                else

+                {

+                    // We have a difference or the upId is not a valid OID :

+                    // we will use the attributeTypeOID in this case.

+                    LOG.warn( "The upID ({}) is not an OID or is different from the AttributeType OID({})",

+                        upId, attributeType.getOid() );

+                    super.setUpId( attributeType.getOid() );

+                    this.attributeType = attributeType;

+                }

+            }

+            else

+            {

+                // We have at least one name. Check that the normalized upId

+                // is one of those names. Otherwise, the upId may be an OID too.

+                // In this case, it must be equals to the attributeType OID.

+                String normUpId = StringTools.lowerCaseAscii( StringTools.trim( upId ) );

+                

+                for ( String atId:attributeType.getNamesRef() )

+                {

+                    if ( atId.equalsIgnoreCase( normUpId ) )

+                    {

+                        // Found ! We can store the upId and get out

+                        super.setUpId( upId );

+                        this.attributeType = attributeType;

+                        return;

+                    }

+                }

+    

+                // UpId was not found in names. It should be an OID, or if not, we 

+                // will use the AttributeType name.

+                if ( OID.isOID( normUpId ) && attributeType.getOid().equals( normUpId ) )

+                {

+                    // We have an OID : stores it

+                    super.setUpId( upId );

+                    this.attributeType = attributeType;

+                }

+                else

+                {

+                    String message = "The upID (" + upId + ") is not an OID or is different from the AttributeType OID (" + 

+                                        attributeType.getOid() + ")";

+                    // Not a valid OID : use the AttributeTypes OID name instead

+                    LOG.error( message );

+                    throw new IllegalArgumentException( message );

+                }

+            }

+        }

+    }

+    

+    

+    /**

+     * <p>

+     * Set the attribute type associated with this ServerAttribute.

+     * </p>

+     * <p>

+     * The current attributeType will be replaced. It is the responsibility of

+     * the caller to insure that the existing values are compatible with the new

+     * AttributeType

+     * </p>

+     *

+     * @param attributeType the attributeType associated with this entry attribute

+     */

+    public void setAttributeType( AttributeType attributeType )

+    {

+        if ( attributeType == null )

+        {

+            throw new IllegalArgumentException( "The AttributeType parameter should not be null" );

+        }

+

+        this.attributeType = attributeType;

+        setUpId( null, attributeType );

+        

+        try

+        {

+            if ( attributeType.getSyntax().isHumanReadable() )

+            {

+                isHR = true;

+            }

+            else

+            {

+                isHR = false;

+            }

+        }

+        catch ( NamingException ne )

+        {

+            // If we have an exception while trying to get the Syntax for this attribute

+            // just set it as Binary

+            isHR = false;

+        }

+    }

+    

+    

+    /**

+     * Get the attribute type associated with this ServerAttribute.

+     *

+     * @return the attributeType associated with this entry attribute

+     */

+    public AttributeType getAttributeType()

+    {

+        return attributeType;

+    }

+    

+    

+    // maybe have some additional convenience constructors which take

+    // an initial value as a string or a byte[]

+    /**

+     * Create a new instance of a EntryAttribute, without ID nor value.

+     * 

+     * @param attributeType the attributeType for the empty attribute added into the entry

+     */

+    public DefaultServerAttribute( AttributeType attributeType )

+    {

+        if ( attributeType == null )

+        {

+            throw new IllegalArgumentException( "The AttributeType parameter should not be null" );

+        }

+        

+        setAttributeType( attributeType );

+    }

+

+

+    /**

+     * Create a new instance of a EntryAttribute, without value.

+     * 

+     * @param upId the ID for the added attributeType

+     * @param attributeType the added AttributeType

+     */

+    public DefaultServerAttribute( String upId, AttributeType attributeType )

+    {

+        if ( attributeType == null ) 

+        {

+            String message = "The AttributeType parameter should not be null";

+            LOG.error( message );

+            throw new IllegalArgumentException( message );

+        }

+

+        setAttributeType( attributeType );

+        setUpId( upId );

+    }

+

+

+    /**

+     * Doc me more!

+     *

+     * If the value does not correspond to the same attributeType, then it's

+     * wrapped value is copied into a new Value which uses the specified

+     * attributeType.

+     *

+     * @param attributeType the attribute type according to the schema

+     * @param vals an initial set of values for this attribute

+     */

+    public DefaultServerAttribute( AttributeType attributeType, Value<?>... vals )

+    {

+        this( null, attributeType, vals );

+    }

+

+

+    /**

+     * Doc me more!

+     *

+     * If the value does not correspond to the same attributeType, then it's

+     * wrapped value is copied into a new Value which uses the specified

+     * attributeType.

+     * 

+     * Otherwise, the value is stored, but as a reference. It's not a copy.

+     *

+     * @param upId the ID of the added attribute

+     * @param attributeType the attribute type according to the schema

+     * @param vals an initial set of values for this attribute

+     */

+    public DefaultServerAttribute( String upId, AttributeType attributeType, Value<?>... vals )

+    {

+        if ( attributeType == null )

+        {

+            throw new IllegalArgumentException( "The AttributeType parameter should not be null" );

+        }

+        

+        setAttributeType( attributeType );

+        setUpId( upId, attributeType );

+        add( vals );

+    }

+

+

+    /**

+     * Create a new instance of a EntryAttribute, without ID but with some values.

+     * 

+     * @param attributeType The attributeType added on creation

+     * @param vals The added value for this attribute

+     */

+    public DefaultServerAttribute( AttributeType attributeType, String... vals )

+    {

+        this( null, attributeType, vals );

+    }

+

+

+    /**

+     * Create a new instance of a EntryAttribute.

+     * 

+     * @param upId the ID for the added attribute

+     * @param attributeType The attributeType added on creation

+     * @param vals the added values for this attribute

+     */

+    public DefaultServerAttribute( String upId, AttributeType attributeType, String... vals )

+    {

+        if ( attributeType == null )

+        {

+            throw new IllegalArgumentException( "The AttributeType parameter should not be null" );

+        }

+

+        setAttributeType( attributeType );

+        add( vals );

+        setUpId( upId, attributeType );

+    }

+

+

+    /**

+     * Create a new instance of a EntryAttribute, with some byte[] values.

+     * 

+     * @param attributeType The attributeType added on creation

+     * @param vals The value for the added attribute

+     */

+    public DefaultServerAttribute( AttributeType attributeType, byte[]... vals )

+    {

+        this( null, attributeType, vals );

+    }

+

+

+    /**

+     * Create a new instance of a EntryAttribute, with some byte[] values.

+     * 

+     * @param upId the ID for the added attribute

+     * @param attributeType the AttributeType to be added

+     * @param vals the values for the added attribute

+     */

+    public DefaultServerAttribute( String upId, AttributeType attributeType, byte[]... vals )

+    {

+        if ( attributeType == null )

+        {

+            throw new IllegalArgumentException( "The AttributeType parameter should not be null" );

+        }

+

+        setAttributeType( attributeType );

+        add( vals );

+        setUpId( upId, attributeType );

+    }

+    

+    

+    /**

+     * Clone an attribute. All the element are duplicated, so a modification on

+     * the original object won't affect the cloned object, as a modification

+     * on the cloned object has no impact on the original object

+     * 

+     * @return a clone of the current attribute

+     */

+    public ServerAttribute clone()

+    {

+        // clone the structure by cloner the inherited class

+        ServerAttribute clone = (ServerAttribute)super.clone();

+        

+        // We are done !

+        return clone;

+    }

+

+

+    /**

+     * <p>

+     * Overload the ClientAttribte isHR method : we can't change this flag

+     * for a ServerAttribute, as the HR is already set using the AttributeType.

+     * Set the attribute to Human Readable or to Binary. 

+     * </p>

+     * 

+     * @param isHR <code>true</code> for a Human Readable attribute, 

+     * <code>false</code> for a Binary attribute.

+     */

+    public void setHR( boolean isHR )

+    {

+        // Do nothing...

+    }

+

+    

+    /**

+     * <p>

+     * Checks to see if this attribute is valid along with the values it contains.

+     * </p>

+     * <p>

+     * An attribute is valid if :

+     * <li>All of its values are valid with respect to the attributeType's syntax checker</li>

+     * <li>If the attributeType is SINGLE-VALUE, then no more than a value should be present</li>

+     *</p>

+     * @return true if the attribute and it's values are valid, false otherwise

+     * @throws NamingException if there is a failure to check syntaxes of values

+     */

+    public boolean isValid() throws NamingException

+    {

+        // First check if the attribute has more than one value

+        // if the attribute is supposed to be SINGLE_VALUE

+        if ( attributeType.isSingleValue() && ( values.size() > 1 ) )

+        {

+            return false;

+        }

+

+        for ( Value<?> value : values )

+        {

+            if ( ! value.isValid() )

+            {

+                return false;

+            }

+        }

+

+        return true;

+    }

+

+

+    /**

+     * @see EntryAttribute#add(org.apache.directory.shared.ldap.entry.Value...)

+     * 

+     * @return the number of added values into this attribute

+     */

+    public int add( Value<?>... vals )

+    {

+        int nbAdded = 0;

+        

+        for ( Value<?> val:vals )

+        {

+            try

+            {

+                if ( attributeType.getSyntax().isHumanReadable() )

+                {

+                    if ( val == null )

+                    {

+                        Value<String> nullSV = new ServerStringValue( attributeType, (String)null );

+                        

+                        if ( !values.contains( nullSV ) )

+                        {

+                            values.add( nullSV );

+                            nbAdded++;

+                        }

+                    }

+                    else if ( val instanceof ServerStringValue )

+                    {

+                        if ( !values.contains( val ) )

+                        {

+                            if ( values.add( val ) )

+                            {

+                                nbAdded++;

+                            }

+                        }

+                    }

+                    else if ( val instanceof ClientStringValue )

+                    {

+                        // If we get a Client value, convert it to a Server value first 

+                        Value<String> serverStringValue = new ServerStringValue( attributeType, (String)val.get() ); 

+                        

+                        if ( !values.contains( serverStringValue ) )

+                        {

+                            if ( values.add( serverStringValue ) )

+                            {

+                                nbAdded++;

+                            }

+                        }

+                    }

+                    else

+                    {

+                        String message = "The value must be a String, as its AttributeType is H/R";

+                        LOG.error( message );

+                    }

+                }

+                else

+                {

+                    if ( val == null )

+                    {

+                        Value<byte[]> nullSV = new ServerBinaryValue( attributeType, (byte[])null );

+                        

+                        if ( !values.contains( nullSV ) )

+                        {

+                            values.add( nullSV );

+                            nbAdded++;

+                        }

+                    }

+                    else if ( ( val instanceof ClientBinaryValue ) )

+                    {

+                        Value<byte[]> serverBinaryValue = new ServerBinaryValue( attributeType, (byte[])val.get() ); 

+                        

+                        if ( !values.contains( serverBinaryValue ) )

+                        {

+                            if ( values.add( serverBinaryValue ) )

+                            {

+                                nbAdded++;

+                            }

+                        }

+                    }

+                    else if ( val instanceof ServerBinaryValue )

+                    {

+                        if ( !values.contains( val ) )

+                        {

+                            if ( values.add( val ) )

+                            {

+                                nbAdded++;

+                            }

+                        }

+                    }

+                    else

+                    {

+                        String message = "The value must be a byte[], as its AttributeType is not H/R";

+                        LOG.error( message );

+                    }

+                }

+            }

+            catch ( NamingException ne )

+            {

+                String message = "Error while adding value '" + val.toString() +"' : " + ne.getMessage();

+                LOG.error( message );

+            }

+        }

+        

+        return nbAdded;

+    }

+

+

+    /**

+     * <p>

+     * Adds some values to this attribute. If the new values are already present in

+     * the attribute values, the method has no effect.

+     * </p>

+     * <p>

+     * The new values are added at the end of list of values.

+     * </p>

+     * <p>

+     * This method returns the number of values that were added.

+     * </p>

+     * If the value's type is different from the attribute's type,

+     * the value is not added.

+     *

+     * @param vals some new values to be added which may be null

+     * @return the number of added values, or 0 if none has been added

+     */

+    public int add( String... vals )

+    {

+        if ( isHR )

+        {

+            int nbAdded = 0;

+            

+            for ( String val:vals )

+            {

+                if ( add( new ServerStringValue( attributeType, val ) ) != 0 )

+                {

+                    nbAdded++;

+                }

+                else

+                {

+                    LOG.error( "The value '" + val + "' is incorrect, it hasn't been added" );

+                }

+            }

+            

+            return nbAdded;

+        }

+        else

+        {

+            // We can't add String values into a Binary serverAttribute

+            return 0;

+        }

+    }    

+    

+    

+    /**

+     * <p>

+     * Adds some values to this attribute. If the new values are already present in

+     * the attribute values, the method has no effect.

+     * </p>

+     * <p>

+     * The new values are added at the end of list of values.

+     * </p>

+     * <p>

+     * This method returns the number of values that were added.

+     * </p>

+     * <p>

+     * If the value's type is different from the attribute's type,

+     * the value is not added.

+     * </p>

+     * It's the responsibility of the caller to check if the stored

+     * values are consistent with the attribute's type.

+     * <p>

+     *

+     * @param vals some new values to be added which may be null

+     * @return the number of added values, or 0 if none has been added

+     */

+    public int add( byte[]... vals )

+    {

+        if ( !isHR )

+        {

+            int nbAdded = 0;

+            

+            for ( byte[] val:vals )

+            {

+                if ( add( new ServerBinaryValue( attributeType, val ) ) != 0 )

+                {

+                    nbAdded++;

+                }

+                else

+                {

+                    LOG.error( "The value '" + val + "' is incorrect, it hasn't been added" );

+                }

+            }

+            

+            return nbAdded;

+        }

+        else

+        {

+            // We can't add Binary values into a String serverAttribute

+            return 0;

+        }

+    }    

+    

+    

+    /**

+     * Remove all the values from this attribute type, including a 

+     * null value. 

+     */

+    public void clear()

+    {

+        values.clear();

+    }

+

+

+    /**

+     * <p>

+     * Indicates whether the specified values are some of the attribute's values.

+     * </p>

+     * <p>

+     * If the Attribute is HR, te metho will only accept String Values. Otherwise, 

+     * it will only accept Binary values.

+     * </p>

+     *

+     * @param vals the values

+     * @return true if this attribute contains all the values, otherwise false

+     */

+    public boolean contains( Value<?>... vals )

+    {

+        // Iterate through all the values, and quit if we 

+        // don't find one in the values. We have to separate the check

+        // depending on the isHR flag value.

+        if ( isHR )

+        {

+            for ( Value<?> val:vals )

+            {

+                if ( val instanceof ServerStringValue )

+                {

+                    if ( !values.contains( val ) )

+                    {

+                        return false;

+                    }

+                }

+                else if ( val instanceof ClientStringValue )

+                {

+                    ServerStringValue serverValue = new ServerStringValue( attributeType, (String)val.get() );

+                    

+                    if ( !values.contains( serverValue ) )

+                    {

+                        return false;

+                    }

+                }

+                else

+                {

+                    // Not a String value

+                    return false;

+                }

+            }

+        }

+        else

+        {

+            for ( Value<?> val:vals )

+            {

+                if ( val instanceof ClientBinaryValue )

+                {

+                    if ( !values.contains( val ) )

+                    {

+                        return false;

+                    }

+                }

+                else

+                {

+                    // Not a Binary value

+                    return false;

+                }

+            }

+        }

+        

+        return true;

+    }

+

+

+    /**

+     * <p>

+     * Indicates whether all the specified values are attribute's values. If

+     * at least one value is not an attribute's value, this method will return 

+     * <code>false</code>

+     * </p>

+     * <p>

+     * If the Attribute is not HR, this method will returns <code>false</code>

+     * </p>

+     *

+     * @param vals the values

+     * @return true if this attribute contains all the values, otherwise false

+     */

+    public boolean contains( String... vals )

+    {

+        if ( isHR )

+        {

+            // Iterate through all the values, and quit if we 

+            // don't find one in the values

+            for ( String val:vals )

+            {

+                ServerStringValue value = new ServerStringValue( attributeType, val );

+                

+                if ( !values.contains( value ) )

+                {

+                    return false;

+                }

+            }

+            

+            return true;

+        }

+        else

+        {

+            return false;

+        }

+    }

+    

+    

+    /**

+     * <p>

+     * Indicates whether all the specified values are attribute's values. If

+     * at least one value is not an attribute's value, this method will return 

+     * <code>false</code>

+     * </p>

+     * <p>

+     * If the Attribute is HR, this method will returns <code>false</code>

+     * </p>

+     *

+     * @param vals the values

+     * @return true if this attribute contains all the values, otherwise false

+     */

+    public boolean contains( byte[]... vals )

+    {

+        if ( !isHR )

+        {

+            // Iterate through all the values, and quit if we 

+            // don't find one in the values

+            for ( byte[] val:vals )

+            {

+                ServerBinaryValue value = new ServerBinaryValue( attributeType, val );

+                

+                if ( !values.contains( value ) )

+                {

+                    return false;

+                }

+            }

+            

+            return true;

+        }

+        else

+        {

+            return false;

+        }

+    }

+    

+    

+    /**

+     * @see EntryAttribute#remove(org.apache.directory.shared.ldap.entry.Value...)

+     * 

+     * @return <code>true</code> if all the values shave been removed from this attribute

+     */

+    public boolean remove( Value<?>... vals )

+    {

+        boolean removed = true;

+        

+        // Loop through all the values to remove. If one of

+        // them is not present, the method will return false.

+        // As the attribute may be HR or not, we have two separated treatments

+        if ( isHR )

+        {

+            for ( Value<?> val:vals )

+            {

+                if ( val instanceof ClientStringValue )

+                {

+                    ServerStringValue ssv = new ServerStringValue( attributeType, (String)val.get() );

+                    removed &= values.remove( ssv );

+                }

+                else if ( val instanceof ServerStringValue )

+                {

+                    removed &= values.remove( val );

+                }

+                else

+                {

+                    removed = false;

+                }

+            }

+        }

+        else

+        {

+            for ( Value<?> val:vals )

+            {

+                if ( val instanceof ClientBinaryValue )

+                {

+                    ServerBinaryValue sbv = new ServerBinaryValue( attributeType, (byte[])val.get() );

+                    removed &= values.remove( sbv );

+                }

+                else if ( val instanceof ServerBinaryValue )

+                {

+                    removed &= values.remove( val );

+                }

+                else

+                {

+                    removed = false;

+                }

+            }

+        }

+        

+        return removed;

+    }

+

+

+    /**

+     * @see EntryAttribute#remove(byte[]...)

+     * 

+     * @return <code>true</code> if all the values shave been removed from this attribute

+     */

+    public boolean remove( byte[]... vals )

+    {

+        if ( isHR ) 

+        {

+            return false;

+        }

+        

+        boolean removed = true;

+        

+        for ( byte[] val:vals )

+        {

+            ServerBinaryValue value = new ServerBinaryValue( attributeType, val );

+            removed &= values.remove( value );

+        }

+        

+        return removed;

+    }

+

+

+    /**

+     * @see EntryAttribute#remove(String...)

+     * 

+     * @return <code>true</code> if all the values shave been removed from this attribute

+     */

+    public boolean remove( String... vals )

+    {

+        if ( !isHR )

+        {

+            return false;

+        }

+        

+        boolean removed = true;

+        

+        for ( String val:vals )

+        {

+            ServerStringValue value = new ServerStringValue( attributeType, val );

+            removed &= values.remove( value );

+        }

+        

+        return removed;

+    }

+

+

+    //-------------------------------------------------------------------------

+    // Overloaded Object classes

+    //-------------------------------------------------------------------------

+    /**

+     * The hashCode is based on the id, the isHR flag and 

+     * on the internal values.

+     *  

+     * @see Object#hashCode()

+     * 

+     * @return the instance's hash code 

+     */

+    public int hashCode()

+    {

+        int h = super.hashCode();

+        

+        if ( attributeType != null )

+        {

+            h = h*17 + attributeType.hashCode();

+        }

+        

+        return h;

+    }

+

+

+    /**

+     * @see Object#equals(Object)

+     * 

+     * @return <code>true</code> if the two objects are equal

+     */

+    public boolean equals( Object obj )

+    {

+        if ( obj == this )

+        {

+            return true;

+        }

+        

+        if ( ! (obj instanceof ServerAttribute ) )

+        {

+            return false;

+        }

+        

+        ServerAttribute other = (ServerAttribute)obj;

+        

+        if ( !attributeType.equals( other.getAttributeType() ) )

+        {

+            return false;

+        }

+        

+        if ( values.size() != other.size() )

+        {

+            return false;

+        }

+        

+        for ( Value<?> val:values )

+        {

+            if ( ! other.contains( val ) )

+            {

+                return false;

+            }

+        }

+        

+        return true;

+    }

+    

+    

+    /**

+     * @see Object#toString()

+     * 

+     * @return A String representation of this instance

+     */

+    public String toString()

+    {

+        StringBuilder sb = new StringBuilder();

+        

+        if ( ( values != null ) && ( values.size() != 0 ) )

+        {

+            for ( Value<?> value:values )

+            {

+                sb.append( "    " ).append( upId ).append( ": " );

+                

+                if ( value.isNull() )

+                {

+                    sb.append( "''" );

+                }

+                else

+                {

+                    sb.append( value );

+                }

+                

+                sb.append( '\n' );

+            }

+        }

+        else

+        {

+            sb.append( "    " ).append( upId ).append( ": (null)\n" );

+        }

+        

+        return sb.toString();

+    }

+}

diff --git a/old_trunk/core-entry/src/main/java/org/apache/directory/server/core/entry/DefaultServerEntry.java b/old_trunk/core-entry/src/main/java/org/apache/directory/server/core/entry/DefaultServerEntry.java
new file mode 100644
index 0000000..58731f4
--- /dev/null
+++ b/old_trunk/core-entry/src/main/java/org/apache/directory/server/core/entry/DefaultServerEntry.java
@@ -0,0 +1,2395 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+package org.apache.directory.server.core.entry;
+
+
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.NotImplementedException;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.entry.AbstractEntry;
+import org.apache.directory.shared.ldap.entry.Entry;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.entry.Value;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.util.StringTools;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * A default implementation of a ServerEntry which should suite most
+ * use cases.
+ * 
+ * This class is final, it should not be extended.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public final class DefaultServerEntry extends AbstractEntry<AttributeType> implements ServerEntry, Externalizable
+{
+    /** Used for serialization */
+    private static final long serialVersionUID = 2L;
+    
+    /** The logger for this class */
+    private static final Logger LOG = LoggerFactory.getLogger( DefaultServerEntry.class );
+
+    /** The AttributeType registries */
+    private final transient AttributeTypeRegistry atRegistry;
+    
+    /** A speedup to get the ObjectClass attribute */
+    private static transient AttributeType OBJECT_CLASS_AT;
+    
+    /** A mutex to manage synchronization*/
+    private static transient Object MUTEX = new Object();
+
+
+    //-------------------------------------------------------------------------
+    // Helper methods
+    //-------------------------------------------------------------------------
+    /**
+     * Returns the attributeType from an Attribute ID.
+     */
+    private AttributeType getAttributeType( String upId ) throws NamingException
+    {
+        if ( StringTools.isEmpty( StringTools.trim( upId ) ) )
+        {
+            String message = "The ID should not be null or empty";
+            LOG.error( message );
+            throw new IllegalArgumentException( message );
+        }
+        
+        return atRegistry.lookup( upId );
+    }
+
+    
+    /**
+     * Get the UpId if it was null.
+     */
+    public static String getUpId( String upId, AttributeType attributeType )
+    {
+        String normUpId = StringTools.trim( upId );
+
+        if ( ( attributeType == null ) )
+        {
+            if ( StringTools.isEmpty( normUpId ) )
+            {
+                String message = "Cannot add an attribute without an ID";
+                LOG.error( message );
+                throw new IllegalArgumentException( message );
+            }
+        }
+        else if ( StringTools.isEmpty( normUpId ) )
+        {
+            upId = attributeType.getName();
+            
+            if ( StringTools.isEmpty( upId ) )
+            {
+                upId = attributeType.getOid();
+            }
+        }
+        
+        return upId;
+    }
+
+    
+    /**
+     * This method is used to initialize the OBJECT_CLASS_AT attributeType.
+     * 
+     * We want to do it only once, so it's a synchronized method. Note that
+     * the alternative would be to call the lookup() every time, but this won't
+     * be very efficient, as it will get the AT from a map, which is also
+     * synchronized, so here, we have a very minimal cost.
+     * 
+     * We can't do it once as a static part in the body of this class, because
+     * the access to the registries is mandatory to get back the AttributeType.
+     */
+    private void initObjectClassAT( Registries registries )
+    {
+        try
+        {
+            if ( OBJECT_CLASS_AT == null )
+            {
+                synchronized ( MUTEX )
+                {
+                    OBJECT_CLASS_AT = atRegistry.lookup( SchemaConstants.OBJECT_CLASS_AT );
+                }
+            }
+        }
+        catch ( NamingException ne )
+        {
+            // do nothing...
+        }
+    }
+
+    
+    /**
+     * Add a new ServerAttribute, with its upId. If the upId is null,
+     * default to the AttributeType name.
+     * 
+     * Updates the serverAttributeMap.
+     */
+    private void createAttribute( String upId, AttributeType attributeType, byte[]... values ) 
+    {
+        ServerAttribute attribute = new DefaultServerAttribute( attributeType, values );
+        attribute.setUpId( upId, attributeType );
+        attributes.put( attributeType, attribute );
+    }
+    
+    
+    /**
+     * Add a new ServerAttribute, with its upId. If the upId is null,
+     * default to the AttributeType name.
+     * 
+     * Updates the serverAttributeMap.
+     */
+    private void createAttribute( String upId, AttributeType attributeType, String... values ) 
+    {
+        ServerAttribute attribute = new DefaultServerAttribute( attributeType, values );
+        attribute.setUpId( upId, attributeType );
+        attributes.put( attributeType, attribute );
+    }
+    
+    
+    /**
+     * Add a new ServerAttribute, with its upId. If the upId is null,
+     * default to the AttributeType name.
+     * 
+     * Updates the serverAttributeMap.
+     */
+    private void createAttribute( String upId, AttributeType attributeType, Value<?>... values ) 
+    {
+        ServerAttribute attribute = new DefaultServerAttribute( attributeType, values );
+        attribute.setUpId( upId, attributeType );
+        attributes.put( attributeType, attribute );
+    }
+    
+    
+    //-------------------------------------------------------------------------
+    // Constructors
+    //-------------------------------------------------------------------------
+    /**
+     * <p>
+     * Creates a new instance of DefaultServerEntry.
+     * </p> 
+     * <p>
+     * This entry <b>must</b> be initialized before being used !
+     * </p>
+     */
+    /* no protection ! */ DefaultServerEntry()
+    {
+        atRegistry = null;
+    }
+
+
+    /**
+     * <p>
+     * Creates a new instance of DefaultServerEntry, with registries. 
+     * </p>
+     * <p>
+     * No attributes will be created.
+     * </p> 
+     * 
+     * @param registries The reference to the global registries
+     */
+    public DefaultServerEntry( Registries registries )
+    {
+        this.atRegistry = registries.getAttributeTypeRegistry();
+
+        // Initialize the ObjectClass object
+        initObjectClassAT( registries );
+    }
+
+
+    /**
+     * <p>
+     * Creates a new instance of DefaultServerEntry, with a 
+     * DN and registries. 
+     * </p>
+     * <p>
+     * No attributes will be created.
+     * </p> 
+     * 
+     * @param registries The reference to the global registries
+     * @param dn The DN for this serverEntry. Can be null.
+     */
+    public DefaultServerEntry( Registries registries, LdapDN dn )
+    {
+        this.dn = dn;
+        this.atRegistry = registries.getAttributeTypeRegistry();
+
+        // Initialize the ObjectClass object
+        initObjectClassAT( registries );
+    }
+
+
+    /**
+     * <p>
+     * Creates a new instance of DefaultServerEntry, with a 
+     * DN, registries and a list of attributeTypes. 
+     * </p>
+     * <p>
+     * The newly created entry is fed with the list of attributeTypes. No
+     * values are associated with those attributeTypes.
+     * </p>
+     * <p>
+     * If any of the AttributeType does not exist, they it's simply discarded.
+     * </p>
+     * 
+     * @param registries The reference to the global registries
+     * @param dn The DN for this serverEntry. Can be null.
+     * @param attributeTypes The list of attributes to create, without value.
+     */
+    public DefaultServerEntry( Registries registries, LdapDN dn, AttributeType... attributeTypes )
+    {
+        this.dn = dn;
+        this.atRegistry = registries.getAttributeTypeRegistry();
+
+        // Initialize the ObjectClass object
+        initObjectClassAT( registries );
+
+        // Add the attributeTypes
+        set( attributeTypes );
+    }
+
+    
+    /**
+     * <p>
+     * Creates a new instance of DefaultServerEntry, with a 
+     * DN, registries and an attributeType with the user provided ID. 
+     * </p>
+     * <p>
+     * The newly created entry is fed with the given attributeType. No
+     * values are associated with this attributeType.
+     * </p>
+     * <p>
+     * If the AttributeType does not exist, they it's simply discarded.
+     * </p>
+     * <p>
+     * We also check that the normalized upID equals the AttributeType ID
+     * </p>
+     * 
+     * @param registries The reference to the global registries
+     * @param dn The DN for this serverEntry. Can be null.
+     * @param attributeType The attribute to create, without value.
+     * @param upId The User Provided ID fro this AttributeType
+     */
+    public DefaultServerEntry( Registries registries, LdapDN dn, AttributeType attributeType, String upId )
+    {
+        this.dn = dn;
+        this.atRegistry = registries.getAttributeTypeRegistry();
+        // Initialize the ObjectClass object
+
+        // Initialize the ObjectClass object
+        initObjectClassAT( registries );
+
+        try
+        {
+            put( upId, attributeType, (String)null );
+        }
+        catch ( NamingException ne )
+        {
+            // Just discard the AttributeType
+            LOG.error( "We have had an error while adding the '{}' AttributeType : {}", upId, ne.getMessage() );
+        }
+    }
+    
+    
+    /**
+     * Creates a new instance of DefaultServerEntry, with a 
+     * DN, registries and a list of IDs. 
+     * <p>
+     * No attributes will be created except the ObjectClass attribute,
+     * which will contains "top". 
+     * <p>
+     * If any of the AttributeType does not exist, they are simply discarded.
+     * 
+     * @param registries The reference to the global registries
+     * @param dn The DN for this serverEntry. Can be null.
+     * @param upIds The list of attributes to create.
+     */
+    public DefaultServerEntry( Registries registries, LdapDN dn, String... upIds )
+    {
+        this.dn = dn;
+        this.atRegistry = registries.getAttributeTypeRegistry();
+
+        initObjectClassAT( registries );
+
+        set( upIds );
+    }
+
+    
+    /**
+     * Creates a new instance of DefaultServerEntry, with a 
+     * DN, registries and a list of ServerAttributes. 
+     * <p>
+     * No attributes will be created except the ObjectClass attribute,
+     * which will contains "top". 
+     * <p>
+     * If any of the AttributeType does not exist, they are simply discarded.
+     * 
+     * @param registries The reference to the global registries
+     * @param dn The DN for this serverEntry. Can be null
+     * @param attributes The list of attributes to create
+     */
+    public DefaultServerEntry( Registries registries, LdapDN dn, ServerAttribute... attributes )
+    {
+        this.dn = dn;
+        this.atRegistry = registries.getAttributeTypeRegistry();
+
+        initObjectClassAT( registries );
+
+        for ( ServerAttribute attribute:attributes )
+        {
+            // Store a new ServerAttribute
+            try
+            {
+                put( attribute );
+            }
+            catch ( NamingException ne )
+            {
+                LOG.warn( "The ServerAttribute '{}' does not exist. It has been discarded", attribute );
+            }
+        }
+    }
+
+    
+    //-------------------------------------------------------------------------
+    // API
+    //-------------------------------------------------------------------------
+    /**
+     * <p>
+     * Add an attribute (represented by its AttributeType and some binary values) into an 
+     * entry.
+     * </p>
+     * <p> 
+     * If we already have an attribute with the same values, nothing is done 
+     * (duplicated values are not allowed)
+     * </p>
+     * <p>
+     * If the value cannot be added, or if the AttributeType is null or invalid, 
+     * a NamingException is thrown.
+     * </p>
+     *
+     * @param attributeType The attribute Type.
+     * @param values The list of binary values to inject. It can be empty.
+     * @throws NamingException If the attribute does not exist
+     */
+    public void add( AttributeType attributeType, byte[]... values ) throws NamingException
+    {
+        if ( attributeType == null )
+        {
+            String message = "The attributeType should not be null";
+            LOG.error( message );
+            throw new IllegalArgumentException( message );
+        }
+        
+        // ObjectClass with binary values are not allowed
+        if ( attributeType.equals( OBJECT_CLASS_AT ) )
+        {
+            String message = "Only String values supported for objectClass attribute";
+            LOG.error(  message  );
+            throw new UnsupportedOperationException( message );
+        }
+
+        EntryAttribute attribute = attributes.get( attributeType );
+        
+        if ( attribute != null )
+        {
+            // This Attribute already exist, we add the values 
+            // into it
+            attribute.add( values );
+        }
+        else
+        {
+            // We have to create a new Attribute and set the values.
+            // The upId, which is set to null, will be setup by the 
+            // createAttribute method
+            createAttribute( null, attributeType, values );
+        }
+    }
+
+
+    /**
+     * <p>
+     * Add an attribute (represented by its AttributeType and some String values) into an 
+     * entry.
+     * </p>
+     * <p> 
+     * If we already have an attribute with public the same value, nothing is done 
+     * (duplicated values are not allowed)
+     * </p>
+     * <p>public 
+     * If the value cannot be added, or if the AttributeType is null or invalid, 
+     * a NamingException is thrown.
+     * </p>
+     *public 
+     * @param attributeType The attribute Type
+     * @param values The list of binary values to inject. It can be empty
+     * @throws NamingException If the attribute does not exist
+     */
+    public void add( AttributeType attributeType, String... values ) throws NamingException
+    {    
+        if ( attributeType == null )
+        {
+            String message = "The attributeType should not be null";
+            LOG.error( message );
+            throw new IllegalArgumentException( message );
+        }
+        
+        EntryAttribute attribute = attributes.get( attributeType );
+        
+        if ( attribute != null )
+        {
+            // This Attribute already exist, we add the values 
+            // into it
+            attribute.add( values );
+        }
+        else
+        {
+            // We have to create a new Attribute and set the values.
+            // The upId, which is set to null, will be setup by the 
+            // createAttribute method
+            createAttribute( null, attributeType, values );
+        }
+    }
+
+    
+    /**
+     * <p>
+     * Add an attribute (represented by its AttributeType and some values) into an 
+     * entry.
+     * </p>
+     * <p> 
+     * If we already have an attribute with the same value, nothing is done.
+     * (duplicated values are not allowed)
+     * </p>
+     * <p>
+     * If the value cannot be added, or if the AttributeType is null or invalid, 
+     * a NamingException is thrown.
+     * </p>
+     *
+     * @param attributeType The attribute Type
+     * @param values The list of binary values to inject. It can be empty
+     * @throws NamingException If the attribute does not exist
+     */
+    public void add( AttributeType attributeType, Value<?>... values ) throws NamingException
+    {
+        if ( attributeType == null )
+        {
+            String message = "The attributeType should not be null";
+            LOG.error( message );
+            throw new IllegalArgumentException( message );
+        }
+        
+        EntryAttribute attribute = attributes.get( attributeType );
+    
+        if ( attribute != null )
+        {
+            // This Attribute already exist, we add the values 
+            // into it
+            attribute.add( values );
+        }
+        else
+        {
+            // We have to create a new Attribute and set the values.
+            // The upId, which is set to null, will be setup by the 
+            // createAttribute method
+            createAttribute( null, attributeType, values );
+        }
+    }
+
+
+    /**
+     * Add some EntryAttributes to the current Entry.
+     *
+     * @param attributes The attributes to add
+     * @throws NamingException If we can't add any of the attributes
+     */
+    public void add( EntryAttribute... attributes ) throws NamingException
+    {
+        for ( EntryAttribute attribute:attributes )
+        {
+            ServerAttribute serverAttribute = (ServerAttribute)attribute;
+            AttributeType attributeType = serverAttribute.getAttributeType();
+            
+            if ( this.attributes.containsKey( attributeType ) )
+            {
+                // We already have an attribute with the same AttributeType
+                // Just add the new values into it.
+                EntryAttribute oldAttribute = this.attributes.get( attributeType );
+                
+                for ( Value<?> value:serverAttribute )
+                {
+                    oldAttribute.add( value );
+                }
+                
+                // And update the upId
+                oldAttribute.setUpId( serverAttribute.getUpId() );
+            }
+            else
+            {
+                // The attributeType does not exist, add it
+                this.attributes.put( attributeType, attribute );
+            }
+        }
+    }
+
+
+    /**
+     * <p>
+     * Add an attribute (represented by its AttributeType and some binary values) into an 
+     * entry. Set the User Provider ID at the same time
+     * </p>
+     * <p> 
+     * If we already have an attribute with the same values, nothing is done 
+     * (duplicated values are not allowed)
+     * </p>
+     * <p>
+     * If the value cannot be added, or if the AttributeType is null or invalid, 
+     * a NamingException is thrown.
+     * </p>
+     *
+     * @param upId The user provided ID for the added AttributeType
+     * @param attributeType The attribute Type.
+     * @param values The list of binary values to add. It can be empty.
+     * @throws NamingException If the attribute does not exist
+     */
+    public void add( String upId, AttributeType attributeType, byte[]... values ) throws NamingException
+    {
+        // ObjectClass with binary values are not allowed
+        if ( attributeType.equals( OBJECT_CLASS_AT ) )
+        {
+            String message = "Only String values supported for objectClass attribute";
+            LOG.error(  message  );
+            throw new UnsupportedOperationException( message );
+        }
+
+        ServerAttribute attribute = (ServerAttribute)attributes.get( attributeType );
+        
+        upId = getUpId( upId, attributeType );
+        
+        if ( attribute != null )
+        {
+            // This Attribute already exist, we add the values 
+            // into it
+            attribute.add( values );
+            attribute.setUpId( upId, attributeType );
+        }
+        else
+        {
+            // We have to create a new Attribute and set the values
+            // and the upId
+            createAttribute( upId, attributeType, values );
+        }
+    }
+
+
+    /**
+     * <p>
+     * Add an attribute (represented by its AttributeType and some values) into an 
+     * entry. Set the User Provider ID at the same time
+     * </p>
+     * <p> 
+     * If we already have an attribute with the same values, nothing is done 
+     * (duplicated values are not allowed)
+     * </p>
+     * <p>
+     * If the value cannot be added, or if the AttributeType is null or invalid, 
+     * a NamingException is thrown.
+     * </p>
+     *
+     * @param upId The user provided ID for the added AttributeType
+     * @param attributeType The attribute Type.
+     * @param values The list of values to add. It can be empty.
+     * @throws NamingException If the attribute does not exist
+     */
+    public void add( String upId, AttributeType attributeType, Value<?>... values ) throws NamingException
+    {
+        if ( attributeType == null )
+        {
+            String message = "The attributeType should not be null";
+            LOG.error( message );
+            throw new IllegalArgumentException( message );
+        }
+        
+        upId = getUpId( upId, attributeType );
+        
+        ServerAttribute attribute = (ServerAttribute)attributes.get( attributeType );
+    
+        if ( attribute != null )
+        {
+            // This Attribute already exist, we add the values 
+            // into it
+            attribute.add( values );
+            attribute.setUpId( upId, attributeType );
+        }
+        else
+        {
+            createAttribute( upId, attributeType, values );
+        }
+    }
+
+    
+    /**
+     * Adds a new attribute with some String values into an entry, setting
+     * the User Provided ID in the same time.
+     *
+     * @param upId The User provided ID
+     * @param attributeType The associated AttributeType
+     * @param values The String values to store into the new Attribute
+     * @throws NamingException 
+     */
+    public void add( String upId, AttributeType attributeType, String... values ) throws NamingException
+    {
+        if ( attributeType == null )
+        {
+            String message = "The attributeType should not be null";
+            LOG.error( message );
+            throw new IllegalArgumentException( message );
+        }
+        
+        upId = getUpId( upId, attributeType );
+
+        ServerAttribute attribute = (ServerAttribute)attributes.get( attributeType );
+        
+        if ( attribute != null )
+        {
+            // This Attribute already exist, we add the values 
+            // into it
+            attribute.add( values );
+            attribute.setUpId( upId, attributeType );
+        }
+        else
+        {
+            // We have to create a new Attribute and set the values
+            // and the upId
+            createAttribute( upId, attributeType, values );
+        }
+    }
+
+
+    /**                 
+     * Add an attribute (represented by its ID and binary values) into an entry. 
+     *
+     * @param upId The attribute ID
+     * @param values The list of binary values to inject. It can be empty
+     * @throws NamingException If the attribute does not exist
+     */
+    public void add( String upId, byte[]... values ) throws NamingException
+    {
+        add( upId, getAttributeType( upId ), values );
+    }
+
+
+    /**
+     * Add an attribute (represented by its ID and string values) into an entry. 
+     *
+     * @param upId The attribute ID
+     * @param values The list of string values to inject. It can be empty
+     * @throws NamingException If the attribute does not exist
+     */
+    public void add( String upId, String... values ) throws NamingException
+    {
+        add( upId, getAttributeType( upId ), values );
+    }
+
+
+    /**
+     * Add an attribute (represented by its ID and Value values) into an entry. 
+     *
+     * @param upId The attribute ID
+     * @param values The list of Value values to inject. It can be empty
+     * @throws NamingException If the attribute does not exist
+     */
+    public void add( String upId, Value<?>... values ) throws NamingException
+    {
+        add( upId, getAttributeType( upId ), values );
+    }
+
+
+    /**
+     * Checks if an entry contains an attribute with some given binary values.
+     *
+     * @param attributeType The Attribute we are looking for.
+     * @param values The searched values.
+     * @return <code>true</code> if all the values are found within the attribute,
+     * <code>false</code> otherwise, or if the attributes does not exist.
+     */
+    public boolean contains( AttributeType attributeType, byte[]... values )
+    {
+        if ( attributeType == null )
+        {
+            return false;
+        }
+        
+        EntryAttribute attribute = attributes.get( attributeType );
+        
+        if ( attribute != null )
+        {
+            return attribute.contains( values );
+        }
+        else
+        {
+            return false;
+        }
+    }
+    
+    
+    /**
+     * Checks if an entry contains an attribute with some given String values.
+     *
+     * @param attributeType The Attribute we are looking for.
+     * @param values The searched values.
+     * @return <code>true</code> if all the values are found within the attribute,
+     * <code>false</code> otherwise, or if the attributes does not exist.
+     */
+    public boolean contains( AttributeType attributeType, String... values )
+    {
+        if ( attributeType == null )
+        {
+            return false;
+        }
+
+        EntryAttribute attribute = attributes.get( attributeType );
+        
+        if ( attribute != null )
+        {
+            return attribute.contains( values );
+        }
+        else
+        {
+            return false;
+        }
+    }
+    
+    
+    /**
+     * Checks if an entry contains an attribute with some given binary values.
+     *
+     * @param attributeType The Attribute we are looking for.
+     * @param values The searched values.
+     * @return <code>true</code> if all the values are found within the attribute,
+     * <code>false</code> otherwise, or if the attributes does not exist.
+     */
+    public boolean contains( AttributeType attributeType, Value<?>... values )
+    {
+        if ( attributeType == null )
+        {
+            return false;
+        }
+        
+        EntryAttribute attribute = attributes.get( attributeType );
+        
+        if ( attribute != null )
+        {
+            return attribute.contains( values );
+        }
+        else
+        {
+            return false;
+        }
+    }
+    
+    
+    /**
+     * <p>
+     * Checks if an entry contains a list of attributes.
+     * </p>
+     * <p>
+     * If the list is null or empty, this method will return <code>true</code>
+     * if the entry has no attribute, <code>false</code> otherwise.
+     * </p>
+     *
+     * @param attributes The Attributes to look for
+     * @return <code>true</code> if all the attributes are found within 
+     * the entry, <code>false</code> if at least one of them is not present.
+     * @throws NamingException If the attribute does not exist
+     */
+    public boolean contains( EntryAttribute... attributes ) throws NamingException
+    {
+        for ( EntryAttribute entryAttribute:attributes )
+        {
+            if ( entryAttribute == null )
+            {
+                return this.attributes.size() == 0;
+            }
+            
+            if ( !this.attributes.containsKey( ((ServerAttribute)entryAttribute).getAttributeType() ) )
+            {
+                return false;
+            }
+        }
+        
+        return true;
+    }
+
+
+    /**
+     * Checks if an entry contains an attribute with some binary values.
+     *
+     * @param id The Attribute we are looking for.
+     * @param values The searched values.
+     * @return <code>true</code> if all the values are found within the attribute,
+     * false if at least one value is not present or if the ID is not valid. 
+     */
+    public boolean contains( String id, byte[]... values )
+    {
+        if ( id == null )
+        {
+            return false;
+        }
+        
+        try
+        {
+            AttributeType attributeType = atRegistry.lookup( id );
+            
+            if ( attributeType == null )
+            {
+                return false;
+            }
+            
+            EntryAttribute attribute = attributes.get( attributeType );
+            
+            if ( attribute != null )
+            {
+                return attribute.contains( values );
+            }
+            else
+            {
+                return false;
+            }
+        }
+        catch ( NamingException ne )
+        {
+            return false;
+        }
+    }
+    
+    
+    /**
+     * Checks if an entry contains an attribute with some String values.
+     *
+     * @param id The Attribute we are looking for.
+     * @param values The searched values.
+     * @return <code>true</code> if all the values are found within the attribute,
+     * false if at least one value is not present or if the ID is not valid. 
+     */
+    public boolean contains( String id, String... values )
+    {
+        if ( id == null )
+        {
+            return false;
+        }
+        
+        try
+        {
+            AttributeType attributeType = atRegistry.lookup( id );
+            
+            if ( attributeType == null )
+            {
+                return false;
+            }
+            
+            EntryAttribute attribute = attributes.get( attributeType );
+            
+            if ( attribute != null )
+            {
+                return attribute.contains( values );
+            }
+            else
+            {
+                return false;
+            }
+        }
+        catch ( NamingException ne )
+        {
+            return false;
+        }
+    }
+    
+    
+    /**
+     * Checks if an entry contains an attribute with some values.
+     *
+     * @param id The Attribute we are looking for.
+     * @param values The searched values.
+     * @return <code>true</code> if all the values are found within the attribute,
+     * false if at least one value is not present or if the ID is not valid. 
+     */
+    public boolean contains( String id, Value<?>... values )
+    {
+        if ( id == null )
+        {
+            return false;
+        }
+        
+        try
+        {
+            AttributeType attributeType = atRegistry.lookup( id );
+            
+            if ( attributeType == null )
+            {
+                return false;
+            }
+
+            EntryAttribute attribute = attributes.get( attributeType );
+            
+            if ( attribute != null )
+            {
+                return attribute.contains( values );
+            }
+            else
+            {
+                return false;
+            }
+        }
+        catch ( NamingException ne )
+        {
+            return false;
+        }
+    }
+    
+    
+    /**
+     * Checks if an entry contains a specific AttributeType.
+     *
+     * @param attributeType The AttributeType to look for.
+     * @return <code>true</code> if the attribute is found within the entry.
+     */
+    public boolean containsAttribute( AttributeType attributeType )
+    {
+        return attributes.containsKey( attributeType );
+    }
+
+    
+    /**
+     * Checks if an entry contains some specific attributes.
+     *
+     * @param attributes The Attributes to look for.
+     * @return <code>true</code> if the attributes are all found within the entry.
+     */
+    public boolean containsAttribute( String... attributes )
+    {
+        for ( String attribute:attributes )
+        {
+            try
+            {
+                if ( !this.attributes.containsKey( getAttributeType( attribute ) ) )
+                {
+                    return false;
+                }
+            }
+            catch ( NamingException ne )
+            {
+                return false;
+            }
+        }
+        
+        return true;
+    }
+
+    
+    /**
+     * <p>
+     * Returns the attribute with the specified AttributeType. The return value
+     * is <code>null</code> if no match is found.  
+     * </p>
+     *
+     * @param attributeType The attributeType we are looking for.
+     * @return the attribute associated with the AttributeType.
+     */
+    /**
+     * Returns the attribute associated with an AttributeType
+     * 
+     * @param attributeType the AttributeType we are looking for
+     * @return the associated attribute
+     */
+    public EntryAttribute get( AttributeType attributeType )
+    {
+        return attributes.get( attributeType );
+    }
+
+
+    /**
+     * <p>
+     * Returns the attribute with the specified alias. The return value
+     * is <code>null</code> if no match is found.  
+     * </p>
+     * <p>An Attribute with an id different from the supplied alias may 
+     * be returned: for example a call with 'cn' may in some implementations 
+     * return an Attribute whose getId() field returns 'commonName'.
+     * </p>
+     * <p>
+     * If the attributeType is not found, returns null.
+     * </p>
+     *
+     * @param alias an aliased name of the attribute identifier
+     * @return the attribute associated with the alias
+     */
+    public EntryAttribute get( String alias )
+    {
+        try
+        {
+            return get( atRegistry.lookup( alias ) );
+        }
+        catch ( NamingException ne )
+        {
+            String message = ne.getMessage();
+            LOG.error( message );
+            return null;
+        }
+    }
+
+
+    /**
+     * Gets all the attributes type
+     *
+     * @return The combined set of all the attributes, including ObjectClass.
+     */
+    public Set<AttributeType> getAttributeTypes()
+    {
+        return attributes.keySet();
+    }
+    
+    
+    /**
+     * Tells if an entry has a specific ObjectClass value
+     * 
+     * @param objectClass The ObjectClass ID we want to check
+     * @return <code>true</code> if the ObjectClass value is present 
+     * in the ObjectClass attribute
+     */
+    public boolean hasObjectClass( String objectClass )
+    {
+        return contains( OBJECT_CLASS_AT, objectClass );
+    }
+
+    
+    /**
+     * Tells if an entry has a specific ObjectClass Attribute
+     * 
+     * @param objectClass The ObjectClass we want to check
+     * @return <code>true</code> if the ObjectClass value is present 
+     * in the ObjectClass attribute
+     */
+    public boolean hasObjectClass( EntryAttribute objectClass )
+    {
+        if ( objectClass == null )
+        {
+            return false;
+        }
+        
+        // We have to check that we are checking the ObjectClass attributeType
+        if ( ((ServerAttribute)objectClass).getAttributeType() != OBJECT_CLASS_AT )
+        {
+            return false;
+        }
+        
+        EntryAttribute attribute = attributes.get( OBJECT_CLASS_AT );
+        
+        if ( attribute == null )
+        {
+            // The entry does not have an ObjectClass attribute
+            return false;
+        }
+        
+        for ( Value<?> value:objectClass )
+        {
+            // Loop on all the values, and check if they are present
+            if ( !attribute.contains( value ) )
+            {
+                return false;
+            }
+        }
+        
+        return true;
+    }
+
+    
+    /**
+     * Fail fast check performed to determine entry consistency according to schema
+     * characteristics.
+     *
+     * @return true if the entry, it's attributes and their values are consistent
+     * with the schema
+     */
+    public boolean isValid()
+    {
+        // @TODO Implement me !
+        throw new NotImplementedException();
+    }
+
+
+    /**
+     * Check performed to determine entry consistency according to the schema
+     * requirements of a particular objectClass.  The entry must be of that objectClass
+     * to return true: meaning if the entry's objectClass attribute does not contain
+     * the objectClass argument, then false should be returned.
+     *
+     * @param objectClass the objectClass to use while checking for validity
+     * @return true if the entry, it's attributes and their values are consistent
+     * with the objectClass
+     */
+    public boolean isValid( EntryAttribute objectClass )
+    {
+        // @TODO Implement me !
+        throw new NotImplementedException();
+    }
+
+
+    /**
+     * Check performed to determine entry consistency according to the schema
+     * requirements of a particular objectClass.  The entry must be of that objectClass
+     * to return true: meaning if the entry's objectClass attribute does not contain
+     * the objectClass argument, then false should be returned.
+     *
+     * @param objectClass the objectClass to use while checking for validity
+     * @return true if the entry, it's attributes and their values are consistent
+     * with the objectClass
+     */
+    public boolean isValid( String objectClass )
+    {
+        // @TODO Implement me !
+        throw new NotImplementedException();
+    }
+
+
+    /**
+     * <p>
+     * Places a new attribute with the supplied AttributeType and binary values 
+     * into the attribute collection. 
+     * </p>
+     * <p>
+     * If there is already an attribute with the same AttributeType, the old
+     * one is removed from the collection and is returned by this method. 
+     * </p>
+     * <p>
+     * This method provides a mechanism to put an attribute with a
+     * <code>null</code> value: the value may be <code>null</code>.
+     *
+     * @param attributeType the type of the new attribute to be put
+     * @param values the binary values of the new attribute to be put
+     * @return the old attribute with the same identifier, if exists; otherwise
+     * <code>null</code>
+     * @throws NamingException if there are failures
+     */
+    public EntryAttribute put( AttributeType attributeType, byte[]... values ) throws NamingException
+    {
+        return put( null, attributeType, values );
+    }
+
+
+    /**
+     * <p>
+     * Places a new attribute with the supplied AttributeType and String values 
+     * into the attribute collection. 
+     * </p>
+     * <p>
+     * If there is already an attribute with the same AttributeType, the old
+     * one is removed from the collection and is returned by this method. 
+     * </p>
+     * <p>
+     * This method provides a mechanism to put an attribute with a
+     * <code>null</code> value: the value may be <code>null</code>.
+     *
+     * @param attributeType the type of the new attribute to be put
+     * @param values the String values of the new attribute to be put
+     * @return the old attribute with the same identifier, if exists; otherwise
+     * <code>null</code>
+     * @throws NamingException if there are failures
+     */
+    public EntryAttribute put( AttributeType attributeType, String... values ) throws NamingException
+    {
+        return put( null, attributeType, values );
+    }
+
+    
+    /**
+     * <p>
+     * Places a new attribute with the supplied AttributeType and some values 
+     * into the attribute collection. 
+     * </p>
+     * <p>
+     * If there is already an attribute with the same AttributeType, the old
+     * one is removed from the collection and is returned by this method. 
+     * </p>
+     * <p>
+     * This method provides a mechanism to put an attribute with a
+     * <code>null</code> value: the value may be <code>null</code>.
+     *
+     * @param attributeType the type of the new attribute to be put
+     * @param values the values of the new attribute to be put
+     * @return the old attribute with the same identifier, if exists; otherwise
+     * <code>null</code>
+     * @throws NamingException if there are failures
+     */
+    public EntryAttribute put( AttributeType attributeType, Value<?>... values ) throws NamingException
+    {
+        return put( null, attributeType, values );
+    }
+
+
+    /**
+     * <p>
+     * Places attributes in the attribute collection. 
+     * </p>
+     * <p>If there is already an attribute with the same ID as any of the 
+     * new attributes, the old ones are removed from the collection and 
+     * are returned by this method. If there was no attribute with the 
+     * same ID the return value is <code>null</code>.
+     *</p>
+     *
+     * @param attributes the attributes to be put
+     * @return the old attributes with the same OID, if exist; otherwise
+     *         <code>null</code>
+     * @exception NamingException if the operation fails
+     */
+    public List<EntryAttribute> put( EntryAttribute... attributes ) throws NamingException
+    {
+        List<EntryAttribute> previous = new ArrayList<EntryAttribute>();
+        
+        for ( EntryAttribute serverAttribute:attributes )
+        {
+            if ( serverAttribute == null )
+            {
+                String message = "The ServerAttribute list should not contain null elements";
+                LOG.error( message );
+                throw new IllegalArgumentException( message );
+            }
+            
+            EntryAttribute removed = this.attributes.put( ((ServerAttribute)serverAttribute).getAttributeType(), serverAttribute );
+            
+            if ( removed != null )
+            {
+                previous.add( removed );
+            }
+        }
+        
+        return previous;
+    }
+
+
+    /**
+     * <p>
+     * Places a new attribute with the supplied AttributeType and some binary values 
+     * into the attribute collection. 
+     * </p>
+     * <p>
+     * The given User provided ID will be used for this new AttributeEntry.
+     * </p>
+     * <p>
+     * If there is already an attribute with the same AttributeType, the old
+     * one is removed from the collection and is returned by this method. 
+     * </p>
+     * <p>
+     * This method provides a mechanism to put an attribute with a
+     * <code>null</code> value: the value may be <code>null</code>.
+     *
+     * @param upId The User Provided ID to be stored into the AttributeEntry
+     * @param attributeType the type of the new attribute to be put
+     * @param values the binary values of the new attribute to be put
+     * @return the old attribute with the same identifier, if exists; otherwise
+     * <code>null</code>
+     * @throws NamingException if there are failures.
+     */
+    public EntryAttribute put( String upId, AttributeType attributeType, byte[]... values ) throws NamingException
+    {
+        if ( attributeType == null )
+        {
+            String message = "The attributeType should not be null";
+            LOG.error( message );
+            throw new IllegalArgumentException( message );
+        }
+
+        if ( !StringTools.isEmpty( upId ) )
+        {
+            AttributeType tempAT = getAttributeType( upId );
+        
+            if ( !tempAT.equals( attributeType ) )
+            {
+                String message = "The '" + upId + "' id is not compatible with the '" + attributeType + "' attribute type";
+                LOG.error( message );
+                throw new IllegalArgumentException( message );
+            }
+        }
+        else
+        {
+            upId = getUpId( upId, attributeType );
+        }
+        
+        if ( attributeType.equals( OBJECT_CLASS_AT ) )
+        {
+            String message = "Only String values supported for objectClass attribute";
+            LOG.error( message );
+            throw new UnsupportedOperationException( message );
+        }
+
+        EntryAttribute attribute = new DefaultServerAttribute( upId, attributeType, values );
+        return attributes.put( attributeType, attribute );
+    }
+
+
+    /**
+     * <p>
+     * Places a new attribute with the supplied AttributeType and some String values 
+     * into the attribute collection. 
+     * </p>
+     * <p>
+     * The given User provided ID will be used for this new AttributeEntry.
+     * </p>
+     * <p>
+     * If there is already an attribute with the same AttributeType, the old
+     * one is removed from the collection and is returned by this method. 
+     * </p>
+     * <p>
+     * This method provides a mechanism to put an attribute with a
+     * <code>null</code> value: the value may be <code>null</code>.
+     *
+     * @param upId The User Provided ID to be stored into the AttributeEntry
+     * @param attributeType the type of the new attribute to be put
+     * @param values the String values of the new attribute to be put
+     * @return the old attribute with the same identifier, if exists; otherwise
+     * <code>null</code>
+     * @throws NamingException if there are failures.
+     */
+    public EntryAttribute put( String upId, AttributeType attributeType, String... values ) throws NamingException
+    {
+        if ( attributeType == null )
+        {
+            try
+            {
+                attributeType = getAttributeType( upId );
+            }
+            catch ( IllegalArgumentException iae )
+            {
+                String message = "The attributeType should not be null";
+                LOG.error( message );
+                throw new IllegalArgumentException( message );
+            }
+        }
+        else
+        {
+            if ( !StringTools.isEmpty( upId ) )
+            {
+                AttributeType tempAT = getAttributeType( upId );
+            
+                if ( !tempAT.equals( attributeType ) )
+                {
+                    String message = "The '" + upId + "' id is not compatible with the '" + attributeType + "' attribute type";
+                    LOG.error( message );
+                    throw new IllegalArgumentException( message );
+                }
+            }
+            else
+            {
+                upId = getUpId( upId, attributeType );
+            }
+        }
+        
+        EntryAttribute attribute = new DefaultServerAttribute( upId, attributeType, values );
+        return attributes.put( attributeType, attribute );
+    }
+
+
+    /**
+     * <p>
+     * Places a new attribute with the supplied AttributeType and some values 
+     * into the attribute collection. 
+     * </p>
+     * <p>
+     * The given User provided ID will be used for this new AttributeEntry.
+     * </p>
+     * <p>
+     * If there is already an attribute with the same AttributeType, the old
+     * one is removed from the collection and is returned by this method. 
+     * </p>
+     * <p>
+     * This method provides a mechanism to put an attribute with a
+     * <code>null</code> value: the value may be <code>null</code>.
+     *
+     * @param upId The User Provided ID to be stored into the AttributeEntry
+     * @param attributeType the type of the new attribute to be put
+     * @param values the values of the new attribute to be put
+     * @return the old attribute with the same identifier, if exists; otherwise
+     * <code>null</code>
+     * @throws NamingException if there are failures.
+     */
+    public EntryAttribute put( String upId, AttributeType attributeType, Value<?>... values ) throws NamingException
+    {
+        if ( attributeType == null )
+        {
+            String message = "The attributeType should not be null";
+            LOG.error( message );
+            throw new IllegalArgumentException( message );
+        }
+
+        if ( !StringTools.isEmpty( upId ) )
+        {
+            AttributeType tempAT = getAttributeType( upId );
+        
+            if ( !tempAT.equals( attributeType ) )
+            {
+                String message = "The '" + upId + "' id is not compatible with the '" + attributeType + "' attribute type";
+                LOG.error( message );
+                throw new IllegalArgumentException( message );
+            }
+        }
+        else
+        {
+            upId = getUpId( upId, attributeType );
+        }
+        
+        EntryAttribute attribute = new DefaultServerAttribute( upId, attributeType, values );
+        return attributes.put( attributeType, attribute );
+    }
+
+
+    /**
+     * <p>
+     * Put an attribute (represented by its ID and some binary values) into an entry. 
+     * </p>
+     * <p> 
+     * If the attribute already exists, the previous attribute will be 
+     * replaced and returned.
+     * </p>
+     * <p>
+     * If the upId is not the ID of an existing AttributeType, an IllegalArgumentException is thrown.
+     * </p>
+     *
+     * @param upId The attribute ID
+     * @param values The list of binary values to put. It can be empty.
+     * @return The replaced attribute
+     */
+    public EntryAttribute put( String upId, byte[]... values )
+    {
+        try
+        {
+            return put( upId, getAttributeType( upId ), values );
+        }
+        catch ( NamingException ne )
+        {
+            String message = "Error while adding values into the '" + upId + "' attribute. Error : " + 
+                ne.getMessage();
+            LOG.error( message );
+            throw new IllegalArgumentException( message );
+        }
+    }
+
+
+    /**
+     * <p>
+     * Put an attribute (represented by its ID and some String values) into an entry. 
+     * </p>
+     * <p> 
+     * If the attribute already exists, the previous attribute will be 
+     * replaced and returned.
+     * </p>
+     * <p>
+     * If the upId is not the ID of an existing AttributeType, an IllegalArgumentException is thrown.
+     * </p>
+     *
+     * @param upId The attribute ID
+     * @param values The list of String values to put. It can be empty.
+     * @return The replaced attribute
+     */
+    public EntryAttribute put( String upId, String... values )
+    {            
+
+        try
+        {
+            return put( upId, getAttributeType( upId ), values );
+        }
+        catch ( NamingException ne )
+        {
+            String message = "Error while adding values into the '" + upId + "' attribute. Error : " + 
+                ne.getMessage();
+            LOG.error( message );
+            throw new IllegalArgumentException( message );
+        }
+    }
+
+
+    /**
+     * <p>
+     * Put an attribute (represented by its ID and some values) into an entry. 
+     * </p>
+     * <p> 
+     * If the attribute already exists, the previous attribute will be 
+     * replaced and returned.
+     * </p>
+     * <p>
+     * If the upId is not the ID of an existing AttributeType, an IllegalArgumentException is thrown.
+     * </p>
+     *
+     * @param upId The attribute ID
+     * @param values The list of values to put. It can be empty.
+     * @return The replaced attribute
+     */
+    public EntryAttribute put( String upId, Value<?>... values )
+    {
+        try
+        {
+            return put( upId, getAttributeType( upId ), values );
+        }
+        catch ( NamingException ne )
+        {
+            String message = "Error while adding values into the '" + upId + "' attribute. Error : " + 
+                ne.getMessage();
+            LOG.error( message );
+            throw new IllegalArgumentException( message );
+        }
+    }
+
+
+    /**
+     * <p>
+     * Removes the specified binary values from an attribute.
+     * </p>
+     * <p>
+     * If at least one value is removed, this method returns <code>true</code>.
+     * </p>
+     * <p>
+     * If there is no more value after having removed the values, the attribute
+     * will be removed too.
+     * </p>
+     * <p>
+     * If the attribute does not exist, nothing is done and the method returns 
+     * <code>false</code>
+     * </p> 
+     *
+     * @param attributeType The attribute type  
+     * @param values the values to be removed
+     * @return <code>true</code> if at least a value is removed, <code>false</code>
+     * if not all the values have been removed or if the attribute does not exist. 
+     */
+    public boolean remove( AttributeType attributeType, byte[]... values ) throws NamingException
+    {
+        try
+        {
+            EntryAttribute attribute = attributes.get( attributeType );
+            
+            if ( attribute == null )
+            {
+                // Can't remove values from a not existing attribute !
+                return false;
+            }
+            
+            int nbOldValues = attribute.size();
+            
+            // Remove the values
+            attribute.remove( values );
+            
+            if ( attribute.size() == 0 )
+            {
+                // No mare values, remove the attribute
+                attributes.remove( attributeType );
+                
+                return true;
+            }
+            
+            if ( nbOldValues != attribute.size() )
+            {
+                // At least one value have been removed, return true.
+                return true;
+            }
+            else
+            {
+                // No values have been removed, return false.
+                return false;
+            }
+        }
+        catch ( IllegalArgumentException iae )
+        {
+            LOG.error( "The removal of values for the missing '{}' attribute is not possible", attributeType );
+            return false;
+        }
+    }
+    
+    
+    /**
+     * <p>
+     * Removes the specified String values from an attribute.
+     * </p>
+     * <p>
+     * If at least one value is removed, this method returns <code>true</code>.
+     * </p>
+     * <p>
+     * If there is no more value after having removed the values, the attribute
+     * will be removed too.
+     * </p>
+     * <p>
+     * If the attribute does not exist, nothing is done and the method returns 
+     * <code>false</code>
+     * </p> 
+     *
+     * @param attributeType The attribute type  
+     * @param values the values to be removed
+     * @return <code>true</code> if at least a value is removed, <code>false</code>
+     * if not all the values have been removed or if the attribute does not exist. 
+     */
+    public boolean remove( AttributeType attributeType, String... values ) throws NamingException
+    {
+        try
+        {
+            EntryAttribute attribute = attributes.get( attributeType );
+            
+            if ( attribute == null )
+            {
+                // Can't remove values from a not existing attribute !
+                return false;
+            }
+            
+            int nbOldValues = attribute.size();
+            
+            // Remove the values
+            attribute.remove( values );
+            
+            if ( attribute.size() == 0 )
+            {
+                // No mare values, remove the attribute
+                attributes.remove( attributeType );
+                
+                return true;
+            }
+            
+            if ( nbOldValues != attribute.size() )
+            {
+                // At least one value have been removed, return true.
+                return true;
+            }
+            else
+            {
+                // No values have been removed, return false.
+                return false;
+            }
+        }
+        catch ( IllegalArgumentException iae )
+        {
+            LOG.error( "The removal of values for the missing '{}' attribute is not possible", attributeType );
+            return false;
+        }
+    }
+    
+    
+    /**
+     * <p>
+     * Removes the specified values from an attribute.
+     * </p>
+     * <p>
+     * If at least one value is removed, this method returns <code>true</code>.
+     * </p>
+     * <p>
+     * If there is no more value after having removed the values, the attribute
+     * will be removed too.
+     * </p>
+     * <p>
+     * If the attribute does not exist, nothing is done and the method returns 
+     * <code>false</code>
+     * </p> 
+     *
+     * @param attributeType The attribute type  
+     * @param values the values to be removed
+     * @return <code>true</code> if at least a value is removed, <code>false</code>
+     * if not all the values have been removed or if the attribute does not exist. 
+     */
+    public boolean remove( AttributeType attributeType, Value<?>... values ) throws NamingException
+    {
+        try
+        {
+            EntryAttribute attribute = attributes.get( attributeType );
+            
+            if ( attribute == null )
+            {
+                // Can't remove values from a not existing attribute !
+                return false;
+            }
+            
+            int nbOldValues = attribute.size();
+            
+            // Remove the values
+            attribute.remove( values );
+            
+            if ( attribute.size() == 0 )
+            {
+                // No mare values, remove the attribute
+                attributes.remove( attributeType );
+                
+                return true;
+            }
+            
+            if ( nbOldValues != attribute.size() )
+            {
+                // At least one value have been removed, return true.
+                return true;
+            }
+            else
+            {
+                // No values have been removed, return false.
+                return false;
+            }
+        }
+        catch ( IllegalArgumentException iae )
+        {
+            LOG.error( "The removal of values for the missing '{}' attribute is not possible", attributeType );
+            return false;
+        }
+    }
+    
+    
+    public List<EntryAttribute> remove( EntryAttribute... attributes ) throws NamingException
+    {
+        List<EntryAttribute> removedAttributes = new ArrayList<EntryAttribute>();
+        
+        for ( EntryAttribute serverAttribute:attributes )
+        {
+            if ( this.attributes.containsKey( ((ServerAttribute)serverAttribute).getAttributeType() ) )
+            {
+                this.attributes.remove( ((ServerAttribute)serverAttribute).getAttributeType() );
+                removedAttributes.add( serverAttribute );
+            }
+        }
+        
+        return removedAttributes;
+    }
+
+
+    /**
+     * <p>
+     * Removes the specified binary values from an attribute.
+     * </p>
+     * <p>
+     * If at least one value is removed, this method returns <code>true</code>.
+     * </p>
+     * <p>
+     * If there is no more value after having removed the values, the attribute
+     * will be removed too.
+     * </p>
+     * <p>
+     * If the attribute does not exist, nothing is done and the method returns 
+     * <code>false</code>
+     * </p> 
+     *
+     * @param upId The attribute ID 
+     * @param values the values to be removed
+     * @return <code>true</code> if at least a value is removed, <code>false</code>
+     * if not all the values have been removed or if the attribute does not exist. 
+     */
+    public boolean remove( String upId, byte[]... values ) throws NamingException
+    {
+        try
+        {
+            AttributeType attributeType = getAttributeType( upId );
+
+            return remove( attributeType, values );
+        }
+        catch ( NamingException ne )
+        {
+            LOG.error( "The removal of values for the missing '{}' attribute is not possible", upId );
+            return false;
+        }
+        catch ( IllegalArgumentException iae )
+        {
+            LOG.error( "The removal of values for the bad '{}' attribute is not possible", upId );
+            return false;
+        }
+    }
+    
+    
+    /**
+     * <p>
+     * Removes the specified String values from an attribute.
+     * </p>
+     * <p>
+     * If at least one value is removed, this method returns <code>true</code>.
+     * </p>
+     * <p>
+     * If there is no more value after having removed the values, the attribute
+     * will be removed too.
+     * </p>
+     * <p>
+     * If the attribute does not exist, nothing is done and the method returns 
+     * <code>false</code>
+     * </p> 
+     *
+     * @param upId The attribute ID 
+     * @param values the values to be removed
+     * @return <code>true</code> if at least a value is removed, <code>false</code>
+     * if not all the values have been removed or if the attribute does not exist. 
+     */
+    public boolean remove( String upId, String... values ) throws NamingException
+    {
+        try
+        {
+            AttributeType attributeType = getAttributeType( upId );
+
+            return remove( attributeType, values );
+        }
+        catch ( NamingException ne )
+        {
+            LOG.error( "The removal of values for the missing '{}' attribute is not possible", upId );
+            return false;
+        }
+        catch ( IllegalArgumentException iae )
+        {
+            LOG.error( "The removal of values for the bad '{}' attribute is not possible", upId );
+            return false;
+        }
+    }
+    
+    
+    /**
+     * <p>
+     * Removes the specified Value values from an attribute.
+     * </p>
+     * <p>
+     * If at least one value is removed, this method returns <code>true</code>.
+     * </p>
+     * <p>
+     * If there is no more value after having removed the values, the attribute
+     * will be removed too.
+     * </p>
+     * <p>
+     * If the attribute does not exist, nothing is done and the method returns 
+     * <code>false</code>
+     * </p> 
+     *
+     * @param upId The attribute ID 
+     * @param values the values to be removed
+     * @return <code>true</code> if at least a value is removed, <code>false</code>
+     * if not all the values have been removed or if the attribute does not exist. 
+     */
+    public boolean remove( String upId, Value<?>... values ) throws NamingException
+    {
+        try
+        {
+            AttributeType attributeType = getAttributeType( upId );
+
+            return remove( attributeType, values );
+        }
+        catch ( NamingException ne )
+        {
+            LOG.error( "The removal of values for the missing '{}' attribute is not possible", upId );
+            return false;
+        }
+        catch ( IllegalArgumentException iae )
+        {
+            LOG.error( "The removal of values for the bad '{}' attribute is not possible", upId );
+            return false;
+        }
+    }
+    
+    
+    /**
+     * <p>
+     * Removes the attribute with the specified AttributeTypes. 
+     * </p>
+     * <p>
+     * The removed attribute are returned by this method. 
+     * </p>
+     * <p>
+     * If there is no attribute with the specified AttributeTypes,
+     * the return value is <code>null</code>.
+     * </p>
+     *
+     * @param attributes the AttributeTypes to be removed
+     * @return the removed attributes, if any, as a list; otherwise <code>null</code>
+     */
+    public List<EntryAttribute> removeAttributes( AttributeType... attributes )
+    {
+        if ( attributes.length == 0 )
+        {
+            return null;
+        }
+        
+        List<EntryAttribute> removed = new ArrayList<EntryAttribute>( attributes.length );
+        
+        for ( AttributeType attributeType:attributes )
+        {
+            EntryAttribute attr = this.attributes.remove( attributeType );
+            
+            if ( attr != null )
+            {
+                removed.add( attr );
+            }
+        }
+        
+        if ( removed.size() == 0 )
+        {
+            return null;
+        }
+        else
+        {
+            return removed;
+        }
+    }
+    
+    
+    /**
+     * <p>
+     * Removes the attribute with the specified alias. 
+     * </p>
+     * <p>
+     * The removed attribute are returned by this method. 
+     * </p>
+     * <p>
+     * If there is no attribute with the specified alias,
+     * the return value is <code>null</code>.
+     * </p>
+     *
+     * @param attributes an aliased name of the attribute to be removed
+     * @return the removed attributes, if any, as a list; otherwise <code>null</code>
+     */
+    public List<EntryAttribute> removeAttributes( String... attributes )
+    {
+        if ( attributes.length == 0 )
+        {
+            return null;
+        }
+        
+        List<EntryAttribute> removed = new ArrayList<EntryAttribute>( attributes.length );
+        
+        for ( String attribute:attributes )
+        {
+            AttributeType attributeType = null;
+            
+            try
+            {
+                attributeType = atRegistry.lookup( attribute );
+            }
+            catch ( NamingException ne )
+            {
+                String message = "The attribute '" + attribute + "' does not exist in the entry";
+                LOG.warn( message );
+                continue;
+            }
+    
+            EntryAttribute attr = this.attributes.remove( attributeType );
+            
+            if ( attr != null )
+            {
+                removed.add( attr );
+            }
+        }
+        
+        if ( removed.size() == 0 )
+        {
+            return null;
+        }
+        else
+        {
+            return removed;
+        }
+    }
+
+
+    /**
+     * <p>
+     * Put some new attributes using the attributeTypes. 
+     * No value is inserted. 
+     * </p>
+     * <p>
+     * If an existing Attribute is found, it will be replaced by an
+     * empty attribute, and returned to the caller.
+     * </p>
+     * 
+     * @param attributeTypes The AttributeTypes to add.
+     * @return A list of replaced Attributes, of <code>null</code> if no attribute are removed.
+     */
+    public List<EntryAttribute> set( AttributeType... attributeTypes )
+    {
+        List<EntryAttribute> removed = new ArrayList<EntryAttribute>();
+        
+        // Now, loop on all the attributeType to add
+        for ( AttributeType attributeType:attributeTypes )
+        {
+            if ( attributeType == null )
+            {
+                String message = "The AttributeType list should not contain null values";
+                LOG.error( message );
+                continue;
+            }
+            
+            EntryAttribute attribute = attributes.put( attributeType, new DefaultServerAttribute( attributeType ) );
+
+            if ( attribute != null )
+            {
+                removed.add( attribute );
+            }
+        }
+        
+        if ( removed.size() == 0 )
+        {
+            return null;
+        }
+        else
+        {
+            return removed;
+        }
+    }
+
+    
+    /**
+     * <p>
+     * Put some new EntryAttribute using the User Provided ID. 
+     * No value is inserted. 
+     * </p>
+     * <p>
+     * If an existing Attribute is found, it will be replaced by an
+     * empty attribute, and returned to the caller.
+     * </p>
+     * 
+     * @param upIds The user provided IDs of the AttributeTypes to add.
+     * @return A list of replaced Attributes.
+     */
+    public List<EntryAttribute> set( String... upIds )
+    {
+        List<EntryAttribute> removed = new ArrayList<EntryAttribute>();
+        
+        for ( String upId:upIds )
+        {
+            // Search for the corresponding AttributeType, based on the upID 
+            AttributeType attributeType = null;
+            
+            try
+            {
+                attributeType = getAttributeType( upId );
+            }
+            catch ( NamingException ne )
+            {
+                LOG.warn( "Trying to add a bad attribute type '{}', error : ", upId, ne.getMessage() );
+                continue;
+            }
+            catch ( IllegalArgumentException iae )
+            {
+                LOG.warn( "Trying to add a bad attribute type '{}', error : ", upId, iae.getMessage() );
+                continue;
+            }
+            
+            EntryAttribute attribute = attributes.put( attributeType, 
+                new DefaultServerAttribute( upId, attributeType ));
+            
+            if ( attribute != null )
+            {
+                removed.add( attribute );
+            }
+        }
+        
+        if ( removed.size() == 0 )
+        {
+            return null;
+        }
+        else
+        {
+            return removed;
+        }
+    }
+
+
+    //-------------------------------------------------------------------------
+    // Object methods
+    //-------------------------------------------------------------------------
+    /**
+     * Clone an entry. All the element are duplicated, so a modification on
+     * the original object won't affect the cloned object, as a modification
+     * on the cloned object has no impact on the original object
+     */
+    public Entry clone()
+    {
+        // First, clone the structure
+        DefaultServerEntry clone = (DefaultServerEntry)super.clone();
+        
+        // A serverEntry has a DN, an ObjectClass attribute
+        // and many attributes.
+        // Clone the DN  first.
+        if ( dn != null )
+        {
+            clone.dn = (LdapDN)dn.clone();
+        }
+        
+        // clone the ServerAttribute Map
+        clone.attributes = (Map<AttributeType, EntryAttribute>)(((HashMap<AttributeType, EntryAttribute>)attributes).clone());
+        
+        // now clone all the servrAttributes
+        clone.attributes.clear();
+        
+        for ( AttributeType key:attributes.keySet() )
+        {
+            EntryAttribute value = (ServerAttribute)attributes.get( key ).clone();
+            clone.attributes.put( key, value );
+        }
+        
+        // We are done !
+        return clone;
+    }
+    
+
+    /**
+     * @see Externalizable#writeExternal(ObjectOutput)
+     * <p>
+     * 
+     * This is the place where we serialize entries, and all theirs
+     * elements. the reason why we don't call the underlying methods
+     * (<code>ServerAttribute.write(), Value.write()</code>) is that we need
+     * access to the registries to read back the values.
+     * <p>
+     * The structure used to store the entry is the following :
+     * <li><b>[DN length]</b> : can be -1 if we don't have a DN, 0 if the 
+     * DN is empty, otherwise contains the DN's length.<p> 
+     * <b>NOTE :</b>This should be unnecessary, as the DN should always exists
+     * <p>
+     * </li>
+     * <li>
+     * <b>DN</b> : The entry's DN. Can be empty (rootDSE=<p>
+     * </li>
+     * We have to store the UPid, and all the values, if any.
+     */
+    public void writeExternal( ObjectOutput out ) throws IOException
+    {
+        // First, the DN
+        if ( dn == null )
+        {
+            // Write an empty DN
+            LdapDN.EMPTY_LDAPDN.writeExternal( out );
+        }
+        else
+        {
+            // Write the DN
+            dn.writeExternal( out );
+        }
+        
+        // Then the attributes. 
+        if ( attributes == null )
+        {
+            out.writeInt( -1 );
+        }
+        else
+        {
+            out.writeInt( attributes.size() );
+            
+            // Iterate through the keys. We store the Attribute
+            // here, to be able to restore it in the readExternal :
+            // we need access to the registries, which are not available
+            // in the ServerAttribute class.
+            for ( AttributeType attributeType:attributes.keySet() )
+            {
+                // We store the OID, as the AttributeType might have no name
+                out.writeUTF( attributeType.getOid() );
+                
+                // And store the attribute.
+                EntryAttribute attribute = attributes.get( attributeType );
+
+                // Store the UP id
+                out.writeUTF( attribute.getUpId() );
+                
+                // The number of values
+                int nbValues = attribute.size();
+                
+                if ( nbValues == 0 ) 
+                {
+                    out.writeInt( 0 );
+                }
+                else 
+                {
+                    out.writeInt( nbValues );
+
+                    for ( Value<?> value:attribute )
+                    {
+                        value.writeExternal( out );
+                    }
+                }
+            }
+
+            // Note : we don't store the ObjectClassAttribute. I has already
+            // been stored as an attribute.
+        }
+        
+        out.flush();
+    }
+
+    
+    /**
+     * @see Externalizable#readExternal(ObjectInput)
+     */
+    public void readExternal( ObjectInput in ) throws IOException, ClassNotFoundException
+    {
+        // Read the DN
+        dn = (LdapDN)in.readObject();
+        
+        // Read the number of attributes
+        int nbAttributes = in.readInt();
+        
+        attributes = new HashMap<AttributeType, EntryAttribute>( nbAttributes );
+
+        // Read the attributes
+        for ( int i = 0; i < nbAttributes; i++ )
+        {
+            String oid = in.readUTF();
+            
+            try
+            {
+                AttributeType attributeType = atRegistry.lookup( oid );
+                
+                ServerAttribute attribute = new DefaultServerAttribute( attributeType );
+                
+                // Read the attribute upID
+                String upId = in.readUTF();
+                attribute.setUpId( upId, attributeType );
+                
+                // Read the number of values
+                int nbValues = in.readInt();
+                
+                for ( int j = 0; j < nbValues; j++ )
+                {
+                    Value<?> value = (Value<?>)in.readObject();
+                    attribute.add( value );
+                }
+                
+                attributes.put(  attributeType, attribute );
+            }
+            catch ( NamingException ne )
+            {
+                
+            }
+        }
+    }
+    
+    
+    /**
+    * Gets the hashCode of this ServerEntry.
+    *
+    * @see java.lang.Object#hashCode()
+     * @return the instance's hash code 
+     */
+    public int hashCode()
+    {
+        int result = 37;
+        
+        result = result*17 + dn.hashCode();
+        
+        for ( EntryAttribute attribute:attributes.values() )
+        {
+            result = result*17 + attribute.hashCode();
+        }
+
+        return result;
+    }
+
+    
+    /**
+     * @see Object#equals(Object)
+     */
+    public boolean equals( Object o )
+    {
+        // Short circuit
+        if ( this == o )
+        {
+            return true;
+        }
+        
+        if ( ! ( o instanceof DefaultServerEntry ) )
+        {
+            return false;
+        }
+        
+        ServerEntry other = (DefaultServerEntry)o;
+        
+        if ( dn == null )
+        {
+            if ( other.getDn() != null )
+            {
+                return false;
+            }
+        }
+        else
+        {
+            if ( !dn.equals( other.getDn() ) )
+            {
+                return false;
+            }
+        }
+        
+        if ( size() != other.size() )
+        {
+            return false;
+        }
+        
+        for ( EntryAttribute attribute:other )
+        {
+            EntryAttribute attr = attributes.get( ((ServerAttribute)attribute).getAttributeType() );
+            
+            if ( attr == null )
+            {
+                return false;
+            }
+            
+            if ( !attribute.equals( attr ) )
+            {
+                return false;
+            }
+        }
+        
+        return true;
+    }
+        
+    /**
+     * @see Object#toString()
+     */
+    public String toString()
+    {
+        StringBuilder sb = new StringBuilder();
+        
+        sb.append( "ServerEntry\n" );
+        sb.append( "    dn: " ).append( dn ).append( '\n' );
+        
+        // First dump the ObjectClass attribute
+        if ( containsAttribute( OBJECT_CLASS_AT ) )
+        {
+            EntryAttribute objectClass = get( OBJECT_CLASS_AT );
+            
+            sb.append( objectClass );
+        }
+        
+        if ( attributes.size() != 0 )
+        {
+            for ( EntryAttribute attribute:attributes.values() )
+            {
+                if ( !((ServerAttribute)attribute).getAttributeType().equals( OBJECT_CLASS_AT ) )
+                {
+                    sb.append( attribute );
+                }
+            }
+        }
+        
+        return sb.toString();
+    }
+}
diff --git a/old_trunk/core-entry/src/main/java/org/apache/directory/server/core/entry/ServerAttribute.java b/old_trunk/core-entry/src/main/java/org/apache/directory/server/core/entry/ServerAttribute.java
new file mode 100644
index 0000000..a5413b5
--- /dev/null
+++ b/old_trunk/core-entry/src/main/java/org/apache/directory/server/core/entry/ServerAttribute.java
@@ -0,0 +1,114 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements.  See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership.  The ASF licenses this file

+ * to you 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.

+ */

+package org.apache.directory.server.core.entry;

+

+

+import javax.naming.NamingException;

+import javax.naming.directory.InvalidAttributeValueException;

+

+import org.apache.directory.shared.ldap.entry.client.ClientAttribute;

+import org.apache.directory.shared.ldap.schema.AttributeType;

+

+

+/**

+ * The server specific interface extending the EntryAttribute interface. It adds

+ * three more methods which are Server side.

+ *

+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>

+ * @version $Rev$, $Date$

+ */

+public interface ServerAttribute extends ClientAttribute

+{

+    /**

+     * Get the attribute type associated with this ServerAttribute.

+     *

+     * @return the attributeType associated with this entry attribute

+     */

+    AttributeType getAttributeType();

+

+    

+    /**

+     * <p>

+     * Set the attribute type associated with this ServerAttribute.

+     * </p>

+     * <p>

+     * The current attributeType will be replaced. It is the responsibility of

+     * the caller to insure that the existing values are compatible with the new

+     * AttributeType

+     * </p>

+     *

+     * @param attributeType the attributeType associated with this entry attribute

+     */

+    void setAttributeType( AttributeType attributeType );

+

+    

+    /**

+     * <p>

+     * Check if the current attribute type is of the expected attributeType

+     * </p>

+     * <p>

+     * This method won't tell if the current attribute is a descendant of 

+     * the attributeType. For instance, the "CN" serverAttribute will return

+     * false if we ask if it's an instance of "Name". 

+     * </p> 

+     *

+     * @param attributeId The AttributeType ID to check

+     * @return True if the current attribute is of the expected attributeType

+     * @throws InvalidAttributeValueException If there is no AttributeType

+     */

+    boolean instanceOf( String attributeId ) throws InvalidAttributeValueException;

+

+

+    /**

+     * <p>

+     * Set the user provided ID. If we have none, the upId is assigned

+     * the attributetype's name. If it does not have any name, we will

+     * use the OID.

+     * </p>

+     * <p>

+     * If we have an upId and an AttributeType, they must be compatible. :

+     *  - if the upId is an OID, it must be the AttributeType's OID

+     *  - otherwise, its normalized form must be equals to ones of

+     *  the attributeType's names.

+     * </p>

+     * <p>

+     * In any case, the ATtributeType will be changed. The caller is responsible for

+     * the present values to be compatoble with the new AttributeType.

+     * </p>

+     * 

+     * @param upId The attribute ID

+     * @param attributeType The associated attributeType

+     */

+    void setUpId( String upId, AttributeType attributeType );

+    

+    

+    /**

+     * <p>

+     * Checks to see if this attribute is valid along with the values it contains.

+     * </p>

+     * <p>

+     * An attribute is valid if :

+     * <li>All of its values are valid with respect to the attributeType's syntax checker</li>

+     * <li>If the attributeType is SINGLE-VALUE, then no more than a value should be present</li>

+     *</p>

+     * @return true if the attribute and it's values are valid, false otherwise

+     * @throws NamingException if there is a failure to check syntaxes of values

+     */

+    boolean isValid() throws NamingException;

+}

diff --git a/old_trunk/core-entry/src/main/java/org/apache/directory/server/core/entry/ServerBinaryValue.java b/old_trunk/core-entry/src/main/java/org/apache/directory/server/core/entry/ServerBinaryValue.java
new file mode 100644
index 0000000..c62adc4
--- /dev/null
+++ b/old_trunk/core-entry/src/main/java/org/apache/directory/server/core/entry/ServerBinaryValue.java
@@ -0,0 +1,741 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements.  See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership.  The ASF licenses this file

+ * to you 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.

+ */

+package org.apache.directory.server.core.entry;

+

+

+import java.io.Externalizable;

+import java.io.IOException;

+import java.io.ObjectInput;

+import java.io.ObjectOutput;

+import java.util.Arrays;

+import java.util.Comparator;

+

+import javax.naming.NamingException;

+

+import org.apache.directory.shared.ldap.NotImplementedException;

+import org.apache.directory.shared.ldap.entry.Value;

+import org.apache.directory.shared.ldap.entry.client.ClientBinaryValue;

+import org.apache.directory.shared.ldap.schema.AttributeType;

+import org.apache.directory.shared.ldap.schema.ByteArrayComparator;

+import org.apache.directory.shared.ldap.schema.MatchingRule;

+import org.apache.directory.shared.ldap.schema.Normalizer;

+import org.apache.directory.shared.ldap.util.StringTools;

+import org.slf4j.Logger;

+import org.slf4j.LoggerFactory;

+

+

+/**

+ * A server side schema aware wrapper around a binary attribute value.

+ * This value wrapper uses schema information to syntax check values,

+ * and to compare them for equality and ordering.  It caches results

+ * and invalidates them when the wrapped value changes.

+ *

+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>

+ * @version $Rev$, $Date$

+ */

+public class ServerBinaryValue extends ClientBinaryValue

+{

+    /** Used for serialization */

+    private static final long serialVersionUID = 2L;

+    

+    /** logger for reporting errors that might not be handled properly upstream */

+    private static final Logger LOG = LoggerFactory.getLogger( ServerBinaryValue.class );

+

+    /** reference to the attributeType which is not serialized */

+    private transient AttributeType attributeType;

+

+    /** A flag set if the normalized data is different from the wrapped data */

+    private transient boolean same;

+

+

+    // -----------------------------------------------------------------------

+    // utility methods

+    // -----------------------------------------------------------------------

+    /**

+     * Utility method to get some logs if an assert fails

+     */

+    protected String logAssert( String message )

+    {

+        LOG.error(  message );

+        return message;

+    }

+

+    

+    /**

+     *  Check the attributeType member. It should not be null, 

+     *  and it should contains a syntax.

+     */

+    protected String checkAttributeType( AttributeType attributeType )

+    {

+        try

+        {

+            if ( attributeType == null )

+            {

+                return "The AttributeType parameter should not be null";

+            }

+            

+            if ( attributeType.getSyntax() == null )

+            {

+                return "There is no Syntax associated with this attributeType";

+            }

+

+            return null;

+        }

+        catch ( NamingException ne )

+        {

+            return "This AttributeType is incorrect";

+        }

+    }

+

+    

+    // -----------------------------------------------------------------------

+    // Constructors

+    // -----------------------------------------------------------------------

+    /**

+     * Creates a ServerBinaryValue without an initial wrapped value.

+     *

+     * @param attributeType the schema type associated with this ServerBinaryValue

+     */

+    public ServerBinaryValue( AttributeType attributeType )

+    {

+        super();

+        

+        if ( attributeType == null )

+        {

+            throw new IllegalArgumentException( "The AttributeType parameter should not be null" );

+        }

+

+        try

+        {

+            if ( attributeType.getSyntax() == null )

+            {

+                throw new IllegalArgumentException( "There is no Syntax associated with this attributeType" );

+            }

+

+            if ( attributeType.getSyntax().isHumanReadable() )

+            {

+                LOG.warn( "Treating a value of a human readible attribute {} as binary: ", attributeType.getName() );

+            }

+        }

+        catch( NamingException e )

+        {

+            LOG.error( "Failed to resolve syntax for attributeType {}", attributeType, e );

+        }

+

+        this.attributeType = attributeType;

+    }

+

+

+    /**

+     * Creates a ServerBinaryValue with an initial wrapped binary value.

+     *

+     * @param attributeType the schema type associated with this ServerBinaryValue

+     * @param wrapped the binary value to wrap which may be null, or a zero length byte array

+     */

+    public ServerBinaryValue( AttributeType attributeType, byte[] wrapped )

+    {

+        this( attributeType );

+        this.wrapped = wrapped;

+    }

+

+

+    /**

+     * Creates a ServerStringValue with an initial wrapped String value and

+     * a normalized value.

+     *

+     * @param attributeType the schema type associated with this ServerStringValue

+     * @param wrapped the value to wrap which can be null

+     * @param normalizedValue the normalized value

+     */

+    /** No protection */ 

+    ServerBinaryValue( AttributeType attributeType, byte[] wrapped, byte[] normalizedValue, boolean same, boolean valid )

+    {

+        super( wrapped );

+        this.normalized = true;

+        this.attributeType = attributeType;

+        this.normalizedValue = normalizedValue;

+        this.valid = valid;

+        this.same = same;

+    }

+

+

+    // -----------------------------------------------------------------------

+    // ServerValue<byte[]> Methods

+    // -----------------------------------------------------------------------

+    public void normalize() throws NamingException

+    {

+        if ( isNormalized() )

+        {

+            // Bypass the normalization if it has already been done. 

+            return;

+        }

+        

+        if ( getReference() != null )

+        {

+            Normalizer normalizer = getNormalizer();

+    

+            if ( normalizer == null )

+            {

+                normalizedValue = getCopy();

+                setNormalized( false );

+            }

+            else

+            {

+                normalizedValue = ( byte[] ) normalizer.normalize( getCopy() );

+                setNormalized( true );

+            }

+            

+            if ( Arrays.equals( super.getReference(), normalizedValue ) )

+            {

+                same = true;

+            }

+            else

+            {

+                same = false;

+            }

+        }

+        else

+        {

+            normalizedValue = null;

+            same = true;

+            setNormalized( false );

+        }

+    }

+

+    

+    /**

+     * Gets the normalized (cannonical) representation for the wrapped string.

+     * If the wrapped String is null, null is returned, otherwise the normalized

+     * form is returned.  If no the normalizedValue is null, then this method

+     * will attempt to generate it from the wrapped value: repeated calls to

+     * this method do not unnecessarily normalize the wrapped value.  Only changes

+     * to the wrapped value result in attempts to normalize the wrapped value.

+     *

+     * @return a reference to the normalized version of the wrapped value

+     */

+    public byte[] getNormalizedValueReference()

+    {

+        if ( isNull() )

+        {

+            return null;

+        }

+

+        if ( !isNormalized() )

+        {

+            try

+            {

+                normalize();

+            }

+            catch ( NamingException ne )

+            {

+                String message = "Cannot normalize the value :" + ne.getMessage();

+                LOG.warn( message );

+                normalized = false;

+            }

+        }

+

+        return normalizedValue;

+    }

+

+

+    /**

+     * Gets the normalized (canonical) representation for the wrapped byte[].

+     * If the wrapped byte[] is null, null is returned, otherwise the normalized

+     * form is returned.  If no the normalizedValue is null, then this method

+     * will attempt to generate it from the wrapped value: repeated calls to

+     * this method do not unnecessarily normalize the wrapped value.  Only changes

+     * to the wrapped value result in attempts to normalize the wrapped value.

+     *

+     * @return gets the normalized value

+     */

+    public byte[] getNormalizedValue() 

+    {

+        if ( isNull() )

+        {

+            return null;

+        }

+

+        if ( !normalized )

+        {

+            try

+            {

+                normalize();

+            }

+            catch ( NamingException ne )

+            {

+                String message = "Cannot normalize the value :" + ne.getMessage();

+                LOG.warn( message );

+                normalized = false;

+            }

+        }

+

+        return normalizedValue;

+    }

+

+

+    /**

+     * Gets a direct reference to the normalized representation for the

+     * wrapped value of this ServerValue wrapper. Implementations will most

+     * likely leverage the attributeType this value is associated with to

+     * determine how to properly normalize the wrapped value.

+     *

+     * @return the normalized version of the wrapped value

+     */

+    public byte[] getNormalizedValueCopy()

+    {

+        if ( isNull() )

+        {

+            return null;

+        }

+

+        if ( normalizedValue == null )

+        {

+            try

+            {

+                normalize();

+            }

+            catch ( NamingException ne )

+            {

+                String message = "Cannot normalize the value :" + ne.getMessage();

+                LOG.warn( message );

+                normalized = false;

+            }

+        }

+

+        if ( normalizedValue != null )

+        {

+            byte[] copy = new byte[ normalizedValue.length ];

+            System.arraycopy( normalizedValue, 0, copy, 0, normalizedValue.length );

+            return copy;

+        }

+        else

+        {

+            return null;

+        }

+    }

+

+

+    /**

+     * Uses the syntaxChecker associated with the attributeType to check if the

+     * value is valid.  Repeated calls to this method do not attempt to re-check

+     * the syntax of the wrapped value every time if the wrapped value does not

+     * change. Syntax checks only result on the first check, and when the wrapped

+     * value changes.

+     *

+     * @see Value#isValid()

+     */

+    public final boolean isValid()

+    {

+        if ( valid != null )

+        {

+            return valid;

+        }

+

+        try

+        {

+            valid = attributeType.getSyntax().getSyntaxChecker().isValidSyntax( getReference() );

+        }

+        catch ( NamingException ne )

+        {

+            String message = "Cannot check the syntax : " + ne.getMessage();

+            LOG.error( message );

+            valid = false;

+        }

+        

+        return valid;

+    }

+

+    

+    /**

+     * @return Tells if the wrapped value and the normalized value are the same 

+     */

+    public final boolean isSame()

+    {

+        return same;

+    }

+    

+

+    /**

+     *

+     * @see Value#compareTo(Value)

+     * @throws IllegalStateException on failures to extract the comparator, or the

+     * normalizers needed to perform the required comparisons based on the schema

+     */

+    public int compareTo( Value<byte[]> value )

+    {

+        if ( isNull() )

+        {

+            if ( ( value == null ) || value.isNull() )

+            {

+                return 0;

+            }

+            else

+            {

+                return -1;

+            }

+        }

+        else

+        {

+            if ( ( value == null ) || value.isNull() ) 

+            {

+                return 1;

+            }

+        }

+

+        if ( value instanceof ServerBinaryValue )

+        {

+            ServerBinaryValue binaryValue = ( ServerBinaryValue ) value;

+

+            try

+            {

+                Comparator<? super Value<byte[]>> comparator = getComparator();

+                

+                if ( comparator != null )

+                {

+                    return getComparator().compare( getNormalizedValueReference(), binaryValue.getNormalizedValueReference() );

+                }

+                else

+                {

+                    return ByteArrayComparator.INSTANCE.compare( getNormalizedValueReference(), 

+                        binaryValue.getNormalizedValueReference() );

+                }

+            }

+            catch ( NamingException e )

+            {

+                String msg = "Failed to compare normalized values for " + Arrays.toString( getReference() )

+                        + " and " + value;

+                LOG.error( msg, e );

+                throw new IllegalStateException( msg, e );

+            }

+        }

+

+        String message = "I don't really know how to compare anything other " +

+        "than ServerBinaryValues at this point in time.";

+        LOG.error( message );

+        throw new NotImplementedException( message );

+    }

+

+

+    /**

+     * Get the associated AttributeType

+     * @return The AttributeType

+     */

+    public AttributeType getAttributeType()

+    {

+        return attributeType;

+    }

+

+

+    /**

+     * Check if the value is stored into an instance of the given 

+     * AttributeType, or one of its ascendant.

+     * 

+     * For instance, if the Value is associated with a CommonName,

+     * checking for Name will match.

+     * 

+     * @param attributeType The AttributeType we are looking at

+     * @return <code>true</code> if the value is associated with the given

+     * attributeType or one of its ascendant

+     */

+    public boolean instanceOf( AttributeType attributeType ) throws NamingException

+    {

+        if ( this.attributeType.equals( attributeType ) )

+        {

+            return true;

+        }

+

+        return this.attributeType.isDescentantOf( attributeType );

+    }

+

+

+    // -----------------------------------------------------------------------

+    // Object Methods

+    // -----------------------------------------------------------------------

+    /**

+     * @see Object#hashCode()

+     * @return the instance's hash code 

+     */

+    public int hashCode()

+    {

+        // return zero if the value is null so only one null value can be

+        // stored in an attribute - the string version does the same

+        if ( isNull() )

+        {

+            return 0;

+        }

+

+        return Arrays.hashCode( getNormalizedValueReference() );

+    }

+

+

+    /**

+     * Checks to see if this ServerBinaryValue equals the supplied object.

+     *

+     * This equals implementation overrides the BinaryValue implementation which

+     * is not schema aware.

+     * @throws IllegalStateException on failures to extract the comparator, or the

+     * normalizers needed to perform the required comparisons based on the schema

+     */

+    public boolean equals( Object obj )

+    {

+        if ( this == obj )

+        {

+            return true;

+        }

+

+        if ( ! ( obj instanceof ServerBinaryValue ) )

+        {

+            return false;

+        }

+

+        ServerBinaryValue other = ( ServerBinaryValue ) obj;

+        

+        if ( !attributeType.equals( other.attributeType ) )

+        {

+            return false;

+        }

+        

+        if ( isNull() )

+        {

+            return other.isNull();

+        }

+

+        // Shortcut : if the values are equals, no need to compare

+        // the normalized values

+        if ( Arrays.equals( wrapped, other.get() ) )

+        {

+            return true;

+        }

+        else

+        {

+            try

+            {

+                Comparator<byte[]> comparator = getComparator();

+

+                // Compare normalized values

+                if ( comparator == null )

+                {

+                    return Arrays.equals( getNormalizedValueReference(), other.getNormalizedValueReference() );

+                }

+                else

+                {

+                    return comparator.compare( getNormalizedValueReference(), other.getNormalizedValueReference() ) == 0;

+                }

+            }

+            catch ( NamingException ne )

+            {

+                return false;

+            }

+        }

+    }

+

+

+    // -----------------------------------------------------------------------

+    // Private Helper Methods (might be put into abstract base class)

+    // -----------------------------------------------------------------------

+    /**

+     * Find a matchingRule to use for normalization and comparison.  If an equality

+     * matchingRule cannot be found it checks to see if other matchingRules are

+     * available: SUBSTR, and ORDERING.  If a matchingRule cannot be found null is

+     * returned.

+     *

+     * @return a matchingRule or null if one cannot be found for the attributeType

+     * @throws NamingException if resolution of schema entities fail

+     */

+    private MatchingRule getMatchingRule() throws NamingException

+    {

+        MatchingRule mr = attributeType.getEquality();

+

+        if ( mr == null )

+        {

+            mr = attributeType.getOrdering();

+        }

+

+        if ( mr == null )

+        {

+            mr = attributeType.getSubstr();

+        }

+

+        return mr;

+    }

+

+

+    /**

+     * Gets a normalizer using getMatchingRule() to resolve the matchingRule

+     * that the normalizer is extracted from.

+     *

+     * @return a normalizer associated with the attributeType or null if one cannot be found

+     * @throws NamingException if resolution of schema entities fail

+     */

+    private Normalizer getNormalizer() throws NamingException

+    {

+        MatchingRule mr = getMatchingRule();

+

+        if ( mr == null )

+        {

+            return null;

+        }

+

+        return mr.getNormalizer();

+    }

+

+

+    /**

+     * Gets a comparator using getMatchingRule() to resolve the matching

+     * that the comparator is extracted from.

+     *

+     * @return a comparator associated with the attributeType or null if one cannot be found

+     * @throws NamingException if resolution of schema entities fail

+     */

+    private Comparator getComparator() throws NamingException

+    {

+        MatchingRule mr = getMatchingRule();

+

+        if ( mr == null )

+        {

+            return null;

+        }

+

+        return mr.getComparator();

+    }

+    

+    

+    /**

+     * @return a copy of the current value

+     */

+    public ServerBinaryValue clone()

+    {

+        ServerBinaryValue clone = (ServerBinaryValue)super.clone();

+        

+        if ( normalizedValue != null )

+        {

+            clone.normalizedValue = new byte[ normalizedValue.length ];

+            System.arraycopy( normalizedValue, 0, clone.normalizedValue, 0, normalizedValue.length );

+        }

+        

+        return clone;

+    }

+

+

+    /**

+     * @see Externalizable#writeExternal(ObjectOutput)

+     * 

+     * We will write the value and the normalized value, only

+     * if the normalized value is different.

+     * 

+     * The data will be stored following this structure :

+     * 

+     *  [UP value]

+     *  [Norm value] (will be null if normValue == upValue)

+     */

+    public void writeExternal( ObjectOutput out ) throws IOException

+    {

+        if ( getReference() != null )

+        {

+            out.writeInt( getReference().length );

+            out.write( getReference() );

+            

+            if ( same )

+            {

+                // If the normalized value is equal to the UP value,

+                // don't save it

+                out.writeInt( 0 );

+            }

+            else

+            {

+                out.writeInt( normalizedValue.length );

+                out.write( normalizedValue );

+            }

+        }

+        else

+        {

+            out.writeInt( -1 );

+        }

+        

+        out.flush();

+    }

+

+    

+    /**

+     * @see Externalizable#readExternal(ObjectInput)

+     */

+    public void readExternal( ObjectInput in ) throws IOException, ClassNotFoundException

+    {

+        if ( in.available() == 0 )

+        {

+            set( null );

+            normalizedValue = null;

+        }

+        else

+        {

+            int wrappedLength = in.readInt();

+            byte[] wrappedBytes = null;

+            

+            switch ( wrappedLength )

+            {

+                case -1 :

+                    // No value, no normalized value

+                    same = true;

+                    setNormalized( false );

+                    break;

+                    

+                case 0 :

+                    // Empty value, so is the normalized value

+                    wrappedBytes = StringTools.EMPTY_BYTES;

+                    normalizedValue = wrappedBytes;

+                    setNormalized( true );

+                    same = true;

+                    break;

+                    

+                default :

+                    wrappedBytes = new byte[wrappedLength];

+                    in.readFully( wrappedBytes );

+                    

+                    int normalizedLength = in.readInt();

+                    

+                    // The normalized length should be either 0 or N, 

+                    // but it can't be -1

+                    switch ( normalizedLength )

+                    {

+                        case -1 :

+                            String message = "The normalized value cannot be null when the User Provide value is not";

+                            LOG.error(  message  );

+                            throw new IOException( message );

+                            

+                        case 0 :

+                            normalizedValue = StringTools.EMPTY_BYTES;

+                            same = true;

+                            setNormalized( false );

+                            break;

+                            

+                        default :

+                            same = false;

+                            normalizedValue = new byte[normalizedLength];

+                            in.readFully( normalizedValue );

+                            setNormalized( true );

+                            break;

+                    }

+                    

+                    break;

+            }

+            

+            set( wrappedBytes );

+        }

+    }

+}
\ No newline at end of file
diff --git a/old_trunk/core-entry/src/main/java/org/apache/directory/server/core/entry/ServerEntry.java b/old_trunk/core-entry/src/main/java/org/apache/directory/server/core/entry/ServerEntry.java
new file mode 100644
index 0000000..8fce23c
--- /dev/null
+++ b/old_trunk/core-entry/src/main/java/org/apache/directory/server/core/entry/ServerEntry.java
@@ -0,0 +1,554 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements.  See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership.  The ASF licenses this file

+ * to you 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.

+ */

+package org.apache.directory.server.core.entry;

+

+

+import java.util.List;

+import java.util.Set;

+

+import javax.naming.NamingException;

+

+import org.apache.directory.shared.ldap.entry.Entry;

+import org.apache.directory.shared.ldap.entry.EntryAttribute;

+import org.apache.directory.shared.ldap.entry.Value;

+import org.apache.directory.shared.ldap.schema.AttributeType;

+

+

+/**

+ * A server side entry which is schema aware.

+ *

+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>

+ * @version $Rev$, $Date$

+ */

+public interface ServerEntry extends Entry, Cloneable

+{

+    /**

+     * <p>

+     * Add an attribute (represented by its AttributeType and some binary values) into an 

+     * entry.

+     * </p>

+     * <p> 

+     * If we already have an attribute with the same values, the duplicated values 

+     * are not added (duplicated values are not allowed)

+     * </p>

+     * <p>

+     * If the value cannot be added, or if the AttributeType is null or invalid, 

+     * a NamingException is thrown.

+     * </p>

+     *

+     * @param attributeType The attribute Type.

+     * @param values The list of binary values to inject. It can be empty.

+     * @throws NamingException If the attribute does not exist

+     */

+    void add( AttributeType attributeType, byte[]... values ) throws NamingException;

+

+    

+    /**

+     * <p>

+     * Add an attribute (represented by its AttributeType and some String values) into an 

+     * entry.

+     * </p>

+     * <p> 

+     * If we already have an attribute with the same values, the duplicated values 

+     * are not added (duplicated values are not allowed)

+     * </p>

+     * <p> 

+     * If the value cannot be added, or if the AttributeType is null or invalid, 

+     * a NamingException is thrown.

+     * </p>

+     * 

+     * @param attributeType The attribute Type

+     * @param values The list of binary values to inject. It can be empty

+     * @throws NamingException If the attribute does not exist

+     */

+    void add( AttributeType attributeType, String... values ) throws NamingException;

+

+    

+    /**

+     * <p>

+     * Add an attribute (represented by its AttributeType and some values) into an 

+     * entry.

+     * </p>

+     * <p> 

+     * If we already have an attribute with the same values, the duplicated values 

+     * are not added (duplicated values are not allowed)

+     * </p>

+     * <p>

+     * If the value cannot be added, or if the AttributeType is null or invalid, 

+     * a NamingException is thrown.

+     * </p>

+     *

+     * @param attributeType The attribute Type

+     * @param values The list of binary values to inject. It can be empty

+     * @throws NamingException If the attribute does not exist

+     */

+    void add( AttributeType attributeType, Value<?>... values ) throws NamingException;

+

+    

+    /**

+     * <p>

+     * Add an attribute (represented by its AttributeType and some binary values) into an 

+     * entry. Set the User Provider ID at the same time

+     * </p>

+     * <p> 

+     * If we already have an attribute with the same values, the duplicated values 

+     * are not added (duplicated values are not allowed)

+     * </p>

+     * <p>

+     * If the value cannot be added, or if the AttributeType is null or invalid, 

+     * a NamingException is thrown.

+     * </p>

+     *

+     * @param upId The user provided ID for the added AttributeType

+     * @param attributeType The attribute Type.

+     * @param values The list of binary values to add. It can be empty.

+     * @throws NamingException If the attribute does not exist

+     */

+    void add( String upId, AttributeType attributeType, byte[]... values ) throws NamingException;

+

+    

+    /**

+     * <p>

+     * Add an attribute (represented by its AttributeType and some String values) into an 

+     * entry. Set the User Provider ID at the same time

+     * </p>

+     * <p> 

+     * If we already have an attribute with the same values, the duplicated values 

+     * are not added (duplicated values are not allowed)

+     * </p>

+     * <p>

+     * If the value cannot be added, or if the AttributeType is null or invalid, 

+     * a NamingException is thrown.

+     * </p>

+     *

+     * @param upId The user provided ID for the added AttributeType

+     * @param attributeType The attribute Type.

+     * @param values The list of binary values to add. It can be empty.

+     * @throws NamingException If the attribute does not exist

+     */

+    void add( String upId, AttributeType attributeType, String... values ) throws NamingException;

+

+    

+    /**

+     * <p>

+     * Add an attribute (represented by its AttributeType and some values) into an 

+     * entry. Set the User Provider ID at the same time

+     * </p>

+     * <p> 

+     * If we already have an attribute with the same values, nothing is done 

+     * (duplicated values are not allowed)

+     * </p>

+     * <p>

+     * If the value cannot be added, or if the AttributeType is null or invalid, 

+     * a NamingException is thrown.

+     * </p>

+     *

+     * @param upId The user provided ID for the added AttributeType

+     * @param attributeType The attribute Type.

+     * @param values The list of values to add. It can be empty.

+     * @throws NamingException If the attribute does not exist

+     */

+    void add( String upId, AttributeType attributeType, Value<?>... values ) throws NamingException;

+

+

+    // -----------------------------------------------------------------------

+    // Container (get/put/remove) Methods

+    // -----------------------------------------------------------------------

+    /**

+     * Checks if an entry contains an attribute with some given binary values.

+     *

+     * @param attributeType The Attribute we are looking for.

+     * @param values The searched values.

+     * @return <code>true</code> if all the values are found within the attribute,

+     * <code>false</code> otherwise, or if the attributes does not exist.

+     * @throws NamingException If the attribute does not exists

+     */

+    boolean contains( AttributeType attributeType, byte[]... values );

+

+

+    /**

+     * Checks if an entry contains an attribute with some given String values.

+     *

+     * @param attributeType The Attribute we are looking for.

+     * @param values The searched values.

+     * @return <code>true</code> if all the values are found within the attribute,

+     * <code>false</code> otherwise, or if the attributes does not exist.

+     * @throws NamingException If the attribute does not exists

+     */

+    boolean contains( AttributeType attributeType, String... values );

+

+

+    /**

+     * Checks if an entry contains an attribute with some given binary values.

+     *

+     * @param attributeType The Attribute we are looking for.

+     * @param values The searched values.

+     * @return <code>true</code> if all the values are found within the attribute,

+     * <code>false</code> otherwise, or if the attributes does not exist.

+     * @throws NamingException If the attribute does not exists

+     */

+    boolean contains( AttributeType attributeType, Value<?>... values );

+

+

+    /**

+     * Checks if an entry contains a specific AttributeType.

+     *

+     * @param attributeType The AttributeType to look for.

+     * @return <code>true</code> if the attribute is found within the entry.

+     */

+    boolean containsAttribute( AttributeType attributeType );

+

+    

+    /**

+     * <p>

+     * Returns the attribute with the specified AttributeType. The return value

+     * is <code>null</code> if no match is found.  

+     * </p>

+     *

+     * @param attributeType The attributeType we are looking for.

+     * @return the attribute associated with the AttributeType.

+     */

+    /**

+     * Returns the attribute associated with an AttributeType

+     * 

+     * @param the AttributeType we are looking for

+     * @return the associated attribute

+     */

+    EntryAttribute get( AttributeType attributeType );

+

+

+    /**

+     * Gets all the attributes type

+     *

+     * @return The combined set of all the attributes.

+     */

+    Set<AttributeType> getAttributeTypes();

+    

+    

+    /**

+     * Tells if an entry has a specific ObjectClass Attribute

+     * 

+     * @param objectClass The ObjectClass we want to check

+     * @return <code>true</code> if the ObjectClass value is present 

+     * in the ObjectClass attribute

+     */

+    boolean hasObjectClass( EntryAttribute objectClass );

+

+    

+    /**

+     * Fail fast check performed to determine entry consistency according to schema

+     * characteristics.

+     *

+     * @return true if the entry, it's attributes and their values are consistent

+     * with the schema

+     */

+    boolean isValid();

+

+

+    /**

+     * Check performed to determine entry consistency according to the schema

+     * requirements of a particular objectClass.  The entry must be of that objectClass

+     * to return true: meaning if the entry's objectClass attribute does not contain

+     * the objectClass argument, then false should be returned.

+     *

+     * @param objectClass the objectClass to use while checking for validity

+     * @return true if the entry, it's attributes and their values are consistent

+     * with the objectClass

+     */

+    boolean isValid( String objectClass );

+

+    

+    /**

+     * Check performed to determine entry consistency according to the schema

+     * requirements of a particular objectClass.  The entry must be of that objectClass

+     * to return true: meaning if the entry's objectClass attribute does not contain

+     * the objectClass argument, then false should be returned.

+     *

+     * @param objectClass the objectClass to use while checking for validity

+     * @return true if the entry, it's attributes and their values are consistent

+     * with the objectClass

+     */

+    boolean isValid( EntryAttribute objectClass );

+

+

+    /**

+     * <p>

+     * Places a new attribute with the supplied AttributeType and binary values 

+     * into the attribute collection. 

+     * </p>

+     * <p>

+     * If there is already an attribute with the same AttributeType, the old

+     * one is removed from the collection and is returned by this method. 

+     * </p>

+     * <p>

+     * This method provides a mechanism to put an attribute with a

+     * <code>null</code> value: the value may be <code>null</code>.

+     *

+     * @param attributeType the type of the new attribute to be put

+     * @param values the binary values of the new attribute to be put

+     * @return the old attribute with the same identifier, if exists; otherwise

+     * <code>null</code>

+     * @throws NamingException if there are failures

+     */

+    EntryAttribute put( AttributeType attributeType, byte[]... values ) throws NamingException;

+

+

+    /**

+     * <p>

+     * Places a new attribute with the supplied AttributeType and String values 

+     * into the attribute collection. 

+     * </p>

+     * <p>

+     * If there is already an attribute with the same AttributeType, the old

+     * one is removed from the collection and is returned by this method. 

+     * </p>

+     * <p>

+     * This method provides a mechanism to put an attribute with a

+     * <code>null</code> value: the value may be <code>null</code>.

+     *

+     * @param attributeType the type of the new attribute to be put

+     * @param values the String values of the new attribute to be put

+     * @return the old attribute with the same identifier, if exists; otherwise

+     * <code>null</code>

+     * @throws NamingException if there are failures

+     */

+    EntryAttribute put( AttributeType attributeType, String... values ) throws NamingException;

+

+

+    /**

+     * <p>

+     * Places a new attribute with the supplied AttributeType and some values 

+     * into the attribute collection. 

+     * </p>

+     * <p>

+     * If there is already an attribute with the same AttributeType, the old

+     * one is removed from the collection and is returned by this method. 

+     * </p>

+     * <p>

+     * This method provides a mechanism to put an attribute with a

+     * <code>null</code> value: the value may be <code>null</code>.

+     *

+     * @param attributeType the type of the new attribute to be put

+     * @param values the values of the new attribute to be put

+     * @return the old attribute with the same identifier, if exists; otherwise

+     * <code>null</code>

+     * @throws NamingException if there are failures

+     */

+    EntryAttribute put( AttributeType attributeType, Value<?>... values ) throws NamingException;

+

+

+    /**

+     * <p>

+     * Places a new attribute with the supplied AttributeType and some binary values 

+     * into the attribute collection. 

+     * </p>

+     * <p>

+     * The given User provided ID will be used for this new AttributeEntry.

+     * </p>

+     * <p>

+     * If there is already an attribute with the same AttributeType, the old

+     * one is removed from the collection and is returned by this method. 

+     * </p>

+     * <p>

+     * This method provides a mechanism to put an attribute with a

+     * <code>null</code> value: the value may be <code>null</code>.

+     *

+     * @param upId The User Provided ID to be stored into the AttributeEntry

+     * @param values the binary values of the new attribute to be put

+     * @return the old attribute with the same identifier, if exists; otherwise

+     * <code>null</code>

+     * @throws NamingException if there are failures.

+     */

+    EntryAttribute put( String upId, AttributeType attributeType, byte[]... values ) throws NamingException;

+

+

+    /**

+     * <p>

+     * Places a new attribute with the supplied AttributeType and some String values 

+     * into the attribute collection. 

+     * </p>

+     * <p>

+     * The given User provided ID will be used for this new AttributeEntry.

+     * </p>

+     * <p>

+     * If there is already an attribute with the same AttributeType, the old

+     * one is removed from the collection and is returned by this method. 

+     * </p>

+     * <p>

+     * This method provides a mechanism to put an attribute with a

+     * <code>null</code> value: the value may be <code>null</code>.

+     *

+     * @param upId The User Provided ID to be stored into the AttributeEntry

+     * @param attributeType the type of the new attribute to be put

+     * @param values the String values of the new attribute to be put

+     * @return the old attribute with the same identifier, if exists; otherwise

+     * <code>null</code>

+     * @throws NamingException if there are failures.

+     */

+    EntryAttribute put( String upId, AttributeType attributeType, String... values ) throws NamingException;

+

+

+    /**

+     * <p>

+     * Places a new attribute with the supplied AttributeType and some values 

+     * into the attribute collection. 

+     * </p>

+     * <p>

+     * The given User provided ID will be used for this new AttributeEntry.

+     * </p>

+     * <p>

+     * If there is already an attribute with the same AttributeType, the old

+     * one is removed from the collection and is returned by this method. 

+     * </p>

+     * <p>

+     * This method provides a mechanism to put an attribute with a

+     * <code>null</code> value: the value may be <code>null</code>.

+     *

+     * @param upId The User Provided ID to be stored into the AttributeEntry

+     * @param attributeType the type of the new attribute to be put

+     * @param values the values of the new attribute to be put

+     * @return the old attribute with the same identifier, if exists; otherwise

+     * <code>null</code>

+     * @throws NamingException if there are failures.

+     */

+    EntryAttribute put( String upId, AttributeType attributeType, Value<?>... values ) throws NamingException;

+

+

+    /**

+     * <p>

+     * Removes the specified binary values from an attribute.

+     * </p>

+     * <p>

+     * If at least one value is removed, this method returns <code>true</code>.

+     * </p>

+     * <p>

+     * If there is no more value after having removed the values, the attribute

+     * will be removed too.

+     * </p>

+     * <p>

+     * If the attribute does not exist, nothing is done and the method returns 

+     * <code>false</code>

+     * </p> 

+     *

+     * @param attributeType The attribute type  

+     * @param values the values to be removed

+     * @return <code>true</code> if at least a value is removed, <code>false</code>

+     * if not all the values have been removed or if the attribute does not exist. 

+     */

+    boolean remove( AttributeType attributeType, byte[]... values ) throws NamingException;

+

+    

+    /**

+     * <p>

+     * Removes the specified String values from an attribute.

+     * </p>

+     * <p>

+     * If at least one value is removed, this method returns <code>true</code>.

+     * </p>

+     * <p>

+     * If there is no more value after having removed the values, the attribute

+     * will be removed too.

+     * </p>

+     * <p>

+     * If the attribute does not exist, nothing is done and the method returns 

+     * <code>false</code>

+     * </p> 

+     *

+     * @param attributeType The attribute type  

+     * @param values the values to be removed

+     * @return <code>true</code> if at least a value is removed, <code>false</code>

+     * if not all the values have been removed or if the attribute does not exist. 

+     */

+    boolean remove( AttributeType attributeType, String... values ) throws NamingException;

+

+    

+    /**

+     * <p>

+     * Removes the specified values from an attribute.

+     * </p>

+     * <p>

+     * If at least one value is removed, this method returns <code>true</code>.

+     * </p>

+     * <p>

+     * If there is no more value after having removed the values, the attribute

+     * will be removed too.

+     * </p>

+     * <p>

+     * If the attribute does not exist, nothing is done and the method returns 

+     * <code>false</code>

+     * </p> 

+     *

+     * @param attributeType The attribute type  

+     * @param values the values to be removed

+     * @return <code>true</code> if at least a value is removed, <code>false</code>

+     * if not all the values have been removed or if the attribute does not exist. 

+     */

+    boolean remove( AttributeType attributeType, Value<?>... values ) throws NamingException;

+

+    

+    /**

+     * Removes the specified attributes. The removed attributes are

+     * returned by this method. If there were no attribute the return value

+     * is <code>null</code>.

+     *

+     * @param attributes the attributes to be removed

+     * @return the removed attribute, if exists; otherwise <code>null</code>

+     */

+    List<EntryAttribute> remove( EntryAttribute... attributes ) throws NamingException;

+    

+

+    /**

+     * <p>

+     * Removes the attribute with the specified AttributeTypes. 

+     * </p>

+     * <p>

+     * The removed attribute are returned by this method. 

+     * </p>

+     * <p>

+     * If there is no attribute with the specified AttributeTypes,

+     * the return value is <code>null</code>.

+     * </p>

+     *

+     * @param attributes the AttributeTypes to be removed

+     * @return the removed attributes, if any, as a list; otherwise <code>null</code>

+     */

+    List<EntryAttribute> removeAttributes( AttributeType... attributes );

+

+

+    /**

+     * <p>

+     * Put some new attributes using the attributeTypes. 

+     * No value is inserted. 

+     * </p>

+     * <p>

+     * If an existing Attribute is found, it will be replaced by an

+     * empty attribute, and returned to the caller.

+     * </p>

+     * 

+     * @param attributeTypes The AttributeTypes to add.

+     * @return A list of replaced Attributes, of <code>null</code> if no attribute are removed.

+     */

+    List<EntryAttribute> set( AttributeType... attributeTypes );

+

+

+    /**

+     * A clone method to produce a clone of the current object

+     */

+    Entry clone();

+}

diff --git a/old_trunk/core-entry/src/main/java/org/apache/directory/server/core/entry/ServerEntryFactory.java b/old_trunk/core-entry/src/main/java/org/apache/directory/server/core/entry/ServerEntryFactory.java
new file mode 100644
index 0000000..e9fa1d2
--- /dev/null
+++ b/old_trunk/core-entry/src/main/java/org/apache/directory/server/core/entry/ServerEntryFactory.java
@@ -0,0 +1,48 @@
+/*
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *   or more contributor license agreements.  See the NOTICE file
+ *   distributed with this work for additional information
+ *   regarding copyright ownership.  The ASF licenses this file
+ *   to you 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.
+ *
+ */
+package org.apache.directory.server.core.entry;
+
+
+import javax.naming.NamingException;
+
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+
+/**
+ * A factory which produces ServerEntry objects.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public interface ServerEntryFactory
+{
+    /*
+     * NOTE: 
+     * 
+     * May want to add more newEntry() overrides, some with variable arguments 
+     * to create entries in one method call.
+     */
+    
+    /**
+     * Creates a new ServerEntry which has not yet been added to the 
+     * directory.
+     */
+    ServerEntry newEntry( LdapDN dn ) throws NamingException;
+}
diff --git a/old_trunk/core-entry/src/main/java/org/apache/directory/server/core/entry/ServerEntryPropertyEditor.java b/old_trunk/core-entry/src/main/java/org/apache/directory/server/core/entry/ServerEntryPropertyEditor.java
new file mode 100644
index 0000000..0d0a8d0
--- /dev/null
+++ b/old_trunk/core-entry/src/main/java/org/apache/directory/server/core/entry/ServerEntryPropertyEditor.java
@@ -0,0 +1,247 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.entry;
+
+import java.beans.PropertyEditor;
+import java.beans.PropertyEditorSupport;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+
+import org.apache.directory.shared.ldap.ldif.LdifComposer;
+import org.apache.directory.shared.ldap.ldif.LdifComposerImpl;
+import org.apache.directory.shared.ldap.ldif.LdifReader;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.util.MultiMap;
+import org.apache.directory.shared.ldap.util.StringTools;
+
+
+/**
+ * A JavaBeans {@link PropertyEditor} that can convert {@link ServerEntry} to
+ * LDIF string and vice versa. This class is useful when you're going to
+ * configure a {@link org.apache.directory.server.core.DirectoryService} with 3rd party containers such as <a
+ * href="http://www.springframework.org/">Spring Framework</a>.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 582462 $, $Date: 2007-10-06 08:48:35 +0200 (Sat, 06 Oct 2007) $
+ */
+public class ServerEntryPropertyEditor extends PropertyEditorSupport
+{
+
+    /**
+     * Creates a new instance.
+     */
+    public ServerEntryPropertyEditor()
+    {
+        super();
+    }
+
+    /**
+     * Creates a new instance with source object.
+     */
+    public ServerEntryPropertyEditor( Object source )
+    {
+        super( source );
+    }
+
+    /**
+     * Returns LDIF string of {@link Attributes} object.
+     */
+    public String getAsText()
+    {
+        LdifComposer composer = new LdifComposerImpl();
+        MultiMap map = new MultiMap()
+        {
+            // FIXME Stop forking commons-collections.
+            private final org.apache.commons.collections.MultiHashMap map = 
+                new org.apache.commons.collections.MultiHashMap();
+
+            public Object remove( Object arg0, Object arg1 )
+            {
+                return map.remove( arg0, arg1 );
+            }
+
+            public int size()
+            {
+                return map.size();
+            }
+
+            public Object get( Object arg0 )
+            {
+                return map.get( arg0 );
+            }
+
+            public boolean containsValue( Object arg0 )
+            {
+                return map.containsValue( arg0 );
+            }
+
+            public Object put( Object arg0, Object arg1 )
+            {
+                return map.put( arg0, arg1 );
+            }
+
+            public Object remove( Object arg0 )
+            {
+                return map.remove( arg0 );
+            }
+
+            @SuppressWarnings("unchecked")
+            public Collection<Object> values()
+            {
+                return map.values();
+            }
+
+            public boolean isEmpty()
+            {
+                return map.isEmpty();
+            }
+
+            public boolean containsKey( Object key )
+            {
+                return map.containsKey( key );
+            }
+
+            @SuppressWarnings("unchecked")
+            public void putAll( Map arg0 )
+            {
+                map.putAll( arg0 );
+            }
+
+            public void clear()
+            {
+                map.clear();
+            }
+
+            public Set<?> keySet()
+            {
+                return map.keySet();
+            }
+
+            public Set<?> entrySet()
+            {
+                return map.entrySet();
+            }
+        };
+
+        Attributes attrs = (Attributes) getValue();
+        try
+        {
+            NamingEnumeration<? extends Attribute> e = attrs.getAll();
+            while ( e.hasMore() )
+            {
+                Attribute attr = e.next();
+                NamingEnumeration<? extends Object> e2 = attr.getAll();
+                while ( e2.hasMoreElements() )
+                {
+                    Object value = e2.next();
+                    map.put( attr.getID(), value );
+                }
+            }
+
+            return composer.compose( map );
+        }
+        catch ( Exception e )
+        {
+            return null;
+        }
+    }
+
+    /**
+     * Read an entry (without DN)
+     * 
+     * @param text
+     *            The ldif format file
+     * @return An Attributes.
+     */
+    private Attributes readEntry( String text )
+    {
+        StringReader strIn = new StringReader( text );
+        BufferedReader in = new BufferedReader( strIn );
+
+        String line = null;
+        Attributes attributes = new AttributesImpl( true );
+
+        try
+        {
+            while ( ( line = in.readLine() ) != null )
+            {
+                if ( line.length() == 0 )
+                {
+                    continue;
+                }
+
+                String addedLine = line.trim();
+
+                if ( StringTools.isEmpty( addedLine ) )
+                {
+                    continue;
+                }
+
+                Attribute attribute = LdifReader.parseAttributeValue( addedLine );
+                Attribute oldAttribute = attributes.get( attribute.getID() );
+
+                if ( oldAttribute != null )
+                {
+                    try
+                    {
+                        oldAttribute.add( attribute.get() );
+                        attributes.put( oldAttribute );
+                    }
+                    catch (NamingException ne)
+                    {
+                        // Do nothing
+                    }
+                }
+                else
+                {
+                    attributes.put( attribute );
+                }
+            }
+        }
+        catch (IOException ioe)
+        {
+            // Do nothing : we can't reach this point !
+        }
+
+        return attributes;
+    }
+
+    /**
+     * Converts the specified LDIF string into {@link Attributes}.
+     */
+    public void setAsText( String text ) throws IllegalArgumentException
+    {
+        if ( text == null )
+        {
+            text = "";
+        }
+
+        setValue( readEntry( text ) );
+    }
+}
diff --git a/old_trunk/core-entry/src/main/java/org/apache/directory/server/core/entry/ServerEntrySerializer.java b/old_trunk/core-entry/src/main/java/org/apache/directory/server/core/entry/ServerEntrySerializer.java
new file mode 100644
index 0000000..4aedc5c
--- /dev/null
+++ b/old_trunk/core-entry/src/main/java/org/apache/directory/server/core/entry/ServerEntrySerializer.java
@@ -0,0 +1,382 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.entry;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutput;
+import java.io.ObjectOutputStream;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.entry.Value;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.name.LdapDNSerializer;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import jdbm.helper.Serializer;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ServerEntrySerializer implements Serializer
+{
+    private static final long serialVersionUID = 1L;
+
+    /** the logger for this class */
+    private static final Logger LOG = LoggerFactory.getLogger( ServerEntrySerializer.class );
+
+    /**
+     * Speedup for logs
+     */
+    private static final boolean IS_DEBUG = LOG.isDebugEnabled();
+
+    /** The registries reference */
+    private transient Registries registries;
+
+    /** Flag used for ServerStringValue */
+    private static final transient boolean HR_VALUE = true;
+
+    /** Flag used for streamed values */
+    private static final transient boolean STREAMED_VALUE = true;
+
+
+    /**
+     * Creates a new instance of ServerEntrySerializer.
+     *
+     * @param registries The reference to the global registries
+     */
+    public ServerEntrySerializer( Registries registries )
+    {
+        this.registries = registries;
+    }
+
+
+    /**
+     * @see Externalizable#writeExternal(ObjectOutput)
+     * <p>
+     * 
+     * This is the place where we serialize entries, and all theirs
+     * elements. the reason why we don't call the underlying methods
+     * (<code>ServerAttribute.write(), Value.write()</code>) is that we need
+     * access to the registries to read back the values.
+     * <p>
+     * The structure used to store the entry is the following :
+     * <li><b>[DN length]</b> : can be -1 if we don't have a DN, 0 if the 
+     * DN is empty, otherwise contains the DN's length.<p> 
+     * <b>NOTE :</b>This should be unnecessary, as the DN should always exists
+     * <p>
+     * </li>
+     * <li>
+     * <b>DN</b> : The entry's DN. Can be empty (rootDSE=<p>
+     * </li>
+     * We have to store the UPid, and all the values, if any.
+     */
+    public byte[] serialize( Object object ) throws IOException
+    {
+        ServerEntry entry = ( ServerEntry ) object;
+
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        ObjectOutputStream out = new ObjectOutputStream( baos );
+
+        // First, the DN
+        if ( entry.getDn() == null )
+        {
+            // Write an empty DN
+            LdapDNSerializer.serialize( LdapDN.EMPTY_LDAPDN, out );
+        }
+        else
+        {
+            // Write the DN
+            LdapDNSerializer.serialize( entry.getDn(), out );
+        }
+
+        // Then the attributes.
+        out.writeInt( entry.size() );
+
+        // Iterate through the attributes. We store the Attribute
+        // here, to be able to restore it in the readExternal :
+        // we need access to the registries, which are not available
+        // in the ServerAttribute class.
+        for ( EntryAttribute attribute : entry )
+        {
+            // We store the OID, as the AttributeType might have no name
+            out.writeUTF( ( ( ServerAttribute ) attribute ).getAttributeType().getOid() );
+
+            // And store the attribute.
+            // Store the UP id
+            out.writeUTF( attribute.getUpId() );
+
+            // The number of values
+            out.writeInt( attribute.size() );
+
+            for ( Value<?> value : attribute )
+            {
+                try
+                {
+                    serializeValue( value, out );
+                }
+                catch ( NamingException ne )
+                {
+                    // TODO Handle this exception
+                }
+            }
+        }
+
+        // Note : we don't store the ObjectClassAttribute. I has already
+        // been stored as an attribute.
+
+        out.flush();
+
+        if ( IS_DEBUG )
+        {
+            LOG.debug( ">------------------------------------------------" );
+            LOG.debug( "Serialize " + entry );
+        }
+
+        return baos.toByteArray();
+    }
+
+
+    /**
+     * We will write the value and the normalized value, only
+     * if the normalized value is different.
+     * 
+     * The data will be stored following this structure :
+     *
+     *  [is valid]
+     *  [HR flag]
+     *  [Streamed flag]
+     *  [UP value]
+     *  [Norm value] (will be null if normValue == upValue)
+     */
+    private void serializeValue( Value<?> value, ObjectOutput out ) throws IOException, NamingException
+    {
+        out.writeBoolean( value.isValid() );
+
+        if ( value instanceof ServerStringValue )
+        {
+            out.writeBoolean( HR_VALUE );
+            out.writeBoolean( !STREAMED_VALUE );
+            ServerStringValue ssv = ( ServerStringValue ) value;
+
+            if ( ssv.get() == null )
+            {
+                // Write two empty string for UP and normalized
+                out.writeUTF( "" );
+                out.writeUTF( "" );
+            }
+            else
+            {
+                // Save the UP value and the normalized value
+                out.writeUTF( ssv.get() );
+                ssv.normalize();
+                out.writeUTF( ssv.getNormalizedValue() );
+            }
+        }
+        else if ( value instanceof ServerBinaryValue )
+        {
+            out.writeBoolean( !HR_VALUE );
+            out.writeBoolean( !STREAMED_VALUE );
+            ServerBinaryValue sbv = ( ServerBinaryValue ) value;
+
+            if ( sbv.get() == null )
+            {
+                out.writeInt( 0 );
+                out.writeInt( 0 );
+            }
+            else
+            {
+                // Save the UP value and the normalized value if !=
+                out.writeInt( sbv.get().length );
+                out.write( sbv.get() );
+
+                out.writeBoolean( sbv.isSame() );
+
+                if ( !sbv.isSame() )
+                {
+                    sbv.normalize();
+
+                    out.writeInt( sbv.getNormalizedValueReference().length );
+                    out.write( sbv.getNormalizedValueReference() );
+                }
+            }
+        }
+
+        out.flush();
+    }
+
+
+    /**
+     * We will write the value and the normalized value, only
+     * if the normalized value is different.
+     * 
+     * The data will be stored following this structure :
+     *
+     *  [is valid]
+     *  [HR flag]
+     *  [Streamed flag]
+     *  [UP value]
+     *  [Norm value] (will be null if normValue == upValue)
+     */
+    private Value<?> deserializeValue( ObjectInput in, AttributeType attributeType ) throws IOException
+    {
+        boolean isValid = in.readBoolean();
+        boolean isHR = in.readBoolean();
+        boolean isStreamed = in.readBoolean();
+
+        if ( isHR )
+        {
+            if ( !isStreamed )
+            {
+                String value = in.readUTF();
+
+                if ( value.length() == 0 )
+                {
+                    value = null;
+                }
+
+                String normalized = in.readUTF();
+
+                if ( normalized.length() == 0 )
+                {
+                    normalized = null;
+                }
+
+                Value<?> ssv = new ServerStringValue( attributeType, value, normalized, isValid );
+
+                return ssv;
+            }
+            else
+            {
+                return null;
+            }
+        }
+        else
+        {
+            if ( !isStreamed )
+            {
+                int length = in.readInt();
+
+                byte[] value = new byte[length];
+
+                if ( length != 0 )
+                {
+                    in.read( value );
+                }
+
+                byte[] normalized = null;
+                boolean same = in.readBoolean();
+
+                // Now, if the normalized value is different from the wrapped value,
+                // read the normalized value.
+                if ( !same )
+                {
+                    length = in.readInt();
+
+                    normalized = new byte[length];
+                    if ( length != 0 )
+                    {
+                        in.read( normalized );
+                    }
+                }
+                else
+                {
+                    normalized = value;
+                }
+
+                Value<?> sbv = new ServerBinaryValue( attributeType, value, normalized, same, isValid );
+
+                return sbv;
+            }
+            else
+            {
+                return null;
+            }
+        }
+    }
+
+
+    /**
+     *  Deserialize a ServerEntry
+     */
+    public Object deserialize( byte[] bytes ) throws IOException
+    {
+        ObjectInputStream in = new ObjectInputStream( new ByteArrayInputStream( bytes ) );
+
+        try
+        {
+            // First, read the DN
+            LdapDN dn = LdapDNSerializer.deserialize( in );
+
+            // Read the number of attributes
+            int nbAttrs = in.readInt();
+
+            ServerEntry serverEntry = new DefaultServerEntry( registries, dn );
+
+            // Read all the attributes
+            for ( int i = 0; i < nbAttrs; i++ )
+            {
+                // The oid
+                String oid = in.readUTF();
+
+                AttributeType attributeType = registries.getAttributeTypeRegistry().lookup( oid );
+
+                // The UP id
+                String upId = in.readUTF();
+
+                // The number of values
+                int nbValues = in.readInt();
+
+                ServerAttribute serverAttribute = new DefaultServerAttribute( upId, attributeType );
+
+                for ( int j = 0; j < nbValues; j++ )
+                {
+                    Value<?> value = deserializeValue( in, attributeType );
+                    serverAttribute.add( value );
+                }
+
+                serverEntry.put( serverAttribute );
+            }
+
+            if ( IS_DEBUG )
+            {
+                LOG.debug( "<------------------------------------------------" );
+                LOG.debug( "Deserialize " + serverEntry );
+            }
+
+            return serverEntry;
+        }
+        catch ( NamingException ne )
+        {
+            // TODO Handle this exception
+            return null;
+        }
+    }
+}
diff --git a/old_trunk/core-entry/src/main/java/org/apache/directory/server/core/entry/ServerEntryUtils.java b/old_trunk/core-entry/src/main/java/org/apache/directory/server/core/entry/ServerEntryUtils.java
new file mode 100644
index 0000000..c988eae
--- /dev/null
+++ b/old_trunk/core-entry/src/main/java/org/apache/directory/server/core/entry/ServerEntryUtils.java
@@ -0,0 +1,694 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+package org.apache.directory.server.core.entry;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.BasicAttribute;
+import javax.naming.directory.BasicAttributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.InvalidAttributeIdentifierException;
+import javax.naming.directory.ModificationItem;
+import javax.naming.directory.SearchResult;
+
+import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.entry.Modification;
+import org.apache.directory.shared.ldap.entry.ModificationOperation;
+import org.apache.directory.shared.ldap.entry.Value;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.message.ModificationItemImpl;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.util.EmptyEnumeration;
+import org.apache.directory.shared.ldap.util.StringTools;
+
+/**
+ * A helper class used to manipulate Entries, Attributes and Values.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ServerEntryUtils
+{
+    /**
+     * Convert a ServerEntry into a AttributesImpl. The DN is lost
+     * during this conversion, as the Attributes object does not store
+     * this element.
+     *
+     * @return An instance of a AttributesImpl() object
+     */
+    public static Attributes toAttributesImpl( ServerEntry entry )
+    {
+        if ( entry == null )
+        {
+            return null;
+        }
+        
+        Attributes attributes = new AttributesImpl();
+
+        for ( AttributeType attributeType:entry.getAttributeTypes() )
+        {
+            EntryAttribute attr = entry.get( attributeType );
+            
+            // Deal with a special case : an entry without any ObjectClass
+            if ( attributeType.getOid().equals( SchemaConstants.OBJECT_CLASS_AT_OID ) )
+            {
+                if ( attr.size() == 0 )
+                {
+                    // We don't have any objectClass, just dismiss this element
+                    continue;
+                }
+            }
+            
+            Attribute attribute = new AttributeImpl( attributeType.getName() );
+            
+            for ( Value<?> value: attr )
+            {
+                attribute.add( value.get() );
+            }
+            
+            attributes.put( attribute );
+        }
+        
+        return attributes;
+    }
+
+    
+    /**
+     * Convert a BasicAttribute or a AttributeImpl to a ServerAtribute
+     *
+     * @param attribute the BasicAttributes or AttributesImpl instance to convert
+     * @param attributeType
+     * @return An instance of a ServerEntry object
+     * 
+     * @throws InvalidAttributeIdentifierException If we had an incorrect attribute
+     */
+    public static ServerAttribute toServerAttribute( Attribute attribute, AttributeType attributeType )
+    {
+        if ( attribute == null )
+        {
+            return null;
+        }
+        
+        try 
+        {
+            ServerAttribute serverAttribute = new DefaultServerAttribute( attributeType );
+        
+            for ( NamingEnumeration<?> values = attribute.getAll(); values.hasMoreElements(); )
+            {
+                Object value = values.nextElement();
+                
+                if ( serverAttribute.isHR() )
+                {
+                    if ( value instanceof String )
+                    {
+                        serverAttribute.add( (String)value );
+                    }
+                    else if ( value instanceof byte[] )
+                    {
+                        serverAttribute.add( StringTools.utf8ToString( (byte[])value ) );
+                    }
+                    else
+                    {
+                        return null;
+                    }
+                }
+                else
+                {
+                    if ( value instanceof String )
+                    {
+                        serverAttribute.add( StringTools.getBytesUtf8( (String)value ) );
+                    }
+                    else if ( value instanceof byte[] )
+                    {
+                        serverAttribute.add( (byte[])value );
+                    }
+                    else
+                    {
+                        return null;
+                    }
+                }
+            }
+            
+            return serverAttribute;
+        }
+        catch ( NamingException ne )
+        {
+            return null;
+        }
+    }
+    
+
+    /**
+     * Convert a BasicAttributes or a AttributesImpl to a ServerEntry
+     *
+     * @param attributes the BasicAttributes or AttributesImpl instance to convert
+     * @param registries The registries, needed ro build a ServerEntry
+     * @param dn The DN which is needed by the ServerEntry 
+     * @return An instance of a ServerEntry object
+     * 
+     * @throws InvalidAttributeIdentifierException If we get an invalid attribute
+     */
+    public static ServerEntry toServerEntry( Attributes attributes, LdapDN dn, Registries registries ) 
+            throws InvalidAttributeIdentifierException
+    {
+        if ( ( attributes instanceof BasicAttributes ) || ( attributes instanceof AttributesImpl ) )
+        {
+            try 
+            {
+                ServerEntry entry = new DefaultServerEntry( registries, dn );
+    
+                for ( NamingEnumeration<? extends Attribute> attrs = attributes.getAll(); attrs.hasMoreElements(); )
+                {
+                    Attribute attr = attrs.nextElement();
+
+                    String attributeId = attr.getID();
+                    String id = stripOptions( attributeId );
+                    Set<String> options = getOptions( attributeId );
+                    // TODO : handle options.
+                    AttributeType attributeType = registries.getAttributeTypeRegistry().lookup( id );
+                    ServerAttribute serverAttribute = ServerEntryUtils.toServerAttribute( attr, attributeType );
+                    
+                    if ( serverAttribute != null )
+                    {
+                        entry.put( serverAttribute );
+                    }
+                }
+                
+                return entry;
+            }
+            catch ( NamingException ne )
+            {
+                throw new InvalidAttributeIdentifierException( ne.getMessage() );
+            }
+        }
+        else
+        {
+            return null;
+        }
+    }
+
+
+    /**
+     * Convert a ServerEntry into a BasicAttributes. The DN is lost
+     * during this conversion, as the Attributes object does not store
+     * this element.
+     *
+     * @return An instance of a BasicAttributes() object
+     */
+    public static Attributes toBasicAttributes( ServerEntry entry )
+    {
+        Attributes attributes = new BasicAttributes( true );
+
+        for ( AttributeType attributeType:entry.getAttributeTypes() )
+        {
+            Attribute attribute = new BasicAttribute( attributeType.getName(), true );
+            
+            EntryAttribute attr = entry.get( attributeType );
+            
+            for ( Value<?> value:attr )
+            {
+                attribute.add( value );
+            }
+            
+            attributes.put( attribute );
+        }
+        
+        return attributes;
+    }
+    
+    
+    /**
+     * Convert a ServerAttributeEntry into a BasicAttribute.
+     *
+     * @return An instance of a BasicAttribute() object
+     */
+    public static Attribute toBasicAttribute( ServerAttribute attr )
+    {
+        Attribute attribute = new BasicAttribute( attr.getUpId(), false );
+
+        for ( Value<?> value:attr )
+        {
+            attribute.add( value.get() );
+        }
+        
+        return attribute;
+    }
+
+
+    /**
+     * Convert a ServerAttributeEntry into a AttributeImpl.
+     *
+     * @return An instance of a BasicAttribute() object
+     */
+    public static Attribute toAttributeImpl( EntryAttribute attr )
+    {
+        Attribute attribute = new AttributeImpl( attr.getUpId() );
+
+        for ( Value<?> value:attr )
+        {
+            attribute.add( value.get() );
+        }
+        
+        return attribute;
+    }
+
+
+    /**
+     * Gets the target entry as it would look after a modification operation 
+     * was performed on it.
+     * 
+     * @param mod the modification
+     * @param entry the source entry that is modified
+     * @return the resultant entry after the modification has taken place
+     * @throws NamingException if there are problems accessing attributes
+     */
+    public static ServerEntry getTargetEntry( Modification mod, ServerEntry entry, Registries registries ) throws NamingException
+    {
+        ServerEntry targetEntry = ( ServerEntry ) entry.clone();
+        ModificationOperation modOp = mod.getOperation();
+        String id = mod.getAttribute().getId();
+        AttributeType attributeType = registries.getAttributeTypeRegistry().lookup( id );
+        
+        switch ( modOp )
+        {
+            case REPLACE_ATTRIBUTE :
+                targetEntry.put( (ServerAttribute)mod.getAttribute() );
+                break;
+                
+            case REMOVE_ATTRIBUTE :
+                ServerAttribute toBeRemoved = (ServerAttribute)mod.getAttribute();
+
+                if ( toBeRemoved.size() == 0 )
+                {
+                    targetEntry.removeAttributes( id );
+                }
+                else
+                {
+                    EntryAttribute existing = targetEntry.get( id );
+
+                    if ( existing != null )
+                    {
+                        for ( Value<?> value:toBeRemoved )
+                        {
+                            existing.remove( value );
+                        }
+                    }
+                }
+                break;
+                
+            case ADD_ATTRIBUTE :
+                ServerAttribute combined = new DefaultServerAttribute( id, attributeType );
+                ServerAttribute toBeAdded = (ServerAttribute)mod.getAttribute();
+                EntryAttribute existing = entry.get( id );
+
+                if ( existing != null )
+                {
+                    for ( Value<?> value:existing )
+                    {
+                        combined.add( value );
+                    }
+                }
+
+                for ( Value<?> value:toBeAdded )
+                {
+                    combined.add( value );
+                }
+                
+                targetEntry.put( combined );
+                break;
+                
+            default:
+                throw new IllegalStateException( "undefined modification type: " + modOp );
+        }
+
+        return targetEntry;
+    }
+
+
+    /**
+     * Creates a new attribute which contains the values representing the union
+     * of two attributes. If one attribute is null then the resultant attribute
+     * returned is a copy of the non-null attribute. If both are null then we
+     * cannot determine the attribute ID and an {@link IllegalArgumentException}
+     * is raised.
+     * 
+     * @param attr0 the first attribute
+     * @param attr1 the second attribute
+     * @return a new attribute with the union of values from both attribute
+     *         arguments
+     * @throws NamingException if there are problems accessing attribute values
+     */
+    public static ServerAttribute getUnion( ServerAttribute attr0, ServerAttribute attr1 )
+    {
+        if ( attr0 == null && attr1 == null )
+        {
+            throw new IllegalArgumentException( "Cannot figure out attribute ID if both args are null" );
+        }
+        else if ( attr0 == null )
+        {
+            return (ServerAttribute)attr1.clone();
+        }
+        else if ( attr1 == null )
+        {
+            return (ServerAttribute)attr0.clone();
+        }
+        else if ( !attr0.getAttributeType().equals( attr1.getAttributeType() ) )
+        {
+            throw new IllegalArgumentException( "Cannot take union of attributes with different IDs!" );
+        }
+
+        ServerAttribute attr = (ServerAttribute)attr0.clone();
+
+        for ( Value<?> value:attr1 )
+        {
+            attr.add( value );
+        }
+
+        return attr;
+    }
+    
+    
+    public static ModificationItemImpl toModificationItemImpl( Modification modification )
+    {
+        ModificationItemImpl modificationItem = new ModificationItemImpl( 
+            modification.getOperation().getValue(),
+            toAttributeImpl( (ServerAttribute)modification.getAttribute() ) ); 
+        
+        return modificationItem;
+        
+    }
+
+
+    public static Modification toModification( ModificationItemImpl modificationImpl, AttributeType attributeType ) 
+    {
+        Modification modification = new ServerModification( 
+            modificationImpl.getModificationOp(),
+            ServerEntryUtils.toServerAttribute( modificationImpl.getAttribute(), attributeType ) ); 
+        
+        return modification;
+        
+    }
+
+
+    public static List<ModificationItemImpl> toModificationItemImpl( List<Modification> modifications )
+    {
+        if ( modifications != null )
+        {
+            List<ModificationItemImpl> modificationItems = new ArrayList<ModificationItemImpl>();
+
+            for ( Modification modification: modifications )
+            {
+                modificationItems.add( toModificationItemImpl( modification ) );
+            }
+        
+            return modificationItems;
+        }
+        else
+        {
+            return null;
+        }
+    }
+    
+    
+    public static List<Modification> toServerModification( List<ModificationItemImpl> modificationImpls, 
+        AttributeTypeRegistry atRegistry ) throws NamingException
+    {
+        if ( modificationImpls != null )
+        {
+            List<Modification> modifications = new ArrayList<Modification>();
+
+            for ( ModificationItemImpl modificationImpl: modificationImpls )
+            {
+                AttributeType attributeType = atRegistry.lookup( modificationImpl.getAttribute().getID() );
+                modifications.add( toModification( modificationImpl, attributeType ) );
+            }
+        
+            return modifications;
+        }
+        else
+        {
+            return null;
+        }
+    }
+    
+    
+    public static List<Modification> toServerModification( ModificationItem[] modifications, 
+        AttributeTypeRegistry atRegistry ) throws NamingException
+    {
+        if ( modifications != null )
+        {
+            List<Modification> modificationsList = new ArrayList<Modification>();
+    
+            for ( ModificationItem modification: modifications )
+            {
+                String attributeId = modification.getAttribute().getID();
+                String id = stripOptions( attributeId );
+                Set<String> options = getOptions( attributeId );
+
+                // -------------------------------------------------------------------
+                // DIRSERVER-646 Fix: Replacing an unknown attribute with no values 
+                // (deletion) causes an error
+                // -------------------------------------------------------------------
+                
+                // TODO - after removing JNDI we need to make the server handle 
+                // this in the codec
+                
+                if ( ! atRegistry.hasAttributeType( id ) 
+                     && modification.getAttribute().size() == 0 
+                     && modification.getModificationOp() == DirContext.REPLACE_ATTRIBUTE )
+                {
+                    continue;
+                }
+
+                // -------------------------------------------------------------------
+                // END DIRSERVER-646 Fix
+                // -------------------------------------------------------------------
+                
+                
+                // TODO : handle options
+                AttributeType attributeType = atRegistry.lookup( id );
+                modificationsList.add( toModification( (ModificationItemImpl)modification, attributeType ) );
+            }
+        
+            return modificationsList;
+        }
+        else
+        {
+            return null;
+        }
+    }
+
+
+    /**
+     * Utility method to extract a modification item from an array of modifications.
+     * 
+     * @param mods the array of ModificationItems to extract the Attribute from.
+     * @param type the attributeType spec of the Attribute to extract
+     * @return the modification item on the attributeType specified
+     */
+    public static final Modification getModificationItem( List<Modification> mods, AttributeType type )
+    {
+        for ( Modification modification:mods )
+        {
+            ServerAttribute attribute = (ServerAttribute)modification.getAttribute();
+            
+            if ( attribute.getAttributeType() == type )
+            {
+                return modification;
+            }
+        }
+        
+        return null;
+    }
+    
+    
+    /**
+     * Utility method to extract an attribute from a list of modifications.
+     * 
+     * @param mods the list of ModificationItems to extract the Attribute from.
+     * @param type the attributeType spec of the Attribute to extract
+     * @return the extract Attribute or null if no such attribute exists
+     */
+    public static ServerAttribute getAttribute( List<Modification> mods, AttributeType type )
+    {
+        Modification mod = getModificationItem( mods, type );
+        
+        if ( mod != null )
+        {
+            return (ServerAttribute)mod.getAttribute();
+        }
+        
+        return null;
+    }
+    
+    
+    /**
+     * Encapsulate a ServerSearchResult enumeration into a SearchResult enumeration
+     * @param result The ServerSearchResult enumeration
+     * @return A SearchResultEnumeration
+     */
+    public static NamingEnumeration<SearchResult> toSearchResultEnum( final NamingEnumeration<ServerSearchResult> result )
+    {
+        if ( result instanceof EmptyEnumeration<?> )
+        {
+            return new EmptyEnumeration<SearchResult>();
+        }
+        
+        return new NamingEnumeration<SearchResult> ()
+        {
+            public void close() throws NamingException
+            {
+                result.close();
+            }
+
+
+            /**
+             * @see javax.naming.NamingEnumeration#hasMore()
+             */
+            public boolean hasMore() throws NamingException
+            {
+                return result.hasMore();
+            }
+
+
+            /**
+             * @see javax.naming.NamingEnumeration#next()
+             */
+            public SearchResult next() throws NamingException
+            {
+                ServerSearchResult rec = result.next();
+                
+                SearchResult searchResult = new SearchResult( 
+                        rec.getDn().getUpName(), 
+                        rec.getObject(), 
+                        toAttributesImpl( rec.getServerEntry() ), 
+                        rec.isRelative() );
+                
+                return searchResult;
+            }
+            
+            
+            /**
+             * @see java.util.Enumeration#hasMoreElements()
+             */
+            public boolean hasMoreElements()
+            {
+                return result.hasMoreElements();
+            }
+
+
+            /**
+             * @see java.util.Enumeration#nextElement()
+             */
+            public SearchResult nextElement()
+            {
+                try
+                {
+                    ServerSearchResult rec = result.next();
+    
+                    SearchResult searchResult = new SearchResult( 
+                            rec.getDn().getUpName(), 
+                            rec.getObject(), 
+                            toAttributesImpl( rec.getServerEntry() ), 
+                            rec.isRelative() );
+                    
+                    return searchResult;
+                }
+                catch ( NamingException ne )
+                {
+                    NoSuchElementException nsee = 
+                        new NoSuchElementException( "Encountered NamingException on underlying enumeration." );
+                    nsee.initCause( ne );
+                    throw nsee;
+                }
+            }
+        };
+    }
+    
+    
+    /**
+     * Remove the options from the attributeType, and returns the ID.
+     * 
+     * RFC 4512 :
+     * attributedescription = attributetype options
+     * attributetype = oid
+     * options = *( SEMI option )
+     * option = 1*keychar
+     */
+    private static String stripOptions( String attributeId )
+    {
+        int optionsPos = attributeId.indexOf( ";" ); 
+        
+        if ( optionsPos != -1 )
+        {
+            return attributeId.substring( 0, optionsPos );
+        }
+        else
+        {
+            return attributeId;
+        }
+    }
+    
+    /**
+     * Get the options from the attributeType.
+     * 
+     * For instance, given :
+     * jpegphoto;binary;lang=jp
+     * 
+     * your get back a set containing { "binary", "lang=jp" }
+     */
+    private static Set<String> getOptions( String attributeId )
+    {
+        int optionsPos = attributeId.indexOf( ";" ); 
+
+        if ( optionsPos != -1 )
+        {
+            Set<String> options = new HashSet<String>();
+            
+            String[] res = attributeId.substring( optionsPos + 1 ).split( ";" );
+            
+            for ( String option:res )
+            {
+                if ( !StringTools.isEmpty( option ) )
+                {
+                    options.add( option );
+                }
+            }
+            
+            return options;
+        }
+        else
+        {
+            return null;
+        }
+    }
+}
diff --git a/old_trunk/core-entry/src/main/java/org/apache/directory/server/core/entry/ServerModification.java b/old_trunk/core-entry/src/main/java/org/apache/directory/server/core/entry/ServerModification.java
new file mode 100644
index 0000000..61bc2aa
--- /dev/null
+++ b/old_trunk/core-entry/src/main/java/org/apache/directory/server/core/entry/ServerModification.java
@@ -0,0 +1,167 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.entry;
+
+import javax.naming.directory.DirContext;
+
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.entry.Modification;
+import org.apache.directory.shared.ldap.entry.ModificationOperation;
+
+/**
+ * An internal implementation for a ModificationItem. The name has been
+ * chosen so that it does not conflict with @see ModificationItem
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ServerModification implements Modification
+{
+    /** The modification operation */
+    private ModificationOperation operation;
+    
+    /** The attribute which contains the modification */
+    private EntryAttribute attribute;
+ 
+    
+    public ServerModification( ModificationOperation operation, EntryAttribute attribute )
+    {
+        this.operation = operation;
+        this.attribute = attribute;
+    }
+    
+    
+    public ServerModification( int operation, EntryAttribute attribute )
+    {
+        setOperation( operation );
+        this.attribute = attribute;
+    }
+    
+    
+    /**
+     *  @return the operation
+     */
+    public ModificationOperation getOperation()
+    {
+        return operation;
+    }
+    
+    
+    /**
+     * Store the modification operation
+     *
+     * @param operation The DirContext value to assign
+     */
+    public void setOperation( int operation )
+    {
+        switch ( operation )
+        {
+            case DirContext.ADD_ATTRIBUTE :
+                this.operation = ModificationOperation.ADD_ATTRIBUTE;
+                break;
+
+            case DirContext.REPLACE_ATTRIBUTE :
+                this.operation = ModificationOperation.REPLACE_ATTRIBUTE;
+                break;
+            
+            case DirContext.REMOVE_ATTRIBUTE :
+                this.operation = ModificationOperation.REMOVE_ATTRIBUTE;
+                break;
+        }
+    }
+
+    
+    /**
+     * Store the modification operation
+     *
+     * @param operation The DirContext value to assign
+     */
+    public void setOperation( ModificationOperation operation )
+    {
+        this.operation = operation;
+    }
+        
+    
+    /**
+     * @return the attribute containing the modifications
+     */
+    public EntryAttribute getAttribute()
+    {
+        return attribute;
+    }
+    
+    
+    /**
+     * Set the attribute's modification
+     *
+     * @param attribute The modified attribute 
+     */
+    public void setAttribute( EntryAttribute attribute )
+    {
+        this.attribute = (ServerAttribute)attribute;
+    }
+    
+    
+    /**
+     * Compute the modification @see Object#hashCode
+     * @return the instance's hash code 
+     */
+    public int hashCode()
+    {
+        int h = 37;
+        
+        h += h*17 + operation.getValue();
+        h += h*17 + attribute.hashCode();
+        
+        return h;
+    }
+    
+    
+    public ServerModification clone()
+    {
+        try
+        {
+            ServerModification clone = (ServerModification)super.clone();
+            
+            clone.attribute = (ServerAttribute)this.attribute.clone();
+            return clone;
+        }
+        catch ( CloneNotSupportedException cnse )
+        {
+            return null;
+        }
+    }
+    
+    /**
+     * @see Object#toString()
+     */
+    public String toString()
+    {
+        StringBuilder sb = new StringBuilder();
+        
+        sb.append( "Modification: " ).
+            append( operation ).
+            append( "\n" ).
+            append( ", attribute : " ).
+            append( attribute );
+        
+        return sb.toString();
+    }
+}
diff --git a/old_trunk/core-entry/src/main/java/org/apache/directory/server/core/entry/ServerSearchResult.java b/old_trunk/core-entry/src/main/java/org/apache/directory/server/core/entry/ServerSearchResult.java
new file mode 100644
index 0000000..027cf5d
--- /dev/null
+++ b/old_trunk/core-entry/src/main/java/org/apache/directory/server/core/entry/ServerSearchResult.java
@@ -0,0 +1,137 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.entry;
+
+
+
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+/**
+ * Creates a wrapper around a SearchResult object so that we can use the LdapDN
+ * instead of parser it over and over
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ServerSearchResult
+{
+    /** Distinguished name for this result */
+    private LdapDN dn;
+    
+    /** The associated entry */
+    private ServerEntry serverEntry;
+    
+    /** Tells if the name is relative to the target context */
+    private boolean isRelative;
+    
+    /** The bound object */
+    private Object object;
+    
+
+    public ServerSearchResult( LdapDN dn, Object obj, ServerEntry serverEntry )
+    {
+        this.dn = dn;
+        this.serverEntry = serverEntry;
+        this.serverEntry.setDn( dn );
+    }
+
+
+    public ServerSearchResult( LdapDN dn, Object obj, ServerEntry serverEntry, boolean isRelative )
+    {
+        this.dn = dn;
+        this.serverEntry = serverEntry;
+        this.serverEntry.setDn( dn );
+        this.isRelative = isRelative;
+    }
+
+
+    public ServerSearchResult( LdapDN dn, String className, Object obj, ServerEntry serverEntry )
+    {
+        this.dn = dn;
+        this.serverEntry = serverEntry;
+        this.serverEntry.setDn( dn );
+    }
+
+
+    public ServerSearchResult( LdapDN dn, String className, Object obj, ServerEntry serverEntry, boolean isRelative ) 
+    {
+        this.dn = dn;
+        this.serverEntry = serverEntry;
+        this.serverEntry.setDn( dn );
+    }
+
+
+    /**
+     * @return The result DN
+     */
+    public LdapDN getDn()
+    {
+        return dn;
+    }
+    
+    
+    /**
+     * @return The entry
+     */
+    public ServerEntry getServerEntry()
+    {
+        return serverEntry;
+    }
+
+
+    public boolean isRelative() 
+    {
+        return isRelative;
+    }
+
+
+    public void setRelative( boolean isRelative ) 
+    {
+        this.isRelative = isRelative;
+    }
+
+
+    public void setServerEntry( ServerEntry serverEntry ) 
+    {
+        this.serverEntry = serverEntry;
+    }
+
+
+    public Object getObject() 
+    {
+        return object;
+    }
+
+
+    public void setObject( Object object ) 
+    {
+        this.object = object;
+    }
+    
+    
+    /**
+     * @see Object#toString()
+     */
+    public String toString()
+    {
+        String name = (dn == null ? "null" : ( dn == LdapDN.EMPTY_LDAPDN ? "\"\"" : dn.getUpName() ) );
+        return "ServerSearchResult : " + name + "\n" + serverEntry;
+    }
+}
diff --git a/old_trunk/core-entry/src/main/java/org/apache/directory/server/core/entry/ServerStringValue.java b/old_trunk/core-entry/src/main/java/org/apache/directory/server/core/entry/ServerStringValue.java
new file mode 100644
index 0000000..56d09d2
--- /dev/null
+++ b/old_trunk/core-entry/src/main/java/org/apache/directory/server/core/entry/ServerStringValue.java
@@ -0,0 +1,636 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements.  See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership.  The ASF licenses this file

+ * to you 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.

+ */

+package org.apache.directory.server.core.entry;

+

+

+import java.io.Externalizable;

+import java.io.IOException;

+import java.io.ObjectInput;

+import java.io.ObjectOutput;

+import java.util.Comparator;

+

+import javax.naming.NamingException;

+

+import org.apache.directory.shared.ldap.NotImplementedException;

+import org.apache.directory.shared.ldap.entry.Value;

+import org.apache.directory.shared.ldap.entry.client.ClientStringValue;

+import org.apache.directory.shared.ldap.schema.AttributeType;

+import org.apache.directory.shared.ldap.schema.MatchingRule;

+import org.apache.directory.shared.ldap.schema.Normalizer;

+import org.slf4j.Logger;

+import org.slf4j.LoggerFactory;

+

+

+/**

+ * A server side schema aware wrapper around a String attribute value.

+ * This value wrapper uses schema information to syntax check values,

+ * and to compare them for equality and ordering.  It caches results

+ * and invalidates them when the wrapped value changes.

+ *

+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>

+ * @version $Rev$, $Date$

+ */

+public class ServerStringValue extends ClientStringValue

+{

+    /** Used for serialization */

+    private static final long serialVersionUID = 2L;

+    

+    /** logger for reporting errors that might not be handled properly upstream */

+    private static final Logger LOG = LoggerFactory.getLogger( ServerStringValue.class );

+

+    /** reference to the attributeType which is not serialized */

+    private transient AttributeType attributeType;

+

+

+    // -----------------------------------------------------------------------

+    // utility methods

+    // -----------------------------------------------------------------------

+    /**

+     * Utility method to get some logs if an assert fails

+     */

+    protected String logAssert( String message )

+    {

+        LOG.error(  message );

+        return message;

+    }

+

+    

+    /**

+     *  Check the attributeType member. It should not be null, 

+     *  and it should contains a syntax.

+     */

+    protected String checkAttributeType( AttributeType attributeType )

+    {

+        try

+        {

+            if ( attributeType == null )

+            {

+                return "The AttributeType parameter should not be null";

+            }

+            

+            if ( attributeType.getSyntax() == null )

+            {

+                return "There is no Syntax associated with this attributeType";

+            }

+

+            return null;

+        }

+        catch ( NamingException ne )

+        {

+            return "This AttributeType is incorrect";

+        }

+    }

+

+    

+    // -----------------------------------------------------------------------

+    // Constructors

+    // -----------------------------------------------------------------------

+    /**

+     * Creates a ServerStringValue without an initial wrapped value.

+     *

+     * @param attributeType the schema type associated with this ServerStringValue

+     */

+    public ServerStringValue( AttributeType attributeType )

+    {

+        super();

+        if ( attributeType == null )

+        {

+            throw new IllegalArgumentException( "The AttributeType parameter should not be null" );

+        }

+

+        try

+        {

+            if ( attributeType.getSyntax() == null )

+            {

+                throw new IllegalArgumentException( "There is no Syntax associated with this attributeType" );

+            }

+

+            if ( ! attributeType.getSyntax().isHumanReadable() )

+            {

+                LOG.warn( "Treating a value of a binary attribute {} as a String: " +

+                        "\nthis could cause data corruption!", attributeType.getName() );

+            }

+        }

+        catch( NamingException e )

+        {

+            LOG.error( "Failed to resolve syntax for attributeType {}", attributeType, e );

+        }

+

+        this.attributeType = attributeType;

+    }

+

+

+    /**

+     * Creates a ServerStringValue with an initial wrapped String value.

+     *

+     * @param attributeType the schema type associated with this ServerStringValue

+     * @param wrapped the value to wrap which can be null

+     */

+    public ServerStringValue( AttributeType attributeType, String wrapped )

+    {

+        this( attributeType );

+        this.wrapped = wrapped;

+    }

+

+

+    /**

+     * Creates a ServerStringValue with an initial wrapped String value and

+     * a normalized value.

+     *

+     * @param attributeType the schema type associated with this ServerStringValue

+     * @param wrapped the value to wrap which can be null

+     * @param normalizedValue the normalized value

+     */

+    /** No protection */ ServerStringValue( AttributeType attributeType, String wrapped, String normalizedValue, boolean valid )

+    {

+        super( wrapped );

+        this.normalized = true;

+        this.attributeType = attributeType;

+        this.normalizedValue = normalizedValue;

+        this.valid = valid;

+    }

+

+

+    // -----------------------------------------------------------------------

+    // Value<String> Methods, overloaded

+    // -----------------------------------------------------------------------

+    /**

+     * @return a copy of the current value

+     */

+    public ServerStringValue clone()

+    {

+        ServerStringValue clone = (ServerStringValue)super.clone();

+        

+        return clone;

+    }

+    

+    

+

+

+    // -----------------------------------------------------------------------

+    // ServerValue<String> Methods

+    // -----------------------------------------------------------------------

+    /**

+     * Compute the normalized (canonical) representation for the wrapped string.

+     * If the wrapped String is null, the normalized form will be null too.  

+     *

+     * @throws NamingException if the value cannot be properly normalized

+     */

+    public void normalize() throws NamingException

+    {

+        // If the value is already normalized, get out.

+        if ( normalized )

+        {

+            return;

+        }

+        

+        Normalizer normalizer = getNormalizer();

+

+        if ( normalizer == null )

+        {

+            normalizedValue = wrapped;

+        }

+        else

+        {

+            normalizedValue = ( String ) normalizer.normalize( wrapped );

+        }

+

+        normalized = true;

+    }

+    

+

+    /**

+     * Gets the normalized (canonical) representation for the wrapped string.

+     * If the wrapped String is null, null is returned, otherwise the normalized

+     * form is returned.  If no the normalizedValue is null, then this method

+     * will attempt to generate it from the wrapped value: repeated calls to

+     * this method do not unnecessarily normalize the wrapped value.  Only changes

+     * to the wrapped value result in attempts to normalize the wrapped value.

+     *

+     * @return gets the normalized value

+     * @throws NamingException if the value cannot be properly normalized

+     */

+    public String getNormalizedValue() 

+    {

+        if ( isNull() )

+        {

+            normalized = true;

+            return null;

+        }

+

+        if ( !normalized )

+        {

+            try

+            {

+                normalize();

+            }

+            catch ( NamingException ne )

+            {

+                String message = "Cannot normalize the value :" + ne.getMessage();

+                LOG.warn( message );

+                normalized = false;

+            }

+        }

+

+        return normalizedValue;

+    }

+

+

+    /**

+     * Uses the syntaxChecker associated with the attributeType to check if the

+     * value is valid.  Repeated calls to this method do not attempt to re-check

+     * the syntax of the wrapped value every time if the wrapped value does not

+     * change. Syntax checks only result on the first check, and when the wrapped

+     * value changes.

+     *

+     * @see Value#isValid()

+     */

+    public final boolean isValid()

+    {

+        if ( valid != null )

+        {

+            return valid;

+        }

+

+        try

+        {

+            valid = attributeType.getSyntax().getSyntaxChecker().isValidSyntax( get() );

+        }

+        catch ( NamingException ne )

+        {

+            String message = "Cannot check the syntax : " + ne.getMessage();

+            LOG.error( message );

+            valid = false;

+        }

+        

+        return valid;

+    }

+

+

+    /**

+     * @see Value#compareTo(Value)

+     * @throws IllegalStateException on failures to extract the comparator, or the

+     * normalizers needed to perform the required comparisons based on the schema

+     */

+    public int compareTo( Value<String> value )

+    {

+        if ( isNull() )

+        {

+            if ( ( value == null ) || value.isNull() )

+            {

+                return 0;

+            }

+            else

+            {

+                return -1;

+            }

+        }

+        else if ( ( value == null ) || value.isNull() )

+        {

+            return 1;

+        }

+

+        if ( value instanceof ServerStringValue )

+        {

+            ServerStringValue stringValue = ( ServerStringValue ) value;

+            

+            // Normalizes the compared value

+            try

+            {

+                stringValue.normalize();

+            }

+            catch ( NamingException ne )

+            {

+                String message = "Cannot normalize the wrapped value " + stringValue; 

+                LOG.error( message );

+            }

+            

+            // Normalizes the value

+            try

+            {

+                normalize();

+            }

+            catch ( NamingException ne )

+            {

+                String message = "Cannot normalize the wrapped value " + this;

+                LOG.error( message );

+            }

+

+            try

+            {

+                //noinspection unchecked

+                return getComparator().compare( getNormalizedValue(), stringValue.getNormalizedValue() );

+            }

+            catch ( NamingException e )

+            {

+                String msg = "Failed to compare normalized values for " + this + " and " + value;

+                LOG.error( msg, e );

+                throw new IllegalStateException( msg, e );

+            }

+        }

+

+        String message = "I don't know what to do if value is not a ServerStringValue";

+        LOG.error( message );

+        throw new NotImplementedException( message );

+    }

+

+

+    /**

+     * Get the associated AttributeType

+     * @return The AttributeType

+     */

+    public AttributeType getAttributeType()

+    {

+        return attributeType;

+    }

+

+

+    /**

+     * Check if the value is stored into an instance of the given 

+     * AttributeType, or one of its ascendant.

+     * 

+     * For instance, if the Value is associated with a CommonName,

+     * checking for Name will match.

+     * 

+     * @param attributeType The AttributeType we are looking at

+     * @return <code>true</code> if the value is associated with the given

+     * attributeType or one of its ascendant

+     */

+    public boolean instanceOf( AttributeType attributeType ) throws NamingException

+    {

+        if ( this.attributeType.equals( attributeType ) )

+        {

+            return true;

+        }

+

+        return this.attributeType.isDescentantOf( attributeType );

+    }

+

+

+    // -----------------------------------------------------------------------

+    // Object Methods

+    // -----------------------------------------------------------------------

+    /**

+     * Checks to see if this ServerStringValue equals the supplied object.

+     *

+     * This equals implementation overrides the StringValue implementation which

+     * is not schema aware.

+     * 

+     * Two ServerStringValues are equal if they have the same AttributeType,

+     * they are both null, their value are equal or their normalized value 

+     * are equal. If the AttributeType has a comparator, we use it to

+     * compare both values.

+     * @throws IllegalStateException on failures to extract the comparator, or the

+     * normalizers needed to perform the required comparisons based on the schema

+     */

+    public boolean equals( Object obj )

+    {

+        if ( this == obj )

+        {

+            return true;

+        }

+        

+        if ( ! ( obj instanceof ServerStringValue ) )

+        {

+            return false;

+        }

+

+        ServerStringValue other = ( ServerStringValue ) obj;

+        

+        if ( !attributeType.equals( other.attributeType ) )

+        {

+            return false;

+        }

+        

+        if ( isNull() )

+        {

+            return other.isNull();

+        }

+

+        // Shortcut : compare the values without normalization

+        // If they are equal, we may avoid a normalization.

+        // Note : if two values are equal, then their normalized

+        // value are equal too if their attributeType are equal. 

+        if ( get().equals( other.get() ) )

+        {

+            return true;

+        }

+        else 

+        {

+            try

+            {

+                Comparator<String> comparator = getComparator();

+

+                // Compare normalized values

+                if ( comparator == null )

+                {

+                    return getNormalizedValue().equals( other.getNormalizedValue() );

+                }

+                else

+                {

+                    return comparator.compare( getNormalizedValue(), other.getNormalizedValue() ) == 0;

+                }

+            }

+            catch ( NamingException ne )

+            {

+                return false;

+            }

+        }

+    }

+

+

+    // -----------------------------------------------------------------------

+    // Private Helper Methods (might be put into abstract base class)

+    // -----------------------------------------------------------------------

+

+

+    /**

+     * Find a matchingRule to use for normalization and comparison.  If an equality

+     * matchingRule cannot be found it checks to see if other matchingRules are

+     * available: SUBSTR, and ORDERING.  If a matchingRule cannot be found null is

+     * returned.

+     *

+     * @return a matchingRule or null if one cannot be found for the attributeType

+     * @throws NamingException if resolution of schema entities fail

+     */

+    private MatchingRule getMatchingRule() throws NamingException

+    {

+        MatchingRule mr = attributeType.getEquality();

+

+        if ( mr == null )

+        {

+            mr = attributeType.getOrdering();

+        }

+

+        if ( mr == null )

+        {

+            mr = attributeType.getSubstr();

+        }

+

+        return mr;

+    }

+

+

+    /**

+     * Gets a normalizer using getMatchingRule() to resolve the matchingRule

+     * that the normalizer is extracted from.

+     *

+     * @return a normalizer associated with the attributeType or null if one cannot be found

+     * @throws NamingException if resolution of schema entities fail

+     */

+    private Normalizer getNormalizer() throws NamingException

+    {

+        MatchingRule mr = getMatchingRule();

+

+        if ( mr == null )

+        {

+            return null;

+        }

+

+        return mr.getNormalizer();

+    }

+

+

+    /**

+     * Implement the hashCode method.

+     * 

+     * @see Object#hashCode()

+     * @throws IllegalStateException on failures to extract the comparator, or the

+     * normalizers needed to perform the required comparisons based on the schema

+     * @return the instance's hash code 

+     */

+    public int hashCode()

+    {

+        // return the OID hashcode if the value is null. 

+        if ( isNull() )

+        {

+            return attributeType.getOid().hashCode();

+        }

+

+        // If the normalized value is null, will default to wrapped

+        // which cannot be null at this point.

+        int h = 17;

+        

+        String normalized = getNormalizedValue();

+        

+        if ( normalized != null )

+        {

+            h = h*37 + normalized.hashCode();

+        }

+        

+        // Add the OID hashcode

+        h = h*37 + attributeType.getOid().hashCode();

+        

+        return h;

+    }

+

+    

+    /**

+     * Gets a comparator using getMatchingRule() to resolve the matching

+     * that the comparator is extracted from.

+     *

+     * @return a comparator associated with the attributeType or null if one cannot be found

+     * @throws NamingException if resolution of schema entities fail

+     */

+    private Comparator getComparator() throws NamingException

+    {

+        MatchingRule mr = getMatchingRule();

+

+        if ( mr == null )

+        {

+            return null;

+        }

+

+        return mr.getComparator();

+    }

+    

+    

+    /**

+     * @see Externalizable#writeExternal(ObjectOutput)

+     * 

+     * We will write the value and the normalized value, only

+     * if the normalized value is different.

+     * 

+     * The data will be stored following this structure :

+     * 

+     *  [UP value]

+     *  [Norm value] (will be null if normValue == upValue)

+     */

+    public void writeExternal( ObjectOutput out ) throws IOException

+    {

+        if ( get() != null )

+        {

+            out.writeUTF( get() );

+            

+            try

+            {

+                normalize();

+            }

+            catch ( NamingException ne )

+            {

+                normalizedValue = null;

+            }

+            

+            if ( get().equals( normalizedValue ) )

+            {

+                // If the normalized value is equal to the UP value,

+                // don't save it

+                out.writeUTF( "" );

+            }

+            else

+            {

+                out.writeUTF( normalizedValue );

+            }

+        }

+        

+        out.flush();

+    }

+

+    

+    /**

+     * @see Externalizable#readExternal(ObjectInput)

+     */

+    public void readExternal( ObjectInput in ) throws IOException, ClassNotFoundException

+    {

+        if ( in.available() == 0 )

+        {

+            set( null );

+            normalizedValue = null;

+        }

+        else

+        {

+            String wrapped = in.readUTF();

+            

+            set( wrapped );

+            

+            normalizedValue = in.readUTF();

+            

+            if ( ( normalizedValue.length() == 0 ) &&  ( wrapped.length() != 0 ) )

+            {

+                // In this case, the normalized value is equal to the UP value

+                normalizedValue = wrapped;

+                setNormalized( true );

+            }

+            else

+            {

+                setNormalized( false );

+            }

+        }

+    }

+}

diff --git a/old_trunk/core-entry/src/main/java/org/apache/directory/server/core/entry/TestServerEntryUtils.java b/old_trunk/core-entry/src/main/java/org/apache/directory/server/core/entry/TestServerEntryUtils.java
new file mode 100644
index 0000000..769c1ea
--- /dev/null
+++ b/old_trunk/core-entry/src/main/java/org/apache/directory/server/core/entry/TestServerEntryUtils.java
@@ -0,0 +1,402 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.entry;
+
+import java.util.Comparator;
+
+import javax.naming.NamingException;
+import javax.naming.directory.InvalidAttributeValueException;
+
+import org.apache.directory.shared.ldap.schema.AbstractAttributeType;
+import org.apache.directory.shared.ldap.schema.AbstractMatchingRule;
+import org.apache.directory.shared.ldap.schema.AbstractSyntax;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.schema.ByteArrayComparator;
+import org.apache.directory.shared.ldap.schema.DeepTrimToLowerNormalizer;
+import org.apache.directory.shared.ldap.schema.MatchingRule;
+import org.apache.directory.shared.ldap.schema.Normalizer;
+import org.apache.directory.shared.ldap.schema.Syntax;
+import org.apache.directory.shared.ldap.schema.syntax.SyntaxChecker;
+import org.apache.directory.shared.ldap.util.StringTools;
+
+/**
+ * Some common declaration used by the serverEntry tests.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class TestServerEntryUtils
+{
+    /**
+     * A local Syntax class for tests
+     */
+    static class AT extends AbstractAttributeType
+    {
+        private static final long serialVersionUID = 0L;
+        AttributeType superior;
+        Syntax syntax;
+        MatchingRule equality;
+        MatchingRule ordering;
+        MatchingRule substr;
+
+        protected AT( String oid )
+        {
+            super( oid );
+        }
+
+        public AttributeType getSuperior() throws NamingException
+        {
+            return superior;
+        }
+
+
+        public Syntax getSyntax() throws NamingException
+        {
+            return syntax;
+        }
+
+
+        public MatchingRule getEquality() throws NamingException
+        {
+            return equality;
+        }
+
+
+        public MatchingRule getOrdering() throws NamingException
+        {
+            return ordering;
+        }
+
+
+        public MatchingRule getSubstr() throws NamingException
+        {
+            return substr;
+        }
+
+
+        public void setSuperior( AttributeType superior )
+        {
+            this.superior = superior;
+        }
+
+
+        public void setSyntax( Syntax syntax )
+        {
+            this.syntax = syntax;
+        }
+
+
+        public void setEquality( MatchingRule equality )
+        {
+            this.equality = equality;
+        }
+
+
+        public void setOrdering( MatchingRule ordering )
+        {
+            this.ordering = ordering;
+        }
+
+
+        public void setSubstr( MatchingRule substr )
+        {
+            this.substr = substr;
+        }
+    }
+
+    /**
+     * A local MatchingRule class for tests
+     */
+    static class MR extends AbstractMatchingRule
+    {
+        private static final long serialVersionUID = 0L;
+        Syntax syntax;
+        Comparator comparator;
+        Normalizer normalizer;
+
+        protected MR( String oid )
+        {
+            super( oid );
+        }
+
+        public Syntax getSyntax() throws NamingException
+        {
+            return syntax;
+        }
+
+        public Comparator getComparator() throws NamingException
+        {
+            return comparator;
+        }
+
+
+        public Normalizer getNormalizer() throws NamingException
+        {
+            return normalizer;
+        }
+
+
+        public void setSyntax( Syntax syntax )
+        {
+            this.syntax = syntax;
+        }
+
+
+        public void setComparator( Comparator<?> comparator )
+        {
+            this.comparator = comparator;
+        }
+
+
+        public void setNormalizer( Normalizer normalizer )
+        {
+            this.normalizer = normalizer;
+        }
+    }
+
+
+    /**
+     * A local Syntax class used for the tests
+     */
+    static class S extends AbstractSyntax
+    {
+        private static final long serialVersionUID = 0L;
+        SyntaxChecker checker;
+
+        public S( String oid, boolean humanReadible )
+        {
+            super( oid, humanReadible );
+        }
+
+        public void setSyntaxChecker( SyntaxChecker checker )
+        {
+            this.checker = checker;
+        }
+
+        public SyntaxChecker getSyntaxChecker() throws NamingException
+        {
+            return checker;
+        }
+    }
+
+    /* no protection*/ static AttributeType getCaseIgnoringAttributeNoNumbersType()
+    {
+        S s = new S( "1.1.1.1", true );
+
+        s.setSyntaxChecker( new SyntaxChecker()
+        {
+            public String getSyntaxOid()
+            {
+                return "1.1.1.1";
+            }
+            public boolean isValidSyntax( Object value )
+            {
+                if ( !( value instanceof String ) )
+                {
+                    return false;
+                }
+
+                String strval = ( String ) value;
+                
+                for ( char c:strval.toCharArray() )
+                {
+                    if ( Character.isDigit( c ) )
+                    {
+                        return false;
+                    }
+                }
+                return true;
+            }
+
+            public void assertSyntax( Object value ) throws NamingException
+            {
+                if ( ! isValidSyntax( value ) )
+                {
+                    throw new InvalidAttributeValueException();
+                }
+            }
+        } );
+
+        final MR mr = new MR( "1.1.2.1" );
+        mr.syntax = s;
+        mr.comparator = new Comparator<String>()
+        {
+            public int compare( String o1, String o2 )
+            {
+                return ( o1 == null ? 
+                    ( o2 == null ? 0 : -1 ) :
+                    ( o2 == null ? 1 : o1.compareTo( o2 ) ) );
+            }
+
+            int getValue( String val )
+            {
+                if ( val.equals( "LOW" ) ) 
+                {
+                    return 0;
+                }
+                else if ( val.equals( "MEDIUM" ) ) 
+                {
+                    return 1;
+                }
+                else if ( val.equals( "HIGH" ) ) 
+                {
+                    return 2;
+                }
+                
+                throw new IllegalArgumentException( "Not a valid value" );
+            }
+        };
+        
+        mr.normalizer = new Normalizer()
+        {
+            private static final long serialVersionUID = 1L;
+
+            public Object normalize( Object value ) throws NamingException
+            {
+                if ( value instanceof String )
+                {
+                    return ( ( String ) value ).toLowerCase();
+                }
+
+                throw new IllegalStateException( "expected string to normalize" );
+            }
+        };
+        
+        AT at = new AT( "1.1.3.1" );
+        at.setEquality( mr );
+        at.setSyntax( s );
+        return at;
+    }
+
+
+    /* no protection*/ static AttributeType getIA5StringAttributeType()
+    {
+        AT at = new AT( "1.1" );
+
+        S s = new S( "1.1.1", true );
+
+        s.setSyntaxChecker( new SyntaxChecker()
+        {
+            public String getSyntaxOid()
+            {
+                return "1.1.1";
+            }
+            public boolean isValidSyntax( Object value )
+            {
+                return ((String)value == null) || (((String)value).length() < 7) ;
+            }
+
+            public void assertSyntax( Object value ) throws NamingException
+            {
+                if ( ! isValidSyntax( value ) )
+                {
+                    throw new InvalidAttributeValueException();
+                }
+            }
+        } );
+
+        final MR mr = new MR( "1.1.2" );
+        mr.syntax = s;
+        mr.comparator = new Comparator<String>()
+        {
+            public int compare( String o1, String o2 )
+            {
+                return ( ( o1 == null ) ? 
+                    ( o2 == null ? 0 : -1 ) :
+                    ( o2 == null ? 1 : o1.compareTo( o2 ) ) );
+            }
+        };
+        
+        mr.normalizer = new DeepTrimToLowerNormalizer();
+        
+        at.setEquality( mr );
+        at.setSyntax( s );
+        return at;
+    }
+
+
+    /* No protection */ static AttributeType getBytesAttributeType()
+    {
+        AT at = new AT( "1.2" );
+
+        S s = new S( "1.2.1", true );
+
+        s.setSyntaxChecker( new SyntaxChecker()
+        {
+            public String getSyntaxOid()
+            {
+                return "1.2.1";
+            }
+            public boolean isValidSyntax( Object value )
+            {
+                return ( value == null ) || ( ((byte[])value).length < 5 );
+            }
+
+            public void assertSyntax( Object value ) throws NamingException
+            {
+                if ( ! isValidSyntax( value ) )
+                {
+                    throw new InvalidAttributeValueException();
+                }
+            }
+        } );
+
+        final MR mr = new MR( "1.2.2" );
+        mr.syntax = s;
+        mr.comparator = new Comparator<byte[]>()
+        {
+            public int compare( byte[] o1, byte[] o2 )
+            {
+                return ( ( o1 == null ) ? 
+                    ( o2 == null ? 0 : -1 ) :
+                    ( o2 == null ? 1 : ByteArrayComparator.INSTANCE.compare( o1, o2 ) ) );
+            }
+        };
+        
+        mr.normalizer = new Normalizer()
+        {
+            private static final long serialVersionUID = 1L;
+            
+            public Object normalize( Object value ) throws NamingException
+            {
+                if ( value instanceof byte[] )
+                {
+                    byte[] val = (byte[])value;
+                    // each byte will be changed to be > 0, and spaces will be trimmed
+                    byte[] newVal = new byte[ val.length ];
+                    int i = 0;
+                    
+                    for ( byte b:val )
+                    {
+                        newVal[i++] = (byte)(b & 0x007F); 
+                    }
+                    
+                    return StringTools.trim( newVal );
+                }
+
+                throw new IllegalStateException( "expected byte[] to normalize" );
+            }
+        };
+        
+        at.setEquality( mr );
+        at.setSyntax( s );
+        return at;
+    }
+}
diff --git a/old_trunk/core-entry/src/site/site.xml b/old_trunk/core-entry/src/site/site.xml
new file mode 100644
index 0000000..9ec7c16
--- /dev/null
+++ b/old_trunk/core-entry/src/site/site.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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.
+-->
+<!--
+  @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+-->
+<project name="${project.name}">
+    <bannerLeft>
+        <name>${project.name}</name>
+    </bannerLeft>
+    <publishDate position="navigation-bottom" format="dd-MM-yyyy HH:mm" />
+    <body>
+        <menu name="Parent">
+            <item name="Apache Directory ApacheDS" href="../index.html" />
+        </menu>
+        <menu ref="reports" />
+    </body>
+</project>
diff --git a/old_trunk/core-entry/src/test/java/org/apache/directory/server/core/entry/DefaultServerAttributeTest.java b/old_trunk/core-entry/src/test/java/org/apache/directory/server/core/entry/DefaultServerAttributeTest.java
new file mode 100644
index 0000000..2d1f597
--- /dev/null
+++ b/old_trunk/core-entry/src/test/java/org/apache/directory/server/core/entry/DefaultServerAttributeTest.java
@@ -0,0 +1,1917 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+package org.apache.directory.server.core.entry;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.InvalidAttributeValueException;
+
+import org.apache.directory.server.schema.bootstrap.ApacheSchema;
+import org.apache.directory.server.schema.bootstrap.ApachemetaSchema;
+import org.apache.directory.server.schema.bootstrap.BootstrapSchemaLoader;
+import org.apache.directory.server.schema.bootstrap.CoreSchema;
+import org.apache.directory.server.schema.bootstrap.CosineSchema;
+import org.apache.directory.server.schema.bootstrap.InetorgpersonSchema;
+import org.apache.directory.server.schema.bootstrap.Schema;
+import org.apache.directory.server.schema.bootstrap.SystemSchema;
+import org.apache.directory.server.schema.registries.DefaultOidRegistry;
+import org.apache.directory.server.schema.registries.DefaultRegistries;
+import org.apache.directory.server.schema.registries.OidRegistry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.entry.Value;
+import org.apache.directory.shared.ldap.entry.client.ClientBinaryValue;
+import org.apache.directory.shared.ldap.entry.client.ClientStringValue;
+import org.apache.directory.shared.ldap.entry.client.DefaultClientAttribute;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.util.StringTools;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+
+/**
+ * Tests for the DefaultServerAttribute class
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class DefaultServerAttributeTest
+{
+    private static BootstrapSchemaLoader loader;
+    private static Registries registries;
+    private static OidRegistry oidRegistry;
+    
+    private static AttributeType atCN;
+    private static AttributeType atSN;
+    
+    // A SINGLE-VALUE attribute
+    private static AttributeType atC;   
+    
+    // A Binary attribute
+    private static AttributeType atPwd;
+
+    private static final Value<String> NULL_STRING_VALUE = new ClientStringValue( null );
+    private static final Value<byte[]> NULL_BINARY_VALUE = new ClientBinaryValue( null );
+    private static final byte[] BYTES1 = new byte[]{ 'a', 'b' };
+    private static final byte[] BYTES2 = new byte[]{ 'b' };
+    private static final byte[] BYTES3 = new byte[]{ 'c' };
+    private static final byte[] BYTES4 = new byte[]{ 'd' };
+    
+    private static final ClientStringValue STR_VALUE1 = new ClientStringValue( "a" );
+    private static final ClientStringValue STR_VALUE2 = new ClientStringValue( "b" );
+    private static final ClientStringValue STR_VALUE3 = new ClientStringValue( "c" );
+    private static final ClientStringValue STR_VALUE4 = new ClientStringValue( "d" );
+
+    private static final ClientBinaryValue BIN_VALUE1 = new ClientBinaryValue( BYTES1 );
+    private static final ClientBinaryValue BIN_VALUE2 = new ClientBinaryValue( BYTES2 );
+    private static final ClientBinaryValue BIN_VALUE3 = new ClientBinaryValue( BYTES3 );
+    private static final ClientBinaryValue BIN_VALUE4 = new ClientBinaryValue( BYTES4 );
+
+    /**
+     * Initialize the registries once for the whole test suite
+     */
+    @BeforeClass
+    public static void setup() throws NamingException
+    {
+        loader = new BootstrapSchemaLoader();
+        oidRegistry = new DefaultOidRegistry();
+        registries = new DefaultRegistries( "bootstrap", loader, oidRegistry );
+        
+        // load essential bootstrap schemas 
+        Set<Schema> bootstrapSchemas = new HashSet<Schema>();
+        bootstrapSchemas.add( new ApachemetaSchema() );
+        bootstrapSchemas.add( new ApacheSchema() );
+        bootstrapSchemas.add( new CoreSchema() );
+        bootstrapSchemas.add( new SystemSchema() );
+        bootstrapSchemas.add( new InetorgpersonSchema() );
+        bootstrapSchemas.add( new CosineSchema() );
+        loader.loadWithDependencies( bootstrapSchemas, registries );
+        
+        atCN = registries.getAttributeTypeRegistry().lookup( "cn" );
+        atC = registries.getAttributeTypeRegistry().lookup( "c" );
+        atSN = registries.getAttributeTypeRegistry().lookup( "sn" );
+        atPwd = registries.getAttributeTypeRegistry().lookup( "userpassword" );
+    }
+
+    @Test public void testAddOneValue() throws NamingException
+    {
+        AttributeType at = TestServerEntryUtils.getIA5StringAttributeType();
+        
+        DefaultServerAttribute attr = new DefaultServerAttribute( at );
+        
+        // Add a String value
+        attr.add( "test" );
+        
+        assertEquals( 1, attr.size() );
+        
+        assertTrue( attr.getAttributeType().getSyntax().isHumanReadable() );
+        
+        Value<?> value = attr.get();
+        
+        assertTrue( value instanceof ServerStringValue );
+        assertEquals( "test", ((ServerStringValue)value).get() );
+        
+        // Add a binary value
+        assertEquals( 0, attr.add( new byte[]{0x01} ) );
+        
+        // Add a Value
+        Value<?> ssv = new ServerStringValue( at, "test2" );
+        
+        attr.add( ssv );
+        
+        assertEquals( 2, attr.size() );
+        
+        Set<String> expected = new HashSet<String>();
+        expected.add( "test" );
+        expected.add( "test2" );
+        
+        for ( Value<?> val:attr )
+        {
+            if ( expected.contains( val.get() ) )
+            {
+                expected.remove( val.get() );
+            }
+            else
+            {
+                fail();
+            }
+        }
+        
+        assertEquals( 0, expected.size() );
+    }
+
+
+    @Test public void testAddTwoValue() throws NamingException
+    {
+        AttributeType at = TestServerEntryUtils.getIA5StringAttributeType();
+        
+        DefaultServerAttribute attr = new DefaultServerAttribute( at );
+        
+        // Add String values
+        attr.add( "test" );
+        attr.add( "test2" );
+        
+        assertEquals( 2, attr.size() );
+        
+        assertTrue( attr.getAttributeType().getSyntax().isHumanReadable() );
+        
+        Set<String> expected = new HashSet<String>();
+        expected.add( "test" );
+        expected.add( "test2" );
+        
+        for ( Value<?> val:attr )
+        {
+            if ( expected.contains( val.get() ) )
+            {
+                expected.remove( val.get() );
+            }
+            else
+            {
+                fail();
+            }
+        }
+        
+        assertEquals( 0, expected.size() );
+    }
+
+
+    @Test public void testAddNullValue() throws NamingException
+    {
+        AttributeType at = TestServerEntryUtils.getIA5StringAttributeType();
+        
+        DefaultServerAttribute attr = new DefaultServerAttribute( at );
+        
+        // Add a null value
+        attr.add( new ServerStringValue( at, null ) );
+        
+        assertEquals( 1, attr.size() );
+        
+        assertTrue( attr.getAttributeType().getSyntax().isHumanReadable() );
+        
+        Value<?> value = attr.get();
+        
+        assertTrue( value instanceof ServerStringValue );
+        assertNull( ((ServerStringValue)value).get() );
+    }
+    
+    @Test public void testGetAttribute()
+    {
+        AttributeType at = TestServerEntryUtils.getIA5StringAttributeType();
+        
+        DefaultServerAttribute attr = new DefaultServerAttribute( at );
+        
+        attr.add( "Test1" );
+        attr.add( "Test2" );
+        attr.add( "Test3" );
+        
+        Attribute attribute = ServerEntryUtils.toBasicAttribute( attr );
+        
+        assertEquals( "1.1",attribute.getID() );
+        assertEquals( 3, attribute.size() );
+        assertTrue( attribute.contains( "Test1" ) );
+        assertTrue( attribute.contains( "Test2" ) );
+        assertTrue( attribute.contains( "Test3" ) );
+    }
+
+
+    /**
+     * Test the contains() method
+     */
+    @Test public void testContains()
+    {
+        AttributeType at = TestServerEntryUtils.getIA5StringAttributeType();
+        
+        DefaultServerAttribute attr = new DefaultServerAttribute( at );
+        
+        attr.add( "Test  1" );
+        attr.add( "Test  2" );
+        attr.add( "Test  3" );
+        
+        assertTrue( attr.contains( "test 1" ) );
+        assertTrue( attr.contains( "Test 2" ) );
+        assertTrue( attr.contains( "TEST     3" ) );
+    }
+
+
+    /**
+     * Test method getBytes()
+     */
+    @Test
+    public void testGetBytes() throws InvalidAttributeValueException
+    {
+        ServerAttribute attr1 = new DefaultServerAttribute( atPwd );
+        
+        attr1.add( (byte[])null );
+        assertNull( attr1.getBytes() );
+
+        ServerAttribute attr2 = new DefaultServerAttribute( atPwd );
+        
+        attr2.add( BYTES1, BYTES2 );
+        assertTrue( Arrays.equals( BYTES1, attr2.getBytes() ) );
+        
+        ServerAttribute attr3 = new DefaultServerAttribute( atCN );
+        
+        attr3.add( "a", "b" );
+        
+        try
+        {
+            attr3.getBytes();
+            fail();
+        }
+        catch ( InvalidAttributeValueException ivae )
+        {
+            assertTrue( true );
+        }
+    }
+
+
+    /**
+     * Test method getId()
+     */
+    @Test
+    public void testGetId()
+    {
+        ServerAttribute attr = new DefaultServerAttribute( atCN );
+
+        assertEquals( "cn", attr.getId() );
+        
+        attr.setId(  "  CN  " );
+        assertEquals( "cn", attr.getId() );
+
+        attr.setId(  "  CommonName  " );
+        assertEquals( "commonname", attr.getId() );
+
+        attr.setId(  "  2.5.4.3  " );
+        assertEquals( "2.5.4.3", attr.getId() );
+    }
+
+
+    /**
+     * Test method getString()
+     */
+    @Test
+    public void testGetString() throws InvalidAttributeValueException
+    {
+        ServerAttribute attr1 = new DefaultServerAttribute( atCN );
+        
+        attr1.add( (String)null );
+        assertNull( attr1.getString() );
+
+        ServerAttribute attr2 = new DefaultServerAttribute( atCN );
+        
+        attr2.add( "a", "b" );
+        assertEquals( "a", attr2.getString() );
+        
+        ServerAttribute attr3 = new DefaultServerAttribute( atPwd );
+        
+        attr3.add( BYTES1, BYTES2 );
+        
+        try
+        {
+            attr3.getString();
+            fail();
+        }
+        catch ( InvalidAttributeValueException ivae )
+        {
+            assertTrue( true );
+        }
+    }
+
+
+    /**
+     * Test method getUpId
+     */
+    @Test
+    public void testGetUpId()
+    {
+        ServerAttribute attr = new DefaultServerAttribute( atCN );
+
+        assertNotNull( attr.getUpId() );
+        assertEquals( "cn", attr.getUpId() );
+        
+        attr.setUpId( "CN" );
+        assertEquals( "CN", attr.getUpId() );
+        
+        attr.setUpId(  "  Cn  " );
+        assertEquals( "Cn", attr.getUpId() );
+
+        attr.setUpId(  "  2.5.4.3  " );
+        assertEquals( "2.5.4.3", attr.getUpId() );
+    }
+
+
+    /**
+     * Test method hashCode()
+     */
+    @Test
+    public void testHashCode()
+    {
+        ServerAttribute attr1 = new DefaultServerAttribute( atCN );
+        ServerAttribute attr2 = new DefaultServerAttribute( atSN );
+        assertNotSame( attr1.hashCode(), attr2.hashCode() );
+        
+        attr2.setAttributeType( atCN );
+        assertEquals( attr1.hashCode(), attr2.hashCode() );
+        
+        attr1.put( (String)null );
+        assertNotSame( attr1.hashCode(), attr2.hashCode() );
+
+        attr1.clear();
+        assertEquals( attr1.hashCode(), attr2.hashCode() );
+        
+        attr1.put( "a", "b" );
+        assertNotSame( attr1.hashCode(), attr2.hashCode() );
+
+        attr2.put( "a", "b" );
+        assertEquals( attr1.hashCode(), attr2.hashCode() );
+        
+        // Order matters
+        attr2.put( "b", "a" );
+        assertNotSame( attr1.hashCode(), attr2.hashCode() );
+        
+        ServerAttribute attr3 = new DefaultServerAttribute( atPwd );
+        ServerAttribute attr4 = new DefaultServerAttribute( atPwd );
+        assertNotSame( attr3.hashCode(), attr4.hashCode() );
+        
+        attr3.put( (byte[])null );
+        assertNotSame( attr3.hashCode(), attr4.hashCode() );
+
+        attr3.clear();
+        assertEquals( attr3.hashCode(), attr4.hashCode() );
+        
+        attr3.put( new byte[]{0x01, 0x02}, new byte[]{0x03, 0x04} );
+        assertNotSame( attr1.hashCode(), attr2.hashCode() );
+
+        attr4.put( new byte[]{0x01, 0x02}, new byte[]{0x03, 0x04} );
+        assertNotSame( attr1.hashCode(), attr2.hashCode() );
+
+        // Order matters
+        attr4.put( new byte[]{0x03, 0x04}, new byte[]{0x01, 0x02} );
+        assertNotSame( attr1.hashCode(), attr2.hashCode() );
+    }
+    
+    
+    /**
+     * Test method SetId(String)
+     */
+    @Test
+    public void testSetId()
+    {
+        ServerAttribute attr = new DefaultServerAttribute( atCN );
+
+        attr.setId( "Cn" );
+        assertEquals( "cn", attr.getId() );
+        
+        attr.setId( " CN " );
+        assertEquals( "cn", attr.getId() );
+        
+        attr.setId( " 2.5.4.3 " );
+        assertEquals( "2.5.4.3", attr.getId() );
+        
+        attr.setId( " commonName " );
+        assertEquals( "commonname", attr.getId() );
+
+        try
+        {
+            attr.setId( null );
+            fail();
+        }
+        catch ( IllegalArgumentException iae )
+        {
+            assertTrue( true );
+        }
+        
+        try
+        {
+            attr.setId( "" );
+            fail();
+        }
+        catch ( IllegalArgumentException iae )
+        {
+            assertTrue( true );
+        }
+        
+        try
+        {
+            attr.setId( "  " );
+            fail();
+        }
+        catch ( IllegalArgumentException iae )
+        {
+            assertTrue( true );
+        }
+        
+        try
+        {
+            attr.setId( " SN " );
+            fail();
+        }
+        catch ( IllegalArgumentException iae )
+        {
+            assertTrue( true );
+        }
+    }
+
+
+    /**
+     * Test method isValid()
+     */
+    @Test
+    public void testIsValid() throws NamingException
+    {
+        ServerAttribute attr = new DefaultServerAttribute( atCN );
+        
+        // No value, this should be valid
+        assertTrue( attr.isValid() );
+        
+        attr.add( "test", "test2", "A123\\;" );
+        assertTrue( attr.isValid() );
+
+        // If we try to add a wrong value, it will not be added. The
+        // attribute remains valid.
+        assertEquals(0, attr.add( new byte[]{0x01} ) );
+        assertTrue( attr.isValid() );
+
+        // test a SINGLE-VALUE attribute. CountryName is SINGLE-VALUE
+        attr.setAttributeType( atC );
+        attr.put( "FR" );
+        assertTrue( attr.isValid() );
+        attr.add( "US" );
+        assertFalse( attr.isValid() );
+    }
+
+
+    /**
+     * Test method add( Value... )
+     */
+    @Test
+    public void testAddValueArray()
+    {
+        ServerAttribute attr1 = new DefaultServerAttribute( atCN );
+        
+        int nbAdded = attr1.add( new ServerStringValue( atCN, null ) );
+        assertEquals( 1, nbAdded );
+        assertTrue( attr1.isHR() );
+        assertEquals( NULL_STRING_VALUE, attr1.get() );
+        
+        ServerAttribute attr2 = new DefaultServerAttribute( atPwd );
+        
+        nbAdded = attr2.add( new ServerBinaryValue( atPwd, null ) );
+        assertEquals( 1, nbAdded );
+        assertFalse( attr2.isHR() );
+        assertEquals( NULL_BINARY_VALUE, attr2.get() );
+        
+        ServerAttribute attr3 = new DefaultServerAttribute( atCN );
+        
+        nbAdded = attr3.add( new ServerStringValue( atCN, "a" ), new ServerStringValue( atCN, "b" ) );
+        assertEquals( 2, nbAdded );
+        assertTrue( attr3.isHR() );
+        assertTrue( attr3.contains( "a" ) );
+        assertTrue( attr3.contains( "b" ) );
+        
+        ServerAttribute attr4 = new DefaultServerAttribute( atCN );
+        
+        nbAdded = attr4.add( new ServerBinaryValue( atPwd, BYTES1 ), new ServerBinaryValue( atPwd, BYTES2 ) );
+        assertEquals( 0, nbAdded );
+        assertTrue( attr4.isHR() );
+        assertFalse( attr4.contains( BYTES1 ) );
+        assertFalse( attr4.contains( BYTES2 ) );
+        
+        ServerAttribute attr5 = new DefaultServerAttribute( atCN );
+        
+        nbAdded = attr5.add( new ServerStringValue( atCN, "c" ), new ServerBinaryValue( atPwd, BYTES1 ) );
+        assertEquals( 1, nbAdded );
+        assertTrue( attr5.isHR() );
+        assertFalse( attr5.contains( "ab" ) );
+        assertTrue( attr5.contains( "c" ) );
+
+        ServerAttribute attr6 = new DefaultServerAttribute( atPwd );
+        
+        nbAdded = attr6.add( new ServerBinaryValue( atPwd, BYTES1 ), new ServerStringValue( atCN, "c" ) );
+        assertEquals( 1, nbAdded );
+        assertFalse( attr6.isHR() );
+        assertTrue( attr6.contains( BYTES1 ) );
+        assertFalse( attr6.contains( BYTES3 ) );
+
+        ServerAttribute attr7 = new DefaultServerAttribute( atPwd );
+        
+        nbAdded = attr7.add( new ServerBinaryValue( atPwd, null ), new ServerStringValue( atCN, "c" ) );
+        assertEquals( 1, nbAdded );
+        assertFalse( attr7.isHR() );
+        assertTrue( attr7.contains( NULL_BINARY_VALUE ) );
+        assertFalse( attr7.contains( BYTES3 ) );
+
+        ServerAttribute attr8 = new DefaultServerAttribute( atCN );
+        
+        nbAdded = attr8.add( new ServerStringValue( atCN, null ), new ServerBinaryValue( atPwd, BYTES1 ) );
+        assertEquals( 1, nbAdded );
+        assertTrue( attr8.isHR() );
+        assertTrue( attr8.contains( NULL_STRING_VALUE ) );
+        assertFalse( attr8.contains( "ab" ) );
+
+    
+        ServerAttribute attr9 = new DefaultServerAttribute( atCN );
+        
+        nbAdded = attr9.add( new ClientStringValue( null ), new ClientStringValue( "ab" ) );
+        assertEquals( 2, nbAdded );
+        assertTrue( attr9.isHR() );
+        assertTrue( attr9.contains( NULL_STRING_VALUE ) );
+        assertTrue( attr9.contains( "ab" ) );
+
+        ServerAttribute attr10 = new DefaultServerAttribute( atPwd );
+        
+        nbAdded = attr10.add( new ClientBinaryValue( null ), new ClientBinaryValue( BYTES1 ) );
+        assertEquals( 2, nbAdded );
+        assertFalse( attr10.isHR() );
+        assertTrue( attr10.contains( NULL_BINARY_VALUE ) );
+        assertTrue( attr10.contains( BYTES1 ) );
+    }
+
+
+    /**
+     * Test method add( String... )
+     */
+    @Test
+    public void testAddStringArray() throws InvalidAttributeValueException
+    {
+        ServerAttribute attr1 = new DefaultServerAttribute( atCN );
+        
+        int nbAdded = attr1.add( (String)null );
+        assertEquals( 1, nbAdded );
+        assertTrue( attr1.isHR() );
+        assertEquals( NULL_STRING_VALUE, attr1.get() );
+        
+        ServerAttribute attr2 = new DefaultServerAttribute( atCN );
+        
+        nbAdded = attr2.add( "" );
+        assertEquals( 1, nbAdded );
+        assertTrue( attr2.isHR() );
+        assertEquals( "", attr2.getString() );
+        
+        ServerAttribute attr3 = new DefaultServerAttribute( atCN );
+        
+        nbAdded = attr3.add( "t" );
+        assertEquals( 1, nbAdded );
+        assertTrue( attr3.isHR() );
+        assertEquals( "t", attr3.getString() );
+        
+        ServerAttribute attr4 = new DefaultServerAttribute( atCN );
+        
+        nbAdded = attr4.add( "a", "b", "c", "d" );
+        assertEquals( 4, nbAdded );
+        assertTrue( attr4.isHR() );
+        assertEquals( "a", attr4.getString() );
+        assertTrue( attr4.contains( "a" ) );
+        assertTrue( attr4.contains( "b" ) );
+        assertTrue( attr4.contains( "c" ) );
+        assertTrue( attr4.contains( "d" ) );
+        
+        nbAdded = attr4.add( "e" );
+        assertEquals( 1, nbAdded );
+        assertTrue( attr4.isHR() );
+        assertEquals( "a", attr4.getString() );
+        assertTrue( attr4.contains( "a" ) );
+        assertTrue( attr4.contains( "b" ) );
+        assertTrue( attr4.contains( "c" ) );
+        assertTrue( attr4.contains( "d" ) );
+        assertTrue( attr4.contains( "e" ) );
+        
+        nbAdded = attr4.add( BYTES1 );
+        assertEquals( 0, nbAdded );
+        assertTrue( attr4.isHR() );
+        assertEquals( "a", attr4.getString() );
+        assertTrue( attr4.contains( "a" ) );
+        assertTrue( attr4.contains( "b" ) );
+        assertTrue( attr4.contains( "c" ) );
+        assertTrue( attr4.contains( "d" ) );
+        assertTrue( attr4.contains( "e" ) );
+        assertFalse( attr4.contains( "ab" ) );
+        
+        ServerAttribute attr5 = new DefaultServerAttribute( atCN );
+        
+        nbAdded = attr5.add( "a", "b", (String)null, "d" );
+        assertEquals( 4, nbAdded );
+        assertTrue( attr5.isHR() );
+        assertTrue( attr5.contains( "a" ) );
+        assertTrue( attr5.contains( "b" ) );
+        assertTrue( attr5.contains( NULL_STRING_VALUE ) );
+        assertTrue( attr5.contains( "d" ) );
+
+        ServerAttribute attr6 = new DefaultServerAttribute( atPwd );
+        
+        nbAdded = attr6.add( "a", (String)null );
+        assertEquals( 0, nbAdded );
+        assertFalse( attr6.isHR() );
+    }
+
+
+    /**
+     * Test method add( byte[]... )
+     */
+    @Test
+    public void testAddByteArray() throws InvalidAttributeValueException
+    {
+        ServerAttribute attr1 = new DefaultServerAttribute( atPwd );
+        
+        int nbAdded = attr1.add( (byte[])null );
+        assertEquals( 1, nbAdded );
+        assertFalse( attr1.isHR() );
+        assertTrue( Arrays.equals( NULL_BINARY_VALUE.get(), attr1.getBytes() ) );
+        
+        ServerAttribute attr2 = new DefaultServerAttribute( atPwd );
+        
+        nbAdded = attr2.add( StringTools.EMPTY_BYTES );
+        assertEquals( 1, nbAdded );
+        assertFalse( attr2.isHR() );
+        assertTrue( Arrays.equals( StringTools.EMPTY_BYTES, attr2.getBytes() ) );
+        
+        ServerAttribute attr3 = new DefaultServerAttribute( atPwd );
+        
+        nbAdded = attr3.add( BYTES1 );
+        assertEquals( 1, nbAdded );
+        assertFalse( attr3.isHR() );
+        assertTrue( Arrays.equals( BYTES1, attr3.getBytes() ) );
+        
+        ServerAttribute attr4 = new DefaultServerAttribute( atPwd );
+        
+        nbAdded = attr4.add( BYTES1, BYTES2, BYTES3, BYTES4 );
+        assertEquals( 4, nbAdded );
+        assertFalse( attr4.isHR() );
+        assertTrue( attr4.contains( BYTES1 ) );
+        assertTrue( attr4.contains( BYTES2 ) );
+        assertTrue( attr4.contains( BYTES3 ) );
+        assertTrue( attr4.contains( BYTES4 ) );
+        
+        ServerAttribute attr5 = new DefaultServerAttribute( atPwd );
+        
+        nbAdded = attr5.add( BYTES1, BYTES2, (byte[])null, BYTES3 );
+        assertEquals( 4, nbAdded );
+        assertFalse( attr5.isHR() );
+        assertTrue( attr5.contains( BYTES1 ) );
+        assertTrue( attr5.contains( BYTES2 ) );
+        assertTrue( attr5.contains( (byte[])null ) );
+        assertTrue( attr5.contains( BYTES3 ) );
+
+        ServerAttribute attr6 = new DefaultServerAttribute( atPwd );
+        
+        nbAdded = attr6.add( "ab", (String)null );
+        assertEquals( 0, nbAdded );
+        assertFalse( attr6.isHR() );
+    }
+
+
+    /**
+     * Test method clear()
+     */
+    @Test
+    public void testClear()
+    {
+        ServerAttribute attr = new DefaultServerAttribute( "cn", atCN );
+        
+        assertEquals( 0, attr.size() );
+        
+        attr.add( (String)null, "a", "b" );
+        assertEquals( 3, attr.size() );
+        
+        attr.clear();
+        assertTrue( attr.isHR() );
+        assertEquals( 0, attr.size() );
+        assertEquals( atCN, attr.getAttributeType() );
+    }
+
+
+    /**
+     * Test method contains( Value... )
+     */
+    @Test
+    public void testContainsValueArray()
+    {
+        ServerAttribute attr1 = new DefaultServerAttribute( atCN );
+        
+        assertEquals( 0, attr1.size() );
+        assertFalse( attr1.contains( STR_VALUE1 ) );
+        assertFalse( attr1.contains( NULL_STRING_VALUE ) );
+        
+        attr1.add( (String)null );
+        assertEquals( 1, attr1.size() );
+        assertTrue( attr1.contains( NULL_STRING_VALUE ) );
+        
+        attr1.remove( (String)null );
+        assertFalse( attr1.contains( NULL_STRING_VALUE ) );
+        assertEquals( 0, attr1.size() );
+        
+        attr1.add(  "a", "b", "c" );
+        assertEquals( 3, attr1.size() );
+        assertTrue( attr1.contains( STR_VALUE1 ) );
+        assertTrue( attr1.contains( STR_VALUE2 ) );
+        assertTrue( attr1.contains( STR_VALUE3 ) );
+        assertTrue( attr1.contains( STR_VALUE1, STR_VALUE3 ) );
+        assertFalse( attr1.contains( STR_VALUE4 ) );
+        assertFalse( attr1.contains( NULL_STRING_VALUE ) );
+
+        ServerAttribute attr2 = new DefaultServerAttribute( atPwd );
+        assertEquals( 0, attr2.size() );
+        assertFalse( attr2.contains( BYTES1 ) );
+        assertFalse( attr2.contains( NULL_BINARY_VALUE ) );
+        
+        attr2.add( (byte[])null );
+        assertEquals( 1, attr2.size() );
+        assertTrue( attr2.contains( NULL_BINARY_VALUE ) );
+        
+        attr2.remove( (byte[])null );
+        assertFalse( attr2.contains( NULL_BINARY_VALUE ) );
+        assertEquals( 0, attr2.size() );
+        
+        attr2.add( BYTES1, BYTES2, BYTES3 );
+        assertEquals( 3, attr2.size() );
+        assertTrue( attr2.contains( BIN_VALUE1 ) );
+        assertTrue( attr2.contains( BIN_VALUE2 ) );
+        assertTrue( attr2.contains( BIN_VALUE3 ) );
+        assertFalse( attr2.contains( NULL_BINARY_VALUE ) );
+    }
+
+
+    /**
+     * Test method contains( String... )
+     */
+    @Test
+    public void testContainsStringArray()
+    {
+        ServerAttribute attr1 = new DefaultServerAttribute( atCN );
+        
+        assertEquals( 0, attr1.size() );
+        assertFalse( attr1.contains( "a" ) );
+        assertFalse( attr1.contains( (String)null ) );
+        
+        attr1.add( (String)null );
+        assertEquals( 1, attr1.size() );
+        assertTrue( attr1.contains( (String)null ) );
+        
+        attr1.remove( (String)null );
+        assertFalse( attr1.contains( (String)null ) );
+        assertEquals( 0, attr1.size() );
+        
+        attr1.add(  "a", "b", "c" );
+        assertEquals( 3, attr1.size() );
+        assertTrue( attr1.contains( "a" ) );
+        assertTrue( attr1.contains( "b" ) );
+        assertTrue( attr1.contains( "c" ) );
+        assertFalse( attr1.contains( "e" ) );
+        assertFalse( attr1.contains( (String)null ) );
+    }
+
+
+    /**
+     * Test method contains( byte[]... )
+     */
+    @Test
+    public void testContainsByteArray()
+    {
+        ServerAttribute attr1 = new DefaultServerAttribute( atPwd );
+        
+        assertEquals( 0, attr1.size() );
+        assertFalse( attr1.contains( BYTES1 ) );
+        assertFalse( attr1.contains( (byte[])null ) );
+        
+        attr1.add( (byte[])null );
+        assertEquals( 1, attr1.size() );
+        assertTrue( attr1.contains( (byte[])null ) );
+        
+        attr1.remove( (byte[])null );
+        assertFalse( attr1.contains( (byte[])null ) );
+        assertEquals( 0, attr1.size() );
+        
+        attr1.add(  BYTES1, BYTES2, BYTES3 );
+        assertEquals( 3, attr1.size() );
+        assertTrue( attr1.contains( BYTES1 ) );
+        assertTrue( attr1.contains( BYTES2 ) );
+        assertTrue( attr1.contains( BYTES3 ) );
+        assertFalse( attr1.contains( BYTES4 ) );
+        assertFalse( attr1.contains( (byte[])null ) );
+    }
+
+
+    /**
+     * Test method testEquals()
+     */
+    @Test
+    public void testEquals()
+    {
+        ServerAttribute attr1 = new DefaultServerAttribute( atCN );
+        
+        assertFalse( attr1.equals( null ) );
+        
+        ServerAttribute attr2 = new DefaultServerAttribute( atCN );
+        
+        assertTrue( attr1.equals( attr2 ) );
+        
+        attr2.setId( "CN" );
+        assertTrue( attr1.equals( attr2 ) );
+
+        attr1.setId( "CommonName" );
+        assertTrue( attr1.equals( attr2 ) );
+        
+        attr1.setUpId( "CN" );
+        assertTrue( attr1.equals( attr2 ) );
+        
+        attr1.add( "a", "b", "c" );
+        attr2.add( "c", "b", "a" );
+        assertTrue( attr1.equals( attr2 ) );
+        
+        attr1.setHR( true );
+        attr2.setHR( false );
+        assertTrue( attr1.equals( attr2 ) );
+        
+        ServerAttribute attr3 = new DefaultServerAttribute( atPwd );
+        ServerAttribute attr4 = new DefaultServerAttribute( atPwd );
+        
+        attr3.put( NULL_BINARY_VALUE );
+        attr4.put( NULL_BINARY_VALUE );
+        assertTrue( attr3.equals( attr4 ) );
+        
+        ServerAttribute attr5 = new DefaultServerAttribute( atPwd );
+        ServerAttribute attr6 = new DefaultServerAttribute( atCN );
+        assertFalse( attr5.equals( attr6 ) );
+        
+        attr5.put( NULL_BINARY_VALUE );
+        attr6.put( NULL_STRING_VALUE );
+        assertFalse( attr5.equals( attr6 ) );
+
+        ServerAttribute attr7 = new DefaultServerAttribute( atCN );
+        ServerAttribute attr8 = new DefaultServerAttribute( atPwd );
+        
+        attr7.put( "a" );
+        attr8.put( BYTES2 );
+        assertFalse( attr7.equals( attr8 ) );
+
+        ServerAttribute attr9 = new DefaultServerAttribute( atCN );
+        ServerAttribute attr10 = new DefaultServerAttribute( atPwd );
+        
+        attr7.put( "a" );
+        attr7.add( BYTES2 );
+        attr8.put( "a", "b" );
+        assertFalse( attr9.equals( attr10 ) );
+    }
+
+
+    /**
+     * Test method get()
+     */
+    @Test
+    public void testGet()
+    {
+        ServerAttribute attr1 = new DefaultServerAttribute( "cn", atCN );
+        
+        attr1.add( (String)null );
+        assertEquals( NULL_STRING_VALUE,attr1.get() );
+
+        ServerAttribute attr2 = new DefaultServerAttribute( "cn", atCN );
+        
+        attr2.add( "a", "b", "c" );
+        assertEquals( "a", attr2.get().get() );
+        
+        attr2.remove( "a" );
+        assertEquals( "b", attr2.get().get() );
+
+        attr2.remove( "b" );
+        assertEquals( "c", attr2.get().get() );
+
+        attr2.remove( "c" );
+        assertNull( attr2.get() );
+
+        ServerAttribute attr3 = new DefaultServerAttribute( "userPassword", atPwd );
+        
+        attr3.add( BYTES1, BYTES2, BYTES3 );
+        assertTrue( Arrays.equals( BYTES1, (byte[])attr3.get().get() ) );
+        
+        attr3.remove( BYTES1 );
+        assertTrue( Arrays.equals( BYTES2, (byte[])attr3.get().get() ) );
+
+        attr3.remove( BYTES2 );
+        assertTrue( Arrays.equals( BYTES3, (byte[])attr3.get().get() ) );
+
+        attr3.remove( BYTES3 );
+        assertNull( attr2.get() );
+    }
+
+
+    /**
+     * Test method getAll()
+     */
+    @Test
+    public void testGetAll()
+    {
+        ServerAttribute attr = new DefaultServerAttribute( atCN );
+        
+        Iterator<Value<?>> iterator = attr.getAll(); 
+        assertFalse( iterator.hasNext() );
+        
+        attr.add( NULL_STRING_VALUE );
+        iterator = attr.getAll(); 
+        assertTrue( iterator.hasNext() );
+        
+        Value<?> value = iterator.next();
+        assertEquals( NULL_STRING_VALUE, value );
+        
+        attr.clear();
+        iterator = attr.getAll(); 
+        assertFalse( iterator.hasNext() );
+        
+        attr.add(  "a", "b", "c" );
+        iterator = attr.getAll(); 
+        assertTrue( iterator.hasNext() );
+        assertEquals( "a", iterator.next().get() );
+        assertEquals( "b", iterator.next().get() );
+        assertEquals( "c", iterator.next().get() );
+        assertFalse( iterator.hasNext() );
+    }
+
+
+    /**
+     * Test method size()
+     */
+    @Test
+    public void testSize()
+    {
+        ServerAttribute attr1 = new DefaultServerAttribute( atCN );
+
+        assertEquals( 0, attr1.size() );
+        
+        attr1.add( (String)null );
+        assertEquals( 1, attr1.size() );
+
+        ServerAttribute attr2 = new DefaultServerAttribute( atCN );
+        
+        attr2.add( "a", "b" );
+        assertEquals( 2, attr2.size() );
+        
+        attr2.clear();
+        assertEquals( 0, attr2.size() );
+
+        ServerAttribute attr3 = new DefaultServerAttribute( atC );
+        
+        attr3.add( "US" );
+        assertEquals( 1, attr3.size() );
+        
+        // TODO : forbid addition of more than 1 value for SINGLE-VALUE attributes
+        attr3.add( "FR" );
+        assertEquals( 2, attr3.size() );
+    }
+
+
+    /**
+     * Test method put( byte[]... )
+     */
+    @Test
+    public void testPutByteArray() throws InvalidAttributeValueException, NamingException
+    {
+        ServerAttribute attr1 = new DefaultServerAttribute( atPwd );
+        
+        int nbAdded = attr1.put( (byte[])null );
+        assertEquals( 1, nbAdded );
+        assertFalse( attr1.isHR() );
+        assertTrue( Arrays.equals( NULL_BINARY_VALUE.get(), attr1.getBytes() ) );
+        
+        ServerAttribute attr2 = new DefaultServerAttribute( atPwd );
+        
+        nbAdded = attr2.put( StringTools.EMPTY_BYTES );
+        assertEquals( 1, nbAdded );
+        assertFalse( attr2.isHR() );
+        assertTrue( Arrays.equals( StringTools.EMPTY_BYTES, attr2.getBytes() ) );
+        
+        ServerAttribute attr3 = new DefaultServerAttribute( atPwd );
+        
+        nbAdded = attr3.put( BYTES1 );
+        assertEquals( 1, nbAdded );
+        assertFalse( attr3.isHR() );
+        assertTrue( Arrays.equals( BYTES1, attr3.getBytes() ) );
+        
+        ServerAttribute attr4 = new DefaultServerAttribute( atPwd );
+        
+        nbAdded = attr4.put( BYTES1, BYTES2 );
+        assertEquals( 2, nbAdded );
+        assertFalse( attr4.isHR() );
+        assertTrue( attr4.contains( BYTES1 ) );
+        assertTrue( attr4.contains( BYTES2 ) );
+        
+        nbAdded = attr4.put( BYTES3, BYTES4 );
+        assertEquals( 2, nbAdded );
+        assertFalse( attr4.isHR() );
+        assertTrue( attr4.contains( BYTES3 ) );
+        assertTrue( attr4.contains( BYTES4 ) );
+        
+        ServerAttribute attr5 = new DefaultServerAttribute( atPwd );
+        
+        nbAdded = attr5.put( BYTES1, BYTES2, (byte[])null, BYTES3 );
+        assertEquals( 4, nbAdded );
+        assertFalse( attr5.isHR() );
+        assertTrue( attr5.contains( BYTES1 ) );
+        assertTrue( attr5.contains( BYTES2 ) );
+        assertTrue( attr5.contains( (byte[])null ) );
+        assertTrue( attr5.contains( BYTES3 ) );
+
+        ServerAttribute attr6 = new DefaultServerAttribute( atPwd );
+        
+        attr6.setHR( true );
+        assertFalse( attr6.isHR() );
+        nbAdded = attr6.put( BYTES1, (byte[])null );
+        assertEquals( 2, nbAdded );
+        assertTrue( attr6.contains( BYTES1 ) );
+        assertTrue( attr6.contains( (byte[])null ) );
+    }
+
+
+    /**
+     * Test method put( String... )
+     */
+    @Test
+    public void testPutStringArray() throws InvalidAttributeValueException
+    {
+        ServerAttribute attr1 = new DefaultServerAttribute( atCN );
+        
+        int nbAdded = attr1.put( (String)null );
+        assertEquals( 1, nbAdded );
+        assertTrue( attr1.isHR() );
+        assertEquals( NULL_STRING_VALUE, attr1.get() );
+        
+        ServerAttribute attr2 = new DefaultServerAttribute( atCN );
+        
+        nbAdded = attr2.put( "" );
+        assertEquals( 1, nbAdded );
+        assertTrue( attr2.isHR() );
+        assertEquals( "", attr2.getString() );
+        
+        ServerAttribute attr3 = new DefaultServerAttribute( atCN );
+        
+        nbAdded = attr3.put( "t" );
+        assertEquals( 1, nbAdded );
+        assertTrue( attr3.isHR() );
+        assertEquals( "t", attr3.getString() );
+        
+        ServerAttribute attr4 = new DefaultServerAttribute( atCN );
+        
+        nbAdded = attr4.put( "a", "b", "c", "d" );
+        assertEquals( 4, nbAdded );
+        assertTrue( attr4.isHR() );
+        assertEquals( "a", attr4.getString() );
+        assertTrue( attr4.contains( "a" ) );
+        assertTrue( attr4.contains( "b" ) );
+        assertTrue( attr4.contains( "c" ) );
+        assertTrue( attr4.contains( "d" ) );
+        
+        nbAdded = attr4.put( "e" );
+        assertEquals( 1, nbAdded );
+        assertTrue( attr4.isHR() );
+        assertEquals( "e", attr4.getString() );
+        assertFalse( attr4.contains( "a" ) );
+        assertFalse( attr4.contains( "b" ) );
+        assertFalse( attr4.contains( "c" ) );
+        assertFalse( attr4.contains( "d" ) );
+        assertTrue( attr4.contains( "e" ) );
+        
+        nbAdded = attr4.put( BYTES1 );
+        assertEquals( 0, nbAdded );
+        assertTrue( attr4.isHR() );
+        
+        ServerAttribute attr5 = new DefaultServerAttribute( atCN );
+        
+        nbAdded = attr5.put( "a", "b", (String)null, "d" );
+        assertEquals( 4, nbAdded );
+        assertTrue( attr5.isHR() );
+        assertTrue( attr5.contains( "a" ) );
+        assertTrue( attr5.contains( "b" ) );
+        assertTrue( attr5.contains( NULL_STRING_VALUE ) );
+        assertTrue( attr5.contains( "d" ) );
+
+        ServerAttribute attr6 = new DefaultServerAttribute( atPwd );
+        
+        nbAdded = attr6.put( "a", (String)null );
+        assertEquals( 0, nbAdded );
+        assertFalse( attr6.isHR() );
+    }
+
+
+    /**
+     * Test method put( Value... )
+     */
+    @Test
+    public void testPutValueArray()
+    {
+        ServerAttribute attr1 = new DefaultServerAttribute( atCN );
+        
+        assertEquals( 0, attr1.size() );
+        
+        attr1.put( NULL_STRING_VALUE );
+        assertEquals( 1, attr1.size() );
+        assertTrue( attr1.contains( NULL_STRING_VALUE ) );
+        
+        attr1.put( STR_VALUE1, STR_VALUE2, STR_VALUE3 );
+        assertEquals( 3, attr1.size() );
+        assertTrue( attr1.contains( STR_VALUE1 ) );
+        assertTrue( attr1.contains( STR_VALUE2 ) );
+        assertTrue( attr1.contains( STR_VALUE3 ) );
+
+        attr1.put( STR_VALUE1, NULL_STRING_VALUE, STR_VALUE3 );
+        assertEquals( 3, attr1.size() );
+        assertTrue( attr1.contains( STR_VALUE1 ) );
+        assertTrue( attr1.contains( NULL_STRING_VALUE ) );
+        assertTrue( attr1.contains( STR_VALUE3 ) );
+        
+        attr1.put( STR_VALUE1, NULL_STRING_VALUE, BIN_VALUE3 );
+        assertEquals( 2, attr1.size() );
+        assertTrue( attr1.contains( STR_VALUE1 ) );
+        assertTrue( attr1.contains( NULL_STRING_VALUE ) );
+        assertFalse( attr1.contains( STR_VALUE3 ) );
+        
+
+        ServerAttribute attr2 = new DefaultServerAttribute( atPwd );
+        assertEquals( 0, attr2.size() );
+        
+        attr2.put( NULL_BINARY_VALUE );
+        assertEquals( 1, attr2.size() );
+        assertTrue( attr2.contains( NULL_BINARY_VALUE ) );
+        
+        attr2.put( BIN_VALUE1, BIN_VALUE2, BIN_VALUE3 );
+        assertEquals( 3, attr2.size() );
+        assertTrue( attr2.contains( BIN_VALUE1 ) );
+        assertTrue( attr2.contains( BIN_VALUE2 ) );
+        assertTrue( attr2.contains( BIN_VALUE3 ) );
+        
+        attr2.put( BIN_VALUE1, NULL_BINARY_VALUE, STR_VALUE3 );
+        assertEquals( 2, attr2.size() );
+        assertTrue( attr2.contains( BIN_VALUE1 ) );
+        assertTrue( attr2.contains( NULL_BINARY_VALUE ) );
+        assertFalse( attr2.contains( BIN_VALUE3 ) );
+    }
+
+
+    /**
+     * Test method put( List&lt;Value&gt; )
+     */
+    @Test
+    public void testPutListOfValues()
+    {
+        ServerAttribute attr1 = new DefaultServerAttribute( atCN );
+        
+        assertEquals( 0, attr1.size() );
+        
+        List<Value<?>> list = new ArrayList<Value<?>>();
+        list.add( NULL_STRING_VALUE );
+        
+        attr1.put( list );
+        assertEquals( 1, attr1.size() );
+        assertTrue( attr1.contains( NULL_STRING_VALUE ) );
+        
+        list.clear();
+        list.add( STR_VALUE1 );
+        list.add( STR_VALUE2 );
+        list.add( STR_VALUE3 );
+        attr1.put( list );
+        assertEquals( 3, attr1.size() );
+        assertTrue( attr1.contains( STR_VALUE1 ) );
+        assertTrue( attr1.contains( STR_VALUE2 ) );
+        assertTrue( attr1.contains( STR_VALUE3 ) );
+
+        list.clear();
+        list.add( STR_VALUE1 );
+        list.add( NULL_STRING_VALUE );
+        list.add( STR_VALUE3 );
+        attr1.put( list );
+        assertEquals( 3, attr1.size() );
+        assertTrue( attr1.contains( STR_VALUE1 ) );
+        assertTrue( attr1.contains( NULL_STRING_VALUE ) );
+        assertTrue( attr1.contains( STR_VALUE3 ) );
+        
+        list.clear();
+        list.add( STR_VALUE1 );
+        list.add( NULL_STRING_VALUE );
+        list.add( BIN_VALUE3 );
+        attr1.put( list );
+        assertEquals( 2, attr1.size() );
+        assertTrue( attr1.contains( STR_VALUE1 ) );
+        assertTrue( attr1.contains( NULL_STRING_VALUE ) );
+        assertFalse( attr1.contains( STR_VALUE3 ) );
+        
+
+        ServerAttribute attr2 = new DefaultServerAttribute( atPwd );
+        assertEquals( 0, attr2.size() );
+        
+        list.clear();
+        list.add( NULL_BINARY_VALUE );
+        attr2.put( list );
+        assertEquals( 1, attr2.size() );
+        assertTrue( attr2.contains( NULL_BINARY_VALUE ) );
+        
+        list.clear();
+        list.add( BIN_VALUE1 );
+        list.add( BIN_VALUE2 );
+        list.add( BIN_VALUE3 );
+        attr2.put( list );
+        assertEquals( 3, attr2.size() );
+        assertTrue( attr2.contains( BIN_VALUE1 ) );
+        assertTrue( attr2.contains( BIN_VALUE2 ) );
+        assertTrue( attr2.contains( BIN_VALUE3 ) );
+        
+        list.clear();
+        list.add( BIN_VALUE1 );
+        list.add( NULL_BINARY_VALUE );
+        list.add( STR_VALUE3 );
+        attr2.put( list );
+        assertEquals( 2, attr2.size() );
+        assertTrue( attr2.contains( BIN_VALUE1 ) );
+        assertTrue( attr2.contains( NULL_BINARY_VALUE ) );
+        assertFalse( attr2.contains( BIN_VALUE3 ) );
+    }
+
+
+    /**
+     * Test method remove( Value... )
+     */
+    @Test
+    public void testRemoveValueArray()
+    {
+        ServerAttribute attr1 = new DefaultServerAttribute( atCN );
+
+        assertFalse( attr1.remove( STR_VALUE1 ) );
+
+        attr1.setHR( true );
+        assertFalse( attr1.remove( STR_VALUE1 ) );
+        
+        attr1.put( "a", "b", "c" );
+        assertTrue( attr1.remove( STR_VALUE1 ) );
+        assertEquals( 2, attr1.size() );
+        
+        assertTrue( attr1.remove( STR_VALUE2, STR_VALUE3 ) );
+        assertEquals( 0, attr1.size() );
+        
+        assertFalse( attr1.remove( STR_VALUE4 ) );
+        
+        attr1.put( "a", "b", "c" );
+        assertFalse( attr1.remove( STR_VALUE2, STR_VALUE4 ) );
+        assertEquals( 2, attr1.size() );
+        
+        attr1.clear();
+        attr1.put( "a", (String)null, "b" );
+        assertTrue( attr1.remove( NULL_STRING_VALUE, STR_VALUE1 ) );
+        assertEquals( 1, attr1.size() );
+        
+        attr1.clear();
+        attr1.put( "a", (String)null, "b" );
+        attr1.add( BYTES3 );
+        assertFalse( attr1.remove( NULL_STRING_VALUE, STR_VALUE1, BIN_VALUE3 ) );
+        assertEquals( 1, attr1.size() );
+        
+        ServerAttribute attr2 = new DefaultServerAttribute( atPwd );
+
+        assertFalse( attr2.remove( BIN_VALUE1 ) );
+
+        attr2.setHR( true );
+        assertFalse( attr2.remove( BIN_VALUE1 ) );
+        
+        attr2.put( BYTES1, BYTES2, BYTES3 );
+        assertTrue( attr2.remove( BIN_VALUE1 ) );
+        assertEquals( 2, attr2.size() );
+        
+        assertTrue( attr2.remove( BIN_VALUE2, BIN_VALUE3 ) );
+        assertEquals( 0, attr2.size() );
+        
+        assertFalse( attr2.remove( BIN_VALUE4 ) );
+        
+        attr2.put( BYTES1, BYTES2, BYTES3 );
+        assertFalse( attr2.remove( BIN_VALUE2, STR_VALUE4 ) );
+        assertEquals( 2, attr2.size() );
+        
+        attr2.clear();
+        attr2.put( BYTES1, (byte[])null, BYTES3 );
+        assertFalse( attr2.remove( NULL_STRING_VALUE, BIN_VALUE1 ) );
+        assertEquals( 2, attr2.size() );
+        
+        attr2.clear();
+        attr2.put( BYTES1, (byte[])null, BYTES2 );
+        attr2.add( "c" );
+        assertFalse( attr2.remove( NULL_STRING_VALUE, BIN_VALUE1, STR_VALUE3 ) );
+        assertEquals( 2, attr2.size() );
+    }
+
+
+    /**
+     * Test method remove( byte... )
+     */
+    @Test
+    public void testRemoveByteArray()
+    {
+        ServerAttribute attr1 = new DefaultServerAttribute( atPwd );
+
+        assertFalse( attr1.remove( BYTES1 ) );
+
+        attr1.put( BYTES1, BYTES2, BYTES3 );
+        assertTrue( attr1.remove( BYTES1 ) );
+        assertEquals( 2, attr1.size() );
+        
+        assertTrue( attr1.remove( BYTES2, BYTES3 ) );
+        assertEquals( 0, attr1.size() );
+        
+        assertFalse( attr1.remove( BYTES4 ) );
+        
+        attr1.put( BYTES1, BYTES2, BYTES3 );
+        assertFalse( attr1.remove( BYTES3, BYTES4 ) );
+        assertEquals( 2, attr1.size() );
+        
+        attr1.clear();
+        attr1.put( BYTES1, (byte[])null, BYTES2 ) ;
+        assertTrue( attr1.remove( (byte[])null, BYTES1 ) );
+        assertEquals( 1, attr1.size() );
+    }
+
+
+    /**
+     * Test method remove( String... )
+     */
+    @Test
+    public void testRemoveStringArray()
+    {
+        ServerAttribute attr1 = new DefaultServerAttribute( atCN );
+
+        assertFalse( attr1.remove( "a" ) );
+
+        attr1.setHR( true );
+        assertFalse( attr1.remove( "a" ) );
+        
+        attr1.put( "a", "b", "c" );
+        assertTrue( attr1.remove( "a" ) );
+        assertEquals( 2, attr1.size() );
+        
+        assertTrue( attr1.remove( "b", "c" ) );
+        assertEquals( 0, attr1.size() );
+        
+        assertFalse( attr1.remove( "d" ) );
+        
+        attr1.put( "a", "b", "c" );
+        assertFalse( attr1.remove( "b", "e" ) );
+        assertEquals( 2, attr1.size() );
+        
+        attr1.clear();
+        attr1.put( "a", (String)null, "b" );
+        assertTrue( attr1.remove( (String )null, "a" ) );
+        assertEquals( 1, attr1.size() );
+        
+        EntryAttribute attr2 = new DefaultClientAttribute( "test" );
+        
+        attr2.put( BYTES1, BYTES2, BYTES3 );
+        
+        assertFalse( attr2.remove( (String)null ) );
+        assertTrue( attr2.remove( "ab", "c" ) );
+        assertFalse( attr2.remove( "d" ) );
+    }
+
+
+    /**
+     * Test method iterator()
+     */
+    @Test
+    public void testIterator()
+    {
+        ServerAttribute attr1 = new DefaultServerAttribute( atCN );
+        attr1.add(  "a", "b", "c" );
+        
+        Iterator<Value<?>> iter = attr1.iterator();
+        
+        assertTrue( iter.hasNext() );
+        
+        String[] values = new String[]{ "a", "b", "c" };
+        int pos = 0;
+        
+        for ( Value<?> val:attr1 )
+        {
+            assertTrue( val instanceof ServerStringValue );
+            assertEquals( values[pos++], val.get() );
+        }
+    }
+
+
+    /**
+     * Test method toString
+     */
+    @Test
+    public void testToString()
+    {
+        ServerAttribute attr = new DefaultServerAttribute( atCN );
+        
+        assertEquals( "    cn: (null)\n", attr.toString() );
+        
+        attr.setUpId( "CommonName" );
+        assertEquals( "    CommonName: (null)\n", attr.toString() );
+        
+        attr.add( (String)null );
+        assertEquals( "    CommonName: ''\n", attr.toString() );
+
+        attr.put( "a", "b" );
+        assertEquals( "    CommonName: a\n    CommonName: b\n", attr.toString() );
+    }
+
+
+    /**
+     * Test method instanceOf()
+     */
+    @Test
+    public void testInstanceOf() throws NamingException
+    {
+        ServerAttribute attr = new DefaultServerAttribute( atCN );
+        
+        assertTrue( attr.instanceOf( "CommonName" ) );
+        assertTrue( attr.instanceOf( "2.5.4.3" ) );
+        assertTrue( attr.instanceOf( "  Cn  " ) );
+        assertFalse( attr.instanceOf( "  " ) );
+        assertFalse( attr.instanceOf( "sn" ) );
+        assertFalse( attr.instanceOf( "name" ) );
+    }
+
+
+    /**
+     * Test method setUpId( String, AttributeType )
+     */
+    @Test
+    public void testSetUpIdStringAttributeType()
+    {
+        ServerAttribute attr = new DefaultServerAttribute( atSN );
+        
+        attr.setUpId( null, atCN );
+        assertEquals( "cn", attr.getId() );
+        assertEquals( "cn", attr.getUpId() );
+        assertEquals( atCN, attr.getAttributeType() );
+
+        attr.setUpId( "  ", atCN );
+        assertEquals( "cn", attr.getId() );
+        assertEquals( "cn", attr.getUpId() );
+        assertEquals( atCN, attr.getAttributeType() );
+
+        attr.setUpId( "  CN  ", atCN );
+        assertEquals( "cn", attr.getId() );
+        assertEquals( "CN", attr.getUpId() );
+        assertEquals( atCN, attr.getAttributeType() );
+
+        attr.setUpId( "  CommonName  ", atCN );
+        assertEquals( "commonname", attr.getId() );
+        assertEquals( "CommonName", attr.getUpId() );
+        assertEquals( atCN, attr.getAttributeType() );
+
+        attr.setUpId( "  2.5.4.3  ", atCN );
+        assertEquals( "2.5.4.3", attr.getId() );
+        assertEquals( "2.5.4.3", attr.getUpId() );
+        assertEquals( atCN, attr.getAttributeType() );
+
+        // Check with wrong IDs
+        try
+        {
+            attr.setUpId( "sn", atCN );
+            fail();
+        }
+        catch ( IllegalArgumentException iae )
+        {
+            assertTrue( true );
+        }
+
+        try
+        {
+            attr.setUpId( "  2.5.4.4  ", atCN );
+            fail();
+        }
+        catch ( IllegalArgumentException iae )
+        {
+            assertTrue( true );
+        }
+    }
+
+
+    /**
+     * Test method setUpId( String ) inherited from ClientAttribute
+     */
+    @Test
+    public void testSetUpIdString()
+    {
+        ServerAttribute attr = new DefaultServerAttribute( atCN );
+        
+        attr.setUpId( "cn" );
+        assertEquals( "cn", attr.getId() );
+        assertEquals( "cn", attr.getUpId() );
+        assertEquals( atCN, attr.getAttributeType() );
+
+        attr.setUpId( "  CN  " );
+        assertEquals( "cn", attr.getId() );
+        assertEquals( "CN", attr.getUpId() );
+        assertEquals( atCN, attr.getAttributeType() );
+
+        attr.setUpId( "  CommonName  ");
+        assertEquals( "commonname", attr.getId() );
+        assertEquals( "CommonName", attr.getUpId() );
+        assertEquals( atCN, attr.getAttributeType() );
+
+        attr.setUpId( "  2.5.4.3  " );
+        assertEquals( "2.5.4.3", attr.getId() );
+        assertEquals( "2.5.4.3", attr.getUpId() );
+        assertEquals( atCN, attr.getAttributeType() );
+        
+        // Now check wrong IDs
+        attr = new DefaultServerAttribute( atCN );
+        attr.setUpId( "sn" );
+        assertEquals( "cn", attr.getId() );
+        assertEquals( "cn", attr.getUpId() );
+        assertEquals( atCN, attr.getAttributeType() );
+
+        attr.setUpId( "  SN  " );
+        assertEquals( "cn", attr.getId() );
+        assertEquals( "cn", attr.getUpId() );
+        assertEquals( atCN, attr.getAttributeType() );
+
+        attr.setUpId( "  surname  " );
+        assertEquals( "cn", attr.getId() );
+        assertEquals( "cn", attr.getUpId() );
+        assertEquals( atCN, attr.getAttributeType() );
+
+        attr.setUpId( "  2.5.4.4  " );
+        assertEquals( "cn", attr.getId() );
+        assertEquals( "cn", attr.getUpId() );
+        assertEquals( atCN, attr.getAttributeType() );
+    }
+
+
+    /**
+     * Test method setAttributeType( AttributeType )
+     */
+    @Test
+    public void testSetAttributeType() throws NamingException
+    {
+        ServerAttribute attr = new DefaultServerAttribute( atCN );
+        
+        try
+        {
+            attr.setAttributeType( null );
+            fail();
+        }
+        catch ( IllegalArgumentException iae )
+        {
+            assertTrue( true );
+        }
+        
+        attr.setAttributeType( atSN );
+        
+        assertTrue( attr.instanceOf( "Surname" ) );
+        assertEquals( "sn", attr.getId() );
+    }
+
+
+    /**
+     * Test method getAttributeType()
+     */
+    @Test
+    public void testGetAttributeType()
+    {
+        ServerAttribute attr = new DefaultServerAttribute( atSN );
+        
+        assertEquals( atSN, attr.getAttributeType() );
+    }
+
+
+    /**
+     * Test constructor DefaultServerAttribute( AttributeType )
+     */
+    @Test
+    public void testDefaultServerAttributeAttributeType()
+    {
+        ServerAttribute attr = new DefaultServerAttribute( atCN );
+        
+        assertTrue( attr.isHR() );
+        assertEquals( 0, attr.size() );
+        assertEquals( "cn", attr.getId() );
+        assertEquals( "cn", attr.getUpId() );
+        assertEquals( atCN, attr.getAttributeType() );
+    }
+
+
+    /**
+     * Test constructor DefaultServerAttribute( String, AttributeType )
+     */
+    @Test
+    public void testDefaultServerAttributeStringAttributeType()
+    {
+        ServerAttribute attr1 = new DefaultServerAttribute( "cn", atCN );
+        
+        assertTrue( attr1.isHR() );
+        assertEquals( 0, attr1.size() );
+        assertEquals( "cn", attr1.getId() );
+        assertEquals( "cn", attr1.getUpId() );
+        assertEquals( atCN, attr1.getAttributeType() );
+
+        ServerAttribute attr2 = new DefaultServerAttribute( "  CommonName  ", atCN );
+        
+        assertTrue( attr2.isHR() );
+        assertEquals( 0, attr2.size() );
+        assertEquals( "commonname", attr2.getId() );
+        assertEquals( "CommonName", attr2.getUpId() );
+        assertEquals( atCN, attr2.getAttributeType() );
+
+        ServerAttribute attr3 = new DefaultServerAttribute( "  ", atCN );
+        
+        assertTrue( attr3.isHR() );
+        assertEquals( 0, attr3.size() );
+        assertEquals( "cn", attr3.getId() );
+        assertEquals( "cn", attr3.getUpId() );
+        assertEquals( atCN, attr3.getAttributeType() );
+    }
+
+
+    /**
+     * Test constructor DefaultServerAttribute( AttributeType, Value... )
+     */
+    @Test
+    public void testDefaultServerAttributeAttributeTypeValueArray()
+    {
+        ServerAttribute attr1 = new DefaultServerAttribute( atCN, STR_VALUE1, STR_VALUE2, NULL_STRING_VALUE );
+        
+        assertTrue( attr1.isHR() );
+        assertEquals( 3, attr1.size() );
+        assertEquals( "cn", attr1.getId() );
+        assertEquals( "cn", attr1.getUpId() );
+        assertEquals( atCN, attr1.getAttributeType() );
+        assertTrue( attr1.contains( "a", "b" ) );
+        assertTrue( attr1.contains( NULL_STRING_VALUE ) );
+
+        ServerAttribute attr2 = new DefaultServerAttribute( atCN, STR_VALUE1, BIN_VALUE2, NULL_STRING_VALUE );
+        
+        assertTrue( attr2.isHR() );
+        assertEquals( 2, attr2.size() );
+        assertEquals( "cn", attr2.getId() );
+        assertEquals( "cn", attr2.getUpId() );
+        assertEquals( atCN, attr2.getAttributeType() );
+        assertTrue( attr2.contains( "a" ) );
+        assertTrue( attr2.contains( NULL_STRING_VALUE ) );
+    }
+
+
+    /**
+     * Test constructor DefaultServerAttribute( String, AttributeType, Value... )
+     */
+    @Test
+    public void testDefaultServerAttributeStringAttributeTypeValueArray()
+    {
+        ServerAttribute attr1 = new DefaultServerAttribute( "cn", atCN, STR_VALUE1, STR_VALUE2, NULL_STRING_VALUE );
+        
+        assertTrue( attr1.isHR() );
+        assertEquals( 3, attr1.size() );
+        assertEquals( "cn", attr1.getId() );
+        assertEquals( "cn", attr1.getUpId() );
+        assertEquals( atCN, attr1.getAttributeType() );
+        assertTrue( attr1.contains( "a", "b" ) );
+        assertTrue( attr1.contains( NULL_STRING_VALUE ) );
+
+        ServerAttribute attr2 = new DefaultServerAttribute( atCN, STR_VALUE1, BIN_VALUE2, NULL_STRING_VALUE );
+        
+        assertTrue( attr2.isHR() );
+        assertEquals( 2, attr2.size() );
+        assertEquals( "cn", attr2.getId() );
+        assertEquals( "cn", attr2.getUpId() );
+        assertEquals( atCN, attr2.getAttributeType() );
+        assertTrue( attr2.contains( "a" ) );
+        assertTrue( attr2.contains( NULL_STRING_VALUE ) );
+
+        ServerAttribute attr3 = new DefaultServerAttribute( "CommonName", atCN, STR_VALUE1, STR_VALUE2, NULL_STRING_VALUE );
+        
+        assertTrue( attr3.isHR() );
+        assertEquals( 3, attr3.size() );
+        assertEquals( "commonname", attr3.getId() );
+        assertEquals( "CommonName", attr3.getUpId() );
+        assertEquals( atCN, attr3.getAttributeType() );
+        assertTrue( attr3.contains( "a", "b" ) );
+        assertTrue( attr3.contains( NULL_STRING_VALUE ) );
+
+        ServerAttribute attr4 = new DefaultServerAttribute( " 2.5.4.3 ", atCN, STR_VALUE1, STR_VALUE2, NULL_STRING_VALUE );
+        
+        assertTrue( attr4.isHR() );
+        assertEquals( 3, attr4.size() );
+        assertEquals( "2.5.4.3", attr4.getId() );
+        assertEquals( "2.5.4.3", attr4.getUpId() );
+        assertEquals( atCN, attr4.getAttributeType() );
+        assertTrue( attr4.contains( "a", "b" ) );
+        assertTrue( attr4.contains( NULL_STRING_VALUE ) );
+    }
+
+
+    /**
+     * Test constructor DefaultServerAttribute( AttributeType, String... ) 
+     */
+    @Test
+    public void testDefaultServerAttributeAttributeTypeStringArray()
+    {
+        ServerAttribute attr1 = new DefaultServerAttribute( atCN, "a", "b", (String)null );
+        
+        assertTrue( attr1.isHR() );
+        assertEquals( 3, attr1.size() );
+        assertEquals( "cn", attr1.getId() );
+        assertEquals( "cn", attr1.getUpId() );
+        assertEquals( atCN, attr1.getAttributeType() );
+        assertTrue( attr1.contains( "a", "b" ) );
+        assertTrue( attr1.contains( NULL_STRING_VALUE ) );
+
+        ServerAttribute attr2 = new DefaultServerAttribute( atCN, STR_VALUE1, BIN_VALUE2, NULL_STRING_VALUE );
+        
+        assertTrue( attr2.isHR() );
+        assertEquals( 2, attr2.size() );
+        assertEquals( "cn", attr2.getId() );
+        assertEquals( "cn", attr2.getUpId() );
+        assertEquals( atCN, attr2.getAttributeType() );
+        assertTrue( attr2.contains( "a" ) );
+        assertTrue( attr2.contains( NULL_STRING_VALUE ) );
+    }
+
+
+    /**
+     * Test constructor DefaultServerAttribute( String, AttributeType, String... )
+     */
+    @Test
+    public void testDefaultServerAttributeStringAttributeTypeStringArray()
+    {
+        ServerAttribute attr1 = new DefaultServerAttribute( "cn", atCN, "a", "b", (String)null );
+        
+        assertTrue( attr1.isHR() );
+        assertEquals( 3, attr1.size() );
+        assertEquals( "cn", attr1.getId() );
+        assertEquals( "cn", attr1.getUpId() );
+        assertEquals( atCN, attr1.getAttributeType() );
+        assertTrue( attr1.contains( "a", "b" ) );
+        assertTrue( attr1.contains( NULL_STRING_VALUE ) );
+
+        ServerAttribute attr2 = new DefaultServerAttribute( "CommonName", atCN, "a", "b", (String)null );
+        
+        assertTrue( attr2.isHR() );
+        assertEquals( 3, attr2.size() );
+        assertEquals( "commonname", attr2.getId() );
+        assertEquals( "CommonName", attr2.getUpId() );
+        assertEquals( atCN, attr2.getAttributeType() );
+        assertTrue( attr2.contains( "a", "b" ) );
+        assertTrue( attr2.contains( NULL_STRING_VALUE ) );
+
+        ServerAttribute attr3 = new DefaultServerAttribute( " 2.5.4.3 ", atCN, "a", "b", (String)null );
+        
+        assertTrue( attr3.isHR() );
+        assertEquals( 3, attr3.size() );
+        assertEquals( "2.5.4.3", attr3.getId() );
+        assertEquals( "2.5.4.3", attr3.getUpId() );
+        assertEquals( atCN, attr3.getAttributeType() );
+        assertTrue( attr3.contains( "a", "b" ) );
+        assertTrue( attr3.contains( NULL_STRING_VALUE ) );
+    }
+
+
+    /**
+     * Test method DefaultServerAttribute( AttributeType, byte[]... ) 
+     */
+    @Test
+    public void testDefaultServerAttributeAttributeTypeByteArray()
+    {
+        ServerAttribute attr1 = new DefaultServerAttribute( atPwd, BYTES1, BYTES2, (byte[])null );
+        
+        assertFalse( attr1.isHR() );
+        assertEquals( 3, attr1.size() );
+        assertEquals( "userpassword", attr1.getId() );
+        assertEquals( "userPassword", attr1.getUpId() );
+        assertEquals( atPwd, attr1.getAttributeType() );
+        assertTrue( attr1.contains( BYTES1, BYTES2 ) );
+        assertTrue( attr1.contains( NULL_BINARY_VALUE ) );
+
+        ServerAttribute attr2 = new DefaultServerAttribute( atPwd, STR_VALUE1, BIN_VALUE2, NULL_BINARY_VALUE );
+        
+        assertFalse( attr2.isHR() );
+        assertEquals( 2, attr2.size() );
+        assertEquals( "userpassword", attr2.getId() );
+        assertEquals( "userPassword", attr2.getUpId() );
+        assertEquals( atPwd, attr2.getAttributeType() );
+        assertTrue( attr2.contains( BYTES2 ) );
+        assertTrue( attr2.contains( NULL_BINARY_VALUE ) );
+    }
+
+
+    /**
+     * Test method DefaultServerAttribute( String, AttributeType, byte[]... )
+     */
+    @Test
+    public void testDefaultServerAttributeStringAttributeTypeByteArray()
+    {
+        ServerAttribute attr1 = new DefaultServerAttribute( "userPassword", atPwd, BYTES1, BYTES2, (byte[])null );
+        
+        assertFalse( attr1.isHR() );
+        assertEquals( 3, attr1.size() );
+        assertEquals( "userpassword", attr1.getId() );
+        assertEquals( "userPassword", attr1.getUpId() );
+        assertEquals( atPwd, attr1.getAttributeType() );
+        assertTrue( attr1.contains( BYTES1, BYTES2 ) );
+        assertTrue( attr1.contains( NULL_BINARY_VALUE ) );
+
+        ServerAttribute attr2 = new DefaultServerAttribute( "2.5.4.35", atPwd, STR_VALUE1, BIN_VALUE2, NULL_BINARY_VALUE );
+        
+        assertFalse( attr2.isHR() );
+        assertEquals( 2, attr2.size() );
+        assertEquals( "2.5.4.35", attr2.getId() );
+        assertEquals( "2.5.4.35", attr2.getUpId() );
+        assertEquals( atPwd, attr2.getAttributeType() );
+        assertTrue( attr2.contains( BYTES2 ) );
+        assertTrue( attr2.contains( NULL_BINARY_VALUE ) );
+    }
+
+
+    /**
+     * Test method testClone()
+     */
+    @Test
+    public void testClone()
+    {
+        ServerAttribute attr = new DefaultServerAttribute( atCN );
+        
+        EntryAttribute clone = attr.clone();
+        
+        assertEquals( attr, clone );
+        attr.setUpId( "CommonName" );
+        assertEquals( "cn", clone.getId() );
+        
+        attr.add( "a", (String)null, "b" );
+        clone = attr.clone();
+        assertEquals( attr, clone );
+        
+        attr.remove( "a" );
+        assertNotSame( attr, clone );
+        
+        clone = attr.clone();
+        assertEquals( attr, clone );
+    }
+}
diff --git a/old_trunk/core-entry/src/test/java/org/apache/directory/server/core/entry/DefaultServerEntryTest.java b/old_trunk/core-entry/src/test/java/org/apache/directory/server/core/entry/DefaultServerEntryTest.java
new file mode 100644
index 0000000..87c12ca
--- /dev/null
+++ b/old_trunk/core-entry/src/test/java/org/apache/directory/server/core/entry/DefaultServerEntryTest.java
@@ -0,0 +1,3907 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.entry;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import javax.naming.InvalidNameException;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.BasicAttributes;
+import javax.naming.directory.NoSuchAttributeException;
+
+import org.apache.directory.server.schema.bootstrap.ApacheSchema;
+import org.apache.directory.server.schema.bootstrap.ApachemetaSchema;
+import org.apache.directory.server.schema.bootstrap.BootstrapSchemaLoader;
+import org.apache.directory.server.schema.bootstrap.CoreSchema;
+import org.apache.directory.server.schema.bootstrap.CosineSchema;
+import org.apache.directory.server.schema.bootstrap.InetorgpersonSchema;
+import org.apache.directory.server.schema.bootstrap.Schema;
+import org.apache.directory.server.schema.bootstrap.SystemSchema;
+import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
+import org.apache.directory.server.schema.registries.DefaultOidRegistry;
+import org.apache.directory.server.schema.registries.DefaultRegistries;
+import org.apache.directory.server.schema.registries.OidRegistry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.entry.Entry;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.entry.Value;
+import org.apache.directory.shared.ldap.entry.client.ClientBinaryValue;
+import org.apache.directory.shared.ldap.entry.client.ClientStringValue;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.util.StringTools;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+
+/**
+ * Test the DefaultServerEntry class.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class DefaultServerEntryTest
+{
+    private static final byte[] BYTES1 = new byte[]{ 'a', 'b' };
+    private static final byte[] BYTES2 = new byte[]{ 'b' };
+    private static final byte[] BYTES3 = new byte[]{ 'c' };
+
+    private static BootstrapSchemaLoader loader;
+    private static Registries registries;
+    private static AttributeTypeRegistry atr;
+    private static OidRegistry oidRegistry;
+    
+    private static AttributeType atObjectClass;
+    private static AttributeType atCN;
+    private static AttributeType atSN;
+    private static AttributeType atC;   
+    private static AttributeType atL;   
+    private static AttributeType atOC;   
+    
+    // A Binary attribute
+    private static AttributeType atPwd;
+
+    private static LdapDN EXAMPLE_DN;
+    
+    /**
+     * Initialize the registries once for the whole test suite
+     */
+    @BeforeClass
+    public static void setup() throws NamingException
+    {
+        loader = new BootstrapSchemaLoader();
+        oidRegistry = new DefaultOidRegistry();
+        registries = new DefaultRegistries( "bootstrap", loader, oidRegistry );
+        
+        // load essential bootstrap schemas 
+        Set<Schema> bootstrapSchemas = new HashSet<Schema>();
+        bootstrapSchemas.add( new ApachemetaSchema() );
+        bootstrapSchemas.add( new ApacheSchema() );
+        bootstrapSchemas.add( new CoreSchema() );
+        bootstrapSchemas.add( new SystemSchema() );
+        bootstrapSchemas.add( new InetorgpersonSchema() );
+        bootstrapSchemas.add( new CosineSchema() );
+        loader.loadWithDependencies( bootstrapSchemas, registries );
+
+        atr = registries.getAttributeTypeRegistry();
+
+        atObjectClass = registries.getAttributeTypeRegistry().lookup( "objectClass" );
+        atCN = registries.getAttributeTypeRegistry().lookup( "cn" );
+        atC = registries.getAttributeTypeRegistry().lookup( "c" );
+        atL = registries.getAttributeTypeRegistry().lookup( "l" );
+        atOC = registries.getAttributeTypeRegistry().lookup( "objectClass" );
+        atSN = registries.getAttributeTypeRegistry().lookup( "sn" );
+        atPwd = registries.getAttributeTypeRegistry().lookup( "userpassword" );
+        
+        EXAMPLE_DN = new LdapDN( "dc=example,dc=com" );
+    }
+
+
+    //-------------------------------------------------------------------------
+    // Test the Constructors
+    //-------------------------------------------------------------------------
+    /**
+     * Test for method DefaultServerEntry()
+     */
+    @Test
+    public void testDefaultServerEntry()
+    {
+        Entry entry = new DefaultServerEntry();
+        assertNotNull( entry );
+        assertNull( entry.getDn() );
+        assertEquals( 0, entry.size() );
+    }
+    
+    
+    /**
+     * Test for method DefaultServerEntry( registries )
+     */
+    @Test
+    public void testDefaultServerEntryRegistries()
+    {
+        Entry entry = new DefaultServerEntry( registries );
+        assertNotNull( entry );
+        assertNull( entry.getDn() );
+        assertEquals( 0, entry.size() );
+    }
+    
+    
+    /**
+     * Test for method DefaultServerEntry( registries, LdapDN )
+     */
+    @Test
+    public void testDefaultServerEntryRegistriesDN()
+    {
+        Entry entry = new DefaultServerEntry( registries, EXAMPLE_DN );
+        assertNotNull( entry );
+        assertEquals( EXAMPLE_DN, entry.getDn() );
+        assertEquals( 0, entry.size() );
+    }
+    
+    
+    /**
+     * Test for method DefaultServerEntry( registries, LdapDN, AttributeType... )
+     */
+    @Test
+    public void testDefaultServerEntryRegistriesDNAttributeTypeArray()
+    {
+        ServerEntry entry = new DefaultServerEntry( registries, EXAMPLE_DN, atOC, atPwd, atCN );
+        assertNotNull( entry );
+        assertEquals( EXAMPLE_DN, entry.getDn() );
+        assertEquals( 3, entry.size() );
+        assertTrue( entry.containsAttribute( atOC ) );
+        assertTrue( entry.containsAttribute( atPwd ) );
+        assertTrue( entry.containsAttribute( atCN ) );
+    }
+    
+    
+    /**
+     * Test for method DefaultServerEntry( registries, LdapDN, AttributeType, upId )
+     */
+    @Test
+    public void testDefaultServerEntryRegistriesDNAttributeTypeUpId()
+    {
+        ServerEntry entry = new DefaultServerEntry( registries, EXAMPLE_DN, atOC, "  OBJECTCLASS  " );
+        assertNotNull( entry );
+        assertEquals( EXAMPLE_DN, entry.getDn() );
+        assertEquals( 1, entry.size() );
+        assertTrue( entry.containsAttribute( atOC ) );
+        assertEquals( "objectclass", entry.get( atOC ).getId() );
+        assertEquals( "OBJECTCLASS", entry.get( atOC ).getUpId() );
+    }
+    
+    
+    /**
+     * Test for method DefaultServerEntry( registries, LdapDN, AttributeType, upId )
+     */
+    @Test
+    public void testDefaultServerEntryRegistriesDNUpIdArray()
+    {
+        ServerEntry entry = new DefaultServerEntry( registries, EXAMPLE_DN, "  OBJECTCLASS  ", " Cn " );
+        assertNotNull( entry );
+        assertEquals( EXAMPLE_DN, entry.getDn() );
+        assertEquals( 2, entry.size() );
+        assertTrue( entry.containsAttribute( "objectClass" ) );
+        assertEquals( "objectclass", entry.get( atOC ).getId() );
+        assertEquals( "OBJECTCLASS", entry.get( atOC ).getUpId() );
+        assertTrue( entry.containsAttribute( "2.5.4.3" ) );
+        assertEquals( "cn", entry.get( atCN ).getId() );
+        assertEquals( "Cn", entry.get( atCN ).getUpId() );
+    }
+    
+    
+    //-------------------------------------------------------------------------
+    // Test the Add methods
+    //-------------------------------------------------------------------------
+    /**
+     * Test for method add( EntryAttribute...)
+     */
+    @Test
+    public void testAddEntryAttribute() throws NamingException
+    {
+        Entry entry = new DefaultServerEntry( registries, EXAMPLE_DN );
+        
+        EntryAttribute oc = new DefaultServerAttribute( atObjectClass, "top", "person" );
+        EntryAttribute cn = new DefaultServerAttribute( atCN, "test1", "test2" );
+        EntryAttribute sn = new DefaultServerAttribute( atSN, "Test1", "Test2" );
+        EntryAttribute up = new DefaultServerAttribute( atPwd, BYTES1, BYTES2 );
+        EntryAttribute c = new DefaultServerAttribute( atC, "FR", "US" );
+        
+        entry.add( oc, cn, sn, c );
+        
+        assertEquals( 4, entry.size() );
+        assertTrue( entry.containsAttribute( "ObjectClass" ) );
+        assertTrue( entry.containsAttribute( "CN" ) );
+        assertTrue( entry.containsAttribute( "  sn  " ) );
+        assertTrue( entry.containsAttribute( " countryName  " ) );
+    
+        EntryAttribute attr = entry.get( "objectclass" );
+        assertEquals( 2, attr.size() );
+        
+        EntryAttribute c2 = new DefaultServerAttribute( atC, "UK", "DE" );
+        entry.add( c2, up );
+        assertEquals( 5, entry.size() );
+        
+        assertTrue( entry.containsAttribute( "userPassword" ) );
+        assertTrue( entry.containsAttribute( " countryName " ) );
+
+        EntryAttribute attrC = entry.get( "countryName" );
+        assertEquals( 4, attrC.size() );
+        
+        entry.clear();
+    }
+
+    
+    /**
+     * Test for method add( String, byte[]...)
+     */
+    @Test
+    public void testAddStringByteArrayArray() throws NamingException
+    {
+        Entry entry = new DefaultServerEntry( registries, EXAMPLE_DN );
+        
+        entry.add( "userPassword", (byte[])null );
+        assertEquals( 1, entry.size() );
+        EntryAttribute attributePWD = entry.get( "userPassword" );
+        assertEquals( 1, attributePWD.size() );
+        assertNotNull( attributePWD.get() );
+        assertNull( attributePWD.get().get() );
+        
+        entry.clear();
+        
+        entry.add( "jpegPhoto", BYTES1, BYTES1, BYTES2 );
+        assertEquals( 1, entry.size() );
+        EntryAttribute attributeJPG = entry.get( "jpegPhoto" );
+        assertEquals( 2, attributeJPG.size() );
+        assertNotNull( attributeJPG.get() );
+        assertTrue( attributeJPG.contains( BYTES1 ) );
+        assertTrue( attributeJPG.contains( BYTES2 ) );
+        
+        entry.clear();
+        
+        try
+        {
+            // Cannot add an attribute which does not exist
+            entry.add( "wrongAT", BYTES1, BYTES2 );
+            fail();
+         }
+         catch ( NoSuchAttributeException nsae )
+         {
+             assertTrue( true );
+         }
+
+         // Cannot add String values into a binary attribute
+         entry.add( "jpegPhoto", "test", "test2" );
+         assertEquals( 0, entry.get( "jpegPhoto" ).size() );
+    }
+     
+
+    /**
+     * Test for method add( String, String...)
+     */
+    @Test
+    public void testAddStringStringArray() throws NamingException
+    {
+        Entry entry = new DefaultServerEntry( registries, EXAMPLE_DN );
+        
+        entry.add( "cn", (String)null );
+        assertEquals( 1, entry.size() );
+        EntryAttribute attributeCN = entry.get( "cn" );
+        
+        assertEquals( 1, attributeCN.size() );
+        assertNotNull( attributeCN.get() );
+        assertNull( attributeCN.get().get() );
+         
+        entry.add( "sn", "test", "test", "TEST" );
+        assertEquals( 2, entry.size() );
+        EntryAttribute attributeSN = entry.get( "sn" );
+         
+        // 'TEST' and 'test' are the same value for 'sn' (this is a case insensitive attributeType)
+        assertEquals( 1, attributeSN.size() );
+        assertNotNull( attributeSN.get() );
+        assertTrue( attributeSN.contains( "test" ) );
+        assertTrue( attributeSN.contains( "TEST" ) );
+         
+        entry.clear();
+
+        try
+        {
+            // Cannot add an attribute which does not exist
+            entry.add( "wrongAT", "wrong", "wrong" );
+            fail();
+        }
+        catch ( NoSuchAttributeException nsae )
+        {
+            assertTrue( true );
+        }
+
+        // Cannot add binary values into a String attribute
+        entry.add( "sn",BYTES1, BYTES2 );
+        assertEquals( 0, entry.get( "sn" ).size() );
+    }
+     
+
+    /**
+     * Test for method add( String, Value<?>...)
+     */
+    @Test
+    public void testAddStringValueArray() throws NamingException
+    {
+        Entry entry = new DefaultServerEntry( registries, EXAMPLE_DN );
+        Value<String> value = new ServerStringValue( atCN, (String)null );
+        
+        entry.add( "cn", value );
+        assertEquals( 1, entry.size() );
+        EntryAttribute attributeCN = entry.get( "cn" );
+        assertEquals( 1, attributeCN.size() );
+        assertNotNull( attributeCN.get() );
+        assertNull( attributeCN.get().get() );
+         
+        Value<String> value1 = new ServerStringValue( atCN, "test1" );
+        Value<String> value2 = new ServerStringValue( atCN, "test2" );
+        Value<String> value3 = new ServerStringValue( atCN, "test1" );
+
+        entry.add( "sn", value1, value2, value3 );
+        assertEquals( 2, entry.size() );
+        EntryAttribute attributeSN = entry.get( "sn" );
+        assertEquals( 2, attributeSN.size() );
+        assertNotNull( attributeSN.get() );
+        assertTrue( attributeSN.contains( value1 ) );
+        assertTrue( attributeSN.contains( value2 ) );
+         
+        Value<byte[]> value4 = new ServerBinaryValue( atPwd, BYTES1 );
+        entry.add( "l", value1, value4 );
+        assertEquals( 3, entry.size() );
+        EntryAttribute attributeL = entry.get( "l" );
+         
+        // Cannot store a binary value in a String attribute
+        assertEquals( 1, attributeL.size() );
+        assertNotNull( attributeL.get() );
+        assertTrue( attributeL.contains( value1 ) );
+
+        entry.clear();
+
+        try
+        {
+            // Cannot add an attribute which does not exist
+            entry.add( "wrongAT", value1, value2 );
+            fail();
+        }
+        catch ( NoSuchAttributeException nsae )
+        {
+            assertTrue( true );
+        }
+    }
+
+
+    /**
+     * Test method for add( AttributeType, byte[]... )
+     */
+    @Test
+    public void testAddAttributeTypeByteArrayArray() throws NamingException
+    {
+        ServerEntry entry = new DefaultServerEntry( registries, EXAMPLE_DN );
+        
+        entry.add( atPwd, BYTES1, BYTES2 );
+        assertEquals( 1, entry.size() );
+        assertTrue( entry.contains( atPwd, BYTES1, BYTES2 ) );
+        
+        entry.add( atPwd, (byte[])null, BYTES1 );
+        assertEquals( 1, entry.size() );
+        
+        EntryAttribute attribute = entry.get( atPwd );
+        assertEquals( 3, attribute.size() );
+        assertTrue( attribute.contains( BYTES1 ) );
+        assertTrue( attribute.contains( BYTES2 ) );
+        assertTrue( attribute.contains( (byte[])null ) );
+    }
+    
+     
+    /**
+     * Test method for add( AttributeType, String... )
+     */
+    @Test
+    public void testAddAttributeTypeStringArray() throws NamingException
+    {
+        ServerEntry entry = new DefaultServerEntry( registries, EXAMPLE_DN );
+        
+        entry.add( atC, "us", "fr" );
+        assertEquals( 1, entry.size() );
+        assertTrue( entry.contains( atC, "fr", "us" ) );
+        
+        entry.add( atC, (String)null, "de", "fr" );
+        assertEquals( 1, entry.size() );
+        
+        EntryAttribute attribute = entry.get( atC );
+        assertEquals( 4, attribute.size() );
+        assertTrue( attribute.contains( "de" ) );
+        assertTrue( attribute.contains( "fr" ) );
+        assertTrue( attribute.contains( (String)null ) );
+        assertTrue( attribute.contains( "us" ) );
+        
+        entry.clear();
+        
+        assertEquals( 0, entry.size() );
+    }
+    
+     
+    /**
+     * Test method for add( AttributeType, Value<?>... )
+     */
+    @Test
+    public void testAddAttributeTypeValueArray() throws NamingException
+    {
+        ServerEntry entry = new DefaultServerEntry( registries, EXAMPLE_DN );
+        
+        Value<String> strValue1 = new ServerStringValue( atCN, "test1" );
+        Value<String> strValue2 = new ServerStringValue( atCN, "test2" );
+        Value<String> strValue3 = new ServerStringValue( atCN, "test3" );
+        Value<String> strNullValue = new ServerStringValue( atCN, null);
+
+        Value<byte[]> binValue1 = new ServerBinaryValue( atPwd, BYTES1 );
+        Value<byte[]> binValue2 = new ServerBinaryValue( atPwd, BYTES2 );
+        Value<byte[]> binValue3 = new ServerBinaryValue( atPwd, BYTES3 );
+        
+        try
+        {
+            entry.add( (AttributeType)null, strValue1 );
+            fail();
+        }
+        catch( IllegalArgumentException iae )
+        {
+            assertTrue( true );
+        }
+        
+        entry.add( atCN, strValue1, strValue2, strValue1 );
+        entry.add( atPwd, binValue1, binValue2, binValue1 );
+        
+        assertEquals( 2, entry.size() );
+        assertTrue( entry.contains( atCN, "test1", "test2" ) );
+        assertTrue( entry.contains( atPwd, BYTES1, BYTES2 ) );
+        
+        entry.add( atCN, strValue3, strNullValue );
+        
+        assertEquals( 4, entry.get( atCN ).size() );
+        assertTrue( entry.contains( atCN, strNullValue ) );
+        
+        entry.add( atCN, binValue3 );
+        assertFalse( entry.contains( atCN, binValue3 ) );
+    }
+    
+    
+
+
+    /**
+     * Test method for add( String, AttributeType, byte[]... )
+     */
+    @Test
+    public void testAddStringAttributeTypeByteArrayArray() throws NamingException
+    {
+        ServerEntry entry = new DefaultServerEntry( registries, EXAMPLE_DN );
+        
+        entry.add( "UserPassword", atPwd, BYTES1, BYTES2 );
+        assertEquals( 1, entry.size() );
+        assertTrue( entry.contains( atPwd, BYTES1, BYTES2 ) );
+        assertEquals( "UserPassword", entry.get( atPwd ).getUpId() );
+        assertEquals( "userpassword", entry.get( atPwd ).getId() );
+        
+        entry.add( "  UserPassword  ", atPwd, (byte[])null, BYTES1 );
+        assertEquals( 1, entry.size() );
+        
+        EntryAttribute attribute = entry.get( atPwd );
+        assertEquals( 3, attribute.size() );
+        assertTrue( attribute.contains( BYTES1 ) );
+        assertTrue( attribute.contains( BYTES2 ) );
+        assertTrue( attribute.contains( (byte[])null ) );
+        assertEquals( "UserPassword", attribute.getUpId() );
+        assertEquals( "userpassword", attribute.getId() );
+
+        try
+        {
+            entry.add( "  ObjectClass  ", atOC, BYTES1 );
+            fail();
+        }
+        catch( UnsupportedOperationException uoe )
+        {
+            assertTrue( true );
+        }
+    }
+    
+     
+    /**
+     * Test method for add( String, AttributeType, String... )
+     */
+    @Test
+    public void testAddStringAttributeTypeStringArray() throws NamingException
+    {
+        ServerEntry entry = new DefaultServerEntry( registries, EXAMPLE_DN );
+        
+        entry.add( "CommonName", atCN, "test1", "test2" );
+        assertEquals( 1, entry.size() );
+        assertTrue( entry.contains( atCN, "test1", "test2" ) );
+        assertEquals( "CommonName", entry.get( atCN ).getUpId() );
+        assertEquals( "commonname", entry.get( atCN ).getId() );
+        
+        entry.add( "  CN  ", atCN, (String)null, "test1" );
+        assertEquals( 1, entry.size() );
+        
+        EntryAttribute attribute = entry.get( atCN );
+        assertEquals( 3, attribute.size() );
+        assertTrue( attribute.contains( "test1" ) );
+        assertTrue( attribute.contains( (String)null ) );
+        assertTrue( attribute.contains( "test2" ) );
+        assertEquals( "CN", attribute.getUpId() );
+        assertEquals( "cn", attribute.getId() );
+
+        entry.clear();
+        
+        // Binary values are not allowed
+        entry.add( "  CN  ", atCN, BYTES1 );
+        assertEquals( 1, entry.size() );
+        assertNotNull( entry.get( atCN ) );
+        assertEquals( 0, entry.get( atCN ).size() );
+    }
+    
+     
+    /**
+     * Test method for add( String, AttributeType, Value<?>... )
+     */
+    @Test
+    public void testAddStringAttributeTypeValueArray() throws NamingException
+    {
+        ServerEntry entry = new DefaultServerEntry( registries, EXAMPLE_DN );
+        
+        Value<String> strValue1 = new ServerStringValue( atCN, "test1" );
+        Value<String> strValue2 = new ServerStringValue( atCN, "test2" );
+        Value<String> strValue3 = new ServerStringValue( atCN, "test3" );
+        Value<String> strNullValue = new ServerStringValue( atCN, null);
+
+        Value<byte[]> binValue1 = new ServerBinaryValue( atPwd, BYTES1 );
+        Value<byte[]> binValue2 = new ServerBinaryValue( atPwd, BYTES2 );
+        Value<byte[]> binValue3 = new ServerBinaryValue( atPwd, BYTES3 );
+        
+        try
+        {
+            entry.add( "cn", (AttributeType)null, strValue1 );
+            fail();
+        }
+        catch( IllegalArgumentException iae )
+        {
+            assertTrue( true );
+        }
+        
+        entry.add( "CN", atCN, strValue1, strValue2, strValue1 );
+        entry.add( "UserPassword", atPwd, binValue1, binValue2, binValue1 );
+        
+        assertEquals( 2, entry.size() );
+        assertTrue( entry.contains( atCN, "test1", "test2" ) );
+        assertTrue( entry.contains( atPwd, BYTES1, BYTES2 ) );
+        assertEquals( "CN", entry.get( atCN ).getUpId() );
+        assertEquals( "cn", entry.get( atCN ).getId() );
+        assertEquals( "UserPassword", entry.get( atPwd ).getUpId() );
+        assertEquals( "userpassword", entry.get( atPwd ).getId() );
+        
+        entry.add( "CN", atCN, strValue3, strNullValue );
+        
+        assertEquals( 4, entry.get( atCN ).size() );
+        assertTrue( entry.contains( atCN, strNullValue ) );
+        
+        entry.add( atCN, binValue3 );
+        assertFalse( entry.contains( atCN, binValue3 ) );
+        
+        try
+        {
+            entry.add( "SN", atCN, "test" );
+            fail();
+        }
+        catch ( IllegalArgumentException iae )
+        {
+            assertTrue( true );
+        }
+    }
+    
+    
+    /**
+     * Test the add( AT, String... ) method
+     */
+    @Test public void testAddAtStringElipsis() throws NamingException
+    {
+        LdapDN dn = new LdapDN( "cn=test" );
+        DefaultServerEntry entry = new DefaultServerEntry( registries, dn );
+        
+        // Test a simple addition
+        entry.add( atCN, "test1" );
+        assertNotNull( entry.get( atCN ) );
+        assertEquals( 1, entry.get( atCN ).size() );
+        assertEquals( "test1", entry.get( atCN ).get().get() );
+        
+        // Test some more addition
+        entry.add( atCN, "test2", "test3" );
+        assertNotNull( entry.get( atCN ) );
+        assertEquals( 3, entry.get( atCN ).size() );
+        assertTrue( entry.contains( atCN, "test1" ) );
+        assertTrue( entry.contains( atCN, "test2" ) );
+        assertTrue( entry.contains( atCN, "test3" ) );
+        
+        // Test some addition of existing values
+        entry.add( atCN, "test2" );
+        assertNotNull( entry.get( atCN ) );
+        assertEquals( 3, entry.get( atCN ).size() );
+        assertTrue( entry.contains( atCN, "test1" ) );
+        assertTrue( entry.contains( atCN, "test2" ) );
+        assertTrue( entry.contains( atCN, "test3" ) );
+        
+        // Test the addition of a null value
+        entry.add( atCN, (String)null );
+        assertNotNull( entry.get( atCN ) );
+        assertEquals( 4, entry.get( atCN ).size() );
+        assertTrue( entry.contains( atCN, "test1" ) );
+        assertTrue( entry.contains( atCN, "test2" ) );
+        assertTrue( entry.contains( atCN, "test3" ) );
+        assertTrue( entry.contains( atCN, (String )null ) ); 
+        
+        entry.clear();
+        
+        // Test the addition of a binary value
+        byte[] test4 = StringTools.getBytesUtf8( "test4" );
+        
+        entry.add( atCN, test4 );
+        assertFalse( entry.get( atCN ).contains( test4 ) );
+    }
+
+
+    /**
+     * Test the add( AT, byte[]... ) method
+     */
+    @Test public void testAddAtBytesElipsis() throws NamingException
+    {
+        LdapDN dn = new LdapDN( "cn=test" );
+        DefaultServerEntry entry = new DefaultServerEntry( registries, dn );
+        
+        AttributeType atPassword = registries.getAttributeTypeRegistry().lookup( "userPassword" );
+        
+        byte[] test1 = StringTools.getBytesUtf8( "test1" );
+        byte[] test2 = StringTools.getBytesUtf8( "test2" );
+        byte[] test3 = StringTools.getBytesUtf8( "test3" );
+        
+        // Test a simple addition
+        entry.add( atPassword, test1 );
+        assertNotNull( entry.get( atPassword ) );
+        assertEquals( 1, entry.get( atPassword ).size() );
+        assertTrue( Arrays.equals( test1, (byte[])entry.get( atPassword ).get().get() ) );
+        
+        // Test some more addition
+        entry.add( atPassword, test2, test3 );
+        assertNotNull( entry.get( atPassword ) );
+        assertEquals( 3, entry.get( atPassword ).size() );
+        assertTrue( entry.contains( atPassword, test1 ) );
+        assertTrue( entry.contains( atPassword, test2 ) );
+        assertTrue( entry.contains( atPassword, test3 ) );
+        
+        // Test some addition of existing values
+        entry.add( atPassword, test2 );
+        assertNotNull( entry.get( atPassword ) );
+        assertEquals( 3, entry.get( atPassword ).size() );
+        assertTrue( entry.contains( atPassword, test1 ) );
+        assertTrue( entry.contains( atPassword, test2 ) );
+        assertTrue( entry.contains( atPassword, test3 ) );
+        
+        // Test the addition of a null value
+        entry.add( atPassword, (byte[])null );
+        assertNotNull( entry.get( atPassword ) );
+        assertEquals( 4, entry.get( atPassword ).size() );
+        assertTrue( entry.contains( atPassword, test1 ) );
+        assertTrue( entry.contains( atPassword, test2 ) );
+        assertTrue( entry.contains( atPassword, test3 ) );
+        assertTrue( entry.contains( atPassword, (byte[] )null ) ); 
+        
+        entry.clear();
+        
+        // Test the addition of a String value. It should be converted to a byte array
+        byte[] test4 = StringTools.getBytesUtf8( "test4" );
+
+        entry.add( atPassword, "test4" );
+        assertNotNull( entry.get( atPassword ) );
+        assertEquals( 0, entry.get( atPassword ).size() );
+        assertFalse( entry.contains( atPassword, test4 ) );
+    }
+
+
+    /**
+     * Test the add( AT, SV... ) method
+     */
+    @Test public void testAddAtServerValueElipsis() throws NamingException
+    {
+        LdapDN dn = new LdapDN( "cn=test" );
+        DefaultServerEntry entry = new DefaultServerEntry( registries, dn );
+        
+        AttributeType atPassword = registries.getAttributeTypeRegistry().lookup( "userPassword" );
+        
+        byte[] b1 = StringTools.getBytesUtf8( "test1" );
+        byte[] b2 = StringTools.getBytesUtf8( "test2" );
+        byte[] b3 = StringTools.getBytesUtf8( "test3" );
+
+        Value<String> test1 = new ServerStringValue( atCN, "test1" );
+        Value<String> test2 = new ServerStringValue( atCN, "test2" );
+        Value<String> test3 = new ServerStringValue( atCN, "test3" );
+        
+        Value<byte[]> testB1 = new ServerBinaryValue( atPassword, b1 );
+        Value<byte[]> testB2 = new ServerBinaryValue( atPassword, b2 );
+        Value<byte[]> testB3 = new ServerBinaryValue( atPassword, b3 );
+        
+        // Test a simple addition in atCN
+        entry.add( atCN, test1 );
+        assertNotNull( entry.get( atCN ) );
+        assertEquals( 1, entry.get( atCN ).size() );
+        assertEquals( "test1", entry.get( atCN ).get().get() );
+        
+        // Test some more addition
+        entry.add( atCN, test2, test3 );
+        assertNotNull( entry.get( atCN ) );
+        assertEquals( 3, entry.get( atCN ).size() );
+        assertTrue( entry.contains( atCN, "test1" ) );
+        assertTrue( entry.contains( atCN, "test2" ) );
+        assertTrue( entry.contains( atCN, "test3" ) );
+        
+        // Test some addition of existing values
+        entry.add( atCN, test2 );
+        assertNotNull( entry.get( atCN ) );
+        assertEquals( 3, entry.get( atCN ).size() );
+        assertTrue( entry.contains( atCN, "test1" ) );
+        assertTrue( entry.contains( atCN, "test2" ) );
+        assertTrue( entry.contains( atCN, "test3" ) );
+        
+        // Test the addition of a null value
+        entry.add( atCN, (String)null );
+        assertNotNull( entry.get( atCN ) );
+        assertEquals( 4, entry.get( atCN ).size() );
+        assertTrue( entry.contains( atCN, "test1" ) );
+        assertTrue( entry.contains( atCN, "test2" ) );
+        assertTrue( entry.contains( atCN, "test3" ) );
+        assertTrue( entry.contains( atCN, (String )null ) ); 
+        
+        entry.clear();
+        
+        // Test the addition of a String value. It should be converted to a byte array
+        byte[] test4 = StringTools.getBytesUtf8( "test4" );
+
+        entry.add( atCN, test4 );
+        assertFalse( entry.contains( atCN, test4 ) );
+
+        // Now, work with a binary attribute
+        // Test a simple addition
+        entry.add( atPassword, testB1 );
+        assertNotNull( entry.get( atPassword ) );
+        assertEquals( 1, entry.get( atPassword ).size() );
+        assertTrue( Arrays.equals( b1, (byte[])entry.get( atPassword ).get().get() ) );
+        
+        // Test some more addition
+        entry.add( atPassword, testB2, testB3 );
+        assertNotNull( entry.get( atPassword ) );
+        assertEquals( 3, entry.get( atPassword ).size() );
+        assertTrue( entry.contains( atPassword, b1 ) );
+        assertTrue( entry.contains( atPassword, b2 ) );
+        assertTrue( entry.contains( atPassword, b3 ) );
+        
+        // Test some addition of existing values
+        entry.add( atPassword, testB2 );
+        assertNotNull( entry.get( atPassword ) );
+        assertEquals( 3, entry.get( atPassword ).size() );
+        assertTrue( entry.contains( atPassword, b1 ) );
+        assertTrue( entry.contains( atPassword, b2 ) );
+        assertTrue( entry.contains( atPassword, b3 ) );
+        
+        // Test the addition of a null value
+        entry.add( atPassword, (byte[])null );
+        assertNotNull( entry.get( atPassword ) );
+        assertEquals( 4, entry.get( atPassword ).size() );
+        assertTrue( entry.contains( atPassword, b1 ) );
+        assertTrue( entry.contains( atPassword, b2 ) );
+        assertTrue( entry.contains( atPassword, b3 ) );
+        assertTrue( entry.contains( atPassword, (byte[] )null ) ); 
+        
+        entry.clear();
+        
+        // Test the addition of a String value. It should be converted to a byte array
+        byte[] b4 = StringTools.getBytesUtf8( "test4" );
+
+        entry.add( atPassword, "test4" );
+        assertNotNull( entry.get( atPassword ) );
+        assertEquals( 0, entry.get( atPassword ).size() );
+        assertFalse( entry.contains( atPassword, b4 ) );
+    }
+
+
+    /**
+     * Test the add( upId, String... ) method
+     */
+    @Test public void testAddUpIdStringElipsis() throws NamingException
+    {
+        LdapDN dn = new LdapDN( "cn=test" );
+        DefaultServerEntry entry = new DefaultServerEntry( registries, dn );
+        
+        // Test a simple addition
+        entry.add( "CN", "test1" );
+        assertNotNull( entry.get( atCN ) );
+        assertTrue( entry.containsAttribute( atCN ) );
+        assertEquals( "cn", entry.get( atCN ).getId() );
+        assertEquals( "CN", entry.get( atCN ).getUpId() );
+        assertEquals( 1, entry.get( atCN ).size() );
+        assertEquals( "test1", entry.get( atCN ).get().get() );
+        
+        // Test some more addition
+        entry.add( "CN", "test2", "test3" );
+        assertNotNull( entry.get( atCN ) );
+        assertEquals( 3, entry.get( atCN ).size() );
+        assertTrue( entry.contains( atCN, "test1" ) );
+        assertTrue( entry.contains( atCN, "test2" ) );
+        assertTrue( entry.contains( atCN, "test3" ) );
+        
+        // Test some addition of existing values
+        entry.add( "CN", "test2" );
+        assertNotNull( entry.get( atCN ) );
+        assertEquals( 3, entry.get( atCN ).size() );
+        assertTrue( entry.contains( atCN, "test1" ) );
+        assertTrue( entry.contains( atCN, "test2" ) );
+        assertTrue( entry.contains( atCN, "test3" ) );
+        
+        // Test the addition of a null value
+        entry.add( "CN", (String)null );
+        assertNotNull( entry.get( atCN ) );
+        assertEquals( 4, entry.get( atCN ).size() );
+        assertTrue( entry.contains( atCN, "test1" ) );
+        assertTrue( entry.contains( atCN, "test2" ) );
+        assertTrue( entry.contains( atCN, "test3" ) );
+        assertTrue( entry.contains( atCN, (String )null ) ); 
+        
+        entry.clear();
+        
+        // Test the addition of a binary value
+        byte[] test4 = StringTools.getBytesUtf8( "test4" );
+        
+        entry.add( "CN", test4 );
+        assertFalse( entry.contains(  "CN", test4 ) );
+    }
+
+
+    /**
+     * Test the add( upId, byte[]... ) method
+     */
+    @Test public void testAddUpIdBytesElipsis() throws NamingException
+    {
+        LdapDN dn = new LdapDN( "cn=test" );
+        DefaultServerEntry entry = new DefaultServerEntry( registries, dn );
+        
+        AttributeType atPassword = registries.getAttributeTypeRegistry().lookup( "userPassword" );
+        
+        byte[] test1 = StringTools.getBytesUtf8( "test1" );
+        byte[] test2 = StringTools.getBytesUtf8( "test2" );
+        byte[] test3 = StringTools.getBytesUtf8( "test3" );
+        
+        // Test a simple addition
+        entry.add( "userPassword", test1 );
+        assertNotNull( entry.get( atPassword ) );
+        assertEquals( 1, entry.get( atPassword ).size() );
+        assertTrue( Arrays.equals( test1, (byte[])entry.get( atPassword ).get().get() ) );
+        
+        // Test some more addition
+        entry.add( "userPassword", test2, test3 );
+        assertNotNull( entry.get( atPassword ) );
+        assertEquals( 3, entry.get( atPassword ).size() );
+        assertTrue( entry.contains( atPassword, test1 ) );
+        assertTrue( entry.contains( atPassword, test2 ) );
+        assertTrue( entry.contains( atPassword, test3 ) );
+        
+        // Test some addition of existing values
+        entry.add( "userPassword", test2 );
+        assertNotNull( entry.get( atPassword ) );
+        assertEquals( 3, entry.get( atPassword ).size() );
+        assertTrue( entry.contains( atPassword, test1 ) );
+        assertTrue( entry.contains( atPassword, test2 ) );
+        assertTrue( entry.contains( atPassword, test3 ) );
+        
+        // Test the addition of a null value
+        entry.add( "userPassword", (byte[])null );
+        assertNotNull( entry.get( atPassword ) );
+        assertEquals( 4, entry.get( atPassword ).size() );
+        assertTrue( entry.contains( atPassword, test1 ) );
+        assertTrue( entry.contains( atPassword, test2 ) );
+        assertTrue( entry.contains( atPassword, test3 ) );
+        assertTrue( entry.contains( atPassword, (byte[] )null ) ); 
+        
+        entry.clear();
+        
+        // Test the addition of a String value. It should be converted to a byte array
+        byte[] test4 = StringTools.getBytesUtf8( "test4" );
+
+        entry.add( "userPassword", "test4" );
+        assertNotNull( entry.get( atPassword ) );
+        assertEquals( 0, entry.get( atPassword ).size() );
+        assertFalse( entry.contains( atPassword, test4 ) );
+    }
+
+
+    /**
+     * Test the add( upId, SV... ) method
+     */
+    @Test public void testAddUpIdServerValueElipsis() throws NamingException
+    {
+        LdapDN dn = new LdapDN( "cn=test" );
+        ServerEntry entry = new DefaultServerEntry( registries, dn );
+        
+        AttributeType atPassword = registries.getAttributeTypeRegistry().lookup( "userPassword" );
+        
+        byte[] b1 = StringTools.getBytesUtf8( "test1" );
+        byte[] b2 = StringTools.getBytesUtf8( "test2" );
+        byte[] b3 = StringTools.getBytesUtf8( "test3" );
+
+        Value<String> test1 = new ServerStringValue( atCN, "test1" );
+        Value<String> test2 = new ServerStringValue( atCN, "test2" );
+        Value<String> test3 = new ServerStringValue( atCN, "test3" );
+        
+        Value<byte[]> testB1 = new ServerBinaryValue( atPassword, b1 );
+        Value<byte[]> testB2 = new ServerBinaryValue( atPassword, b2 );
+        Value<byte[]> testB3 = new ServerBinaryValue( atPassword, b3 );
+        
+        // Test a simple addition in atCN
+        entry.add( "cN", test1 );
+        assertNotNull( entry.get( atCN ) );
+        assertEquals( 1, entry.get( atCN ).size() );
+        assertEquals( "test1", entry.get( atCN ).get().get() );
+        assertTrue( entry.containsAttribute( atCN ) );
+        assertEquals( "cN", entry.get( atCN ).getUpId() );
+        
+        // Test some more addition
+        entry.add( "cN", test2, test3 );
+        assertNotNull( entry.get( atCN ) );
+        assertEquals( 3, entry.get( atCN ).size() );
+        assertTrue( entry.contains( atCN, "test1" ) );
+        assertTrue( entry.contains( atCN, "test2" ) );
+        assertTrue( entry.contains( atCN, "test3" ) );
+        assertTrue( entry.containsAttribute( atCN ) );
+        assertEquals( "cN", entry.get( atCN ).getUpId() );
+        
+        // Test some addition of existing values
+        entry.add( "cN", test2 );
+        assertNotNull( entry.get( atCN ) );
+        assertEquals( 3, entry.get( atCN ).size() );
+        assertTrue( entry.contains( atCN, "test1" ) );
+        assertTrue( entry.contains( atCN, "test2" ) );
+        assertTrue( entry.contains( atCN, "test3" ) );
+        
+        // Test the addition of a null value
+        entry.add( "cN", (String)null );
+        assertNotNull( entry.get( atCN ) );
+        assertEquals( 4, entry.get( atCN ).size() );
+        assertTrue( entry.contains( atCN, "test1" ) );
+        assertTrue( entry.contains( atCN, "test2" ) );
+        assertTrue( entry.contains( atCN, "test3" ) );
+        assertTrue( entry.contains( atCN, (String )null ) ); 
+        
+        entry.clear();
+        
+        // Test the addition of a String value. It should be converted to a byte array
+        byte[] test4 = StringTools.getBytesUtf8( "test4" );
+
+        entry.add( "cN", test4 );
+        assertFalse( entry.contains( "cN", test4 ) );
+
+        // Now, work with a binary attribute
+        // Test a simple addition
+        entry.add( "userPASSWORD", testB1 );
+        assertNotNull( entry.get( atPassword ) );
+        assertEquals( 1, entry.get( atPassword ).size() );
+        assertTrue( Arrays.equals( b1, (byte[])entry.get( atPassword ).get().get() ) );
+        assertTrue( entry.containsAttribute( atPassword ) );
+        assertEquals( "userPASSWORD", entry.get( atPassword ).getUpId() );
+        
+        // Test some more addition
+        entry.add( "userPASSWORD", testB2, testB3 );
+        assertNotNull( entry.get( atPassword ) );
+        assertEquals( 3, entry.get( atPassword ).size() );
+        assertTrue( entry.contains( atPassword, b1 ) );
+        assertTrue( entry.contains( atPassword, b2 ) );
+        assertTrue( entry.contains( atPassword, b3 ) );
+        
+        // Test some addition of existing values
+        entry.add( "userPASSWORD", testB2 );
+        assertNotNull( entry.get( atPassword ) );
+        assertEquals( 3, entry.get( atPassword ).size() );
+        assertTrue( entry.contains( atPassword, b1 ) );
+        assertTrue( entry.contains( atPassword, b2 ) );
+        assertTrue( entry.contains( atPassword, b3 ) );
+        
+        // Test the addition of a null value
+        entry.add( "userPASSWORD", (byte[])null );
+        assertNotNull( entry.get( atPassword ) );
+        assertEquals( 4, entry.get( atPassword ).size() );
+        assertTrue( entry.contains( atPassword, b1 ) );
+        assertTrue( entry.contains( atPassword, b2 ) );
+        assertTrue( entry.contains( atPassword, b3 ) );
+        assertTrue( entry.contains( atPassword, (byte[] )null ) ); 
+        
+        entry.clear();
+        
+        // Test the addition of a String value. It should be converted to a byte array
+        byte[] b4 = StringTools.getBytesUtf8( "test4" );
+
+        entry.add( "userPASSWORD", "test4" );
+        assertNotNull( entry.get( atPassword ) );
+        assertEquals( 0, entry.get( atPassword ).size() );
+        assertFalse( entry.contains( atPassword, b4 ) );
+    }
+
+
+    /**
+     * Test the add( UpId, AT, String... ) method
+     */
+    @Test public void testAddUpIdAtStringElipsis() throws NamingException
+    {
+        LdapDN dn = new LdapDN( "cn=test" );
+        DefaultServerEntry entry = new DefaultServerEntry( registries, dn );
+        
+        // Test a simple addition
+        entry.add( "cn", atCN, "test1" );
+        assertNotNull( entry.get( atCN ) );
+        assertEquals( 1, entry.get( atCN ).size() );
+        assertEquals( "test1", entry.get( atCN ).get().get() );
+        
+        // Test some more addition
+        entry.add( "CN", atCN, "test2", "test3" );
+        assertNotNull( entry.get( atCN ) );
+        assertEquals( 3, entry.get( atCN ).size() );
+        assertTrue( entry.contains( atCN, "test1" ) );
+        assertTrue( entry.contains( atCN, "test2" ) );
+        assertTrue( entry.contains( atCN, "test3" ) );
+        
+        // Test some addition of existing values
+        entry.add( "commonName", atCN, "test2" );
+        assertNotNull( entry.get( atCN ) );
+        assertEquals( 3, entry.get( atCN ).size() );
+        assertTrue( entry.contains( atCN, "test1" ) );
+        assertTrue( entry.contains( atCN, "test2" ) );
+        assertTrue( entry.contains( atCN, "test3" ) );
+        
+        // Test the addition of a null value
+        entry.add( "COMMONname", atCN, (String)null );
+        assertNotNull( entry.get( atCN ) );
+        assertEquals( 4, entry.get( atCN ).size() );
+        assertTrue( entry.contains( atCN, "test1" ) );
+        assertTrue( entry.contains( atCN, "test2" ) );
+        assertTrue( entry.contains( atCN, "test3" ) );
+        assertTrue( entry.contains( atCN, (String )null ) ); 
+        
+        entry.clear();
+        
+        // Test the addition of a binary value
+        byte[] test4 = StringTools.getBytesUtf8( "test4" );
+        
+        entry.add( "cn", atCN, test4 );
+        assertFalse( entry.contains( "cn", test4 ) );
+    }
+
+
+    /**
+     * Test the add( upId, AT, byte[]... ) method
+     */
+    @Test public void testAddUpIdAtBytesElipsis() throws NamingException
+    {
+        LdapDN dn = new LdapDN( "cn=test" );
+        DefaultServerEntry entry = new DefaultServerEntry( registries, dn );
+        
+        AttributeType atPassword = registries.getAttributeTypeRegistry().lookup( "userPassword" );
+        
+        byte[] test1 = StringTools.getBytesUtf8( "test1" );
+        byte[] test2 = StringTools.getBytesUtf8( "test2" );
+        byte[] test3 = StringTools.getBytesUtf8( "test3" );
+        
+        // Test a simple addition
+        entry.add( "userPassword", atPassword, test1 );
+        assertNotNull( entry.get( atPassword ) );
+        assertEquals( 1, entry.get( atPassword ).size() );
+        assertTrue( Arrays.equals( test1, (byte[])entry.get( atPassword ).get().get() ) );
+        
+        // Test some more addition
+        entry.add( "userPassword", atPassword, test2, test3 );
+        assertNotNull( entry.get( atPassword ) );
+        assertEquals( 3, entry.get( atPassword ).size() );
+        assertTrue( entry.contains( atPassword, test1 ) );
+        assertTrue( entry.contains( atPassword, test2 ) );
+        assertTrue( entry.contains( atPassword, test3 ) );
+        
+        // Test some addition of existing values
+        entry.add( "userPassword", atPassword, test2 );
+        assertNotNull( entry.get( atPassword ) );
+        assertEquals( 3, entry.get( atPassword ).size() );
+        assertTrue( entry.contains( atPassword, test1 ) );
+        assertTrue( entry.contains( atPassword, test2 ) );
+        assertTrue( entry.contains( atPassword, test3 ) );
+        
+        // Test the addition of a null value
+        entry.add( "userPassword", atPassword, (byte[])null );
+        assertNotNull( entry.get( atPassword ) );
+        assertEquals( 4, entry.get( atPassword ).size() );
+        assertTrue( entry.contains( atPassword, test1 ) );
+        assertTrue( entry.contains( atPassword, test2 ) );
+        assertTrue( entry.contains( atPassword, test3 ) );
+        assertTrue( entry.contains( atPassword, (byte[] )null ) ); 
+        
+        entry.clear();
+        
+        // Test the addition of a String value. It should be converted to a byte array
+        byte[] test4 = StringTools.getBytesUtf8( "test4" );
+
+        entry.add( "userPassword", atPassword, "test4" );
+        assertNotNull( entry.get( atPassword ) );
+        assertEquals( 0, entry.get( atPassword ).size() );
+        assertFalse( entry.contains( atPassword, test4 ) );
+    }
+
+
+    /**
+     * Test the add( upId, AT, SV... ) method
+     */
+    @Test public void testAddUpIdAtServerValueElipsis() throws NamingException
+    {
+        LdapDN dn = new LdapDN( "cn=test" );
+        ServerEntry entry = new DefaultServerEntry( registries, dn );
+        
+        AttributeType atPassword = registries.getAttributeTypeRegistry().lookup( "userPassword" );
+        
+        byte[] b1 = StringTools.getBytesUtf8( "test1" );
+        byte[] b2 = StringTools.getBytesUtf8( "test2" );
+        byte[] b3 = StringTools.getBytesUtf8( "test3" );
+
+        Value<String> test1 = new ServerStringValue( atCN, "test1" );
+        Value<String> test2 = new ServerStringValue( atCN, "test2" );
+        Value<String> test3 = new ServerStringValue( atCN, "test3" );
+        
+        Value<byte[]> testB1 = new ServerBinaryValue( atPassword, b1 );
+        Value<byte[]> testB2 = new ServerBinaryValue( atPassword, b2 );
+        Value<byte[]> testB3 = new ServerBinaryValue( atPassword, b3 );
+        
+        // Test a simple addition in atCN
+        entry.add( "cN", atCN, test1 );
+        assertNotNull( entry.get( atCN ) );
+        assertEquals( 1, entry.get( atCN ).size() );
+        assertEquals( "test1", entry.get( atCN ).get().get() );
+        assertTrue( entry.containsAttribute( atCN ) );
+        assertEquals( "cN", entry.get( atCN ).getUpId() );
+        
+        // Test some more addition
+        entry.add( "cN", atCN, test2, test3 );
+        assertNotNull( entry.get( atCN ) );
+        assertEquals( 3, entry.get( atCN ).size() );
+        assertTrue( entry.contains( atCN, "test1" ) );
+        assertTrue( entry.contains( atCN, "test2" ) );
+        assertTrue( entry.contains( atCN, "test3" ) );
+        assertTrue( entry.containsAttribute( atCN ) );
+        assertEquals( "cN", entry.get( atCN ).getUpId() );
+        
+        // Test some addition of existing values
+        entry.add( "cN", atCN, test2 );
+        assertNotNull( entry.get( atCN ) );
+        assertEquals( 3, entry.get( atCN ).size() );
+        assertTrue( entry.contains( atCN, "test1" ) );
+        assertTrue( entry.contains( atCN, "test2" ) );
+        assertTrue( entry.contains( atCN, "test3" ) );
+        
+        // Test the addition of a null value
+        entry.add( "cN", atCN, (String)null );
+        assertNotNull( entry.get( atCN ) );
+        assertEquals( 4, entry.get( atCN ).size() );
+        assertTrue( entry.contains( atCN, "test1" ) );
+        assertTrue( entry.contains( atCN, "test2" ) );
+        assertTrue( entry.contains( atCN, "test3" ) );
+        assertTrue( entry.contains( atCN, (String )null ) ); 
+        
+        entry.clear();
+        
+        // Test the addition of a String value. It should be converted to a byte array
+        byte[] test4 = StringTools.getBytesUtf8( "test4" );
+
+        entry.add( "cN", atCN, test4 );
+        assertFalse( entry.contains( "cN", test4 ) );
+
+        // Now, work with a binary attribute
+        // Test a simple addition
+        entry.add( "userPASSWORD", atPassword, testB1 );
+        assertNotNull( entry.get( atPassword ) );
+        assertEquals( 1, entry.get( atPassword ).size() );
+        assertTrue( Arrays.equals( b1, (byte[])entry.get( atPassword ).get().get() ) );
+        assertTrue( entry.containsAttribute( atPassword ) );
+        assertEquals( "userPASSWORD", entry.get( atPassword ).getUpId() );
+        
+        // Test some more addition
+        entry.add( "userPASSWORD", atPassword, testB2, testB3 );
+        assertNotNull( entry.get( atPassword ) );
+        assertEquals( 3, entry.get( atPassword ).size() );
+        assertTrue( entry.contains( atPassword, b1 ) );
+        assertTrue( entry.contains( atPassword, b2 ) );
+        assertTrue( entry.contains( atPassword, b3 ) );
+        
+        // Test some addition of existing values
+        entry.add( "userPASSWORD", atPassword, testB2 );
+        assertNotNull( entry.get( atPassword ) );
+        assertEquals( 3, entry.get( atPassword ).size() );
+        assertTrue( entry.contains( atPassword, b1 ) );
+        assertTrue( entry.contains( atPassword, b2 ) );
+        assertTrue( entry.contains( atPassword, b3 ) );
+        
+        // Test the addition of a null value
+        entry.add( "userPASSWORD", atPassword, (byte[])null );
+        assertNotNull( entry.get( atPassword ) );
+        assertEquals( 4, entry.get( atPassword ).size() );
+        assertTrue( entry.contains( atPassword, b1 ) );
+        assertTrue( entry.contains( atPassword, b2 ) );
+        assertTrue( entry.contains( atPassword, b3 ) );
+        assertTrue( entry.contains( atPassword, (byte[] )null ) ); 
+        
+        entry.clear();
+        
+        // Test the addition of a String value. It should be converted to a byte array
+        byte[] b4 = StringTools.getBytesUtf8( "test4" );
+
+        entry.add( "userPASSWORD", atPassword, "test4" );
+        assertNotNull( entry.get( atPassword ) );
+        assertEquals( 0, entry.get( atPassword ).size() );
+        assertFalse( entry.contains( atPassword, b4 ) );
+    }
+
+
+    /**
+     * Test method for clear()
+     */
+    @Test
+    public void testClear() throws NamingException
+    {
+        Entry entry = new DefaultServerEntry( registries, EXAMPLE_DN );
+         
+        assertEquals( 0, entry.size() );
+        assertNull( entry.get( "ObjectClass" ) );
+        entry.clear();
+        assertEquals( 0, entry.size() );
+        assertNull( entry.get( "ObjectClass" ) );
+         
+        entry.add( "ObjectClass", "top", "person" );
+        assertEquals( 1, entry.size() );
+        assertNotNull( entry.get( "ObjectClass" ) );
+        
+        entry.clear();
+        assertEquals( 0, entry.size() );
+        assertNull( entry.get( "ObjectClass" ) );
+    }
+
+
+    /**
+     * Test method for clone()
+     */
+    @Test
+    public void testClone() throws NamingException
+    {
+        Entry entry1 = new DefaultServerEntry( registries );
+        
+        Entry entry2 = entry1.clone();
+        
+        assertEquals( entry1, entry2 );
+        entry2.setDn( EXAMPLE_DN );
+        
+        assertNull( entry1.getDn() );
+        
+        entry1.setDn( EXAMPLE_DN );
+        entry2 = entry1.clone();
+        assertEquals( entry1, entry2 );
+        
+        entry1.add( "objectClass", "top", "person" );
+        entry1.add( "cn", "test1", "test2" );
+        
+        entry2 = entry1.clone();
+        assertEquals( entry1, entry2 );
+        
+        entry1.add( "cn", "test3" );
+        assertEquals( 2, entry2.get( "cn" ).size() );
+        assertFalse( entry2.contains( "cn", "test3" ) );
+        
+        entry1.add( "sn", (String)null );
+        assertFalse( entry2.containsAttribute( "sn" ) );
+    }
+     
+    
+    //-------------------------------------------------------------------------
+    // Test the Contains methods
+    //-------------------------------------------------------------------------
+    /**
+     * Test for method contains( AttributeType, byte[]... )
+     */
+    @Test
+    public void testContainsAttributeTypeByteArrayArray() throws NamingException
+    {
+        ServerEntry entry = new DefaultServerEntry( registries, EXAMPLE_DN );
+        
+        assertFalse( entry.contains( (AttributeType )null, BYTES1 ) );
+        assertFalse( entry.contains( atPwd, BYTES1 ) );
+        
+        EntryAttribute attrPWD = new DefaultServerAttribute( atPwd, BYTES1, BYTES2 );
+
+        assertFalse( entry.contains( attrPWD ) );
+        
+        entry.add( attrPWD );
+        
+        assertTrue( entry.contains( atPwd, BYTES1, BYTES2 ) );
+        assertFalse( entry.contains( atPwd, BYTES1, BYTES2, BYTES3 ) );
+        assertFalse( entry.contains( atPwd, "ab" ) );
+    }
+    
+    
+    /**
+     * Test for method contains( AttributeType, String... )
+     */
+    @Test
+    public void testContainsAttributeTypeStringArray() throws NamingException
+    {
+        ServerEntry entry = new DefaultServerEntry( registries, EXAMPLE_DN );
+        
+        assertFalse( entry.contains( (AttributeType )null, "test" ) );
+        assertFalse( entry.contains( atCN, "test" ) );
+        
+        EntryAttribute attrCN = new DefaultServerAttribute( atCN, "test1", "test2" );
+
+        assertFalse( entry.contains( attrCN ) );
+        
+        entry.add( attrCN );
+        
+        assertTrue( entry.contains( atCN, "test1", "test2" ) );
+        assertFalse( entry.contains( atCN, "test1", "test2", "test3" ) );
+        assertFalse( entry.contains( atCN, BYTES1 ) );
+    }
+    
+    
+    /**
+     * Test for method contains( AttributeType, Value<?>... )
+     */
+    @Test
+    public void testContainsAttributeTypeValuesArray() throws NamingException
+    {
+        ServerEntry entry = new DefaultServerEntry( registries, EXAMPLE_DN );
+        
+        Value<String> strValue1 = new ServerStringValue( atCN, "test1" );
+        Value<String> strValue2 = new ServerStringValue( atCN, "test2" );
+        Value<String> strValue3 = new ServerStringValue( atCN, "test3" );
+        Value<String> strNullValue = new ServerStringValue( atCN, null);
+
+        Value<byte[]> binValue1 = new ServerBinaryValue( atPwd, BYTES1 );
+        Value<byte[]> binValue2 = new ServerBinaryValue( atPwd, BYTES2 );
+        Value<byte[]> binValue3 = new ServerBinaryValue( atPwd, BYTES3 );
+        Value<byte[]> binNullValue = new ServerBinaryValue( atPwd, null );
+
+        assertFalse( entry.contains( (String)null, strValue1 ) );
+        assertFalse( entry.contains( atCN, binValue1 ) );
+        
+        EntryAttribute attrCN = new DefaultServerAttribute( atCN, strValue1, strValue2 );
+        EntryAttribute attrPWD = new DefaultServerAttribute( atPwd, binValue1, binValue2, binNullValue );
+
+        entry.add( attrCN, attrPWD );
+        
+        assertTrue( entry.contains( atCN, strValue1, strValue2 ) );
+        assertTrue( entry.contains( atPwd, binValue1, binValue2, binNullValue ) );
+        
+        assertFalse( entry.contains( atCN, strValue3 ) );
+        assertFalse( entry.contains( atCN, strNullValue ) );
+        assertFalse( entry.contains( atPwd, binValue3 ) );
+    }
+    
+    
+    /**
+     * Test for method contains( EntryAttribute... )
+     */
+    @Test
+    public void testContainsEntryAttributeArray() throws NamingException
+    {
+        Entry entry = new DefaultServerEntry( registries, EXAMPLE_DN );
+        
+        EntryAttribute attrOC = new DefaultServerAttribute( atOC, "top", "person" );
+        EntryAttribute attrCN = new DefaultServerAttribute( atCN, "test1", "test2" );
+        EntryAttribute attrSN = new DefaultServerAttribute( atSN, "Test1", "Test2" );
+        EntryAttribute attrPWD = new DefaultServerAttribute( atPwd, BYTES1, BYTES2 );
+
+        assertFalse( entry.contains( attrOC, attrCN ) );
+        
+        entry.add( attrOC, attrCN );
+
+        assertTrue( entry.contains( attrOC, attrCN ) );
+        assertFalse( entry.contains( attrOC, attrCN, attrSN ) );
+        
+        entry.add( attrSN, attrPWD );
+
+        assertTrue( entry.contains( attrSN, attrPWD ) );
+        
+        assertFalse( entry.contains( (EntryAttribute)null ) );
+        entry.clear();
+        assertTrue( entry.contains( (EntryAttribute)null ) );
+    }
+    
+    
+    /**
+     * Test for method contains( String, byte[]... )
+     */
+    @Test
+    public void testContainsStringByteArrayArray() throws NamingException
+    {
+        Entry entry = new DefaultServerEntry( registries, EXAMPLE_DN );
+        
+        assertFalse( entry.contains( (String)null, BYTES3 ) );
+        assertFalse( entry.containsAttribute( "objectClass" ) );
+        
+        EntryAttribute attrPWD = new DefaultServerAttribute( atPwd, BYTES1, (byte[])null, BYTES2 );
+
+        entry.add( attrPWD );
+        
+        assertTrue( entry.contains( "  userPASSWORD  ", BYTES1, BYTES2 ) );
+        assertTrue( entry.contains( "  userPASSWORD  ", (byte[])null ) );
+        
+        assertFalse( entry.contains( "  userPASSWORD  ", "ab", "b" ) );
+        assertFalse( entry.contains( "  userPASSWORD  ", BYTES3 ) );
+        assertFalse( entry.contains( "  userASSWORD  ", BYTES3 ) );
+    }
+    
+    
+    /**
+     * Test for method contains( String, String... )
+     */
+    @Test
+    public void testContainsStringStringArray() throws NamingException
+    {
+        Entry entry = new DefaultServerEntry( registries, EXAMPLE_DN );
+        
+        assertFalse( entry.contains( (String)null, "test" ) );
+        assertFalse( entry.containsAttribute( "objectClass" ) );
+        
+        EntryAttribute attrCN = new DefaultServerAttribute( atCN, "test1", (String)null, "test2" );
+
+        entry.add( attrCN );
+        
+        assertTrue( entry.contains( "  CN  ", "test1", "test2" ) );
+        
+        assertTrue( entry.contains( "  CN  ", (String)null ) );
+        assertFalse( entry.contains( "  CN  ", BYTES1, BYTES2 ) );
+        assertFalse( entry.contains( "  CN  ", "test3" ) );
+        assertFalse( entry.contains( "  CNN  ", "test3" ) );
+    }
+    
+    
+    /**
+     * Test for method contains( String, Value<?>... )
+     */
+    @Test
+    public void testContainsStringValueArray() throws NamingException
+    {
+        Entry entry = new DefaultServerEntry( registries, EXAMPLE_DN );
+        
+        assertFalse( entry.contains( (String)null, "test" ) );
+        assertFalse( entry.containsAttribute( "objectClass" ) );
+        
+        EntryAttribute attrCN = new DefaultServerAttribute( atCN, "test1", "test2", (String)null );
+        EntryAttribute attrPWD = new DefaultServerAttribute( atPwd, BYTES1, BYTES2, (byte[])null );
+
+        entry.add( attrCN, attrPWD );
+        
+        Value<String> strValue1 = new ServerStringValue( atCN, "test1" );
+        Value<String> strValue2 = new ServerStringValue( atCN, "test2" );
+        Value<String> strValue3 = new ServerStringValue( atCN, "test3" );
+        Value<String> strNullValue = new ServerStringValue( atCN, null);
+
+        Value<byte[]> binValue1 = new ServerBinaryValue( atPwd, BYTES1 );
+        Value<byte[]> binValue2 = new ServerBinaryValue( atPwd, BYTES2 );
+        Value<byte[]> binValue3 = new ServerBinaryValue( atPwd, BYTES3 );
+        Value<byte[]> binNullValue = new ServerBinaryValue( atPwd, null );
+
+        assertTrue( entry.contains( "CN", strValue1, strValue2 ) );
+        assertTrue( entry.contains( "userpassword", binValue1, binValue2, binNullValue ) );
+        
+        assertFalse( entry.contains( "cn", strValue3 ) );
+        assertTrue( entry.contains( "cn", strNullValue ) );
+        assertFalse( entry.contains( "UserPassword", binValue3 ) );
+    }
+    
+    
+    /**
+     * Test method for containsAttribute( AttributeType )
+     */
+    @Test
+    public void testContainsAttributeAttributeType() throws NamingException
+    {
+        ServerEntry entry = new DefaultServerEntry( registries, EXAMPLE_DN );
+        
+        assertFalse( entry.containsAttribute( atOC ) );
+        
+        EntryAttribute attrOC = new DefaultServerAttribute( atOC, "top", "person" );
+        EntryAttribute attrCN = new DefaultServerAttribute( atCN, "test1", "test2" );
+        EntryAttribute attrSN = new DefaultServerAttribute( atSN, "Test1", "Test2" );
+        EntryAttribute attrPWD = new DefaultServerAttribute( atPwd, BYTES1, BYTES2 );
+
+        entry.add( attrOC, attrCN, attrSN, attrPWD );
+        
+        assertTrue( entry.containsAttribute( atOC ) );
+        assertTrue( entry.containsAttribute( atCN ) );
+        assertTrue( entry.containsAttribute( atSN ) );
+        assertTrue( entry.containsAttribute( atPwd ) );
+        
+        entry.clear();
+
+        assertFalse( entry.containsAttribute( atOC ) );
+        assertFalse( entry.containsAttribute( atCN ) );
+        assertFalse( entry.containsAttribute( atSN ) );
+        assertFalse( entry.containsAttribute( atPwd ) );
+    }
+
+
+    /**
+     * Test method for containsAttribute( String )
+     */
+    @Test
+    public void testContainsAttributeString() throws NamingException
+    {
+        Entry entry = new DefaultServerEntry( registries, EXAMPLE_DN );
+        
+        assertFalse( entry.containsAttribute( "objectClass" ) );
+        
+        EntryAttribute attrOC = new DefaultServerAttribute( atOC, "top", "person" );
+        EntryAttribute attrCN = new DefaultServerAttribute( atCN, "test1", "test2" );
+        EntryAttribute attrSN = new DefaultServerAttribute( atSN, "Test1", "Test2" );
+        EntryAttribute attrPWD = new DefaultServerAttribute( atPwd, BYTES1, BYTES2 );
+
+        entry.add( attrOC, attrCN, attrSN, attrPWD );
+        
+        assertTrue( entry.containsAttribute( "OBJECTCLASS", " cn ", "Sn", "  userPASSWORD  " ) );
+        
+        entry.clear();
+
+        assertFalse( entry.containsAttribute( "OBJECTCLASS" ) );
+        assertFalse( entry.containsAttribute( " cn " ) );
+        assertFalse( entry.containsAttribute( "Sn" ) );
+        assertFalse( entry.containsAttribute( "  userPASSWORD  " ) );
+        assertFalse( entry.containsAttribute( "  userASSWORD  " ) );
+    }
+
+    
+    /**
+     * Test method for equals()
+     */
+    @Test
+    public void testEqualsObject() throws NamingException
+    {
+        Entry entry1 = new DefaultServerEntry( registries );
+        Entry entry2 = new DefaultServerEntry( registries );
+        
+        assertEquals( entry1, entry2 );
+        
+        entry1.setDn( EXAMPLE_DN );
+        assertNotSame( entry1, entry2 );
+        
+        entry2.setDn( EXAMPLE_DN );
+        assertEquals( entry1, entry2 );
+
+        EntryAttribute attrOC = new DefaultServerAttribute( "objectClass", atOC, "top", "person" );
+        EntryAttribute attrCN = new DefaultServerAttribute( "cn", atCN, "test1", "test2" );
+        EntryAttribute attrSN = new DefaultServerAttribute( "sn", atSN, "Test1", "Test2" );
+        EntryAttribute attrPWD = new DefaultServerAttribute( "userPassword", atPwd, BYTES1, BYTES2 );
+        
+        entry1.put( attrOC, attrCN, attrSN, attrPWD );
+        entry2.put( attrOC, attrCN, attrSN );
+        assertNotSame( entry1, entry2 );
+        
+        entry2.put( attrPWD );
+        assertEquals( entry1, entry2 );
+        
+        EntryAttribute attrL1 = new DefaultServerAttribute( "l", atL, "Paris", "New-York" );
+        EntryAttribute attrL2 = new DefaultServerAttribute( "l", atL, "Paris", "Tokyo" );
+        
+        entry1.put( attrL1 );
+        entry2.put( attrL1 );
+        assertEquals( entry1, entry2 );
+        
+        entry1.add( "l", "London" );
+        assertNotSame( entry1, entry2 );
+
+        entry2.add( attrL2 );
+        assertNotSame( entry1, entry2 );
+
+        entry1.clear();
+        entry2.clear();
+        assertEquals( entry1, entry2 );
+    }
+
+
+    /**
+     * Test method for getAttributeTypes()
+     */
+    @Test
+    public void testGetAttributeTypes() throws NamingException
+    {
+        ServerEntry entry = new DefaultServerEntry( registries, EXAMPLE_DN );
+        
+        assertEquals( 0, entry.getAttributeTypes().size() );
+
+        EntryAttribute attrOC = new DefaultServerAttribute( atOC, "top", "person" );
+        EntryAttribute attrCN = new DefaultServerAttribute( atCN, "test1", "test2" );
+        EntryAttribute attrSN = new DefaultServerAttribute( atSN, "Test1", "Test2" );
+        EntryAttribute attrPWD = new DefaultServerAttribute( atPwd, BYTES1, BYTES2 );
+
+        entry.add( attrOC, attrCN, attrSN, attrPWD );
+        
+        Set<AttributeType> attributeTypes = entry.getAttributeTypes();
+        
+        assertEquals( 4, attributeTypes.size() );
+        assertTrue( attributeTypes.contains( atOC ) );
+        assertTrue( attributeTypes.contains( atCN ) );
+        assertTrue( attributeTypes.contains( atSN ) );
+        assertTrue( attributeTypes.contains( atPwd ) );
+        assertFalse( attributeTypes.contains( atC ) );
+    }
+
+
+    /**
+     * Test method for get( AttributeType )
+     */
+    @Test
+    public void testGetAttributeType() throws NamingException 
+    {
+        ServerEntry entry = new DefaultServerEntry( registries, EXAMPLE_DN );
+
+        assertNull( entry.get( atCN ) );
+        assertNull( entry.get( (AttributeType)null ) );
+        
+        EntryAttribute attrOC = new DefaultServerAttribute( atOC, "top", "person" );
+        EntryAttribute attrCN = new DefaultServerAttribute( atCN, "test1", "test2" );
+        EntryAttribute attrSN = new DefaultServerAttribute( atSN, "Test1", "Test2" );
+        EntryAttribute attrPWD = new DefaultServerAttribute( atPwd, BYTES1, BYTES2 );
+        
+        entry.add( attrOC, attrCN, attrSN, attrPWD );
+        
+        assertNotNull( entry.get( atCN ) );
+        
+        assertEquals( attrCN, entry.get( atCN ) );
+        assertEquals( attrOC, entry.get( atOC ) );
+        assertEquals( attrSN, entry.get( atSN ) );
+        assertEquals( attrPWD, entry.get( atPwd ) );
+    }
+        
+
+    /**
+     * Test method for get( String )
+     */
+    @Test
+    public void testGetString() throws NamingException 
+    {
+        Entry entry = new DefaultServerEntry( registries, EXAMPLE_DN );
+
+        assertNull( entry.get( "cn" ) );
+        assertNull( entry.get( "badId" ) );
+        
+        EntryAttribute attrOC = new DefaultServerAttribute( atOC, "top", "person" );
+        EntryAttribute attrCN = new DefaultServerAttribute( atCN, "test1", "test2" );
+        EntryAttribute attrSN = new DefaultServerAttribute( atSN, "Test1", "Test2" );
+        EntryAttribute attrPWD = new DefaultServerAttribute( atPwd, BYTES1, BYTES2 );
+        
+        entry.add( attrOC, attrCN, attrSN, attrPWD );
+        
+        assertNotNull( entry.get( "CN" ) );
+        assertNotNull( entry.get( " commonName " ) );
+        assertNotNull( entry.get( "2.5.4.3" ) );
+        
+        assertEquals( attrCN, entry.get( "2.5.4.3" ) );
+        assertEquals( attrOC, entry.get( " OBJECTCLASS" ) );
+        assertEquals( attrSN, entry.get( "sn" ) );
+        assertEquals( attrPWD, entry.get( "  userPassword  " ) );
+    }
+        
+
+    /**
+     * Test method for getDN()
+     */
+    @Test
+    public void testGetDn() throws InvalidNameException 
+    {
+        Entry entry = new DefaultServerEntry( registries, EXAMPLE_DN );
+         
+        assertEquals( EXAMPLE_DN, entry.getDn() );
+         
+        LdapDN testDn = new LdapDN( "cn=test" );
+        entry.setDn( testDn );
+         
+        assertEquals( testDn, entry.getDn() );
+    }
+
+
+    /**
+     * Test method for hashcode()
+     */
+    @Test
+    public void testHashCode() throws InvalidNameException, NamingException
+    {
+        Entry entry1 = new DefaultServerEntry( registries, EXAMPLE_DN );
+        Entry entry2 = new DefaultServerEntry( registries, EXAMPLE_DN );
+        
+        assertEquals( entry1.hashCode(), entry2.hashCode() );
+        
+        entry2.setDn( new LdapDN( "ou=system,dc=com" ) );
+        assertNotSame( entry1.hashCode(), entry2.hashCode() );
+        
+        entry2.setDn( EXAMPLE_DN );
+        assertEquals( entry1.hashCode(), entry2.hashCode() );
+        
+        
+        EntryAttribute attrOC = new DefaultServerAttribute( "objectClass", atOC, "top", "person" );
+        EntryAttribute attrCN = new DefaultServerAttribute( "cn", atCN, "test1", "test2" );
+        EntryAttribute attrSN = new DefaultServerAttribute( "sn", atSN, "Test1", "Test2" );
+        EntryAttribute attrPWD = new DefaultServerAttribute( "userPassword", atPwd, BYTES1, BYTES2 );
+
+        entry1.add( attrOC, attrCN, attrSN, attrPWD );
+        entry2.add( attrOC, attrCN, attrSN, attrPWD );
+
+        assertEquals( entry1.hashCode(), entry2.hashCode() );
+        
+        Entry entry3 = new DefaultServerEntry( registries, EXAMPLE_DN );
+        entry3.add( attrOC, attrSN, attrCN, attrPWD );
+
+        assertEquals( entry1.hashCode(), entry3.hashCode() );
+    }
+
+    
+    /**
+     * Test method for hasObjectClass( EntryAttribute )
+     */
+    @Test
+    public void testHasObjectClassEntryAttribute() throws NamingException
+    {
+        ServerEntry entry = new DefaultServerEntry( registries, EXAMPLE_DN );
+        
+        EntryAttribute attrOC = new DefaultServerAttribute( atOC, "top", "person" );
+        
+        assertFalse( entry.contains( attrOC ) );
+        assertFalse( entry.hasObjectClass( attrOC ) );
+        
+        entry.add( attrOC );
+        
+        assertTrue( entry.hasObjectClass( attrOC ) );
+
+        EntryAttribute attrOC2 = new DefaultServerAttribute( atOC, "person" );
+        assertTrue( entry.hasObjectClass( attrOC2 ) );
+
+        EntryAttribute attrOC3 = new DefaultServerAttribute( atOC, "inetOrgPerson" );
+        assertFalse( entry.hasObjectClass( attrOC3 ) );
+        assertFalse( entry.hasObjectClass( (EntryAttribute)null ) );
+
+        EntryAttribute attrCN = new DefaultServerAttribute( atCN, "top" );
+        assertFalse( entry.hasObjectClass( attrCN ) );
+    }
+
+    
+    /**
+     * Test method for hasObjectClass( String )
+     */
+    @Test
+    public void testHasObjectClassString() throws NamingException
+    {
+        Entry entry = new DefaultServerEntry( registries, EXAMPLE_DN );
+        
+        assertFalse( entry.containsAttribute( "objectClass" ) );
+        assertFalse( entry.hasObjectClass( "top" ) );
+        
+        entry.add( new DefaultServerAttribute( atOC, "top", "person" ) );
+        
+        assertTrue( entry.hasObjectClass( "top" ) );
+        assertTrue( entry.hasObjectClass( "person" ) );
+        assertFalse( entry.hasObjectClass( "inetorgperson" ) );
+        assertFalse( entry.hasObjectClass( null ) );
+        assertFalse( entry.hasObjectClass( "" ) );
+    }
+
+    
+    /**
+     * Test method for isValid()
+     */
+    @Test
+    public void testIsValid()
+    {
+        // @TODO Implement me !
+        assertTrue( true );
+    }
+
+
+    /**
+     * Test method for isValid( AttributeType )
+     */
+    @Test
+    public void testIsValidAttributeType()
+    {
+        // @TODO Implement me !
+        assertTrue( true );
+    }
+    
+    
+    /**
+     * Test method for isValid( String )
+     */
+    @Test
+    public void testIsValidString()
+    {
+        // @TODO Implement me !
+        assertTrue( true );
+    }
+    
+    
+    /**
+     * Test method for Iterator()
+     */
+    @Test
+    public void testIterator() throws NamingException
+    {
+        Entry entry = new DefaultServerEntry( registries, EXAMPLE_DN );
+        
+        EntryAttribute attrOC = new DefaultServerAttribute( atOC, "top", "person" );
+        EntryAttribute attrCN = new DefaultServerAttribute( atCN, "test1", "test2" );
+        EntryAttribute attrSN = new DefaultServerAttribute( atSN, "Test1", "Test2" );
+        EntryAttribute attrPWD = new DefaultServerAttribute( atPwd, BYTES1, BYTES2 );
+        
+        entry.put( attrOC, attrCN, attrSN, attrPWD );
+        
+        Iterator<EntryAttribute> iterator = entry.iterator();
+        
+        assertTrue( iterator.hasNext() );
+        
+        Set<AttributeType> expectedIds = new HashSet<AttributeType>();
+        expectedIds.add( atOC );
+        expectedIds.add( atCN );
+        expectedIds.add( atSN );
+        expectedIds.add( atPwd );
+        
+        while ( iterator.hasNext() )
+        {
+            EntryAttribute attribute = iterator.next();
+            
+            AttributeType attributeType = ((ServerAttribute)attribute).getAttributeType();
+            assertTrue( expectedIds.contains( attributeType ) );
+            expectedIds.remove( attributeType );
+        }
+        
+        assertEquals( 0, expectedIds.size() );
+    }
+
+    
+    //-------------------------------------------------------------------------
+    // Test the Put methods
+    //-------------------------------------------------------------------------
+    /**
+     * Test for method put( AttributeType, byte[]... )
+     */
+    @Test
+    public void testPutAttributeTypeByteArrayArray() throws NamingException
+    {
+        ServerEntry entry = new DefaultServerEntry( registries, EXAMPLE_DN );
+        
+        try
+        {
+            entry.put( (AttributeType)null, BYTES1 );
+            fail();
+        }
+        catch ( IllegalArgumentException iae )
+        {
+            assertTrue( true );
+        }
+        
+        entry.put( atPwd, (byte[])null );
+        assertEquals( 1, entry.size() );
+        assertTrue( entry.containsAttribute( atPwd ) );
+        assertTrue( entry.contains( atPwd, (byte[])null ) );
+        
+        EntryAttribute replaced = entry.put( atPwd, BYTES1, BYTES2, BYTES1 );
+        assertNotNull( replaced );
+        assertEquals( atPwd, ((ServerAttribute)replaced).getAttributeType() );
+        assertTrue( replaced.contains( (byte[])null ) );
+        assertEquals( 1, entry.size() );
+        assertTrue( entry.contains( atPwd, BYTES1, BYTES2 ) );
+        assertFalse( entry.contains( atPwd, BYTES3 ) );
+        assertEquals( 2, entry.get( atPwd ).size() );
+        
+        replaced = entry.put( atPwd, "test" );
+        assertNotNull( replaced );
+        assertTrue( replaced.contains( BYTES1, BYTES2 ) );
+        
+        EntryAttribute attribute = entry.get( atPwd );
+        assertEquals( 0, attribute.size() );
+    }
+    
+    
+    /**
+     * Test for method put( AttributeType, String... )
+     */
+    @Test
+    public void testPutAttributeTypeStringArray() throws NamingException
+    {
+        ServerEntry entry = new DefaultServerEntry( registries, EXAMPLE_DN );
+        
+        try
+        {
+            entry.put( (AttributeType)null, "test" );
+            fail();
+        }
+        catch ( IllegalArgumentException iae )
+        {
+            assertTrue( true );
+        }
+        
+        entry.put( atCN, (String)null );
+        assertEquals( 1, entry.size() );
+        assertTrue( entry.containsAttribute( atCN) );
+        assertTrue( entry.contains( atCN, (String)null ) );
+        
+        EntryAttribute replaced = entry.put( atCN, "test1", "test2", "test1" );
+        assertNotNull( replaced );
+        assertEquals( atCN, ((ServerAttribute)replaced).getAttributeType() );
+        assertTrue( replaced.contains( (String)null ) );
+        assertEquals( 1, entry.size() );
+        assertTrue( entry.contains( atCN, "test1", "test2" ) );
+        assertFalse( entry.contains( atCN, "test3" ) );
+        assertEquals( 2, entry.get( atCN ).size() );
+        
+        replaced = entry.put( atCN, BYTES1 );
+        assertNotNull( replaced );
+        assertTrue( replaced.contains( "test1", "test2" ) );
+        
+        EntryAttribute attribute = entry.get( atCN );
+        assertEquals( 0, attribute.size() );
+    }
+    
+    
+    /**
+     * Test for method put( AttributeType, Value<?>... )
+     */
+    @Test
+    public void testPutAttributeTypeValueArray() throws NamingException
+    {
+        ServerEntry entry = new DefaultServerEntry( registries, EXAMPLE_DN );
+        
+        Value<String> strValue1 = new ServerStringValue( atCN, "test1" );
+        Value<String> strValue2 = new ServerStringValue( atCN, "test2" );
+        Value<String> strValue3 = new ServerStringValue( atCN, "test3" );
+        Value<String> strNullValue = new ServerStringValue( atCN, null);
+
+        Value<byte[]> binValue1 = new ServerBinaryValue( atPwd, BYTES1 );
+
+        try
+        {
+            entry.put( (AttributeType)null, strValue1 );
+            fail();
+        }
+        catch ( IllegalArgumentException iae )
+        {
+            assertTrue( true );
+        }
+        
+        entry.put( atCN, strNullValue );
+        assertEquals( 1, entry.size() );
+        assertTrue( entry.containsAttribute( atCN) );
+        assertTrue( entry.contains( atCN, (String)null ) );
+        
+        EntryAttribute replaced = entry.put( atCN, strValue1, strValue2, strValue1 );
+        assertNotNull( replaced );
+        assertEquals( atCN, ((ServerAttribute)replaced).getAttributeType() );
+        assertTrue( replaced.contains( (String)null ) );
+        assertEquals( 1, entry.size() );
+        assertTrue( entry.contains( atCN, strValue1, strValue2 ) );
+        assertFalse( entry.contains( atCN, strValue3 ) );
+        assertEquals( 2, entry.get( atCN ).size() );
+        
+        replaced = entry.put( atCN, binValue1 );
+        assertNotNull( replaced );
+        assertTrue( replaced.contains( strValue1, strValue2 ) );
+        
+        EntryAttribute attribute = entry.get( atCN );
+        assertEquals( 0, attribute.size() );
+    }
+    
+    
+    /**
+     * Test for method put( EntryAttribute...)
+     */
+    @Test
+    public void testPutEntryAttribute() throws NamingException
+    {
+        Entry entry = new DefaultServerEntry( registries, EXAMPLE_DN );
+        
+        EntryAttribute oc = new DefaultServerAttribute( atObjectClass, "top", "person" );
+        EntryAttribute cn = new DefaultServerAttribute( atCN, "test1", "test2" );
+        EntryAttribute sn = new DefaultServerAttribute( atSN, "Test1", "Test2" );
+        EntryAttribute up = new DefaultServerAttribute( atPwd, BYTES1, BYTES2 );
+        EntryAttribute c = new DefaultServerAttribute( atC, "FR", "US" );
+        
+        List<EntryAttribute> removed = entry.put( oc, cn, sn, c );
+        
+        assertEquals( 4, entry.size() );
+        assertEquals( 0, removed.size() );
+        assertTrue( entry.containsAttribute( "ObjectClass" ) );
+        assertTrue( entry.containsAttribute( "CN" ) );
+        assertTrue( entry.containsAttribute( "  sn  " ) );
+        assertTrue( entry.containsAttribute( " countryName  " ) );
+    
+        EntryAttribute attr = entry.get( "objectclass" );
+        assertEquals( 2, attr.size() );
+        
+        EntryAttribute c2 = new DefaultServerAttribute( atC, "UK", "DE" );
+        removed = entry.put( c2, up );
+        assertEquals( 1, removed.size() );
+        assertEquals( c, removed.get( 0 ) );
+        assertTrue( removed.get( 0 ).contains( "FR" ) );
+        assertTrue( removed.get( 0 ).contains( "US" ) );
+        
+        assertEquals( 5, entry.size() );
+        
+        assertTrue( entry.containsAttribute( "userPassword" ) );
+        assertTrue( entry.containsAttribute( " countryName " ) );
+
+        EntryAttribute attrC = entry.get( "countryName" );
+        assertEquals( 2, attrC.size() );
+        assertTrue( attrC.contains( "UK", "DE" ) );
+
+        c2.clear();
+        entry.put( c2 );
+        assertEquals( 5, entry.size() );
+        attrC = entry.get( "countryName" );
+        assertEquals( 0, attrC.size() );
+    }
+
+    
+    /**
+     * Test for method put( String, AttributeType, byte[]... )
+     */
+    @Test
+    public void testPutStringAttributeTypeByteArrayArray() throws NamingException
+    {
+        ServerEntry entry = new DefaultServerEntry( registries, EXAMPLE_DN );
+        
+        try
+        {
+            entry.put( (String)null, (AttributeType)null, BYTES1 );
+            fail();
+        }
+        catch ( IllegalArgumentException iae )
+        {
+            assertTrue( true );
+        }
+        
+        try
+        {
+            entry.put( " ", (AttributeType)null, BYTES1 );
+            fail();
+        }
+        catch ( IllegalArgumentException iae )
+        {
+            assertTrue( true );
+        }
+        
+        try
+        {
+            entry.put( "badAttr", (AttributeType)null, BYTES1 );
+            fail();
+        }
+        catch ( IllegalArgumentException iae )
+        {
+            assertTrue( true );
+        }
+        
+        try
+        {
+            entry.put( "badAttr", atPwd, BYTES1 );
+            fail();
+        }
+        catch ( NoSuchAttributeException nsae )
+        {
+            assertTrue( true );
+        }
+        
+        entry.put( "UserPassword", atPwd, (byte[])null );
+        assertEquals( 1, entry.size() );
+        assertTrue( entry.containsAttribute( atPwd ) );
+        assertTrue( entry.contains( atPwd, (byte[])null ) );
+        
+        assertEquals( "UserPassword", entry.get( atPwd ).getUpId() );
+        
+        EntryAttribute replaced = entry.put( "USERpassword ", atPwd, BYTES1, BYTES2, BYTES1 );
+        assertNotNull( replaced );
+        assertEquals( atPwd, ((ServerAttribute)replaced).getAttributeType() );
+        assertTrue( replaced.contains( (byte[])null ) );
+        assertEquals( 1, entry.size() );
+        assertTrue( entry.contains( atPwd, BYTES1, BYTES2 ) );
+        assertFalse( entry.contains( atPwd, BYTES3 ) );
+        assertEquals( 2, entry.get( atPwd ).size() );
+        assertEquals( "USERpassword", entry.get( atPwd ).getUpId() );
+        
+        replaced = entry.put( "userpassword", atPwd, "test" );
+        assertNotNull( replaced );
+        assertTrue( replaced.contains( BYTES1, BYTES2 ) );
+        assertEquals( "userpassword", entry.get( atPwd ).getUpId() );
+        
+        EntryAttribute attribute = entry.get( atPwd );
+        assertEquals( 0, attribute.size() );
+    }
+    
+    
+    /**
+     * Test for method put( String, AttributeType, String... )
+     */
+    @Test
+    public void testPutStringAttributeTypeStringArray() throws NamingException
+    {
+        ServerEntry entry = new DefaultServerEntry( registries, EXAMPLE_DN );
+        
+        try
+        {
+            entry.put( (String)null, (AttributeType)null, "test" );
+            fail();
+        }
+        catch ( IllegalArgumentException iae )
+        {
+            assertTrue( true );
+        }
+        
+        try
+        {
+            entry.put( " ", (AttributeType)null, "test" );
+            fail();
+        }
+        catch ( IllegalArgumentException iae )
+        {
+            assertTrue( true );
+        }
+        
+        try
+        {
+            entry.put( "badAttr", (AttributeType)null, "test" );
+            fail();
+        }
+        catch ( NoSuchAttributeException nsae )
+        {
+            assertTrue( true );
+        }
+        
+        try
+        {
+            entry.put( "badAttr", atCN, "test" );
+            fail();
+        }
+        catch ( NoSuchAttributeException nsae )
+        {
+            assertTrue( true );
+        }
+        
+        entry.put( "CN", atCN, (String)null );
+        assertEquals( 1, entry.size() );
+        assertTrue( entry.containsAttribute( atCN) );
+        assertTrue( entry.contains( atCN, (String)null ) );
+        assertEquals( "CN", entry.get( atCN ).getUpId() );
+        
+        EntryAttribute replaced = entry.put( "commonName", atCN, "test1", "test2", "test1" );
+        assertNotNull( replaced );
+        assertEquals( atCN, ((ServerAttribute)replaced).getAttributeType() );
+        assertEquals( "commonName", entry.get( atCN).getUpId() );
+        assertTrue( replaced.contains( (String)null ) );
+        assertEquals( 1, entry.size() );
+        assertTrue( entry.contains( atCN, "test1", "test2" ) );
+        assertFalse( entry.contains( atCN, "test3" ) );
+        assertEquals( 2, entry.get( atCN ).size() );
+        
+        replaced = entry.put( "2.5.4.3", atCN, BYTES1 );
+        assertNotNull( replaced );
+        assertTrue( replaced.contains( "test1", "test2" ) );
+        assertEquals( "2.5.4.3", entry.get( atCN).getUpId() );
+        
+        EntryAttribute attribute = entry.get( atCN );
+        assertEquals( 0, attribute.size() );
+    }
+    
+    
+    /**
+     * Test for method put( String, AttributeType, Value<?>... )
+     */
+    @Test
+    public void testPutStringAttributeTypeValueArray() throws NamingException
+    {
+        ServerEntry entry = new DefaultServerEntry( registries, EXAMPLE_DN );
+        
+        Value<String> strValue1 = new ServerStringValue( atCN, "test1" );
+        Value<String> strValue2 = new ServerStringValue( atCN, "test2" );
+        Value<String> strValue3 = new ServerStringValue( atCN, "test3" );
+        Value<String> strNullValue = new ServerStringValue( atCN, null);
+
+        Value<byte[]> binValue1 = new ServerBinaryValue( atPwd, BYTES1 );
+
+        try
+        {
+            entry.put( (String)null, (AttributeType)null, strValue1 );
+            fail();
+        }
+        catch ( IllegalArgumentException iae )
+        {
+            assertTrue( true );
+        }
+        
+        try
+        {
+            entry.put( " ", (AttributeType)null, strValue1 );
+            fail();
+        }
+        catch ( IllegalArgumentException iae )
+        {
+            assertTrue( true );
+        }
+        
+        try
+        {
+            entry.put( "badAttr", (AttributeType)null, strValue1 );
+            fail();
+        }
+        catch ( IllegalArgumentException iae )
+        {
+            assertTrue( true );
+        }
+        
+        try
+        {
+            entry.put( "badAttr", atCN, strValue1 );
+            fail();
+        }
+        catch ( NoSuchAttributeException nsae )
+        {
+            assertTrue( true );
+        }
+        
+        entry.put( "Cn", atCN, strNullValue );
+        assertEquals( 1, entry.size() );
+        assertTrue( entry.containsAttribute( atCN) );
+        assertTrue( entry.contains( atCN, (String)null ) );
+        assertEquals( "Cn", entry.get( atCN ).getUpId() );
+        
+        EntryAttribute replaced = entry.put( "commonName", atCN, strValue1, strValue2, strValue1 );
+        assertNotNull( replaced );
+        assertEquals( atCN, ((ServerAttribute)replaced).getAttributeType() );
+        assertTrue( replaced.contains( (String)null ) );
+        assertEquals( 1, entry.size() );
+        assertTrue( entry.contains( atCN, strValue1, strValue2 ) );
+        assertFalse( entry.contains( atCN, strValue3 ) );
+        assertEquals( 2, entry.get( atCN ).size() );
+        assertEquals( "commonName", entry.get( atCN ).getUpId() );
+        
+        replaced = entry.put( "2.5.4.3", atCN, binValue1 );
+        assertNotNull( replaced );
+        assertTrue( replaced.contains( strValue1, strValue2 ) );
+        
+        EntryAttribute attribute = entry.get( atCN );
+        assertEquals( 0, attribute.size() );
+        assertEquals( "2.5.4.3", entry.get( atCN ).getUpId() );
+    }
+    
+    
+    /**
+     * Test method for put( String, byte[]... )
+     */
+    @Test
+    public void testPutStringByteArrayArray()
+    {
+        Entry entry = new DefaultServerEntry( registries, EXAMPLE_DN );
+        
+        try
+        {
+            entry.put( (String)null, BYTES1 );
+            fail();
+        }
+        catch ( IllegalArgumentException iae)
+        {
+            assertTrue( true );
+        }
+        
+        try
+        {
+            entry.put( "   ", BYTES1 );
+            fail();
+        }
+        catch ( IllegalArgumentException iae)
+        {
+            assertTrue( true );
+        }
+        
+        try
+        {
+            entry.put( "userAssword", BYTES1 );
+            fail();
+        }
+        catch ( IllegalArgumentException iae )
+        {
+            assertTrue( true );
+        }
+        
+        EntryAttribute replaced = entry.put( "userPassword", (byte[])null );
+        assertNull( replaced );
+        assertEquals( 1, entry.size() );
+        assertNotNull( entry.get( "userPassword" ) );
+        assertEquals( 1, entry.get( "userPassword" ).size() );
+        assertNull( entry.get( "userPassword" ).get().get() );
+        
+        replaced = entry.put( "UserPassword", BYTES1 );
+        assertNotNull( replaced );
+        assertEquals( atPwd, ((ServerAttribute)replaced).getAttributeType() );
+        assertTrue( replaced.contains( (byte[] )null ) );
+        assertEquals( 1, entry.size() );
+        assertNotNull( entry.get( "userPassword" ) );
+        assertEquals( 1, entry.get( "userPassword" ).size() );
+        assertNotNull( entry.get( "userPassword" ).get().get() );
+        assertTrue( entry.get( "userPassword" ).contains( BYTES1 ) );
+        
+        replaced = entry.put(  "jpegPhoto", BYTES1, BYTES2, BYTES1 );
+        assertNull( replaced );
+        assertEquals( 2, entry.size() );
+        assertNotNull( entry.get( "jpegPhoto" ) );
+        assertEquals( 2, entry.get( "JPEGPhoto" ).size() );
+        EntryAttribute attribute = entry.get( "jpegPhoto" );
+        assertTrue( attribute.contains( BYTES1 ) );
+        assertTrue( attribute.contains( BYTES2 ) );
+        assertEquals( "jpegphoto", attribute.getId() );
+        assertEquals( "jpegPhoto", attribute.getUpId() );
+    }
+
+
+    /**
+     * Test method for put( String, String... )
+     */
+    @Test
+    public void testPutStringStringArray()
+    {
+        Entry entry = new DefaultServerEntry( registries, EXAMPLE_DN );
+        
+        try
+        {
+            entry.put( (String)null, "test" );
+            fail();
+        }
+        catch ( IllegalArgumentException iae)
+        {
+            assertTrue( true );
+        }
+        
+        try
+        {
+            entry.put( "   ", "test" );
+            fail();
+        }
+        catch ( IllegalArgumentException iae)
+        {
+            assertTrue( true );
+        }
+        
+        try
+        {
+            entry.put( "cnn", "test" );
+            fail();
+        }
+        catch ( IllegalArgumentException iae )
+        {
+            assertTrue( true );
+        }
+        
+        EntryAttribute replaced = entry.put( "description", (String)null );
+        assertNull( replaced );
+        assertEquals( 1, entry.size() );
+        assertNotNull( entry.get( "description" ) );
+        assertEquals( 1, entry.get( "description" ).size() );
+        assertNotNull( entry.get( "description" ).get() );
+        assertNull( entry.get( "description" ).get().get() );
+        
+        replaced = entry.put( "CN", "test" );
+        assertNull( replaced );
+        assertEquals( 2, entry.size() );
+        assertNotNull( entry.get( "cn" ) );
+        assertEquals( 1, entry.get( "cn" ).size() );
+        assertNotNull( entry.get( "cn" ).get().get() );
+        assertTrue( entry.get( "cn" ).contains( "test" ) );
+        
+        replaced = entry.put(  "cN", "test1", "test2", "test1" );
+        assertNotNull( replaced );
+        assertEquals( "test", replaced.get().get() );
+        
+        assertEquals( 2, entry.size() );
+        assertNotNull( entry.get( "cn" ) );
+        assertEquals( 2, entry.get( "CN" ).size() );
+        
+        EntryAttribute attribute = entry.get( "cn" );
+        assertTrue( attribute.contains( "test1" ) );
+        assertTrue( attribute.contains( "test2" ) );
+        assertEquals( "cn", attribute.getId() );
+        assertEquals( "cN", attribute.getUpId() );
+    }
+
+
+    /**
+     * Test method for put( String, Value<?>... )
+     */
+    @Test
+    public void testPutStringValueArray()
+    {
+        Entry entry = new DefaultServerEntry( registries, EXAMPLE_DN );
+        
+        Value<String> strValue1 = new ServerStringValue( atCN, "test1" );
+        Value<String> strValue2 = new ServerStringValue( atCN, "test2" );
+        Value<String> strValue3 = new ServerStringValue( atCN, "test3" );
+        Value<String> strNullValue = new ServerStringValue( atCN, null);
+
+        Value<byte[]> binValue1 = new ServerBinaryValue( atPwd, BYTES1 );
+
+        try
+        {
+            entry.put( (String)null, strValue1 );
+            fail();
+        }
+        catch ( IllegalArgumentException iae)
+        {
+            assertTrue( true );
+        }
+        
+        try
+        {
+            entry.put( "   ", strValue1 );
+            fail();
+        }
+        catch ( IllegalArgumentException iae)
+        {
+            assertTrue( true );
+        }
+        
+        try
+        {
+            entry.put( "cnn", strValue1 );
+            fail();
+        }
+        catch ( IllegalArgumentException iae )
+        {
+            assertTrue( true );
+        }
+        
+        EntryAttribute replaced = entry.put( "description", strNullValue );
+        assertNull( replaced );
+        assertEquals( 1, entry.size() );
+        assertNotNull( entry.get( "description" ) );
+        assertEquals( 1, entry.get( "description" ).size() );
+        assertNotNull( entry.get( "description" ).get() );
+        assertNull( entry.get( "description" ).get().get() );
+        
+        replaced = entry.put( "CN", strValue3 );
+        assertNull( replaced );
+        assertEquals( 2, entry.size() );
+        assertNotNull( entry.get( "cn" ) );
+        assertEquals( 1, entry.get( "cn" ).size() );
+        assertNotNull( entry.get( "cn" ).get().get() );
+        assertTrue( entry.get( "cn" ).contains( strValue3 ) );
+        
+        replaced = entry.put(  "cN", strValue1, strValue2, strValue1 );
+        assertNotNull( replaced );
+        assertEquals( strValue3, replaced.get() );
+        
+        assertEquals( 2, entry.size() );
+        assertNotNull( entry.get( "cn" ) );
+        assertEquals( 2, entry.get( "CN" ).size() );
+        
+        EntryAttribute attribute = entry.get( "cn" );
+        assertTrue( attribute.contains( strValue1 ) );
+        assertTrue( attribute.contains( strValue2 ) );
+        assertEquals( "cn", attribute.getId() );
+        assertEquals( "cN", attribute.getUpId() );
+        
+        // Bin values are not allowed, so the new CN will be empty
+        entry.put( "cn", binValue1 );
+        assertNull( entry.get( "cn" ).get() );
+    }
+
+
+    /**
+     * Test the put( SA... ) method
+     */
+    @Test public void tesPutServerAttributeElipsis() throws NamingException
+    {
+        LdapDN dn = new LdapDN( "cn=test" );
+        DefaultServerEntry entry = new DefaultServerEntry( registries, dn );
+
+        // first test a null SA addition. It should be allowed.
+        try
+        {
+            entry.put( (ServerAttribute)null );
+            fail();
+        }
+        catch ( IllegalArgumentException iae )
+        {
+            assertTrue( true );
+        }
+        
+        // Adding some serverAttributes
+        //AttributeType atCo = registries.getAttributeTypeRegistry().lookup( "countryName" );
+        AttributeType atGN = registries.getAttributeTypeRegistry().lookup( "givenname" );
+        AttributeType atStreet = registries.getAttributeTypeRegistry().lookup( "2.5.4.9" );
+
+        ServerAttribute sa = new DefaultServerAttribute( atL, "france" );
+        entry.put( sa );
+        
+        assertEquals( 1, entry.size() );
+        assertNotNull( entry.get( "l" ) );
+        assertEquals( "france", entry.get( "l" ).get().get() );
+        
+        ServerAttribute sb = new DefaultServerAttribute( atC, "countryTest" );
+        ServerAttribute sc = new DefaultServerAttribute( atGN, "test" );
+        ServerAttribute sd = new DefaultServerAttribute( atStreet, "testStreet" );
+        entry.put( sb, sc, sd );
+
+        assertEquals( 4, entry.size() );
+        assertNotNull( entry.get( atC ) );
+        assertEquals( "countryTest", entry.get( atC ).get().get() );
+        assertNotNull( entry.get( atGN ) );
+        assertEquals( "test", entry.get( atGN ).get().get() );
+        assertNotNull( entry.get( atStreet) );
+        assertEquals( "testStreet", entry.get( atStreet ).get().get() );
+        
+        // Test a replacement
+        EntryAttribute sbb = new DefaultServerAttribute( atC, "countryTestTest" );
+        EntryAttribute scc = new DefaultServerAttribute( atGN, "testtest" );
+        List<EntryAttribute> result = entry.put( sbb, scc );
+        
+        assertEquals( 2, result.size() );
+        assertEquals( "countryTest", result.get(0).get().get() );
+        assertEquals( "test", result.get(1).get().get() );
+        assertEquals( 4, entry.size() );
+        assertNotNull( entry.get( atC ) );
+        assertEquals( "countryTestTest", entry.get( atC ).get().get() );
+        assertNotNull( entry.get( atGN ) );
+        assertEquals( "testtest", entry.get( atGN ).get().get() );
+        assertNotNull( entry.get( atStreet) );
+        assertEquals( "testStreet", entry.get( atStreet ).get().get() );
+        
+        // test an ObjectClass replacement
+        AttributeType OBJECT_CLASS_AT = registries.getAttributeTypeRegistry().lookup( SchemaConstants.OBJECT_CLASS_AT );
+        ServerAttribute oc = new DefaultServerAttribute( "OBJECTCLASS", OBJECT_CLASS_AT, "person", "inetorgperson" );
+        List<EntryAttribute> oldOc = entry.put( oc );
+        
+        assertNotNull( oldOc );
+        assertEquals( 0, oldOc.size() );
+        
+        assertNotNull( entry.get( "objectClass" ) );
+
+        EntryAttribute newOc = entry.get( "objectClass" );
+        
+        assertNotNull( newOc );
+        assertEquals( OBJECT_CLASS_AT, ((ServerAttribute)newOc).getAttributeType() );
+        assertEquals( 2, newOc.size() );
+        assertEquals( "OBJECTCLASS", newOc.getUpId() );
+        assertTrue( newOc.contains( "person", "inetOrgPerson" ) );
+    }
+
+    
+    /**
+     * Test the put( AT, String... ) method
+     */
+    @Test public void tesPutAtStringElipsis() throws NamingException
+    {
+        LdapDN dn = new LdapDN( "cn=test" );
+        DefaultServerEntry entry = new DefaultServerEntry( registries, dn );
+        
+        // Test an empty AT
+        entry.put( atCN, (String)null );
+        assertEquals( 1, entry.size() );
+        assertEquals( "cn", entry.get( atCN ).getUpId() );
+        assertNull( entry.get( atCN ).get().get() );
+        
+        // Check that we can't use invalid arguments
+        try
+        {
+            entry.put( (AttributeType)null, (String)null );
+            fail();
+        }
+        catch( IllegalArgumentException iae )
+        {
+            assertTrue( true );
+        }
+        
+        // Add a single value
+        atCN = registries.getAttributeTypeRegistry().lookup( "cn" );
+        entry.put( atCN, "test" );
+        
+        assertEquals( 1, entry.size() );
+        assertEquals( "cn", entry.get( atCN ).getUpId() );
+        assertEquals( 1, entry.get( atCN ).size() );
+        assertEquals( "test", entry.get( atCN ).get().get() );
+        
+        // Add more than one value
+        entry.put( atCN, "test1", "test2", "test3" );
+        
+        assertEquals( 1, entry.size() );
+        assertEquals( "cn", entry.get( atCN ).getUpId() );
+        assertEquals( 3, entry.get( atCN ).size() );
+        assertTrue( entry.contains( "cn", "test1" ) );
+        assertTrue( entry.contains( "cn", "test2" ) );
+        assertTrue( entry.contains( "cn", "test3" ) );
+        
+        // Add twice the same value
+        EntryAttribute sa = entry.put( atCN, "test1", "test2", "test1" );
+        
+        assertEquals( 3, sa.size() );
+        assertTrue( sa.contains( "test1", "test2", "test3" ) );
+        assertEquals( 1, entry.size() );
+        assertEquals( "cn", entry.get( atCN ).getUpId() );
+        assertEquals( 2, entry.get( atCN ).size() );
+        assertTrue( entry.contains( "cn", "test1" ) );
+        assertTrue( entry.contains( "cn", "test2" ) );
+    }
+    
+
+    /**
+     * Test the put( AT, Byte[]... ) method
+     */
+    @Test public void tesPutAtByteElipsis() throws NamingException
+    {
+        LdapDN dn = new LdapDN( "cn=test" );
+        DefaultServerEntry entry = new DefaultServerEntry( registries, dn );
+        
+        // Test an empty AT
+        entry.put( atPwd, (byte[])null );
+        assertEquals( 1, entry.size() );
+        assertEquals( "userPassword", entry.get( atPwd ).getUpId() );
+        assertNull( entry.get( atPwd ).get().get() );
+        
+        // Check that we can't use invalid arguments
+        try
+        {
+            entry.put( (AttributeType)null, (byte[])null );
+            fail();
+        }
+        catch( IllegalArgumentException iae )
+        {
+            assertTrue( true );
+        }
+        
+        byte[] password = StringTools.getBytesUtf8( "test" );
+        byte[] test1 = StringTools.getBytesUtf8( "test1" );
+        byte[] test2 = StringTools.getBytesUtf8( "test2" );
+        byte[] test3 = StringTools.getBytesUtf8( "test3" );
+        
+        // Add a single value
+        atPwd = registries.getAttributeTypeRegistry().lookup( "userPassword" );
+        entry.put( atPwd, password );
+        
+        assertEquals( 1, entry.size() );
+        assertEquals( "userPassword", entry.get( atPwd ).getUpId() );
+        assertEquals( 1, entry.get( atPwd ).size() );
+        assertTrue( Arrays.equals( password, (byte[])entry.get( atPwd ).get().get() ) );
+        
+        // Add more than one value
+        entry.put( atPwd, test1, test2, test3 );
+        
+        assertEquals( 1, entry.size() );
+        assertEquals( "userPassword", entry.get( atPwd ).getUpId() );
+        assertEquals( 3, entry.get( atPwd ).size() );
+        assertTrue( entry.contains( "userpassword", test1 ) );
+        assertTrue( entry.contains( "userpassword", test2 ) );
+        assertTrue( entry.contains( "userpassword", test3 ) );
+        
+        // Add twice the same value
+        EntryAttribute sa = entry.put( atPwd, test1, test2, test1 );
+        
+        assertEquals( 3, sa.size() );
+        assertTrue( sa.contains( test1, test2, test3 ) );
+        assertEquals( 1, entry.size() );
+        assertEquals( "userPassword", entry.get( atPwd ).getUpId() );
+        assertEquals( 2, entry.get( atPwd ).size() );
+        assertTrue( entry.contains( "userpassword", test1 ) );
+        assertTrue( entry.contains( "userpassword", test2 ) );
+    }
+    
+
+    /**
+     * Test the put( AT, Value... ) method
+     */
+    @Test public void tesPutAtSVs() throws NamingException
+    {
+        LdapDN dn = new LdapDN( "cn=test" );
+        DefaultServerEntry entry = new DefaultServerEntry( registries, dn );
+        
+        // Adding a null value to an attribute
+        entry.put( atCN, (Value<?>)null );
+        
+        assertEquals( 1, entry.size() );
+        assertEquals( "cn", entry.get( atCN ).getUpId() );
+        
+        // Check that we can't use invalid arguments
+        try
+        {
+            entry.put( (AttributeType)null, (Value<?>)null );
+            fail();
+        }
+        catch( IllegalArgumentException iae )
+        {
+            assertTrue( true );
+        }
+        
+        // Add a single value
+        atCN = registries.getAttributeTypeRegistry().lookup( "cn" );
+        Value<?> ssv = new ServerStringValue( atCN, "test" );
+        entry.put( atCN, ssv );
+        
+        assertEquals( 1, entry.size() );
+        assertEquals( "cn", entry.get( atCN ).getUpId() );
+        assertEquals( 1, entry.get( atCN ).size() );
+        assertEquals( "test", entry.get( atCN ).get().get() );
+        
+        // Add more than one value
+        entry.put( atCN, new ServerStringValue( atCN, "test1" ),
+                         new ServerStringValue( atCN, "test2" ), 
+                         new ServerStringValue( atCN, "test3" ));
+        
+        assertEquals( 1, entry.size() );
+        assertEquals( "cn", entry.get( atCN ).getUpId() );
+        assertEquals( 3, entry.get( atCN ).size() );
+        assertTrue( entry.contains( "cn", "test1" ) );
+        assertTrue( entry.contains( "cn", "test2" ) );
+        assertTrue( entry.contains( "cn", "test3" ) );
+        
+        // Add twice the same value
+        EntryAttribute sa = entry.put( atCN, new ServerStringValue( atCN, "test1" ),
+                         new ServerStringValue( atCN, "test2" ), 
+                         new ServerStringValue( atCN, "test1" ));
+        
+        assertEquals( 3, sa.size() );
+        assertTrue( sa.contains( "test1", "test2", "test3" ) );
+        assertEquals( 1, entry.size() );
+        assertEquals( "cn", entry.get( atCN ).getUpId() );
+        assertEquals( 2, entry.get( atCN ).size() );
+        assertTrue( entry.contains( "cn", "test1" ) );
+        assertTrue( entry.contains( "cn", "test2" ) );
+    }
+
+
+    /**
+     * Test the put( upId, String... ) method
+     */
+    @Test public void tesPutUpIdStringElipsis() throws NamingException
+    {
+        LdapDN dn = new LdapDN( "cn=test" );
+        DefaultServerEntry entry = new DefaultServerEntry( registries, dn );
+        
+        // Adding a null value should be possible
+        entry.put( "cn", (String)null );
+        
+        assertEquals( 1, entry.size() );
+        assertEquals( "cn", entry.get( atCN ).getUpId() );
+        assertNull( entry.get( atCN ).get().get() );
+        
+        // Check that we can't use invalid arguments
+        try
+        {
+            entry.put( (String)null, (String)null );
+            fail();
+        }
+        catch( IllegalArgumentException iae )
+        {
+            assertTrue( true );
+        }
+        
+        // Add a single value
+        atCN = registries.getAttributeTypeRegistry().lookup( "cn" );
+        entry.put( "cn", "test" );
+        
+        assertEquals( 1, entry.size() );
+        assertEquals( "cn", entry.get( atCN ).getUpId() );
+        assertEquals( 1, entry.get( atCN ).size() );
+        assertEquals( "test", entry.get( atCN ).get().get() );
+        
+        // Add more than one value
+        entry.put( "cn", "test1", "test2", "test3" );
+        
+        assertEquals( 1, entry.size() );
+        assertEquals( "cn", entry.get( atCN ).getUpId() );
+        assertEquals( 3, entry.get( atCN ).size() );
+        assertTrue( entry.contains( "cn", "test1" ) );
+        assertTrue( entry.contains( "cn", "test2" ) );
+        assertTrue( entry.contains( "cn", "test3" ) );
+        
+        // Add twice the same value
+        EntryAttribute sa = entry.put( "cn", "test1", "test2", "test1" );
+        
+        assertEquals( 3, sa.size() );
+        assertTrue( sa.contains( "test1", "test2", "test3" ) );
+        assertEquals( 1, entry.size() );
+        assertEquals( "cn", entry.get( atCN ).getUpId() );
+        assertEquals( 2, entry.get( atCN ).size() );
+        assertTrue( entry.contains( "cn", "test1" ) );
+        assertTrue( entry.contains( "cn", "test2" ) );
+        
+        // Check the UpId
+        entry.put( "CN", "test4" );
+        assertEquals( "CN", entry.get( atCN ).getUpId() );
+    }
+    
+
+    /**
+     * Test the put( upId, byte[]... ) method
+     */
+    @Test public void tesPutUpIdBytesElipsis() throws NamingException
+    {
+        LdapDN dn = new LdapDN( "cn=test" );
+        DefaultServerEntry entry = new DefaultServerEntry( registries, dn );
+        
+        AttributeType atPassword = registries.getAttributeTypeRegistry().lookup( "userPassword" );
+        
+        // Adding a null value should be possible
+        entry.put( "userPassword", (byte[])null );
+        assertEquals( 1, entry.size() );
+        assertEquals( "userPassword", entry.get( atPassword ).getUpId() );
+        assertNull( entry.get( atPassword ).get().get() );
+        
+        // Check that we can't use invalid arguments
+        try
+        {
+            entry.put( (String)null, (String)null );
+            fail();
+        }
+        catch( IllegalArgumentException iae )
+        {
+            assertTrue( true );
+        }
+        
+        // Add a single value
+        byte[] test = StringTools.getBytesUtf8( "test" );
+        byte[] test1 = StringTools.getBytesUtf8( "test1" );
+        byte[] test2 = StringTools.getBytesUtf8( "test2" );
+        byte[] test3 = StringTools.getBytesUtf8( "test3" );
+        
+        entry.put( "userPassword", test );
+        
+        assertEquals( 1, entry.size() );
+        assertEquals( "userPassword", entry.get( atPassword ).getUpId() );
+        assertEquals( 1, entry.get( atPassword ).size() );
+        assertTrue( Arrays.equals( test, (byte[])entry.get( atPassword ).get().get() ) );
+        
+        // Add more than one value
+        entry.put( "userPassword", test1, test2, test3 );
+        
+        assertEquals( 1, entry.size() );
+        assertEquals( "userPassword", entry.get( atPassword ).getUpId() );
+        assertEquals( 3, entry.get( atPassword ).size() );
+        assertTrue( entry.contains( "userPassword", test1 ) );
+        assertTrue( entry.contains( "userPassword", test2 ) );
+        assertTrue( entry.contains( "userPassword", test3 ) );
+        
+        // Add twice the same value
+        EntryAttribute sa = entry.put( "userPassword", test1, test2, test1 );
+        
+        assertEquals( 3, sa.size() );
+        assertTrue( sa.contains( test1, test2, test3 ) );
+        assertEquals( 1, entry.size() );
+        assertEquals( "userPassword", entry.get( atPassword ).getUpId() );
+        assertEquals( 2, entry.get( atPassword ).size() );
+        assertTrue( entry.contains( "userPassword", test1 ) );
+        assertTrue( entry.contains( "userPassword", test2 ) );
+    }
+
+
+    /**
+     * Test the put( upId, AT, String... ) method
+     */
+    @Test public void tesPutUpIDAtStringElipsis() throws NamingException
+    {
+        LdapDN dn = new LdapDN( "cn=test" );
+        DefaultServerEntry entry = new DefaultServerEntry( registries, dn );
+        
+        // Test that we get an error when the ID or AT are null
+        try
+        {
+            entry.put( null, (AttributeType)null, (String)null );
+            fail();
+        }
+        catch( IllegalArgumentException iae )
+        {
+            assertTrue( true );
+        }
+        
+        // Test an empty AT
+        entry.put( "commonName", atCN, (String)null );
+        assertEquals( 1, entry.size() );
+        assertEquals( "commonName", entry.get( atCN ).getUpId() );
+        assertTrue( entry.containsAttribute( "cn" ) );
+        assertNull( entry.get( atCN ).get().get() );
+        
+        // Check that we can use a null AttributeType
+        entry.put( "commonName", (AttributeType)null, (String)null );
+        assertEquals( 1, entry.size() );
+        assertEquals( "commonName", entry.get( atCN ).getUpId() );
+        assertTrue( entry.containsAttribute( "cn" ) );
+        assertNull( entry.get( atCN ).get().get() );
+        
+        // Test that we can use a null upId
+        entry.put( null, atCN, (String)null );
+        assertEquals( 1, entry.size() );
+        assertEquals( "cn", entry.get( atCN ).getUpId() );
+        assertTrue( entry.containsAttribute( "cn" ) );
+        assertNull( entry.get( atCN ).get().get() );
+
+        try
+        {
+            entry.put( "sn", atCN, (String)null );
+            fail();
+        }
+        catch ( IllegalArgumentException iae )
+        {
+            assertTrue( true );
+        }
+        
+        // Test that we can add some new attributes with values
+        EntryAttribute result = entry.put( "CN", atCN, "test1", "test2", "test3" );
+        assertNotNull( result );
+        assertEquals( "cn", result.getUpId() );
+        assertEquals( 1, entry.size() );
+        assertEquals( "CN", entry.get( atCN ).getUpId() );
+        assertNotNull( entry.get( atCN ).get() );
+        assertTrue( entry.contains( "cn", "test1" ) );
+        assertTrue( entry.contains( "CN", "test2" ) );
+        assertTrue( entry.contains( "commonName", "test3" ) );
+    }
+
+
+    /**
+     * Test the put( upId, AT, byte[]... ) method
+     */
+    @Test public void tesPutUpIDAtBytesElipsis() throws NamingException
+    {
+        LdapDN dn = new LdapDN( "cn=test" );
+        DefaultServerEntry entry = new DefaultServerEntry( registries, dn );
+        
+        AttributeType atPassword = registries.getAttributeTypeRegistry().lookup( "userPassword" );
+        
+        // Test that we get an error when the ID or AT are null
+        try
+        {
+            entry.put( null, (AttributeType)null, (String)null );
+            fail();
+        }
+        catch( IllegalArgumentException iae )
+        {
+            assertTrue( true );
+        }
+        
+        // Test an empty AT
+        entry.put( "userPassword", atPassword, (byte[])null );
+        assertEquals( 1, entry.size() );
+        assertEquals( "userPassword", entry.get( atPassword ).getUpId() );
+        assertTrue( entry.containsAttribute( "userPassword" ) );
+        assertNull( entry.get( atPassword ).get().get() );
+        
+        // Check that we can use a null AttributeType
+        try
+        {
+            entry.put( "userPassword", (AttributeType)null, (byte[])null );
+            fail();
+        }
+        catch ( IllegalArgumentException iae )
+        {
+            assertTrue( true );
+        }
+        
+        assertEquals( 1, entry.size() );
+        assertEquals( "userPassword", entry.get( atPassword ).getUpId() );
+        assertTrue( entry.containsAttribute( "userPassword" ) );
+        assertNull( entry.get( atPassword ).get().get() );
+        
+        // Test that we can use a null upId
+        entry.put( null, atPassword, (byte[])null );
+        assertEquals( 1, entry.size() );
+        assertEquals( "userPassword", entry.get( atPassword ).getUpId() );
+        assertTrue( entry.containsAttribute( "userPassword" ) );
+        assertNull( entry.get( atPassword ).get().get() );
+        
+        // Test that if we use an upId which is not compatible
+        // with the AT, it is changed to the AT default name
+        try
+        {
+            entry.put( "sn", atPassword, (byte[])null );
+            fail();
+        }
+        catch( IllegalArgumentException iae )
+        {
+            assertTrue( true );
+        }
+        
+        assertEquals( "userpassword", entry.get( atPassword ).getId() );
+        
+        // Test that we can add some new attributes with values
+        byte[] test1 = StringTools.getBytesUtf8( "test1" );
+        byte[] test2 = StringTools.getBytesUtf8( "test2" );
+        byte[] test3 = StringTools.getBytesUtf8( "test3" );
+
+        EntryAttribute result = entry.put( "UserPassword", atPassword, test1, test2, test3 );
+        assertNotNull( result );
+        assertEquals( "userPassword", result.getUpId() );
+        assertEquals( 1, entry.size() );
+        assertEquals( "UserPassword", entry.get( atPassword ).getUpId() );
+        assertNotNull( entry.get( atPassword ).get() );
+        assertEquals( 3, entry.get( atPassword ).size() );
+        assertTrue( entry.contains( "UserPassword", test1 ) );
+        assertTrue( entry.contains( "userPassword", test2 ) );
+        assertTrue( entry.contains( "2.5.4.35", test3 ) );
+    }
+
+
+    /**
+     * Test the put( upId, AT, SV... ) method
+     */
+    @Test public void tesPutUpIDAtSVElipsis() throws NamingException
+    {
+        LdapDN dn = new LdapDN( "cn=test" );
+        DefaultServerEntry entry = new DefaultServerEntry( registries, dn );
+        
+        // Test that we get an error when the ID or AT are null
+        try
+        {
+            entry.put( null, (AttributeType)null, (Value<?>)null );
+            fail();
+        }
+        catch( IllegalArgumentException iae )
+        {
+            assertTrue( true );
+        }
+        
+        // Test an empty AT
+        entry.put( "commonName", atCN, (Value<?>)null );
+        assertEquals( 1, entry.size() );
+        assertEquals( "commonName", entry.get( atCN ).getUpId() );
+        assertTrue( entry.containsAttribute( "cn" ) );
+        assertNull( entry.get( atCN ).get().get() );
+        
+        // Check that we can use a null AttributeType
+        try
+        {
+            entry.put( "commonName", (AttributeType)null, (Value<?>)null );
+            fail();
+        }
+        catch( IllegalArgumentException iae )
+        {
+            assertTrue( true );
+        }
+
+        assertEquals( 1, entry.size() );
+        assertEquals( "commonName", entry.get( atCN ).getUpId() );
+        assertTrue( entry.containsAttribute( "cn" ) );
+        assertNull( entry.get( atCN ).get().get() );
+        
+        // Test that we can use a null upId
+        entry.put( null, atCN, (Value<?>)null );
+        assertEquals( 1, entry.size() );
+        assertEquals( "cn", entry.get( atCN ).getUpId() );
+        assertTrue( entry.containsAttribute( "cn" ) );
+        assertNull( entry.get( atCN ).get().get() );
+        
+        // Test that we can't use an upId which is not compatible
+        // with the AT
+        try
+        {
+            entry.put( "sn", atCN, (Value<?>)null );
+            fail();
+        }
+        catch( IllegalArgumentException iae )
+        {
+            assertTrue( true );
+        }
+        
+        // Test that we can add some new attributes with values
+        Value<String> test1 = new ServerStringValue( atCN, "test1" );
+        Value<String> test2 = new ServerStringValue( atCN, "test2" );
+        Value<String> test3 = new ServerStringValue( atCN, "test3" );
+
+        EntryAttribute result = entry.put( "CN", atCN, test1, test2, test3 );
+        assertNotNull( result );
+        assertEquals( "cn", result.getUpId() );
+        assertEquals( 1, entry.size() );
+        assertEquals( "CN", entry.get( atCN ).getUpId() );
+        assertNotNull( entry.get( atCN ).get() );
+        assertTrue( entry.contains( "cn", "test1" ) );
+        assertTrue( entry.contains( "CN", "test2" ) );
+        assertTrue( entry.contains( "commonName", "test3" ) );
+    }
+
+
+    /**
+     * Test the put( upId, SV... ) method
+     */
+    @Test public void tesPutUpIDSVElipsis() throws NamingException
+    {
+        LdapDN dn = new LdapDN( "cn=test" );
+        DefaultServerEntry entry = new DefaultServerEntry( registries, dn );
+        
+        // Test that we get an error when the ID or AT are null
+        try
+        {
+            entry.put( (String)null, (Value<?>)null );
+            fail();
+        }
+        catch( IllegalArgumentException iae )
+        {
+            assertTrue( true );
+        }
+        
+        // Test an null valued AT
+        entry.put( "commonName", (Value<?>)null );
+        assertEquals( 1, entry.size() );
+        assertEquals( "commonName", entry.get( atCN ).getUpId() );
+        assertTrue( entry.containsAttribute( "cn" ) );
+        assertNull( entry.get( atCN ).get().get() );
+
+        // Test that we can add some new attributes with values
+        Value<String> test1 = new ServerStringValue( atCN, "test1" );
+        Value<String> test2 = new ServerStringValue( atCN, "test2" );
+        Value<String> test3 = new ServerStringValue( atCN, "test3" );
+
+        EntryAttribute result = entry.put( "CN", test1, test2, test3 );
+        assertNotNull( result );
+        assertEquals( "commonName", result.getUpId() );
+        assertEquals( 1, entry.size() );
+        assertEquals( "CN", entry.get( atCN ).getUpId() );
+        assertNotNull( entry.get( atCN ).get() );
+        assertTrue( entry.contains( "cn", "test1" ) );
+        assertTrue( entry.contains( "CN", "test2" ) );
+        assertTrue( entry.contains( "commonName", "test3" ) );
+    }
+
+    
+    //-------------------------------------------------------------------------
+    // Test the Remove methods
+    //-------------------------------------------------------------------------
+    /**
+     * Test method for remove( AttributeType, byte[]... )
+     */
+    @Test
+    public void testRemoveAttributeTypeByteArrayArray() throws NamingException
+    {
+        ServerEntry entry = new DefaultServerEntry( registries, EXAMPLE_DN );
+        
+        EntryAttribute attrPWD = new DefaultServerAttribute( atPwd, BYTES1, (byte[])null, BYTES2 );
+
+        entry.put( attrPWD );
+        assertTrue( entry.remove( atPwd, (byte[])null ) );
+        assertTrue( entry.remove( atPwd, BYTES1, BYTES2 ) );
+        assertFalse( entry.containsAttribute( atPwd ) );
+        
+        entry.add( atPwd, BYTES1, (byte[])null, BYTES2 );
+        assertTrue( entry.remove( atPwd, (byte[])null ) );
+        assertEquals( 2, entry.get( atPwd ).size() );
+        assertFalse( entry.contains( atPwd, (byte[])null ) );
+        assertTrue( entry.remove( atPwd, BYTES1, BYTES3 ) );
+        assertEquals( 1, entry.get( atPwd ).size() );
+        assertTrue( entry.contains( atPwd, BYTES2 ) );
+        assertFalse( entry.contains( atPwd, BYTES1 ) );
+        
+        assertFalse( entry.remove( atPwd, BYTES3 ) );
+        assertFalse( entry.remove( atPwd, new byte[]{ 0x00 } ) );
+    }
+
+    
+    /**
+     * Test method for remove( AttributeType, String... )
+     */
+    @Test
+    public void testRemoveAttributeTypeStringArray() throws NamingException
+    {
+        ServerEntry entry = new DefaultServerEntry( registries, EXAMPLE_DN );
+        
+        EntryAttribute attrCN = new DefaultServerAttribute( atCN, "test1", (String)null, "test2" );
+
+        entry.put( attrCN );
+        assertTrue( entry.remove( atCN, (String)null ) );
+        assertTrue( entry.remove( atCN, "test1", "test2" ) );
+        assertFalse( entry.containsAttribute( atCN ) );
+        
+        entry.add( atCN, "test1", (String)null, "test2" );
+        assertTrue( entry.remove( atCN, (String)null ) );
+        assertEquals( 2, entry.get( atCN ).size() );
+        assertFalse( entry.contains( atCN, (String)null ) );
+        assertTrue( entry.remove( atCN, "test1", "test3" ) );
+        assertEquals( 1, entry.get( atCN ).size() );
+        assertTrue( entry.contains( atCN, "test2" ) );
+        assertFalse( entry.contains( atCN, "test1" ) );
+        
+        assertFalse( entry.remove( atCN, "test3" ) );
+        assertFalse( entry.remove( atCN, "test" ) );
+    }
+
+    
+    /**
+     * Test method for remove( AttributeType, Value<?>... )
+     */
+    @Test
+    public void testRemoveAttributeTypeValueArray() throws NamingException
+    {
+        ServerEntry entry = new DefaultServerEntry( registries, EXAMPLE_DN );
+        
+        Value<String> strValue1 = new ServerStringValue( atCN, "test1" );
+        Value<String> strValue2 = new ServerStringValue( atCN, "test2" );
+        Value<String> strValue3 = new ServerStringValue( atCN, "test3" );
+        Value<String> strNullValue = new ServerStringValue( atCN, null);
+
+        Value<byte[]> binValue1 = new ServerBinaryValue( atPwd, BYTES1 );
+
+        EntryAttribute attrPWD = new DefaultServerAttribute( atCN, "test1", (String)null, "test2" );
+
+        entry.put( attrPWD );
+        assertTrue( entry.remove( atCN, strNullValue ) );
+        assertTrue( entry.remove( atCN, strValue1, strValue2 ) );
+        assertFalse( entry.containsAttribute( atCN ) );
+        
+        entry.add( atCN, strValue1, strNullValue, strValue2 );
+        assertTrue( entry.remove( atCN, strNullValue ) );
+        assertEquals( 2, entry.get( atCN ).size() );
+        assertFalse( entry.contains( atCN, strNullValue ) );
+        assertTrue( entry.remove( atCN, strValue1, strValue3 ) );
+        assertEquals( 1, entry.get( atCN ).size() );
+        assertTrue( entry.contains( atCN, strValue2 ) );
+        assertFalse( entry.contains( atCN, strValue1 ) );
+        
+        assertFalse( entry.remove( atCN, strValue3 ) );
+        assertFalse( entry.remove( atCN, binValue1 ) );
+    }
+
+    
+    /**
+     * Test method for remove( EntryAttribute... )
+     */
+    @Test
+    public void testRemoveEntryAttribute() throws NamingException
+    {
+        ServerEntry entry = new DefaultServerEntry( registries, EXAMPLE_DN );
+        
+        EntryAttribute attrOC = new DefaultServerAttribute( atOC, "top", "person" );
+        EntryAttribute attrCN = new DefaultServerAttribute( atCN, "test1", "test2" );
+        EntryAttribute attrSN = new DefaultServerAttribute( atSN, "Test1", "Test2" );
+        EntryAttribute attrPWD = new DefaultServerAttribute( atPwd, BYTES1, BYTES2 );
+        
+        entry.put( attrOC, attrCN, attrSN, attrPWD );
+        
+        List<EntryAttribute> removed = entry.remove( attrSN, attrPWD );
+        
+        assertEquals( 2, removed.size() ); 
+        assertEquals( 2, entry.size() );
+        assertTrue( removed.contains( attrSN ) );
+        assertTrue( removed.contains( attrPWD ) );
+        assertTrue( entry.contains( "objectClass", "top", "person" ) );
+        assertTrue( entry.contains( "cn", "test1", "test2" ) );
+        assertFalse( entry.containsAttribute( atSN ) );
+        assertFalse( entry.containsAttribute( "userPassword" ) );
+
+        removed = entry.remove( attrSN, attrPWD );
+        
+        assertEquals( 0, removed.size() );
+    }
+
+
+    /**
+     * Test method for removeAttributes( AttributeType... )
+     */
+    @Test
+    public void testRemoveAttributesAttributeTypeArray() throws NamingException
+    {
+        ServerEntry entry = new DefaultServerEntry( registries, EXAMPLE_DN );
+
+        EntryAttribute attrOC = new DefaultServerAttribute( atOC, "top", "person" );
+        EntryAttribute attrCN = new DefaultServerAttribute( atCN, "test1", "test2" );
+        EntryAttribute attrSN = new DefaultServerAttribute( atSN, "Test1", "Test2" );
+        EntryAttribute attrPWD = new DefaultServerAttribute( atPwd, BYTES1, BYTES2 );
+        
+        entry.put( attrOC, attrCN, attrSN, attrPWD );
+        
+        entry.removeAttributes( atCN, atSN );
+        
+        assertFalse( entry.containsAttribute( "cn", "sn" ) );
+        assertTrue( entry.containsAttribute( "objectclass", "userpassword" ) );
+        
+        List<EntryAttribute> removed = entry.removeAttributes( (AttributeType)null );
+        assertNull( removed );
+
+        removed = entry.removeAttributes( atC );
+        assertNull( removed );
+    }
+    
+    
+    /**
+     * Test method for removeAttributes( String... )
+     */
+    @Test
+    public void testRemoveAttributesStringArray() throws NamingException
+    {
+        Entry entry = new DefaultServerEntry( registries, EXAMPLE_DN );
+
+        EntryAttribute attrOC = new DefaultServerAttribute( atOC, "top", "person" );
+        EntryAttribute attrCN = new DefaultServerAttribute( atCN, "test1", "test2" );
+        EntryAttribute attrSN = new DefaultServerAttribute( atSN, "Test1", "Test2" );
+        EntryAttribute attrPWD = new DefaultServerAttribute( atPwd, BYTES1, BYTES2 );
+        
+        entry.put( attrOC, attrCN, attrSN, attrPWD );
+        
+        entry.removeAttributes( "CN", "SN" );
+        
+        assertFalse( entry.containsAttribute( "cn", "sn" ) );
+        assertTrue( entry.containsAttribute( "objectclass", "userpassword" ) );
+        
+        List<EntryAttribute> removed = entry.removeAttributes( "badId" );
+        assertNull( removed );
+        
+        removed = entry.removeAttributes( "l" );
+        assertNull( removed );
+        
+        removed = entry.removeAttributes( (String )null );
+        assertNull( removed );
+    }
+    
+    
+    /**
+     * Test method for remove( String, byte[]... )
+     */
+    @Test
+    public void testRemoveStringByteArrayArray() throws NamingException
+    {
+        ServerEntry entry = new DefaultServerEntry( registries, EXAMPLE_DN );
+        
+        EntryAttribute attrPWD = new DefaultServerAttribute( atPwd, BYTES1, (byte[])null, BYTES2 );
+
+        assertFalse( entry.remove( (String)null, BYTES1 ) );
+        assertFalse( entry.remove( " ", BYTES1 ) );
+        assertFalse( entry.remove( "badId", BYTES1 ) );
+
+        entry.put( attrPWD );
+        assertTrue( entry.remove( "userPassword", (byte[])null ) );
+        assertTrue( entry.remove( "UserPassword", BYTES1, BYTES2 ) );
+        assertFalse( entry.containsAttribute( atPwd ) );
+        
+        entry.add( atPwd, BYTES1, (byte[])null, BYTES2 );
+        assertTrue( entry.remove( "userPassword", (byte[])null ) );
+        assertEquals( 2, entry.get( atPwd ).size() );
+        assertFalse( entry.contains( atPwd, (byte[])null ) );
+        assertTrue( entry.remove( "userPassword", BYTES1, BYTES3 ) );
+        assertEquals( 1, entry.get( atPwd ).size() );
+        assertTrue( entry.contains( atPwd, BYTES2 ) );
+        assertFalse( entry.contains( atPwd, BYTES1 ) );
+        
+        assertFalse( entry.remove( "userPassword", BYTES3 ) );
+        assertFalse( entry.remove( "userPassword", new byte[]{ 0x00 } ) );
+    }
+
+    
+    /**
+     * Test method for remove( String, String... )
+     */
+    @Test
+    public void testRemoveStringStringArray() throws NamingException
+    {
+        ServerEntry entry = new DefaultServerEntry( registries, EXAMPLE_DN );
+        
+        EntryAttribute attrCN = new DefaultServerAttribute( atCN, "test1", (String)null, "test2" );
+
+        assertFalse( entry.remove( (String)null, "test1" ) );
+        assertFalse( entry.remove( " ", "test1" ) );
+        assertFalse( entry.remove( "badId", "test1" ) );
+
+        entry.put( attrCN );
+        assertTrue( entry.remove( "cn", (String)null ) );
+        assertTrue( entry.remove( "commonName", "test1", "test2" ) );
+        assertFalse( entry.containsAttribute( atCN ) );
+        
+        entry.add( atCN, "test1", (String)null, "test2" );
+        assertTrue( entry.remove( "2.5.4.3", (String)null ) );
+        assertEquals( 2, entry.get( atCN ).size() );
+        assertFalse( entry.contains( atCN, (byte[])null ) );
+        assertTrue( entry.remove( "COMMONNAME", "test1", "test3" ) );
+        assertEquals( 1, entry.get( atCN ).size() );
+        assertTrue( entry.contains( atCN, "test2" ) );
+        assertFalse( entry.contains( atCN, "test1" ) );
+        
+        assertFalse( entry.remove( "Cn", "test3" ) );
+        assertFalse( entry.remove( "cN", "whatever" ) );
+    }
+
+    
+    /**
+     * Test method for remove( String, Value<?>... )
+     */
+    @Test
+    public void testRemoveStringValueArray() throws NamingException
+    {
+        ServerEntry entry = new DefaultServerEntry( registries, EXAMPLE_DN );
+        
+        Value<String> strValue1 = new ServerStringValue( atCN, "test1" );
+        Value<String> strValue2 = new ServerStringValue( atCN, "test2" );
+        Value<String> strValue3 = new ServerStringValue( atCN, "test3" );
+        Value<String> strNullValue = new ServerStringValue( atCN, null);
+
+        Value<byte[]> binValue1 = new ServerBinaryValue( atPwd, BYTES1 );
+
+        EntryAttribute attrPWD = new DefaultServerAttribute( atCN, "test1", (String)null, "test2" );
+
+        entry.put( attrPWD );
+        assertTrue( entry.remove( "CN", strNullValue ) );
+        assertTrue( entry.remove( "CommonName", strValue1, strValue2 ) );
+        assertFalse( entry.containsAttribute( atCN ) );
+        
+        entry.add( atCN, strValue1, strNullValue, strValue2 );
+        assertTrue( entry.remove( "2.5.4.3", strNullValue ) );
+        assertEquals( 2, entry.get( atCN ).size() );
+        assertFalse( entry.contains( atCN, strNullValue ) );
+        assertTrue( entry.remove( "  cn", strValue1, strValue3 ) );
+        assertEquals( 1, entry.get( atCN ).size() );
+        assertTrue( entry.contains( atCN, strValue2 ) );
+        assertFalse( entry.contains( atCN, strValue1 ) );
+        
+        assertFalse( entry.remove( " Cn", strValue3 ) );
+        assertFalse( entry.remove( "cN ", binValue1 ) );
+    }
+
+    
+    /**
+     * Test the remove( upId...) method
+     */
+    @Test public void testRemoveUpIdElipsis() throws NamingException
+    {
+        LdapDN dn = new LdapDN( "cn=test" );
+        DefaultServerEntry entry = new DefaultServerEntry( registries, dn );
+        
+        AttributeType atPassword = registries.getAttributeTypeRegistry().lookup( "userPassword" );
+        
+        byte[] b1 = StringTools.getBytesUtf8( "test1" );
+        byte[] b2 = StringTools.getBytesUtf8( "test2" );
+
+        Value<String> test1 = new ServerStringValue( atCN, "test1" );
+        Value<String> test2 = new ServerStringValue( atCN, "test2" );
+        
+        Value<byte[]> testB1 = new ServerBinaryValue( atPassword, b1 );
+        Value<byte[]> testB2 = new ServerBinaryValue( atPassword, b2 );
+        
+        // test a removal of an non existing attribute
+        List<EntryAttribute> removed = entry.removeAttributes( atCN );
+        assertNull( removed );
+        
+        // Test a simple removal
+        entry.add( "cN", atCN, test1 );
+        assertEquals( 1, entry.size() );
+        assertNotNull( entry.get( atCN ) );
+        entry.removeAttributes( "CN" );
+        assertEquals( 0, entry.size() );
+        assertNull( entry.get( atCN ) );
+        
+        // Test a removal of many elements
+        entry.put( "CN", test1, test2 );
+        entry.put( "userPassword", testB1, testB2 );
+        assertEquals( 2, entry.size() );
+        assertNotNull( entry.get( atCN ) );
+        assertNotNull( entry.get( atPassword ) );
+        
+        AttributeType OBJECT_CLASS_AT = registries.getAttributeTypeRegistry().lookup( SchemaConstants.OBJECT_CLASS_AT );
+        
+        entry.removeAttributes( "cN", "UsErPaSsWoRd" );
+        assertEquals( 0, entry.size() );
+        assertNull( entry.get( atCN ) );
+        assertNull( entry.get( atPassword ) );
+        assertFalse( entry.contains( OBJECT_CLASS_AT, "top" ) );
+        
+        // test the removal of a bad Attribute
+        entry.put( "CN", test1, test2 );
+        entry.put( "userPassword", testB1, testB2 );
+        assertEquals( 2, entry.size() );
+        assertNotNull( entry.get( atCN ) );
+        assertNotNull( entry.get( atPassword ) );
+        
+        removed = entry.removeAttributes( "badAttribute" );
+        
+        assertNull( removed );
+    }
+
+    
+    /**
+     * Test the set(AT...) method
+     */
+    @Test public void testSetATElipsis() throws NamingException
+    {
+        LdapDN dn = new LdapDN( "cn=test" );
+        ServerEntry entry = new DefaultServerEntry( registries, dn );
+        
+        List<EntryAttribute> result = null;
+        
+        // First check that this method fails if we pass an empty list of ATs
+        result = entry.set( (AttributeType)null);
+        assertNull( result );
+        
+        // Now, check what we get when adding one existing AT
+        result = entry.set( atSN );
+        
+        assertNull( result );
+        EntryAttribute sa = entry.get( "sn" );
+        assertNotNull( sa );
+        assertEquals( atSN, ((ServerAttribute)sa).getAttributeType() );
+        assertEquals( "sn", ((ServerAttribute)sa).getAttributeType().getName() );
+        
+        // Add two AT now
+        AttributeType atGN = registries.getAttributeTypeRegistry().lookup( "givenname" );
+        AttributeType atStreet = registries.getAttributeTypeRegistry().lookup( "2.5.4.9" );
+        result = entry.set( atL, atC, atGN, atStreet );
+        
+        assertNull( result );
+
+        sa = entry.get( "l" );
+        assertNotNull( sa );
+        assertEquals( atL, ((ServerAttribute)sa).getAttributeType() );
+        assertEquals( "l", ((ServerAttribute)sa).getAttributeType().getName() );
+
+        sa = entry.get( "c" );
+        assertNotNull( sa );
+        assertEquals( atC, ((ServerAttribute)sa).getAttributeType() );
+        assertEquals( "c", ((ServerAttribute)sa).getAttributeType().getName() );
+
+        sa = entry.get( "2.5.4.9" );
+        assertNotNull( sa );
+        assertEquals( atStreet, ((ServerAttribute)sa).getAttributeType() );
+        assertEquals( "street", ((ServerAttribute)sa).getAttributeType().getName() );
+
+        sa = entry.get( "givenName" );
+        assertNotNull( sa );
+        assertEquals( atGN, ((ServerAttribute)sa).getAttributeType() );
+        assertEquals( "givenName", ((ServerAttribute)sa).getAttributeType().getName() );
+        
+        // Now try to add existing ATs
+        // First, set some value to the modified AT
+        sa = entry.get( "sn" );
+        sa.add( "test" );
+        
+        // Check that the value has been added to the entry
+        assertEquals( "test", entry.get( "sn" ).get().get() ); 
+        
+        // Now add a new SN empty AT : it should replace the existing one.
+        AttributeType atSNEmpty = registries.getAttributeTypeRegistry().lookup( "sn" );
+        sa = entry.set( atSNEmpty ).get( 0 );
+        assertEquals( "test", sa.get().get() ); 
+        assertNotNull( entry.get(  "sn" ) );
+        assertNull( entry.get(  "sn" ).get() );
+        
+        // Last, not least, put an ObjectClass AT
+        AttributeType OBJECT_CLASS_AT = registries.getAttributeTypeRegistry().lookup( SchemaConstants.OBJECT_CLASS_AT );
+        
+        entry.set( OBJECT_CLASS_AT );
+        
+        assertNotNull( entry.get( "objectClass" ) );
+
+        EntryAttribute oc = entry.get( "objectClass" );
+        
+        assertEquals( OBJECT_CLASS_AT, ((ServerAttribute)oc).getAttributeType() );
+        assertNull( oc.get() );
+    }
+    
+    
+    /**
+     * Test the set( upId ) method
+     */
+    @Test public void testSetUpID() throws NamingException
+    {
+        LdapDN dn = new LdapDN( "cn=test" );
+        DefaultServerEntry entry = new DefaultServerEntry( registries, dn );
+        
+        // First check that this method fails if we pass a null or empty ID
+        List<EntryAttribute> result = entry.set( (String)null );
+        assertNull( result );
+        
+        result = entry.set( "  " );
+        assertNull( result );
+        
+        // Now check that we can't put invalid IDs
+        result = entry.set( "ThisIsNotAnAttributeType" );
+        assertNull( result );
+        
+        // Now, check what we get when adding one existing AT
+        result = entry.set( "sn" );
+        
+        assertNull( result );
+
+        EntryAttribute sa = entry.get( "sn" );
+        assertNotNull( sa );
+        assertEquals( "sn", sa.getId() );
+        
+        // Add different upIds now
+        AttributeType atGN = registries.getAttributeTypeRegistry().lookup( "givenname" );
+        AttributeType atStreet = registries.getAttributeTypeRegistry().lookup( "2.5.4.9" );
+        
+        entry.set( "L" );
+        entry.set( "CountryName" );
+        entry.set( "gn" );
+        entry.set( "2.5.4.9" );
+        
+
+        sa = entry.get( "l" );
+        assertNotNull( sa );
+        assertEquals( atL, ((ServerAttribute)sa).getAttributeType() );
+        assertEquals( "l", sa.getId() );
+        assertEquals( "L", sa.getUpId() );
+
+        sa = entry.get( "c" );
+        assertNotNull( sa );
+        assertEquals( atC, ((ServerAttribute)sa).getAttributeType() );
+        assertEquals( "countryname", sa.getId() );
+        assertEquals( "CountryName", sa.getUpId() );
+
+        sa = entry.get( "2.5.4.9" );
+        assertNotNull( sa );
+        assertEquals( atStreet, ((ServerAttribute)sa).getAttributeType() );
+        assertEquals( "2.5.4.9", sa.getId() );
+        assertEquals( "2.5.4.9", sa.getUpId() );
+
+        sa = entry.get( "givenName" );
+        assertNotNull( sa );
+        assertEquals( atGN, ((ServerAttribute)sa).getAttributeType() );
+        assertEquals( "gn", sa.getId() );
+        assertEquals( "gn", sa.getUpId() );
+        
+        // Now try to add existing ATs
+        // First, set some value to the modified AT
+        sa = entry.get( "sn" );
+        sa.add( "test" );
+        
+        // Check that the value has been added to the entry
+        assertEquals( "test", entry.get( "sn" ).get().get() ); 
+        
+        // Now add a new SN empty AT : it should replace the existing one.
+        AttributeType atSNEmpty = registries.getAttributeTypeRegistry().lookup( "sn" );
+        sa = entry.set( atSNEmpty ).get( 0 );
+        assertEquals( "test", sa.get().get() ); 
+        assertNotNull( entry.get(  "sn" ) );
+        assertNull( entry.get(  "sn" ).get() );
+    }
+
+    
+    /**
+     * Test method for set( AttributeType... )
+     */
+    @Test
+    public void testSetAttributeTypeArray() throws NamingException
+    {
+        ServerEntry entry = new DefaultServerEntry( registries, EXAMPLE_DN );
+
+        entry.add( "ObjectClass", "top", "person" );
+        entry.add( "cn", "test1", "test2" );
+        entry.add( "sn", "Test" );
+        
+        List<EntryAttribute> removed = entry.set( atOC, atCN, atPwd );
+        
+        assertEquals( 4, entry.size() );
+        assertNotNull( entry.get( "objectclass" ) );
+        assertNotNull( entry.get( "cn" ) );
+        assertNotNull( entry.get( "userPassword" ) );
+        assertNotNull( entry.get( "sn" ) );
+        
+        assertNull( entry.get( "objectclass" ).get() );
+        assertNull( entry.get( "cn" ).get() );
+        assertNull( entry.get( "userPassword" ).get() );
+        assertNotNull( entry.get( "sn" ).get() );
+        
+        assertNotNull( removed );
+        assertEquals( 2, removed.size() );
+    }
+
+
+    /**
+     * Test method for set( String... )
+     */
+    @Test
+    public void testSetStringArray() throws NamingException
+    {
+        Entry entry = new DefaultServerEntry( registries, EXAMPLE_DN );
+
+        entry.add( "ObjectClass", "top", "person" );
+        entry.add( "cn", "test1", "test2" );
+        entry.add( "sn", "Test" );
+        
+        List<EntryAttribute> removed = entry.set( "objectClass", "CN", "givenName" );
+        
+        assertEquals( 4, entry.size() );
+        assertNotNull( entry.get( "objectclass" ) );
+        assertNotNull( entry.get( "cn" ) );
+        assertNotNull( entry.get( "givenname" ) );
+        assertNotNull( entry.get( "sn" ) );
+        
+        assertNull( entry.get( "objectclass" ).get() );
+        assertNull( entry.get( "cn" ).get() );
+        assertNull( entry.get( "givenname" ).get() );
+        assertNotNull( entry.get( "sn" ).get() );
+        
+        assertNotNull( removed );
+        assertEquals( 2, removed.size() );
+    }
+
+
+    /**
+     * Test method for setDN( LdapDN )
+     */
+    @Test
+    public void testSetDn()
+    {
+        Entry entry = new DefaultServerEntry( registries );
+         
+        assertNull( entry.getDn() );
+         
+        entry.setDn( EXAMPLE_DN );
+        assertEquals( EXAMPLE_DN, entry.getDn() );
+    }
+     
+     
+    /**
+     * Test for method size()
+     */
+     @Test
+     public void testSize() throws NamingException
+     {
+         ServerEntry entry = new DefaultServerEntry( registries, EXAMPLE_DN );
+          
+         assertEquals( 0, entry.size() );
+         entry.add( "ObjectClass", atr.lookup( "ObjectClass" ), "top", "person" );
+         entry.add( "CN", atr.lookup( "Cn" ), "test" );
+         entry.add( "SN", atr.lookup( "Sn" ), "Test" );
+          
+         assertEquals( 3, entry.size() );
+         
+         entry.clear();
+         assertEquals( 0, entry.size() );
+     }
+      
+      
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    /**
+     * Test a conversion from a ServerEntry to an AttributesImpl
+     */
+    @Test public void testToAttributesImpl() throws InvalidNameException, NamingException
+    {
+        LdapDN dn = new LdapDN( "cn=test" );
+        DefaultServerEntry entry = new DefaultServerEntry( registries, dn );
+        
+        AttributeType OBJECT_CLASS_AT = registries.getAttributeTypeRegistry().lookup( SchemaConstants.OBJECT_CLASS_AT );
+        
+        entry.put( "objectClass", OBJECT_CLASS_AT, "top", "person", "inetOrgPerson", "organizationalPerson" );
+        entry.put( "cn", registries.getAttributeTypeRegistry().lookup( "cn" ), "test" );
+        
+        Attributes attributes = ServerEntryUtils.toAttributesImpl( entry );
+        
+        assertNotNull( attributes );
+        assertTrue( attributes instanceof AttributesImpl );
+        
+        Set<String> expected = new HashSet<String>();
+        expected.add( "objectClass" );
+        expected.add( "cn" );
+     
+        for ( NamingEnumeration<String> ids = attributes.getIDs(); ids.hasMoreElements();)
+        {
+            String id = ids.nextElement();
+            
+            assertTrue( expected.contains( id ) );
+            expected.remove( id );
+            
+        }
+
+        // It should be empty
+        assertEquals( 0, expected.size() );
+    }
+
+
+    /**
+     * Test a conversion from a ServerEntry to an BasicAttributes
+     */
+    @Test public void testToBasicAttributes() throws InvalidNameException, NamingException
+    {
+        LdapDN dn = new LdapDN( "cn=test" );
+        DefaultServerEntry entry = new DefaultServerEntry( registries,dn );
+        
+        AttributeType OBJECT_CLASS_AT = registries.getAttributeTypeRegistry().lookup( SchemaConstants.OBJECT_CLASS_AT );
+        
+        entry.put( "objectClass", OBJECT_CLASS_AT, "top", "person", "inetOrgPerson", "organizationalPerson" );
+        
+        Attributes attributes = ServerEntryUtils.toBasicAttributes( entry );
+        
+        assertNotNull( attributes );
+        assertTrue( attributes instanceof BasicAttributes );
+        
+        Set<String> expected = new HashSet<String>();
+        expected.add( "objectClass" );
+        expected.add( "cn" );
+     
+        for ( NamingEnumeration<String> ids = attributes.getIDs(); ids.hasMoreElements();)
+        {
+            String id = ids.nextElement();
+            
+            assertTrue( expected.contains( id ) );
+            expected.remove( id );
+            
+        }
+
+        // We should still have the ObjectClass Attribute
+        assertEquals( 1, expected.size() );
+    }
+    
+    
+    /**
+     * Test method for toString().
+     */
+    @Test
+    public void testToString() throws NamingException
+    {
+        ServerEntry entry = new DefaultServerEntry( registries, EXAMPLE_DN );
+        
+        assertEquals( "ServerEntry\n    dn: dc=example,dc=com\n", entry.toString() );
+        
+        Value<String> strValueTop = new ClientStringValue( "top" );
+        Value<String> strValuePerson = new ClientStringValue( "person" );
+        Value<String> strNullValue = new ClientStringValue( null);
+
+        Value<byte[]> binValue1 = new ClientBinaryValue( BYTES1 );
+        Value<byte[]> binValue2 = new ClientBinaryValue( BYTES2 );
+        Value<byte[]> binNullValue = new ClientBinaryValue( null );
+        
+        entry.put( "ObjectClass", atOC, strValueTop, strValuePerson, strNullValue );
+        entry.put( "UserPassword", atPwd, binValue1, binValue2, binNullValue );
+
+        String expected = 
+            "ServerEntry\n" +
+            "    dn: dc=example,dc=com\n" +
+            "    ObjectClass: top\n" +
+            "    ObjectClass: person\n" +
+            "    ObjectClass: ''\n" +
+            "    UserPassword: '0x61 0x62 '\n" +
+            "    UserPassword: '0x62 '\n" +
+            "    UserPassword: ''\n";
+
+        assertEquals( expected, entry.toString() );
+    }
+}
+
diff --git a/old_trunk/core-entry/src/test/java/org/apache/directory/server/core/entry/ServerBinaryValueTest.java b/old_trunk/core-entry/src/test/java/org/apache/directory/server/core/entry/ServerBinaryValueTest.java
new file mode 100644
index 0000000..c674333
--- /dev/null
+++ b/old_trunk/core-entry/src/test/java/org/apache/directory/server/core/entry/ServerBinaryValueTest.java
@@ -0,0 +1,597 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.entry;
+
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.Arrays;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.schema.ByteArrayComparator;
+import org.apache.directory.shared.ldap.schema.Normalizer;
+import org.apache.directory.shared.ldap.schema.syntax.AcceptAllSyntaxChecker;
+import org.apache.directory.shared.ldap.util.StringTools;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import org.junit.Before;
+import org.junit.Test;
+
+
+/**
+ * Tests that the ServerBinaryValue class works properly as expected.
+ *
+ * Some notes while conducting tests:
+ *
+ * <ul>
+ *   <li>comparing values with different types - how does this behave</li>
+ *   <li>exposing access to at from value or to a comparator?</li>
+ * </ul>
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ServerBinaryValueTest
+{
+    private TestServerEntryUtils.S s;
+    private TestServerEntryUtils.AT at;
+    private TestServerEntryUtils.MR mr;
+    
+    private static final byte[] BYTES1 = new byte[]{0x01, 0x02, 0x03, 0x04};
+    private static final byte[] BYTES2 = new byte[]{(byte)0x81, (byte)0x82, (byte)0x83, (byte)0x84};
+
+    /**
+     * Initialize an AttributeType and the associated MatchingRule 
+     * and Syntax
+     */
+    @Before public void initAT()
+    {
+        s = new TestServerEntryUtils.S( "1.1.1.1", false );
+        s.setSyntaxChecker( new AcceptAllSyntaxChecker( "1.1.1.1" ) );
+        mr = new TestServerEntryUtils.MR( "1.1.2.1" );
+        mr.syntax = s;
+        mr.comparator = new ByteArrayComparator();
+        mr.normalizer = new Normalizer()
+        {
+            private static final long serialVersionUID = 1L;
+            
+            public Object normalize( Object value ) throws NamingException
+            {
+                if ( value instanceof byte[] )
+                {
+                    byte[] val = (byte[])value;
+                    // each byte will be changed to be > 0, and spaces will be trimmed
+                    byte[] newVal = new byte[ val.length ];
+                    int i = 0;
+                    
+                    for ( byte b:val )
+                    {
+                        newVal[i++] = (byte)(b & 0x007F); 
+                    }
+                    
+                    return StringTools.trim( newVal );
+                }
+
+                throw new IllegalStateException( "expected byte[] to normalize" );
+            }
+        };
+        at = new TestServerEntryUtils.AT( "1.1.3.1" );
+        at.setEquality( mr );
+        at.setOrdering( mr );
+        at.setSubstr( mr );
+        at.setSyntax( s );
+    }
+    
+
+    /**
+     * Test the constructor with bad AttributeType
+     */
+    @Test public void testBadConstructor()
+    {
+        try
+        {
+            new ServerBinaryValue( null );
+            fail();
+        }
+        catch ( IllegalArgumentException iae )
+        {
+            // Expected...
+        }
+        
+        // create a AT without any syntax
+        AttributeType attribute = new TestServerEntryUtils.AT( "1.1.3.1" );
+        
+        try
+        {
+            new ServerBinaryValue( attribute );
+            fail();
+        }
+        catch ( IllegalArgumentException ae )
+        {
+            // Expected...
+        }
+    }
+
+
+    /**
+     * Test the constructor with a null value
+     */
+    @Test public void testServerBinaryValueNullValue()
+    {
+        AttributeType attribute = TestServerEntryUtils.getBytesAttributeType();
+        
+        ServerBinaryValue value = new ServerBinaryValue( attribute, null );
+        
+        assertNull( value.getReference() );
+        assertTrue( value.isNull() );
+    }
+    
+    
+    /**
+     * Test the constructor with an empty value
+     */
+    @Test public void testServerBinaryValueEmptyValue()
+    {
+        AttributeType attribute = TestServerEntryUtils.getBytesAttributeType();
+        
+        ServerBinaryValue value = new ServerBinaryValue( attribute, StringTools.EMPTY_BYTES );
+        
+        assertEquals( StringTools.EMPTY_BYTES, value.getReference() );
+        assertFalse( value.isNull() );
+    }
+    
+    
+    /**
+     * Test the constructor with a value
+     */
+    @Test public void testServerBinaryValueNoValue()
+    {
+        AttributeType attribute = TestServerEntryUtils.getBytesAttributeType();
+        byte[] val = new byte[]{0x01};
+        ServerBinaryValue value = new ServerBinaryValue( attribute );
+        
+        value.set( val );
+        assertTrue( Arrays.equals( val, value.getReference() ) );
+        assertFalse( value.isNull() );
+        assertTrue( Arrays.equals( val, value.getCopy() ) );
+    }
+    
+    
+    /**
+     * Test the constructor with a value
+     */
+    @Test public void testServerBinaryValue()
+    {
+        AttributeType attribute = TestServerEntryUtils.getBytesAttributeType();
+        byte[] val = new byte[]{0x01};
+        ServerBinaryValue value = new ServerBinaryValue( attribute, val );
+        
+        assertTrue( Arrays.equals( val, value.getReference() ) );
+        assertFalse( value.isNull() );
+        assertTrue( Arrays.equals( val, value.getCopy() ) );
+    }
+    
+    
+    /**
+     * Test the clone method
+     */
+    @Test
+    public void testClone() throws NamingException
+    {
+        AttributeType at1 = TestServerEntryUtils.getBytesAttributeType();
+        ServerBinaryValue sbv = new ServerBinaryValue( at1, null );
+        
+        ServerBinaryValue sbv1 = sbv.clone();
+        
+        assertEquals( sbv, sbv1 );
+        
+        sbv.set( StringTools.EMPTY_BYTES );
+        
+        assertNotSame( sbv, sbv1 );
+        assertTrue( Arrays.equals( StringTools.EMPTY_BYTES, sbv.get() ) );
+        
+        sbv.set(  BYTES2 );
+        sbv1 = sbv.clone();
+        
+        assertEquals( sbv, sbv1 );
+        
+        sbv.normalize();
+        
+        // Even if we didn't normalized sbv2, it should be equal to sbv,
+        // as if they have the same AT, and the same value, they are equal.
+        assertEquals( sbv, sbv1 );
+    }
+    
+
+    /**
+     * Test the equals method
+     */
+    @Test public void testEquals()
+    {
+        AttributeType at1 = TestServerEntryUtils.getBytesAttributeType();
+        
+        ServerBinaryValue value1 = new ServerBinaryValue( at1, new byte[]{0x01, (byte)0x02} );
+        ServerBinaryValue value2 = new ServerBinaryValue( at1, new byte[]{0x01, (byte)0x02} );
+        ServerBinaryValue value3 = new ServerBinaryValue( at1, new byte[]{0x01, (byte)0x82} );
+        ServerBinaryValue value4 = new ServerBinaryValue( at1, new byte[]{0x01} );
+        ServerBinaryValue value5 = new ServerBinaryValue( at1, null );
+        ServerBinaryValue value6 = new ServerBinaryValue( at, new byte[]{0x01, 0x02} );
+        ServerStringValue value7 = new ServerStringValue( TestServerEntryUtils.getIA5StringAttributeType(), 
+            "test" );
+        
+        assertTrue( value1.equals( value1 ) );
+        assertTrue( value1.equals( value2 ) );
+        assertTrue( value1.equals( value3 ) );
+        assertFalse( value1.equals( value4 ) );
+        assertFalse( value1.equals( value5 ) );
+        assertFalse( value1.equals( "test" ) );
+        assertFalse( value1.equals( null ) );
+        
+        assertFalse( value1.equals( value6 ) );
+        assertFalse( value1.equals( value7 ) );
+    }
+
+    
+    /**
+     * Test the getNormalizedValue method
+     */
+    @Test public void testGetNormalizedValue()
+    {
+        AttributeType attribute = TestServerEntryUtils.getBytesAttributeType();
+        
+        ServerBinaryValue value = new ServerBinaryValue( attribute, null );
+        assertNull( value.getNormalizedValue() );
+
+        value = new ServerBinaryValue( attribute, StringTools.EMPTY_BYTES );
+        assertTrue( Arrays.equals(  StringTools.EMPTY_BYTES, value.getNormalizedValue() ) );
+
+        value = new ServerBinaryValue( attribute, BYTES2 );
+        assertTrue( Arrays.equals( BYTES1, value.getNormalizedValue() ) );
+    }
+    
+    
+    /**
+     * Test the getNormalizedValue method
+     */
+    @Test public void testGetNormalizedValueCopy()
+    {
+        AttributeType attribute = TestServerEntryUtils.getBytesAttributeType();
+        
+        ServerBinaryValue value = new ServerBinaryValue( attribute, null );
+        assertNull( value.getNormalizedValueCopy() );
+
+        value = new ServerBinaryValue( attribute, StringTools.EMPTY_BYTES );
+        assertTrue( Arrays.equals(  StringTools.EMPTY_BYTES, value.getNormalizedValueCopy() ) );
+
+        value = new ServerBinaryValue( attribute, BYTES2 );
+        assertTrue( Arrays.equals( BYTES1, value.getNormalizedValueCopy() ) );
+    }
+    
+    
+    /**
+     * Test the getNormalizedValue method
+     */
+    @Test public void testGetNormalizedValueReference()
+    {
+        AttributeType attribute = TestServerEntryUtils.getBytesAttributeType();
+        
+        ServerBinaryValue value = new ServerBinaryValue( attribute, null );
+        assertNull( value.getNormalizedValueReference() );
+
+        value = new ServerBinaryValue( attribute, StringTools.EMPTY_BYTES );
+        assertTrue( Arrays.equals(  StringTools.EMPTY_BYTES, value.getNormalizedValueReference() ) );
+
+        value = new ServerBinaryValue( attribute, BYTES2 );
+        assertTrue( Arrays.equals( BYTES1, value.getNormalizedValueReference() ) );
+    }
+    
+    
+    /**
+     * Test the getAttributeType method
+     */
+    @Test
+    public void testgetAttributeType()
+    {
+        AttributeType attribute = TestServerEntryUtils.getBytesAttributeType();
+        ServerBinaryValue sbv = new ServerBinaryValue( attribute );
+        
+        assertEquals( attribute, sbv.getAttributeType() );
+    }    
+
+    
+    /**
+     * Test the isValid method
+     * 
+     * The SyntaxChecker does not accept values longer than 5 chars.
+     */
+    @Test public void testIsValid()
+    {
+        AttributeType attribute = TestServerEntryUtils.getBytesAttributeType();
+        
+        ServerBinaryValue value = new ServerBinaryValue( attribute, null );
+        assertTrue( value.isValid() );
+        
+        value = new ServerBinaryValue( attribute, StringTools.EMPTY_BYTES );
+        assertTrue( value.isValid() );
+
+        value = new ServerBinaryValue( attribute, new byte[]{0x01, 0x02} );
+        assertTrue( value.isValid() );
+
+        value = new ServerBinaryValue( attribute, new byte[]{0x01, 0x02, 0x03, 0x04, 0x05, 0x06} );
+        assertFalse( value.isValid() );
+    }
+    
+    
+    /**
+     * Tests to make sure the hashCode method is working properly.
+     * @throws Exception on errors
+     */
+    @Test public void testHashCode()
+    {
+        AttributeType attribute = TestServerEntryUtils.getBytesAttributeType();
+        ServerBinaryValue v0 = new ServerBinaryValue( attribute, new byte[]{0x01, 0x02} );
+        ServerBinaryValue v1 = new ServerBinaryValue( attribute, new byte[]{(byte)0x81, (byte)0x82} );
+        ServerBinaryValue v2 = new ServerBinaryValue( attribute, new byte[]{0x01, 0x02} );
+        assertEquals( v0.hashCode(), v1.hashCode() );
+        assertEquals( v1.hashCode(), v2.hashCode() );
+        assertEquals( v0.hashCode(), v2.hashCode() );
+        assertEquals( v0, v1 );
+        assertEquals( v0, v2 );
+        assertEquals( v1, v2 );
+        assertTrue( v0.isValid() );
+        assertTrue( v1.isValid() );
+        assertTrue( v2.isValid() );
+
+        ServerBinaryValue v3 = new ServerBinaryValue( attribute, new byte[]{0x01, 0x03} );
+        assertFalse( v3.equals( v0 ) );
+        assertFalse( v3.equals( v1 ) );
+        assertFalse( v3.equals( v2 ) );
+        assertTrue( v3.isValid() );
+    }
+
+
+    /**
+     * Test the same method
+     */
+    @Test
+    public void testSame() throws NamingException
+    {
+        AttributeType attribute = TestServerEntryUtils.getBytesAttributeType();
+        ServerBinaryValue sbv = new ServerBinaryValue( attribute );
+
+        sbv.normalize();
+        assertTrue( sbv.isSame() );
+        
+        sbv.set( StringTools.EMPTY_BYTES );
+        sbv.normalize();
+        assertTrue( sbv.isSame() );
+
+        sbv.set( BYTES1 );
+        sbv.normalize();
+        assertTrue( sbv.isSame() );
+
+        sbv.set( BYTES2 );
+        sbv.normalize();
+        assertFalse( sbv.isSame() );
+    }
+    
+    
+    /**
+     * Test the instanceOf method
+     */
+    @Test
+    public void testInstanceOf() throws NamingException
+    {
+        AttributeType attribute = TestServerEntryUtils.getBytesAttributeType();
+        ServerBinaryValue sbv = new ServerBinaryValue( attribute );
+        
+        assertTrue( sbv.instanceOf( attribute ) );
+        
+        attribute = TestServerEntryUtils.getIA5StringAttributeType();
+        
+        assertFalse( sbv.instanceOf( attribute ) );
+    }    
+    
+
+    /**
+     * Test the normalize method
+     */
+    @Test
+    public void testNormalize() throws NamingException
+    {
+        AttributeType attribute = TestServerEntryUtils.getBytesAttributeType();
+        ServerBinaryValue sbv = new ServerBinaryValue( attribute );
+
+        sbv.normalize();
+        assertEquals( null, sbv.getNormalizedValue() );
+        
+        sbv.set( StringTools.EMPTY_BYTES );
+        sbv.normalize();
+        assertTrue( Arrays.equals( StringTools.EMPTY_BYTES, sbv.getNormalizedValue() ) );
+
+        sbv.set( BYTES2 );
+        sbv.normalize();
+        assertTrue( Arrays.equals( BYTES1, sbv.getNormalizedValue() ) );
+    }
+    
+
+    /**
+     * Test the compareTo method
+     */
+    @Test
+    public void testCompareTo()
+    {
+        AttributeType at1 = TestServerEntryUtils.getBytesAttributeType();
+        ServerBinaryValue v0 = new ServerBinaryValue( at1, BYTES1 );
+        ServerBinaryValue v1 = new ServerBinaryValue( at1, BYTES2 );
+        
+        assertEquals( 0, v0.compareTo( v1 ) );
+        assertEquals( 0, v1.compareTo( v0 ) );
+
+        ServerBinaryValue v2 = new ServerBinaryValue( at1, null );
+        
+        assertEquals( 1, v0.compareTo( v2 ) );
+        assertEquals( -1, v2.compareTo( v0 ) );
+    }
+
+
+    /**
+     * Test serialization of a BinaryValue which has a normalized value
+     */
+    @Test public void testNormalizedBinaryValueSerialization() throws NamingException, IOException, ClassNotFoundException
+    {
+        byte[] v1 = StringTools.getBytesUtf8( "  Test   Test  " );
+        byte[] v1Norm = StringTools.getBytesUtf8( "Test   Test" );
+        
+        // First check with a value which will be normalized
+        ServerBinaryValue sv = new ServerBinaryValue( at, v1 );
+        
+        sv.normalize();
+        byte[] normalized = sv.getNormalizedValueReference();
+        
+        assertTrue( Arrays.equals( v1Norm, normalized ) );
+        assertTrue( Arrays.equals( v1, sv.getReference() ) );
+        
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        ObjectOutputStream out = new ObjectOutputStream( baos );
+        
+        sv.writeExternal( out );
+        
+        ObjectInputStream in = null;
+
+        byte[] data = baos.toByteArray();
+        in = new ObjectInputStream( new ByteArrayInputStream( data ) );
+        
+        ServerBinaryValue sv2 = new ServerBinaryValue( at );
+        sv2.readExternal( in );
+        
+        assertEquals( sv, sv2 );
+    }
+
+
+    /**
+     * Test serialization of a BinaryValue which does not have a normalized value
+     */
+    @Test public void testNoNormalizedBinaryValueSerialization() throws NamingException, IOException, ClassNotFoundException
+    {
+        byte[] v1 = StringTools.getBytesUtf8( "test" );
+        byte[] v1Norm = StringTools.getBytesUtf8( "test" );
+
+        // First check with a value which will be normalized
+        ServerBinaryValue sv = new ServerBinaryValue( at, v1 );
+        
+        sv.normalize();
+        byte[] normalized = sv.getNormalizedValueReference();
+        
+        assertTrue( Arrays.equals( v1Norm, normalized ) );
+        assertTrue( Arrays.equals( v1, sv.get() ) );
+        
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        ObjectOutputStream out = new ObjectOutputStream( baos );
+        
+        sv.writeExternal( out );
+        
+        ObjectInputStream in = null;
+
+        byte[] data = baos.toByteArray();
+        
+        in = new ObjectInputStream( new ByteArrayInputStream( data ) );
+        
+        ServerBinaryValue sv2 = new ServerBinaryValue( at );
+        sv2.readExternal( in );
+        
+        assertEquals( sv, sv2 );
+   }
+
+
+    /**
+     * Test serialization of a null BinaryValue
+     */
+    @Test public void testNullBinaryValueSerialization() throws NamingException, IOException, ClassNotFoundException
+    {
+        // First check with a value which will be normalized
+        ServerBinaryValue sv = new ServerBinaryValue( at );
+        
+        sv.normalize();
+        byte[] normalized = sv.getNormalizedValueReference();
+        
+        assertEquals( null, normalized );
+        assertEquals( null, sv.get() );
+        
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        ObjectOutputStream out = new ObjectOutputStream( baos );
+        
+        sv.writeExternal( out );
+        
+        ObjectInputStream in = null;
+
+        byte[] data = baos.toByteArray();
+        
+        in = new ObjectInputStream( new ByteArrayInputStream( data ) );
+        
+        ServerBinaryValue sv2 = new ServerBinaryValue( at );
+        sv2.readExternal( in );
+        
+        assertEquals( sv, sv2 );
+   }
+
+
+    /**
+     * Test serialization of an empty BinaryValue
+     */
+    @Test public void testEmptyBinaryValueSerialization() throws NamingException, IOException, ClassNotFoundException
+    {
+        // First check with a value which will be normalized
+        ServerBinaryValue sv = new ServerBinaryValue( at, StringTools.EMPTY_BYTES );
+        
+        sv.normalize();
+        byte[] normalized = sv.getNormalizedValueReference();
+        
+        assertTrue( Arrays.equals( StringTools.EMPTY_BYTES, normalized ) );
+        assertTrue( Arrays.equals( StringTools.EMPTY_BYTES, sv.get() ) );
+        
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        ObjectOutputStream out = new ObjectOutputStream( baos );
+        
+        sv.writeExternal( out );
+        
+        ObjectInputStream in = null;
+
+        byte[] data = baos.toByteArray();
+        
+        in = new ObjectInputStream( new ByteArrayInputStream( data ) );
+        
+        ServerBinaryValue sv2 = new ServerBinaryValue( at );
+        sv2.readExternal( in );
+        
+        assertEquals( sv, sv2 );
+   }
+}
\ No newline at end of file
diff --git a/old_trunk/core-entry/src/test/java/org/apache/directory/server/core/entry/ServerEntrySerializerTest.java b/old_trunk/core-entry/src/test/java/org/apache/directory/server/core/entry/ServerEntrySerializerTest.java
new file mode 100644
index 0000000..bd07901
--- /dev/null
+++ b/old_trunk/core-entry/src/test/java/org/apache/directory/server/core/entry/ServerEntrySerializerTest.java
@@ -0,0 +1,177 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.entry;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.schema.bootstrap.ApacheSchema;
+import org.apache.directory.server.schema.bootstrap.ApachemetaSchema;
+import org.apache.directory.server.schema.bootstrap.BootstrapSchemaLoader;
+import org.apache.directory.server.schema.bootstrap.CoreSchema;
+import org.apache.directory.server.schema.bootstrap.CosineSchema;
+import org.apache.directory.server.schema.bootstrap.InetorgpersonSchema;
+import org.apache.directory.server.schema.bootstrap.Schema;
+import org.apache.directory.server.schema.bootstrap.SystemSchema;
+import org.apache.directory.server.schema.registries.DefaultOidRegistry;
+import org.apache.directory.server.schema.registries.DefaultRegistries;
+import org.apache.directory.server.schema.registries.OidRegistry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.schema.DeepTrimToLowerNormalizer;
+import org.apache.directory.shared.ldap.schema.OidNormalizer;
+import org.apache.directory.shared.ldap.util.StringTools;
+
+import static org.junit.Assert.assertEquals;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+
+/**
+ * Test the ServerEntry serialization/deserialization class
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ServerEntrySerializerTest
+{
+    private static BootstrapSchemaLoader loader;
+    private static Registries registries;
+    private static OidRegistry oidRegistry;
+    private static Map<String, OidNormalizer> oids;
+    private static Map<String, OidNormalizer> oidOids;
+
+    /**
+     * Initialize the registries once for the whole test suite
+     */
+    @BeforeClass
+    public static void setup() throws NamingException
+    {
+        loader = new BootstrapSchemaLoader();
+        oidRegistry = new DefaultOidRegistry();
+        registries = new DefaultRegistries( "bootstrap", loader, oidRegistry );
+        
+        // load essential bootstrap schemas 
+        Set<Schema> bootstrapSchemas = new HashSet<Schema>();
+        bootstrapSchemas.add( new ApachemetaSchema() );
+        bootstrapSchemas.add( new ApacheSchema() );
+        bootstrapSchemas.add( new CoreSchema() );
+        bootstrapSchemas.add( new SystemSchema() );
+        bootstrapSchemas.add( new InetorgpersonSchema() );
+        bootstrapSchemas.add( new CosineSchema() );
+        loader.loadWithDependencies( bootstrapSchemas, registries );
+        
+        oids = new HashMap<String, OidNormalizer>();
+
+        oids.put( "dc", new OidNormalizer( "dc", new DeepTrimToLowerNormalizer() ) );
+        oids.put( "domaincomponent", new OidNormalizer( "dc", new DeepTrimToLowerNormalizer() ) );
+        oids.put( "0.9.2342.19200300.100.1.25", new OidNormalizer( "dc", new DeepTrimToLowerNormalizer() ) );
+        oids.put( "ou", new OidNormalizer( "ou", new DeepTrimToLowerNormalizer() ) );
+        oids.put( "organizationalUnitName", new OidNormalizer( "ou", new DeepTrimToLowerNormalizer() ) );
+        oids.put( "2.5.4.11", new OidNormalizer( "ou", new DeepTrimToLowerNormalizer() ) );
+    
+    
+        // Another map where we store OIDs instead of names.
+        oidOids = new HashMap<String, OidNormalizer>();
+
+        oidOids.put( "dc", new OidNormalizer( "0.9.2342.19200300.100.1.25", new DeepTrimToLowerNormalizer() ) );
+        oidOids.put( "domaincomponent", new OidNormalizer( "0.9.2342.19200300.100.1.25", new DeepTrimToLowerNormalizer() ) );
+        oidOids.put( "0.9.2342.19200300.100.1.25", 
+            new OidNormalizer( "0.9.2342.19200300.100.1.25", new DeepTrimToLowerNormalizer() ) );
+        oidOids.put( "ou", new OidNormalizer( "2.5.4.11", new DeepTrimToLowerNormalizer() ) );
+        oidOids.put( "organizationalUnitName", new OidNormalizer( "2.5.4.11", new DeepTrimToLowerNormalizer() ) );
+        oidOids.put( "2.5.4.11", new OidNormalizer( "2.5.4.11", new DeepTrimToLowerNormalizer() ) );
+    }
+
+    
+    @Test public void testSerializeEmtpyServerEntry() throws IOException
+    {
+        LdapDN dn = LdapDN.EMPTY_LDAPDN;
+        ServerEntry entry = new DefaultServerEntry( registries, dn );
+
+        ServerEntrySerializer ses = new ServerEntrySerializer( registries );
+        
+        byte[] data = ses.serialize( entry );
+        
+        ServerEntry result = (ServerEntry)ses.deserialize( data );
+        
+        assertEquals( entry, result );
+    }
+
+    @Test public void testSerializeDNServerEntry() throws IOException, NamingException
+    {
+        LdapDN dn = new LdapDN( "cn=text, dc=example, dc=com" );
+        dn.normalize( oids );
+        
+        ServerEntry entry = new DefaultServerEntry( registries, dn );
+
+        ServerEntrySerializer ses = new ServerEntrySerializer( registries );
+        
+        byte[] data = ses.serialize( entry );
+        
+        ServerEntry result = (ServerEntry)ses.deserialize( data );
+        
+        assertEquals( entry, result );
+    }
+
+
+    @Test public void testSerializeServerEntryOC() throws IOException, NamingException
+    {
+        LdapDN dn = new LdapDN( "cn=text, dc=example, dc=com" );
+        dn.normalize( oids );
+        
+        ServerEntry entry = new DefaultServerEntry( registries, dn );
+        entry.add( "objectClass", "top", "person", "inetOrgPerson", "organizationalPerson" );
+
+        ServerEntrySerializer ses = new ServerEntrySerializer( registries );
+
+        byte[] data = ses.serialize( entry );
+        
+        ServerEntry result = (ServerEntry)ses.deserialize( data );
+        
+        assertEquals( entry, result );
+    }
+
+
+    @Test public void testSerializeServerEntry() throws IOException, NamingException
+    {
+        LdapDN dn = new LdapDN( "cn=text, dc=example, dc=com" );
+        dn.normalize( oids );
+        
+        ServerEntry entry = new DefaultServerEntry( registries, dn );
+        entry.add( "objectClass", "top", "person", "inetOrgPerson", "organizationalPerson" );
+        entry.add( "cn", "text", "test" );
+        entry.add( "SN", (String)null );
+        entry.add( "userPassword", StringTools.getBytesUtf8( "password" ) );
+
+        ServerEntrySerializer ses = new ServerEntrySerializer( registries );
+        
+        byte[] data = ses.serialize( entry );
+        
+        ServerEntry result = (ServerEntry)ses.deserialize( data );
+        
+        assertEquals( entry, result );
+    }
+}
diff --git a/old_trunk/core-entry/src/test/java/org/apache/directory/server/core/entry/ServerModificationTest.java b/old_trunk/core-entry/src/test/java/org/apache/directory/server/core/entry/ServerModificationTest.java
new file mode 100644
index 0000000..269f16e
--- /dev/null
+++ b/old_trunk/core-entry/src/test/java/org/apache/directory/server/core/entry/ServerModificationTest.java
@@ -0,0 +1,60 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.entry;
+
+import org.apache.directory.shared.ldap.entry.Modification;
+import org.apache.directory.shared.ldap.entry.ModificationOperation;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+
+/**
+ * Test the ServerModification class
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ServerModificationTest
+{
+    @Test public void testCreateServerModification()
+    {
+        AttributeType at = TestServerEntryUtils.getIA5StringAttributeType();
+        ServerAttribute attribute = new DefaultServerAttribute( at );
+        attribute.add( "test1", "test2" );
+        
+        Modification mod = new ServerModification( ModificationOperation.ADD_ATTRIBUTE, attribute );
+        Modification clone = mod.clone();
+        
+        attribute.remove( "test2" );
+        
+        ServerAttribute clonedAttribute = (ServerAttribute)clone.getAttribute();
+        
+        assertEquals( 1, mod.getAttribute().size() );
+        assertTrue( mod.getAttribute().contains( "test1" ) );
+
+        assertEquals( 2, clonedAttribute.size() );
+        assertTrue( clone.getAttribute().contains( "test1" ) );
+        assertTrue( clone.getAttribute().contains( "test2" ) );
+    }
+}
diff --git a/old_trunk/core-entry/src/test/java/org/apache/directory/server/core/entry/ServerStringValueTest.java b/old_trunk/core-entry/src/test/java/org/apache/directory/server/core/entry/ServerStringValueTest.java
new file mode 100644
index 0000000..b8827a3
--- /dev/null
+++ b/old_trunk/core-entry/src/test/java/org/apache/directory/server/core/entry/ServerStringValueTest.java
@@ -0,0 +1,737 @@
+/*

+ *  Licensed to the Apache Software Foundation (ASF) under one

+ *  or more contributor license agreements.  See the NOTICE file

+ *  distributed with this work for additional information

+ *  regarding copyright ownership.  The ASF licenses this file

+ *  to you 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. 

+ *  

+ */

+package org.apache.directory.server.core.entry;

+

+

+import java.io.ByteArrayInputStream;

+import java.io.ByteArrayOutputStream;

+import java.io.IOException;

+import java.io.ObjectInputStream;

+import java.io.ObjectOutputStream;

+import java.util.ArrayList;

+import java.util.Collections;

+import java.util.Comparator;

+import java.util.HashSet;

+import java.util.List;

+

+import javax.naming.NamingException;

+import javax.naming.directory.InvalidAttributeValueException;

+

+import org.apache.directory.shared.ldap.entry.Value;

+import org.apache.directory.shared.ldap.schema.AttributeType;

+import org.apache.directory.shared.ldap.schema.DeepTrimToLowerNormalizer;

+import org.apache.directory.shared.ldap.schema.NoOpNormalizer;

+import org.apache.directory.shared.ldap.schema.syntax.AcceptAllSyntaxChecker;

+import org.apache.directory.shared.ldap.schema.syntax.SyntaxChecker;

+

+import static org.junit.Assert.assertEquals;

+import static org.junit.Assert.assertFalse;

+import static org.junit.Assert.assertNotSame;

+import static org.junit.Assert.assertNull;

+import static org.junit.Assert.assertTrue;

+import static org.junit.Assert.fail;

+

+import org.junit.Before;

+import org.junit.Test;

+

+import jdbm.helper.StringComparator;

+

+

+/**

+ * Tests that the ServerStringValue class works properly as expected.

+ *

+ * Some notes while conducting tests:

+ *

+ * <ul>

+ *   <li>comparing values with different types - how does this behave</li>

+ *   <li>exposing access to at from value or to a comparator?</li>

+ * </ul>

+ *

+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>

+ * @version $Rev$, $Date$

+ */

+public class ServerStringValueTest

+{

+    private TestServerEntryUtils.S s;

+    private TestServerEntryUtils.AT at;

+    private TestServerEntryUtils.MR mr;

+

+    

+    /**

+     * Initialize an AttributeType and the associated MatchingRule 

+     * and Syntax

+     */

+    @Before public void initAT()

+    {

+        s = new TestServerEntryUtils.S( "1.1.1.1", false );

+        s.setSyntaxChecker( new AcceptAllSyntaxChecker( "1.1.1.1" ) );

+        mr = new TestServerEntryUtils.MR( "1.1.2.1" );

+        mr.syntax = s;

+        mr.comparator = new StringComparator();

+        mr.normalizer = new DeepTrimToLowerNormalizer();

+        at = new TestServerEntryUtils.AT( "1.1.3.1" );

+        at.setEquality( mr );

+        at.setOrdering( mr );

+        at.setSubstr( mr );

+        at.setSyntax( s );

+    }

+    

+    /**

+     * Test the constructor with a null value

+     */

+    @Test 

+    public void testServerStringValueNullValue()

+    {

+        AttributeType attribute = TestServerEntryUtils.getIA5StringAttributeType();

+        

+        ServerStringValue value = new ServerStringValue( attribute, null );

+        

+        assertNull( value.get() );

+        assertTrue( value.isNull() );

+    }

+    

+    

+    /**

+     * Test the getNormalizedValue method

+     */

+    @Test public void testGetNormalizedValue()

+    {

+        AttributeType attribute = TestServerEntryUtils.getIA5StringAttributeType();

+        

+        ServerStringValue value = new ServerStringValue( attribute, null );

+        

+        assertFalse( value.isNormalized() );

+        assertNull( value.getNormalizedValue() );

+        assertTrue( value.isNormalized() );

+

+        value.set( "" );

+        assertFalse( value.isNormalized() );

+        assertEquals( "", value.getNormalizedValue() );

+        assertTrue( value.isNormalized() );

+

+        value.set( "TEST" );

+        assertFalse( value.isNormalized() );

+        assertEquals( "test", value.getNormalizedValue() );

+        assertTrue( value.isNormalized() );

+    }

+    

+

+    /**

+     * Test the isValid method

+     * 

+     * The SyntaxChecker does not accept values longer than 5 chars.

+     */

+    @Test public void testIsValid()

+    {

+        AttributeType attribute = TestServerEntryUtils.getIA5StringAttributeType();

+        

+        ServerStringValue value = new ServerStringValue( attribute, null );

+        assertTrue( value.isValid() );

+

+        value.set( "" );

+        assertTrue( value.isValid() );

+

+        value.set( "TEST" );

+        assertTrue( value.isValid() );

+

+        value.set( "testlong" );

+        assertFalse( value.isValid() );

+    }

+    

+    

+    /**

+     * Test the normalize method

+     */

+    @Test

+    public void testNormalize() throws NamingException

+    {

+        AttributeType attribute = TestServerEntryUtils.getIA5StringAttributeType();

+        ServerStringValue ssv = new ServerStringValue( attribute );

+

+        ssv.normalize();

+        assertEquals( null, ssv.getNormalizedValue() );

+        

+        ssv.set( "" );

+        ssv.normalize();

+        assertEquals( "", ssv.getNormalizedValue() );

+

+        ssv.set(  "  This is    a   TEST  " );

+        ssv.normalize();

+        assertEquals( "this is a test", ssv.getNormalizedValue() );

+    }

+    

+

+    /**

+     * Test the instanceOf method

+     */

+    @Test

+    public void testInstanceOf() throws NamingException

+    {

+        AttributeType attribute = TestServerEntryUtils.getIA5StringAttributeType();

+        ServerStringValue ssv = new ServerStringValue( attribute );

+        

+        assertTrue( ssv.instanceOf( attribute ) );

+        

+        attribute = TestServerEntryUtils.getBytesAttributeType();

+        

+        assertFalse( ssv.instanceOf( attribute ) );

+    }    

+    

+

+    /**

+     * Test the getAttributeType method

+     */

+    @Test

+    public void testgetAttributeType()

+    {

+        AttributeType attribute = TestServerEntryUtils.getIA5StringAttributeType();

+        ServerStringValue ssv = new ServerStringValue( attribute );

+        

+        assertEquals( attribute, ssv.getAttributeType() );

+    }    

+

+    

+    /**

+     * Test the equals method

+     */

+    @Test public void testEquals()

+    {

+        AttributeType at1 = TestServerEntryUtils.getIA5StringAttributeType();

+        AttributeType at2 = TestServerEntryUtils.getBytesAttributeType();

+        

+        ServerStringValue value1 = new ServerStringValue( at1, "test" );

+        ServerStringValue value2 = new ServerStringValue( at1, "test" );

+        ServerStringValue value3 = new ServerStringValue( at1, "TEST" );

+        ServerStringValue value4 = new ServerStringValue( at1, "tes" );

+        ServerStringValue value5 = new ServerStringValue( at1, null );

+        ServerBinaryValue valueBytes = new ServerBinaryValue( at2, new byte[]{0x01} );

+        ServerStringValue valueString = new ServerStringValue( at, "test" );

+        

+        assertTrue( value1.equals( value1 ) );

+        assertTrue( value1.equals( value2 ) );

+        assertTrue( value1.equals( value3 ) );

+        assertFalse( value1.equals( value4 ) );

+        assertFalse( value1.equals( value5 ) );

+        assertFalse( value1.equals( "test" ) );

+        assertFalse( value1.equals( null ) );

+        

+        assertFalse( value1.equals( valueString ) );

+        assertFalse( value1.equals( valueBytes ) );

+    }

+

+    

+    

+    

+    

+    

+    

+    

+    

+    

+    /**

+     * Test the constructor with bad AttributeType

+     */

+    @Test public void testBadConstructor()

+    {

+        try

+        {

+            new ServerStringValue( null );

+            fail();

+        }

+        catch ( IllegalArgumentException iae )

+        {

+            // Expected...

+        }

+        

+        // create a AT without any syntax

+        AttributeType attribute = new TestServerEntryUtils.AT( "1.1.3.1" );

+        

+        try

+        {

+            new ServerStringValue( attribute );

+            fail();

+        }

+        catch ( IllegalArgumentException iae )

+        {

+            // Expected...

+        }

+    }

+

+

+    /**

+     * Tests to make sure the hashCode method is working properly.

+     * @throws Exception on errors

+     */

+    @Test public void testHashCode()

+    {

+        AttributeType at1 = TestServerEntryUtils.getCaseIgnoringAttributeNoNumbersType();

+        ServerStringValue v0 = new ServerStringValue( at1, "Alex" );

+        ServerStringValue v1 = new ServerStringValue( at1, "ALEX" );

+        ServerStringValue v2 = new ServerStringValue( at1, "alex" );

+        

+        assertEquals( v0.hashCode(), v1.hashCode() );

+        assertEquals( v0.hashCode(), v2.hashCode() );

+        assertEquals( v1.hashCode(), v2.hashCode() );

+        

+        assertEquals( v0, v1 );

+        assertEquals( v0, v2 );

+        assertEquals( v1, v2 );

+        

+        assertTrue( v0.isValid() );

+        assertTrue( v1.isValid() );

+        assertTrue( v2.isValid() );

+

+        ServerStringValue v3 = new ServerStringValue( at1, "Timber" );

+        

+        assertTrue( v3.isValid() );

+        assertNotSame( v0.hashCode(), v3.hashCode() );

+

+        ServerStringValue v4 = new ServerStringValue( at, "Alex" );

+        

+        assertNotSame( v0.hashCode(), v4.hashCode() );

+    }

+    

+    

+    /**

+     * Test the compareTo method

+     */

+    @Test

+    public void testCompareTo()

+    {

+        AttributeType at1 = TestServerEntryUtils.getCaseIgnoringAttributeNoNumbersType();

+        ServerStringValue v0 = new ServerStringValue( at1, "Alex" );

+        ServerStringValue v1 = new ServerStringValue( at1, "ALEX" );

+        

+        assertEquals( 0, v0.compareTo( v1 ) );

+        assertEquals( 0, v1.compareTo( v0 ) );

+

+        ServerStringValue v2 = new ServerStringValue( at1, null );

+        

+        assertEquals( 1, v0.compareTo( v2 ) );

+        assertEquals( -1, v2.compareTo( v0 ) );

+    }

+

+

+    /**

+     * Test the clone method

+     */

+    @Test

+    public void testClone() throws NamingException

+    {

+        AttributeType at1 = TestServerEntryUtils.getCaseIgnoringAttributeNoNumbersType();

+        ServerStringValue ssv = new ServerStringValue( at1, "Test" );

+        

+        ServerStringValue ssv1 = ssv.clone();

+        

+        assertEquals( ssv, ssv1 );

+        

+        ssv.set( "" );

+        

+        assertNotSame( ssv, ssv1 );

+        assertEquals( "", ssv.get() );

+        

+        ssv.set(  "  This is    a   TEST  " );

+        ssv1 = ssv.clone();

+        

+        assertEquals( ssv, ssv1 );

+        

+        ssv.normalize();

+        

+        assertEquals( ssv, ssv1 );

+    }

+    

+

+    /**

+     * Presumes an attribute which constrains it's values to some constant

+     * strings: LOW, MEDIUM, HIGH.  Normalization does nothing. MatchingRules

+     * are exact case matching.

+     *

+     * @throws Exception on errors

+     */

+    @Test public void testConstrainedString()

+    {

+        s.setSyntaxChecker( new SyntaxChecker() {

+            public String getSyntaxOid() { return "1.1.1.1"; }

+            public boolean isValidSyntax( Object value )

+            {

+                if ( value instanceof String )

+                {

+                    String strval = ( String ) value;

+                    return strval.equals( "HIGH" ) || strval.equals( "LOW" ) || strval.equals( "MEDIUM" );

+                }

+                return false;

+            }

+            public void assertSyntax( Object value ) throws NamingException

+            { if ( ! isValidSyntax( value ) ) {throw new InvalidAttributeValueException(); }}

+        });

+

+        mr.syntax = s;

+        mr.comparator = new Comparator<String>()

+        {

+            public int compare( String o1, String o2 )

+            {

+                if ( o1 == null )

+                {

+                    if ( o2 == null )

+                    {

+                        return 0;

+                    }

+                    else

+                    {

+                        return -1;

+                    }

+                }

+                else if ( o2 == null )

+                {

+                    return 1;

+                }

+

+                int i1 = getValue( o1 );

+                int i2 = getValue( o2 );

+

+                if ( i1 == i2 ) { return 0; }

+                if ( i1 > i2 ) { return 1; }

+                if ( i1 < i2 ) { return -1; }

+

+                throw new IllegalStateException( "should not get here at all" );

+            }

+

+            public int getValue( String val )

+            {

+                if ( val.equals( "LOW" ) ) { return 0; }

+                if ( val.equals( "MEDIUM" ) ) { return 1; }

+                if ( val.equals( "HIGH" ) ) { return 2; }

+                throw new IllegalArgumentException( "Not a valid value" );

+            }

+        };

+        mr.normalizer = new NoOpNormalizer();

+        at.setEquality( mr );

+        at.setSyntax( s );

+

+        // check that normalization and syntax checks work as expected

+        ServerStringValue value = new ServerStringValue( at, "HIGH" );

+        assertEquals( value.get(), value.get() );

+        assertTrue( value.isValid() );

+        value = new ServerStringValue( at, "high" );

+        assertFalse( value.isValid() );

+

+        // create a bunch to best tested for equals and in containers

+        ServerStringValue v0 = new ServerStringValue( at, "LOW" );

+        assertTrue( v0.isValid() );

+        ServerStringValue v1 = new ServerStringValue( at, "LOW" );

+        assertTrue( v1.isValid() );

+        ServerStringValue v2 = new ServerStringValue( at, "MEDIUM" );

+        assertTrue( v2.isValid() );

+        ServerStringValue v3 = new ServerStringValue( at, "HIGH" );

+        assertTrue( v3.isValid() );

+        ServerStringValue v4 = new ServerStringValue( at );

+        assertFalse( v4.isValid() );

+        ServerStringValue v5 = new ServerStringValue( at );

+        assertFalse( v5.isValid() );

+

+        // check equals

+        assertTrue( v0.equals( v1 ) );

+        assertTrue( v1.equals( v0 ) );

+        assertEquals( 0, v0.compareTo( v1 ) );

+

+        assertTrue( v4.equals( v5 ) );

+        assertTrue( v5.equals( v4 ) );

+        assertEquals( 0, v4.compareTo( v5 ) );

+

+        assertFalse( v2.equals( v3 ) );

+        assertFalse( v3.equals( v2 ) );

+        assertTrue( v2.compareTo( v3 ) < 0 );

+        assertTrue( v3.compareTo( v2 ) > 0 );

+

+        // add all except v1 and v5 to a set

+        HashSet<ServerStringValue> set = new HashSet<ServerStringValue>();

+        set.add( v0 );

+        set.add( v2 );

+        set.add( v3 );

+        set.add( v4 );

+

+        // check contains method

+        assertTrue( "since v1.equals( v0 ) and v0 was added then this should be true", set.contains( v1 ) );

+        assertTrue( "since v4.equals( v5 ) and v4 was added then this should be true", set.contains( v5 ) );

+

+        // check ordering based on the comparator

+        List<Value<String>> list = new ArrayList<Value<String>>();

+        list.add( v1 );

+        list.add( v3 );

+        list.add( v5 );

+        list.add( v0 );

+        list.add( v2 );

+        list.add( v4 );

+

+        Collections.sort( list );

+

+        // null ones are at first 2 indices

+        assertTrue( "since v4 equals v5 and has no value either could be at index 0 & 1", list.get( 0 ).equals( v4 ) );

+        assertTrue( "since v4 equals v5 and has no value either could be at index 0 & 1", list.get( 0 ).equals( v5 ) );

+        assertTrue( "since v4 equals v5 and has no value either could be at index 0 & 1", list.get( 1 ).equals( v4 ) );

+        assertTrue( "since v4 equals v5 and has no value either could be at index 0 & 1", list.get( 1 ).equals( v5 ) );

+

+        // low ones are at the 3rd and 4th indices

+        assertTrue( "since v0 equals v1 either could be at index 2 & 3", list.get( 2 ).equals( v0 ) );

+        assertTrue( "since v0 equals v1 either could be at index 2 & 3", list.get( 2 ).equals( v1 ) );

+        assertTrue( "since v0 equals v1 either could be at index 2 & 3", list.get( 3 ).equals( v0 ) );

+        assertTrue( "since v0 equals v1 either could be at index 2 & 3", list.get( 3 ).equals( v1 ) );

+

+        // medium then high next

+        assertTrue( "since v2 \"MEDIUM\" should be at index 4", list.get( 4 ).equals( v2 ) );

+        assertTrue( "since v3 \"HIGH\" should be at index 5", list.get( 5 ).equals( v3 ) );

+

+        assertEquals( 6, list.size() );

+    }

+

+

+    /**

+     * Creates a string value with an attribute type that is of a syntax

+     * which accepts anything.  Also there is no normalization since the

+     * value is the same as the normalized value.  This makes the at technically

+     * a binary value however it can be dealt with as a string so this test

+     * is still OK.

+     * @throws Exception on errors

+     */

+    @Test public void testAcceptAllNoNormalization()

+    {

+        // check that normalization and syntax checks work as expected

+        ServerStringValue value = new ServerStringValue( at, "hello" );

+        assertEquals( value.get(), value.get() );

+        assertTrue( value.isValid() );

+

+        // create a bunch to best tested for equals and in containers

+        ServerStringValue v0 = new ServerStringValue( at, "hello" );

+        ServerStringValue v1 = new ServerStringValue( at, "hello" );

+        ServerStringValue v2 = new ServerStringValue( at, "next0" );

+        ServerStringValue v3 = new ServerStringValue( at, "next1" );

+        ServerStringValue v4 = new ServerStringValue( at );

+        ServerStringValue v5 = new ServerStringValue( at );

+

+        // check equals

+        assertTrue( v0.equals( v1 ) );

+        assertTrue( v1.equals( v0 ) );

+        assertTrue( v4.equals( v5 ) );

+        assertTrue( v5.equals( v4 ) );

+        assertFalse( v2.equals( v3 ) );

+        assertFalse( v3.equals( v2 ) );

+

+        // add all except v1 and v5 to a set

+        HashSet<ServerStringValue> set = new HashSet<ServerStringValue>();

+        set.add( v0 );

+        set.add( v2 );

+        set.add( v3 );

+        set.add( v4 );

+

+        // check contains method

+        assertTrue( "since v1.equals( v0 ) and v0 was added then this should be true", set.contains( v1 ) );

+        assertTrue( "since v4.equals( v5 ) and v4 was added then this should be true", set.contains( v5 ) );

+

+        // check ordering based on the comparator

+        ArrayList<ServerStringValue> list = new ArrayList<ServerStringValue>();

+        list.add( v1 );

+        list.add( v3 );

+        list.add( v5 );

+        list.add( v0 );

+        list.add( v2 );

+        list.add( v4 );

+

+        Comparator<ServerStringValue> c = new Comparator<ServerStringValue>()

+        {

+            public int compare( ServerStringValue o1, ServerStringValue o2 )

+            {

+                String n1 = null;

+                String n2 = null;

+                

+                if ( o1 != null )

+                {

+                    n1 = o1.get();

+                }

+

+                if ( o2 != null )

+                {

+                    n2 = o2.get();

+                }

+

+                if ( n1 == null )

+                {

+                    return ( n2 == null ) ? 0 : -1;

+                }

+                else if ( n2 == null )

+                {

+                    return 1;

+                }

+

+                try

+                {

+                    return mr.getComparator().compare( n1, n2 );

+                }

+                catch ( NamingException ne )

+                {

+                    throw new IllegalStateException( "Normalization and comparison should succeed!", ne );

+                }

+            }

+        };

+

+        Collections.sort( list, c );

+

+        assertTrue( "since v4 equals v5 and has no value either could be at index 0 & 1", list.get( 0 ).equals( v4 ) );

+        assertTrue( "since v4 equals v5 and has no value either could be at index 0 & 1", list.get( 0 ).equals( v5 ) );

+        assertTrue( "since v4 equals v5 and has no value either could be at index 0 & 1", list.get( 1 ).equals( v4 ) );

+        assertTrue( "since v4 equals v5 and has no value either could be at index 0 & 1", list.get( 1 ).equals( v5 ) );

+

+        assertTrue( "since v0 equals v1 either could be at index 2 & 3", list.get( 2 ).equals( v0 ) );

+        assertTrue( "since v0 equals v1 either could be at index 2 & 3", list.get( 2 ).equals( v1 ) );

+        assertTrue( "since v0 equals v1 either could be at index 2 & 3", list.get( 3 ).equals( v0 ) );

+        assertTrue( "since v0 equals v1 either could be at index 2 & 3", list.get( 3 ).equals( v1 ) );

+

+        assertTrue( "since v2 \"next0\" should be at index 4", list.get( 4 ).equals( v2 ) );

+        assertTrue( "since v3 \"next1\" should be at index 5", list.get( 5 ).equals( v3 ) );

+

+        assertEquals( 6, list.size() );

+    }

+

+    

+    /**

+     * Test serialization of a StringValue which has a normalized value

+     */

+    @Test public void testNormalizedStringValueSerialization() throws NamingException, IOException, ClassNotFoundException

+    {

+        // First check with a value which will be normalized

+        ServerStringValue sv = new ServerStringValue( at, "  Test   Test  " );

+        

+        sv.normalize();

+        String normalized = sv.getNormalizedValue();

+        

+        assertEquals( "test test", normalized );

+        assertEquals( "  Test   Test  ", sv.get() );

+        

+        ByteArrayOutputStream baos = new ByteArrayOutputStream();

+        ObjectOutputStream out = new ObjectOutputStream( baos );

+        

+        sv.writeExternal( out );

+        

+        ObjectInputStream in = null;

+

+        byte[] data = baos.toByteArray();

+        in = new ObjectInputStream( new ByteArrayInputStream( data ) );

+        

+        ServerStringValue sv2 = new ServerStringValue( at );

+        sv2.readExternal( in );

+        

+        assertEquals( sv, sv2 );

+   }

+

+

+    /**

+     * Test serialization of a StringValue which does not have a normalized value

+     */

+    @Test public void testNoNormalizedStringValueSerialization() throws NamingException, IOException, ClassNotFoundException

+    {

+        // First check with a value which will be normalized

+        ServerStringValue sv = new ServerStringValue( at, "test" );

+        

+        sv.normalize();

+        String normalized = sv.getNormalizedValue();

+        

+        assertEquals( "test", normalized );

+        assertEquals( "test", sv.get() );

+        

+        ByteArrayOutputStream baos = new ByteArrayOutputStream();

+        ObjectOutputStream out = new ObjectOutputStream( baos );

+        

+        sv.writeExternal( out );

+        

+        ObjectInputStream in = null;

+

+        byte[] data = baos.toByteArray();

+        

+        in = new ObjectInputStream( new ByteArrayInputStream( data ) );

+        

+        ServerStringValue sv2 = new ServerStringValue( at );

+        sv2.readExternal( in );

+        

+        assertEquals( sv, sv2 );

+   }

+

+

+    /**

+     * Test serialization of a null StringValue

+     */

+    @Test public void testNullStringValueSerialization() throws NamingException, IOException, ClassNotFoundException

+    {

+        // First check with a value which will be normalized

+        ServerStringValue sv = new ServerStringValue( at );

+        

+        sv.normalize();

+        String normalized = sv.getNormalizedValue();

+        

+        assertEquals( null, normalized );

+        assertEquals( null, sv.get() );

+        

+        ByteArrayOutputStream baos = new ByteArrayOutputStream();

+        ObjectOutputStream out = new ObjectOutputStream( baos );

+        

+        sv.writeExternal( out );

+        

+        ObjectInputStream in = null;

+

+        byte[] data = baos.toByteArray();

+        

+        in = new ObjectInputStream( new ByteArrayInputStream( data ) );

+        

+        ServerStringValue sv2 = new ServerStringValue( at );

+        sv2.readExternal( in );

+        

+        assertEquals( sv, sv2 );

+   }

+

+

+    /**

+     * Test serialization of an empty StringValue

+     */

+    @Test public void testEmptyStringValueSerialization() throws NamingException, IOException, ClassNotFoundException

+    {

+        // First check with a value which will be normalized

+        ServerStringValue sv = new ServerStringValue( at, "" );

+        

+        sv.normalize();

+        String normalized = sv.getNormalizedValue();

+        

+        assertEquals( "", normalized );

+        assertEquals( "", sv.get() );

+        

+        ByteArrayOutputStream baos = new ByteArrayOutputStream();

+        ObjectOutputStream out = new ObjectOutputStream( baos );

+        

+        sv.writeExternal( out );

+        

+        ObjectInputStream in = null;

+

+        byte[] data = baos.toByteArray();

+        

+        in = new ObjectInputStream( new ByteArrayInputStream( data ) );

+        

+        ServerStringValue sv2 = new ServerStringValue( at );

+        sv2.readExternal( in );

+        

+        assertEquals( sv, sv2 );

+   }

+}
\ No newline at end of file
diff --git a/old_trunk/core-integ/pom.xml b/old_trunk/core-integ/pom.xml
new file mode 100644
index 0000000..0ccc207
--- /dev/null
+++ b/old_trunk/core-integ/pom.xml
@@ -0,0 +1,136 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you 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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.directory.server</groupId>
+    <artifactId>apacheds-parent</artifactId>
+    <version>1.5.4-SNAPSHOT</version>
+  </parent>
+  <artifactId>apacheds-core-integ</artifactId>
+  <name>ApacheDS Core Integration</name>
+  <packaging>jar</packaging>  
+
+  <description>
+    Integration testing framework for Apache Directory Server.
+  </description>
+
+  <dependencies>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>provided</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-io</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-core</artifactId>
+      <version>${pom.version}</version>
+    </dependency>  
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-schema-extras</artifactId>
+      <version>${pom.version}</version>
+      <scope>test</scope>
+    </dependency>  
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-bootstrap-partition</artifactId>
+      <version>${pom.version}</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <profiles>
+    <profile>
+      <id>integration</id>
+      <activation>
+        <activeByDefault>true</activeByDefault>
+      </activation>
+      <build>
+        <plugins>
+          <plugin>
+            <artifactId>maven-surefire-plugin</artifactId>
+            <configuration>
+              <includes>
+                <include>**/*ISuite.java</include>
+                <include>**/*ITest.java</include>
+              </includes>
+            </configuration>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+    
+    <profile>
+      <id>quicktest</id>
+      <activation>
+        <property><name>quicktest</name></property>
+      </activation>
+      <build>
+        <plugins>
+          <plugin>
+            <artifactId>maven-surefire-plugin</artifactId>
+            <configuration>
+              <excludes>
+                <exclude>**/*PTest.java</exclude>
+                <exclude>**/*ITest.java</exclude>
+              </excludes>
+            </configuration>
+          </plugin>
+
+          <plugin>
+            <artifactId>maven-antrun-plugin</artifactId>
+            <executions>
+              <execution>
+                <phase>validate</phase>
+                <configuration>
+                  <tasks>
+                    <echo>
+=================================================================
+                   Q U I C K   T E S T S
+                   ---------------------
+                  
+WARNING: Long running integration tests have been disabled!
+=================================================================
+                    </echo>
+                  </tasks>
+                </configuration>
+                <goals>
+                  <goal>run</goal>
+                </goals>
+              </execution>
+            </executions>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+  </profiles>
+</project>
+
diff --git a/old_trunk/core-integ/src/main/java/org/apache/directory/server/core/integ/AnnotationUtils.java b/old_trunk/core-integ/src/main/java/org/apache/directory/server/core/integ/AnnotationUtils.java
new file mode 100644
index 0000000..c048216
--- /dev/null
+++ b/old_trunk/core-integ/src/main/java/org/apache/directory/server/core/integ/AnnotationUtils.java
@@ -0,0 +1,76 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements.  See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership.  The ASF licenses this file

+ * to you 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.

+ */

+package org.apache.directory.server.core.integ;

+

+

+import org.apache.directory.server.core.integ.annotations.*;

+

+

+/**

+ * Various utility methods for dealing with annotations all over.

+ *

+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>

+ * @version $Rev$, $Date$

+ */

+public class AnnotationUtils

+{

+    public static SetupMode getMode( Mode modeAnnotation, SetupMode defaultMode )

+    {

+        if ( modeAnnotation != null && modeAnnotation.value() != null )

+        {

+            return modeAnnotation.value();

+        }

+        else

+        {

+            return defaultMode;

+        }

+    }

+

+

+    public static DirectoryServiceFactory newFactory( Factory factoryAnnotation,

+                                                      DirectoryServiceFactory defaultFactory )

+    {

+        DirectoryServiceFactory factory = defaultFactory;

+

+        if ( factoryAnnotation != null )

+        {

+            try

+            {

+                factory = ( DirectoryServiceFactory ) factoryAnnotation.getClass().newInstance();

+            }

+            catch ( ClassCastException e )

+            {

+                throw new RuntimeException( "The specified factory '" +

+                        factoryAnnotation.getClass() + "' does not implement DirectoryServiceFactory", e );

+            }

+            catch ( InstantiationException e )

+            {

+                throw new RuntimeException( "The specified factory '" +

+                        factoryAnnotation.getClass() + "' does not contain a default constructor", e );

+            }

+            catch ( IllegalAccessException e )

+            {

+                throw new RuntimeException( "The specified factory '" +

+                        factoryAnnotation.getClass() + "' does not contain a public default constructor", e );

+            }

+        }

+

+        return factory;

+    }

+}

diff --git a/old_trunk/core-integ/src/main/java/org/apache/directory/server/core/integ/CiRunner.java b/old_trunk/core-integ/src/main/java/org/apache/directory/server/core/integ/CiRunner.java
new file mode 100644
index 0000000..0109171
--- /dev/null
+++ b/old_trunk/core-integ/src/main/java/org/apache/directory/server/core/integ/CiRunner.java
@@ -0,0 +1,149 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements.  See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership.  The ASF licenses this file

+ * to you 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.

+ */

+package org.apache.directory.server.core.integ;

+

+

+import java.io.IOException;

+import java.lang.reflect.Method;

+

+import javax.naming.NamingException;

+

+import static org.apache.directory.server.core.integ.state.TestServiceContext.cleanup;

+import static org.apache.directory.server.core.integ.state.TestServiceContext.destroy;

+import static org.apache.directory.server.core.integ.state.TestServiceContext.shutdown;

+import static org.apache.directory.server.core.integ.state.TestServiceContext.test;

+import org.junit.internal.runners.InitializationError;

+import org.junit.internal.runners.JUnit4ClassRunner;

+import org.junit.runner.Description;

+import org.junit.runner.notification.Failure;

+import org.junit.runner.notification.RunNotifier;

+import org.slf4j.Logger;

+import org.slf4j.LoggerFactory;

+

+

+/**

+ * A test runner for ApacheDS Core integration tests.

+ *

+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>

+ * @version $Rev$, $Date$

+ */

+public class CiRunner extends JUnit4ClassRunner

+{

+    private static final Logger LOG = LoggerFactory.getLogger( CiRunner.class );

+    private CiSuite suite;

+    private InheritableSettings settings;

+

+

+    public CiRunner( Class<?> clazz ) throws InitializationError

+    {

+        super( clazz );

+    }

+

+

+    protected InheritableSettings getSettings()

+    {

+        if ( settings != null )

+        {

+            return settings;

+        }

+

+        if ( suite == null )

+        {

+            settings = new InheritableSettings( getDescription(), null );

+        }

+

+        return settings;

+    }

+

+

+    @Override

+    public void run( final RunNotifier notifier )

+    {

+        super.run( notifier );

+        Level cleanupLevel = getSettings().getCleanupLevel();

+        

+        if ( cleanupLevel == Level.CLASS )

+        {

+            try

+            {

+                shutdown();

+                cleanup();

+                destroy();

+            }

+            catch ( NamingException e )

+            {

+                LOG.error( "Encountered exception while trying to cleanup after test class: "

+                        + this.getDescription().getDisplayName(), e );

+                notifier.fireTestFailure( new Failure( getDescription(), e ) );

+            }

+            catch ( IOException ioe )

+            {

+                LOG.error( "Encountered exception while trying to cleanup after test class: "

+                        + this.getDescription().getDisplayName(), ioe );

+                notifier.fireTestFailure( new Failure( getDescription(), ioe ) );

+            }

+        }

+    }

+

+

+    @Override

+    protected void invokeTestMethod( Method method, final RunNotifier notifier )

+    {

+        LOG.debug( "About to invoke test method {}", method.getName() );

+        Description description = methodDescription( method );

+        test( getTestClass(), wrapMethod( method ), notifier, new InheritableSettings( description, getSettings() ) );

+

+        Level cleanupLevel = getSettings().getCleanupLevel();

+        

+        if ( cleanupLevel == Level.METHOD )

+        {

+            try

+            {

+                shutdown();

+                cleanup();

+                destroy();

+            }

+            catch ( NamingException ne )

+            {

+                LOG.error( "Encountered exception while trying to cleanup after test class: "

+                        + this.getDescription().getDisplayName(), ne );

+                notifier.fireTestFailure( new Failure( getDescription(), ne ) );

+            }

+            catch ( IOException ioe )

+            {

+                LOG.error( "Encountered exception while trying to cleanup after test class: "

+                        + this.getDescription().getDisplayName(), ioe );

+                notifier.fireTestFailure( new Failure( getDescription(), ioe ) );

+            }

+        }

+    }

+

+

+    public void setSuite( CiSuite suite )

+    {

+        this.suite = suite;

+        this.settings = new InheritableSettings( getDescription(), suite.getSettings() );

+    }

+

+

+    public CiSuite getSuite()

+    {

+        return suite;

+    }

+}

diff --git a/old_trunk/core-integ/src/main/java/org/apache/directory/server/core/integ/CiSuite.java b/old_trunk/core-integ/src/main/java/org/apache/directory/server/core/integ/CiSuite.java
new file mode 100644
index 0000000..d41279f
--- /dev/null
+++ b/old_trunk/core-integ/src/main/java/org/apache/directory/server/core/integ/CiSuite.java
@@ -0,0 +1,141 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements.  See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership.  The ASF licenses this file

+ * to you 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.

+ */

+package org.apache.directory.server.core.integ;

+

+

+import java.io.IOException;

+import java.util.List;

+import javax.naming.NamingException;

+

+import static org.apache.directory.server.core.integ.state.TestServiceContext.cleanup;

+import static org.apache.directory.server.core.integ.state.TestServiceContext.destroy;

+import static org.apache.directory.server.core.integ.state.TestServiceContext.shutdown;

+import org.junit.internal.requests.IgnoredClassRunner;

+import org.junit.internal.runners.InitializationError;

+import org.junit.runner.Runner;

+import org.junit.runner.notification.Failure;

+import org.junit.runner.notification.RunNotifier;

+import org.junit.runners.Suite;

+

+

+/**

+ * A replacement for standard JUnit 4 suites. Note that this test suite

+ * will not startup an DirectoryService instance but will clean it up if

+ * one remains.

+ *

+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>

+ * @version $Rev$, $Date$

+ */

+public class CiSuite extends Suite

+{

+    private InheritableSettings settings;

+

+

+    public CiSuite( Class<?> clazz ) throws InitializationError

+    {

+        super( clazz );

+        settings = new InheritableSettings( getDescription() );

+    }

+

+

+    public void addAll( List<? extends Runner> runners )

+    {

+        for ( Runner runner : getRunners() )

+        {

+            if ( runner instanceof CiRunner )

+            {

+                CiRunner cir = ( CiRunner) runner;

+                cir.setSuite( this );

+            }

+            else if ( runner instanceof IgnoredClassRunner )

+            {

+                // allow this one

+            }

+            else

+            {

+                throw new IllegalArgumentException( String.format( "Unexpected runner type \"%s\".  " +

+                        "Test classes within CiSuites must use CiRunners.", runner ) );

+            }

+        }

+

+        super.addAll( runners );

+    }

+

+

+    public void add( Runner runner )

+    {

+        if ( runner instanceof CiRunner )

+        {

+            CiRunner cir = ( CiRunner) runner;

+            cir.setSuite( this );

+            super.add( runner );

+        }

+        else if ( runner instanceof IgnoredClassRunner )

+        {

+            // allow this one

+        }

+        else

+        {

+            throw new IllegalArgumentException( String.format( "Unexpected runner type \"%s\".  " +

+                    "Test classes within CiSuites must use CiRunners.", runner ) );

+        }

+    }

+

+

+    @Override

+    public void run( final RunNotifier notifier )

+    {

+        super.run( notifier );

+

+        /*

+         * For any service scope other than test system scope, we must have to

+         * shutdown the sevice and cleanup the working directory.  Failures to

+         * do this without exception shows that something is wrong with the

+         * server and so the entire test should be marked as failed.  So we

+         * presume that tests have failed in the suite if the fixture is in an

+         * inconsistent state.  Who knows if this inconsistent state of the

+         * service could have made it so false results were acquired while

+         * running tests.

+         */

+

+        if ( settings.getCleanupLevel() != Level.SYSTEM )

+        {

+            try

+            {

+                shutdown();

+                cleanup();

+                destroy();

+            }

+            catch ( NamingException e )

+            {

+                notifier.fireTestFailure( new Failure( getDescription(), e ) );

+            }

+            catch ( IOException e )

+            {

+                notifier.fireTestFailure( new Failure( getDescription(), e ) );

+            }

+        }

+    }

+

+

+    public InheritableSettings getSettings()

+    {

+        return settings;

+    }

+}

diff --git a/old_trunk/core-integ/src/main/java/org/apache/directory/server/core/integ/DirectoryServiceFactory.java b/old_trunk/core-integ/src/main/java/org/apache/directory/server/core/integ/DirectoryServiceFactory.java
new file mode 100644
index 0000000..7b4833e
--- /dev/null
+++ b/old_trunk/core-integ/src/main/java/org/apache/directory/server/core/integ/DirectoryServiceFactory.java
@@ -0,0 +1,59 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements.  See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership.  The ASF licenses this file

+ * to you 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.

+ */

+package org.apache.directory.server.core.integ;

+

+

+import javax.naming.NamingException;

+

+import org.apache.directory.server.core.DefaultDirectoryService;

+import org.apache.directory.server.core.DirectoryService;

+

+

+/**

+ * A factory used to generate differently configured DirectoryService objects.

+ * Since the DirectoryService itself is what is configured then a factory for

+ * these objects acts as a configurator.  Tests can provide different factory

+ * methods to be used.

+ *

+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>

+ * @version $Rev$, $Date$

+ */

+public interface DirectoryServiceFactory

+{

+    /**

+     * The default factory returns stock instances of a directory

+     * service with smart defaults

+     */

+    DirectoryServiceFactory DEFAULT = new DirectoryServiceFactory()

+    {

+        public DirectoryService newInstance()

+        {

+            DirectoryService service = new DefaultDirectoryService();

+            service.getChangeLog().setEnabled( true );

+

+            // change the working directory to something that is unique

+            // on the system and somewhere either under target directory

+            // or somewhere in a temp area of the machine.

+

+            return service;

+        }

+    };

+

+    DirectoryService newInstance() throws NamingException;

+}

diff --git a/old_trunk/core-integ/src/main/java/org/apache/directory/server/core/integ/InheritableSettings.java b/old_trunk/core-integ/src/main/java/org/apache/directory/server/core/integ/InheritableSettings.java
new file mode 100644
index 0000000..4d203b0
--- /dev/null
+++ b/old_trunk/core-integ/src/main/java/org/apache/directory/server/core/integ/InheritableSettings.java
@@ -0,0 +1,271 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements.  See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership.  The ASF licenses this file

+ * to you 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.

+ */

+package org.apache.directory.server.core.integ;

+

+

+import java.util.ArrayList;

+import java.util.Arrays;

+import java.util.List;

+

+import org.apache.directory.server.core.integ.annotations.ApplyLdifFiles;

+import org.apache.directory.server.core.integ.annotations.ApplyLdifs;

+import org.apache.directory.server.core.integ.annotations.CleanupLevel;

+import org.apache.directory.server.core.integ.annotations.Factory;

+import org.apache.directory.server.core.integ.annotations.Mode;

+

+

+import org.junit.runner.Description;

+

+

+/**

+ * Inheritable settings of a test suite, test class, or test method.

+ *

+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>

+ * @version $Rev$, $Date$

+ */

+public class InheritableSettings

+{

+    /** the default setup mode to use if inheritance leads to null value */

+    public static final SetupMode DEFAULT_MODE = SetupMode.ROLLBACK;

+    

+    /** the default factory to use if inheritance leads to a null value */

+    public static final DirectoryServiceFactory DEFAULT_FACTORY = DirectoryServiceFactory.DEFAULT;

+

+    /** parent settings to inherit from */

+    private final InheritableSettings parent;

+    

+    /** JUnit test description containing all annotations queried */

+    private final Description description;

+    

+    /** default level at which a service is cleaned up */

+    private static final Level DEFAULT_CLEANUP_LEVEL = Level.SUITE;

+

+

+    /**

+     * Creates a new InheritableSettings instance for test suites description.

+     *

+     * @param description JUnit description for the suite

+     */

+    public InheritableSettings( Description description )

+    {

+        this.description = description;

+        this.parent = null;

+    }

+

+

+    /**

+     * Creates a new InheritableSettings instance based on a test object's

+     * description and it's parent's settings.

+     *

+     * @param description JUnit description for the test object

+     * @param parent the parent settings or null if the test entity is a suite

+     */

+    public InheritableSettings( Description description, InheritableSettings parent )

+    {

+        this.description = description;

+        this.parent = parent;

+

+        if ( description.isSuite() && ! isSuiteLevel() )

+        {

+            throw new IllegalStateException( String.format( "The parent must be null for %s suite",

+                    description.getDisplayName() ) );

+        }

+    }

+

+

+    /**

+     * @return the description of the running test

+     */

+    public Description getDescription()

+    {

+        return description;

+    }

+

+

+    /**

+     * @return the settings inherited from the parent

+     */

+    public InheritableSettings getParent()

+    {

+        return parent;

+    }

+

+

+    /**

+     * @return <code>true</code> if we are at the suite level

+     */

+    public boolean isSuiteLevel()

+    {

+        return parent == null;

+    }

+

+

+    /**

+     * @return <code>true</code> if we are at the class level

+     */

+    public boolean isClassLevel()

+    {

+        return ( parent != null ) && ( parent.getParent() == null );

+    }

+

+

+    /**

+     * @return <code>true</code> if we are at the method level

+     */

+    public boolean isMethodLevel()

+    {

+        return ( parent != null ) && ( parent.getParent() != null );

+    }

+

+

+    /**

+     * @return the test mode. Default to ROLLBACK

+     */

+    public SetupMode getMode()

+    {

+        SetupMode parentMode = DEFAULT_MODE;

+        

+        if ( parent != null )

+        {

+            parentMode = parent.getMode();

+        }

+

+        // Get the @Mode annotation

+        Mode annotation = description.getAnnotation( Mode.class );

+        

+        if ( annotation == null )

+        {

+            return parentMode;

+        }

+        else

+        {

+            return annotation.value();

+        }

+    }

+

+

+    /**

+     * @return the DirectoryService factory 

+     * @throws IllegalAccessException if we can't access the factory

+     * @throws InstantiationException if the DirectoryService can't be instanciated

+     */

+    public DirectoryServiceFactory getFactory() throws IllegalAccessException, InstantiationException

+    {

+        DirectoryServiceFactory parentFactory = DEFAULT_FACTORY;

+        

+        if ( parent != null )

+        {

+            parentFactory = parent.getFactory();

+        }

+

+        Factory annotation = description.getAnnotation( Factory.class );

+        

+        if ( annotation == null )

+        {

+            return parentFactory;

+        }

+        else

+        {

+            return ( DirectoryServiceFactory ) annotation.value().newInstance();

+        }

+    }

+

+

+    /**

+     * Get a list of entries from a LDIF declared as an annotation

+     *

+     * @param ldifs the list of LDIFs we want to feed  

+     * @return a list of entries described using a LDIF format

+     */

+    public List<String> getLdifs( List<String> ldifs )

+    {

+        if ( ldifs == null )

+        {

+            ldifs = new ArrayList<String>();

+        }

+

+        if ( parent != null )

+        {

+            parent.getLdifs( ldifs );

+        }

+

+        ApplyLdifs annotation = description.getAnnotation( ApplyLdifs.class );

+        

+        if ( ( annotation != null ) && ( annotation.value() != null ) )

+        {

+            ldifs.addAll( Arrays.asList( annotation.value() ) );

+        }

+

+        return ldifs;

+    }

+

+

+    /**

+     * Get a list of files containing entries described using the LDIF format.

+     *

+     * @param ldifFiles the list to feed

+     * @return a list of files containing some LDIF data

+     */

+    public List<String> getLdifFiles( List<String> ldifFiles )

+    {

+        if ( ldifFiles == null )

+        {

+            ldifFiles = new ArrayList<String>();

+        }

+

+        if ( parent != null )

+        {

+            parent.getLdifFiles( ldifFiles );

+        }

+

+        ApplyLdifFiles annotation = description.getAnnotation( ApplyLdifFiles.class );

+        

+        if ( annotation != null && annotation.value() != null )

+        {

+            ldifFiles.addAll( Arrays.asList( annotation.value() ) );

+        }

+

+        return ldifFiles;

+    }

+

+

+    /**

+     * @return teh cleanup level. Defualt to SUITE

+     */

+    public Level getCleanupLevel()

+    {

+        Level parentCleanupLevel = DEFAULT_CLEANUP_LEVEL;

+        

+        if ( parent != null )

+        {

+            parentCleanupLevel = parent.getCleanupLevel();

+        }

+

+        CleanupLevel annotation = description.getAnnotation( CleanupLevel.class );

+        

+        if ( annotation == null )

+        {

+            return parentCleanupLevel;

+        }

+        else

+        {

+            return annotation.value();

+        }

+    }

+}

diff --git a/old_trunk/core-integ/src/main/java/org/apache/directory/server/core/integ/IntegrationUtils.java b/old_trunk/core-integ/src/main/java/org/apache/directory/server/core/integ/IntegrationUtils.java
new file mode 100644
index 0000000..ef06626
--- /dev/null
+++ b/old_trunk/core-integ/src/main/java/org/apache/directory/server/core/integ/IntegrationUtils.java
@@ -0,0 +1,240 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements.  See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership.  The ASF licenses this file

+ * to you 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.

+ */

+package org.apache.directory.server.core.integ;

+

+

+import java.io.File;

+import java.io.IOException;

+import java.util.List;

+import javax.naming.InvalidNameException;

+import javax.naming.NamingException;

+import javax.naming.ldap.LdapContext;

+

+import org.apache.commons.io.FileUtils;

+import org.apache.directory.server.constants.ServerDNConstants;

+import org.apache.directory.server.core.DirectoryService;

+import org.apache.directory.server.core.authn.LdapPrincipal;

+import org.apache.directory.shared.ldap.constants.AuthenticationLevel;

+import org.apache.directory.shared.ldap.ldif.ChangeType;

+import org.apache.directory.shared.ldap.ldif.LdifEntry;

+import org.apache.directory.shared.ldap.ldif.LdifReader;

+import org.apache.directory.shared.ldap.message.AttributeImpl;

+import org.apache.directory.shared.ldap.name.LdapDN;

+import org.slf4j.Logger;

+import org.slf4j.LoggerFactory;

+

+

+/**

+ * Integration test utility methods.

+ *

+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>

+ * @version $Rev$, $Date$

+ */

+public class IntegrationUtils

+{

+    private static final Logger LOG = LoggerFactory.getLogger( IntegrationUtils.class );

+

+

+    /**

+     * Deletes the working directory.

+     *

+     * @param wkdir the working directory to delete

+     * @throws IOException if the working directory cannot be deleted

+     */

+    public static void doDelete( File wkdir ) throws IOException

+    {

+        if ( wkdir.exists() )

+        {

+            try

+            {

+                FileUtils.deleteDirectory( wkdir );

+            }

+            catch ( IOException e )

+            {

+                LOG.error( "Failed to delete the working directory.", e );

+            }

+        }

+        if ( wkdir.exists() )

+        {

+            throw new IOException( "Failed to delete: " + wkdir );

+        }

+    }

+

+

+    /**

+     * Inject an ldif String into the server. DN must be relative to the

+     * root.

+     *

+     * @param service the directory service to use 

+     * @param ldif the ldif containing entries to add to the server.

+     * @throws NamingException if there is a problem adding the entries from the LDIF

+     */

+    public static void injectEntries( DirectoryService service, String ldif ) throws NamingException

+    {

+        LdapContext rootDSE = getRootContext( service );

+        LdifReader reader = new LdifReader();

+        List<LdifEntry> entries = reader.parseLdif( ldif );

+

+        for ( LdifEntry entry : entries )

+        {

+            rootDSE.createSubcontext( new LdapDN( entry.getDn() ), entry.getAttributes() );

+        }

+    }

+

+

+    public static LdifEntry getUserAddLdif() throws InvalidNameException

+    {

+        return getUserAddLdif( "uid=akarasulu,ou=users,ou=system", "test".getBytes(), "Alex Karasulu", "Karasulu" );

+    }

+

+

+    public static LdapContext getContext( String principalDn, DirectoryService service, String dn )

+            throws NamingException

+    {

+        if ( principalDn == null )

+        {

+            principalDn = "";

+        }

+

+        LdapDN userDn = new LdapDN( principalDn );

+        userDn.normalize( service.getRegistries().getAttributeTypeRegistry().getNormalizerMapping() );

+        LdapPrincipal principal = new LdapPrincipal( userDn, AuthenticationLevel.SIMPLE );

+

+        if ( dn == null )

+        {

+            dn = "";

+        }

+

+        return service.getJndiContext( principal, dn );

+    }

+

+

+    public static LdapContext getSystemContext( DirectoryService service ) throws NamingException

+    {

+        return getContext( ServerDNConstants.ADMIN_SYSTEM_DN, service, ServerDNConstants.SYSTEM_DN );

+    }

+

+

+    public static LdapContext getSchemaContext( DirectoryService service ) throws NamingException

+    {

+        return getContext( ServerDNConstants.ADMIN_SYSTEM_DN, service, ServerDNConstants.OU_SCHEMA_DN );

+    }

+

+

+    public static LdapContext getRootContext( DirectoryService service ) throws NamingException

+    {

+        return getContext( ServerDNConstants.ADMIN_SYSTEM_DN, service, "" );

+    }

+

+

+    public static void apply( LdapContext root, LdifEntry entry ) throws NamingException

+    {

+        LdapDN dn = new LdapDN( entry.getDn() );

+

+        switch( entry.getChangeType().getChangeType() )

+        {

+            case( ChangeType.ADD_ORDINAL ):

+                root.createSubcontext( dn, entry.getAttributes() );

+                break;

+            case( ChangeType.DELETE_ORDINAL ):

+                root.destroySubcontext( entry.getDn() );

+                break;

+            case( ChangeType.MODDN_ORDINAL ):

+                LdapDN target = new LdapDN( entry.getNewSuperior() );

+                if ( entry.getNewRdn() != null )

+                {

+                    target.add( entry.getNewRdn() );

+                }

+                else

+                {

+                    target.add( dn.getRdn().toString() );

+                }

+

+                if ( entry.isDeleteOldRdn() )

+                {

+                    root.addToEnvironment( "java.naming.ldap.deleteRDN", "true" );

+                }

+                else

+                {

+                    root.addToEnvironment( "java.naming.ldap.deleteRDN", "false" );

+                }

+

+                root.rename( dn, target );

+                break;

+            case( ChangeType.MODRDN_ORDINAL ):

+                target = ( LdapDN ) dn.clone();

+                target.remove( dn.size() - 1 );

+                target.add( entry.getNewRdn() );

+

+                if ( entry.isDeleteOldRdn() )

+                {

+                    root.addToEnvironment( "java.naming.ldap.deleteRDN", "true" );

+                }

+                else

+                {

+                    root.addToEnvironment( "java.naming.ldap.deleteRDN", "false" );

+                }

+

+                root.rename( dn, target );

+                break;

+            case( ChangeType.MODIFY_ORDINAL ):

+                root.modifyAttributes( dn, entry.getModificationItemsArray() );

+                break;

+

+            default:

+                throw new IllegalStateException( "Unidentified change type value: " + entry.getChangeType() );

+        }

+    }

+

+

+    public static LdifEntry getUserAddLdif( String dnstr, byte[] password, String cn, String sn )

+            throws InvalidNameException

+    {

+        LdapDN dn = new LdapDN( dnstr );

+        LdifEntry ldif = new LdifEntry();

+        ldif.setDn( dnstr );

+        ldif.setChangeType( ChangeType.Add );

+

+        AttributeImpl attr = new AttributeImpl( "objectClass", "top" );

+        attr.add( "person" );

+        attr.add( "organizationalPerson" );

+        attr.add( "inetOrgPerson" );

+        ldif.addAttribute( attr );

+

+        attr = new AttributeImpl( "ou", "Engineering" );

+        attr.add( "People" );

+        ldif.addAttribute( attr );

+

+        String uid = ( String ) dn.getRdn().getValue();

+        ldif.putAttribute( "uid", uid );

+

+        ldif.putAttribute( "l", "Bogusville" );

+        ldif.putAttribute( "cn", cn );

+        ldif.putAttribute( "sn", sn );

+        ldif.putAttribute( "mail", uid + "@apache.org" );

+        ldif.putAttribute( "telephoneNumber", "+1 408 555 4798" );

+        ldif.putAttribute( "facsimileTelephoneNumber", "+1 408 555 9751" );

+        ldif.putAttribute( "roomnumber", "4612" );

+        ldif.putAttribute( "userPassword", password );

+

+        String givenName = cn.split( " " )[0];

+        ldif.putAttribute( "givenName", givenName );

+        return ldif;

+    }

+}

diff --git a/old_trunk/core-integ/src/main/java/org/apache/directory/server/core/integ/Level.java b/old_trunk/core-integ/src/main/java/org/apache/directory/server/core/integ/Level.java
new file mode 100644
index 0000000..03558e3
--- /dev/null
+++ b/old_trunk/core-integ/src/main/java/org/apache/directory/server/core/integ/Level.java
@@ -0,0 +1,64 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements.  See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership.  The ASF licenses this file

+ * to you 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.

+ */

+package org.apache.directory.server.core.integ;

+

+

+/**

+ * A scope or level of testing.  There are four levels:

+ *

+ * <ul>

+ *   <li>

+ *     <b>system level</b>: the level external to the testing framework</li>

+ *   </li>

+ *   <li>

+ *     <b>suite level</b>: the level representing test suite scope</li>

+ *   </li>

+ *   <li>

+ *     <b>class level</b>: the level representing test class scope</li>

+ *   </li>

+ *   <li>

+ *     <b>method level</b>: the lowest level representing test method scope</li>

+ *   </li>

+ * </ul>

+ *

+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>

+ * @version $Rev$, $Date$

+ */

+public enum Level

+{

+    SUITE( 0, "test suite level" ),

+    CLASS( 1, "test class level" ),

+    SYSTEM( 2, "test system level" ),

+    METHOD( 3, "test method level" );

+

+    public final int ordinal;

+    public final String description;

+

+    public static final int SUITE_ORDINAL = 0;

+    public static final int CLASS_ORDINAL = 1;

+    public static final int SYSTEM_ORDINAL = 2;

+    public static final int METHOD_ORDINAL = 3;

+

+

+    Level( int ordinal, String description )

+    {

+        this.ordinal = ordinal;

+        this.description = description;

+    }

+}

diff --git a/old_trunk/core-integ/src/main/java/org/apache/directory/server/core/integ/SetupMode.java b/old_trunk/core-integ/src/main/java/org/apache/directory/server/core/integ/SetupMode.java
new file mode 100644
index 0000000..d2a59ed
--- /dev/null
+++ b/old_trunk/core-integ/src/main/java/org/apache/directory/server/core/integ/SetupMode.java
@@ -0,0 +1,125 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements.  See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership.  The ASF licenses this file

+ * to you 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.

+ */

+package org.apache.directory.server.core.integ;

+

+

+/**

+ * Different modes of conducting core tests.

+ *

+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>

+ * @version $Rev$, $Date$

+ */

+public enum SetupMode

+{

+    /*

+

+      MATRIX FOR MODE ACTIONS BASED ON SERVER STATE

+    

+                | NOSERVICE | PRISTINE  | RESTART  | ROLLBACK | CUMULATIVE

+                +===========+===========+==========+==========+===========

+                |           |           |          |          |

+       RUNNING  |  NOTHING  | SHUTDOWN  | SHUTDOWN |  REVERT  |  NOTHING

+       STOPPED  |  NOTHING  | CLEANUP   | STARTUP  |  CLEANUP |  RESTART

+       MISSING  |  NOTHING  | CREATE    | CREATE   |  CREATE  |  CREATE

+

+    */

+

+

+    /**

+     * If a service is running this mode will shutdown the service, destroy

+     * it's working directory, null it out and start all over again with a

+     * new service object to start it up fresh.  If no service is running,

+     * yet a valid handle to a stopped service exists, this handle is used

+     * to destroy the working directory then the handle is nulled out.

+     * Whether or not a valid service exists a new one is created, and

+     * started up. 

+     */

+    PRISTINE( 0, "PRISTINE: Fresh test with full working directory cleanout." ),

+    /**

+     * If a service is running this mode will shutdown the service, WITHOUT

+     * destroying it's working directory, so changes made in tests are or

+     * should be persistant. The same service object is restarted without

+     * creating a new one.  If the service exists yet is found to have been

+     * shutdown it is restarted.  If no service is available, one is created

+     * and started up.

+     */

+    RESTART( 1, "RESTART: Working directories are not cleaned out but the core is restarted." ),

+    /**

+     * If a service is running this mode will NOT shutdown the service,

+     * instead the service's state will be reverted to it's previous state

+     * before the last test which operated on it.  So changes are not

+     * persisted across tests.  If the service exists yet has been shutdown

+     * the working directory is cleaned out and the service is started up.

+     * We must destroy working directories since reverts are not possible

+     * across shutdowns at this point in time (change log is not persistent).

+     */

+    ROLLBACK( 2, "ROLLBACK: The service is not stopped, it's state is restored to the original startup state." ),

+    /**

+     * If a service is running it is used as is.  Changes across tests have

+     * no isolation.  If the service has been stopped it is simply restarted.

+     * If the service does not exists it is created then started.  There is

+     * no attempt to destroy existing working directories if any at all exist.

+     */

+    CUMULATIVE( 3, "CUMULATIVE: Nothing is done to the service between tests so changes accumulate." ),

+    /**

+     * Does nothing at all.  Does not care if service is running or if it

+     * exists.  This is the default.  Really useful with suites which you

+     * may not want to do anything with.  Otherwise for all other modes a

+     * suite will start up a server before all runs and shut it down after

+     * all runs.

+     */

+    NOSERVICE( 4, "NOSERVICE: No service is required at all." );

+

+    public static final int PRISTINE_ORDINAL = 0;

+    public static final int RESTART_ORDINAL = 1;

+    public static final int ROLLBACK_ORDINAL = 2;

+    public static final int CUMULATIVE_ORDINAL = 3;

+    public static final int NOSERVICE_ORDINAL = 4;

+

+

+    public final int ordinal;

+    public final String description;

+

+

+    private SetupMode( int ordinal, String description )

+    {

+        this.ordinal = ordinal;

+        this.description = description;

+    }

+

+

+    public boolean isStartedDirtyTestable()

+    {

+        switch( ordinal )

+        {

+            case( PRISTINE_ORDINAL ):

+                return false;

+            case( ROLLBACK_ORDINAL ):

+                return false;

+            case( NOSERVICE_ORDINAL ):

+                return true;

+            case( RESTART_ORDINAL ):

+                return true;

+            case( CUMULATIVE_ORDINAL ):

+                return true;

+            default:

+                throw new IllegalStateException( "Unidentified ordinal value: " + ordinal );

+        }

+    }

+}

diff --git a/old_trunk/core-integ/src/main/java/org/apache/directory/server/core/integ/annotations/ApplyLdifFiles.java b/old_trunk/core-integ/src/main/java/org/apache/directory/server/core/integ/annotations/ApplyLdifFiles.java
new file mode 100644
index 0000000..0637b6b
--- /dev/null
+++ b/old_trunk/core-integ/src/main/java/org/apache/directory/server/core/integ/annotations/ApplyLdifFiles.java
@@ -0,0 +1,46 @@
+/*

+ *  Licensed to the Apache Software Foundation (ASF) under one

+ *  or more contributor license agreements.  See the NOTICE file

+ *  distributed with this work for additional information

+ *  regarding copyright ownership.  The ASF licenses this file

+ *  to you 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.

+ *

+ */

+package org.apache.directory.server.core.integ.annotations;

+

+import java.lang.annotation.Documented;

+import java.lang.annotation.ElementType;

+import java.lang.annotation.Inherited;

+import java.lang.annotation.Retention;

+import java.lang.annotation.RetentionPolicy;

+import java.lang.annotation.Target;

+

+

+

+

+/**

+ * A annotation used to specify an array of ldif files to load and apply

+ * on the instance used for integration testing.

+ *

+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>

+ * @version $Rev$, $Date$

+ */

+@Documented

+@Inherited

+@Retention ( RetentionPolicy.RUNTIME )

+@Target ( { ElementType.METHOD, ElementType.TYPE } )

+public @interface ApplyLdifFiles

+{

+     String[] value();

+}
\ No newline at end of file
diff --git a/old_trunk/core-integ/src/main/java/org/apache/directory/server/core/integ/annotations/ApplyLdifs.java b/old_trunk/core-integ/src/main/java/org/apache/directory/server/core/integ/annotations/ApplyLdifs.java
new file mode 100644
index 0000000..e79ee61
--- /dev/null
+++ b/old_trunk/core-integ/src/main/java/org/apache/directory/server/core/integ/annotations/ApplyLdifs.java
@@ -0,0 +1,46 @@
+/*

+ *  Licensed to the Apache Software Foundation (ASF) under one

+ *  or more contributor license agreements.  See the NOTICE file

+ *  distributed with this work for additional information

+ *  regarding copyright ownership.  The ASF licenses this file

+ *  to you 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.

+ *

+ */

+package org.apache.directory.server.core.integ.annotations;

+

+import java.lang.annotation.Documented;

+import java.lang.annotation.ElementType;

+import java.lang.annotation.Inherited;

+import java.lang.annotation.Retention;

+import java.lang.annotation.RetentionPolicy;

+import java.lang.annotation.Target;

+

+

+

+

+/**

+ * A annotation used to specify an sequence of LDIF's to be applied to

+ * the instance for integration testing.

+ *

+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>

+ * @version $Rev$, $Date$

+ */

+@Documented

+@Inherited

+@Retention ( RetentionPolicy.RUNTIME )

+@Target ( { ElementType.METHOD, ElementType.TYPE } )

+public @interface ApplyLdifs

+{

+     String[] value();

+}
\ No newline at end of file
diff --git a/old_trunk/core-integ/src/main/java/org/apache/directory/server/core/integ/annotations/CleanupLevel.java b/old_trunk/core-integ/src/main/java/org/apache/directory/server/core/integ/annotations/CleanupLevel.java
new file mode 100644
index 0000000..c416830
--- /dev/null
+++ b/old_trunk/core-integ/src/main/java/org/apache/directory/server/core/integ/annotations/CleanupLevel.java
@@ -0,0 +1,44 @@
+/*

+ *  Licensed to the Apache Software Foundation (ASF) under one

+ *  or more contributor license agreements.  See the NOTICE file

+ *  distributed with this work for additional information

+ *  regarding copyright ownership.  The ASF licenses this file

+ *  to you 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.

+ *

+ */

+package org.apache.directory.server.core.integ.annotations;

+

+import java.lang.annotation.Documented;

+import java.lang.annotation.ElementType;

+import java.lang.annotation.Inherited;

+import java.lang.annotation.Retention;

+import java.lang.annotation.RetentionPolicy;

+import java.lang.annotation.Target;

+

+import org.apache.directory.server.core.integ.Level;

+

+/**

+ * An annotation to control the mode used to setup for a test.

+ *

+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>

+ * @version $Rev$, $Date$

+ */

+@Documented

+@Inherited

+@Retention ( RetentionPolicy.RUNTIME )

+@Target ( { ElementType.METHOD, ElementType.TYPE } )

+public @interface CleanupLevel

+{

+     Level value();

+}
\ No newline at end of file
diff --git a/old_trunk/core-integ/src/main/java/org/apache/directory/server/core/integ/annotations/Factory.java b/old_trunk/core-integ/src/main/java/org/apache/directory/server/core/integ/annotations/Factory.java
new file mode 100644
index 0000000..22a3913
--- /dev/null
+++ b/old_trunk/core-integ/src/main/java/org/apache/directory/server/core/integ/annotations/Factory.java
@@ -0,0 +1,46 @@
+/*

+ *  Licensed to the Apache Software Foundation (ASF) under one

+ *  or more contributor license agreements.  See the NOTICE file

+ *  distributed with this work for additional information

+ *  regarding copyright ownership.  The ASF licenses this file

+ *  to you 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.

+ *

+ */

+package org.apache.directory.server.core.integ.annotations;

+

+import java.lang.annotation.Documented;

+import java.lang.annotation.ElementType;

+import java.lang.annotation.Inherited;

+import java.lang.annotation.Retention;

+import java.lang.annotation.RetentionPolicy;

+import java.lang.annotation.Target;

+

+

+

+

+/**

+ * A annotation used to specify a factory which builds a DirectoryService

+ * instance for integration testing.

+ *

+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>

+ * @version $Rev$, $Date$

+ */

+@Documented

+@Inherited

+@Retention ( RetentionPolicy.RUNTIME )

+@Target ( { ElementType.METHOD, ElementType.TYPE } )

+public @interface Factory

+{

+     Class value();

+}

diff --git a/old_trunk/core-integ/src/main/java/org/apache/directory/server/core/integ/annotations/Mode.java b/old_trunk/core-integ/src/main/java/org/apache/directory/server/core/integ/annotations/Mode.java
new file mode 100644
index 0000000..86ebe16
--- /dev/null
+++ b/old_trunk/core-integ/src/main/java/org/apache/directory/server/core/integ/annotations/Mode.java
@@ -0,0 +1,46 @@
+/*

+ *  Licensed to the Apache Software Foundation (ASF) under one

+ *  or more contributor license agreements.  See the NOTICE file

+ *  distributed with this work for additional information

+ *  regarding copyright ownership.  The ASF licenses this file

+ *  to you 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.

+ *

+ */

+package org.apache.directory.server.core.integ.annotations;

+

+import java.lang.annotation.Documented;

+import java.lang.annotation.ElementType;

+import java.lang.annotation.Inherited;

+import java.lang.annotation.Retention;

+import java.lang.annotation.RetentionPolicy;

+import java.lang.annotation.Target;

+

+import org.apache.directory.server.core.integ.SetupMode;

+

+

+

+/**

+ * An annotation to control the mode used to setup for a test.

+ *

+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>

+ * @version $Rev$, $Date$

+ */

+@Documented

+@Inherited

+@Retention ( RetentionPolicy.RUNTIME )

+@Target ( { ElementType.METHOD, ElementType.TYPE } )

+public @interface Mode

+{

+     SetupMode value();

+}

diff --git a/old_trunk/core-integ/src/main/java/org/apache/directory/server/core/integ/state/AbstractState.java b/old_trunk/core-integ/src/main/java/org/apache/directory/server/core/integ/state/AbstractState.java
new file mode 100644
index 0000000..1626254
--- /dev/null
+++ b/old_trunk/core-integ/src/main/java/org/apache/directory/server/core/integ/state/AbstractState.java
@@ -0,0 +1,204 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements.  See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership.  The ASF licenses this file

+ * to you 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.

+ */

+package org.apache.directory.server.core.integ.state;

+

+

+import java.io.IOException;

+import java.io.StringReader;

+import java.util.ArrayList;

+import java.util.List;

+

+import javax.naming.NamingException;

+import javax.naming.ldap.LdapContext;

+

+import org.apache.directory.server.core.DirectoryService;

+import org.apache.directory.server.core.integ.InheritableSettings;

+import org.apache.directory.server.core.integ.IntegrationUtils;

+import org.apache.directory.shared.ldap.ldif.LdifEntry;

+import org.apache.directory.shared.ldap.ldif.LdifReader;

+import org.junit.internal.runners.TestClass;

+import org.junit.internal.runners.TestMethod;

+import org.junit.runner.notification.RunNotifier;

+import org.slf4j.Logger;

+import org.slf4j.LoggerFactory;

+

+

+/**

+ * The abstract state of a test service, containing the default state 

+ * transitions

+ *

+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>

+ * @version $Rev$, $Date$

+ */

+public abstract class AbstractState implements TestServiceState

+{

+    /** The class logger */

+    private static final Logger LOG = LoggerFactory.getLogger( AbstractState.class );

+

+    /** The context for this test */

+    protected final TestServiceContext context;

+

+    /** Error message when we can't destroy the service */

+    private static final String DESTROY_ERR = "Cannot destroy when service is in NonExistant state";

+    private static final String CLEANUP_ERROR = "Cannot cleanup when service is in NonExistant state";

+    private static final String STARTUP_ERR = "Cannot startup when service is in NonExistant state";

+    private static final String SHUTDOWN_ERR = "Cannot shutdown service in NonExistant state.";

+    private static final String REVERT_ERROR = "Cannot revert when service is in NonExistant state";

+

+    /**

+     * 

+     * Creates a new instance of AbstractState.

+     *

+     * @param context The associated context

+     */

+    protected AbstractState( TestServiceContext context )

+    {

+        this.context = context;

+    }

+

+

+    /**

+     * Action where an attempt is made to create the service.  Service

+     * creation in this system is the combined instantiation and

+     * configuration which takes place when the factory is used to get

+     * a new instance of the service.

+     *

+     * @param settings The inherited settings

+     * @throws NamingException if we can't create the service

+     */

+    public void create( InheritableSettings settings ) throws NamingException

+    {

+    }

+

+

+    /**

+     * Action where an attempt is made to destroy the service. This

+     * entails nulling out reference to it and triggering garbage

+     * collection.

+     */

+    public void destroy()

+    {

+        LOG.error( DESTROY_ERR );

+        throw new IllegalStateException( DESTROY_ERR );

+    }

+

+

+    /**

+     * Action where an attempt is made to erase the contents of the

+     * working directory used by the service for various files including

+     * partition database files.

+     *

+     * @throws IOException on errors while deleting the working directory

+     */

+    public void cleanup() throws  IOException

+    {

+        LOG.error( CLEANUP_ERROR );

+        throw new IllegalStateException( CLEANUP_ERROR );

+    }

+

+

+    /**

+     * Action where an attempt is made to start up the service.

+     *

+     * @throws NamingException on failures to start the core directory service

+     */

+    public void startup() throws NamingException

+    {

+        LOG.error( STARTUP_ERR );

+        throw new IllegalStateException( STARTUP_ERR );

+    }

+

+

+    /**

+     * Action where an attempt is made to shutdown the service.

+     *

+     * @throws NamingException on failures to stop the core directory service

+     */

+    public void shutdown() throws NamingException

+    {

+        LOG.error( SHUTDOWN_ERR );

+        throw new IllegalStateException( SHUTDOWN_ERR );

+    }

+

+

+    /**

+     * Action where an attempt is made to run a test against the service.

+     *

+     * All annotations should have already been processed for

+     * InheritableSettings yet they and others can be processed since we have

+     * access to the method annotations below

+     *

+     * @param testClass the class whose test method is to be run

+     * @param testMethod the test method which is to be run

+     * @param notifier a notifier to report failures to

+     * @param settings the inherited settings and annotations associated with

+     * the test method

+     */

+    public void test( TestClass testClass, TestMethod testMethod, RunNotifier notifier, InheritableSettings settings )

+    {

+    }

+

+

+    /**

+     * Action where an attempt is made to revert the service to it's

+     * initial start up state by using a previous snapshot.

+     *

+     * @throws NamingException on failures to revert the state of the core

+     * directory service

+     */

+    public void revert() throws NamingException

+    {

+        LOG.error( REVERT_ERROR );

+        throw new IllegalStateException( REVERT_ERROR );

+    }

+

+    

+    /**

+     * Inject the Ldifs if any

+     *

+     * @param service the instantiated directory service

+     * @param settings the settings containing the ldif

+     */

+    protected void injectLdifs( DirectoryService service, InheritableSettings settings )

+    {

+        List<String> ldifs = new ArrayList<String>();

+

+        ldifs =  settings.getLdifs( ldifs );

+        

+        if ( ldifs.size() != 0 )

+        {

+            for ( String ldif:ldifs )

+            {

+                try

+                {

+                    StringReader in = new StringReader( ldif );

+                    LdifReader ldifReader = new LdifReader( in );

+                    LdifEntry entry = ldifReader.next();

+                    

+                    LdapContext root = IntegrationUtils.getRootContext( service );

+                    root.createSubcontext( entry.getDn(), entry.getAttributes() );

+                }

+                catch ( NamingException ne )

+                {

+                    LOG.error( "Cannot inject the following entry : {}. Skipped.", ldif );

+                }

+            }

+        }

+    }

+}

diff --git a/old_trunk/core-integ/src/main/java/org/apache/directory/server/core/integ/state/NonExistentState.java b/old_trunk/core-integ/src/main/java/org/apache/directory/server/core/integ/state/NonExistentState.java
new file mode 100644
index 0000000..8cacf6f
--- /dev/null
+++ b/old_trunk/core-integ/src/main/java/org/apache/directory/server/core/integ/state/NonExistentState.java
@@ -0,0 +1,214 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+package org.apache.directory.server.core.integ.state;
+
+
+import java.io.IOException;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.core.integ.DirectoryServiceFactory;
+import org.apache.directory.server.core.integ.InheritableSettings;
+import static org.apache.directory.server.core.integ.IntegrationUtils.doDelete;
+import org.junit.internal.runners.TestClass;
+import org.junit.internal.runners.TestMethod;
+import org.junit.runner.notification.RunNotifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * The state of a test service when it has not yet been created.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class NonExistentState extends AbstractState
+{
+    private static final Logger LOG = LoggerFactory.getLogger( NonExistentState.class );
+
+
+    /**
+     * Creates a new instance of NonExistentState.
+     *
+     * @param context the test context
+     */
+    public NonExistentState( TestServiceContext context )
+    {
+        super( context );
+    }
+
+
+    /**
+     * Action where an attempt is made to create the service.  Service
+     * creation in this system is the combined instantiation and
+     * configuration which takes place when the factory is used to get
+     * a new instance of the service.
+     *
+     * @param settings The inherited settings
+     * @throws NamingException if we can't create the service
+     */
+    public void create( InheritableSettings settings ) throws NamingException
+    {
+        LOG.debug( "calling create()" );
+
+        try
+        {
+            DirectoryServiceFactory factory = settings.getFactory();
+            context.setService( factory.newInstance() );
+        }
+        catch ( InstantiationException ie )
+        {
+            throw new NamingException( ie.getMessage() );
+        }
+        catch ( IllegalAccessException iae )
+        {
+            throw new NamingException( iae.getMessage() );
+        }
+    }
+
+
+    /**
+     * Action where an attempt is made to erase the contents of the
+     * working directory used by the service for various files including
+     * partition database files.
+     *
+     * @throws IOException on errors while deleting the working directory
+     */
+    public void cleanup() throws IOException
+    {
+        LOG.debug( "calling cleanup()" );
+        doDelete( context.getService().getWorkingDirectory() );
+    }
+
+
+    /**
+     * Action where an attempt is made to start up the service.
+     *
+     * @throws NamingException on failures to start the core directory service
+     */
+    public void startup() throws NamingException
+    {
+        LOG.debug( "calling startup()" );
+        context.getService().startup();
+    }
+
+
+    /**
+     * This method is a bit different.  Consider this method to hold the logic
+     * which is needed to shift the context state from the present state to a
+     * started state so we can call test on the current state of the context.
+     *
+     * Basically if the service is not needed or the test is ignored, then we
+     * just invoke the test: if ignored the test is not dealt with by the
+     * MethodRoadie run method.
+     *
+     * In tests not ignored requiring setup modes RESTART and CUMULATIVE we
+     * simply create the service and start it up without a cleanup.  In the
+     * PRISTINE and ROLLBACK modes we do the same but cleanup() before a
+     * restart.
+     *
+     * @see TestServiceState#test(TestClass, TestMethod, RunNotifier, InheritableSettings) 
+     */
+    public void test( TestClass testClass, TestMethod testMethod, RunNotifier notifier, InheritableSettings settings )
+    {
+        LOG.debug( "calling test(): {}, mode {}", settings.getDescription().getDisplayName(), settings.getMode() );
+
+        if ( testMethod.isIgnored() )
+        {
+            // The test is ignored
+            return;
+        }
+
+        switch ( settings.getMode() )
+        {
+            case CUMULATIVE:
+            case RESTART:
+                try
+                {
+                    create( settings );
+                }
+                catch ( NamingException ne )
+                {
+                    LOG.error( "Failed to create and start new server instance: " + ne );
+                    notifier.testAborted( settings.getDescription(), ne );
+                    return;
+                }
+
+                try
+                {
+                    startup();
+                }
+                catch ( NamingException ne )
+                {
+                    LOG.error( "Failed to create and start new server instance: " + ne );
+                    notifier.testAborted( settings.getDescription(), ne );
+                    return;
+                }
+
+                
+                context.setState( context.getStartedNormalState() );
+                context.getState().test( testClass, testMethod, notifier, settings );
+                return;
+
+
+            case PRISTINE:
+            case ROLLBACK:
+                try
+                {
+                    create( settings );
+                }
+                catch ( NamingException ne )
+                {
+                    LOG.error( "Failed to create and start new server instance: " + ne );
+                    notifier.testAborted( settings.getDescription(), ne );
+                    return;
+                }
+
+                try
+                {
+                    cleanup();
+                }
+                catch ( IOException ioe )
+                {
+                    LOG.error( "Failed to create and start new server instance: " + ioe );
+                    notifier.testAborted( settings.getDescription(), ioe );
+                    return;
+                }
+
+                try
+                {
+                    startup();
+                }
+                catch ( NamingException ne )
+                {
+                    LOG.error( "Failed to create and start new server instance: " + ne );
+                    notifier.testAborted( settings.getDescription(), ne );
+                    return;
+                }
+
+                context.setState( context.getStartedPristineState() );
+                context.getState().test( testClass, testMethod, notifier, settings );
+                return;
+
+            default:
+                return;
+        }
+    }
+}
diff --git a/old_trunk/core-integ/src/main/java/org/apache/directory/server/core/integ/state/StartedNormalState.java b/old_trunk/core-integ/src/main/java/org/apache/directory/server/core/integ/state/StartedNormalState.java
new file mode 100644
index 0000000..95a1825
--- /dev/null
+++ b/old_trunk/core-integ/src/main/java/org/apache/directory/server/core/integ/state/StartedNormalState.java
@@ -0,0 +1,225 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+package org.apache.directory.server.core.integ.state;
+
+
+import java.io.IOException;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.core.integ.InheritableSettings;
+import static org.apache.directory.server.core.integ.IntegrationUtils.doDelete;
+import org.junit.internal.runners.TestClass;
+import org.junit.internal.runners.TestMethod;
+import org.junit.runner.notification.RunNotifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+
+/**
+ * The state of a running test service which has been used for running
+ * integration tests and has been reverted to contain the same content as it
+ * did when created and started.  It is not really pristine however for all
+ * practical purposes of integration testing it appears to be the same as
+ * when first started.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class StartedNormalState extends AbstractState
+{
+    private static final Logger LOG = LoggerFactory.getLogger( StartedNormalState.class );
+
+
+    /**
+     * 
+     * Creates a new instance of StartedNormalState.
+     *
+     * @param context the test's context
+     */
+    public StartedNormalState( TestServiceContext context )
+    {
+        super( context );
+    }
+
+
+    /**
+     * Action where an attempt is made to destroy the service. This
+     * entails nulling out reference to it and triggering garbage
+     * collection.
+     */
+    public void destroy()
+    {
+        LOG.debug( "calling destroy()" );
+        context.setService( null );
+        context.setState( context.getNonExistentState() );
+        System.gc();
+    }
+
+
+    /**
+     * Action where an attempt is made to erase the contents of the
+     * working directory used by the service for various files including
+     * partition database files.
+     *
+     * @throws IOException on errors while deleting the working directory
+     */
+    public void cleanup() throws IOException
+    {
+        LOG.debug( "calling cleanup()" );
+        doDelete( context.getService().getWorkingDirectory() );
+    }
+
+
+    /**
+     * Action where an attempt is made to start up the service.
+     *
+     * @throws NamingException on failures to start the core directory service
+     */
+    public void startup() throws NamingException
+    {
+        LOG.debug( "calling startup()" );
+        context.getService().startup();
+    }
+
+
+    /**
+     * Action where an attempt is made to shutdown the service.
+     *
+     * @throws NamingException on failures to stop the core directory service
+     */
+    public void shutdown() throws NamingException
+    {
+        LOG.debug( "calling shutdown()" );
+        context.getService().shutdown();
+    }
+
+
+    /**
+     * Action where an attempt is made to revert the service to it's
+     * initial start up state by using a previous snapshot.
+     *
+     * @throws NamingException on failures to revert the state of the core
+     * directory service
+     */
+    public void revert() throws NamingException
+    {
+        LOG.debug( "calling revert()" );
+        context.getService().revert();
+    }
+
+
+    /**
+     * Action where an attempt is made to run a test against the service.
+     *
+     * All annotations should have already been processed for
+     * InheritableSettings yet they and others can be processed since we have
+     * access to the method annotations below
+     *
+     * @param testClass the class whose test method is to be run
+     * @param testMethod the test method which is to be run
+     * @param notifier a notifier to report failures to
+     * @param settings the inherited settings and annotations associated with
+     * the test method
+     */
+    public void test( TestClass testClass, TestMethod testMethod, RunNotifier notifier, InheritableSettings settings )
+    {
+        LOG.debug( "calling test(): {}, mode {}", settings.getDescription().getDisplayName(), settings.getMode() );
+
+        if ( testMethod.isIgnored() )
+        {
+            // The test is ignored
+            return;
+        }
+
+        switch ( settings.getMode() )
+        {
+            case ROLLBACK:
+                try
+                {
+                    context.getService().getChangeLog().tag();
+                }
+                catch ( NamingException e )
+                {
+                    // @TODO - we might want to check the revision of the service before
+                    // we presume that it has been soiled.  Some tests may simply perform
+                    // some read operations or checks on the service and may not alter it
+                    notifier.testAborted( settings.getDescription(), e );
+                    return;
+                }
+
+                // Inject the LDIFs, if any 
+                injectLdifs( context.getService(), settings );
+                
+                TestServiceContext.invokeTest( testClass, testMethod, notifier, settings.getDescription() );
+                
+                try
+                {
+                    revert();
+                }
+                catch ( NamingException ne )
+                {
+                    // @TODO - we might want to check the revision of the service before
+                    // we presume that it has been soiled.  Some tests may simply perform
+                    // some read operations or checks on the service and may not alter it
+                    notifier.testAborted( settings.getDescription(), ne );
+                    return;
+                }
+                
+                return;
+                
+            case RESTART :
+                // Inject the LDIFs, if any 
+                injectLdifs( context.getService(), settings );
+                
+
+                TestServiceContext.invokeTest( testClass, testMethod, notifier, settings.getDescription() );
+
+                try
+                {
+                    shutdown();
+                }
+                catch ( NamingException ne )
+                {
+                    // @TODO - we might want to check the revision of the service before
+                    // we presume that it has been soiled.  Some tests may simply perform
+                    // some read operations or checks on the service and may not alter it
+                    notifier.testAborted( settings.getDescription(), ne );
+                    return;
+                }
+                
+                try
+                {
+                    startup();
+                }
+                catch ( NamingException ne )
+                {
+                    LOG.error( "Failed to create and start new server instance: " + ne );
+                    notifier.testAborted( settings.getDescription(), ne );
+                    return;
+                }
+                
+                return;
+                
+            default:
+                return;
+        }
+    }
+}
\ No newline at end of file
diff --git a/old_trunk/core-integ/src/main/java/org/apache/directory/server/core/integ/state/StartedPristineState.java b/old_trunk/core-integ/src/main/java/org/apache/directory/server/core/integ/state/StartedPristineState.java
new file mode 100644
index 0000000..bc9dba0
--- /dev/null
+++ b/old_trunk/core-integ/src/main/java/org/apache/directory/server/core/integ/state/StartedPristineState.java
@@ -0,0 +1,194 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+package org.apache.directory.server.core.integ.state;
+
+
+import java.io.IOException;
+import javax.naming.NamingException;
+
+import org.apache.directory.server.core.integ.InheritableSettings;
+import static org.apache.directory.server.core.integ.IntegrationUtils.doDelete;
+import org.junit.internal.runners.TestClass;
+import org.junit.internal.runners.TestMethod;
+import org.junit.runner.notification.RunNotifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+
+/**
+ * A test service state where the server is running and has not been used for
+ * any integration test since it was created.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class StartedPristineState extends AbstractState
+{
+    private static final Logger LOG = LoggerFactory.getLogger( StartedPristineState.class );
+
+
+    /**
+     * 
+     * Creates a new instance of StartedPristineState.
+     *
+     * @param context the test's context
+     */
+    public StartedPristineState( TestServiceContext context )
+    {
+        super( context );
+    }
+
+
+    /**
+     * Action where an attempt is made to erase the contents of the
+     * working directory used by the service for various files including
+     * partition database files.
+     *
+     * @throws IOException on errors while deleting the working directory
+     */
+    public void cleanup() throws IOException
+    {
+        LOG.debug( "calling cleanup()" );
+        doDelete( context.getService().getWorkingDirectory() );
+    }
+
+
+    /**
+     * Action where an attempt is made to start up the service.
+     *
+     * @throws NamingException on failures to start the core directory service
+     */
+    public void startup() throws NamingException
+    {
+        LOG.debug( "calling startup()" );
+        context.getService().startup();
+    }
+
+
+    /**
+     * Action where an attempt is made to shutdown the service.
+     *
+     * @throws NamingException on failures to stop the core directory service
+     */
+    public void shutdown() throws NamingException
+    {
+        LOG.debug( "calling shutdown()" );
+        context.getService().shutdown();
+    }
+
+
+    /**
+     * Action where an attempt is made to run a test against the service.
+     *
+     * All annotations should have already been processed for
+     * InheritableSettings yet they and others can be processed since we have
+     * access to the method annotations below
+     *
+     * @param testClass the class whose test method is to be run
+     * @param testMethod the test method which is to be run
+     * @param notifier a notifier to report failures to
+     * @param settings the inherited settings and annotations associated with
+     * the test method
+     */
+    public void test( TestClass testClass, TestMethod testMethod, RunNotifier notifier, InheritableSettings settings )
+    {
+        LOG.debug( "calling test(): {}, mode {}", settings.getDescription().getDisplayName(), settings.getMode() );
+
+        if ( testMethod.isIgnored() )
+        {
+            // The test is ignored
+            return;
+        }
+
+        switch ( settings.getMode() )
+        {
+            case PRISTINE:
+                // Inject the LDIFs, if any 
+                injectLdifs( context.getService(), settings );
+                
+                TestServiceContext.invokeTest( testClass, testMethod, notifier, settings.getDescription() );
+                
+                try
+                {
+                    shutdown();
+                }
+                catch ( NamingException ne )
+                {
+                    // @TODO - we might want to check the revision of the service before
+                    // we presume that it has been soiled.  Some tests may simply perform
+                    // some read operations or checks on the service and may not alter it
+                    notifier.testAborted( settings.getDescription(), ne );
+                    return;
+                }
+                
+                try
+                {
+                    cleanup();
+                }
+                catch ( IOException ioe )
+                {
+                    LOG.error( "Failed to cleanup new server instance: " + ioe );
+                    notifier.testAborted( settings.getDescription(), ioe );
+                    return;
+                }
+
+                destroy();
+                context.setState( context.getNonExistentState() );
+                return;
+                
+            case ROLLBACK:
+                try
+                {
+                    context.getService().getChangeLog().tag();
+                }
+                catch ( NamingException e )
+                {
+                    // @TODO - we might want to check the revision of the service before
+                    // we presume that it has been soiled.  Some tests may simply perform
+                    // some read operations or checks on the service and may not alter it
+                    notifier.testAborted( settings.getDescription(), e );
+                    return;
+                }
+
+                // Inject the LDIFs, if any 
+                injectLdifs( context.getService(), settings );
+                
+                TestServiceContext.invokeTest( testClass, testMethod, notifier, settings.getDescription() );
+                context.setState( context.getStartedNormalState() );
+
+                try
+                {
+                    context.getState().revert();
+                }
+                catch ( NamingException ne )
+                {
+                    // @TODO - we might want to check the revision of the service before
+                    // we presume that it has been soiled.  Some tests may simply perform
+                    // some read operations or checks on the service and may not alter it
+                    notifier.testAborted( settings.getDescription(), ne );
+                    return;
+                }
+                return;
+
+            default:
+                return;
+        }
+    }
+}
\ No newline at end of file
diff --git a/old_trunk/core-integ/src/main/java/org/apache/directory/server/core/integ/state/TestServiceContext.java b/old_trunk/core-integ/src/main/java/org/apache/directory/server/core/integ/state/TestServiceContext.java
new file mode 100644
index 0000000..4293eb1
--- /dev/null
+++ b/old_trunk/core-integ/src/main/java/org/apache/directory/server/core/integ/state/TestServiceContext.java
@@ -0,0 +1,290 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+package org.apache.directory.server.core.integ.state;
+
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import javax.naming.NamingException;
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.integ.InheritableSettings;
+import org.junit.internal.runners.MethodRoadie;
+import org.junit.internal.runners.TestClass;
+import org.junit.internal.runners.TestMethod;
+import org.junit.runner.Description;
+import org.junit.runner.notification.RunNotifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * The context for managing the state of an integration test service.
+ * Each thread of execution driving tests manages it's own service context.
+ * Hence parallelism can be achieved while running integration tests.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class TestServiceContext
+{
+    /** The logger */
+    private static final Logger LOG = LoggerFactory.getLogger( TestServiceContext.class );
+    
+    /** The ThreadLocal containing the contexts */
+    private static final ThreadLocal<TestServiceContext> CONTEXTS = new ThreadLocal<TestServiceContext>();
+
+    /** The NonExistant state instance */
+    private final TestServiceState nonExistentState = new NonExistentState( this );
+
+    /** The StartedPristine state instance */
+    private final TestServiceState startedPristineState = new StartedPristineState( this );
+    
+    /** The StartedNormal state instance */
+    private final TestServiceState startedNormalState = new StartedNormalState( this );
+
+
+    /** current service state with respect to the testing life cycle */
+    private TestServiceState state = nonExistentState;
+
+    /** the core directory service managed by this context */
+    private DirectoryService service;
+
+
+    /**
+     * A private constructor, the clas contains only static methods, 
+     * no need to construct an instance.
+     */
+    private TestServiceContext()
+    {
+        
+    }
+
+
+    /**
+     * Gets the TestServiceContext associated with the current thread of
+     * execution.  If one does not yet exist it will be created.
+     *
+     * @return the context associated with the calling thread
+     */
+    public static TestServiceContext get()
+    {
+        TestServiceContext context = CONTEXTS.get();
+
+        if ( context == null )
+        {
+            context = new TestServiceContext();
+            CONTEXTS.set( context );
+        }
+
+        return context;
+    }
+    
+
+    /**
+     * Sets the TestServiceContext for this current thread
+     *
+     * @param context the context associated with the calling thread
+     */
+    public static void set( TestServiceContext context )
+    {
+        CONTEXTS.set( context );
+    }
+
+
+    /**
+     * Action where an attempt is made to create the service.  Service
+     * creation in this system is the combined instantiation and
+     * configuration which takes place when the factory is used to get
+     * a new instance of the service.
+     *
+     * @param settings the settings for this test
+     * @throws NamingException if we can't create the service
+     */
+    public static void create( InheritableSettings settings ) throws NamingException
+    {
+        get().state.create( settings );
+    }
+
+
+    /**
+     * Action where an attempt is made to destroy the service.  This
+     * entails nulling out reference to it and triggering garbage
+     * collection.
+     */
+    public static void destroy()
+    {
+        get().state.destroy();
+    }
+
+
+    /**
+     * Action where an attempt is made to erase the contents of the
+     * working directory used by the service for various files including
+     * partition database files.
+     *
+     * @throws IOException on errors while deleting the working directory
+     */
+    public static void cleanup() throws IOException
+    {
+        get().state.cleanup();
+    }
+
+
+    /**
+     * Action where an attempt is made to start up the service.
+     *
+     * @throws NamingException on failures to start the core directory service
+     */
+    public static void startup() throws NamingException
+    {
+        get().state.startup();
+    }
+
+
+    /**
+     * Action where an attempt is made to shutdown the service.
+     *
+     * @throws NamingException on failures to stop the core directory service
+     */
+    public static void shutdown() throws NamingException
+    {
+        get().state.shutdown();
+    }
+
+
+    /**
+     * Action where an attempt is made to run a test against the service.
+     *
+     * @param testClass the class whose test method is to be run
+     * @param testMethod the test method which is to be run
+     * @param notifier a notifier to report failures to
+     * @param settings the inherited settings and annotations associated with
+     * the test method
+     */
+    public static void test( TestClass testClass, TestMethod testMethod, RunNotifier notifier,
+                             InheritableSettings settings )
+    {
+        LOG.debug( "calling test(): {}", settings.getDescription().getDisplayName() );
+        get().getState().test( testClass, testMethod, notifier, settings );
+    }
+
+
+    /**
+     * Action where an attempt is made to revert the service to it's
+     * initial start up state by using a previous snapshot.
+     *
+     * @throws NamingException on failures to revert the state of the core
+     * directory service
+     */
+    public static void revert() throws NamingException
+    {
+        get().state.revert();
+    }
+
+
+    static void invokeTest( TestClass testClass, TestMethod testMethod, RunNotifier notifier, Description description )
+    {
+        try
+        {
+            Object test = testClass.getConstructor().newInstance();
+            Field field = testClass.getJavaClass().getDeclaredField( "service" );
+            field.set( testClass.getJavaClass(), get().getService() );
+            new MethodRoadie( test, testMethod, notifier, description ).run();
+        }
+        catch ( InvocationTargetException e )
+        {
+            LOG.error( "Failed to invoke test method: " + description.getDisplayName(), e.getCause() );
+            notifier.testAborted( description, e.getCause() );
+            return;
+        }
+        catch ( InstantiationException ie )
+        {
+            LOG.error( "Failed to invoke test method: " + description.getDisplayName(), ie );
+            notifier.testAborted( description, ie );
+            return;
+        }
+        catch ( IllegalAccessException iae )
+        {
+            LOG.error( "Failed to invoke test method: " + description.getDisplayName(), iae );
+            notifier.testAborted( description, iae );
+            return;
+        }
+        catch ( NoSuchMethodException nsme )
+        {
+            LOG.error( "Failed to invoke test method: " + description.getDisplayName(), nsme );
+            notifier.testAborted( description, nsme );
+            return;
+        }
+        catch ( NoSuchFieldException nsfe )
+        {
+            LOG.error( "Failed to invoke test method: " + description.getDisplayName(), nsfe );
+            notifier.testAborted( description, nsfe );
+            return;
+        }
+    }
+
+
+    // -----------------------------------------------------------------------
+    // Package Friendly Instance Methods
+    // -----------------------------------------------------------------------
+
+
+    void setState( TestServiceState state )
+    {
+        this.state = state;
+    }
+
+
+    TestServiceState getState()
+    {
+        return state;
+    }
+
+
+    TestServiceState getNonExistentState()
+    {
+        return nonExistentState;
+    }
+
+
+    TestServiceState getStartedPristineState()
+    {
+        return startedPristineState;
+    }
+
+
+    TestServiceState getStartedNormalState()
+    {
+        return startedNormalState;
+    }
+
+
+    DirectoryService getService()
+    {
+        return service;
+    }
+
+
+    void setService( DirectoryService service )
+    {
+        this.service = service;
+    }
+}
diff --git a/old_trunk/core-integ/src/main/java/org/apache/directory/server/core/integ/state/TestServiceState.java b/old_trunk/core-integ/src/main/java/org/apache/directory/server/core/integ/state/TestServiceState.java
new file mode 100644
index 0000000..4f1c846
--- /dev/null
+++ b/old_trunk/core-integ/src/main/java/org/apache/directory/server/core/integ/state/TestServiceState.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+package org.apache.directory.server.core.integ.state;
+
+import java.io.IOException;
+import javax.naming.NamingException;
+
+import org.apache.directory.server.core.integ.InheritableSettings;
+import org.junit.internal.runners.TestClass;
+import org.junit.internal.runners.TestMethod;
+import org.junit.runner.notification.RunNotifier;
+
+
+/**
+ * The interface representing a state in the lifecycle of a service
+ * during integration testing.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public interface TestServiceState
+{
+    /**
+     * Action where an attempt is made to create the service.  Service
+     * creation in this system is the combined instantiation and
+     * configuration which takes place when the factory is used to get
+     * a new instance of the service.
+     *
+     * @param settings The inherited settings
+     * @throws NamingException if we can't create the service
+     */
+    void create( InheritableSettings settings ) throws NamingException;
+
+
+    /**
+     * Action where an attempt is made to destroy the service. This
+     * entails nulling out reference to it and triggering garbage
+     * collection.
+     */
+    void destroy();
+
+
+    /**
+     * Action where an attempt is made to erase the contents of the
+     * working directory used by the service for various files including
+     * partition database files.
+     *
+     * @throws IOException on errors while deleting the working directory
+     */
+    void cleanup() throws IOException;
+
+
+    /**
+     * Action where an attempt is made to start up the service.
+     *
+     * @throws NamingException on failures to start the core directory service
+     */
+    void startup() throws NamingException;
+
+
+    /**
+     * Action where an attempt is made to shutdown the service.
+     *
+     * @throws NamingException on failures to stop the core directory service
+     */
+    void shutdown() throws NamingException;
+
+
+    /**
+     * Action where an attempt is made to run a test against the service.
+     *
+     * All annotations should have already been processed for
+     * InheritableSettings yet they and others can be processed since we have
+     * access to the method annotations below
+     *
+     * @param testClass the class whose test method is to be run
+     * @param testMethod the test method which is to be run
+     * @param notifier a notifier to report failures to
+     * @param settings the inherited settings and annotations associated with
+     * the test method
+     */
+    void test( TestClass testClass, TestMethod testMethod, RunNotifier notifier, InheritableSettings settings );
+
+
+    /**
+     * Action where an attempt is made to revert the service to it's
+     * initial start up state by using a previous snapshot.
+     *
+     * @throws NamingException on failures to revert the state of the core
+     * directory service
+     */
+    void revert() throws NamingException;
+}
diff --git a/old_trunk/core-integ/src/site/site.xml b/old_trunk/core-integ/src/site/site.xml
new file mode 100644
index 0000000..9ec7c16
--- /dev/null
+++ b/old_trunk/core-integ/src/site/site.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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.
+-->
+<!--
+  @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+-->
+<project name="${project.name}">
+    <bannerLeft>
+        <name>${project.name}</name>
+    </bannerLeft>
+    <publishDate position="navigation-bottom" format="dd-MM-yyyy HH:mm" />
+    <body>
+        <menu name="Parent">
+            <item name="Apache Directory ApacheDS" href="../index.html" />
+        </menu>
+        <menu ref="reports" />
+    </body>
+</project>
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/StockCoreISuite.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/StockCoreISuite.java
new file mode 100644
index 0000000..bcdee20
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/StockCoreISuite.java
@@ -0,0 +1,106 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements.  See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership.  The ASF licenses this file

+ * to you 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.

+ */

+package org.apache.directory.server.core;

+

+import org.apache.directory.server.core.authn.SimpleAuthenticationIT;

+import org.apache.directory.server.core.changelog.DefaultChangeLogIT;

+import org.apache.directory.server.core.collective.CollectiveAttributeServiceIT;

+import org.apache.directory.server.core.configuration.PartitionConfigurationIT;

+import org.apache.directory.server.core.event.EventServiceIT;

+import org.apache.directory.server.core.exception.ExceptionServiceIT;

+import org.apache.directory.server.core.integ.CiSuite;

+import org.apache.directory.server.core.integ.Level;

+import org.apache.directory.server.core.integ.SetupMode;

+import org.apache.directory.server.core.integ.annotations.*;

+import org.apache.directory.server.core.jndi.AddIT;

+import org.apache.directory.server.core.jndi.CreateContextIT;

+import org.apache.directory.server.core.jndi.DIRSERVER169IT;

+import org.apache.directory.server.core.jndi.DIRSERVER759IT;

+import org.apache.directory.server.core.jndi.DIRSERVER783IT;

+import org.apache.directory.server.core.jndi.DIRSERVER791IT;

+import org.apache.directory.server.core.jndi.DestroyContextIT;

+import org.apache.directory.server.core.jndi.ExtensibleObjectIT;

+import org.apache.directory.server.core.jndi.ListIT;

+import org.apache.directory.server.core.jndi.ModifyContextIT;

+import org.apache.directory.server.core.jndi.ObjStateFactoryIT;

+import org.apache.directory.server.core.jndi.RFC2713IT;

+import org.apache.directory.server.core.jndi.ReferralIT;

+import org.apache.directory.server.core.jndi.RootDSEIT;

+import org.apache.directory.server.core.jndi.SearchIT;

+import org.apache.directory.server.core.jndi.UniqueMemberIT;

+import org.apache.directory.server.core.normalization.NormalizationServiceIT;

+import org.apache.directory.server.core.operational.OperationalAttributeServiceIT;

+import org.apache.directory.server.core.prefs.PreferencesIT;

+import org.apache.directory.server.core.sp.LdapClassLoaderIT;

+import org.apache.directory.server.core.subtree.BadSubentryServiceIT;

+import org.apache.directory.server.core.subtree.SubentryServiceEntryModificationHandlingIT;

+import org.apache.directory.server.core.subtree.SubentryServiceIT;

+import org.apache.directory.server.core.subtree.SubentryServiceObjectClassChangeHandlingIT;

+import org.apache.directory.server.core.trigger.SubentryServiceForTriggersIT;

+import org.apache.directory.server.core.trigger.TriggerInterceptorIT;

+import org.junit.runner.RunWith;

+import org.junit.runners.Suite;

+

+

+/**

+ * Document me!

+ *

+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>

+ * @version $Rev$, $Date$

+ */

+@RunWith ( CiSuite.class )

+@Suite.SuiteClasses ( {

+        SimpleAuthenticationIT.class,

+        CollectiveAttributeServiceIT.class,

+        ExceptionServiceIT.class,

+        EventServiceIT.class,

+        AddIT.class,

+        CreateContextIT.class,

+        DestroyContextIT.class,

+        DIRSERVER169IT.class,

+        DIRSERVER759IT.class,

+        DIRSERVER783IT.class,

+        DIRSERVER791IT.class,

+        ListIT.class,

+        ObjStateFactoryIT.class,

+        ExtensibleObjectIT.class,

+        ModifyContextIT.class,

+        RFC2713IT.class,

+        RootDSEIT.class,

+        SearchIT.class,

+        UniqueMemberIT.class,

+        OperationalAttributeServiceIT.class,

+        PreferencesIT.class,

+        TriggerInterceptorIT.class,

+        SubentryServiceForTriggersIT.class,

+        BadSubentryServiceIT.class,

+        SubentryServiceEntryModificationHandlingIT.class,

+        SubentryServiceObjectClassChangeHandlingIT.class,

+        SubentryServiceIT.class,

+        LdapClassLoaderIT.class,

+        NormalizationServiceIT.class,

+        DefaultChangeLogIT.class,

+        ReferralIT.class,

+        PartitionConfigurationIT.class  // Leaves the server in a bad state (partition removal is incomplete)

+        } )

+@CleanupLevel ( Level.SUITE )

+@Mode ( SetupMode.ROLLBACK )

+public class StockCoreISuite

+{

+}

diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/authn/SimpleAuthenticationIT.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/authn/SimpleAuthenticationIT.java
new file mode 100644
index 0000000..b40f1b9
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/authn/SimpleAuthenticationIT.java
@@ -0,0 +1,584 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.authn;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.integ.CiRunner;
+import static org.apache.directory.server.core.integ.IntegrationUtils.*;
+import org.apache.directory.shared.ldap.constants.AuthenticationLevel;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.ModificationItemImpl;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.util.ArrayUtils;
+import org.apache.directory.shared.ldap.util.StringTools;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.ldap.LdapContext;
+
+
+/**
+ * A set of simple tests to make sure simple authentication is working as it
+ * should.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+@RunWith( CiRunner.class )
+public class SimpleAuthenticationIT
+{
+    public static DirectoryService service;
+
+
+    public static LdapContext getRootDSE() throws NamingException
+    {
+        if ( service.isStarted() )
+        {
+            LdapDN dn = new LdapDN( "uid=admin,ou=system" );
+            dn.normalize( service.getRegistries().getAttributeTypeRegistry().getNormalizerMapping() );
+            return service.getJndiContext( new LdapPrincipal( dn, AuthenticationLevel.SIMPLE ) );
+        }
+
+        throw new IllegalStateException( "Cannot acquire rootDSE before the service has been started!" );
+    }
+
+
+    public static LdapContext getRootDSE( String bindDn ) throws NamingException
+    {
+        if ( service.isStarted() )
+        {
+            LdapDN dn = new LdapDN( bindDn );
+            dn.normalize( service.getRegistries().getAttributeTypeRegistry().getNormalizerMapping() );
+            return service.getJndiContext( new LdapPrincipal( dn, AuthenticationLevel.SIMPLE ) );
+        }
+
+        throw new IllegalStateException( "Cannot acquire rootDSE before the service has been started!" );
+    }
+
+
+    public static LdapContext getSystemRoot() throws NamingException
+    {
+        if ( service.isStarted() )
+        {
+            LdapDN dn = new LdapDN( "uid=admin,ou=system" );
+            dn.normalize( service.getRegistries().getAttributeTypeRegistry().getNormalizerMapping() );
+            return service.getJndiContext( new LdapPrincipal(
+                    dn, AuthenticationLevel.SIMPLE ), "ou=system" );
+        }
+
+        throw new IllegalStateException( "Cannot acquire rootDSE before the service has been started!" );
+    }
+
+
+    public static LdapContext getSystemRoot( String bindDn ) throws NamingException
+    {
+        if ( service.isStarted() )
+        {
+            LdapDN dn = new LdapDN( bindDn );
+            dn.normalize( service.getRegistries().getAttributeTypeRegistry().getNormalizerMapping() );
+            return service.getJndiContext( new LdapPrincipal( dn, AuthenticationLevel.SIMPLE ), "ou=system" );
+        }
+
+        throw new IllegalStateException( "Cannot acquire rootDSE before the service has been started!" );
+    }
+
+
+    /**
+     * Checks all attributes of the admin account entry minus the userPassword
+     * attribute.
+     *
+     * @param attrs the entries attributes
+     */
+    protected void performAdminAccountChecks( Attributes attrs )
+    {
+        assertTrue( attrs.get( "objectClass" ).contains( "top" ) );
+        assertTrue( attrs.get( "objectClass" ).contains( "person" ) );
+        assertTrue( attrs.get( "objectClass" ).contains( "organizationalPerson" ) );
+        assertTrue( attrs.get( "objectClass" ).contains( "inetOrgPerson" ) );
+        assertTrue( attrs.get( "displayName" ).contains( "Directory Superuser" ) );
+    }
+
+
+    /**
+     * Check the creation of the admin account and persistence across restarts.
+     *
+     * @throws NamingException if there are failures
+     */
+    @Test
+    public void testAdminAccountCreation() throws NamingException
+    {
+        String userDn = "uid=admin,ou=system";
+        LdapContext ctx = service.getJndiContext( new LdapDN( userDn ), userDn,
+                "secret".getBytes(), "simple", "ou=system" );
+        Attributes attrs = ctx.getAttributes( "uid=admin" );
+        performAdminAccountChecks( attrs );
+        assertTrue( ArrayUtils.isEquals( attrs.get( "userPassword" ).get(), StringTools.getBytesUtf8( "secret" ) ) );
+        ctx.close();
+
+        service.shutdown();
+        service.startup();
+
+        ctx = service.getJndiContext( new LdapDN( userDn ), userDn,
+                "secret".getBytes(), "simple", "ou=system" );
+        attrs = ctx.getAttributes( "uid=admin" );
+        performAdminAccountChecks( attrs );
+        assertTrue( ArrayUtils.isEquals( attrs.get( "userPassword" ).get(), StringTools.getBytesUtf8( "secret" ) ) );
+        ctx.close();
+    }
+
+
+    @Test
+    public void test3UseAkarasulu() throws NamingException
+    {
+        apply( getRootDSE(), getUserAddLdif() );
+        String userDn = "uid=akarasulu,ou=users,ou=system";
+        LdapContext ctx = service.getJndiContext( new LdapDN( userDn ), userDn, "test".getBytes(), "simple", userDn );
+
+        Attributes attrs = ctx.getAttributes( "" );
+        Attribute ou = attrs.get( "ou" );
+        assertTrue( ou.contains( "Engineering" ) );
+        assertTrue( ou.contains( "People" ) );
+
+        Attribute objectClass = attrs.get( "objectClass" );
+        assertTrue( objectClass.contains( "top" ) );
+        assertTrue( objectClass.contains( "person" ) );
+        assertTrue( objectClass.contains( "organizationalPerson" ) );
+        assertTrue( objectClass.contains( "inetOrgPerson" ) );
+
+        assertTrue( attrs.get( "telephonenumber" ).contains( "+1 408 555 4798" ) );
+        assertTrue( attrs.get( "uid" ).contains( "akarasulu" ) );
+        assertTrue( attrs.get( "givenname" ).contains( "Alex" ) );
+        assertTrue( attrs.get( "mail" ).contains( "akarasulu@apache.org" ) );
+        assertTrue( attrs.get( "l" ).contains( "Bogusville" ) );
+        assertTrue( attrs.get( "sn" ).contains( "Karasulu" ) );
+        assertTrue( attrs.get( "cn" ).contains( "Alex Karasulu" ) );
+        assertTrue( attrs.get( "facsimiletelephonenumber" ).contains( "+1 408 555 9751" ) );
+        assertTrue( attrs.get( "roomnumber" ).contains( "4612" ) );
+    }
+
+
+    /**
+     * Tests to make sure we can authenticate after the database has already
+     * been started by the admin user when simple authentication is in effect.
+     *
+     * @throws Exception if anything goes wrong
+     */
+    @Test
+    public void test8PassPrincAuthTypeSimple() throws Exception
+    {
+        String userDn = "uid=admin,ou=system";
+        assertNotNull( service.getJndiContext( new LdapDN( userDn ), userDn, "secret".getBytes(), "simple", userDn ) );
+    }
+
+
+    /**
+     * Checks to see if we can authenticate as a test user after the admin fires
+     * up and builds the the system database.
+     *
+     * @throws Exception if anything goes wrong
+     */
+    @Test
+    public void test10TestNonAdminUser() throws Exception
+    {
+        apply( getRootDSE(), getUserAddLdif() );
+        String userDn = "uid=akarasulu,ou=users,ou=system";
+        assertNotNull( service.getJndiContext( new LdapDN( userDn ), userDn, "test".getBytes(), "simple", userDn ) );
+    }
+
+
+    @Test
+    public void test11InvalidateCredentialCache() throws NamingException
+    {
+        apply( getRootDSE(), getUserAddLdif() );
+        String userDn = "uid=akarasulu,ou=users,ou=system";
+        LdapContext ctx = service.getJndiContext( new LdapDN( userDn ), userDn, "test".getBytes(), "simple", userDn );
+        assertNotNull( ctx );
+        Attributes attrs = ctx.getAttributes( "" );
+        Attribute ou = attrs.get( "ou" );
+        assertTrue( ou.contains( "Engineering" ) );
+        assertTrue( ou.contains( "People" ) );
+
+        Attribute objectClass = attrs.get( "objectClass" );
+        assertTrue( objectClass.contains( "top" ) );
+        assertTrue( objectClass.contains( "person" ) );
+        assertTrue( objectClass.contains( "organizationalPerson" ) );
+        assertTrue( objectClass.contains( "inetOrgPerson" ) );
+
+        assertTrue( attrs.get( "telephonenumber" ).contains( "+1 408 555 4798" ) );
+        assertTrue( attrs.get( "uid" ).contains( "akarasulu" ) );
+        assertTrue( attrs.get( "givenname" ).contains( "Alex" ) );
+        assertTrue( attrs.get( "mail" ).contains( "akarasulu@apache.org" ) );
+        assertTrue( attrs.get( "l" ).contains( "Bogusville" ) );
+        assertTrue( attrs.get( "sn" ).contains( "Karasulu" ) );
+        assertTrue( attrs.get( "cn" ).contains( "Alex Karasulu" ) );
+        assertTrue( attrs.get( "facsimiletelephonenumber" ).contains( "+1 408 555 9751" ) );
+        assertTrue( attrs.get( "roomnumber" ).contains( "4612" ) );
+
+        // now modify the password for akarasulu
+        AttributeImpl userPasswordAttribute = new AttributeImpl( "userPassword", "newpwd" );
+        ctx.modifyAttributes( "", new ModificationItemImpl[] {
+            new ModificationItemImpl( DirContext.REPLACE_ATTRIBUTE, userPasswordAttribute ) } );
+
+        // close and try with old password (should fail)
+        ctx.close();
+        try
+        {
+            service.getJndiContext( new LdapDN( userDn ), userDn, "test".getBytes(), "simple", userDn );
+            fail( "Authentication with old password should fail" );
+        }
+        catch ( NamingException e )
+        {
+            // we should fail
+        }
+
+        // close and try again now with new password (should fail)
+        ctx.close();
+        ctx = service.getJndiContext( new LdapDN( userDn ), userDn, "newpwd".getBytes(), "simple", userDn );
+        attrs = ctx.getAttributes( "" );
+        ou = attrs.get( "ou" );
+        assertTrue( ou.contains( "Engineering" ) );
+        assertTrue( ou.contains( "People" ) );
+
+        objectClass = attrs.get( "objectClass" );
+        assertTrue( objectClass.contains( "top" ) );
+        assertTrue( objectClass.contains( "person" ) );
+        assertTrue( objectClass.contains( "organizationalPerson" ) );
+        assertTrue( objectClass.contains( "inetOrgPerson" ) );
+
+        assertTrue( attrs.get( "telephonenumber" ).contains( "+1 408 555 4798" ) );
+        assertTrue( attrs.get( "uid" ).contains( "akarasulu" ) );
+        assertTrue( attrs.get( "givenname" ).contains( "Alex" ) );
+        assertTrue( attrs.get( "mail" ).contains( "akarasulu@apache.org" ) );
+        assertTrue( attrs.get( "l" ).contains( "Bogusville" ) );
+        assertTrue( attrs.get( "sn" ).contains( "Karasulu" ) );
+        assertTrue( attrs.get( "cn" ).contains( "Alex Karasulu" ) );
+        assertTrue( attrs.get( "facsimiletelephonenumber" ).contains( "+1 408 555 9751" ) );
+        assertTrue( attrs.get( "roomnumber" ).contains( "4612" ) );
+    }
+
+
+    @Test
+    public void testSHA() throws NamingException
+    {
+        apply( getRootDSE(), getUserAddLdif() );
+        String userDn = "uid=akarasulu,ou=users,ou=system";
+        LdapContext ctx = service.getJndiContext( new LdapDN( userDn ), userDn, "test".getBytes(), "simple", userDn );
+
+        // Check that we can get the attributes
+        Attributes attrs = ctx.getAttributes( "" );
+        assertNotNull( attrs );
+        assertTrue( attrs.get( "uid" ).contains( "akarasulu" ) );
+
+        // now modify the password for akarasulu : 'secret', encrypted using SHA
+        AttributeImpl userPasswordAttribute = new AttributeImpl( "userPassword", "{SHA}5en6G6MezRroT3XKqkdPOmY/BfQ=" );
+        ctx.modifyAttributes( "", new ModificationItemImpl[] {
+            new ModificationItemImpl( DirContext.REPLACE_ATTRIBUTE, userPasswordAttribute ) } );
+
+        // close and try with old password (should fail)
+        ctx.close();
+
+        try
+        {
+            ctx = service.getJndiContext( new LdapDN( userDn ), userDn, "test".getBytes(), "simple", userDn );
+            fail( "Authentication with old password should fail" );
+        }
+        catch ( NamingException e )
+        {
+            // we should fail
+        }
+        finally
+        {
+            if ( ctx != null )
+            {
+                ctx.close();
+            }
+        }
+
+        // try again now with new password (should be successfull)
+        ctx = service.getJndiContext( new LdapDN( userDn ), userDn, "secret".getBytes(), "simple", userDn );
+        attrs = ctx.getAttributes( "" );
+        assertNotNull( attrs );
+        assertTrue( attrs.get( "uid" ).contains( "akarasulu" ) );
+
+        // close and try again now with new password, to check that the
+        // cache is updated (should be successfull)
+        ctx.close();
+        ctx = service.getJndiContext( new LdapDN( userDn ), userDn, "secret".getBytes(), "simple", userDn );
+        attrs = ctx.getAttributes( "" );
+        assertNotNull( attrs );
+        assertTrue( attrs.get( "uid" ).contains( "akarasulu" ) );
+    }
+
+
+    @Test
+    public void testSSHA() throws NamingException
+    {
+        apply( getRootDSE(), getUserAddLdif() );
+        String userDn = "uid=akarasulu,ou=users,ou=system";
+        LdapContext ctx = service.getJndiContext( new LdapDN( userDn ), userDn, "test".getBytes(), "simple", userDn );
+
+        // Check that we can get the attributes
+        Attributes attrs = ctx.getAttributes( "" );
+        assertNotNull( attrs );
+        assertTrue( attrs.get( "uid" ).contains( "akarasulu" ) );
+
+        // now modify the password for akarasulu : 'secret', encrypted using SHA
+        AttributeImpl userPasswordAttribute = new AttributeImpl( "userPassword", "{SSHA}mjVVxasFkk59wMW4L1Ldt+YCblfhULHs03WW7g==" );
+        ctx.modifyAttributes( "", new ModificationItemImpl[] {
+            new ModificationItemImpl( DirContext.REPLACE_ATTRIBUTE, userPasswordAttribute ) } );
+
+        // close and try with old password (should fail)
+        ctx.close();
+
+        try
+        {
+            ctx = service.getJndiContext( new LdapDN( userDn ), userDn, "test".getBytes(), "simple", userDn );
+            fail( "Authentication with old password should fail" );
+        }
+        catch ( NamingException e )
+        {
+            // we should fail
+        }
+        finally
+        {
+            if ( ctx != null )
+            {
+                ctx.close();
+            }
+        }
+
+        // try again now with new password (should be successfull)
+        ctx = service.getJndiContext( new LdapDN( userDn ), userDn, "secret".getBytes(), "simple", userDn );
+        attrs = ctx.getAttributes( "" );
+        assertNotNull( attrs );
+        assertTrue( attrs.get( "uid" ).contains( "akarasulu" ) );
+
+        // close and try again now with new password, to check that the
+        // cache is updated (should be successfull)
+        ctx = service.getJndiContext( new LdapDN( userDn ), userDn, "secret".getBytes(), "simple", userDn );
+        attrs = ctx.getAttributes( "" );
+        assertNotNull( attrs );
+        assertTrue( attrs.get( "uid" ).contains( "akarasulu" ) );
+    }
+
+
+    @Test
+    public void testMD5() throws NamingException
+    {
+        apply( getRootDSE(), getUserAddLdif() );
+        String userDn = "uid=akarasulu,ou=users,ou=system";
+        LdapContext ctx = service.getJndiContext( new LdapDN( userDn ), userDn, "test".getBytes(), "simple", userDn );
+
+        // Check that we can get the attributes
+        Attributes attrs = ctx.getAttributes( "" );
+        assertNotNull( attrs );
+        assertTrue( attrs.get( "uid" ).contains( "akarasulu" ) );
+
+        // now modify the password for akarasulu : 'secret', encrypted using MD5
+        AttributeImpl userPasswordAttribute = new AttributeImpl( "userPassword", "{MD5}Xr4ilOzQ4PCOq3aQ0qbuaQ==" );
+        ctx.modifyAttributes( "", new ModificationItemImpl[] {
+            new ModificationItemImpl( DirContext.REPLACE_ATTRIBUTE, userPasswordAttribute ) } );
+
+        // close and try with old password (should fail)
+        ctx.close();
+
+        try
+        {
+            ctx = service.getJndiContext( new LdapDN( userDn ), userDn, "test".getBytes(), "simple", userDn );
+            fail( "Authentication with old password should fail" );
+        }
+        catch ( NamingException e )
+        {
+            // we should fail
+        }
+        finally
+        {
+            if ( ctx != null )
+            {
+                ctx.close();
+            }
+        }
+
+        // try again now with new password (should be successfull)
+        ctx = service.getJndiContext( new LdapDN( userDn ), userDn, "secret".getBytes(), "simple", userDn );
+        attrs = ctx.getAttributes( "" );
+        assertNotNull( attrs );
+        assertTrue( attrs.get( "uid" ).contains( "akarasulu" ) );
+
+        // try again now with new password, to check that the
+        // cache is updated (should be successfull)
+        ctx = service.getJndiContext( new LdapDN( userDn ), userDn, "secret".getBytes(), "simple", userDn );
+        attrs = ctx.getAttributes( "" );
+        assertNotNull( attrs );
+        assertTrue( attrs.get( "uid" ).contains( "akarasulu" ) );
+    }
+
+
+    @Test
+    public void testSMD5() throws NamingException
+    {
+        apply( getRootDSE(), getUserAddLdif() );
+        String userDn = "uid=akarasulu,ou=users,ou=system";
+        LdapContext ctx = service.getJndiContext( new LdapDN( userDn ), userDn, "test".getBytes(), "simple", userDn );
+
+        // Check that we can get the attributes
+        Attributes attrs = ctx.getAttributes( "" );
+        assertNotNull( attrs );
+        assertTrue( attrs.get( "uid" ).contains( "akarasulu" ) );
+
+        // now modify the password for akarasulu : 'secret', encrypted using SHA
+        AttributeImpl userPasswordAttribute = new AttributeImpl( "userPassword", "{SMD5}tQ9wo/VBuKsqBtylMMCcORbnYOJFMyDJ" );
+        ctx.modifyAttributes( "", new ModificationItemImpl[] {
+            new ModificationItemImpl( DirContext.REPLACE_ATTRIBUTE, userPasswordAttribute ) } );
+
+        // close and try with old password (should fail)
+        ctx.close();
+
+        try
+        {
+            ctx = service.getJndiContext( new LdapDN( userDn ), userDn, "test".getBytes(), "simple", userDn );
+            fail( "Authentication with old password should fail" );
+        }
+        catch ( NamingException e )
+        {
+            // we should fail
+        }
+        finally
+        {
+            if ( ctx != null )
+            {
+                ctx.close();
+            }
+        }
+
+        // try again now with new password (should be successfull)
+        ctx = service.getJndiContext( new LdapDN( userDn ), userDn, "secret".getBytes(), "simple", userDn );
+        attrs = ctx.getAttributes( "" );
+        assertNotNull( attrs );
+        assertTrue( attrs.get( "uid" ).contains( "akarasulu" ) );
+
+        // try again now with new password, to check that the
+        // cache is updated (should be successfull)
+        ctx = service.getJndiContext( new LdapDN( userDn ), userDn, "secret".getBytes(), "simple", userDn );
+        attrs = ctx.getAttributes( "" );
+        assertNotNull( attrs );
+        assertTrue( attrs.get( "uid" ).contains( "akarasulu" ) );
+    }
+
+
+    @Test
+    public void testCRYPT() throws NamingException
+    {
+        apply( getRootDSE(), getUserAddLdif() );
+        String userDn = "uid=akarasulu,ou=users,ou=system";
+        LdapContext ctx = service.getJndiContext( new LdapDN( userDn ), userDn, "test".getBytes(), "simple", userDn );
+
+        // Check that we can get the attributes
+        Attributes attrs = ctx.getAttributes( "" );
+        assertNotNull( attrs );
+        assertTrue( attrs.get( "uid" ).contains( "akarasulu" ) );
+
+        // now modify the password for akarasulu : 'secret', encrypted using CRYPT
+        AttributeImpl userPasswordAttribute = new AttributeImpl( "userPassword", "{crypt}qFkH8Z1woBlXw" );
+        ctx.modifyAttributes( "", new ModificationItemImpl[] {
+            new ModificationItemImpl( DirContext.REPLACE_ATTRIBUTE, userPasswordAttribute ) } );
+
+        // close and try with old password (should fail)
+        ctx.close();
+
+        try
+        {
+            ctx = service.getJndiContext( new LdapDN( userDn ), userDn, "test".getBytes(), "simple", userDn );
+            fail( "Authentication with old password should fail" );
+        }
+        catch ( NamingException e )
+        {
+            // we should fail
+        }
+        finally
+        {
+            if ( ctx != null )
+            {
+                ctx.close();
+            }
+        }
+
+        // try again now with new password (should be successfull)
+        ctx = service.getJndiContext( new LdapDN( userDn ), userDn, "secret".getBytes(), "simple", userDn );
+        attrs = ctx.getAttributes( "" );
+        assertNotNull( attrs );
+        assertTrue( attrs.get( "uid" ).contains( "akarasulu" ) );
+
+        // try again now with new password, to check that the
+        // cache is updated (should be successfull)
+        ctx = service.getJndiContext( new LdapDN( userDn ), userDn, "secret".getBytes(), "simple", userDn );
+        attrs = ctx.getAttributes( "" );
+        assertNotNull( attrs );
+        assertTrue( attrs.get( "uid" ).contains( "akarasulu" ) );
+    }
+
+
+    @Test
+    public void testInvalidateCredentialCacheForUpdatingAnotherUsersPassword() throws NamingException
+    {
+        apply( getRootDSE(), getUserAddLdif() );
+
+        // bind as akarasulu
+        String userDn = "uid=akarasulu,ou=users,ou=system";
+        LdapContext ctx = service.getJndiContext( new LdapDN( userDn ), userDn, "test".getBytes(), "simple", userDn );
+        ctx.close();
+
+        // bind as admin
+        userDn = "uid=admin,ou=system";
+        ctx = service.getJndiContext( new LdapDN( userDn ), userDn, "secret".getBytes(), "simple", userDn );
+
+        // now modify the password for akarasulu (while we're admin)
+        AttributeImpl userPasswordAttribute = new AttributeImpl( "userPassword", "newpwd" );
+        ctx.modifyAttributes( "", new ModificationItemImpl[] {
+            new ModificationItemImpl( DirContext.REPLACE_ATTRIBUTE, userPasswordAttribute ) } );
+        ctx.close();
+
+        try
+        {
+            ctx = service.getJndiContext( new LdapDN( userDn ), userDn, "test".getBytes(), "simple", userDn );
+            fail( "Authentication with old password should fail" );
+        }
+        catch ( NamingException e )
+        {
+            // we should fail
+        }
+        finally
+        {
+            if ( ctx != null )
+            {
+                ctx.close();
+            }
+        }
+    }
+}
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/authz/AddAuthorizationIT.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/authz/AddAuthorizationIT.java
new file mode 100644
index 0000000..21794e8
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/authz/AddAuthorizationIT.java
@@ -0,0 +1,210 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.authz;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import static org.apache.directory.server.core.authz.AutzIntegUtils.createUser;
+import static org.apache.directory.server.core.authz.AutzIntegUtils.getContextAs;
+import static org.apache.directory.server.core.authz.AutzIntegUtils.getContextAsAdmin;
+import static org.apache.directory.server.core.authz.AutzIntegUtils.createAccessControlSubentry;
+import static org.apache.directory.server.core.authz.AutzIntegUtils.addUserToGroup;
+import org.apache.directory.server.core.integ.CiRunner;
+import org.apache.directory.server.core.integ.annotations.Factory;
+import org.apache.directory.shared.ldap.exception.LdapNoPermissionException;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+
+
+/**
+ * Tests whether or not authorization around entry addition works properly.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+@RunWith ( CiRunner.class )
+@Factory ( AutzIntegUtils.ServiceFactory.class )
+public class AddAuthorizationIT
+{
+    public static DirectoryService service;
+
+    
+    /**
+     * Checks if a simple entry (organizationalUnit) can be added to the DIT at an
+     * RDN relative to ou=system by a specific non-admin user.  If a permission exception
+     * is encountered it is caught and false is returned, otherwise true is returned
+     * when the entry is created.  The entry is deleted after being created just in case
+     * subsequent calls to this method do not fail: the admin account is used to delete
+     * this test entry so permissions to delete are not required to delete it by the user.
+     *
+     * @param uid the unique identifier for the user (presumed to exist under ou=users,ou=system)
+     * @param password the password of this user
+     * @param entryRdn the relative DN, relative to ou=system where entry creation is tested
+     * @return true if the entry can be created by the user at the specified location, false otherwise
+     * @throws NamingException if there are problems conducting the test
+     */
+    public boolean checkCanAddEntryAs( String uid, String password, String entryRdn ) throws NamingException
+    {
+        Attributes testEntry = new AttributesImpl( "ou", "testou", true );
+        Attribute objectClass = new AttributeImpl( "objectClass" );
+        testEntry.put( objectClass );
+        objectClass.add( "top" );
+        objectClass.add( "organizationalUnit" );
+
+        try
+        {
+            LdapDN userName = new LdapDN( "uid=" + uid + ",ou=users,ou=system" );
+            DirContext userContext = getContextAs( userName, password );
+            userContext.createSubcontext( entryRdn, testEntry );
+
+            // delete the newly created context as the admin user
+            DirContext adminContext = getContextAsAdmin();
+            adminContext.destroySubcontext( entryRdn );
+
+            return true;
+        }
+        catch ( LdapNoPermissionException e )
+        {
+            return false;
+        }
+    }
+
+
+    /**
+     * Checks to make sure group membership based userClass works for add operations.
+     *
+     * @throws NamingException if the test encounters an error
+     */
+    @Test
+    public void testGrantAddAdministrators() throws NamingException
+    {
+        // create the non-admin user
+        createUser( "billyd", "billyd" );
+
+        // try an add operation which should fail without any ACI
+        assertFalse( checkCanAddEntryAs( "billyd", "billyd", "ou=testou" ) );
+
+        // Gives grantAdd perm to all users in the Administrators group for
+        // entries and all attribute types and values
+        createAccessControlSubentry( "administratorAdd", "{ " + "identificationTag \"addAci\", " + "precedence 14, "
+            + "authenticationLevel none, " + "itemOrUserFirst userFirst: { "
+            + "userClasses { userGroup { \"cn=Administrators,ou=groups,ou=system\" } }, " + "userPermissions { { "
+            + "protectedItems {entry, allUserAttributeTypesAndValues}, "
+            + "grantsAndDenials { grantAdd, grantBrowse } } } } }" );
+
+        // see if we can now add that test entry which we could not before
+        // add op should still fail since billd is not in the admin group
+        assertFalse( checkCanAddEntryAs( "billyd", "billyd", "ou=testou" ) );
+
+        // now add billyd to the Administrator group and try again
+        addUserToGroup( "billyd", "Administrators" );
+
+        // try an add operation which should succeed with ACI and group membership change
+        assertTrue( checkCanAddEntryAs( "billyd", "billyd", "ou=testou" ) );
+    }
+
+
+    /**
+     * Checks to make sure name based userClass works for add operations.
+     *
+     * @throws NamingException if the test encounters an error
+     */
+    @Test
+    public void testGrantAddByName() throws NamingException
+    {
+        // create the non-admin user
+        createUser( "billyd", "billyd" );
+
+        // try an add operation which should fail without any ACI
+        assertFalse( checkCanAddEntryAs( "billyd", "billyd", "ou=testou" ) );
+
+        // now add a subentry that enables user billyd to add an entry below ou=system
+        createAccessControlSubentry( "billydAdd", "{ " + "identificationTag \"addAci\", " + "precedence 14, "
+            + "authenticationLevel none, " + "itemOrUserFirst userFirst: { "
+            + "userClasses { name { \"uid=billyd,ou=users,ou=system\" } }, " + "userPermissions { { "
+            + "protectedItems {entry, allUserAttributeTypesAndValues}, "
+            + "grantsAndDenials { grantAdd, grantBrowse } } } } }" );
+
+        // should work now that billyd is authorized by name
+        assertTrue( checkCanAddEntryAs( "billyd", "billyd", "ou=testou" ) );
+    }
+
+
+    /**
+     * Checks to make sure subtree based userClass works for add operations.
+     *
+     * @throws NamingException if the test encounters an error
+     */
+    @Test
+    public void testGrantAddBySubtree() throws NamingException
+    {
+        // create the non-admin user
+        createUser( "billyd", "billyd" );
+
+        // try an add operation which should fail without any ACI
+        assertFalse( checkCanAddEntryAs( "billyd", "billyd", "ou=testou" ) );
+
+        // now add a subentry that enables user billyd to add an entry below ou=system
+        createAccessControlSubentry( "billyAddBySubtree", "{ " + "identificationTag \"addAci\", " + "precedence 14, "
+            + "authenticationLevel none, " + "itemOrUserFirst userFirst: { "
+            + "userClasses { subtree { { base \"ou=users,ou=system\" } } }, " + "userPermissions { { "
+            + "protectedItems {entry, allUserAttributeTypesAndValues}, "
+            + "grantsAndDenials { grantAdd, grantBrowse } } } } }" );
+
+        // should work now that billyd is authorized by the subtree userClass
+        assertTrue( checkCanAddEntryAs( "billyd", "billyd", "ou=testou" ) );
+    }
+
+
+    /**
+     * Checks to make sure <b>allUsers</b> userClass works for add operations.
+     *
+     * @throws NamingException if the test encounters an error
+     */
+    @Test
+    public void testGrantAddAllUsers() throws NamingException
+    {
+        // create the non-admin user
+        createUser( "billyd", "billyd" );
+
+        // try an add operation which should fail without any ACI
+        assertFalse( checkCanAddEntryAs( "billyd", "billyd", "ou=testou" ) );
+
+        // now add a subentry that enables anyone to add an entry below ou=system
+        createAccessControlSubentry( "anybodyAdd", "{ " + "identificationTag \"addAci\", " + "precedence 14, "
+            + "authenticationLevel none, " + "itemOrUserFirst userFirst: { " + "userClasses { allUsers }, "
+            + "userPermissions { { " + "protectedItems {entry, allUserAttributeTypesAndValues}, "
+            + "grantsAndDenials { grantAdd, grantBrowse } } } } }" );
+
+        // see if we can now add that test entry which we could not before
+        // should work now with billyd now that all users are authorized
+        assertTrue( checkCanAddEntryAs( "billyd", "billyd", "ou=testou" ) );
+    }
+}
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/authz/AdministratorsGroupIT.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/authz/AdministratorsGroupIT.java
new file mode 100644
index 0000000..61bb9ae
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/authz/AdministratorsGroupIT.java
@@ -0,0 +1,131 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.authz;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import static org.apache.directory.server.core.authz.AutzIntegUtils.createUser;
+import static org.apache.directory.server.core.authz.AutzIntegUtils.getContextAs;
+import static org.apache.directory.server.core.authz.AutzIntegUtils.addUserToGroup;
+import org.apache.directory.server.core.integ.CiRunner;
+import org.apache.directory.server.core.integ.SetupMode;
+import org.apache.directory.server.core.integ.annotations.*;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.fail;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.naming.Name;
+import javax.naming.NamingException;
+import javax.naming.NoPermissionException;
+import javax.naming.directory.DirContext;
+
+
+/**
+ * Some tests to make sure users in the cn=Administrators,ou=groups,ou=system 
+ * group behave as admin like users will full access rights.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+@RunWith ( CiRunner.class )
+public class AdministratorsGroupIT
+{
+    public static DirectoryService service;
+
+
+    boolean canReadAdministrators( DirContext ctx ) throws NamingException
+    {
+        try
+        {
+            ctx.getAttributes( "cn=Administrators,ou=groups" );
+            return true;
+        }
+        catch ( NoPermissionException e )
+        {
+            return false;
+        }
+    }
+
+
+    /**
+     * Checks to make sure a non-admin user which is not in the Administrators 
+     * group cannot access entries under ou=groups,ou=system.  Also check that 
+     * after adding that user to the group they see those groups.  This test 
+     * does NOT use the DefaultAuthorizationInterceptor but uses the one based on
+     * ACI.
+     * 
+     * @throws Exception on failures
+     */
+    @Test
+    @Factory ( AutzIntegUtils.ServiceFactory.class )
+    public void testNonAdminReadAccessToGroups() throws Exception
+    {
+        Name billydDn = createUser( "billyd", "s3kr3t" );
+        
+        // this should fail with a no permission exception because we
+        // are not allowed to browse ou=system without an ACI 
+        try
+        {
+            getContextAs( billydDn, "s3kr3t" );
+            fail( "Should not get here since we cannot browse ou=system" );
+        }
+        catch( NoPermissionException e )
+        {
+        }
+        
+        // add billyd to administrators and try again
+        addUserToGroup( "billyd", "Administrators" );
+
+        // billyd should now be able to read ou=system and the admin group
+        DirContext ctx = getContextAs( billydDn, "s3kr3t" );
+        assertTrue( canReadAdministrators( ctx ) );
+    }
+
+
+    /**
+     * Checks to make sure a non-admin user which is not in the Administrators
+     * group cannot access entries under ou=groups,ou=system.  Also check that
+     * after adding that user to the group they see those groups.
+     *
+     * @throws Exception on failure
+     */
+    @Test
+    @Factory ( AutzIntegUtils.DefaultServiceFactory.class )
+    @Mode ( SetupMode.PRISTINE )
+    public void testDefaultNonAdminReadAccessToGroups() throws Exception
+    {
+        Name billydDn = createUser( "billyd", "s3kr3t" );
+        assertFalse( service.isAccessControlEnabled() );
+        DirContext ctx = getContextAs( billydDn, "s3kr3t" );
+
+        // billyd should not be able to read the admin group
+        assertFalse( canReadAdministrators( ctx ) );
+
+        // add billyd to administrators and try again
+        addUserToGroup( "billyd", "Administrators" );
+
+        // billyd should now be able to read the admin group
+        assertTrue( canReadAdministrators( ctx ) );
+
+        service.shutdown();
+    }
+}
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/authz/AuthorizationServiceAsAdminIT.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/authz/AuthorizationServiceAsAdminIT.java
new file mode 100644
index 0000000..7a87c40
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/authz/AuthorizationServiceAsAdminIT.java
@@ -0,0 +1,144 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.authz;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.integ.CiRunner;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getSystemContext;
+import org.apache.directory.server.core.integ.annotations.Factory;
+import org.apache.directory.shared.ldap.exception.LdapNoPermissionException;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.util.ArrayUtils;
+import org.apache.directory.shared.ldap.util.StringTools;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.LdapContext;
+import java.util.HashSet;
+
+
+/**
+ * Tests the Authorization service to make sure it is enforcing policies
+ * correctly.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+@RunWith ( CiRunner.class )
+@Factory ( AutzIntegUtils.ServiceFactory.class )
+public class AuthorizationServiceAsAdminIT
+{
+    public static DirectoryService service;
+
+
+    /**
+     * Makes sure the admin cannot delete the admin account.
+     *
+     * @throws NamingException if there are problems
+     */
+    @Test
+    public void testNoDeleteOnAdminByAdmin() throws NamingException
+    {
+        try
+        {
+            getSystemContext( service ).destroySubcontext( "uid=admin" );
+            fail( "admin should not be able to delete his account" );
+        }
+        catch ( LdapNoPermissionException e )
+        {
+            assertNotNull( e );
+        }
+    }
+
+
+    /**
+     * Makes sure the admin cannot rename the admin account.
+     *
+     * @throws NamingException if there are problems
+     */
+    @Test
+    public void testNoRdnChangesOnAdminByAdmin() throws NamingException
+    {
+        try
+        {
+            getSystemContext( service ).rename( "uid=admin", "uid=alex" );
+            fail( "admin should not be able to rename his account" );
+        }
+        catch ( LdapNoPermissionException e )
+        {
+            assertNotNull( e );
+        }
+    }
+
+
+    /**
+     * Makes sure the admin cannot rename the admin account.
+     *
+     * @throws NamingException if there are problems
+     */
+    @Test
+    public void testModifyOnAdminByAdmin() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        Attributes attributes = new AttributesImpl();
+        attributes.put( "userPassword", "replaced" );
+        sysRoot.modifyAttributes( "uid=admin", DirContext.REPLACE_ATTRIBUTE, attributes );
+        Attributes newAttrs = sysRoot.getAttributes( "uid=admin" );
+        assertTrue( ArrayUtils.isEquals( StringTools.getBytesUtf8( "replaced" ), newAttrs.get( "userPassword" ).get() ) );
+    }
+
+
+    /**
+     * Makes sure the admin can see all entries we know of on a subtree search.
+     *
+     * @throws NamingException if there are problems
+     */
+    @Test
+    public void testSearchSubtreeByAdmin() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        HashSet<String> set = new HashSet<String>();
+        NamingEnumeration<SearchResult> list = sysRoot.search( "", "(objectClass=*)", controls );
+
+        while ( list.hasMore() )
+        {
+            SearchResult result = list.next();
+            set.add( result.getName() );
+        }
+
+        assertTrue( set.contains( "ou=system" ) );
+        assertTrue( set.contains( "ou=groups,ou=system" ) );
+        assertTrue( set.contains( "ou=users,ou=system" ) );
+        assertTrue( set.contains( "uid=admin,ou=system" ) );
+    }
+}
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/authz/AuthorizationServiceAsNonAdminIT.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/authz/AuthorizationServiceAsNonAdminIT.java
new file mode 100644
index 0000000..88dc3bf
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/authz/AuthorizationServiceAsNonAdminIT.java
@@ -0,0 +1,167 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.authz;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.integ.CiRunner;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getRootContext;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getContext;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getUserAddLdif;
+import org.apache.directory.server.core.integ.annotations.Factory;
+import org.apache.directory.shared.ldap.exception.LdapNoPermissionException;
+import org.apache.directory.shared.ldap.ldif.LdifEntry;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.LdapContext;
+import java.util.HashSet;
+
+
+/**
+ * Tests the Authorization service to make sure it is enforcing policies
+ * correctly.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+@RunWith ( CiRunner.class )
+@Factory ( AutzIntegUtils.ServiceFactory.class )
+public class AuthorizationServiceAsNonAdminIT 
+{
+    public static DirectoryService service;
+
+
+    /**
+     * Makes sure a non-admin user cannot delete the admin account.
+     *
+     * @throws NamingException if there are problems
+     */
+    @Test
+    public void testNoDeleteOnAdminByNonAdmin() throws NamingException
+    {
+        LdifEntry akarasulu = getUserAddLdif();
+        getRootContext( service ).createSubcontext( akarasulu.getDn(), akarasulu.getAttributes() );
+
+        try
+        {
+            getContext( akarasulu.getDn(), service, "ou=system" ).destroySubcontext( "uid=admin" );
+            fail( "User 'admin' should not be able to delete his account" );
+        }
+        catch ( LdapNoPermissionException e )
+        {
+            assertNotNull( e );
+        }
+    }
+
+
+    /**
+     * Makes sure a non-admin user cannot rename the admin account.
+     *
+     * @throws NamingException if there are problems
+     */
+    @Test
+    public void testNoRdnChangesOnAdminByNonAdmin() throws NamingException
+    {
+        LdifEntry akarasulu = getUserAddLdif();
+        getRootContext( service ).createSubcontext( akarasulu.getDn(), akarasulu.getAttributes() );
+        LdapContext sysRoot = getContext( akarasulu.getDn(), service, "ou=system" );
+
+        try
+        {
+            sysRoot.rename( "uid=admin", "uid=alex" );
+            fail( "admin should not be able to rename his account" );
+        }
+        catch ( LdapNoPermissionException e )
+        {
+            assertNotNull( e );
+        }
+    }
+
+
+    /**
+     * Makes sure the a non-admin user cannot rename the admin account.
+     *
+     * @throws NamingException on error
+     */
+    @Test
+    public void testModifyOnAdminByNonAdmin() throws NamingException
+    {
+        LdifEntry akarasulu = getUserAddLdif();
+        getRootContext( service ).createSubcontext( akarasulu.getDn(), akarasulu.getAttributes() );
+        LdapContext sysRoot = getContext( akarasulu.getDn(), service, "ou=system" );
+
+        Attributes attributes = new AttributesImpl();
+        attributes.put( "userPassword", "replaced" );
+
+        //noinspection EmptyCatchBlock
+        try
+        {
+            sysRoot.modifyAttributes( "uid=admin", DirContext.REPLACE_ATTRIBUTE, attributes );
+            fail( "User 'uid=admin,ou=system' should not be able to modify attributes on admin" );
+        }
+        catch ( Exception e )
+        {
+        }
+    }
+
+
+    /**
+     * Makes sure the admin can see all entries we know of on a subtree search.
+     *
+     * @throws NamingException if there are problems
+     */
+    @Test
+    public void testSearchSubtreeByNonAdmin() throws NamingException
+    {
+        LdifEntry akarasulu = getUserAddLdif();
+        getRootContext( service ).createSubcontext( akarasulu.getDn(), akarasulu.getAttributes() );
+        LdapContext sysRoot = getContext( akarasulu.getDn(), service, "ou=system" );
+
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+
+        HashSet<String> set = new HashSet<String>();
+        NamingEnumeration<SearchResult> list = sysRoot.search( "", "(objectClass=*)", controls );
+        
+        while ( list.hasMore() )
+        {
+            SearchResult result = list.next();
+            set.add( result.getName() );
+        }
+
+        // @todo this assertion fails now - is this the expected behavoir?
+//        assertTrue( set.contains( "ou=system" ) );
+//        assertTrue( set.contains( "ou=groups,ou=system" ) );
+//        assertFalse( set.contains( "cn=administrators,ou=groups,ou=system" ) );
+//        assertTrue( set.contains( "ou=users,ou=system" ) );
+//        assertFalse( set.contains( "uid=akarasulu,ou=users,ou=system" ) );
+//        assertFalse( set.contains( "uid=admin,ou=system" ) );
+    }
+}
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/authz/AuthzAuthnIT.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/authz/AuthzAuthnIT.java
new file mode 100644
index 0000000..22ba7d5
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/authz/AuthzAuthnIT.java
@@ -0,0 +1,138 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.authz;
+
+
+import junit.framework.Assert;
+import org.apache.directory.shared.ldap.exception.LdapNoPermissionException;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.server.core.integ.CiRunner;
+import org.apache.directory.server.core.integ.annotations.*;
+import org.apache.directory.server.core.DirectoryService;
+import org.junit.runner.RunWith;
+
+import javax.naming.NamingException;
+import static org.junit.Assert.fail;
+import org.junit.Test;
+import static org.apache.directory.server.core.authz.AutzIntegUtils.createUser;
+import static org.apache.directory.server.core.authz.AutzIntegUtils.getContextAs;
+import static org.apache.directory.server.core.authz.AutzIntegUtils.createAccessControlSubentry;
+
+
+/**
+ * Tests whether or not authentication with authorization works properly.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+@RunWith ( CiRunner.class )
+@Factory ( AutzIntegUtils.ServiceFactory.class )
+public class AuthzAuthnIT
+{
+    public static DirectoryService service;
+
+
+    /**
+     * Checks to make sure a user can authenticate with RootDSE as the
+     * provider URL without need of any access control permissions.
+     *
+     * @throws javax.naming.NamingException if the test encounters an error
+     */
+    @Test
+    public void testAuthnWithRootDSE() throws NamingException
+    {
+        createUser( "billyd", "billyd" );
+
+        LdapDN userName = new LdapDN( "uid=billyd,ou=users,ou=system" ); 
+        try
+        {
+            // Authenticate to RootDSE
+            getContextAs( userName, "billyd", "" );
+        }
+        catch ( LdapNoPermissionException e )
+        {
+            fail( "Authentication should not have failed." );
+        }
+    }
+    
+    
+    /**
+     * Checks to make sure a user cannot authenticate with a naming context
+     * as the provider URL if it does not have appropriate Browse permissions.
+     *
+     * @throws javax.naming.NamingException if the test encounters an error
+     */
+    @Test
+    public void testAuthnFailsWithSystemPartition() throws NamingException
+    {
+        createUser( "billyd", "billyd" );
+        
+        LdapDN userName = new LdapDN( "uid=billyd,ou=users,ou=system" ); 
+        try
+        {
+            // Authenticate to "ou=system"
+            getContextAs( userName, "billyd", "ou=system" );
+            fail( "Authentication should have failed." );
+        }
+        catch ( LdapNoPermissionException e )
+        {
+            Assert.assertNotNull( e ); 
+        }
+    }
+    
+    
+    /**
+     * Checks to make sure a user can authenticate with a naming context
+     * as the provider URL if it has appropriate Browse permissions.
+     *
+     * @throws javax.naming.NamingException if the test encounters an error
+     */
+    @Test
+    public void testAuthnPassesWithSystemPartition() throws NamingException
+    {
+        createUser( "billyd", "billyd" );
+        
+        // Create ACI with minimum level of required privileges:
+        // Only for user "uid=billyd,ou=users,ou=system"
+        // Only to The entry "ou=system"
+        // Only Browse permission
+        // Note: In order to read contents of the bound context
+        //       user will need appropriate Read permissions.
+        createAccessControlSubentry(
+            "grantBrowseForTheWholeNamingContext",
+            "{ maximum 0 }", // !!!!! Replace this with "{ minimum 1 }" for practicing !
+            "{ " + "identificationTag \"browseACI\", "
+            + "precedence 14, " + "authenticationLevel none, " + "itemOrUserFirst userFirst: { "
+            + "userClasses { name { \"uid=billyd,ou=users,ou=system\" } }, " + "userPermissions { { "
+            + "protectedItems { entry }, "
+            + "grantsAndDenials { grantBrowse } } } } }" );
+        
+        LdapDN userName = new LdapDN( "uid=billyd,ou=users,ou=system" ); 
+        try
+        {
+            // Authenticate to "ou=system"
+            getContextAs( userName, "billyd", "ou=system" );
+        }
+        catch ( LdapNoPermissionException e )
+        {
+            fail( "Authentication should not have failed." );
+        }
+    }
+}
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/authz/AuthzISuite.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/authz/AuthzISuite.java
new file mode 100644
index 0000000..9ee4cfb
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/authz/AuthzISuite.java
@@ -0,0 +1,54 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements.  See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership.  The ASF licenses this file

+ * to you 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.

+ */

+package org.apache.directory.server.core.authz;

+

+

+import org.apache.directory.server.core.integ.CiSuite;

+import org.apache.directory.server.core.integ.Level;

+import org.apache.directory.server.core.integ.annotations.*;

+import org.junit.runner.RunWith;

+import org.junit.runners.Suite;

+

+

+/**

+ * Document me!

+ *

+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>

+ * @version $Rev$, $Date$

+ */

+@RunWith ( CiSuite.class )

+@Suite.SuiteClasses ( {

+        AddAuthorizationIT.class,

+        AuthorizationServiceAsAdminIT.class,

+        AuthorizationServiceAsNonAdminIT.class,

+        AuthzAuthnIT.class,

+        CompareAuthorizationIT.class,

+        DeleteAuthorizationIT.class,

+        GeneralAuthorizationIT.class,

+        ModifyAuthorizationIT.class,

+        MoveRenameAuthorizationIT.class,

+        SearchAuthorizationIT.class,

+        AdministratorsGroupIT.class     // make sure this always runs last since it leaves

+                                        // the default factory service running instead of

+                                        // one with 

+        } )

+@CleanupLevel ( Level.SUITE )

+public class AuthzISuite

+{

+}
\ No newline at end of file
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/authz/AutzIntegUtils.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/authz/AutzIntegUtils.java
new file mode 100644
index 0000000..c1dba4b
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/authz/AutzIntegUtils.java
@@ -0,0 +1,387 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.authz;
+
+
+import org.apache.directory.server.constants.ServerDNConstants;
+import org.apache.directory.server.core.DefaultDirectoryService;
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.integ.DirectoryServiceFactory;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getSystemContext;
+import org.apache.directory.server.core.subtree.SubentryInterceptor;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+import javax.naming.Name;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.InitialDirContext;
+import javax.naming.ldap.LdapContext;
+import java.util.Hashtable;
+
+
+/**
+ * Some extra utility methods added to it which are required by all
+ * authorization tests.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class AutzIntegUtils
+{
+    public static DirectoryService service;
+
+
+    public static class ServiceFactory implements DirectoryServiceFactory
+    {
+        public DirectoryService newInstance() 
+        {
+            DefaultDirectoryService service = new DefaultDirectoryService();
+            service.setAccessControlEnabled( true );
+            service.getChangeLog().setEnabled( true );
+            AutzIntegUtils.service = service;
+            return service;
+        }
+    }
+
+
+    public static class DefaultServiceFactory implements DirectoryServiceFactory
+    {
+        public DirectoryService newInstance() 
+        {
+            DefaultDirectoryService service = new DefaultDirectoryService();
+            service.setAccessControlEnabled( false );
+            service.getChangeLog().setEnabled( true );
+            AutzIntegUtils.service = service;
+            return service;
+        }
+    }
+
+
+    // -----------------------------------------------------------------------
+    // Utility methods used by subclasses
+    // -----------------------------------------------------------------------
+
+    /**
+     * Gets a context at ou=system as the admin user.
+     *
+     * @return the admin context at ou=system
+     * @throws NamingException if there are problems creating the context
+     */
+    public static DirContext getContextAsAdmin() throws NamingException
+    {
+        return getSystemContext( service );
+    }
+
+
+    /**
+     * Gets a context at some dn within the directory as the admin user.
+     * Should be a dn of an entry under ou=system since no other partitions
+     * are enabled.
+     *
+     * @param dn the DN of the context to get
+     * @return the context for the DN as the admin user
+     * @throws NamingException if is a problem initializing or getting the context
+     */
+    @SuppressWarnings("unchecked")
+    public static DirContext getContextAsAdmin( String dn ) throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        Hashtable<String,Object> env = ( Hashtable<String,Object> ) sysRoot.getEnvironment().clone();
+        env.put( DirContext.PROVIDER_URL, dn );
+        env.put( DirContext.SECURITY_AUTHENTICATION, "simple" );
+        env.put( DirContext.SECURITY_PRINCIPAL, "uid=admin, ou=system" );
+        env.put( DirContext.SECURITY_CREDENTIALS, "secret" );
+        env.put( DirContext.INITIAL_CONTEXT_FACTORY, "org.apache.directory.server.core.jndi.CoreContextFactory" );
+        env.put( DirectoryService.JNDI_KEY, service );
+        return new InitialDirContext( env );
+    }
+
+
+    /**
+     * Creates a group using the groupOfUniqueNames objectClass under the
+     * ou=groups,ou=sytem container with an initial member.
+     *
+     * @param cn the common name of the group used as the RDN attribute
+     * @param firstMemberDn the DN of the first member of this group
+     * @return the distinguished name of the group entry
+     * @throws NamingException if there are problems creating the new group like
+     * it exists already
+     */
+    public static Name createGroup( String cn, String firstMemberDn ) throws NamingException
+    {
+        DirContext adminCtx = getContextAsAdmin();
+        Attributes group = new AttributesImpl( "cn", cn, true );
+        Attribute objectClass = new AttributeImpl( "objectClass" );
+        group.put( objectClass );
+        objectClass.add( "top" );
+        objectClass.add( "groupOfUniqueNames" );
+        group.put( "uniqueMember", firstMemberDn );
+        adminCtx.createSubcontext( "cn=" + cn + ",ou=groups", group );
+        return new LdapDN( "cn=" + cn + ",ou=groups,ou=system" );
+    }
+
+
+    /**
+     * Deletes a user with a specific UID under ou=users,ou=system.
+     *
+     * @param uid the RDN value for the user to delete
+     * @throws NamingException if there are problems removing the user
+     * i.e. user does not exist
+     */
+    public static void deleteUser( String uid ) throws NamingException
+    {
+        DirContext adminCtx = getContextAsAdmin();
+        adminCtx.destroySubcontext( "uid=" + uid + ",ou=users" );
+    }
+
+
+    /**
+     * Creates a simple user as an inetOrgPerson under the ou=users,ou=system
+     * container.  The user's RDN attribute is the uid argument.  This argument
+     * is also used as the value of the two MUST attributes: sn and cn.
+     *
+     * @param uid the value of the RDN attriubte (uid), the sn and cn attributes
+     * @param password the password to use to create the user
+     * @return the dn of the newly created user entry
+     * @throws NamingException if there are problems creating the user entry
+     */
+    public static Name createUser( String uid, String password ) throws NamingException
+    {
+        DirContext adminCtx = getContextAsAdmin();
+        Attributes user = new AttributesImpl( "uid", uid, true );
+        user.put( "userPassword", password );
+        Attribute objectClass = new AttributeImpl( "objectClass" );
+        user.put( objectClass );
+        objectClass.add( "top" );
+        objectClass.add( "person" );
+        objectClass.add( "organizationalPerson" );
+        objectClass.add( "inetOrgPerson" );
+        user.put( "sn", uid );
+        user.put( "cn", uid );
+        adminCtx.createSubcontext( "uid=" + uid + ",ou=users", user );
+        return new LdapDN( "uid=" + uid + ",ou=users,ou=system" );
+    }
+
+
+    /**
+     * Creates a simple groupOfUniqueNames under the ou=groups,ou=system
+     * container.  The admin user is always a member of this newly created 
+     * group.
+     *
+     * @param groupName the name of the cgroup to create
+     * @return the DN of the group as a Name object
+     * @throws NamingException if the group cannot be created
+     */
+    public static Name createGroup( String groupName ) throws NamingException
+    {
+        DirContext adminCtx = getContextAsAdmin();
+        Attributes group = new AttributesImpl( true );
+        Attribute objectClass = new AttributeImpl( "objectClass" );
+        group.put( objectClass );
+        objectClass.add( "top" );
+        objectClass.add( "groupOfUniqueNames" );
+        
+        // TODO might be ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED
+        group.put( "uniqueMember", "uid=admin, ou=system" );
+        adminCtx.createSubcontext( "cn=" + groupName + ",ou=groups", group );
+        return new LdapDN( "cn=" + groupName + ",ou=groups,ou=system" );
+    }
+
+
+    /**
+     * Adds an existing user under ou=users,ou=system to an existing group under the
+     * ou=groups,ou=system container.
+     *
+     * @param userUid the uid of the user to add to the group
+     * @param groupCn the cn of the group to add the user to
+     * @throws NamingException if the group does not exist
+     */
+    public static void addUserToGroup( String userUid, String groupCn ) throws NamingException
+    {
+        DirContext adminCtx = getContextAsAdmin();
+        Attributes changes = new AttributesImpl( "uniqueMember", "uid=" + userUid + ",ou=users,ou=system", true );
+        adminCtx.modifyAttributes( "cn=" + groupCn + ",ou=groups", DirContext.ADD_ATTRIBUTE, changes );
+    }
+
+
+    /**
+     * Removes a user from a group.
+     *
+     * @param userUid the RDN attribute value of the user to remove from the group
+     * @param groupCn the RDN attribute value of the group to have user removed from
+     * @throws NamingException if there are problems accessing the group
+     */
+    public static void removeUserFromGroup( String userUid, String groupCn ) throws NamingException
+    {
+        DirContext adminCtx = getContextAsAdmin();
+        Attributes changes = new AttributesImpl( "uniqueMember", "uid=" + userUid + ",ou=users,ou=system", true );
+        adminCtx.modifyAttributes( "cn=" + groupCn + ",ou=groups", DirContext.REMOVE_ATTRIBUTE, changes );
+    }
+
+
+    /**
+     * Gets the context at ou=system as a specific user.
+     *
+     * @param user the DN of the user to get the context as
+     * @param password the password of the user
+     * @return the context as the user
+     * @throws NamingException if the user does not exist or authx fails
+     */
+    public static DirContext getContextAs( Name user, String password ) throws NamingException
+    {
+        return getContextAs( user, password, ServerDNConstants.SYSTEM_DN );
+    }
+
+
+    /**
+     * Gets the context at any DN under ou=system as a specific user.
+     *
+     * @param user the DN of the user to get the context as
+     * @param password the password of the user
+     * @param dn the distinguished name of the entry to get the context for
+     * @return the context representing the entry at the dn as a specific user
+     * @throws NamingException if the does not exist or authx fails
+     */
+    @SuppressWarnings("unchecked")
+    public static DirContext getContextAs( Name user, String password, String dn ) throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        Hashtable<String,Object> env = ( Hashtable<String,Object> ) sysRoot.getEnvironment().clone();
+        env.put( DirContext.PROVIDER_URL, dn );
+        env.put( DirContext.SECURITY_AUTHENTICATION, "simple" );
+        env.put( DirContext.SECURITY_PRINCIPAL, user.toString() );
+        env.put( DirContext.SECURITY_CREDENTIALS, password );
+        env.put( DirContext.INITIAL_CONTEXT_FACTORY, "org.apache.directory.server.core.jndi.CoreContextFactory" );
+        env.put( DirectoryService.JNDI_KEY, service );
+        return new InitialDirContext( env );
+    }
+
+
+    public static void deleteAccessControlSubentry( String cn ) throws NamingException
+    {
+        DirContext adminCtx = getContextAsAdmin();
+        adminCtx.destroySubcontext( "cn=" + cn );
+    }
+
+
+    /**
+     * Creates an access control subentry under ou=system whose subtree covers
+     * the entire naming context.
+     *
+     * @param cn the common name and rdn for the subentry
+     * @param aciItem the prescriptive ACI attribute value
+     * @throws NamingException if there is a problem creating the subentry
+     */
+    public static void createAccessControlSubentry( String cn, String aciItem ) throws NamingException
+    {
+        createAccessControlSubentry( cn, "{}", aciItem );
+    }
+
+
+    /**
+     * Creates an access control subentry under ou=system whose subtree covers
+     * the entire naming context.
+     *
+     * @param cn the common name and rdn for the subentry
+     * @param subtree the subtreeSpecification for the subentry
+     * @param aciItem the prescriptive ACI attribute value
+     * @throws NamingException if there is a problem creating the subentry
+     */
+    public static void createAccessControlSubentry( String cn, String subtree, String aciItem ) throws NamingException
+    {
+        DirContext adminCtx = getContextAsAdmin();
+
+        // modify ou=system to be an AP for an A/C AA if it is not already
+        Attributes ap = adminCtx.getAttributes( "", new String[]
+            { "administrativeRole" } );
+        Attribute administrativeRole = ap.get( "administrativeRole" );
+        if ( administrativeRole == null || !administrativeRole.contains( SubentryInterceptor.AC_AREA ) )
+        {
+            Attributes changes = new AttributesImpl( "administrativeRole", SubentryInterceptor.AC_AREA, true );
+            adminCtx.modifyAttributes( "", DirContext.ADD_ATTRIBUTE, changes );
+        }
+
+        // now add the A/C subentry below ou=system
+        Attributes subentry = new AttributesImpl( "cn", cn, true );
+        Attribute objectClass = new AttributeImpl( "objectClass" );
+        subentry.put( objectClass );
+        objectClass.add( "top" );
+        objectClass.add( SchemaConstants.SUBENTRY_OC );
+        objectClass.add( "accessControlSubentry" );
+        subentry.put( "subtreeSpecification", subtree );
+        subentry.put( "prescriptiveACI", aciItem );
+        adminCtx.createSubcontext( "cn=" + cn, subentry );
+    }
+
+
+    /**
+     * Adds and entryACI attribute to an entry specified by a relative name
+     * with respect to ou=system
+     *
+     * @param rdn a name relative to ou=system
+     * @param aciItem the entryACI attribute value
+     * @throws NamingException if there is a problem adding the attribute
+     */
+    public static void addEntryACI( Name rdn, String aciItem ) throws NamingException
+    {
+        DirContext adminCtx = getContextAsAdmin();
+
+        // modify the entry relative to ou=system to include the aciItem
+        Attributes changes = new AttributesImpl( "entryACI", aciItem, true );
+        adminCtx.modifyAttributes( rdn, DirContext.ADD_ATTRIBUTE, changes );
+    }
+
+
+    /**
+     * Adds and subentryACI attribute to ou=system
+     *
+     * @param aciItem the subentryACI attribute value
+     * @throws NamingException if there is a problem adding the attribute
+     */
+    public static void addSubentryACI( String aciItem ) throws NamingException
+    {
+        DirContext adminCtx = getContextAsAdmin();
+
+        // modify the entry relative to ou=system to include the aciItem
+        Attributes changes = new AttributesImpl( "subentryACI", aciItem, true );
+        adminCtx.modifyAttributes( "", DirContext.ADD_ATTRIBUTE, changes );
+    }
+    
+    
+    /**
+     * Replaces values of an prescriptiveACI attribute of a subentry subordinate
+     * to ou=system.
+     *
+     * @param cn the common name of the aci subentry
+     * @param aciItem the new value for the ACI item
+     * @throws NamingException if the modify fails
+     */
+    public static void changePresciptiveACI( String cn, String aciItem ) throws NamingException
+    {
+        DirContext adminCtx = getContextAsAdmin();
+        Attributes changes = new AttributesImpl( "prescriptiveACI", aciItem );
+        adminCtx.modifyAttributes( "cn=" + cn, DirContext.REPLACE_ATTRIBUTE, changes );
+    }
+}
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/authz/CompareAuthorizationIT.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/authz/CompareAuthorizationIT.java
new file mode 100644
index 0000000..74cacf9
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/authz/CompareAuthorizationIT.java
@@ -0,0 +1,284 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.authz;
+
+
+import org.apache.directory.server.core.jndi.ServerLdapContext;
+import org.apache.directory.server.core.integ.CiRunner;
+import org.apache.directory.server.core.integ.annotations.Factory;
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.shared.ldap.exception.LdapNoPermissionException;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.junit.runner.RunWith;
+
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+import org.junit.Test;
+import static org.apache.directory.server.core.authz.AutzIntegUtils.createUser;
+import static org.apache.directory.server.core.authz.AutzIntegUtils.getContextAs;
+import static org.apache.directory.server.core.authz.AutzIntegUtils.getContextAsAdmin;
+import static org.apache.directory.server.core.authz.AutzIntegUtils.createAccessControlSubentry;
+import static org.apache.directory.server.core.authz.AutzIntegUtils.addUserToGroup;
+
+
+/**
+ * Tests whether or not authorization around entry compare operations work properly.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+@RunWith ( CiRunner.class )
+@Factory ( AutzIntegUtils.ServiceFactory.class )
+public class CompareAuthorizationIT
+{
+    public static DirectoryService service;
+
+
+    /**
+     * Checks if an attribute of a simple entry (an organizationalUnit's telephoneNumber)
+     * with an RDN relative to ou=system can be compared by a specific non-admin user.
+     * If a permission exception is encountered it is caught and false is returned,
+     * otherwise true is returned.  The entry is deleted after being created just in case
+     * subsequent calls to this method are made in the same test case: the admin account
+     * is used to add and delete this test entry so permissions to add and delete are not
+     * required to test the compare operation by the user.
+     *
+     * @param uid the unique identifier for the user (presumed to exist under ou=users,ou=system)
+     * @param password the password of this user
+     * @param entryRdn the relative DN, relative to ou=system where entry is created
+     * for comparison test
+     * @param number the telephone number to compare to this one
+     * @return true if the entry's telephoneNumber can be compared by the user at the
+     * specified location, false otherwise.  A false compare result still returns
+     * true.
+     * @throws javax.naming.NamingException if there are problems conducting the test
+     */
+    public boolean checkCanCompareTelephoneNumberAs( String uid, String password, String entryRdn, String number )
+        throws NamingException
+    {
+        // create the entry with the telephoneNumber attribute to compare
+        Attributes testEntry = new AttributesImpl( "ou", "testou", true );
+        Attribute objectClass = new AttributeImpl( "objectClass" );
+        testEntry.put( objectClass );
+        objectClass.add( "top" );
+        objectClass.add( "organizationalUnit" );
+        testEntry.put( "telephoneNumber", "867-5309" ); // jenny don't change your number
+
+        DirContext adminContext = getContextAsAdmin();
+
+        try
+        {
+            // create the entry as admin
+            LdapDN userName = new LdapDN( "uid=" + uid + ",ou=users,ou=system" );
+            adminContext.createSubcontext( entryRdn, testEntry );
+
+            // compare the telephone numbers
+            DirContext userContext = getContextAs( userName, password );
+            ServerLdapContext ctx = ( ServerLdapContext ) userContext.lookup( "" );
+            ctx.compare( new LdapDN( entryRdn + ",ou=system" ), "telephoneNumber", number );
+
+            // don't return compare result which can be false but true since op was permitted
+            return true;
+        }
+        catch ( LdapNoPermissionException e )
+        {
+            return false;
+        }
+        finally
+        {
+            // let's clean up
+            adminContext.destroySubcontext( entryRdn );
+        }
+    }
+
+
+    /**
+     * Checks to make sure group membership based userClass works for compare operations.
+     *
+     * @throws javax.naming.NamingException if the test encounters an error
+     */
+    @Test
+    public void testGrantCompareAdministrators() throws NamingException
+    {
+        // create the non-admin user
+        createUser( "billyd", "billyd" );
+
+        // try a compare operation which should fail without any ACI
+        assertFalse( checkCanCompareTelephoneNumberAs( "billyd", "billyd", "ou=testou", "867-5309" ) );
+
+        // Gives grantCompare, and grantRead perm to all users in the Administrators group for
+        // entries and all attribute types and values
+        createAccessControlSubentry( "administratorAdd", 
+            "{ identificationTag \"addAci\", " +
+            "  precedence 14, " +
+            "  authenticationLevel none, " + 
+            "  itemOrUserFirst userFirst: { " +
+            "    userClasses { " +
+            "      userGroup { " +
+            "        \"cn=Administrators,ou=groups,ou=system\" " +
+            "      } " +
+            "    }, " + 
+            "    userPermissions { " +
+            "      { " +
+            "        protectedItems { entry, allUserAttributeTypesAndValues }, " +
+            "        grantsAndDenials { grantCompare, grantRead, grantBrowse } " +
+            "      } " +
+            "    } " +
+            "  } " +
+            "}" );
+
+        // see if we can now add that test entry which we could not before
+        // add op should still fail since billd is not in the admin group
+        assertFalse( checkCanCompareTelephoneNumberAs( "billyd", "billyd", "ou=testou", "867-5309" ) );
+
+        // now add billyd to the Administrator group and try again
+        addUserToGroup( "billyd", "Administrators" );
+
+        // try an add operation which should succeed with ACI and group membership change
+        assertTrue( checkCanCompareTelephoneNumberAs( "billyd", "billyd", "ou=testou", "976-6969" ) );
+    }
+
+
+    /**
+     * Checks to make sure name based userClass works for compare operations.
+     *
+     * @throws javax.naming.NamingException if the test encounters an error
+     */
+    @Test
+    public void testGrantCompareByName() throws NamingException
+    {
+        // create the non-admin user
+        createUser( "billyd", "billyd" );
+
+        // try an compare operation which should fail without any ACI
+        assertFalse( checkCanCompareTelephoneNumberAs( "billyd", "billyd", "ou=testou", "867-5309" ) );
+
+        // now add a subentry that enables user billyd to compare an entry below ou=system
+        createAccessControlSubentry( "billydAdd", 
+            "{ " +
+            "  identificationTag \"addAci\", precedence 14, authenticationLevel none, itemOrUserFirst userFirst: " + 
+            "  { " +
+            "    userClasses " +
+            "    { " +
+            "      name " +
+            "      { " +
+            "        \"uid=billyd,ou=users,ou=system\" " +
+            "      } " +
+            "    }, " +
+            "    userPermissions " +
+            "    { " +
+            "      { " +
+            "        protectedItems " +
+            "        {" +
+            "          entry, allUserAttributeTypesAndValues" +
+            "        }, " +
+            "        grantsAndDenials " +
+            "        { " +
+            "          grantCompare, grantRead, grantBrowse " +
+            "        } " +
+            "      } " +
+            "    } " +
+            "  } " +
+            "}" );
+
+        // should work now that billyd is authorized by name
+        assertTrue( checkCanCompareTelephoneNumberAs( "billyd", "billyd", "ou=testou", "867-5309" ) );
+    }
+
+
+    /**
+     * Checks to make sure subtree based userClass works for compare operations.
+     *
+     * @throws javax.naming.NamingException if the test encounters an error
+     */
+    @Test
+    public void testGrantCompareBySubtree() throws NamingException
+    {
+        // create the non-admin user
+        createUser( "billyd", "billyd" );
+
+        // try a compare operation which should fail without any ACI
+        assertFalse( checkCanCompareTelephoneNumberAs( "billyd", "billyd", "ou=testou", "867-5309" ) );
+
+        // now add a subentry that enables user billyd to compare an entry below ou=system
+        createAccessControlSubentry( "billyAddBySubtree", "{ " + "identificationTag \"addAci\", " + "precedence 14, "
+            + "authenticationLevel none, " + "itemOrUserFirst userFirst: { "
+            + "userClasses { subtree { { base \"ou=users,ou=system\" } } }, " + "userPermissions { { "
+            + "protectedItems {entry, allUserAttributeTypesAndValues}, "
+            + "grantsAndDenials { grantCompare, grantRead, grantBrowse } } } } }" );
+
+        // should work now that billyd is authorized by the subtree userClass
+        assertTrue( checkCanCompareTelephoneNumberAs( "billyd", "billyd", "ou=testou", "867-5309" ) );
+    }
+
+
+    /**
+     * Checks to make sure <b>allUsers</b> userClass works for compare operations.
+     *
+     * @throws javax.naming.NamingException if the test encounters an error
+     */
+    @Test
+    public void testGrantCompareAllUsers() throws NamingException
+    {
+        // create the non-admin user
+        createUser( "billyd", "billyd" );
+
+        // try an add operation which should fail without any ACI
+        assertFalse( checkCanCompareTelephoneNumberAs( "billyd", "billyd", "ou=testou", "867-5309" ) );
+
+        // now add a subentry that enables anyone to add an entry below ou=system
+        createAccessControlSubentry( "anybodyAdd", "{ " + "identificationTag \"addAci\", " + "precedence 14, "
+            + "authenticationLevel none, " + "itemOrUserFirst userFirst: { " + "userClasses { allUsers }, "
+            + "userPermissions { { " + "protectedItems {entry, allUserAttributeTypesAndValues}, "
+            + "grantsAndDenials { grantCompare, grantRead, grantBrowse } } } } }" );
+
+        // see if we can now compare that test entry's number which we could not before
+        // should work with billyd now that all users are authorized
+        assertTrue( checkCanCompareTelephoneNumberAs( "billyd", "billyd", "ou=testou", "867-5309" ) );
+    }
+
+
+    @Test
+    public void testPasswordCompare() throws NamingException
+    {
+        DirContext adminCtx = getContextAsAdmin();
+        Attributes user = new AttributesImpl( "uid", "bob", true );
+        user.put( "userPassword", "bobspassword" );
+        Attribute objectClass = new AttributeImpl( "objectClass" );
+        user.put( objectClass );
+        objectClass.add( "top" );
+        objectClass.add( "person" );
+        objectClass.add( "organizationalPerson" );
+        objectClass.add( "inetOrgPerson" );
+        user.put( "sn", "bob" );
+        user.put( "cn", "bob" );
+        adminCtx.createSubcontext( "uid=bob,ou=users", user );
+
+        ServerLdapContext ctx = ( ServerLdapContext ) adminCtx.lookup( "" );
+        assertTrue( ctx.compare( new LdapDN( "uid=bob,ou=users,ou=system" ), "userPassword", "bobspassword" ) );
+    }
+
+}
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/authz/DeleteAuthorizationIT.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/authz/DeleteAuthorizationIT.java
new file mode 100644
index 0000000..928350a
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/authz/DeleteAuthorizationIT.java
@@ -0,0 +1,216 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.authz;
+
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+
+import org.apache.directory.shared.ldap.exception.LdapNoPermissionException;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.server.core.integ.CiRunner;
+import org.apache.directory.server.core.integ.annotations.Factory;
+import org.apache.directory.server.core.DirectoryService;
+import org.junit.runner.RunWith;
+
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+import org.junit.Test;
+import static org.apache.directory.server.core.authz.AutzIntegUtils.createUser;
+import static org.apache.directory.server.core.authz.AutzIntegUtils.getContextAs;
+import static org.apache.directory.server.core.authz.AutzIntegUtils.getContextAsAdmin;
+import static org.apache.directory.server.core.authz.AutzIntegUtils.createAccessControlSubentry;
+import static org.apache.directory.server.core.authz.AutzIntegUtils.addUserToGroup;
+
+
+/**
+ * Tests whether or not authorization rules for entry deletion works properly.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+@RunWith ( CiRunner.class )
+@Factory ( AutzIntegUtils.ServiceFactory.class )
+public class DeleteAuthorizationIT 
+{
+    public static DirectoryService service;
+
+
+    /**
+     * Checks if a simple entry (organizationalUnit) can be deleted from the DIT at an
+     * RDN relative to ou=system by a specific non-admin user.  The entry is first
+     * created using the admin account which can do anything without limitations.
+     * After creating the entry as the admin an attempt is made to delete it as the
+     * specified user.
+     *
+     * If a permission exception is encountered it is caught and false is returned,
+     * otherwise true is returned when the entry is created.  The entry is deleted by the
+     * admin user after a delete failure to make sure the entry is deleted if subsequent
+     * calls are made to this method: the admin account is used to delete this test entry
+     * so permissions to delete are not required to delete it by the specified user.
+     *
+     * @param uid the unique identifier for the user (presumed to exist under ou=users,ou=system)
+     * @param password the password of this user
+     * @param entryRdn the relative DN, relative to ou=system where entry creation then deletion is tested
+     * @return true if the entry can be created by the user at the specified location, false otherwise
+     * @throws javax.naming.NamingException if there are problems conducting the test
+     */
+    public boolean checkCanDeleteEntryAs( String uid, String password, String entryRdn ) throws NamingException
+    {
+        Attributes testEntry = new AttributesImpl( "ou", "testou", true );
+        Attribute objectClass = new AttributeImpl( "objectClass" );
+        testEntry.put( objectClass );
+        objectClass.add( "top" );
+        objectClass.add( "organizationalUnit" );
+
+        DirContext adminContext = getContextAsAdmin();
+        try
+        {
+            // create the entry as the admin
+            LdapDN userName = new LdapDN( "uid=" + uid + ",ou=users,ou=system" );
+            adminContext.createSubcontext( entryRdn, testEntry );
+
+            // delete the newly created context as the user
+            DirContext userContext = getContextAs( userName, password );
+            userContext.destroySubcontext( entryRdn );
+
+            return true;
+        }
+        catch ( LdapNoPermissionException e )
+        {
+            adminContext.destroySubcontext( entryRdn );
+            return false;
+        }
+    }
+
+
+    /**
+     * Checks to make sure group membership based userClass works for delete operations.
+     *
+     * @throws javax.naming.NamingException if the test encounters an error
+     */
+    @Test
+    public void testGrantDeleteAdministrators() throws NamingException
+    {
+        // create the non-admin user
+        createUser( "billyd", "billyd" );
+
+        // try a delete operation which should fail without any ACI
+        assertFalse( checkCanDeleteEntryAs( "billyd", "billyd", "ou=testou" ) );
+
+        // Gives grantRemove perm to all users in the Administrators group for
+        // entries and all attribute types and values
+        createAccessControlSubentry( "administratorAdd", "{ " + "identificationTag \"addAci\", " + "precedence 14, "
+            + "authenticationLevel none, " + "itemOrUserFirst userFirst: { "
+            + "userClasses { userGroup { \"cn=Administrators,ou=groups,ou=system\" } }, " + "userPermissions { { "
+            + "protectedItems {entry}, " + "grantsAndDenials { grantRemove, grantBrowse } } } } }" );
+
+        // see if we can now delete that test entry which we could not before
+        // delete op should still fail since billd is not in the admin group
+        assertFalse( checkCanDeleteEntryAs( "billyd", "billyd", "ou=testou" ) );
+
+        // now add billyd to the Administrator group and try again
+        addUserToGroup( "billyd", "Administrators" );
+
+        // try a delete operation which should succeed with ACI and group membership change
+        assertTrue( checkCanDeleteEntryAs( "billyd", "billyd", "ou=testou" ) );
+    }
+
+
+    /**
+     * Checks to make sure name based userClass works for delete operations.
+     *
+     * @throws javax.naming.NamingException if the test encounters an error
+     */
+    @Test
+    public void testGrantDeleteByName() throws NamingException
+    {
+        // create the non-admin user
+        createUser( "billyd", "billyd" );
+
+        // try a delete operation which should fail without any ACI
+        assertFalse( checkCanDeleteEntryAs( "billyd", "billyd", "ou=testou" ) );
+
+        // now add a subentry that enables user billyd to delete an entry below ou=system
+        createAccessControlSubentry( "billydAdd", "{ " + "identificationTag \"addAci\", " + "precedence 14, "
+            + "authenticationLevel none, " + "itemOrUserFirst userFirst: { "
+            + "userClasses { name { \"uid=billyd,ou=users,ou=system\" } }, " + "userPermissions { { "
+            + "protectedItems {entry}, " + "grantsAndDenials { grantRemove, grantBrowse } } } } }" );
+
+        // should work now that billyd is authorized by name
+        assertTrue( checkCanDeleteEntryAs( "billyd", "billyd", "ou=testou" ) );
+    }
+
+
+    /**
+     * Checks to make sure subtree based userClass works for delete operations.
+     *
+     * @throws javax.naming.NamingException if the test encounters an error
+     */
+    @Test
+    public void testGrantDeleteBySubtree() throws NamingException
+    {
+        // create the non-admin user
+        createUser( "billyd", "billyd" );
+
+        // try a delete operation which should fail without any ACI
+        assertFalse( checkCanDeleteEntryAs( "billyd", "billyd", "ou=testou" ) );
+
+        // now add a subentry that enables user billyd to delte an entry below ou=system
+        createAccessControlSubentry( "billyAddBySubtree", "{ " + "identificationTag \"addAci\", " + "precedence 14, "
+            + "authenticationLevel none, " + "itemOrUserFirst userFirst: { "
+            + "userClasses { subtree { { base \"ou=users,ou=system\" } } }, " + "userPermissions { { "
+            + "protectedItems {entry}, " + "grantsAndDenials { grantRemove, grantBrowse } } } } }" );
+
+        // should work now that billyd is authorized by the subtree userClass
+        assertTrue( checkCanDeleteEntryAs( "billyd", "billyd", "ou=testou" ) );
+    }
+
+
+    /**
+     * Checks to make sure <b>allUsers</b> userClass works for delete operations.
+     *
+     * @throws javax.naming.NamingException if the test encounters an error
+     */
+    @Test
+    public void testGrantDeleteAllUsers() throws NamingException
+    {
+        // create the non-admin user
+        createUser( "billyd", "billyd" );
+
+        // try a delete operation which should fail without any ACI
+        assertFalse( checkCanDeleteEntryAs( "billyd", "billyd", "ou=testou" ) );
+
+        // now add a subentry that enables anyone to add an entry below ou=system
+        createAccessControlSubentry( "anybodyAdd", "{ " + "identificationTag \"addAci\", " + "precedence 14, "
+            + "authenticationLevel none, " + "itemOrUserFirst userFirst: { " + "userClasses { allUsers }, "
+            + "userPermissions { { " + "protectedItems {entry}, "
+            + "grantsAndDenials { grantRemove, grantBrowse } } } } }" );
+
+        // see if we can now delete that test entry which we could not before
+        // should work now with billyd now that all users are authorized
+        assertTrue( checkCanDeleteEntryAs( "billyd", "billyd", "ou=testou" ) );
+    }
+}
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/authz/GeneralAuthorizationIT.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/authz/GeneralAuthorizationIT.java
new file mode 100644
index 0000000..5304898
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/authz/GeneralAuthorizationIT.java
@@ -0,0 +1,73 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.authz;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import static org.apache.directory.server.core.authz.AutzIntegUtils.createAccessControlSubentry;
+import org.apache.directory.server.core.integ.CiRunner;
+import org.apache.directory.server.core.integ.annotations.Factory;
+import org.apache.directory.shared.ldap.exception.LdapInvalidAttributeValueException;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.naming.NamingException;
+
+
+/**
+ * Tests various authorization functionality without any specific operation.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 494176 $
+ */
+@RunWith ( CiRunner.class )
+@Factory ( AutzIntegUtils.ServiceFactory.class )
+public class GeneralAuthorizationIT 
+{
+    public static DirectoryService service;
+
+
+    /**
+     * Checks to make sure we cannot create a malformed ACI missing two
+     * last brackets.
+     *
+     * @throws NamingException if the test encounters an error
+     */
+    @Test
+    public void testFailureToAddBadACI() throws NamingException
+    {
+        // add a subentry with malformed ACI
+        try
+        {
+            createAccessControlSubentry( "anybodyAdd", "{ " + "identificationTag \"addAci\", " + "precedence 14, "
+                + "authenticationLevel none, " + "itemOrUserFirst userFirst: { " + "userClasses { allUsers }, "
+                + "userPermissions { { " + "protectedItems {entry, allUserAttributeTypesAndValues}, "
+                + "grantsAndDenials { grantAdd, grantBrowse } } }" );
+            fail( "should never get here due to failure to add bad ACIItem" );
+        }
+        catch( LdapInvalidAttributeValueException e )
+        {
+            assertEquals( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, e.getResultCode() );
+        }
+    }
+}
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/authz/ModifyAuthorizationIT.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/authz/ModifyAuthorizationIT.java
new file mode 100644
index 0000000..ac8d79f
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/authz/ModifyAuthorizationIT.java
@@ -0,0 +1,633 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.authz;
+
+
+import org.apache.directory.shared.ldap.exception.LdapNoPermissionException;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.message.ModificationItemImpl;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.server.core.integ.CiRunner;
+import org.apache.directory.server.core.integ.annotations.Factory;
+import org.apache.directory.server.core.DirectoryService;
+import org.junit.runner.RunWith;
+
+import javax.naming.NamingException;
+import javax.naming.NamingEnumeration;
+import javax.naming.Name;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+
+import java.util.List;
+import java.util.ArrayList;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+import org.junit.Test;
+
+import static org.apache.directory.server.core.authz.AutzIntegUtils.createUser;
+import static org.apache.directory.server.core.authz.AutzIntegUtils.getContextAs;
+import static org.apache.directory.server.core.authz.AutzIntegUtils.getContextAsAdmin;
+import static org.apache.directory.server.core.authz.AutzIntegUtils.createAccessControlSubentry;
+import static org.apache.directory.server.core.authz.AutzIntegUtils.addUserToGroup;
+import static org.apache.directory.server.core.authz.AutzIntegUtils.deleteAccessControlSubentry;
+import static org.apache.directory.server.core.authz.AutzIntegUtils.createGroup;
+import static org.apache.directory.server.core.authz.AutzIntegUtils.changePresciptiveACI;
+
+
+/**
+ * Tests whether or not authorization around entry modify operations work properly.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+@RunWith ( CiRunner.class )
+@Factory ( AutzIntegUtils.ServiceFactory.class )
+public class ModifyAuthorizationIT
+{
+    public static DirectoryService service;
+
+
+    /**
+     * Checks if an attribute of a simple entry (an organizationalUnit) with an RDN
+     * relative to ou=system can be modified by a specific non-admin user.  If a
+     * permission exception is encountered it is caught and false is returned,
+     * otherwise true is returned.  The entry is deleted after being created just in case
+     * subsequent calls to this method are made in the same test case: the admin account
+     * is used to add and delete this test entry so permissions to add and delete are not
+     * required to test the modify operation by the user.
+     *
+     * @param uid the unique identifier for the user (presumed to exist under ou=users,ou=system)
+     * @param password the password of this user
+     * @param entryRdn the relative DN, relative to ou=system where entry is created
+     * for modification test
+     * @param mods the modifications to make to the entry
+     * @return true if the modifications can be made by the user at the specified location,
+     * false otherwise.
+     * @throws javax.naming.NamingException if there are problems conducting the test
+     */
+    public boolean checkCanModifyAs( String uid, String password, String entryRdn, ModificationItemImpl[] mods )
+        throws NamingException
+    {
+        // create the entry with the telephoneNumber attribute to modify
+        Attributes testEntry = new AttributesImpl( "ou", "testou", true );
+        Attribute objectClass = new AttributeImpl( "objectClass" );
+        testEntry.put( objectClass );
+        objectClass.add( "top" );
+        objectClass.add( "organizationalUnit" );
+        testEntry.put( "telephoneNumber", "867-5309" ); // jenny don't change your number
+
+        DirContext adminContext = getContextAsAdmin();
+
+        //noinspection EmptyCatchBlock
+        try
+        {
+            // create the entry as admin
+            LdapDN userName = new LdapDN( "uid=" + uid + ",ou=users,ou=system" );
+            adminContext.createSubcontext( entryRdn, testEntry );
+
+            // modify the entry as the user
+            DirContext userContext = getContextAs( userName, password );
+            userContext.modifyAttributes( entryRdn, mods );
+
+            return true;
+        }
+        catch ( LdapNoPermissionException e )
+        {
+        }
+        finally
+        {
+            // let's clean up
+            adminContext.destroySubcontext( entryRdn );
+        }
+        
+        return false;
+    }
+
+
+    /**
+     * Checks if an attribute of a simple entry (an organizationalUnit) with an RDN
+     * relative to ou=system can be modified by a specific non-admin user.  If a
+     * permission exception is encountered it is caught and false is returned,
+     * otherwise true is returned.  The entry is deleted after being created just in case
+     * subsequent calls to this method are made in the same test case: the admin account
+     * is used to add and delete this test entry so permissions to add and delete are not
+     * required to test the modify operation by the user.
+     *
+     * @param uid the unique identifier for the user (presumed to exist under ou=users,ou=system)
+     * @param password the password of this user
+     * @param entryRdn the relative DN, relative to ou=system where entry is created
+     * for modification test
+     * @param mods the attributes to modify in the entry
+     * @param modOp the modification operation to use for all attributes
+     * @return true if the modifications can be made by the user at the specified location,
+     * false otherwise.
+     * @throws javax.naming.NamingException if there are problems conducting the test
+     */
+    public boolean checkCanModifyAs( String uid, String password, String entryRdn, int modOp, Attributes mods )
+        throws NamingException
+    {
+        // create the entry with the telephoneNumber attribute to modify
+        Attributes testEntry = new AttributesImpl( "ou", "testou", true );
+        Attribute objectClass = new AttributeImpl( "objectClass" );
+        testEntry.put( objectClass );
+        objectClass.add( "top" );
+        objectClass.add( "organizationalUnit" );
+        testEntry.put( "telephoneNumber", "867-5309" ); // jenny don't change your number
+
+        DirContext adminContext = getContextAsAdmin();
+
+        try
+        {
+            // create the entry as admin
+            LdapDN userName = new LdapDN( "uid=" + uid + ",ou=users,ou=system" );
+            adminContext.createSubcontext( entryRdn, testEntry );
+
+            // modify the entry as the user
+            DirContext userContext = getContextAs( userName, password );
+            userContext.modifyAttributes( entryRdn, modOp, mods );
+
+            return true;
+        }
+        catch ( LdapNoPermissionException e )
+        {
+            return false;
+        }
+        finally
+        {
+            // let's clean up
+            adminContext.destroySubcontext( entryRdn );
+        }
+    }
+
+
+    /**
+     * Checks if a user can modify an attribute of their own entry.  Users are
+     * presumed to reside under ou=users,ou=system.  If a permission exception is
+     * encountered it is caught and false is returned, otherwise true is returned.
+     *
+     * @param uid the unique identifier for the user (presumed to exist under ou=users,ou=system)
+     * @param password the password of this user
+     * @param mods the attributes to modify in the entry
+     * @param modOp the modification operation to use for all attributes
+     * @return true if the modifications can be made by the user his/her own entry,
+     * false otherwise.
+     * @throws javax.naming.NamingException if there are problems conducting the test
+     */
+    public boolean checkCanSelfModify( String uid, String password, int modOp, Attributes mods ) throws NamingException
+    {
+        try
+        {
+            // modify the entry as the user
+            Name userEntry = new LdapDN( "uid=" + uid + ",ou=users,ou=system" );
+            DirContext userContext = getContextAs( userEntry, password, userEntry.toString() );
+            userContext.modifyAttributes( "", modOp, mods );
+            return true;
+        }
+        catch ( LdapNoPermissionException e )
+        {
+            return false;
+        }
+    }
+
+
+    /**
+     * Checks if a user can modify an attribute of their own entry.  Users are
+     * presumed to reside under ou=users,ou=system.  If a permission exception is
+     * encountered it is caught and false is returned, otherwise true is returned.
+     *
+     * @param uid the unique identifier for the user (presumed to exist under ou=users,ou=system)
+     * @param password the password of this user
+     * @param mods the attributes to modify in the entry
+     * @return true if the modifications can be made by the user his/her own entry,
+     * false otherwise.
+     * @throws javax.naming.NamingException if there are problems conducting the test
+     */
+    public boolean checkCanSelfModify( String uid, String password, ModificationItemImpl[] mods ) throws NamingException
+    {
+        try
+        {
+            // modify the entry as the user
+            Name userEntry = new LdapDN( "uid=" + uid + ",ou=users,ou=system" );
+            DirContext userContext = getContextAs( userEntry, password, userEntry.toString() );
+            userContext.modifyAttributes( "", mods );
+            return true;
+        }
+        catch ( LdapNoPermissionException e )
+        {
+            return false;
+        }
+    }
+
+
+    /**
+     * Converts a set of attributes and a modification operation type into a MoficationItem array.
+     *
+     * @param modOp the modification operation to perform
+     * @param changes the modifications to the attribute
+     * @return the array of modification items represting the changes
+     * @throws NamingException if there are problems accessing attributes
+     */
+    private ModificationItemImpl[] toItems( int modOp, Attributes changes ) throws NamingException
+    {
+        List<ModificationItemImpl> mods = new ArrayList<ModificationItemImpl>();
+        NamingEnumeration<? extends Attribute> list = changes.getAll();
+        while ( list.hasMore() )
+        {
+            Attribute attr = list.next();
+            mods.add( new ModificationItemImpl( modOp, attr ) );
+        }
+        ModificationItemImpl[] modArray = new ModificationItemImpl[mods.size()];
+        return mods.toArray( modArray );
+    }
+
+
+    @Test
+    public void testSelfModification() throws NamingException
+    {
+        // ----------------------------------------------------------------------------------
+        // Modify with Attribute Addition
+        // ----------------------------------------------------------------------------------
+
+        // create the non-admin user
+        createUser( "billyd", "billyd" );
+
+        // create the password modification
+        ModificationItemImpl[] mods = toItems( DirContext.REPLACE_ATTRIBUTE, new AttributesImpl( "userPassword",
+            "williams", true ) );
+
+        // try a modify operation which should fail without any ACI
+        assertFalse( checkCanSelfModify( "billyd", "billyd", mods ) );
+
+        // Gives grantModify, and grantRead perm to all users in the Administrators group for
+        // entries and all attribute types and values
+        createAccessControlSubentry( "selfModifyUserPassword", "{ " + "identificationTag \"addAci\", "
+            + "precedence 14, " + "authenticationLevel none, " + "itemOrUserFirst userFirst: { "
+            + "userClasses { thisEntry }, " + "userPermissions { "
+            + "{ protectedItems {entry}, grantsAndDenials { grantModify, grantBrowse, grantRead } }, "
+            + "{ protectedItems {allAttributeValues {userPassword}}, grantsAndDenials { grantAdd, grantRemove } } "
+            + "} } }" );
+
+        // try a modify operation which should succeed with ACI
+        assertTrue( checkCanSelfModify( "billyd", "billyd", mods ) );
+        deleteAccessControlSubentry( "selfModifyUserPassword" );
+    }
+
+
+    /**
+     * Checks to make sure group membership based userClass works for modify operations.
+     *
+     * @throws javax.naming.NamingException if the test encounters an error
+     */
+    @Test
+    public void testGrantModifyByTestGroup() throws NamingException
+    {
+        // ----------------------------------------------------------------------------------
+        // Modify with Attribute Addition
+        // ----------------------------------------------------------------------------------
+
+        // create the add modifications
+        ModificationItemImpl[] mods = toItems( DirContext.ADD_ATTRIBUTE, new AttributesImpl( "registeredAddress",
+            "100 Park Ave.", true ) );
+
+        // create the non-admin user
+        createUser( "billyd", "billyd" );
+        
+        createGroup( "TestGroup" );
+
+        // try a modify operation which should fail without any ACI
+        assertFalse( checkCanModifyAs( "billyd", "billyd", "ou=testou", mods ) );
+
+        // Gives grantModify, and grantRead perm to all users in the TestGroup group for
+        // entries and all attribute types and values
+        createAccessControlSubentry( "administratorModifyAdd", "{ " + "identificationTag \"addAci\", "
+            + "precedence 14, " + "authenticationLevel none, " + "itemOrUserFirst userFirst: { "
+            + "userClasses { userGroup { \"cn=TestGroup,ou=groups,ou=system\" } }, " + "userPermissions { "
+            + "{ protectedItems {entry}, grantsAndDenials { grantModify, grantBrowse } }, "
+            + "{ protectedItems {attributeType {registeredAddress}, allAttributeValues {registeredAddress}}, grantsAndDenials { grantAdd } } " + "} } }" );
+
+        // see if we can now add that test entry which we could not before
+        // add op should still fail since billd is not in the admin group
+        assertFalse( checkCanModifyAs( "billyd", "billyd", "ou=testou", mods ) );
+
+        // now add billyd to the TestGroup group and try again
+        addUserToGroup( "billyd", "TestGroup" );
+
+        // try a modify operation which should succeed with ACI and group membership change
+        assertTrue( checkCanModifyAs( "billyd", "billyd", "ou=testou", mods ) );
+        deleteAccessControlSubentry( "administratorModifyAdd" );
+
+        // ----------------------------------------------------------------------------------
+        // Modify with Attribute Removal
+        // ----------------------------------------------------------------------------------
+
+        // now let's test to see if we can perform a modify with a delete op
+        mods = toItems( DirContext.REMOVE_ATTRIBUTE, new AttributesImpl( "telephoneNumber", "867-5309", true ) );
+
+        // make sure we cannot remove the telephone number from the test entry
+        assertFalse( checkCanModifyAs( "billyd", "billyd", "ou=testou", mods ) );
+
+        // Gives grantModify, and grantRead perm to all users in the TestGroup group for
+        // entries and all attribute types and values
+        createAccessControlSubentry( "administratorModifyRemove", "{ " + "identificationTag \"addAci\", "
+            + "precedence 14, " + "authenticationLevel none, " + "itemOrUserFirst userFirst: { "
+            + "userClasses { userGroup { \"cn=TestGroup,ou=groups,ou=system\" } }, " + "userPermissions { "
+            + "{ protectedItems {entry}, grantsAndDenials { grantModify, grantBrowse } }, "
+            + "{ protectedItems {attributeType {telephoneNumber}, allAttributeValues {telephoneNumber}}, grantsAndDenials { grantRemove } } " + "} } }" );
+
+        // try a modify operation which should succeed with ACI and group membership change
+        assertTrue( checkCanModifyAs( "billyd", "billyd", "ou=testou", mods ) );
+        deleteAccessControlSubentry( "administratorModifyRemove" );
+
+        // ----------------------------------------------------------------------------------
+        // Modify with Attribute Replace (requires both grantRemove and grantAdd on attrs)
+        // ----------------------------------------------------------------------------------
+
+        // now let's test to see if we can perform a modify with a delete op
+        mods = toItems( DirContext.REPLACE_ATTRIBUTE, new AttributesImpl( "telephoneNumber", "867-5309", true ) );
+
+        // make sure we cannot remove the telephone number from the test entry
+        assertFalse( checkCanModifyAs( "billyd", "billyd", "ou=testou", mods ) );
+
+        // Gives grantModify, and grantRead perm to all users in the TestGroup group for
+        // entries and all attribute types and values
+        createAccessControlSubentry( "administratorModifyReplace", "{ " + "identificationTag \"addAci\", "
+            + "precedence 14, " + "authenticationLevel none, " + "itemOrUserFirst userFirst: { "
+            + "userClasses { userGroup { \"cn=TestGroup,ou=groups,ou=system\" } }, " + "userPermissions { "
+            + "{ protectedItems {entry}, grantsAndDenials { grantModify, grantBrowse } }, "
+            + "{ protectedItems {attributeType {registeredAddress}, allAttributeValues {telephoneNumber}}, grantsAndDenials { grantAdd, grantRemove } } "
+            + "} } }" );
+
+        // try a modify operation which should succeed with ACI and group membership change
+        assertTrue( checkCanModifyAs( "billyd", "billyd", "ou=testou", mods ) );
+        deleteAccessControlSubentry( "administratorModifyReplace" );
+
+        /* =================================================================================
+         *              DO IT ALL OVER AGAIN BUT USE THE OTHER MODIFY METHOD
+         * ================================================================================= */
+
+        // ----------------------------------------------------------------------------------
+        // Modify with Attribute Addition
+        // ----------------------------------------------------------------------------------
+        // create the add modifications
+        Attributes changes = new AttributesImpl( "registeredAddress", "100 Park Ave.", true );
+
+        // try a modify operation which should fail without any ACI
+        assertFalse( checkCanModifyAs( "billyd", "billyd", "ou=testou", DirContext.ADD_ATTRIBUTE, changes ) );
+
+        // Gives grantModify, and grantRead perm to all users in the TestGroup group for
+        // entries and all attribute types and values
+        createAccessControlSubentry( "administratorModifyAdd", "{ " + "identificationTag \"addAci\", "
+            + "precedence 14, " + "authenticationLevel none, " + "itemOrUserFirst userFirst: { "
+            + "userClasses { userGroup { \"cn=TestGroup,ou=groups,ou=system\" } }, " + "userPermissions { "
+            + "{ protectedItems {entry}, grantsAndDenials { grantModify, grantBrowse } }, "
+            + "{ protectedItems {attributeType {registeredAddress}, allAttributeValues {registeredAddress}}, grantsAndDenials { grantAdd } } " + "} } }" );
+
+        // try a modify operation which should succeed with ACI and group membership change
+        assertTrue( checkCanModifyAs( "billyd", "billyd", "ou=testou", DirContext.ADD_ATTRIBUTE, changes ) );
+        deleteAccessControlSubentry( "administratorModifyAdd" );
+
+        // ----------------------------------------------------------------------------------
+        // Modify with Attribute Removal
+        // ----------------------------------------------------------------------------------
+
+        // now let's test to see if we can perform a modify with a delete op
+        changes = new AttributesImpl( "telephoneNumber", "867-5309", true );
+
+        // make sure we cannot remove the telephone number from the test entry
+        assertFalse( checkCanModifyAs( "billyd", "billyd", "ou=testou", DirContext.REMOVE_ATTRIBUTE, changes ) );
+
+        // Gives grantModify, and grantRead perm to all users in the TestGroup group for
+        // entries and all attribute types and values
+        createAccessControlSubentry( "administratorModifyRemove", "{ " + "identificationTag \"addAci\", "
+            + "precedence 14, " + "authenticationLevel none, " + "itemOrUserFirst userFirst: { "
+            + "userClasses { userGroup { \"cn=TestGroup,ou=groups,ou=system\" } }, " + "userPermissions { "
+            + "{ protectedItems {entry}, grantsAndDenials { grantModify, grantBrowse } }, "
+            + "{ protectedItems {attributeType {telephoneNumber}, allAttributeValues {telephoneNumber}}, grantsAndDenials { grantRemove } } " + "} } }" );
+
+        // try a modify operation which should succeed with ACI and group membership change
+        assertTrue( checkCanModifyAs( "billyd", "billyd", "ou=testou", DirContext.REMOVE_ATTRIBUTE, changes ) );
+        deleteAccessControlSubentry( "administratorModifyRemove" );
+
+        // ----------------------------------------------------------------------------------
+        // Modify with Attribute Replace (requires both grantRemove and grantAdd on attrs)
+        // ----------------------------------------------------------------------------------
+
+        // now let's test to see if we can perform a modify with a delete op
+        changes = new AttributesImpl( "telephoneNumber", "867-5309", true );
+
+        // make sure we cannot remove the telephone number from the test entry
+        assertFalse( checkCanModifyAs( "billyd", "billyd", "ou=testou", DirContext.REPLACE_ATTRIBUTE, changes ) );
+
+        // Gives grantModify, and grantRead perm to all users in the TestGroup group for
+        // entries and all attribute types and values
+        createAccessControlSubentry( "administratorModifyReplace", "{ " + "identificationTag \"addAci\", "
+            + "precedence 14, " + "authenticationLevel none, " + "itemOrUserFirst userFirst: { "
+            + "userClasses { userGroup { \"cn=TestGroup,ou=groups,ou=system\" } }, " + "userPermissions { "
+            + "{ protectedItems {entry}, grantsAndDenials { grantModify, grantBrowse } }, "
+            + "{ protectedItems {attributeType {registeredAddress}, allAttributeValues {telephoneNumber}}, grantsAndDenials { grantAdd, grantRemove } } "
+            + "} } }" );
+
+        // try a modify operation which should succeed with ACI and group membership change
+        assertTrue( checkCanModifyAs( "billyd", "billyd", "ou=testou", DirContext.REPLACE_ATTRIBUTE, changes ) );
+        deleteAccessControlSubentry( "administratorModifyReplace" );
+    }
+
+
+    //    /**
+    //     * Checks to make sure name based userClass works for modify operations.
+    //     *
+    //     * @throws javax.naming.NamingException if the test encounters an error
+    //     */
+    //    public void testGrantModifyByName() throws NamingException
+    //    {
+    //        // create the non-admin user
+    //        createUser( "billyd", "billyd" );
+    //
+    //        // try an modify operation which should fail without any ACI
+    //        assertFalse( checkCanModifyAs( "billyd", "billyd", "ou=testou", "867-5309" ) );
+    //
+    //        // now add a subentry that enables user billyd to modify an entry below ou=system
+    //        createAccessControlSubentry( "billydAdd", "{ " +
+    //                "identificationTag \"addAci\", " +
+    //                "precedence 14, " +
+    //                "authenticationLevel none, " +
+    //                "itemOrUserFirst userFirst: { " +
+    //                "userClasses { name { \"uid=billyd,ou=users,ou=system\" } }, " +
+    //                "userPermissions { { " +
+    //                "protectedItems {entry, allUserAttributeTypesAndValues}, " +
+    //                "grantsAndDenials { grantModify, grantRead, grantBrowse } } } } }" );
+    //
+    //        // should work now that billyd is authorized by name
+    //        assertTrue( checkCanModifyAs( "billyd", "billyd", "ou=testou", "867-5309" ) );
+    //    }
+    //
+    //
+    //    /**
+    //     * Checks to make sure subtree based userClass works for modify operations.
+    //     *
+    //     * @throws javax.naming.NamingException if the test encounters an error
+    //     */
+    //    public void testGrantModifyBySubtree() throws NamingException
+    //    {
+    //        // create the non-admin user
+    //        createUser( "billyd", "billyd" );
+    //
+    //        // try a modify operation which should fail without any ACI
+    //        assertFalse( checkCanModifyAs( "billyd", "billyd", "ou=testou", "867-5309" ) );
+    //
+    //        // now add a subentry that enables user billyd to modify an entry below ou=system
+    //        createAccessControlSubentry( "billyAddBySubtree", "{ " +
+    //                "identificationTag \"addAci\", " +
+    //                "precedence 14, " +
+    //                "authenticationLevel none, " +
+    //                "itemOrUserFirst userFirst: { " +
+    //                "userClasses { subtree { { base \"ou=users,ou=system\" } } }, " +
+    //                "userPermissions { { " +
+    //                "protectedItems {entry, allUserAttributeTypesAndValues}, " +
+    //                "grantsAndDenials { grantModify, grantRead, grantBrowse } } } } }" );
+    //
+    //        // should work now that billyd is authorized by the subtree userClass
+    //        assertTrue( checkCanModifyAs( "billyd", "billyd", "ou=testou", "867-5309" ) );
+    //    }
+    //
+    //
+    //    /**
+    //     * Checks to make sure <b>allUsers</b> userClass works for modify operations.
+    //     *
+    //     * @throws javax.naming.NamingException if the test encounters an error
+    //     */
+    //    public void testGrantModifyAllUsers() throws NamingException
+    //    {
+    //        // create the non-admin user
+    //        createUser( "billyd", "billyd" );
+    //
+    //        // try an add operation which should fail without any ACI
+    //        assertFalse( checkCanModifyAs( "billyd", "billyd", "ou=testou", "867-5309" ) );
+    //
+    //        // now add a subentry that enables anyone to add an entry below ou=system
+    //        createAccessControlSubentry( "anybodyAdd", "{ " +
+    //                "identificationTag \"addAci\", " +
+    //                "precedence 14, " +
+    //                "authenticationLevel none, " +
+    //                "itemOrUserFirst userFirst: { " +
+    //                "userClasses { allUsers }, " +
+    //                "userPermissions { { " +
+    //                "protectedItems {entry, allUserAttributeTypesAndValues}, " +
+    //                "grantsAndDenials { grantModify, grantRead, grantBrowse } } } } }" );
+    //
+    //        // see if we can now modify that test entry's number which we could not before
+    //        // should work with billyd now that all users are authorized
+    //        assertTrue( checkCanModifyAs( "billyd", "billyd", "ou=testou", "867-5309" ) );
+    //    }
+    
+    
+    @Test
+    public void testPresciptiveACIModification() throws NamingException
+    {
+        
+        ModificationItemImpl[] mods = toItems( DirContext.ADD_ATTRIBUTE,
+            new AttributesImpl( "registeredAddress", "100 Park Ave.", true ) );
+
+        createUser( "billyd", "billyd" );
+
+        createAccessControlSubentry( "modifyACI", "{ " + "identificationTag \"modifyAci\", "
+            + "precedence 14, " + "authenticationLevel none, " + "itemOrUserFirst userFirst: { "
+            + "userClasses { allUsers }, " + "userPermissions { "
+            + "{ protectedItems {entry, allUserAttributeTypesAndValues}, grantsAndDenials { grantModify, grantBrowse, grantAdd, grantRemove } } } } }" );
+
+        assertTrue( checkCanModifyAs( "billyd", "billyd", "ou=testou", mods ) );
+        
+        mods = toItems( DirContext.REPLACE_ATTRIBUTE,
+            new AttributesImpl( "registeredAddress", "200 Park Ave.", true ) );
+        
+        changePresciptiveACI( "modifyACI", "{ " + "identificationTag \"modifyAci\", "
+            + "precedence 14, " + "authenticationLevel none, " + "itemOrUserFirst userFirst: { "
+            + "userClasses { allUsers }, " + "userPermissions { "
+            + "{ protectedItems {entry, allUserAttributeTypesAndValues}, grantsAndDenials { denyModify } } } } }" );
+
+        assertFalse( checkCanModifyAs( "billyd", "billyd", "ou=testou", mods ) );
+        
+        deleteAccessControlSubentry( "modifyACI" );
+        
+    }
+    
+
+    @Test
+    public void testMaxValueCountProtectedItem() throws NamingException
+    {
+        createUser( "billyd", "billyd" );
+        createAccessControlSubentry( "mvcACI",
+            " {" +
+                " identificationTag \"mvcACI\"," +
+                " precedence 10," +
+                " authenticationLevel simple," +
+                " itemOrUserFirst userFirst:" + 
+                " {" +
+                    " userClasses { allUsers }," +
+                    " userPermissions" + 
+                    " {" +
+                        " {" +
+                            " protectedItems { entry }," +
+                            " grantsAndDenials { grantModify, grantBrowse }" +
+                        " }" +
+                        " ," +
+                        " {" +
+                            " protectedItems" + 
+                            " {" +
+                                " attributeType { description }," +
+                                " allAttributeValues { description }," +
+                                " maxValueCount" + 
+                                " {" +
+                                    " { type description, maxCount 1 }" + 
+                                " }" +
+                            " }" +
+                            " ," +
+                            " grantsAndDenials" + 
+                            " {" +
+                                " grantRemove," +
+                                " grantAdd" +
+                            " }" +
+                        " }" +
+                     " }" +
+                " }" +
+            " }" );
+        
+        ModificationItemImpl[] mods = toItems( DirContext.ADD_ATTRIBUTE,
+            new AttributesImpl( "description", "description 1", true ) );
+        
+        assertTrue( checkCanModifyAs( "billyd", "billyd", "ou=testou", mods ) );
+        
+        AttributesImpl attrs = new AttributesImpl(true);
+        AttributeImpl attr = new AttributeImpl( "description" );
+        attr.add( "description 1" );
+        attr.add( "description 2" );
+        attrs.put( attr );
+        mods = toItems( DirContext.ADD_ATTRIBUTE, attrs );
+        
+        assertFalse( checkCanModifyAs( "billyd", "billyd", "ou=testou", mods ) );
+        
+        mods = toItems( DirContext.REPLACE_ATTRIBUTE, attrs );
+        
+        assertFalse( checkCanModifyAs( "billyd", "billyd", "ou=testou", mods ) );
+    }
+}
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/authz/MoveRenameAuthorizationIT.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/authz/MoveRenameAuthorizationIT.java
new file mode 100644
index 0000000..6c1c613
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/authz/MoveRenameAuthorizationIT.java
@@ -0,0 +1,532 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.authz;
+
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+
+import org.apache.directory.shared.ldap.exception.LdapNoPermissionException;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.server.core.integ.CiRunner;
+import org.apache.directory.server.core.integ.annotations.Factory;
+import org.apache.directory.server.core.DirectoryService;
+import org.junit.runner.RunWith;
+
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+import org.junit.Test;
+
+import static org.apache.directory.server.core.authz.AutzIntegUtils.createUser;
+import static org.apache.directory.server.core.authz.AutzIntegUtils.deleteUser;
+import static org.apache.directory.server.core.authz.AutzIntegUtils.getContextAs;
+import static org.apache.directory.server.core.authz.AutzIntegUtils.getContextAsAdmin;
+import static org.apache.directory.server.core.authz.AutzIntegUtils.createAccessControlSubentry;
+import static org.apache.directory.server.core.authz.AutzIntegUtils.addUserToGroup;
+import static org.apache.directory.server.core.authz.AutzIntegUtils.deleteAccessControlSubentry;
+import static org.apache.directory.server.core.authz.AutzIntegUtils.removeUserFromGroup;
+
+
+/**
+ * Tests whether or not authorization around entry renames and moves work properly.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+@RunWith ( CiRunner.class )
+@Factory ( AutzIntegUtils.ServiceFactory.class )
+public class MoveRenameAuthorizationIT
+{
+    public static DirectoryService service;
+
+
+    /**
+     * Checks if a simple entry (organizationalUnit) can be renamed at an RDN relative
+     * to ou=system by a specific non-admin user.  If a permission exception
+     * is encountered it is caught and false is returned, otherwise true is returned
+     * when the entry is created.  The entry is deleted after being created just in case
+     * subsequent calls to this method do not fail: the admin account is used to delete
+     * this test entry so permissions to delete are not required to delete it by the user.
+     *
+     * @param uid the unique identifier for the user (presumed to exist under ou=users,ou=system)
+     * @param password the password of this user
+     * @param entryRdn the relative DN, relative to ou=system where entry renames are tested
+     * @param newRdn the new RDN for the entry under ou=system
+     * @return true if the entry can be renamed by the user at the specified location, false otherwise
+     * @throws javax.naming.NamingException if there are problems conducting the test
+     */
+    public boolean checkCanRenameAs( String uid, String password, String entryRdn, String newRdn )
+        throws NamingException
+    {
+        Attributes testEntry = new AttributesImpl( "ou", "testou", true );
+        Attribute objectClass = new AttributeImpl( "objectClass" );
+        testEntry.put( objectClass );
+        objectClass.add( "top" );
+        objectClass.add( "organizationalUnit" );
+
+        DirContext adminContext = getContextAsAdmin();
+        try
+        {
+            // create the new entry as the admin user
+            adminContext.createSubcontext( entryRdn, testEntry );
+
+            LdapDN userName = new LdapDN( "uid=" + uid + ",ou=users,ou=system" );
+            DirContext userContext = getContextAs( userName, password );
+            userContext.rename( entryRdn, newRdn );
+
+            // delete the renamed context as the admin user
+            adminContext.destroySubcontext( newRdn );
+            return true;
+        }
+        catch ( LdapNoPermissionException e )
+        {
+            // delete the original context as the admin user since rename
+            // of newly created test entry did not succeed
+            adminContext.destroySubcontext( entryRdn );
+            return false;
+        }
+    }
+
+
+    /**
+     * Checks to make sure group membership based userClass works for renames,
+     * moves and moves with renames.
+     *
+     * @throws javax.naming.NamingException if the test encounters an error
+     */
+    @Test
+    public void testGrantByAdministrators() throws NamingException
+    {
+        // ----------------------------------------------------------------------------
+        // Test simple RDN change: NO SUBTREE MOVEMENT!
+        // ----------------------------------------------------------------------------
+
+        // create the non-admin user
+        createUser( "billyd", "billyd" );
+
+        // try the rename operation which should fail without any ACI
+        assertFalse( checkCanRenameAs( "billyd", "billyd", "ou=testou", "ou=newname" ) );
+
+        // Gives grantRename perm to all users in the Administrators group for entries
+        createAccessControlSubentry( "grantRenameByAdmin", "{ " + "identificationTag \"addAci\", " + "precedence 14, "
+            + "authenticationLevel none, " + "itemOrUserFirst userFirst: { "
+            + "userClasses { userGroup { \"cn=Administrators,ou=groups,ou=system\" } }, " + "userPermissions { { "
+            + "protectedItems {entry}, " + "grantsAndDenials { grantRename, grantBrowse } } } } }" );
+
+        // see if we can now rename that test entry which we could not before
+        // rename op should still fail since billyd is not in the admin group
+        assertFalse( checkCanRenameAs( "billyd", "billyd", "ou=testou", "ou=newname" ) );
+
+        // now add billyd to the Administrator group and try again
+        addUserToGroup( "billyd", "Administrators" );
+
+        // try a rename operation which should succeed with ACI and group membership change
+        assertTrue( checkCanRenameAs( "billyd", "billyd", "ou=testou", "ou=newname" ) );
+
+        // now let's cleanup
+        removeUserFromGroup( "billyd", "Administrators" );
+        deleteAccessControlSubentry( "grantRenameByAdmin" );
+        deleteUser( "billyd" );
+
+        // ----------------------------------------------------------------------------
+        // Test move and RDN change at the same time.
+        // ----------------------------------------------------------------------------
+
+        // create the non-admin user
+        createUser( "billyd", "billyd" );
+
+        // try an move w/ rdn change which should fail without any ACI
+        assertFalse( checkCanRenameAs( "billyd", "billyd", "ou=testou,ou=users", "ou=newname,ou=groups" ) );
+
+        // Gives grantRename, grantImport, grantExport perm to all users in the Administrators
+        // group for entries - browse is needed just to read navigate the tree at root
+        createAccessControlSubentry( "grantRenameMoveByAdmin", "{ " + "identificationTag \"addAci\", "
+            + "precedence 14, " + "authenticationLevel none, " + "itemOrUserFirst userFirst: { "
+            + "userClasses { userGroup { \"cn=Administrators,ou=groups,ou=system\" } }, " + "userPermissions { { "
+            + "protectedItems {entry}, "
+            + "grantsAndDenials { grantExport, grantImport, grantRename, grantBrowse } } } } }" );
+
+        // see if we can move and rename the test entry which we could not before
+        // op should still fail since billyd is not in the admin group
+        assertFalse( checkCanRenameAs( "billyd", "billyd", "ou=testou,ou=users", "ou=newname,ou=groups" ) );
+
+        // now add billyd to the Administrator group and try again
+        addUserToGroup( "billyd", "Administrators" );
+
+        // try move w/ rdn change which should succeed with ACI and group membership change
+        assertTrue( checkCanRenameAs( "billyd", "billyd", "ou=testou,ou=users", "ou=newname,ou=groups" ) );
+
+        // now let's cleanup
+        removeUserFromGroup( "billyd", "Administrators" );
+        deleteAccessControlSubentry( "grantRenameMoveByAdmin" );
+        deleteUser( "billyd" );
+
+        // ----------------------------------------------------------------------------
+        // Test move ONLY without any RDN changes.
+        // ----------------------------------------------------------------------------
+
+        // create the non-admin user
+        createUser( "billyd", "billyd" );
+
+        // try move operation which should fail without any ACI
+        assertFalse( checkCanRenameAs( "billyd", "billyd", "ou=testou,ou=users", "ou=testou,ou=groups" ) );
+
+        // Gives grantImport, and grantExport perm to all users in the Administrators group for entries
+        createAccessControlSubentry( "grantMoveByAdmin", "{ " + "identificationTag \"addAci\", " + "precedence 14, "
+            + "authenticationLevel none, " + "itemOrUserFirst userFirst: { "
+            + "userClasses { userGroup { \"cn=Administrators,ou=groups,ou=system\" } }, " + "userPermissions { { "
+            + "protectedItems {entry}, " + "grantsAndDenials { grantExport, grantImport, grantBrowse } } } } }" );
+
+        // see if we can now move that test entry which we could not before
+        // op should still fail since billyd is not in the admin group
+        assertFalse( checkCanRenameAs( "billyd", "billyd", "ou=testou,ou=users", "ou=testou,ou=groups" ) );
+
+        // now add billyd to the Administrator group and try again
+        addUserToGroup( "billyd", "Administrators" );
+
+        // try move operation which should succeed with ACI and group membership change
+        assertTrue( checkCanRenameAs( "billyd", "billyd", "ou=testou,ou=users", "ou=testou,ou=groups" ) );
+
+        // now let's cleanup
+        removeUserFromGroup( "billyd", "Administrators" );
+        deleteAccessControlSubentry( "grantMoveByAdmin" );
+        deleteUser( "billyd" );
+    }
+
+
+    /**
+     * Checks to make sure name based userClass works for rename, move, and
+     * rename with move operation access controls.
+     *
+     * @throws javax.naming.NamingException if the test encounters an error
+     */
+    @Test
+    public void testGrantByName() throws NamingException
+    {
+        // ----------------------------------------------------------------------------
+        // Test simple RDN change: NO SUBTREE MOVEMENT!
+        // ----------------------------------------------------------------------------
+
+        // create the non-admin user
+        createUser( "billyd", "billyd" );
+
+        // try the rename operation which should fail without any ACI
+        assertFalse( checkCanRenameAs( "billyd", "billyd", "ou=testou", "ou=newname" ) );
+
+        // Gives grantRename perm specifically to the billyd user
+        createAccessControlSubentry( "grantRenameByName", "{ " + "identificationTag \"addAci\", " + "precedence 14, "
+            + "authenticationLevel none, " + "itemOrUserFirst userFirst: { "
+            + "userClasses { name { \"uid=billyd,ou=users,ou=system\" } }, " + "userPermissions { { "
+            + "protectedItems {entry}, " + "grantsAndDenials { grantRename, grantBrowse } } } } }" );
+
+        // try a rename operation which should succeed with ACI
+        assertTrue( checkCanRenameAs( "billyd", "billyd", "ou=testou", "ou=newname" ) );
+
+        // now let's cleanup
+        deleteAccessControlSubentry( "grantRenameByName" );
+        deleteUser( "billyd" );
+
+        // ----------------------------------------------------------------------------
+        // Test move and RDN change at the same time.
+        // ----------------------------------------------------------------------------
+
+        // create the non-admin user
+        createUser( "billyd", "billyd" );
+
+        // try an move w/ rdn change which should fail without any ACI
+        assertFalse( checkCanRenameAs( "billyd", "billyd", "ou=testou,ou=users", "ou=newname,ou=groups" ) );
+
+        // Gives grantRename, grantImport, grantExport perm to billyd user on entries
+        createAccessControlSubentry( "grantRenameMoveByName", "{ " + "identificationTag \"addAci\", "
+            + "precedence 14, " + "authenticationLevel none, " + "itemOrUserFirst userFirst: { "
+            + "userClasses { name { \"uid=billyd,ou=users,ou=system\" } }, " + "userPermissions { { "
+            + "protectedItems {entry}, "
+            + "grantsAndDenials { grantExport, grantImport, grantRename, grantBrowse } } } } }" );
+
+        // try move w/ rdn change which should succeed with ACI
+        assertTrue( checkCanRenameAs( "billyd", "billyd", "ou=testou,ou=users", "ou=newname,ou=groups" ) );
+
+        // now let's cleanup
+        deleteAccessControlSubentry( "grantRenameMoveByName" );
+        deleteUser( "billyd" );
+
+        // ----------------------------------------------------------------------------
+        // Test move ONLY without any RDN changes.
+        // ----------------------------------------------------------------------------
+
+        // create the non-admin user
+        createUser( "billyd", "billyd" );
+
+        // try move operation which should fail without any ACI
+        assertFalse( checkCanRenameAs( "billyd", "billyd", "ou=testou,ou=users", "ou=testou,ou=groups" ) );
+
+        // Gives grantImport, and grantExport perm to billyd user for entries
+        createAccessControlSubentry( "grantMoveByName", "{ " + "identificationTag \"addAci\", " + "precedence 14, "
+            + "authenticationLevel none, " + "itemOrUserFirst userFirst: { "
+            + "userClasses { name { \"uid=billyd,ou=users,ou=system\" } }, " + "userPermissions { { "
+            + "protectedItems {entry}, " + "grantsAndDenials { grantExport, grantImport, grantBrowse } } } } }" );
+
+        // try move operation which should succeed with ACI
+        assertTrue( checkCanRenameAs( "billyd", "billyd", "ou=testou,ou=users", "ou=testou,ou=groups" ) );
+
+        // now let's cleanup
+        deleteAccessControlSubentry( "grantMoveByName" );
+        deleteUser( "billyd" );
+    }
+
+
+    /**
+     * Checks to make sure subtree based userClass works for rename, move, and
+     * rename with move operation access controls.
+     *
+     * @throws javax.naming.NamingException if the test encounters an error
+     */
+    @Test
+    public void testGrantBySubtree() throws NamingException
+    {
+        // ----------------------------------------------------------------------------
+        // Test simple RDN change: NO SUBTREE MOVEMENT!
+        // ----------------------------------------------------------------------------
+
+        // create the non-admin user
+        createUser( "billyd", "billyd" );
+
+        // try the rename operation which should fail without any ACI
+        assertFalse( checkCanRenameAs( "billyd", "billyd", "ou=testou", "ou=newname" ) );
+
+        // Gives grantRename perm for entries to those users selected by the subtree
+        createAccessControlSubentry( "grantRenameByTree", "{ " + "identificationTag \"addAci\", " + "precedence 14, "
+            + "authenticationLevel none, " + "itemOrUserFirst userFirst: { "
+            + "userClasses { subtree { { base \"ou=users,ou=system\" } } }, " + "userPermissions { { "
+            + "protectedItems {entry}, " + "grantsAndDenials { grantRename, grantBrowse } } } } }" );
+
+        // try a rename operation which should succeed with ACI
+        assertTrue( checkCanRenameAs( "billyd", "billyd", "ou=testou", "ou=newname" ) );
+
+        // now let's cleanup
+        deleteAccessControlSubentry( "grantRenameByTree" );
+        deleteUser( "billyd" );
+
+        // ----------------------------------------------------------------------------
+        // Test move and RDN change at the same time.
+        // ----------------------------------------------------------------------------
+
+        // create the non-admin user
+        createUser( "billyd", "billyd" );
+
+        // try an move w/ rdn change which should fail without any ACI
+        assertFalse( checkCanRenameAs( "billyd", "billyd", "ou=testou,ou=users", "ou=newname,ou=groups" ) );
+
+        // Gives grantRename, grantImport, grantExport for entries to users selected by subtree
+        createAccessControlSubentry( "grantRenameMoveByTree", "{ " + "identificationTag \"addAci\", "
+            + "precedence 14, " + "authenticationLevel none, " + "itemOrUserFirst userFirst: { "
+            + "userClasses { subtree { { base \"ou=users,ou=system\" } } }, " + "userPermissions { { "
+            + "protectedItems {entry}, "
+            + "grantsAndDenials { grantExport, grantImport, grantRename, grantBrowse } } } } }" );
+
+        // try move w/ rdn change which should succeed with ACI
+        assertTrue( checkCanRenameAs( "billyd", "billyd", "ou=testou,ou=users", "ou=newname,ou=groups" ) );
+
+        // now let's cleanup
+        deleteAccessControlSubentry( "grantRenameMoveByTree" );
+        deleteUser( "billyd" );
+
+        // ----------------------------------------------------------------------------
+        // Test move ONLY without any RDN changes.
+        // ----------------------------------------------------------------------------
+
+        // create the non-admin user
+        createUser( "billyd", "billyd" );
+
+        // try move operation which should fail without any ACI
+        assertFalse( checkCanRenameAs( "billyd", "billyd", "ou=testou,ou=users", "ou=testou,ou=groups" ) );
+
+        // Gives grantImport, and grantExport perm for entries to subtree selected users
+        createAccessControlSubentry( "grantMoveByTree", "{ " + "identificationTag \"addAci\", " + "precedence 14, "
+            + "authenticationLevel none, " + "itemOrUserFirst userFirst: { "
+            + "userClasses { subtree { { base \"ou=users,ou=system\" } } }, " + "userPermissions { { "
+            + "protectedItems {entry}, " + "grantsAndDenials { grantExport, grantImport, grantBrowse } } } } }" );
+
+        // try move operation which should succeed with ACI
+        assertTrue( checkCanRenameAs( "billyd", "billyd", "ou=testou,ou=users", "ou=testou,ou=groups" ) );
+
+        // now let's cleanup
+        deleteAccessControlSubentry( "grantMoveByTree" );
+        deleteUser( "billyd" );
+    }
+
+
+    /**
+     * Checks to make sure the <b>anyUser</b> userClass works for rename, move, and
+     * rename with move operation access controls.
+     *
+     * @throws javax.naming.NamingException if the test encounters an error
+     */
+    @Test
+    public void testGrantByAnyuser() throws NamingException
+    {
+        // ----------------------------------------------------------------------------
+        // Test simple RDN change: NO SUBTREE MOVEMENT!
+        // ----------------------------------------------------------------------------
+
+        // create the non-admin user
+        createUser( "billyd", "billyd" );
+
+        // try the rename operation which should fail without any ACI
+        assertFalse( checkCanRenameAs( "billyd", "billyd", "ou=testou", "ou=newname" ) );
+
+        // Gives grantRename perm for entries to any user
+        createAccessControlSubentry( "grantRenameByAny", "{ " + "identificationTag \"addAci\", " + "precedence 14, "
+            + "authenticationLevel none, " + "itemOrUserFirst userFirst: { " + "userClasses { allUsers }, "
+            + "userPermissions { { " + "protectedItems {entry}, "
+            + "grantsAndDenials { grantRename, grantBrowse } } } } }" );
+
+        // try a rename operation which should succeed with ACI
+        assertTrue( checkCanRenameAs( "billyd", "billyd", "ou=testou", "ou=newname" ) );
+
+        // now let's cleanup
+        deleteAccessControlSubentry( "grantRenameByAny" );
+        deleteUser( "billyd" );
+
+        // ----------------------------------------------------------------------------
+        // Test move and RDN change at the same time.
+        // ----------------------------------------------------------------------------
+
+        // create the non-admin user
+        createUser( "billyd", "billyd" );
+
+        // try an move w/ rdn change which should fail without any ACI
+        assertFalse( checkCanRenameAs( "billyd", "billyd", "ou=testou,ou=users", "ou=newname,ou=groups" ) );
+
+        // Gives grantRename, grantImport, grantExport for entries to any user
+        createAccessControlSubentry( "grantRenameMoveByAny", "{ " + "identificationTag \"addAci\", "
+            + "precedence 14, " + "authenticationLevel none, " + "itemOrUserFirst userFirst: { "
+            + "userClasses { allUsers }, " + "userPermissions { { " + "protectedItems {entry}, "
+            + "grantsAndDenials { grantExport, grantImport, grantRename, grantBrowse } } } } }" );
+
+        // try move w/ rdn change which should succeed with ACI
+        assertTrue( checkCanRenameAs( "billyd", "billyd", "ou=testou,ou=users", "ou=newname,ou=groups" ) );
+
+        // now let's cleanup
+        deleteAccessControlSubentry( "grantRenameMoveByAny" );
+        deleteUser( "billyd" );
+
+        // ----------------------------------------------------------------------------
+        // Test move ONLY without any RDN changes.
+        // ----------------------------------------------------------------------------
+
+        // create the non-admin user
+        createUser( "billyd", "billyd" );
+
+        // try move operation which should fail without any ACI
+        assertFalse( checkCanRenameAs( "billyd", "billyd", "ou=testou,ou=users", "ou=testou,ou=groups" ) );
+
+        // Gives grantImport, and grantExport perm for entries to any user
+        createAccessControlSubentry( "grantMoveByAny", "{ " + "identificationTag \"addAci\", " + "precedence 14, "
+            + "authenticationLevel none, " + "itemOrUserFirst userFirst: { " + "userClasses { allUsers }, "
+            + "userPermissions { { " + "protectedItems {entry}, "
+            + "grantsAndDenials { grantExport, grantImport, grantBrowse } } } } }" );
+
+        // try move operation which should succeed with ACI
+        assertTrue( checkCanRenameAs( "billyd", "billyd", "ou=testou,ou=users", "ou=testou,ou=groups" ) );
+
+        // now let's cleanup
+        deleteAccessControlSubentry( "grantMoveByAny" );
+        deleteUser( "billyd" );
+    }
+    
+    
+    /**
+     * Checks to make sure Export and Import permissions work correctly
+     * when they are defined on seperate contexts.
+     *
+     * @throws javax.naming.NamingException if the test encounters an error
+     */
+    @Test
+    public void testExportAndImportSeperately() throws NamingException
+    {
+        // ----------------------------------------------------------------------------
+        // Test move and RDN change at the same time.
+        // ----------------------------------------------------------------------------
+
+        // create the non-admin user
+        createUser( "billyd", "billyd" );
+
+        // try an move w/ rdn change which should fail without any ACI
+        assertFalse( checkCanRenameAs( "billyd", "billyd", "ou=testou,ou=users", "ou=newname,ou=groups" ) );
+
+        
+        // Gives grantBrowse perm to all users in the Administrators
+        // group for entries
+        // It's is needed just to read navigate the tree at root
+        createAccessControlSubentry(
+            "grantBrowseForTheWholeNamingContext",
+            "{ }",
+            "{ " + "identificationTag \"browseACI\", "
+            + "precedence 14, " + "authenticationLevel none, " + "itemOrUserFirst userFirst: { "
+            + "userClasses { userGroup { \"cn=Administrators,ou=groups,ou=system\" } }, " + "userPermissions { { "
+            + "protectedItems { entry }, "
+            + "grantsAndDenials { grantBrowse } } } } }" );
+        
+        // Gives grantExport, grantRename perm to all users in the Administrators
+        // group for entries
+        createAccessControlSubentry(
+            "grantExportFromASubtree",
+            "{ base \"ou=users\" }", // !!!!! =====>>>>> { base "ou=users" }
+            "{ " + "identificationTag \"exportACI\", "
+            + "precedence 14, " + "authenticationLevel none, " + "itemOrUserFirst userFirst: { "
+            + "userClasses { userGroup { \"cn=Administrators,ou=groups,ou=system\" } }, " + "userPermissions { { "
+            + "protectedItems { entry }, "
+            + "grantsAndDenials { grantExport, grantRename } } } } }" );
+        
+        // Gives grantImport perm to all users in the Administrators
+        // group for the target context
+        createAccessControlSubentry(
+            "grantImportToASubtree",
+            "{ base \"ou=groups\" }", // !!!!! =====>>>>> { base "ou=groups" }
+            "{ " + "identificationTag \"importACI\", "
+            + "precedence 14, " + "authenticationLevel none, " + "itemOrUserFirst userFirst: { "
+            + "userClasses { userGroup { \"cn=Administrators,ou=groups,ou=system\" } }, " + "userPermissions { { "
+            + "protectedItems { entry }, "
+            + "grantsAndDenials { grantImport } } } } }" );
+
+        // see if we can move and rename the test entry which we could not before
+        // op should still fail since billyd is not in the admin group
+        assertFalse( checkCanRenameAs( "billyd", "billyd", "ou=testou,ou=users", "ou=newname,ou=groups" ) );
+
+        // now add billyd to the Administrator group and try again
+        addUserToGroup( "billyd", "Administrators" );
+
+        // try move w/ rdn change which should succeed with ACI and group membership change
+        assertTrue( checkCanRenameAs( "billyd", "billyd", "ou=testou,ou=users", "ou=newname,ou=groups" ) );
+
+        // now let's cleanup
+        removeUserFromGroup( "billyd", "Administrators" );
+        deleteAccessControlSubentry( "grantBrowseForTheWholeNamingContext" );
+        deleteAccessControlSubentry( "grantExportFromASubtree" );
+        deleteAccessControlSubentry( "grantImportToASubtree" );
+        deleteUser( "billyd" );
+    }
+}
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/authz/SearchAuthorizationIT.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/authz/SearchAuthorizationIT.java
new file mode 100644
index 0000000..914e421
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/authz/SearchAuthorizationIT.java
@@ -0,0 +1,885 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.authz;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import static org.apache.directory.server.core.authz.AutzIntegUtils.createUser;
+import static org.apache.directory.server.core.authz.AutzIntegUtils.getContextAs;
+import static org.apache.directory.server.core.authz.AutzIntegUtils.createAccessControlSubentry;
+import static org.apache.directory.server.core.authz.AutzIntegUtils.addUserToGroup;
+import static org.apache.directory.server.core.authz.AutzIntegUtils.deleteAccessControlSubentry;
+import static org.apache.directory.server.core.authz.AutzIntegUtils.addEntryACI;
+import static org.apache.directory.server.core.authz.AutzIntegUtils.addSubentryACI;
+import org.apache.directory.server.core.integ.CiRunner;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getSystemContext;
+import org.apache.directory.server.core.integ.annotations.Factory;
+import org.apache.directory.shared.ldap.exception.LdapNameNotFoundException;
+import org.apache.directory.shared.ldap.exception.LdapNoPermissionException;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.naming.Name;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.LdapContext;
+import java.util.HashMap;
+import java.util.Map;
+
+
+/**
+ * Tests whether or not authorization around search, list and lookup operations
+ * work properly.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+@RunWith ( CiRunner.class )
+@Factory ( AutzIntegUtils.ServiceFactory.class )
+public class SearchAuthorizationIT
+{
+    public static DirectoryService service;
+
+
+    /**
+     * The search results of tests are added to this map via put (<String, SearchResult>)
+     * the map is also cleared before each search test.  This allows further inspections
+     * of the results for more specific test cases.
+     */
+    private Map<String, SearchResult> results = new HashMap<String, SearchResult>();
+
+
+    /**
+     * Generates a set of simple organizationalUnit entries where the
+     * ou of the entry returned is the index of the entry in the array.
+     *
+     * @param count the number of entries to produce
+     * @return an array of entries with length = count
+     */
+    private Attributes[] getTestNodes( final int count )
+    {
+        Attributes[] attributes = new Attributes[count];
+        for ( int ii = 0; ii < count; ii++ )
+        {
+            attributes[ii] = new AttributesImpl();
+            Attribute oc = new AttributeImpl( "objectClass" );
+            oc.add( "top" );
+            oc.add( "organizationalUnit" );
+            attributes[ii].put( oc );
+            Attribute ou = new AttributeImpl( "ou" );
+            ou.add( String.valueOf( ii ) );
+            ou.add( "testEntry" );
+            attributes[ii].put( ou );
+            attributes[ii].put( "telephoneNumber", String.valueOf( count ) );
+        }
+
+        return attributes;
+    }
+
+
+    private void recursivelyAddSearchData( Name parent, Attributes[] children, final int sizeLimit, int[] count )
+        throws NamingException
+    {
+        Name[] childRdns = new Name[children.length];
+        for ( int ii = 0; ii < children.length && count[0] < sizeLimit; ii++ )
+        {
+            Name childRdn = new LdapDN();
+            childRdn.addAll( parent );
+            childRdn.add( "ou=" + ii );
+            childRdns[ii] = childRdn;
+            getSystemContext( service ).createSubcontext( childRdn, children[ii] );
+            count[0]++;
+        }
+
+        if ( count[0] >= sizeLimit )
+        {
+            return;
+        }
+
+        for ( int ii = 0; ii < children.length && count[0] < sizeLimit; ii++ )
+        {
+            recursivelyAddSearchData( childRdns[ii], children, sizeLimit, count );
+        }
+    }
+
+
+    /**
+     * Starts creating nodes under a parent with a set number of children.  First
+     * a single node is created under the parent.  Thereafter a number of children
+     * determined by the branchingFactor is added.  Until a sizeLimit is reached
+     * descendants are created this way in a breath first recursive descent.
+     *
+     * @param parent the parent under which the first node is created
+     * @param branchingFactor how to brach the data
+     * @param sizelimit the amount of entries 
+     * @return the immediate child node created under parent which contains the subtree
+     * @throws NamingException on error
+     */
+    private Name addSearchData( Name parent, int branchingFactor, int sizelimit ) throws NamingException
+    {
+        parent = ( Name ) parent.clone();
+        parent.add( "ou=tests" );
+        getSystemContext( service ).createSubcontext( parent, getTestNodes( 1 )[0] );
+        recursivelyAddSearchData( parent, getTestNodes( branchingFactor ), sizelimit, new int[]
+            { 1 } );
+        return parent;
+    }
+
+
+    /**
+     * Recursively deletes all entries including the base specified.
+     *
+     * @param rdn the relative dn from ou=system of the entry to delete recursively
+     * @throws NamingException if there are problems deleting entries
+     */
+    private void recursivelyDelete( Name rdn ) throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        NamingEnumeration<SearchResult> results = sysRoot.search( rdn, "(objectClass=*)", new SearchControls() );
+        
+        while ( results.hasMore() )
+        {
+            SearchResult result = results.next();
+            Name childRdn = new LdapDN( result.getName() );
+            childRdn.remove( 0 );
+            recursivelyDelete( childRdn );
+        }
+        sysRoot.destroySubcontext( rdn );
+    }
+
+
+    /**
+     * Performs a single level search as a specific user on newly created data and checks
+     * that result set count is 3.  The basic (objectClass=*) filter is used.
+     *
+     * @param uid the uid RDN attribute value for the user under ou=users,ou=system
+     * @param password the password of the user
+     * @return true if the search succeeds as expected, false otherwise
+     * @throws NamingException if there are problems conducting the search
+     */
+    private boolean checkCanSearchAs( String uid, String password ) throws NamingException
+    {
+        return checkCanSearchAs( uid, password, "(objectClass=*)", null, 3 );
+    }
+
+
+    /**
+     * Performs a single level search as a specific user on newly created data and checks
+     * that result set count is equal to a user specified amount.  The basic
+     * (objectClass=*) filter is used.
+     *
+     * @param uid the uid RDN attribute value for the user under ou=users,ou=system
+     * @param password the password of the user
+     * @param resultSetSz the expected size of the results
+     * @return true if the search succeeds as expected, false otherwise
+     * @throws NamingException if there are problems conducting the search
+     */
+    private boolean checkCanSearchAs( String uid, String password, int resultSetSz ) throws NamingException
+    {
+        return checkCanSearchAs( uid, password, "(objectClass=*)", null, resultSetSz );
+    }
+
+
+    /**
+     * Performs a search as a specific user on newly created data and checks
+     * that result set count is equal to a user specified amount.  The basic
+     * (objectClass=*) filter is used.
+     *
+     * @param uid the uid RDN attribute value for the user under ou=users,ou=system
+     * @param password the password of the user
+     * @param cons search controls
+     * @param resultSetSz the expected size of the results
+     * @return true if the search succeeds as expected, false otherwise
+     * @throws NamingException if there are problems conducting the search
+     */
+    private boolean checkCanSearchAs( String uid, String password, SearchControls cons, int resultSetSz )
+        throws NamingException
+    {
+        return checkCanSearchAs( uid, password, "(objectClass=*)", cons, resultSetSz );
+    }
+
+
+    /**
+     * Performs a search as a specific user on newly created data and checks
+     * that result set count is equal to a user specified amount.
+     *
+     * @param uid the uid RDN attribute value for the user under ou=users,ou=system
+     * @param password the password of the user
+     * @param filter the search filter to use
+     * @param cons search controls
+     * @param resultSetSz the expected size of the results
+     * @return true if the search succeeds as expected, false otherwise
+     * @throws NamingException if there are problems conducting the search
+     */
+    private boolean checkCanSearchAs( String uid, String password, String filter, SearchControls cons, int resultSetSz )
+        throws NamingException
+    {
+        if ( cons == null )
+        {
+            cons = new SearchControls();
+        }
+
+        Name base = addSearchData( new LdapDN(), 3, 10 );
+        Name userDn = new LdapDN( "uid=" + uid + ",ou=users,ou=system" );
+        try
+        {
+            results.clear();
+            DirContext userCtx = getContextAs( userDn, password );
+            NamingEnumeration<SearchResult> list = userCtx.search( base, filter, cons );
+            int counter = 0;
+            
+            while ( list.hasMore() )
+            {
+                SearchResult result = list.next();
+                results.put( result.getName(), result );
+                counter++;
+            }
+            return counter == resultSetSz;
+        }
+        catch ( LdapNoPermissionException e )
+        {
+            return false;
+        }
+        finally
+        {
+            recursivelyDelete( base );
+        }
+    }
+
+
+    /**
+     * Adds an entryACI to specified entry below ou=system and runs a search.  Then it
+     * checks to see the result size is correct.
+     *
+     * @param uid the uid RDN attribute value for the user under ou=users,ou=system
+     * @param password the password of the user
+     * @param cons the search controls
+     * @param rdn the rdn
+     * @param aci the aci
+     * @param resultSetSz the result sz
+     * @return true if the search succeeds as expected, false otherwise
+     * @throws NamingException if there are problems conducting the search
+     */
+    private boolean checkSearchAsWithEntryACI( String uid, String password, SearchControls cons, Name rdn, String aci,
+        int resultSetSz ) throws NamingException
+    {
+        if ( cons == null )
+        {
+            cons = new SearchControls();
+        }
+
+        Name base = addSearchData( new LdapDN(), 3, 10 );
+        addEntryACI( rdn, aci );
+        Name userDn = new LdapDN( "uid=" + uid + ",ou=users,ou=system" );
+        try
+        {
+            results.clear();
+            DirContext userCtx = getContextAs( userDn, password );
+            NamingEnumeration<SearchResult> list = userCtx.search( base, "(objectClass=*)", cons );
+            int counter = 0;
+            
+            while ( list.hasMore() )
+            {
+                SearchResult result = list.next();
+                results.put( result.getName(), result );
+                counter++;
+            }
+            return counter == resultSetSz;
+        }
+        catch ( LdapNoPermissionException e )
+        {
+            return false;
+        }
+        finally
+        {
+            recursivelyDelete( base );
+        }
+    }
+
+
+    /**
+     * Checks to see that the addSearchData() and the recursiveDelete()
+     * functions in this test work properly.
+     *
+     * @throws NamingException if there is a problem with the implementation of
+     * these utility functions
+     */
+    @Test
+    public void testAddSearchData() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        Name base = addSearchData( new LdapDN(), 3, 10 );
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        NamingEnumeration<SearchResult> results = sysRoot.search( base, "(objectClass=*)", controls );
+        int counter = 0;
+        
+        while ( results.hasMore() )
+        {
+            results.next();
+            counter++;
+        }
+
+        assertEquals( 10, counter );
+        recursivelyDelete( base );
+        //noinspection EmptyCatchBlock
+        try
+        {
+            sysRoot.lookup( base );
+            fail();
+        }
+        catch ( LdapNameNotFoundException e )
+        {
+        }
+    }
+
+
+    // -----------------------------------------------------------------------
+    // All or nothing search ACI rule tests
+    // -----------------------------------------------------------------------
+
+    /**
+     * Checks to make sure group membership based userClass works for add operations.
+     *
+     * @throws javax.naming.NamingException if the test encounters an error
+     */
+    @Test
+    public void testGrantAdministrators() throws NamingException
+    {
+        // create the non-admin user
+        createUser( "billyd", "billyd" );
+
+        // try an add operation which should fail without any ACI
+        assertFalse( checkCanSearchAs( "billyd", "billyd" ) );
+
+        // Gives search perms to all users in the Administrators group for
+        // entries and all attribute types and values
+        createAccessControlSubentry( "searchAdmin", "{ " + "identificationTag \"searchAci\", " + "precedence 14, "
+            + "authenticationLevel none, " + "itemOrUserFirst userFirst: { "
+            + "userClasses { userGroup { \"cn=Administrators,ou=groups,ou=system\" } }, " + "userPermissions { { "
+            + "protectedItems {entry, allUserAttributeTypesAndValues}, "
+            + "grantsAndDenials { grantRead, grantReturnDN, grantBrowse } } } } }" );
+
+        // see if we can now add that test entry which we could not before
+        // add op should still fail since billd is not in the admin group
+        assertFalse( checkCanSearchAs( "billyd", "billyd" ) );
+
+        // now add billyd to the Administrator group and try again
+        addUserToGroup( "billyd", "Administrators" );
+
+        // try an add operation which should succeed with ACI and group membership change
+        assertTrue( checkCanSearchAs( "billyd", "billyd" ) );
+    }
+
+
+    /**
+     * Checks to make sure name based userClass works for search operations.
+     *
+     * @throws javax.naming.NamingException if the test encounters an error
+     */
+    @Test
+    public void testGrantSearchByName() throws NamingException
+    {
+        // create the non-admin user
+        createUser( "billyd", "billyd" );
+
+        // try an add operation which should fail without any ACI
+        assertFalse( checkCanSearchAs( "billyd", "billyd" ) );
+
+        // now add a subentry that enables user billyd to add an entry below ou=system
+        createAccessControlSubentry( "billydSearch", "{ " + "identificationTag \"searchAci\", " + "precedence 14, "
+            + "authenticationLevel none, " + "itemOrUserFirst userFirst: { "
+            + "userClasses { name { \"uid=billyd,ou=users,ou=system\" } }, " + "userPermissions { { "
+            + "protectedItems {entry, allUserAttributeTypesAndValues}, "
+            + "grantsAndDenials { grantRead, grantReturnDN, grantBrowse } } } } }" );
+
+        // should work now that billyd is authorized by name
+        assertTrue( checkCanSearchAs( "billyd", "billyd" ) );
+    }
+
+
+    /**
+     * Checks to make sure name based userClass works for search operations
+     * when we vary the case of the DN.
+     *
+     * @throws javax.naming.NamingException if the test encounters an error
+     */
+    @Test
+    public void testGrantSearchByNameUserDnCase() throws NamingException
+    {
+        // create the non-admin user
+        createUser( "billyd", "billyd" );
+
+        // try an add operation which should fail without any ACI
+        assertFalse( checkCanSearchAs( "BillyD", "billyd" ) );
+
+        // now add a subentry that enables user billyd to add an entry below ou=system
+        createAccessControlSubentry( "billydSearch", "{ " + "identificationTag \"searchAci\", " + "precedence 14, "
+            + "authenticationLevel none, " + "itemOrUserFirst userFirst: { "
+            + "userClasses { name { \"uid=billyd,ou=users,ou=system\" } }, " + "userPermissions { { "
+            + "protectedItems {entry, allUserAttributeTypesAndValues}, "
+            + "grantsAndDenials { grantRead, grantReturnDN, grantBrowse } } } } }" );
+
+        // should work now that billyd is authorized by name
+        assertTrue( checkCanSearchAs( "BillyD", "billyd" ) );
+    }
+
+
+    /**
+     * Checks to make sure subtree based userClass works for search operations.
+     *
+     * @throws javax.naming.NamingException if the test encounters an error
+     */
+    @Test
+    public void testGrantSearchBySubtree() throws NamingException
+    {
+        // create the non-admin user
+        createUser( "billyd", "billyd" );
+
+        // try an add operation which should fail without any ACI
+        assertFalse( checkCanSearchAs( "billyd", "billyd" ) );
+
+        // now add a subentry that enables user billyd to add an entry below ou=system
+        createAccessControlSubentry( "billySearchBySubtree", "{ " + "identificationTag \"searchAci\", "
+            + "precedence 14, " + "authenticationLevel none, " + "itemOrUserFirst userFirst: { "
+            + "userClasses { subtree { { base \"ou=users,ou=system\" } } }, " + "userPermissions { { "
+            + "protectedItems {entry, allUserAttributeTypesAndValues}, "
+            + "grantsAndDenials {  grantRead, grantReturnDN, grantBrowse } } } } }" );
+
+        // should work now that billyd is authorized by the subtree userClass
+        assertTrue( checkCanSearchAs( "billyd", "billyd" ) );
+    }
+
+
+    /**
+     * Checks to make sure <b>allUsers</b> userClass works for search operations.
+     *
+     * @throws javax.naming.NamingException if the test encounters an error
+     */
+    @Test
+    public void testGrantSearchAllUsers() throws NamingException
+    {
+        // create the non-admin user
+        createUser( "billyd", "billyd" );
+
+        // try an search operation which should fail without any ACI
+        assertFalse( checkCanSearchAs( "billyd", "billyd" ) );
+
+        // now add a subentry that enables anyone to search an entry below ou=system
+        createAccessControlSubentry( "anybodySearch", "{ " + "identificationTag \"searchAci\", " + "precedence 14, "
+            + "authenticationLevel none, " + "itemOrUserFirst userFirst: { " + "userClasses { allUsers }, "
+            + "userPermissions { { " + "protectedItems {entry, allUserAttributeTypesAndValues}, "
+            + "grantsAndDenials { grantRead, grantReturnDN, grantBrowse } } } } }" );
+
+        // see if we can now search that tree which we could not before
+        // should work now with billyd now that all users are authorized
+        assertTrue( checkCanSearchAs( "billyd", "billyd" ) );
+    }
+
+
+    // -----------------------------------------------------------------------
+    //
+    // -----------------------------------------------------------------------
+
+    /**
+     * Checks to make sure search does not return entries not assigned the
+     * perscriptiveACI and that it does not fail with an exception.
+     *
+     * @throws javax.naming.NamingException if the test encounters an error
+     */
+    @Test
+    public void testSelectiveGrantsAllUsers() throws NamingException
+    {
+        // create the non-admin user
+        createUser( "billyd", "billyd" );
+
+        // try an add operation which should fail without any ACI
+        SearchControls cons = new SearchControls();
+        cons.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        assertFalse( checkCanSearchAs( "billyd", "billyd", cons, 4 ) );
+
+        // now add a subentry that enables anyone to add an entry below ou=system
+        // down two more rdns for DNs of a max size of 3
+        createAccessControlSubentry( "anybodySearch", "{ maximum 2 }", "{ " + "identificationTag \"searchAci\", "
+            + "precedence 14, " + "authenticationLevel none, " + "itemOrUserFirst userFirst: { "
+            + "userClasses { allUsers }, " + "userPermissions { { "
+            + "protectedItems {entry, allUserAttributeTypesAndValues}, "
+            + "grantsAndDenials { grantRead, grantReturnDN, grantBrowse } } } } }" );
+
+        // see if we can now add that test entry which we could not before
+        // should work now with billyd now that all users are authorized
+        assertTrue( checkCanSearchAs( "billyd", "billyd", cons, 4 ) );
+    }
+
+
+    /**
+     * Checks to make sure attributeTypes are not present when permissions are
+     * not given for reading them and their values.
+     *
+     * @throws javax.naming.NamingException if the test encounters an error
+     */
+    @Test
+    public void testHidingAttributes() throws NamingException
+    {
+        // create the non-admin user
+        createUser( "billyd", "billyd" );
+
+        // try an add operation which should fail without any ACI
+        SearchControls cons = new SearchControls();
+        cons.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        assertFalse( checkCanSearchAs( "billyd", "billyd", cons, 4 ) );
+
+        // now add a subentry that enables anyone to search an entry below ou=system
+        // down two more rdns for DNs of a max size of 3.  It only grants access to
+        // the ou and objectClass attributes however.
+        createAccessControlSubentry( "excluseTelephoneNumber", "{ maximum 2 }", "{ "
+            + "identificationTag \"searchAci\", " + "precedence 14, " + "authenticationLevel none, "
+            + "itemOrUserFirst userFirst: { " + "userClasses { allUsers }, " + "userPermissions { { "
+            + "protectedItems {entry, allAttributeValues { ou, objectClass } }, "
+            + "grantsAndDenials { grantRead, grantReturnDN, grantBrowse } } } } }" );
+
+        // see if we can now add that search and find 4 entries
+        assertTrue( checkCanSearchAs( "billyd", "billyd", cons, 4 ) );
+
+        // check to make sure the telephoneNumber attribute is not present in results
+        for ( SearchResult result : results.values() )
+        {
+            assertNull( result.getAttributes().get( "telephoneNumber" ) );
+        }
+
+        // delete the subentry to test more general rule's inclusion of telephoneNumber
+        deleteAccessControlSubentry( "excluseTelephoneNumber" );
+
+        // now add a subentry that enables anyone to search an entry below ou=system
+        // down two more rdns for DNs of a max size of 3.  This time we should be able
+        // to see the telephoneNumber attribute
+        createAccessControlSubentry( "includeAllAttributeTypesAndValues", "{ maximum 2 }", "{ "
+            + "identificationTag \"searchAci\", " + "precedence 14, " + "authenticationLevel none, "
+            + "itemOrUserFirst userFirst: { " + "userClasses { allUsers }, " + "userPermissions { { "
+            + "protectedItems {entry, allUserAttributeTypesAndValues }, "
+            + "grantsAndDenials { grantRead, grantReturnDN, grantBrowse } } } } }" );
+
+        // again we should find four entries
+        assertTrue( checkCanSearchAs( "billyd", "billyd", cons, 4 ) );
+
+        // check now to make sure the telephoneNumber attribute is present in results
+        for ( SearchResult result : results.values() )
+        {
+            assertNotNull( result.getAttributes().get( "telephoneNumber" ) );
+        }
+    }
+
+
+    /**
+     * Checks to make sure specific attribute values are not present when
+     * read permission is denied.
+     *
+     * @throws javax.naming.NamingException if the test encounters an error
+     */
+    @Test
+    public void testHidingAttributeValues() throws NamingException
+    {
+        // create the non-admin user
+        createUser( "billyd", "billyd" );
+
+        // try an add operation which should fail without any ACI
+        assertFalse( checkCanSearchAs( "billyd", "billyd", 3 ) );
+
+        // now add a subentry that enables anyone to search an entry below ou=system
+        // down two more rdns for DNs of a max size of 3.  It only grants access to
+        // the ou and objectClass attributes however.
+        createAccessControlSubentry(
+            "excluseOUValue",
+            "{ maximum 2 }",
+            "{ "
+                + "identificationTag \"searchAci\", "
+                + "precedence 14, "
+                + "authenticationLevel none, "
+                + "itemOrUserFirst userFirst: { "
+                + "userClasses { allUsers }, "
+                + "userPermissions { { "
+                + "protectedItems {entry, attributeType { ou }, allAttributeValues { objectClass }, attributeValue { ou=0, ou=1, ou=2 } }, "
+                + "grantsAndDenials { grantRead, grantReturnDN, grantBrowse } } } } }" );
+
+        // see if we can now add that search and find 4 entries
+        assertTrue( checkCanSearchAs( "billyd", "billyd", 3 ) );
+
+        // check to make sure the ou attribute value "testEntry" is not present in results
+        for ( SearchResult result : results.values() )
+        {
+            assertFalse( result.getAttributes().get( "ou" ).contains( "testEntry" ) );
+        }
+
+        // delete the subentry to test more general rule's inclusion of all values
+        deleteAccessControlSubentry( "excluseOUValue" );
+
+        // now add a subentry that enables anyone to search an entry below ou=system
+        // down two more rdns for DNs of a max size of 3.  This time we should be able
+        // to see the telephoneNumber attribute
+        createAccessControlSubentry( "includeAllAttributeTypesAndValues", "{ maximum 2 }", "{ "
+            + "identificationTag \"searchAci\", " + "precedence 14, " + "authenticationLevel none, "
+            + "itemOrUserFirst userFirst: { " + "userClasses { allUsers }, " + "userPermissions { { "
+            + "protectedItems {entry, allUserAttributeTypesAndValues }, "
+            + "grantsAndDenials { grantRead, grantReturnDN, grantBrowse } } } } }" );
+
+        // again we should find four entries
+        assertTrue( checkCanSearchAs( "billyd", "billyd", 3 ) );
+
+        // check now to make sure the telephoneNumber attribute is present in results
+        for ( SearchResult result : results.values() )
+        {
+            assertTrue( result.getAttributes().get( "ou" ).contains( "testEntry" ) );
+        }
+    }
+
+
+    /**
+     * Adds a perscriptiveACI to allow search, tests for success, then adds entryACI
+     * to deny read, browse and returnDN to a specific entry and checks to make sure
+     * that entry cannot be accessed via search as a specific user.
+     *
+     * @throws NamingException if the test is broken
+     */
+    @Test
+    public void testPerscriptiveGrantWithEntryDenial() throws NamingException
+    {
+        // create the non-admin user
+        createUser( "billyd", "billyd" );
+
+        // now add an entryACI denies browse, read and returnDN to a specific entry
+        String aci = "{ " + "identificationTag \"denyAci\", " + "precedence 14, " + "authenticationLevel none, "
+            + "itemOrUserFirst userFirst: { " + "userClasses { allUsers }, " + "userPermissions { { "
+            + "protectedItems {entry, allUserAttributeTypesAndValues}, "
+            + "grantsAndDenials { denyRead, denyReturnDN, denyBrowse } } } } }";
+
+        // try a search operation which should fail without any prescriptive ACI
+        SearchControls cons = new SearchControls();
+        cons.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        LdapDN rdn = new LdapDN( "ou=tests" );
+        assertFalse( checkSearchAsWithEntryACI( "billyd", "billyd", cons, rdn, aci, 9 ) );
+
+        // now add a subentry that enables anyone to search below ou=system
+        createAccessControlSubentry( "anybodySearch", "{ " + "identificationTag \"searchAci\", " + "precedence 14, "
+            + "authenticationLevel none, " + "itemOrUserFirst userFirst: { " + "userClasses { allUsers }, "
+            + "userPermissions { { " + "protectedItems {entry, allUserAttributeTypesAndValues}, "
+            + "grantsAndDenials { grantRead, grantReturnDN, grantBrowse } } } } }" );
+
+        // see if we can now search the tree which we could not before
+        // should work with billyd now that all users are authorized
+        // we should NOT see the entry we are about to deny access to
+        assertTrue( checkSearchAsWithEntryACI( "billyd", "billyd", cons, rdn, aci, 9 ) );
+        assertNull( results.get( "ou=tests,ou=system" ) );
+
+        // try without the entry ACI .. just perscriptive and see ou=tests,ou=system
+        assertTrue( checkCanSearchAs( "billyd", "billyd", cons, 10 ) );
+        assertNotNull( results.get( "ou=tests,ou=system" ) );
+    }
+
+
+    /**
+     * Adds a perscriptiveACI to allow search, tests for success, then adds entryACI
+     * to deny read, browse and returnDN to a specific entry and checks to make sure
+     * that entry cannot be accessed via search as a specific user.  Here the
+     * precidence of the ACI is put to the test.
+     *
+     * @throws NamingException if the test is broken
+     */
+    @Test
+    public void testPerscriptiveGrantWithEntryDenialWithPrecidence() throws NamingException
+    {
+        // create the non-admin user
+        createUser( "billyd", "billyd" );
+
+        // now add an entryACI denies browse, read and returnDN to a specific entry
+        String aci = "{ " + "identificationTag \"denyAci\", " + "precedence 14, " + "authenticationLevel none, "
+            + "itemOrUserFirst userFirst: { " + "userClasses { allUsers }, " + "userPermissions { { "
+            + "protectedItems {entry, allUserAttributeTypesAndValues}, "
+            + "grantsAndDenials { denyRead, denyReturnDN, denyBrowse } } } } }";
+
+        // try a search operation which should fail without any prescriptive ACI
+        SearchControls cons = new SearchControls();
+        cons.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        LdapDN rdn = new LdapDN( "ou=tests" );
+        assertFalse( checkSearchAsWithEntryACI( "billyd", "billyd", cons, rdn, aci, 9 ) );
+
+        // now add a subentry that enables anyone to search below ou=system
+        createAccessControlSubentry( "anybodySearch", "{ " + "identificationTag \"searchAci\", " + "precedence 15, "
+            + "authenticationLevel none, " + "itemOrUserFirst userFirst: { " + "userClasses { allUsers }, "
+            + "userPermissions { { " + "protectedItems {entry, allUserAttributeTypesAndValues}, "
+            + "grantsAndDenials { grantRead, grantReturnDN, grantBrowse } } } } }" );
+
+        // see if we can now search the tree which we could not before
+        // should work with billyd now that all users are authorized
+        // we should also see the entry we are about to deny access to
+        // we see it because the precidence of the grant is greater
+        // than the precedence of the denial
+        assertTrue( checkSearchAsWithEntryACI( "billyd", "billyd", cons, rdn, aci, 10 ) );
+        assertNotNull( results.get( "ou=tests,ou=system" ) );
+
+        // now add an entryACI denies browse, read and returnDN to a specific entry
+        // but this time the precedence will be higher than that of the grant
+        aci = "{ " + "identificationTag \"denyAci\", " + "precedence 16, " + "authenticationLevel none, "
+            + "itemOrUserFirst userFirst: { " + "userClasses { allUsers }, " + "userPermissions { { "
+            + "protectedItems {entry, allUserAttributeTypesAndValues}, "
+            + "grantsAndDenials { denyRead, denyReturnDN, denyBrowse } } } } }";
+
+        // see if we can now search the tree which we could not before
+        // should work with billyd now that all users are authorized
+        // we should NOT see the entry we are about to deny access to
+        // we do NOT see it because the precidence of the grant is less
+        // than the precedence of the denial - so the denial wins
+        assertTrue( checkSearchAsWithEntryACI( "billyd", "billyd", cons, rdn, aci, 9 ) );
+        assertNull( results.get( "ou=tests,ou=system" ) );
+    }
+
+
+    /**
+     * Performs an object level search on the specified subentry relative to ou=system as a specific user.
+     *
+     * @param uid the uid RDN attribute value of the user to perform the search as
+     * @param password the password of the user
+     * @param rdn the relative name to the subentry under the ou=system AP
+     * @return the single search result if access is allowed or null
+     * @throws NamingException if the search fails w/ exception other than no permission
+     */
+    private SearchResult checkCanSearhSubentryAs( String uid, String password, Name rdn ) throws NamingException
+    {
+        DirContext userCtx = getContextAs( new LdapDN( "uid=" + uid + ",ou=users,ou=system" ), password );
+        SearchControls cons = new SearchControls();
+        cons.setSearchScope( SearchControls.OBJECT_SCOPE );
+        SearchResult result = null;
+        NamingEnumeration<SearchResult> list = null;
+
+        //noinspection EmptyCatchBlock
+        try
+        {
+            list = userCtx.search( rdn, "(objectClass=*)", cons );
+            if ( list.hasMore() )
+            {
+                result = list.next();
+                list.close();
+                return result;
+            }
+        }
+        catch ( LdapNoPermissionException e )
+        {
+        }
+        finally
+        {
+            if ( list != null )
+            {
+                list.close();
+            }
+        }
+
+        return result;
+    }
+
+
+    @Test
+    public void testSubentryAccess() throws NamingException
+    {
+        // create the non-admin user
+        createUser( "billyd", "billyd" );
+
+        // now add a subentry that enables anyone to search below ou=system
+        createAccessControlSubentry( "anybodySearch", "{ " + "identificationTag \"searchAci\", " + "precedence 14, "
+            + "authenticationLevel none, " + "itemOrUserFirst userFirst: { " + "userClasses { allUsers }, "
+            + "userPermissions { { " + "protectedItems {entry, allUserAttributeTypesAndValues}, "
+            + "grantsAndDenials { grantRead, grantReturnDN, grantBrowse } } } } }" );
+
+        // check and see if we can access the subentry now
+        assertNotNull( checkCanSearhSubentryAs( "billyd", "billyd", new LdapDN( "cn=anybodySearch" ) ) );
+
+        // now add a denial to prevent all users except the admin from accessing the subentry
+        addSubentryACI( "{ " + "identificationTag \"searchAci\", " + "precedence 14, " + "authenticationLevel none, "
+            + "itemOrUserFirst userFirst: { " + "userClasses { allUsers }, " + "userPermissions { { "
+            + "protectedItems {entry, allUserAttributeTypesAndValues}, "
+            + "grantsAndDenials { denyRead, denyReturnDN, denyBrowse } } } } }" );
+
+        // now we should not be able to access the subentry with a search
+        assertNull( checkCanSearhSubentryAs( "billyd", "billyd", new LdapDN( "cn=anybodySearch" ) ) );
+    }
+
+
+    @Test
+    public void testGetMatchedName() throws NamingException
+    {
+        // create the non-admin user
+        createUser( "billyd", "billyd" );
+
+        // now add a subentry that enables anyone to search/lookup and disclose on error
+        // below ou=system, with the exclusion of ou=groups and everything below it
+        createAccessControlSubentry( "selectiveDiscloseOnError", "{ specificExclusions { chopBefore:\"ou=groups\" } }",
+            "{ " + "identificationTag \"searchAci\", " + "precedence 14, " + "authenticationLevel none, "
+                + "itemOrUserFirst userFirst: { " + "userClasses { allUsers }, " + "userPermissions { { "
+                + "protectedItems {entry, allUserAttributeTypesAndValues}, "
+                + "grantsAndDenials { grantRead, grantReturnDN, grantBrowse, grantDiscloseOnError } } } } }" );
+
+        // get a context as the user and try a lookup of a non-existant entry under ou=groups,ou=system
+        DirContext userCtx = getContextAs( new LdapDN( "uid=billyd,ou=users,ou=system" ), "billyd" );
+        try
+        {
+            userCtx.lookup( "cn=blah,ou=groups" );
+        }
+        catch ( NamingException e )
+        {
+            Name matched = e.getResolvedName();
+
+            // we should not see ou=groups,ou=system for the remaining name
+            assertEquals( matched.toString(), "ou=system" );
+        }
+
+        // now delete and replace subentry with one that does not excluse ou=groups,ou=system
+        deleteAccessControlSubentry( "selectiveDiscloseOnError" );
+        createAccessControlSubentry( "selectiveDiscloseOnError", "{ " + "identificationTag \"searchAci\", "
+            + "precedence 14, " + "authenticationLevel none, " + "itemOrUserFirst userFirst: { "
+            + "userClasses { allUsers }, " + "userPermissions { { "
+            + "protectedItems {entry, allUserAttributeTypesAndValues}, "
+            + "grantsAndDenials { grantRead, grantReturnDN, grantBrowse, grantDiscloseOnError } } } } }" );
+
+        // now try a lookup of a non-existant entry under ou=groups,ou=system again
+        try
+        {
+            userCtx.lookup( "cn=blah,ou=groups" );
+        }
+        catch ( NamingException e )
+        {
+            Name matched = e.getResolvedName();
+
+            // we should not see ou=groups,ou=system for the remaining name
+            assertEquals( matched.toString(), "ou=groups,ou=system" );
+        }
+    }
+}
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/changelog/DefaultChangeLogIT.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/changelog/DefaultChangeLogIT.java
new file mode 100644
index 0000000..8cdcd2a
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/changelog/DefaultChangeLogIT.java
@@ -0,0 +1,409 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements.  See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership.  The ASF licenses this file

+ * to you 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.

+ */

+package org.apache.directory.server.core.changelog;

+

+

+import org.apache.directory.server.core.DirectoryService;

+import org.apache.directory.server.core.integ.CiRunner;

+import static org.apache.directory.server.core.integ.state.TestServiceContext.shutdown;

+import static org.apache.directory.server.core.integ.state.TestServiceContext.startup;

+import static org.apache.directory.server.core.integ.IntegrationUtils.getSystemContext;

+import org.apache.directory.shared.ldap.exception.LdapNameNotFoundException;

+import org.apache.directory.shared.ldap.message.AttributeImpl;

+import org.apache.directory.shared.ldap.message.AttributesImpl;

+import org.apache.directory.shared.ldap.message.ModificationItemImpl;

+import static org.junit.Assert.assertTrue;

+import static org.junit.Assert.assertNull;

+import static org.junit.Assert.assertNotNull;

+import static org.junit.Assert.assertEquals;

+import static org.junit.Assert.fail;

+import org.junit.Test;

+import org.junit.runner.RunWith;

+import org.slf4j.Logger;

+import org.slf4j.LoggerFactory;

+

+import javax.naming.NamingException;

+import javax.naming.directory.Attribute;

+import javax.naming.directory.Attributes;

+import javax.naming.directory.DirContext;

+import javax.naming.ldap.LdapContext;

+import java.util.Arrays;

+

+

+/**

+ * Used to test the default change log implementation with an in memory

+ * change log store.  Note that this will probably be removed since this

+ * functionality will be used and tested anyway in all other test cases.

+ *

+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>

+ * @version $Rev$, $Date$

+ */

+@RunWith ( CiRunner.class )

+public class DefaultChangeLogIT

+{

+    public static final Logger LOG = LoggerFactory.getLogger( DefaultChangeLogIT.class );

+

+    public static DirectoryService service;

+

+

+//    service.setShutdownHookEnabled( false );

+

+    @Test

+    public void testManyTagsPersistenceAcrossRestarts() throws NamingException, InterruptedException

+    {

+        LdapContext sysRoot = getSystemContext( service );

+        long revision = service.getChangeLog().getCurrentRevision();

+

+        // add new test entry

+        AttributesImpl attrs = new AttributesImpl( "objectClass", "organizationalUnit", true );

+        attrs.put( "ou", "test0" );

+        sysRoot.createSubcontext( "ou=test0", attrs );

+        assertEquals( revision + 1, service.getChangeLog().getCurrentRevision() );

+

+        Tag t0 = service.getChangeLog().tag();

+        assertEquals( t0, service.getChangeLog().getLatest() );

+        assertEquals( revision + 1, service.getChangeLog().getCurrentRevision() );

+        assertEquals( revision + 1, t0.getRevision() );

+

+        // add another test entry

+        attrs = new AttributesImpl( "objectClass", "organizationalUnit", true );

+        attrs.put( "ou", "test1" );

+        sysRoot.createSubcontext( "ou=test1", attrs );

+        assertEquals( revision + 2, service.getChangeLog().getCurrentRevision() );

+

+        Tag t1 = service.getChangeLog().tag();

+        assertEquals( t1, service.getChangeLog().getLatest() );

+        assertEquals( revision + 2, service.getChangeLog().getCurrentRevision() );

+        assertEquals( revision + 2, t1.getRevision() );

+

+        shutdown();

+        startup();

+

+        assertEquals( revision + 2, service.getChangeLog().getCurrentRevision() );

+        assertEquals( t1, service.getChangeLog().getLatest() );

+        assertEquals( revision + 2, t1.getRevision() );

+

+        // add third test entry

+        attrs = new AttributesImpl( "objectClass", "organizationalUnit", true );

+        attrs.put( "ou", "test2" );

+        sysRoot.createSubcontext( "ou=test2", attrs );

+        assertEquals( revision + 3, service.getChangeLog().getCurrentRevision() );

+

+        service.revert();

+        sysRoot.getAttributes( "ou=test0" ); // test present

+        sysRoot.getAttributes( "ou=test1" ); // test present

+        assertNotPresent( sysRoot, "ou=test2" );

+        assertEquals( revision + 4, service.getChangeLog().getCurrentRevision() );

+        assertEquals( t1, service.getChangeLog().getLatest() );

+

+        service.revert( t0.getRevision() );

+        sysRoot.getAttributes( "ou=test0" ); // test present

+        assertNotPresent( sysRoot, "ou=test1" );

+        assertNotPresent( sysRoot, "ou=test2" );

+        assertEquals( revision + 7, service.getChangeLog().getCurrentRevision() );

+        assertEquals( t1, service.getChangeLog().getLatest() );

+

+        // no sync this time but should happen automatically

+        shutdown();

+        startup();

+        assertEquals( revision + 7, service.getChangeLog().getCurrentRevision() );

+        assertEquals( t1, service.getChangeLog().getLatest() );

+        assertEquals( revision + 2, t1.getRevision() );

+

+        service.revert( revision );

+        assertNotPresent( sysRoot, "ou=test0" );

+        assertNotPresent( sysRoot, "ou=test1" );

+        assertNotPresent( sysRoot, "ou=test2" );

+        assertEquals( revision + 14, service.getChangeLog().getCurrentRevision() );

+        assertEquals( t1, service.getChangeLog().getLatest() );

+    }

+

+

+    @Test

+    public void testTagPersistenceAcrossRestarts() throws NamingException, InterruptedException

+    {

+        LdapContext sysRoot = getSystemContext( service );

+        long revision = service.getChangeLog().getCurrentRevision();

+

+        Tag t0 = service.getChangeLog().tag();

+        assertEquals( t0, service.getChangeLog().getLatest() );

+        assertEquals( revision, service.getChangeLog().getCurrentRevision() );

+

+        // add new test entry

+        AttributesImpl attrs = new AttributesImpl( "objectClass", "organizationalUnit", true );

+        attrs.put( "ou", "test" );

+        sysRoot.createSubcontext( "ou=test", attrs );

+        assertEquals( revision + 1, service.getChangeLog().getCurrentRevision() );

+

+        shutdown();

+        startup();

+

+        assertEquals( revision + 1, service.getChangeLog().getCurrentRevision() );

+        assertEquals( t0, service.getChangeLog().getLatest() );

+

+        service.revert();

+        assertNotPresent( sysRoot, "ou=test" );

+        assertEquals( revision + 2, service.getChangeLog().getCurrentRevision() );

+        assertEquals( t0, service.getChangeLog().getLatest() );

+    }

+

+

+    @Test

+    public void testRevertAddOperations() throws NamingException

+    {

+        LdapContext sysRoot = getSystemContext( service );

+        Tag t0 = service.getChangeLog().tag();

+        AttributesImpl attrs = new AttributesImpl( "objectClass", "organizationalUnit", true );

+        attrs.put( "ou", "test" );

+        sysRoot.createSubcontext( "ou=test", attrs );

+

+        assertNotNull( sysRoot.getAttributes( "ou=test" ) );

+        service.revert( t0.getRevision() );

+

+        try

+        {

+            sysRoot.getAttributes( "ou=test" );

+            fail( "Should not be able to find the entry!" );

+        }

+        catch ( NamingException ne )

+        {

+            assertTrue( ne instanceof LdapNameNotFoundException );

+        }

+    }

+

+

+    @Test

+    public void testRevertAddAndDeleteOperations() throws NamingException

+    {

+        LdapContext sysRoot = getSystemContext( service );

+        Tag t0 = service.getChangeLog().tag();

+

+        // add new test entry

+        AttributesImpl attrs = new AttributesImpl( "objectClass", "organizationalUnit", true );

+        attrs.put( "ou", "test" );

+        sysRoot.createSubcontext( "ou=test", attrs );

+

+        // assert presence

+        assertNotNull( sysRoot.getAttributes( "ou=test" ) );

+

+        // delete the test entry and test that it is gone

+        sysRoot.destroySubcontext( "ou=test" );

+        assertNotPresent( sysRoot, "ou=test" );

+

+        // now revert back to begining the added entry is still gone

+        service.revert( t0.getRevision() );

+        assertNotPresent( sysRoot, "ou=test" );

+    }

+

+

+    @Test

+    public void testRevertDeleteOperations() throws NamingException

+    {

+        LdapContext sysRoot = getSystemContext( service );

+        AttributesImpl attrs = new AttributesImpl( "objectClass", "organizationalUnit", true );

+        attrs.put( "ou", "test" );

+        sysRoot.createSubcontext( "ou=test", attrs );

+

+        // tag after the addition before deletion

+        Tag t0 = service.getChangeLog().tag();

+        assertNotNull( sysRoot.getAttributes( "ou=test" ) );

+

+        // delete the test entry and test that it is gone

+        sysRoot.destroySubcontext( "ou=test" );

+        assertNotPresent( sysRoot, "ou=test" );

+

+        // now revert and assert that the added entry re-appears

+        service.revert( t0.getRevision() );

+        assertNotNull( sysRoot.getAttributes( "ou=test" ) );

+    }

+

+

+    @Test

+    public void testRevertRenameOperations() throws NamingException

+    {

+        LdapContext sysRoot = getSystemContext( service );

+        AttributesImpl attrs = new AttributesImpl( "objectClass", "organizationalUnit", true );

+        attrs.put( "ou", "oldname" );

+        sysRoot.createSubcontext( "ou=oldname", attrs );

+

+        // tag after the addition before rename

+        Tag t0 = service.getChangeLog().tag();

+        assertNotNull( sysRoot.getAttributes( "ou=oldname" ) );

+

+        // rename the test entry and test that the rename occurred

+        sysRoot.rename( "ou=oldname", "ou=newname" );

+        assertNotPresent( sysRoot, "ou=oldname" );

+        assertNotNull( sysRoot.getAttributes( "ou=newname" ) );

+

+        // now revert and assert that the rename was reversed

+        service.revert( t0.getRevision() );

+        assertNotPresent( sysRoot, "ou=newname" );

+        assertNotNull( sysRoot.getAttributes( "ou=oldname" ) );

+    }

+

+

+    @Test

+    public void testRevertModifyOperations() throws NamingException

+    {

+        LdapContext sysRoot = getSystemContext( service );

+        AttributesImpl attrs = new AttributesImpl( "objectClass", "organizationalUnit", true );

+        attrs.put( "ou", "test5" );

+        sysRoot.createSubcontext( "ou=test5", attrs );

+

+        // -------------------------------------------------------------------

+        // Modify ADD Test

+        // -------------------------------------------------------------------

+

+        // tag after the addition before modify ADD

+        Tag t0 = service.getChangeLog().tag();

+        assertNotNull( sysRoot.getAttributes( "ou=test5" ) );

+

+        // modify the test entry to add description and test new attr appears

+        sysRoot.modifyAttributes( "ou=test5", DirContext.ADD_ATTRIBUTE,

+                new AttributesImpl( "description", "a desc value", true ) );

+        Attributes resusitated = sysRoot.getAttributes( "ou=test5" );

+        assertNotNull( resusitated );

+        Attribute description = resusitated.get( "description" );

+        assertNotNull( description );

+        assertEquals( "a desc value", description.get() );

+

+        // now revert and assert that the added entry re-appears

+        service.revert( t0.getRevision() );

+        resusitated = sysRoot.getAttributes( "ou=test5" );

+        assertNotNull( resusitated );

+        assertNull( resusitated.get( "description" ) );

+

+        // -------------------------------------------------------------------

+        // Modify REPLACE Test

+        // -------------------------------------------------------------------

+

+        // add the attribute again and make sure it is old value

+        sysRoot.modifyAttributes( "ou=test5", DirContext.ADD_ATTRIBUTE,

+                new AttributesImpl( "description", "old value", true ) );

+        resusitated = sysRoot.getAttributes( "ou=test5" );

+        assertNotNull( resusitated );

+        description = resusitated.get( "description" );

+        assertNotNull( description );

+        assertEquals( description.get(), "old value" );

+

+        // now tag then replace the value to "new value" and confirm

+        Tag t1 = service.getChangeLog().tag();

+        sysRoot.modifyAttributes( "ou=test5", DirContext.REPLACE_ATTRIBUTE,

+                new AttributesImpl( "description", "new value", true ) );

+        resusitated = sysRoot.getAttributes( "ou=test5" );

+        assertNotNull( resusitated );

+        description = resusitated.get( "description" );

+        assertNotNull( description );

+        assertEquals( description.get(), "new value" );

+

+        // now revert and assert the old value is now reverted

+        service.revert( t1.getRevision() );

+        resusitated = sysRoot.getAttributes( "ou=test5" );

+        assertNotNull( resusitated );

+        description = resusitated.get( "description" );

+        assertNotNull( description );

+        assertEquals( description.get(), "old value" );

+

+

+        // -------------------------------------------------------------------

+        // Modify REMOVE Test

+        // -------------------------------------------------------------------

+

+        Tag t2 = service.getChangeLog().tag();

+        sysRoot.modifyAttributes( "ou=test5", DirContext.REMOVE_ATTRIBUTE,

+                new AttributesImpl( "description", "old value", true ) );

+        resusitated = sysRoot.getAttributes( "ou=test5" );

+        assertNotNull( resusitated );

+        description = resusitated.get( "description" );

+        assertNull( description );

+

+        // now revert and assert the old value is now reverted

+        service.revert( t2.getRevision() );

+        resusitated = sysRoot.getAttributes( "ou=test5" );

+        assertNotNull( resusitated );

+        description = resusitated.get( "description" );

+        assertNotNull( description );

+        assertEquals( description.get(), "old value" );

+

+        // -------------------------------------------------------------------

+        // Modify Multi Operation Test

+        // -------------------------------------------------------------------

+

+        // add a userPassword attribute so we can test replacing it

+        sysRoot.modifyAttributes( "ou=test5", DirContext.ADD_ATTRIBUTE,

+                new AttributesImpl( "userPassword", "to be replaced", true ) );

+        assertPassword( sysRoot.getAttributes( "ou=test5" ), "to be replaced" );

+

+        ModificationItemImpl[] mods = new ModificationItemImpl[]

+        {

+            new ModificationItemImpl( DirContext.REMOVE_ATTRIBUTE,

+                    new AttributeImpl( "description", "old value" ) ),

+            new ModificationItemImpl( DirContext.ADD_ATTRIBUTE,

+                    new AttributeImpl( "seeAlso", "ou=added" ) ),

+            new ModificationItemImpl( DirContext.REPLACE_ATTRIBUTE,

+                    new AttributeImpl( "userPassword", "a replaced value" ) )

+        };

+        Tag t3 = service.getChangeLog().tag();

+

+        // now make the modification and check that description is gone,

+        // seeAlso is added, and that the userPassword has been replaced

+        sysRoot.modifyAttributes( "ou=test5", mods );

+        resusitated = sysRoot.getAttributes( "ou=test5" );

+        assertNotNull( resusitated );

+        description = resusitated.get( "description" );

+        assertNull( description );

+        assertPassword( resusitated, "a replaced value" );

+        Attribute seeAlso = resusitated.get( "seeAlso" );

+        assertNotNull( seeAlso );

+        assertEquals( seeAlso.get(), "ou=added" );

+

+        // now we revert and make sure the old values are as they were

+        service.revert( t3.getRevision() );

+        resusitated = sysRoot.getAttributes( "ou=test5" );

+        assertNotNull( resusitated );

+        description = resusitated.get( "description" );

+        assertNotNull( description );

+        assertEquals( description.get(), "old value" );

+        assertPassword( resusitated, "to be replaced" );

+        seeAlso = resusitated.get( "seeAlso" );

+        assertNull( seeAlso );

+    }

+

+

+    private void assertPassword( Attributes entry, String password ) throws NamingException

+    {

+        Attribute userPassword = entry.get( "userPassword" );

+        assertNotNull( userPassword );

+        Arrays.equals( password.getBytes(), ( byte[] ) userPassword.get() );

+    }

+

+

+    private void assertNotPresent( DirContext ctx, String dn ) throws NamingException

+    {

+        try

+        {

+            ctx.getAttributes( dn );

+            fail( "Should not be able to find the entry " + dn + " but it is still there." );

+        }

+        catch ( NamingException ne )

+        {

+            assertTrue( ne instanceof LdapNameNotFoundException );

+        }

+    }

+}

diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/collective/CollectiveAttributeServiceIT.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/collective/CollectiveAttributeServiceIT.java
new file mode 100644
index 0000000..6bbe977
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/collective/CollectiveAttributeServiceIT.java
@@ -0,0 +1,540 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.collective;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.integ.CiRunner;
+import org.apache.directory.server.core.integ.SetupMode;
+import org.apache.directory.server.core.integ.annotations.Mode;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getSystemContext;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.message.ModificationItemImpl;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+
+import java.util.HashMap;
+import java.util.Map;
+
+
+/**
+ * Test cases for the collective attribute service.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+@RunWith ( CiRunner.class )
+@Mode ( SetupMode.ROLLBACK )
+public class CollectiveAttributeServiceIT
+{
+    public static DirectoryService service;
+
+
+    public Attributes getTestEntry( String cn )
+    {
+        Attributes subentry = new AttributesImpl();
+        Attribute objectClass = new AttributeImpl( "objectClass" );
+        objectClass.add( "top" );
+        objectClass.add( "person" );
+        subentry.put( objectClass );
+        subentry.put( "cn", cn );
+        subentry.put( "sn", "testentry" );
+        return subentry;
+    }
+
+
+    public Attributes getTestSubentry()
+    {
+        Attributes subentry = new AttributesImpl();
+        Attribute objectClass = new AttributeImpl( "objectClass" );
+        objectClass.add( "top" );
+        objectClass.add( "subentry" );
+        objectClass.add( "collectiveAttributeSubentry" );
+        subentry.put( objectClass );
+        subentry.put( "c-ou", "configuration" );
+        subentry.put( "subtreeSpecification", "{ base \"ou=configuration\" }" );
+        subentry.put( "cn", "testsubentry" );
+        return subentry;
+    }
+
+
+    public Attributes getTestSubentry2()
+    {
+        Attributes subentry = new AttributesImpl();
+        Attribute objectClass = new AttributeImpl( "objectClass" );
+        objectClass.add( "top" );
+        objectClass.add( "subentry" );
+        objectClass.add( "collectiveAttributeSubentry" );
+        subentry.put( objectClass );
+        subentry.put( "c-ou", "configuration2" );
+        subentry.put( "subtreeSpecification", "{ base \"ou=configuration\" }" );
+        subentry.put( "cn", "testsubentry2" );
+        return subentry;
+    }
+
+
+    public Attributes getTestSubentry3()
+    {
+        Attributes subentry = new AttributesImpl();
+        Attribute objectClass = new AttributeImpl( "objectClass" );
+        objectClass.add( "top" );
+        objectClass.add( "subentry" );
+        objectClass.add( "collectiveAttributeSubentry" );
+        subentry.put( objectClass );
+        subentry.put( "c-st", "FL" );
+        subentry.put( "subtreeSpecification", "{ base \"ou=configuration\" }" );
+        subentry.put( "cn", "testsubentry3" );
+        return subentry;
+    }
+
+
+    public void addAdministrativeRole( String role ) throws NamingException
+    {
+        Attribute attribute = new AttributeImpl( "administrativeRole" );
+        attribute.add( role );
+        ModificationItemImpl item = new ModificationItemImpl( DirContext.ADD_ATTRIBUTE, attribute );
+        getSystemContext( service ).modifyAttributes( "", new ModificationItemImpl[] { item } );
+    }
+
+
+    public Map<String, Attributes> getAllEntries() throws NamingException
+    {
+        Map<String, Attributes> resultMap = new HashMap<String, Attributes>();
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        controls.setReturningAttributes( new String[]
+            { "+", "*" } );
+        NamingEnumeration<SearchResult> results = getSystemContext( service ).search( "", "(objectClass=*)", controls );
+        
+        while ( results.hasMore() )
+        {
+            SearchResult result = results.next();
+            resultMap.put( result.getName(), result.getAttributes() );
+        }
+        return resultMap;
+    }
+
+
+    public SearchResult getEntry( String name ) throws NamingException
+    {
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        controls.setReturningAttributes( new String[]
+            { "+", "*" } );
+        
+        NamingEnumeration<SearchResult> results = getSystemContext( service ).search( name, "(objectClass=*)", controls );
+        
+        if ( results.hasMore() )
+        {
+            return results.next();
+        }
+        
+        return null;
+    }
+
+
+    public Map<String, Attributes> getAllEntriesRestrictAttributes() throws NamingException
+    {
+        Map<String, Attributes> resultMap = new HashMap<String, Attributes>();
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        controls.setReturningAttributes( new String[]
+            { "cn" } );
+        NamingEnumeration<SearchResult> results = getSystemContext( service ).search( "", "(objectClass=*)", controls );
+        while ( results.hasMore() )
+        {
+            SearchResult result = results.next();
+            resultMap.put( result.getName(), result.getAttributes() );
+        }
+        return resultMap;
+    }
+    
+    
+    public Map<String, Attributes> getAllEntriesCollectiveAttributesOnly() throws NamingException
+    {
+        Map<String, Attributes> resultMap = new HashMap<String, Attributes>();
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        controls.setReturningAttributes( new String[]
+                                                    { "c-ou", "c-st" } );
+        NamingEnumeration<SearchResult> results = getSystemContext( service ).search( "", "(objectClass=*)", controls );
+        
+        while ( results.hasMore() )
+        {
+            SearchResult result = results.next();
+            resultMap.put( result.getName(), result.getAttributes() );
+        }
+        return resultMap;
+    }
+    
+
+    @Test
+    public void testLookup() throws Exception
+    {
+        // -------------------------------------------------------------------
+        // Setup the collective attribute specific administration point
+        // -------------------------------------------------------------------
+
+        addAdministrativeRole( "collectiveAttributeSpecificArea" );
+        getSystemContext( service ).createSubcontext( "cn=testsubentry", getTestSubentry() );
+
+        // -------------------------------------------------------------------
+        // test an entry that should show the collective attribute c-ou
+        // -------------------------------------------------------------------
+
+        Attributes attributes = getSystemContext( service ).getAttributes( "ou=services,ou=configuration" );
+        Attribute c_ou = attributes.get( "c-ou" );
+        assertNotNull( "a collective c-ou attribute should be present", c_ou );
+        assertEquals( "configuration", c_ou.get() );
+
+        // -------------------------------------------------------------------
+        // test an entry that should not show the collective attribute
+        // -------------------------------------------------------------------
+
+        attributes = getSystemContext( service ).getAttributes( "ou=users" );
+        c_ou = attributes.get( "c-ou" );
+        assertNull( "the c-ou collective attribute should not be present", c_ou );
+
+        // -------------------------------------------------------------------
+        // now modify entries included by the subentry to have collectiveExclusions
+        // -------------------------------------------------------------------
+
+        ModificationItemImpl[] items = new ModificationItemImpl[]
+            { new ModificationItemImpl( DirContext.ADD_ATTRIBUTE,
+                new AttributeImpl( "collectiveExclusions", "c-ou" ) ) };
+        getSystemContext( service ).modifyAttributes( "ou=services,ou=configuration", items );
+
+        // entry should not show the c-ou collective attribute anymore
+        attributes = getSystemContext( service ).getAttributes( "ou=services,ou=configuration" );
+        c_ou = attributes.get( "c-ou" );
+        if ( c_ou != null )
+        {
+            assertEquals( "the c-ou collective attribute should not be present", 0, c_ou.size() );
+        }
+
+        // now add more collective subentries - the c-ou should still not show due to exclusions
+        getSystemContext( service ).createSubcontext( "cn=testsubentry2", getTestSubentry2() );
+
+        attributes = getSystemContext( service ).getAttributes( "ou=services,ou=configuration" );
+        c_ou = attributes.get( "c-ou" );
+        if ( c_ou != null )
+        {
+            assertEquals( "the c-ou collective attribute should not be present", 0, c_ou.size() );
+        }
+
+        // entries without the collectiveExclusion should still show both values of c-ou
+        attributes = getSystemContext( service ).getAttributes( "ou=interceptors,ou=configuration" );
+        c_ou = attributes.get( "c-ou" );
+        assertNotNull( "a collective c-ou attribute should be present", c_ou );
+        assertTrue( c_ou.contains( "configuration" ) );
+        assertTrue( c_ou.contains( "configuration2" ) );
+
+        // request the collective attribute specifically
+        
+        attributes = getSystemContext( service ).getAttributes(
+                "ou=interceptors,ou=configuration", new String[] { "c-ou" } );
+        c_ou = attributes.get( "c-ou" );
+        assertNotNull( "a collective c-ou attribute should be present", c_ou );
+        assertTrue( c_ou.contains( "configuration" ) );
+        assertTrue( c_ou.contains( "configuration2" ) );
+        
+        // unspecify the collective attribute in the returning attribute list
+
+        attributes = getSystemContext( service ).getAttributes(
+                "ou=interceptors,ou=configuration", new String[] { "objectClass" } );
+        c_ou = attributes.get( "c-ou" );
+        assertNull( "a collective c-ou attribute should not be present", c_ou );
+        
+        // -------------------------------------------------------------------
+        // now add the subentry for the c-st collective attribute
+        // -------------------------------------------------------------------
+
+        getSystemContext( service ).createSubcontext( "cn=testsubentry3", getTestSubentry3() );
+
+        // the new attribute c-st should appear in the node with the c-ou exclusion
+        attributes = getSystemContext( service ).getAttributes( "ou=services,ou=configuration" );
+        Attribute c_st = attributes.get( "c-st" );
+        assertNotNull( "a collective c-st attribute should be present", c_st );
+        assertTrue( c_st.contains( "FL" ) );
+
+        // in node without exclusions both values of c-ou should appear with c-st value
+        attributes = getSystemContext( service ).getAttributes( "ou=interceptors,ou=configuration" );
+        c_ou = attributes.get( "c-ou" );
+        assertNotNull( "a collective c-ou attribute should be present", c_ou );
+        assertTrue( c_ou.contains( "configuration" ) );
+        assertTrue( c_ou.contains( "configuration2" ) );
+        c_st = attributes.get( "c-st" );
+        assertNotNull( "a collective c-st attribute should be present", c_st );
+        assertTrue( c_st.contains( "FL" ) );
+
+        // -------------------------------------------------------------------
+        // now modify an entry to exclude all collective attributes
+        // -------------------------------------------------------------------
+
+        items = new ModificationItemImpl[]
+            { new ModificationItemImpl( DirContext.REPLACE_ATTRIBUTE, new AttributeImpl( "collectiveExclusions",
+                "excludeAllCollectiveAttributes" ) ) };
+        getSystemContext( service ).modifyAttributes( "ou=interceptors,ou=configuration", items );
+
+        // none of the attributes should appear any longer
+        attributes = getSystemContext( service ).getAttributes( "ou=interceptors,ou=configuration" );
+        c_ou = attributes.get( "c-ou" );
+        if ( c_ou != null )
+        {
+            assertEquals( "the c-ou collective attribute should not be present", 0, c_ou.size() );
+        }
+        c_st = attributes.get( "c-st" );
+        if ( c_st != null )
+        {
+            assertEquals( "the c-st collective attribute should not be present", 0, c_st.size() );
+        }
+    }
+
+
+    @Test
+    public void testSearch() throws Exception
+    {
+        // -------------------------------------------------------------------
+        // Setup the collective attribute specific administration point
+        // -------------------------------------------------------------------
+
+        addAdministrativeRole( "collectiveAttributeSpecificArea" );
+        getSystemContext( service ).createSubcontext( "cn=testsubentry", getTestSubentry() );
+
+        // -------------------------------------------------------------------
+        // test an entry that should show the collective attribute c-ou
+        // -------------------------------------------------------------------
+
+        Map<String, Attributes> entries = getAllEntries();
+        Attributes attributes = entries.get( "ou=services,ou=configuration,ou=system" );
+        Attribute c_ou = attributes.get( "c-ou" );
+        assertNotNull( "a collective c-ou attribute should be present", c_ou );
+        assertEquals( "configuration", c_ou.get() );
+
+        
+        // ------------------------------------------------------------------
+        // test an entry that should show the collective attribute c-ou, 
+        // but restrict returned attributes to c-ou and c-st
+        // ------------------------------------------------------------------
+        
+        entries = getAllEntriesCollectiveAttributesOnly();
+        attributes = entries.get( "ou=services,ou=configuration,ou=system" );
+        c_ou = attributes.get( "c-ou" );
+        assertNotNull( "a collective c-ou attribute should be present", c_ou );
+        assertEquals( "configuration", c_ou.get() );   
+        
+        
+        // -------------------------------------------------------------------
+        // test an entry that should not show the collective attribute
+        // -------------------------------------------------------------------
+
+        attributes = entries.get( "ou=users,ou=system" );
+        c_ou = attributes.get( "c-ou" );
+        assertNull( "the c-ou collective attribute should not be present", c_ou );
+
+        // -------------------------------------------------------------------
+        // now modify entries included by the subentry to have collectiveExclusions
+        // -------------------------------------------------------------------
+
+        ModificationItemImpl[] items = new ModificationItemImpl[]
+            { new ModificationItemImpl( DirContext.ADD_ATTRIBUTE,
+                new AttributeImpl( "collectiveExclusions", "c-ou" ) ) };
+        getSystemContext( service ).modifyAttributes( "ou=services,ou=configuration", items );
+        entries = getAllEntries();
+
+        // entry should not show the c-ou collective attribute anymore
+        attributes = entries.get( "ou=services,ou=configuration,ou=system" );
+        c_ou = attributes.get( "c-ou" );
+        if ( c_ou != null )
+        {
+            assertEquals( "the c-ou collective attribute should not be present", 0, c_ou.size() );
+        }
+
+        // now add more collective subentries - the c-ou should still not show due to exclusions
+        getSystemContext( service ).createSubcontext( "cn=testsubentry2", getTestSubentry2() );
+        entries = getAllEntries();
+
+        attributes = entries.get( "ou=services,ou=configuration,ou=system" );
+        c_ou = attributes.get( "c-ou" );
+        if ( c_ou != null )
+        {
+            assertEquals( "the c-ou collective attribute should not be present", 0, c_ou.size() );
+        }
+
+        // entries without the collectiveExclusion should still show both values of c-ou
+        attributes = entries.get( "ou=interceptors,ou=configuration,ou=system" );
+        c_ou = attributes.get( "c-ou" );
+        assertNotNull( "a collective c-ou attribute should be present", c_ou );
+        assertTrue( c_ou.contains( "configuration" ) );
+        assertTrue( c_ou.contains( "configuration2" ) );
+
+        // -------------------------------------------------------------------
+        // now add the subentry for the c-st collective attribute
+        // -------------------------------------------------------------------
+
+        getSystemContext( service ).createSubcontext( "cn=testsubentry3", getTestSubentry3() );
+        entries = getAllEntries();
+
+        // the new attribute c-st should appear in the node with the c-ou exclusion
+        attributes = entries.get( "ou=services,ou=configuration,ou=system" );
+        Attribute c_st = attributes.get( "c-st" );
+        assertNotNull( "a collective c-st attribute should be present", c_st );
+        assertTrue( c_st.contains( "FL" ) );
+
+        // in node without exclusions both values of c-ou should appear with c-st value
+        attributes = entries.get( "ou=interceptors,ou=configuration,ou=system" );
+        c_ou = attributes.get( "c-ou" );
+        assertNotNull( "a collective c-ou attribute should be present", c_ou );
+        assertTrue( c_ou.contains( "configuration" ) );
+        assertTrue( c_ou.contains( "configuration2" ) );
+        c_st = attributes.get( "c-st" );
+        assertNotNull( "a collective c-st attribute should be present", c_st );
+        assertTrue( c_st.contains( "FL" ) );
+
+        // -------------------------------------------------------------------
+        // now modify an entry to exclude all collective attributes
+        // -------------------------------------------------------------------
+
+        items = new ModificationItemImpl[]
+            { new ModificationItemImpl( DirContext.REPLACE_ATTRIBUTE, new AttributeImpl( "collectiveExclusions",
+                "excludeAllCollectiveAttributes" ) ) };
+        getSystemContext( service ).modifyAttributes( "ou=interceptors,ou=configuration", items );
+        entries = getAllEntries();
+
+        // none of the attributes should appear any longer
+        attributes = entries.get( "ou=interceptors,ou=configuration,ou=system" );
+        c_ou = attributes.get( "c-ou" );
+        if ( c_ou != null )
+        {
+            assertEquals( "the c-ou collective attribute should not be present", 0, c_ou.size() );
+        }
+        c_st = attributes.get( "c-st" );
+        if ( c_st != null )
+        {
+            assertEquals( "the c-st collective attribute should not be present", 0, c_st.size() );
+        }
+
+        // -------------------------------------------------------------------
+        // Now search attributes but restrict returned attributes to cn and ou
+        // -------------------------------------------------------------------
+
+        entries = getAllEntriesRestrictAttributes();
+
+        // we should no longer see collective attributes with restricted return attribs
+        attributes = entries.get( "ou=services,ou=configuration,ou=system" );
+        c_st = attributes.get( "c-st" );
+        assertNull( "a collective c-st attribute should NOT be present", c_st );
+
+        attributes = entries.get( "ou=partitions,ou=configuration,ou=system" );
+        c_ou = attributes.get( "c-ou" );
+        c_st = attributes.get( "c-st" );
+        assertNull( c_ou );
+        assertNull( c_st );
+    }
+    
+    
+    @Test
+    public void testAddRegularEntryWithCollectiveAttribute()
+    {
+        Attributes entry = getTestEntry( "Ersin Er" );
+        entry.put( "c-l", "Turkiye" );
+        try
+        {
+            getSystemContext( service ).createSubcontext( "cn=Ersin Er", entry );
+            fail( "Entry addition with collective attribute should have failed." );
+        }
+        catch ( NamingException e )
+        {
+            // Intended execution point
+        }
+    }
+    
+    
+    @Test
+    public void testModifyRegularEntryAddingCollectiveAttribute() throws NamingException
+    {
+        Attributes entry = getTestEntry( "Ersin Er" );
+        getSystemContext( service ).createSubcontext( "cn=Ersin Er", entry );
+        Attributes changeSet = new AttributesImpl( "c-l", "Turkiye", true );
+        try
+        {
+            
+            getSystemContext( service ).modifyAttributes( "cn=Ersin Er", DirContext.ADD_ATTRIBUTE, changeSet );
+            fail( "Collective attribute addition to non-collectiveAttributeSubentry should have failed." );
+        }
+        catch ( NamingException e )
+        {
+            // Intended execution point
+        }
+    }
+    
+    
+    @Test
+    public void testModifyRegularEntryAddingCollectiveAttribute2() throws NamingException
+    {
+        Attributes entry = getTestEntry( "Ersin Er" );
+        getSystemContext( service ).createSubcontext( "cn=Ersin Er", entry );
+        Attribute change = new AttributeImpl( "c-l", "Turkiye");
+        ModificationItemImpl mod = new ModificationItemImpl(DirContext.ADD_ATTRIBUTE, change);
+        try
+        {
+            getSystemContext( service ).modifyAttributes( "cn=Ersin Er", new ModificationItemImpl[] { mod } );
+            fail( "Collective attribute addition to non-collectiveAttributeSubentry should have failed." );
+        }
+        catch ( NamingException e )
+        {
+            // Intended execution point
+        }
+    }
+    
+    
+    @Test
+    public void testPolymorphicReturnAttrLookup() throws Exception
+    {
+        // -------------------------------------------------------------------
+        // Setup the collective attribute specific administration point
+        // -------------------------------------------------------------------
+    
+        addAdministrativeRole( "collectiveAttributeSpecificArea" );
+        getSystemContext( service ).createSubcontext( "cn=testsubentry", getTestSubentry() );
+    
+        // request the collective attribute's super type specifically
+        Attributes attributes = getSystemContext( service ).getAttributes( "ou=interceptors,ou=configuration",
+            new String[] { "ou" } );
+        Attribute c_ou = attributes.get( "c-ou" );
+        assertNotNull( "a collective c-ou attribute should be present", c_ou );
+        assertTrue( c_ou.contains( "configuration" ) );
+    }
+    
+}
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/configuration/PartitionConfigurationIT.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/configuration/PartitionConfigurationIT.java
new file mode 100644
index 0000000..584c5fa
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/configuration/PartitionConfigurationIT.java
@@ -0,0 +1,91 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.configuration;
+
+
+import junit.framework.Assert;
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.entry.DefaultServerEntry;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.integ.CiRunner;
+import org.apache.directory.server.core.jndi.CoreContextFactory;
+import org.apache.directory.server.core.partition.Partition;
+import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmPartition;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NameNotFoundException;
+import java.util.Hashtable;
+
+
+/**
+ * Tests dynamic partition addition and removal.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+@RunWith ( CiRunner.class )
+public class PartitionConfigurationIT
+{
+    public static DirectoryService service;
+
+
+    @Test
+    public void testAddAndRemove() throws Exception
+    {
+        Partition partition = new JdbmPartition();
+        partition.setId( "removable" );
+        partition.setSuffix( "ou=removable" );
+        
+        ServerEntry ctxEntry = new DefaultServerEntry( service.getRegistries(), new LdapDN( "ou=removable" ) );
+        ctxEntry.put( "objectClass", "top" );
+        ctxEntry.get( "objectClass" ).add( "organizationalUnit" );
+        ctxEntry.put( "ou", "removable" );
+        partition.setContextEntry( ctxEntry );
+
+        // Test AddContextPartition
+        service.addPartition( partition );
+
+        Hashtable<String,Object> env = new Hashtable<String,Object>();
+        env.put( Context.INITIAL_CONTEXT_FACTORY, CoreContextFactory.class.getName() );
+        env.put( DirectoryService.JNDI_KEY, service );
+        env.put( Context.SECURITY_CREDENTIALS, "secret" );
+        env.put( Context.SECURITY_AUTHENTICATION, "simple" );
+        env.put( Context.SECURITY_PRINCIPAL, "uid=admin,ou=system" );
+        Context ctx = new InitialContext( env );
+        Assert.assertNotNull( ctx.lookup( "ou=removable" ) );
+
+        // Test removeContextPartition
+        service.removePartition( partition );
+        ctx = new InitialContext( env );
+        try
+        {
+            ctx.lookup( "ou=removable" );
+            Assert.fail( "NameNotFoundException should be thrown." );
+        }
+        catch ( NameNotFoundException e )
+        {
+            // Partition is removed.
+        }
+    }
+}
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/event/EventServiceIT.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/event/EventServiceIT.java
new file mode 100644
index 0000000..ef08a8f
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/event/EventServiceIT.java
@@ -0,0 +1,191 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.event;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.integ.CiRunner;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getSystemContext;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import static org.junit.Assert.assertEquals;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.SearchControls;
+import javax.naming.event.EventDirContext;
+import javax.naming.event.NamespaceChangeListener;
+import javax.naming.event.NamingEvent;
+import javax.naming.event.NamingExceptionEvent;
+import javax.naming.event.ObjectChangeListener;
+
+import java.util.ArrayList;
+import java.util.EventObject;
+import java.util.List;
+
+
+/**
+ * Test cases for the event service.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+@RunWith ( CiRunner.class )
+public class EventServiceIT
+{
+    public static DirectoryService service;
+
+
+    /**
+     * Test to make sure NamingListener's are no longer registered
+     * after they are removed via the EventContex.removeNamingListener method.
+     *
+     * @throws NamingException on failures
+     */
+    @Test
+    public void testRemoveNamingListener() throws NamingException
+    {
+        TestListener listener = new TestListener();
+        EventDirContext ctx = ( EventDirContext ) getSystemContext( service ).lookup( "" );
+        ctx.addNamingListener( "", SearchControls.SUBTREE_SCOPE, listener );
+        Attributes testEntry = new AttributesImpl( "ou", "testentry", true );
+        Attribute objectClass = new AttributeImpl( "objectClass", "top" );
+        objectClass.add( "organizationalUnit" );
+        testEntry.put( objectClass );
+        ctx.createSubcontext( "ou=testentry", testEntry );
+
+        assertEquals( 1, listener.getEventRecords().size() );
+        EventRecord rec = ( EventRecord ) listener.getEventRecords().get( 0 );
+        assertEquals( "objectAdded", rec.method );
+        assertEquals( ctx, rec.event.getSource() );
+
+        ctx.removeNamingListener( listener );
+        ctx.destroySubcontext( "ou=testentry" );
+
+        assertEquals( 1, listener.getEventRecords().size() );
+        rec = ( EventRecord ) listener.getEventRecords().get( 0 );
+        assertEquals( "objectAdded", rec.method );
+        assertEquals( ctx, rec.event.getSource() );
+
+        // readd the entry once again just to make sure
+        ctx.createSubcontext( "ou=testentry", testEntry );
+        assertEquals( 1, listener.getEventRecords().size() );
+        rec = ( EventRecord ) listener.getEventRecords().get( 0 );
+        assertEquals( "objectAdded", rec.method );
+        assertEquals( ctx, rec.event.getSource() );
+    }
+
+
+    /**
+     * Test to make sure NamingListener's are no longer registered
+     * after the context used for registration is closed.
+     *
+     * @throws NamingException on failures
+     */
+    @Test
+    public void testContextClose() throws NamingException
+    {
+        TestListener listener = new TestListener();
+        EventDirContext ctx = ( EventDirContext ) getSystemContext( service ).lookup( "" );
+        ctx.addNamingListener( "", SearchControls.SUBTREE_SCOPE, listener );
+        Attributes testEntry = new AttributesImpl( "ou", "testentry", true );
+        Attribute objectClass = new AttributeImpl( "objectClass", "top" );
+        objectClass.add( "organizationalUnit" );
+        testEntry.put( objectClass );
+        ctx.createSubcontext( "ou=testentry", testEntry );
+
+        assertEquals( 1, listener.getEventRecords().size() );
+        EventRecord rec = ( EventRecord ) listener.getEventRecords().get( 0 );
+        assertEquals( "objectAdded", rec.method );
+        assertEquals( ctx, rec.event.getSource() );
+
+        ctx.close();
+        ctx = ( EventDirContext ) getSystemContext( service ).lookup( "" );
+        ctx.destroySubcontext( "ou=testentry" );
+
+        assertEquals( 1, listener.getEventRecords().size() );
+        rec = ( EventRecord ) listener.getEventRecords().get( 0 );
+        assertEquals( "objectAdded", rec.method );
+
+        // readd the entry once again just to make sure
+        ctx.createSubcontext( "ou=testentry", testEntry );
+        assertEquals( 1, listener.getEventRecords().size() );
+        rec = ( EventRecord ) listener.getEventRecords().get( 0 );
+        assertEquals( "objectAdded", rec.method );
+    }
+
+    
+    public class TestListener implements ObjectChangeListener, NamespaceChangeListener
+    {
+        List<EventRecord> events = new ArrayList<EventRecord>();
+
+
+        public List<EventRecord> getEventRecords()
+        {
+            return events;
+        }
+
+
+        public void objectChanged( NamingEvent event )
+        {
+            events.add( new EventRecord( "objectChanged", event ) );
+        }
+
+
+        public void namingExceptionThrown( NamingExceptionEvent event )
+        {
+            events.add( new EventRecord( "namingExceptionThrown", event ) );
+        }
+
+
+        public void objectAdded( NamingEvent event )
+        {
+            events.add( new EventRecord( "objectAdded", event ) );
+        }
+
+
+        public void objectRemoved( NamingEvent event )
+        {
+            events.add( new EventRecord( "objectRemoved", event ) );
+        }
+
+
+        public void objectRenamed( NamingEvent event )
+        {
+            events.add( new EventRecord( "objectRenamed", event ) );
+        }
+    }
+
+    public class EventRecord
+    {
+        String method;
+        EventObject event;
+
+
+        EventRecord(String method, EventObject event)
+        {
+            this.method = method;
+            this.event = event;
+        }
+    }
+}
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/exception/ExceptionServiceIT.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/exception/ExceptionServiceIT.java
new file mode 100644
index 0000000..24d9c4c
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/exception/ExceptionServiceIT.java
@@ -0,0 +1,692 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.exception;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.integ.CiRunner;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getSystemContext;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.exception.LdapContextNotEmptyException;
+import org.apache.directory.shared.ldap.exception.LdapNameAlreadyBoundException;
+import org.apache.directory.shared.ldap.exception.LdapNameNotFoundException;
+import org.apache.directory.shared.ldap.exception.LdapSchemaViolationException;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.message.ModificationItemImpl;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.LdapContext;
+
+
+/**
+ * Tests the correct operation of the ServerExceptionService.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+@RunWith ( CiRunner.class )
+public class ExceptionServiceIT
+{
+    public static DirectoryService service;
+
+
+    private DirContext createSubContext( String type, String value ) throws NamingException
+    {
+        return createSubContext( getSystemContext( service ), type, value );
+    }
+
+
+    private DirContext createSubContext( DirContext ctx, String type, String value ) throws NamingException
+    {
+        Attributes attrs = new AttributesImpl( type, value );
+        Attribute attr = new AttributeImpl( "ObjectClass" );
+        attr.add( "top"  );
+        attr.add( "person" );
+        attr.add( "OrganizationalPerson" );
+        attrs.put( attr );
+
+        attrs.put( "sn", value );
+        attrs.put( "cn", value );
+
+        return ctx.createSubcontext( type + "=" + value, attrs );
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Search Operation Tests
+    // ------------------------------------------------------------------------
+
+
+    /**
+     * Test search operation failure when the search base is non-existant.
+     *
+     * @throws NamingException on error
+     */
+    @Test
+    public void testFailSearchNoSuchObject() throws NamingException
+    {
+        SearchControls ctls = new SearchControls();
+        try
+        {
+            getSystemContext( service ).search( "ou=blah", "(objectClass=*)", ctls );
+            fail( "Execution should never get here due to exception!" );
+        }
+        catch ( LdapNameNotFoundException e )
+        {
+            assertEquals( "ou=system", e.getResolvedName().toString() );
+            assertEquals( ResultCodeEnum.NO_SUCH_OBJECT, e.getResultCode() );
+        }
+    }
+
+
+    /**
+     * Search operation control to test if normal search operations occur
+     * correctly.
+     *
+     * @throws NamingException on error
+     */
+    @Test
+    public void testSearchControl() throws NamingException
+    {
+        SearchControls ctls = new SearchControls();
+        NamingEnumeration<SearchResult> list = getSystemContext( service ).search( "ou=users", "(objectClass=*)", ctls );
+
+        if ( list.hasMore() )
+        {
+            SearchResult result = list.next();
+            assertNotNull( result.getAttributes() );
+            assertEquals( "uid=akarasulu,ou=users,ou=system", result.getName() );
+        }
+
+        assertFalse( list.hasMore() );
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Move Operation Tests
+    // ------------------------------------------------------------------------
+
+    /**
+     * Test move operation failure when the object moved is non-existant.
+     *
+     * @throws NamingException on error
+     */
+    @Test
+    public void testFailMoveEntryAlreadyExists() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+
+        try
+        {
+            Attributes attrs = new AttributesImpl( "ou", "users" );
+            Attribute attr = new AttributeImpl( "ObjectClass" );
+            attr.add( "top"  );
+            attr.add( "OrganizationalUnit" );
+            attrs.put( attr );
+
+            sysRoot.createSubcontext( "ou=users,ou=groups", attrs );
+            sysRoot.rename( "ou=users", "ou=users,ou=groups" );
+            fail( "Execution should never get here due to exception!" );
+        }
+        catch ( LdapNameAlreadyBoundException e )
+        {
+            assertEquals( "ou=users,ou=groups,ou=system", e.getResolvedName().toString() );
+            assertEquals( ResultCodeEnum.ENTRY_ALREADY_EXISTS, e.getResultCode() );
+        }
+
+        try
+        {
+            Attributes attrs = new AttributesImpl( "ou", "uzerz" );
+            Attribute attr = new AttributeImpl( "ObjectClass" );
+            attr.add( "top"  );
+            attr.add( "OrganizationalUnit" );
+            attrs.put( attr );
+
+            sysRoot.createSubcontext( "ou=uzerz,ou=groups", attrs );
+            sysRoot.addToEnvironment( "java.naming.ldap.deleteRDN", "false" );
+            sysRoot.rename( "ou=users", "ou=uzerz,ou=groups" );
+            sysRoot.removeFromEnvironment( "java.naming.ldap.deleteRDN" );
+            fail( "Execution should never get here due to exception!" );
+        }
+        catch ( LdapNameAlreadyBoundException e )
+        {
+            assertEquals( "ou=uzerz,ou=groups,ou=system", e.getResolvedName().toString() );
+            assertEquals( ResultCodeEnum.ENTRY_ALREADY_EXISTS, e.getResultCode() );
+        }
+    }
+
+
+    /**
+     * Test move operation failure when the object moved is non-existant.
+
+     * @throws NamingException on error
+     */
+    @Test
+    public void testFailMoveNoSuchObject() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+
+        try
+        {
+            sysRoot.rename( "ou=blah", "ou=blah,ou=groups" );
+            fail( "Execution should never get here due to exception!" );
+        }
+        catch ( LdapNameNotFoundException e )
+        {
+            assertEquals( "ou=system", e.getResolvedName().toString() );
+            assertEquals( ResultCodeEnum.NO_SUCH_OBJECT, e.getResultCode() );
+        }
+
+        try
+        {
+            sysRoot.addToEnvironment( "java.naming.ldap.deleteRDN", "false" );
+            sysRoot.rename( "ou=blah", "ou=blah2,ou=groups" );
+            sysRoot.removeFromEnvironment( "java.naming.ldap.deleteRDN" );
+            fail( "Execution should never get here due to exception!" );
+        }
+        catch ( LdapNameNotFoundException e )
+        {
+            assertEquals( "ou=system", e.getResolvedName().toString() );
+            assertEquals( ResultCodeEnum.NO_SUCH_OBJECT, e.getResultCode() );
+        }
+    }
+
+
+    /**
+     * Move operation control to test if normal move operations occur
+     * correctly.
+     *
+     * @throws NamingException on error
+     */
+    @Test
+    public void testMoveControl() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+
+        sysRoot.rename( "ou=users", "ou=users,ou=groups" );
+        assertNotNull( sysRoot.lookup( "ou=users,ou=groups" ) );
+
+        try
+        {
+            sysRoot.lookup( "ou=users" );
+            fail( "Execution should never get here due to exception!" );
+        }
+        catch ( NamingException e )
+        {
+            assertEquals( "ou=system", e.getResolvedName().toString() );
+            assertTrue( e instanceof LdapNameNotFoundException );
+        }
+    }
+
+
+    // ------------------------------------------------------------------------
+    // ModifyRdn Operation Tests
+    // ------------------------------------------------------------------------
+
+
+    /**
+     * Test modifyRdn operation failure when the object renamed is non-existant.
+     *
+     * @throws NamingException on error
+     */
+    @Test
+    public void testFailModifyRdnEntryAlreadyExists() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+
+        try
+        {
+            sysRoot.rename( "ou=users", "ou=groups" );
+            fail( "Execution should never get here due to exception!" );
+        }
+        catch ( LdapNameAlreadyBoundException e )
+        {
+            assertEquals( "ou=groups,ou=system", e.getResolvedName().toString() );
+            assertEquals( ResultCodeEnum.ENTRY_ALREADY_EXISTS, e.getResultCode() );
+        }
+    }
+
+
+    /**
+     * Test modifyRdn operation failure when the object renamed is non-existant.
+     *
+     * @throws NamingException on error
+     */
+    @Test
+    public void testFailModifyRdnNoSuchObject() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+
+        try
+        {
+            sysRoot.rename( "ou=blah", "ou=asdf" );
+            fail( "Execution should never get here due to exception!" );
+        }
+        catch ( LdapNameNotFoundException e )
+        {
+            assertEquals( "ou=system", e.getResolvedName().toString() );
+            assertEquals( ResultCodeEnum.NO_SUCH_OBJECT, e.getResultCode() );
+        }
+    }
+
+
+    /**
+     * Modify operation control to test if normal modify operations occur
+     * correctly.
+     *
+     * @throws NamingException on error
+     */
+    @Test
+    public void testModifyRdnControl() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+
+        sysRoot.rename( "ou=users", "ou=asdf" );
+        assertNotNull( sysRoot.lookup( "ou=asdf" ) );
+
+        try
+        {
+            sysRoot.lookup( "ou=users" );
+            fail( "Execution should never get here due to exception!" );
+        }
+        catch ( NamingException e )
+        {
+            assertEquals( "ou=system", e.getResolvedName().toString() );
+            assertTrue( e instanceof LdapNameNotFoundException );
+        }
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Modify Operation Tests
+    // ------------------------------------------------------------------------
+
+
+    /**
+     * Test modify operation failure when the object modified is non-existant.
+     *
+     * @throws NamingException on error
+     */
+    @Test
+    public void testFailModifyNoSuchObject() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+
+        Attributes attrs = new AttributesImpl( true );
+        Attribute ou = new AttributeImpl( "ou" );
+        ou.add( "users" );
+        ou.add( "dummyValue" );
+        attrs.put( ou );
+
+        try
+        {
+            sysRoot.modifyAttributes( "ou=blah", DirContext.ADD_ATTRIBUTE, attrs );
+            fail( "Execution should never get here due to exception!" );
+        }
+        catch ( LdapNameNotFoundException e )
+        {
+            assertEquals( "ou=system", e.getResolvedName().toString() );
+            assertEquals( ResultCodeEnum.NO_SUCH_OBJECT, e.getResultCode() );
+        }
+
+        ModificationItemImpl[] mods = new ModificationItemImpl[]
+            { new ModificationItemImpl( DirContext.ADD_ATTRIBUTE, ou ) };
+
+        try
+        {
+            sysRoot.modifyAttributes( "ou=blah", mods );
+            fail( "Execution should never get here due to exception!" );
+        }
+        catch ( LdapNameNotFoundException e )
+        {
+            assertEquals( "ou=system", e.getResolvedName().toString() );
+            assertEquals( ResultCodeEnum.NO_SUCH_OBJECT, e.getResultCode() );
+        }
+    }
+
+
+    /**
+     * Modify operation control to test if normal modify operations occur
+     * correctly.
+     *
+     * @throws NamingException on error
+     */
+    @Test
+    public void testModifyControl() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+
+        Attributes attrs = new AttributesImpl( true );
+        Attribute attr = new AttributeImpl( "ou" );
+        attr.add( "dummyValue" );
+        attrs.put( attr );
+        sysRoot.modifyAttributes( "ou=users", DirContext.ADD_ATTRIBUTE, attrs );
+        Attribute ou = sysRoot.getAttributes( "ou=users" ).get( "ou" );
+        assertTrue( ou.contains( "users" ) );
+        assertTrue( ou.contains( "dummyValue" ) );
+
+        attr = new AttributeImpl( "ou" );
+        attr.add( "another" );
+        ModificationItemImpl[] mods = new ModificationItemImpl[]
+            { new ModificationItemImpl( DirContext.ADD_ATTRIBUTE, attr ) };
+
+        sysRoot.modifyAttributes( "ou=users", mods );
+        ou = sysRoot.getAttributes( "ou=users" ).get( "ou" );
+        assertTrue( ou.contains( "users" ) );
+        assertTrue( ou.contains( "dummyValue" ) );
+        assertTrue( ou.contains( "another" ) );
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Lookup Operation Tests
+    // ------------------------------------------------------------------------
+
+
+    /**
+     * Test lookup operation failure when the object looked up is non-existant.
+     *
+     * @throws NamingException on error
+     */
+    @Test
+    public void testFailLookupNoSuchObject() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+
+        try
+        {
+            sysRoot.lookup( "ou=blah" );
+            fail( "Execution should never get here due to exception!" );
+        }
+        catch ( LdapNameNotFoundException e )
+        {
+            assertEquals( "ou=system", e.getResolvedName().toString() );
+            assertEquals( ResultCodeEnum.NO_SUCH_OBJECT, e.getResultCode() );
+        }
+    }
+
+
+    /**
+     * Lookup operation control to test if normal lookup operations occur
+     * correctly.
+     *
+     * @throws NamingException on error
+     */
+    @Test
+    public void testLookupControl() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+
+        LdapContext ctx = ( LdapContext ) sysRoot.lookup( "ou=users" );
+        assertNotNull( ctx );
+        assertEquals( "users", ctx.getAttributes( "" ).get( "ou" ).get() );
+    }
+
+
+    // ------------------------------------------------------------------------
+    // List Operation Tests
+    // ------------------------------------------------------------------------
+
+
+    /**
+     * Test list operation failure when the base searched is non-existant.
+     *
+     * @throws NamingException on error
+     */
+    @Test
+    public void testFailListNoSuchObject() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+
+        try
+        {
+            sysRoot.list( "ou=blah" );
+            fail( "Execution should never get here due to exception!" );
+        }
+        catch ( LdapNameNotFoundException e )
+        {
+            assertEquals( "ou=system", e.getResolvedName().toString() );
+            assertEquals( ResultCodeEnum.NO_SUCH_OBJECT, e.getResultCode() );
+        }
+    }
+
+
+    /**
+     * List operation control to test if normal list operations occur correctly.
+     *
+     * @throws NamingException on error
+     */
+    @Test
+    public void testListControl() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+
+        NamingEnumeration<?> list = sysRoot.list( "ou=users" );
+
+        if ( list.hasMore() )
+        {
+            SearchResult result = (SearchResult)list.next();
+            assertNotNull( result.getAttributes() );
+            assertEquals( "uid=akarasulu,ou=users,ou=system", result.getName() );
+        }
+
+        assertFalse( list.hasMore() );
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Add Operation Tests
+    // ------------------------------------------------------------------------
+
+
+    /**
+     * Tests for add operation failure when the parent of the entry to add does
+     * not exist.
+     *
+     * @throws NamingException on error
+     */
+    @Test
+    public void testFailAddOnAlias() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+
+        Attributes attrs = new AttributesImpl( true );
+        Attribute attr = new AttributeImpl( "objectClass" );
+        attr.add( "top" );
+        attr.add( "alias" );
+        attr.add( SchemaConstants.EXTENSIBLE_OBJECT_OC );
+        attrs.put( attr );
+        attrs.put( "aliasedObjectName", "ou=users,ou=system" );
+
+        sysRoot.createSubcontext( "cn=toanother", attrs );
+
+        try
+        {
+            sysRoot.createSubcontext( "ou=blah,cn=toanother" );
+            fail( "Execution should never get here due to exception!" );
+        }
+        catch ( LdapSchemaViolationException e )
+        {
+            assertEquals( ResultCodeEnum.OBJECT_CLASS_VIOLATION, e.getResultCode() );
+        }
+    }
+
+
+    /**
+     * Tests for add operation failure when the parent of the entry to add does
+     * not exist.
+     *
+     * @throws NamingException on error
+     */
+    @Test
+    public void testFailAddNoSuchEntry() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+
+        try
+        {
+            sysRoot.createSubcontext( "ou=blah,ou=abc" );
+            fail( "Execution should never get here due to exception!" );
+        }
+        catch ( LdapSchemaViolationException e )
+        {
+            assertEquals( ResultCodeEnum.OBJECT_CLASS_VIOLATION, e.getResultCode() );
+        }
+    }
+
+
+    /**
+     * Tests for add operation failure when the entry to add already exists.
+     *
+     * @throws NamingException on error
+     */
+    @Test
+    public void testFailAddEntryAlreadyExists() throws NamingException
+    {
+        createSubContext( "ou", "blah");
+
+        try
+        {
+            createSubContext( "ou", "blah");
+            fail( "Execution should never get here due to exception!" );
+        }
+        catch ( LdapNameAlreadyBoundException e )
+        {
+            assertEquals( "ou=blah,ou=system", e.getResolvedName().toString() );
+            assertEquals( ResultCodeEnum.ENTRY_ALREADY_EXISTS, e.getResultCode() );
+        }
+    }
+
+
+    /**
+     * Add operation control to test if normal add operations occur correctly.
+     *
+     * @throws NamingException on error
+     */
+    @Test
+    public void testAddControl() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+
+        DirContext ctx = createSubContext( "ou", "blah");
+        createSubContext( ctx, "ou", "subctx");
+        Object obj = sysRoot.lookup( "ou=subctx,ou=blah" );
+        assertNotNull( obj );
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Delete Operation Tests
+    // ------------------------------------------------------------------------
+
+
+    /**
+     * Tests for delete failure when the entry to be deleted has child entires.
+     *
+     * @throws NamingException on error
+     */
+    @Test
+    public void testFailDeleteNotAllowedOnNonLeaf() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+
+        DirContext ctx = createSubContext( "ou", "blah" );
+        createSubContext( ctx,  "ou", "subctx" );
+
+        try
+        {
+            sysRoot.destroySubcontext( "ou=blah" );
+            fail( "Execution should never get here due to exception!" );
+        }
+        catch ( LdapContextNotEmptyException e )
+        {
+            assertEquals( "ou=blah,ou=system", e.getResolvedName().toString() );
+            assertEquals( ResultCodeEnum.NOT_ALLOWED_ON_NON_LEAF, e.getResultCode() );
+        }
+    }
+
+
+    /**
+     * Tests delete to make sure it fails when we try to delete an entry that
+     * does not exist.
+     *
+     * @throws NamingException on error
+     */
+    @Test
+    public void testFailDeleteNoSuchObject() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+
+        try
+        {
+            sysRoot.destroySubcontext( "ou=blah" );
+            fail( "Execution should never get here due to exception!" );
+        }
+        catch ( LdapNameNotFoundException e )
+        {
+            assertEquals( "ou=system", e.getResolvedName().toString() );
+            assertEquals( ResultCodeEnum.NO_SUCH_OBJECT, e.getResultCode() );
+        }
+    }
+
+
+    /**
+     * Delete operation control to test if normal delete operations occur.
+     *
+     * @throws NamingException on error
+     */
+    @Test
+    public void testDeleteControl() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+
+        createSubContext( "ou", "blah" );
+
+        Object obj = sysRoot.lookup( "ou=blah" );
+        assertNotNull( obj );
+        sysRoot.destroySubcontext( "ou=blah" );
+
+        try
+        {
+            sysRoot.lookup( "ou=blah" );
+            fail( "Execution should never get here due to exception!" );
+        }
+        catch ( LdapNameNotFoundException e )
+        {
+            assertEquals( "ou=system", e.getResolvedName().toString() );
+            assertEquals( ResultCodeEnum.NO_SUCH_OBJECT, e.getResultCode() );
+        }
+    }
+}
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/jndi/AddIT.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/jndi/AddIT.java
new file mode 100644
index 0000000..f4023fa
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/jndi/AddIT.java
@@ -0,0 +1,172 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.
+ *
+ */
+package org.apache.directory.server.core.jndi;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.integ.CiRunner;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getSystemContext;
+import org.apache.directory.shared.ldap.exception.LdapInvalidAttributeValueException;
+import org.apache.directory.shared.ldap.exception.LdapSchemaViolationException;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.junit.Assert.assertEquals;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.ldap.LdapContext;
+
+
+/**
+ * Contributed by Luke Taylor to fix DIRSERVER-169.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+@RunWith ( CiRunner.class )
+public class AddIT
+{
+    public static DirectoryService service;
+    
+
+    /**
+     * Test that attribute name case is preserved after adding an entry
+     * in the case the user added them.  This is to test DIRSERVER-832.
+     */
+    public void testAddCasePreservedOnAttributeNames() throws Exception
+    {
+        LdapContext sysRoot = getSystemContext( service );
+
+        Attributes attrs = new AttributesImpl( true );
+        Attribute oc = new AttributeImpl( "ObjectClass", "top" );
+        oc.add( "PERSON" );
+        oc.add( "organizationalPerson" );
+        oc.add( "inetORGperson" );
+        Attribute cn = new AttributeImpl( "Cn", "Kevin Spacey" );
+        Attribute dc = new AttributeImpl( "sN", "Spacey" );
+        attrs.put( oc );
+        attrs.put( cn );
+        attrs.put( dc);
+        sysRoot.createSubcontext( "uID=kevin", attrs );
+        Attributes returned = sysRoot.getAttributes( "UID=kevin" );
+        
+        NamingEnumeration<? extends Attribute> attrList = returned.getAll();
+        
+        while( attrList.hasMore() )
+        {
+            Attribute attr = ( Attribute ) attrList.next();
+            
+            if ( attr.getID().equalsIgnoreCase( "uid" ) )
+            {
+                assertEquals( "uID", attr.getID() );
+            }
+            
+            if ( attr.getID().equalsIgnoreCase( "objectClass" ) )
+            {
+                assertEquals( "ObjectClass", attr.getID() );
+            }
+            
+            if ( attr.getID().equalsIgnoreCase( "sn" ) )
+            {
+                assertEquals( "sN", attr.getID() );
+            }
+            
+            if ( attr.getID().equalsIgnoreCase( "cn" ) )
+            {
+                assertEquals( "Cn", attr.getID() );
+            }
+        }
+    }
+    
+    
+    /**
+     * Test that we can't add an entry with an attribute type not within
+     * any of the MUST or MAY of any of its objectClasses
+     * 
+     * @throws Exception on error
+     */
+    @Test
+    public void testAddAttributesNotInObjectClasses() throws Exception
+    {
+        LdapContext sysRoot = getSystemContext( service );
+
+        Attributes attrs = new AttributesImpl( true );
+        Attribute oc = new AttributeImpl( "ObjectClass", "top" );
+        Attribute cn = new AttributeImpl( "cn", "kevin Spacey" );
+        Attribute dc = new AttributeImpl( "dc", "ke" );
+        attrs.put( oc );
+        attrs.put( cn );
+        attrs.put( dc);
+
+        String base = "uid=kevin";
+
+        //create subcontext
+        try
+        {
+            sysRoot.createSubcontext( base, attrs );
+            fail( "Should not reach this state" );
+        }
+        catch ( LdapSchemaViolationException e )
+        {
+            assertTrue( true );
+        }
+    }
+
+
+    /**
+     * Test that we can't add an entry with an attribute with a bad syntax
+     *
+     * @throws Exception on error
+     */
+    @Test
+    public void testAddAttributesBadSyntax() throws Exception
+    {
+        LdapContext sysRoot = getSystemContext( service );
+
+        Attributes attrs = new AttributesImpl( true );
+        Attribute oc = new AttributeImpl( "ObjectClass", "top" );
+        oc.add( "person" );
+        Attribute cn = new AttributeImpl( "cn", "kevin Spacey" );
+        Attribute sn = new AttributeImpl( "sn", "ke" );
+        Attribute telephone = new AttributeImpl( "telephoneNumber", "0123456abc" );
+        attrs.put( oc );
+        attrs.put( cn );
+        attrs.put( sn );
+        attrs.put( telephone );
+
+        String base = "sn=kevin";
+
+        //create subcontext
+        try
+        {
+            sysRoot.createSubcontext( base, attrs );
+            fail( "Should not reach this state" );
+        }
+        catch ( LdapInvalidAttributeValueException e )
+        {
+            assertTrue( true );
+        }
+    }
+}
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/jndi/CreateContextIT.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/jndi/CreateContextIT.java
new file mode 100644
index 0000000..a94a0fe
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/jndi/CreateContextIT.java
@@ -0,0 +1,454 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.jndi;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.integ.CiRunner;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getSystemContext;
+import org.apache.directory.shared.ldap.exception.LdapSchemaViolationException;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.junit.Test;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import static org.junit.Assert.assertNull;
+import org.junit.runner.RunWith;
+
+import javax.naming.CompositeName;
+import javax.naming.Name;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.BasicAttribute;
+import javax.naming.directory.BasicAttributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.LdapContext;
+
+
+/**
+ * Tests the creation of contexts in various ways.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+@RunWith ( CiRunner.class )
+public class CreateContextIT
+{
+    public static DirectoryService service;
+
+
+    protected Attributes getPersonAttributes( String sn, String cn )
+    {
+        Attributes attrs = new AttributesImpl();
+        Attribute ocls = new AttributeImpl( "objectClass" );
+        ocls.add( "top" );
+        ocls.add( "person" );
+        attrs.put( ocls );
+        attrs.put( "cn", cn );
+        attrs.put( "sn", sn );
+
+        return attrs;
+    }
+
+
+    /**
+     * DIRSERVER-628: Creation of entry with multivalued RDN leads to wrong
+     * attribute value.
+     *
+     * @throws NamingException on error
+     */
+    @Test
+    public void testMultiValuedRdn() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+
+        Attributes attrs = getPersonAttributes( "Bush", "Kate Bush" );
+        String rdn = "cn=Kate Bush+sn=Bush";
+        sysRoot.createSubcontext( rdn, attrs );
+
+        SearchControls sctls = new SearchControls();
+        sctls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        String filter = "(sn=Bush)";
+        String base = "";
+
+        NamingEnumeration<SearchResult> enm = sysRoot.search( base, filter, sctls );
+        
+        while ( enm.hasMore() )
+        {
+            SearchResult sr = enm.next();
+            attrs = sr.getAttributes();
+            Attribute cn = sr.getAttributes().get( "cn" );
+            assertNotNull( cn );
+            assertTrue( cn.contains( "Kate Bush" ) );
+        }
+
+        sysRoot.destroySubcontext( rdn );
+    }
+
+
+    /**
+     * Tests the creation and subsequent read of a new JNDI context under the
+     * system context root.
+     *
+     * @throws javax.naming.NamingException if there are failures
+     */
+    @Test
+    public void testCreateContexts() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+
+        /*
+         * create ou=testing00,ou=system
+         */
+        Attributes attributes = new AttributesImpl( true );
+        Attribute attribute = new AttributeImpl( "objectClass" );
+        attribute.add( "top" );
+        attribute.add( "organizationalUnit" );
+        attributes.put( attribute );
+        attributes.put( "ou", "testing00" );
+        DirContext ctx = sysRoot.createSubcontext( "ou=testing00", attributes );
+        assertNotNull( ctx );
+
+        ctx = ( DirContext ) sysRoot.lookup( "ou=testing00" );
+        assertNotNull( ctx );
+
+        attributes = ctx.getAttributes( "" );
+        assertNotNull( attributes );
+        assertEquals( "testing00", attributes.get( "ou" ).get() );
+        attribute = attributes.get( "objectClass" );
+        assertNotNull( attribute );
+        assertTrue( attribute.contains( "top" ) );
+        assertTrue( attribute.contains( "organizationalUnit" ) );
+
+        /*
+         * create ou=testing01,ou=system
+         */
+        attributes = new AttributesImpl( true );
+        attribute = new AttributeImpl( "objectClass" );
+        attribute.add( "top" );
+        attribute.add( "organizationalUnit" );
+        attributes.put( attribute );
+        attributes.put( "ou", "testing01" );
+        ctx = sysRoot.createSubcontext( "ou=testing01", attributes );
+        assertNotNull( ctx );
+
+        ctx = ( DirContext ) sysRoot.lookup( "ou=testing01" );
+        assertNotNull( ctx );
+
+        attributes = ctx.getAttributes( "" );
+        assertNotNull( attributes );
+        assertEquals( "testing01", attributes.get( "ou" ).get() );
+        attribute = attributes.get( "objectClass" );
+        assertNotNull( attribute );
+        assertTrue( attribute.contains( "top" ) );
+        assertTrue( attribute.contains( "organizationalUnit" ) );
+
+        /*
+         * create ou=testing02,ou=system
+         */
+        attributes = new AttributesImpl( true );
+        attribute = new AttributeImpl( "objectClass" );
+        attribute.add( "top" );
+        attribute.add( "organizationalUnit" );
+        attributes.put( attribute );
+        attributes.put( "ou", "testing02" );
+        ctx = sysRoot.createSubcontext( "ou=testing02", attributes );
+        assertNotNull( ctx );
+
+        ctx = ( DirContext ) sysRoot.lookup( "ou=testing02" );
+        assertNotNull( ctx );
+
+        attributes = ctx.getAttributes( "" );
+        assertNotNull( attributes );
+        assertEquals( "testing02", attributes.get( "ou" ).get() );
+        attribute = attributes.get( "objectClass" );
+        assertNotNull( attribute );
+        assertTrue( attribute.contains( "top" ) );
+        assertTrue( attribute.contains( "organizationalUnit" ) );
+
+        /*
+         * create ou=subtest,ou=testing01,ou=system
+         */
+        ctx = ( DirContext ) sysRoot.lookup( "ou=testing01" );
+
+        attributes = new AttributesImpl( true );
+        attribute = new AttributeImpl( "objectClass" );
+        attribute.add( "top" );
+        attribute.add( "organizationalUnit" );
+        attributes.put( attribute );
+        attributes.put( "ou", "subtest" );
+        ctx = ctx.createSubcontext( "ou=subtest", attributes );
+        assertNotNull( ctx );
+
+        ctx = ( DirContext ) sysRoot.lookup( "ou=subtest,ou=testing01" );
+        assertNotNull( ctx );
+
+        attributes = ctx.getAttributes( "" );
+        assertNotNull( attributes );
+        assertEquals( "subtest", attributes.get( "ou" ).get() );
+        attribute = attributes.get( "objectClass" );
+        assertNotNull( attribute );
+        assertTrue( attribute.contains( "top" ) );
+        assertTrue( attribute.contains( "organizationalUnit" ) );
+    }
+
+
+    @Test
+    public void testFailCreateExisting() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+
+        Attribute attribute;
+        Attributes attributes;
+        DirContext ctx = null;
+
+        /*
+         * create ou=testing00,ou=system
+         */
+        attributes = new AttributesImpl( true );
+        attribute = new AttributeImpl( "objectClass" );
+        attribute.add( "top" );
+        attribute.add( "organizationalUnit" );
+        attributes.put( attribute );
+        attributes.put( "ou", "testing00" );
+        ctx = sysRoot.createSubcontext( "ou=testing00", attributes );
+        assertNotNull( ctx );
+
+        ctx = ( DirContext ) sysRoot.lookup( "ou=testing00" );
+        assertNotNull( ctx );
+
+        attributes = ctx.getAttributes( "" );
+        assertNotNull( attributes );
+        assertEquals( "testing00", attributes.get( "ou" ).get() );
+        attribute = attributes.get( "objectClass" );
+        assertNotNull( attribute );
+        assertTrue( attribute.contains( "top" ) );
+        assertTrue( attribute.contains( "organizationalUnit" ) );
+
+        /*
+         * fail on recreate attempt for ou=testing00,ou=system
+         */
+        attributes = new AttributesImpl( true );
+        attribute = new AttributeImpl( "objectClass" );
+        attribute.add( "top" );
+        attribute.add( "organizationalUnit" );
+        attributes.put( attribute );
+        attributes.put( "ou", "testing00" );
+
+        ctx = null;
+        try
+        {
+            ctx = sysRoot.createSubcontext( "ou=testing00", attributes );
+            fail( "Attempt to create exiting context should fail!" );
+        }
+        catch ( NamingException e )
+        {
+            assertNotNull( e );
+        }
+
+        assertNull( ctx );
+    }
+    
+    
+    @Test
+    public void testCreateContextWithCompositeName() throws Exception
+    {
+        LdapContext sysRoot = getSystemContext( service );
+
+        Attributes attrs = new AttributesImpl( true );
+        Attribute objclass = new AttributeImpl( "objectClass" );
+        objclass.add( "top" );
+        objclass.add( "organizationalUnit" );
+        attrs.put( objclass );
+
+        Name relativeName = new CompositeName( "ou=services" );
+
+        //sysRoot.createSubcontext(relativeName.toString(), attrs);//Passes!
+        sysRoot.createSubcontext( relativeName, attrs );//Fails!
+    }
+
+
+    /**
+     * Tests the creation and subsequent read of a new JNDI context under the
+     * system context root.
+     *
+     * @throws javax.naming.NamingException if there are failures
+     */
+    @Test
+    public void testCreateContextWithBasicAttributesCaseSensitive() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+
+        /*
+         * create ou=testing00,ou=system
+         */
+        Attributes attributes = new BasicAttributes();
+        attributes.put("objectClass", "organizationalUnit");
+        attributes.put("description", "Test OU");
+        attributes.put("OU", "Test");
+        
+        DirContext ctx = sysRoot.createSubcontext( "ou=Test", attributes );
+        assertNotNull( ctx );
+
+        ctx = ( DirContext ) sysRoot.lookup( "ou=Test" );
+        assertNotNull( ctx );
+
+        attributes = ctx.getAttributes( "" );
+        assertNotNull( attributes );
+        assertEquals( "Test", attributes.get( "ou" ).get() );
+        assertEquals( "Test OU", attributes.get( "Description" ).get() );
+        Attribute attribute = attributes.get( "objectclass" );
+        assertNotNull( attribute );
+        assertTrue( attribute.contains( "top" ) );
+        assertTrue( attribute.contains( "organizationalUnit" ) );
+
+        /*
+         * create ou=testing01,ou=system
+         */
+        attributes = new BasicAttributes();
+        attribute = new BasicAttribute( "objectClass" );
+        attribute.add( "top" );
+        attribute.add( "organizationalUnit" );
+        attributes.put( attribute );
+        attributes.put( "ou", "testing01" );
+        ctx = sysRoot.createSubcontext( "ou=testing01", attributes );
+        assertNotNull( ctx );
+
+        ctx = ( DirContext ) sysRoot.lookup( "ou=testing01" );
+        assertNotNull( ctx );
+
+        attributes = ctx.getAttributes( "" );
+        assertNotNull( attributes );
+        assertEquals( "testing01", attributes.get( "ou" ).get() );
+        attribute = attributes.get( "objectClass" );
+        assertNotNull( attribute );
+        assertTrue( attribute.contains( "top" ) );
+        assertTrue( attribute.contains( "organizationalUnit" ) );
+
+        /*
+         * create ou=testing02,ou=system
+         */
+        attributes = new BasicAttributes();
+        attribute = new BasicAttribute( "objectClass" );
+        attribute.add( "top" );
+        attribute.add( "organizationalUnit" );
+        attributes.put( attribute );
+        attributes.put( "ou", "testing02" );
+        ctx = sysRoot.createSubcontext( "ou=testing02", attributes );
+        assertNotNull( ctx );
+
+        ctx = ( DirContext ) sysRoot.lookup( "ou=testing02" );
+        assertNotNull( ctx );
+
+        attributes = ctx.getAttributes( "" );
+        assertNotNull( attributes );
+        assertEquals( "testing02", attributes.get( "ou" ).get() );
+        attribute = attributes.get( "objectClass" );
+        assertNotNull( attribute );
+        assertTrue( attribute.contains( "top" ) );
+        assertTrue( attribute.contains( "organizationalUnit" ) );
+
+        /*
+         * create ou=subtest,ou=testing01,ou=system
+         */
+        ctx = ( DirContext ) sysRoot.lookup( "ou=testing01" );
+
+        attributes = new BasicAttributes();
+        attribute = new BasicAttribute( "objectClass" );
+        attribute.add( "top" );
+        attribute.add( "organizationalUnit" );
+        attributes.put( attribute );
+        attributes.put( "ou", "subtest" );
+        ctx = ctx.createSubcontext( "ou=subtest", attributes );
+        assertNotNull( ctx );
+
+        ctx = ( DirContext ) sysRoot.lookup( "ou=subtest,ou=testing01" );
+        assertNotNull( ctx );
+
+        attributes = ctx.getAttributes( "" );
+        assertNotNull( attributes );
+        assertEquals( "subtest", attributes.get( "ou" ).get() );
+        attribute = attributes.get( "objectClass" );
+        assertNotNull( attribute );
+        assertTrue( attribute.contains( "top" ) );
+        assertTrue( attribute.contains( "organizationalUnit" ) );
+    }
+
+
+    @Test
+    public void testCreateContextWithNoObjectClass() throws Exception
+    {
+        LdapContext sysRoot = getSystemContext( service );
+
+        Attributes attrs = new AttributesImpl( true );
+
+        try
+        {
+            sysRoot.createSubcontext( "ou=subtest", attrs );// should Fails!
+            fail( "It is not allowed to create a context with a bad entry");
+        }
+        catch ( NamingException e )
+        {
+            assertNotNull( e );
+        }
+    }
+
+
+    @Test
+    public void testCreateJavaContainer() throws Exception
+    {
+        LdapContext sysRoot = getSystemContext( service );
+
+        DirContext ctx = (DirContext)sysRoot.createSubcontext( "cn=subtest" );
+        assertNotNull( ctx );
+        
+        Attributes attributes = ctx.getAttributes( "" );
+        assertNotNull( attributes );
+        
+        assertEquals( "subtest", attributes.get( "cn" ).get() );
+        Attribute attribute = attributes.get( "objectClass" );
+        assertNotNull( attribute );
+        assertTrue( attribute.contains( "top" ) );
+        assertTrue( attribute.contains( "javaContainer" ) );
+    }
+
+
+    @Test
+    public void testCreateJavaContainerBadRDN() throws Exception
+    {
+        LdapContext sysRoot = getSystemContext( service );
+
+        try
+        {
+            sysRoot.createSubcontext( "ou=subtest" );
+            fail( "It is not allowed to create a context with a bad RDN. CN is mandatory");
+        }
+        catch ( LdapSchemaViolationException ne )
+        {
+            assertTrue( true );
+        }
+    }
+}
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/jndi/DIRSERVER169IT.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/jndi/DIRSERVER169IT.java
new file mode 100644
index 0000000..0f593c7
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/jndi/DIRSERVER169IT.java
@@ -0,0 +1,162 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.jndi;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.integ.CiRunner;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getSystemContext;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.naming.Context;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.InitialDirContext;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.LdapContext;
+import java.util.Hashtable;
+
+
+/**
+ * Contributed by Luke Taylor to fix DIRSERVER-169.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+@RunWith ( CiRunner.class )
+public class DIRSERVER169IT
+{
+    public static DirectoryService service;
+
+
+    /**
+     * @todo replace this later with an Ldif tag
+     *
+     * @throws NamingException on error
+     */
+    protected void createData() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+
+        Attributes people = new AttributesImpl( true );
+        Attribute attribute = new AttributeImpl( "objectClass" );
+        attribute.add( "top" );
+        attribute.add( "organizationalUnit" );
+        people.put( attribute );
+        people.put( "ou", "people" );
+        sysRoot.createSubcontext( "ou=people", people );
+
+        Attributes user = new AttributesImpl( "uid", "bob" );
+        user.put( "cn", "Bob Hamilton" );
+        user.put( "userPassword", "bobspassword" );
+
+        Attribute objectClass = new AttributeImpl( "objectClass" );
+        user.put( objectClass );
+        objectClass.add( "top" );
+        objectClass.add( "person" );
+        objectClass.add( "organizationalPerson" );
+        objectClass.add( "inetOrgPerson" );
+        user.put( "sn", "Hamilton" );
+
+        sysRoot.createSubcontext( "uid=bob,ou=people", user );
+    }
+
+
+    @Test
+    public void testSearchResultNameIsRelativeToSearchContext() throws Exception
+    {
+        // @todo replace with ldif tags
+        createData();
+
+        LdapContext sysRoot = getSystemContext( service );
+
+        Hashtable<String,Object> env = new Hashtable<String,Object>();
+        env.put( DirectoryService.JNDI_KEY, service );
+        env.put( Context.INITIAL_CONTEXT_FACTORY, CoreContextFactory.class.getName() );
+        env.put( Context.PROVIDER_URL, "ou=system" );
+
+        DirContext ctx = new InitialDirContext( env );
+        SearchControls ctls = new SearchControls();
+        String searchBase = "ou=people";
+
+        NamingEnumeration<SearchResult> results = ctx.search( searchBase, "(uid=bob)", ctls );
+        assertTrue( results.hasMore() );
+        SearchResult searchResult = results.next();
+
+        StringBuffer userDn = new StringBuffer();
+        userDn.append( searchResult.getName() );
+
+        // Note that only if it's returned as a relative name do you need to 
+        // add the search base to the returned name value 
+        if ( searchResult.isRelative() )
+        {
+            if ( searchBase.length() > 0 )
+            {
+                userDn.append( "," );
+                userDn.append( searchBase );
+            }
+            userDn.append( "," );
+            userDn.append( ctx.getNameInNamespace() );
+        }
+        
+        assertEquals( "uid=bob,ou=people," + sysRoot.getNameInNamespace(), userDn.toString() );
+    }
+
+
+    /**
+     * Search over binary attributes now should work via the core JNDI 
+     * provider.
+     *
+     * @throws Exception if there are errors
+     */
+    @Test
+    public void testPasswordComparisonSucceeds() throws Exception
+    {
+        // @todo replace with ldif tags
+        createData();
+
+        Hashtable<String,Object> env = new Hashtable<String,Object>();
+        env.put( DirectoryService.JNDI_KEY, service );
+        env.put( Context.INITIAL_CONTEXT_FACTORY, CoreContextFactory.class.getName() );
+        env.put( Context.PROVIDER_URL, "ou=system" );
+
+        DirContext ctx = new InitialDirContext( env );
+        SearchControls ctls = new SearchControls();
+        ctls.setReturningAttributes( new String[0] );
+        ctls.setSearchScope( SearchControls.OBJECT_SCOPE );
+
+        String filter = "(userPassword={0})";
+        NamingEnumeration<SearchResult> results = 
+            ctx.search( "uid=bob,ou=people", filter, new Object[] { "bobspassword" }, ctls );
+
+        // We should have a match
+        assertTrue( results.hasMore() );
+    }
+}
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/jndi/DIRSERVER759IT.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/jndi/DIRSERVER759IT.java
new file mode 100644
index 0000000..8719ffb
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/jndi/DIRSERVER759IT.java
@@ -0,0 +1,186 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.jndi;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.integ.CiRunner;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getSystemContext;
+import org.apache.directory.shared.ldap.constants.JndiPropertyConstants;
+import org.apache.directory.shared.ldap.message.AliasDerefMode;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.SearchControls;
+import javax.naming.ldap.LdapContext;
+
+
+/**
+ * Tests the search() methods of the provider.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 493916 $
+ */
+@RunWith ( CiRunner.class )
+public class DIRSERVER759IT
+{
+    public static DirectoryService service;
+
+
+    /**
+     * @todo replace with ldif annotations
+     *
+     * @throws NamingException on errors
+     */
+    protected void createData() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+
+        /*
+         * create ou=testing00,ou=system
+         */
+        Attributes attributes = new AttributesImpl( true );
+        Attribute attribute = new AttributeImpl( "objectClass" );
+        attribute.add( "top" );
+        attribute.add( "organizationalUnit" );
+        attributes.put( attribute );
+        attributes.put( "ou", "testing00" );
+
+        DirContext ctx = sysRoot.createSubcontext( "ou=testing00", attributes );
+        assertNotNull( ctx );
+
+        ctx = ( DirContext ) sysRoot.lookup( "ou=testing00" );
+        assertNotNull( ctx );
+        attributes = ctx.getAttributes( "" );
+        assertNotNull( attributes );
+        assertEquals( "testing00", attributes.get( "ou" ).get() );
+        attribute = attributes.get( "objectClass" );
+        assertNotNull( attribute );
+        assertTrue( attribute.contains( "top" ) );
+        assertTrue( attribute.contains( "organizationalUnit" ) );
+
+        /*
+         * create ou=testing01,ou=system
+         */
+        attributes = new AttributesImpl( true );
+        attribute = new AttributeImpl( "objectClass" );
+        attribute.add( "top" );
+        attribute.add( "organizationalUnit" );
+        attributes.put( attribute );
+        attributes.put( "ou", "testing01" );
+
+        ctx = sysRoot.createSubcontext( "ou=testing01", attributes );
+        assertNotNull( ctx );
+
+        ctx = ( DirContext ) sysRoot.lookup( "ou=testing01" );
+        assertNotNull( ctx );
+        attributes = ctx.getAttributes( "" );
+        assertNotNull( attributes );
+        assertEquals( "testing01", attributes.get( "ou" ).get() );
+        attribute = attributes.get( "objectClass" );
+        assertNotNull( attribute );
+        assertTrue( attribute.contains( "top" ) );
+        assertTrue( attribute.contains( "organizationalUnit" ) );
+
+        /*
+         * create ou=testing02,ou=system
+         */
+        attributes = new AttributesImpl( true );
+        attribute = new AttributeImpl( "objectClass" );
+        attribute.add( "top" );
+        attribute.add( "organizationalUnit" );
+        attributes.put( attribute );
+        attributes.put( "ou", "testing02" );
+        ctx = sysRoot.createSubcontext( "ou=testing02", attributes );
+        assertNotNull( ctx );
+
+        ctx = ( DirContext ) sysRoot.lookup( "ou=testing02" );
+        assertNotNull( ctx );
+
+        attributes = ctx.getAttributes( "" );
+        assertNotNull( attributes );
+        assertEquals( "testing02", attributes.get( "ou" ).get() );
+
+        attribute = attributes.get( "objectClass" );
+        assertNotNull( attribute );
+        assertTrue( attribute.contains( "top" ) );
+        assertTrue( attribute.contains( "organizationalUnit" ) );
+
+        /*
+         * create ou=subtest,ou=testing01,ou=system
+         */
+        ctx = ( DirContext ) sysRoot.lookup( "ou=testing01" );
+
+        attributes = new AttributesImpl( true );
+        attribute = new AttributeImpl( "objectClass" );
+        attribute.add( "top" );
+        attribute.add( "organizationalUnit" );
+        attributes.put( attribute );
+        attributes.put( "ou", "subtest" );
+
+        ctx = ctx.createSubcontext( "ou=subtest", attributes );
+        assertNotNull( ctx );
+
+        ctx = ( DirContext ) sysRoot.lookup( "ou=subtest,ou=testing01" );
+        assertNotNull( ctx );
+
+        attributes = ctx.getAttributes( "" );
+        assertNotNull( attributes );
+        assertEquals( "subtest", attributes.get( "ou" ).get() );
+
+        attribute = attributes.get( "objectClass" );
+        assertNotNull( attribute );
+        assertTrue( attribute.contains( "top" ) );
+        assertTrue( attribute.contains( "organizationalUnit" ) );
+    }
+
+
+    @Test
+    public void testSearchBadDN() throws NamingException
+    {
+        createData();
+        LdapContext sysRoot = getSystemContext( service );
+
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+        controls.setDerefLinkFlag( false );
+        sysRoot.addToEnvironment( JndiPropertyConstants.JNDI_LDAP_DAP_DEREF_ALIASES,
+                AliasDerefMode.NEVER_DEREF_ALIASES.getJndiValue() );
+
+        try
+        {
+            sysRoot.search( "cn=admin", "(objectClass=*)", controls );
+        }
+        catch ( NameNotFoundException nnfe )
+        {
+            assertTrue( true );
+        }
+    }
+}
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/jndi/DIRSERVER783IT.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/jndi/DIRSERVER783IT.java
new file mode 100644
index 0000000..1f6a38b
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/jndi/DIRSERVER783IT.java
@@ -0,0 +1,132 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.
+ *
+ */
+package org.apache.directory.server.core.jndi;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.integ.CiRunner;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.message.ModificationItemImpl;
+import org.junit.Test;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertEquals;
+import org.junit.runner.RunWith;
+
+import javax.naming.Context;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.InitialDirContext;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+
+import java.util.Hashtable;
+
+/**
+ * Tries to demonstrate DIRSERVER-783 ("Adding another value to an attribute
+ * results in the value to be added twice").
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+@RunWith ( CiRunner.class )
+public class DIRSERVER783IT
+{
+    public static DirectoryService service;
+
+
+    /**
+     * Try to add entry with required attribute missing.
+     * 
+     * @throws NamingException if there are errors
+     */
+    @Test
+    public void testAddAnotherValueToAnAttribute() throws NamingException
+    {
+        // create a person without sn
+        Attributes attrs = new AttributesImpl();
+        Attribute ocls = new AttributeImpl("objectClass");
+
+        ocls.add("top");
+        ocls.add("person");
+        attrs.put(ocls);
+        attrs.put("cn", "Fiona Apple");
+        attrs.put("sn", "Apple");
+
+        String rdn = "cn=Fiona Apple";
+
+        Hashtable<String,Object> env = new Hashtable<String, Object>();
+        env.put( DirectoryService.JNDI_KEY, service );
+        env.put( Context.INITIAL_CONTEXT_FACTORY, CoreContextFactory.class.getName() );
+        env.put( Context.PROVIDER_URL, "ou=system" );
+
+        DirContext ctx = new InitialDirContext( env );
+
+        ctx.createSubcontext( rdn, attrs );
+
+        // Add the first value for description
+        String description1 = "an American singer-songwriter";
+        Attribute firstDescr = new AttributeImpl("description", description1);
+        ModificationItemImpl modification = new ModificationItemImpl(DirContext.ADD_ATTRIBUTE, firstDescr);
+        ctx.modifyAttributes(rdn, new ModificationItemImpl[] { modification });
+
+        // Add a second value to description
+        String description2 = "Grammy award winning";
+        Attribute otherDescr = new AttributeImpl("description", description2 );
+
+        modification = new ModificationItemImpl(DirContext.ADD_ATTRIBUTE, otherDescr );
+        ctx.modifyAttributes(rdn, new ModificationItemImpl[] { modification } );
+      
+        // Add a third value to description
+        String description3 = "MTV Music Award winning";
+        Attribute thirdDescr = new AttributeImpl("description", description3 );
+
+        modification = new ModificationItemImpl(DirContext.ADD_ATTRIBUTE, thirdDescr );
+        ctx.modifyAttributes(rdn, new ModificationItemImpl[] { modification });
+
+        // Search Entry
+        SearchControls sctls = new SearchControls();
+        sctls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+        String filter = '(' + rdn + ')';
+        String base = "";
+
+        // Check entry
+        NamingEnumeration<SearchResult> enm = ctx.search(base, filter, sctls);
+        assertTrue(enm.hasMore());
+
+        while (enm.hasMore()) 
+        {
+            SearchResult sr = enm.next();
+            Attribute desc = sr.getAttributes().get("description");
+            assertNotNull(desc);
+            assertTrue(desc.contains(description1));
+            assertTrue(desc.contains(description2));
+            assertTrue(desc.contains(description3));
+            assertEquals(3, desc.size());
+        }
+
+        // Remove the person entry
+        ctx.destroySubcontext(rdn);
+    }
+}
+
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/jndi/DIRSERVER791IT.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/jndi/DIRSERVER791IT.java
new file mode 100644
index 0000000..87deb5d
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/jndi/DIRSERVER791IT.java
@@ -0,0 +1,243 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.
+ *
+ */
+package org.apache.directory.server.core.jndi;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.integ.CiRunner;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getSystemContext;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.message.ModificationItemImpl;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.fail;
+import org.junit.runner.RunWith;
+
+import javax.naming.Context;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.InitialDirContext;
+import javax.naming.directory.InvalidAttributeIdentifierException;
+import javax.naming.directory.InvalidAttributeValueException;
+import javax.naming.directory.SchemaViolationException;
+
+import java.util.Hashtable;
+
+
+/**
+ * A test case which demonstrates the three defects described in DIRSERVER-791.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+@RunWith ( CiRunner.class )
+public class DIRSERVER791IT
+{
+    public static DirectoryService service;
+
+
+    /**
+     * Returns the attributes as depicted as test data in DIRSERVER-791
+     * @return attributes for the test entry
+     */
+    protected Attributes getTestEntryAttributes()
+    {
+        Attributes attrs = new AttributesImpl();
+        Attribute ocls = new AttributeImpl("objectClass");
+        ocls.add("top");
+        ocls.add("person");
+        ocls.add("organizationalPerson");
+        ocls.add("inetOrgPerson");
+        attrs.put(ocls);
+        
+        Attribute cn = new AttributeImpl("cn");
+        cn.add("test");
+        cn.add("aaa");
+        attrs.put(cn);
+        
+        attrs.put("sn", "test");
+
+        return attrs;
+    }
+
+
+    /**
+     * @todo  replace this with an ldif annotation
+     *
+     * @throws NamingException on error
+     */
+    protected void createData() throws NamingException
+    {
+        Attributes entry = this.getTestEntryAttributes();
+        getSystemContext( service ).createSubcontext("cn=test", entry);
+    }
+
+    
+    /**
+     * Demonstrates that removal of a value from RDN attribute which is not part
+     * of the RDN is not possible.
+     *
+     * @throws NamingException on error
+     */
+    @Test
+    public void testDefect1a() throws NamingException 
+    {
+        createData();
+        Hashtable<String,Object> env = new Hashtable<String,Object>();
+        env.put( DirectoryService.JNDI_KEY, service );
+        env.put( Context.INITIAL_CONTEXT_FACTORY, CoreContextFactory.class.getName() );
+        env.put( Context.PROVIDER_URL, "ou=system" );
+
+        DirContext ctx = new InitialDirContext( env );
+        Attribute attr = new AttributeImpl("cn", "aaa");
+        ModificationItemImpl modification = new ModificationItemImpl( DirContext.REMOVE_ATTRIBUTE, attr );
+        ctx.modifyAttributes( "cn=test", new ModificationItemImpl[] { modification } );
+
+        Attributes attrs = ctx.getAttributes("cn=test", new String[] { "cn" });
+        Attribute cn = attrs.get("cn");
+
+        assertEquals("number of cn values", 1, cn.size());
+        assertTrue( cn.contains("test") );
+        assertFalse( cn.contains("aaa") );
+    }
+
+
+    /**
+     * Checks whether it is possible to replace the cn attribute with a single
+     * value. The JIRA issue states that this one works.
+     *
+     * @throws NamingException on error
+     */
+    @Test
+    public void testDefect1b() throws NamingException
+    {
+        createData();
+        Hashtable<String,Object> env = new Hashtable<String,Object>();
+        env.put( DirectoryService.JNDI_KEY, service );
+        env.put( Context.INITIAL_CONTEXT_FACTORY, CoreContextFactory.class.getName() );
+        env.put( Context.PROVIDER_URL, "ou=system" );
+
+        DirContext ctx = new InitialDirContext( env );
+
+        Attribute attr = new AttributeImpl("cn", "test");
+        ModificationItemImpl modification = new ModificationItemImpl(DirContext.REPLACE_ATTRIBUTE, attr);
+        ctx.modifyAttributes("cn=test", new ModificationItemImpl[] { modification });
+
+        Attributes attrs = ctx.getAttributes("cn=test", new String[] { "cn" });
+        Attribute cn = attrs.get("cn");
+
+        assertEquals("number of cn values", 1, cn.size());
+        assertTrue(cn.contains("test"));
+        assertFalse(cn.contains("aaa"));
+    }
+
+
+    /**
+     * It is possible to add an value to objectclass, which isn't a valid
+     * objectclass. The server returns an error, but nevertheless the invalid
+     * value is stored. I think this should be rejected from server.
+     *
+     * @throws NamingException on error
+     */
+    @Test
+    public void testDefect2() throws NamingException
+    {
+        createData();
+        Hashtable<String,Object> env = new Hashtable<String,Object>();
+        env.put( DirectoryService.JNDI_KEY, service );
+        env.put( Context.INITIAL_CONTEXT_FACTORY, CoreContextFactory.class.getName() );
+        env.put( Context.PROVIDER_URL, "ou=system" );
+
+        DirContext ctx = new InitialDirContext( env );
+
+
+        Attribute attr = new AttributeImpl( "objectclass", "test" );
+        ModificationItemImpl modification = new ModificationItemImpl(DirContext.ADD_ATTRIBUTE, attr);
+        
+        try 
+        {
+            ctx.modifyAttributes("cn=test", new ModificationItemImpl[] { modification });
+            fail("Exception expected");
+        } 
+        catch ( SchemaViolationException sve ) 
+        {
+            // Valid behavior
+        } 
+        catch ( InvalidAttributeValueException iave ) 
+        {
+            // Valid behavior
+        }
+        catch ( NamingException ne )
+        {
+            // Valid behavior
+        }
+
+        Attributes attrs = ctx.getAttributes("cn=test", new String[] { "objectClass" });
+        Attribute ocls = attrs.get("objectClass");
+
+        assertEquals("number of objectClasses", 4, ocls.size());
+        assertTrue(ocls.contains("top"));
+        assertTrue(ocls.contains("person"));
+        assertTrue(ocls.contains("organizationalPerson"));
+        assertTrue(ocls.contains("inetOrgPerson"));
+        assertFalse(ocls.contains("test"));
+    }
+
+
+    /**
+     * It is possible to add an attribute to the entry that is not allowed
+     * according the objectclasses. The server should reject this.
+     *
+     * @throws NamingException on error
+     */
+    @Test
+    public void testDefect3() throws NamingException
+    {
+        createData();
+        Hashtable<String,Object> env = new Hashtable<String,Object>();
+        env.put( DirectoryService.JNDI_KEY, service );
+        env.put( Context.INITIAL_CONTEXT_FACTORY, CoreContextFactory.class.getName() );
+        env.put( Context.PROVIDER_URL, "ou=system" );
+
+        DirContext ctx = new InitialDirContext( env );
+
+
+        Attribute attr = new AttributeImpl("javaDoc", "test");
+        ModificationItemImpl modification = new ModificationItemImpl(DirContext.ADD_ATTRIBUTE, attr);
+    
+        try 
+        {
+            ctx.modifyAttributes("cn=test", new ModificationItemImpl[] { modification });
+            fail("Exception expected");
+        } 
+        catch (SchemaViolationException sve) 
+        {
+            // Valid behavior
+        } 
+        catch (InvalidAttributeIdentifierException iaie) 
+        {
+            // Valid behavior
+        }
+    }
+}
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/jndi/DestroyContextIT.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/jndi/DestroyContextIT.java
new file mode 100644
index 0000000..d2c7158
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/jndi/DestroyContextIT.java
@@ -0,0 +1,234 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.jndi;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.integ.CiRunner;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getSystemContext;
+import org.apache.directory.shared.ldap.exception.LdapNameNotFoundException;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.ldap.LdapContext;
+
+
+/**
+ * Tests the destroyContext methods of the provider.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+@RunWith ( CiRunner.class )
+public class DestroyContextIT
+{
+    public static DirectoryService service;
+
+    
+    /**
+     * @todo Replace this with an LDIF directive!!!!!!
+     *
+     * @throws NamingException on error
+     */
+    public void createEntries() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+
+        /*
+         * create ou=testing00,ou=system
+         */
+        Attributes attributes = new AttributesImpl( true );
+        Attribute attribute = new AttributeImpl( "objectClass" );
+        attribute.add( "top" );
+        attribute.add( "organizationalUnit" );
+        attributes.put( attribute );
+        attributes.put( "ou", "testing00" );
+        DirContext ctx = sysRoot.createSubcontext( "ou=testing00", attributes );
+        assertNotNull( ctx );
+
+        ctx = ( DirContext ) sysRoot.lookup( "ou=testing00" );
+        assertNotNull( ctx );
+
+        attributes = ctx.getAttributes( "" );
+        assertNotNull( attributes );
+        assertEquals( "testing00", attributes.get( "ou" ).get() );
+        attribute = attributes.get( "objectClass" );
+        assertNotNull( attribute );
+        assertTrue( attribute.contains( "top" ) );
+        assertTrue( attribute.contains( "organizationalUnit" ) );
+
+        /*
+         * create ou=testing01,ou=system
+         */
+        attributes = new AttributesImpl( true );
+        attribute = new AttributeImpl( "objectClass" );
+        attribute.add( "top" );
+        attribute.add( "organizationalUnit" );
+        attributes.put( attribute );
+        attributes.put( "ou", "testing01" );
+        ctx = sysRoot.createSubcontext( "ou=testing01", attributes );
+        assertNotNull( ctx );
+
+        ctx = ( DirContext ) sysRoot.lookup( "ou=testing01" );
+        assertNotNull( ctx );
+
+        attributes = ctx.getAttributes( "" );
+        assertNotNull( attributes );
+        assertEquals( "testing01", attributes.get( "ou" ).get() );
+        attribute = attributes.get( "objectClass" );
+        assertNotNull( attribute );
+        assertTrue( attribute.contains( "top" ) );
+        assertTrue( attribute.contains( "organizationalUnit" ) );
+
+        /*
+         * create ou=testing02,ou=system
+         */
+        attributes = new AttributesImpl( true );
+        attribute = new AttributeImpl( "objectClass" );
+        attribute.add( "top" );
+        attribute.add( "organizationalUnit" );
+        attributes.put( attribute );
+        attributes.put( "ou", "testing02" );
+        ctx = sysRoot.createSubcontext( "ou=testing02", attributes );
+        assertNotNull( ctx );
+
+        ctx = ( DirContext ) sysRoot.lookup( "ou=testing02" );
+        assertNotNull( ctx );
+
+        attributes = ctx.getAttributes( "" );
+        assertNotNull( attributes );
+        assertEquals( "testing02", attributes.get( "ou" ).get() );
+        attribute = attributes.get( "objectClass" );
+        assertNotNull( attribute );
+        assertTrue( attribute.contains( "top" ) );
+        assertTrue( attribute.contains( "organizationalUnit" ) );
+
+        /*
+         * create ou=subtest,ou=testing01,ou=system
+         */
+        ctx = ( DirContext ) sysRoot.lookup( "ou=testing01" );
+
+        attributes = new AttributesImpl( true );
+        attribute = new AttributeImpl( "objectClass" );
+        attribute.add( "top" );
+        attribute.add( "organizationalUnit" );
+        attributes.put( attribute );
+        attributes.put( "ou", "subtest" );
+        ctx = ctx.createSubcontext( "ou=subtest", attributes );
+        assertNotNull( ctx );
+
+        ctx = ( DirContext ) sysRoot.lookup( "ou=subtest,ou=testing01" );
+        assertNotNull( ctx );
+
+        attributes = ctx.getAttributes( "" );
+        assertNotNull( attributes );
+        assertEquals( "subtest", attributes.get( "ou" ).get() );
+        attribute = attributes.get( "objectClass" );
+        assertNotNull( attribute );
+        assertTrue( attribute.contains( "top" ) );
+        assertTrue( attribute.contains( "organizationalUnit" ) );
+    }
+
+
+    /**
+     * Tests the creation and subsequent read of a new JNDI context under the
+     * system context root.
+     *
+     * @throws NamingException if there are failures
+     */
+    @Test
+    public void testDestroyContext() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+
+        createEntries();
+
+        /*
+         * delete ou=testing00,ou=system
+         */
+        sysRoot.destroySubcontext( "ou=testing00" );
+
+        try
+        {
+            sysRoot.lookup( "ou=testing00" );
+            fail( "ou=testing00, ou=system should not exist" );
+        }
+        catch ( NamingException e )
+        {
+            assertTrue( e instanceof LdapNameNotFoundException );
+        }
+
+        /*
+         * delete ou=subtest,ou=testing01,ou=system
+         */
+        sysRoot.destroySubcontext( "ou=subtest,ou=testing01" );
+
+        try
+        {
+            sysRoot.lookup( "ou=subtest,ou=testing01" );
+            fail( "ou=subtest,ou=testing01,ou=system should not exist" );
+        }
+        catch ( NamingException e )
+        {
+            assertTrue( e instanceof LdapNameNotFoundException );
+        }
+
+        /*
+         * delete ou=testing01,ou=system
+         */
+        sysRoot.destroySubcontext( "ou=testing01" );
+
+        try
+        {
+            sysRoot.lookup( "ou=testing01" );
+            fail( "ou=testing01, ou=system should not exist" );
+        }
+        catch ( NamingException e )
+        {
+            assertTrue( e instanceof LdapNameNotFoundException );
+        }
+
+        /*
+         * delete ou=testing01,ou=system
+         */
+        sysRoot.destroySubcontext( "ou=testing02" );
+
+        try
+        {
+            sysRoot.lookup( "ou=testing02" );
+            fail( "ou=testing02, ou=system should not exist" );
+        }
+        catch ( NamingException e )
+        {
+            assertTrue( e instanceof LdapNameNotFoundException );
+        }
+    }
+
+}
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/jndi/ExtensibleObjectIT.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/jndi/ExtensibleObjectIT.java
new file mode 100644
index 0000000..3863f37
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/jndi/ExtensibleObjectIT.java
@@ -0,0 +1,134 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.jndi;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.integ.CiRunner;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getSystemContext;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.junit.Test;import static org.junit.Assert.assertNotNull;import static org.junit.Assert.assertEquals;import static org.junit.Assert.assertTrue;
+import org.junit.runner.RunWith;
+
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.ldap.LdapContext;
+
+
+/**
+ * Tests the use of extensible objects.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+@RunWith ( CiRunner.class )
+public class ExtensibleObjectIT
+{
+    public static DirectoryService service;
+
+
+    @Test
+    public void testExtensibleObjectModify() throws Exception
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        Attributes attributes = new AttributesImpl( true );
+        Attribute attribute = new AttributeImpl( "objectClass" );
+        attribute.add( "top" );
+        attribute.add( "organizationalUnit" );
+        attributes.put( attribute );
+        attributes.put( "ou", "testing00" );
+        DirContext ctx = sysRoot.createSubcontext( "ou=testing00", attributes );
+        assertNotNull( ctx );
+
+        ctx = ( DirContext ) sysRoot.lookup( "ou=testing00" );
+        assertNotNull( ctx );
+
+        attributes = ctx.getAttributes( "" );
+        assertNotNull( attributes );
+        assertEquals( "testing00", attributes.get( "ou" ).get() );
+        attribute = attributes.get( "objectClass" );
+        assertNotNull( attribute );
+        assertTrue( attribute.contains( "top" ) );
+        assertTrue( attribute.contains( "organizationalUnit" ) );
+
+        Attributes newattribs = new AttributesImpl( true );
+        Attribute freeform = new AttributeImpl( "cn" );
+        freeform.add( "testing" );
+        newattribs.put( freeform );
+        Attribute objectClass = new AttributeImpl( "objectClass" );
+        objectClass.add( "top" );
+        objectClass.add( "extensibleObject" );
+        objectClass.add( "organizationalUnit" );
+        newattribs.put( objectClass );
+        ctx.modifyAttributes( "", DirContext.REPLACE_ATTRIBUTE, newattribs );
+
+        attributes = ctx.getAttributes( "" );
+        assertNotNull( attributes );
+        assertEquals( "testing00", attributes.get( "ou" ).get() );
+        attribute = attributes.get( "objectClass" );
+        assertNotNull( attribute );
+        assertTrue( attribute.contains( "top" ) );
+        assertTrue( attribute.contains( "organizationalUnit" ) );
+        assertTrue( attribute.contains( "extensibleObject" ) );
+        attribute = attributes.get( "cn" );
+        assertTrue( attribute.contains( "testing" ) );
+    }
+
+
+    @Test
+    public void testExtensibleObjectAdd() throws Exception
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        Attributes attributes = new AttributesImpl( true );
+        Attribute attribute = new AttributeImpl( "objectClass" );
+        attribute.add( "top" );
+        attribute.add( "extensibleObject" );
+        attribute.add( "organizationalUnit" );
+        attributes.put( attribute );
+        attributes.put( "ou", "testing00" );
+
+        // WARNING: extensible objects cannot accept any arbitrary 
+        // attribute.  The attribute must be defined by the schema
+        // at a bare minimum or the addition will be rejected
+
+        // here's an attribute that is not on the MAY or MUST list for 
+        // an organizationalUnit - it's our test for extensible objects
+        attributes.put( "employeeType", "testing" );
+
+        DirContext ctx = sysRoot.createSubcontext( "ou=testing00", attributes );
+        assertNotNull( ctx );
+
+        ctx = ( DirContext ) sysRoot.lookup( "ou=testing00" );
+        assertNotNull( ctx );
+
+        attributes = ctx.getAttributes( "" );
+        assertNotNull( attributes );
+        assertEquals( "testing00", attributes.get( "ou" ).get() );
+        attribute = attributes.get( "objectClass" );
+        assertNotNull( attribute );
+        assertTrue( attribute.contains( "top" ) );
+        assertTrue( attribute.contains( "extensibleObject" ) );
+        assertTrue( attribute.contains( "organizationalUnit" ) );
+        attribute = attributes.get( "employeeType" );
+        assertTrue( attribute.contains( "testing" ) );
+    }
+}
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/jndi/ListIT.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/jndi/ListIT.java
new file mode 100644
index 0000000..7f7cd1e
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/jndi/ListIT.java
@@ -0,0 +1,137 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.jndi;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.integ.CiRunner;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getUserAddLdif;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getRootContext;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getContext;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getSystemContext;
+import org.apache.directory.shared.ldap.ldif.LdifEntry;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.naming.NameClassPair;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.ldap.LdapContext;
+import java.util.HashSet;
+
+
+/**
+ * Tests our ability to list elements as the admin user and as a non admin user
+ * on security sensitive values.  We do not return results or name class pairs
+ * for user accounts if the user is not the admin.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+@RunWith ( CiRunner.class )
+public class ListIT
+{
+    public static DirectoryService service;
+
+
+    @Test
+    public void testListSystemAsNonAdmin() throws NamingException
+    {
+        LdifEntry akarasulu = getUserAddLdif();
+        getRootContext( service ).createSubcontext( akarasulu.getDn(), akarasulu.getAttributes() );
+
+        LdapContext sysRoot = getContext( akarasulu.getDn(), service, "ou=system" );
+        HashSet<String> set = new HashSet<String>();
+        NamingEnumeration<NameClassPair> list = sysRoot.list( "" );
+
+        while ( list.hasMore() )
+        {
+            NameClassPair ncp = list.next();
+            set.add( ncp.getName() );
+        }
+
+        assertFalse( set.contains( "uid=admin,ou=system" ) );
+        assertTrue( set.contains( "ou=users,ou=system" ) );
+        assertTrue( set.contains( "ou=groups,ou=system" ) );
+    }
+
+
+    @Test
+    public void testListUsersAsNonAdmin() throws NamingException
+    {
+        LdifEntry akarasulu = getUserAddLdif();
+        getRootContext( service ).createSubcontext( akarasulu.getDn(), akarasulu.getAttributes() );
+
+        LdapContext sysRoot = getContext( akarasulu.getDn(), service, "ou=system" );
+        HashSet<String> set = new HashSet<String>();
+        NamingEnumeration<NameClassPair> list = sysRoot.list( "ou=users" );
+
+        while ( list.hasMore() )
+        {
+            NameClassPair ncp = list.next();
+            set.add( ncp.getName() );
+        }
+
+        // @todo this assertion fails now - is this the expected behavoir?
+        // assertFalse( set.contains( "uid=akarasulu,ou=users,ou=system" ) );
+    }
+
+
+    @Test
+    public void testListSystemAsAdmin() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        HashSet<String> set = new HashSet<String>();
+        NamingEnumeration<NameClassPair> list = sysRoot.list( "" );
+
+        while ( list.hasMore() )
+        {
+            NameClassPair ncp = list.next();
+            set.add( ncp.getName() );
+        }
+
+        assertTrue( set.contains( "uid=admin,ou=system" ) );
+        assertTrue( set.contains( "ou=users,ou=system" ) );
+        assertTrue( set.contains( "ou=groups,ou=system" ) );
+    }
+
+
+    @Test
+    public void testListUsersAsAdmin() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        HashSet<String> set = new HashSet<String>();
+        LdifEntry akarasulu = getUserAddLdif();
+        getRootContext( service ).createSubcontext( akarasulu.getDn(), akarasulu.getAttributes() );
+
+        NamingEnumeration<NameClassPair> list = sysRoot.list( "ou=users" );
+        
+        while ( list.hasMore() )
+        {
+            NameClassPair ncp = list.next();
+            set.add( ncp.getName() );
+        }
+
+        assertTrue( set.contains( "uid=akarasulu,ou=users,ou=system" ) );
+    }
+}
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/jndi/MixedCaseITest.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/jndi/MixedCaseITest.java
new file mode 100644
index 0000000..afdd347
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/jndi/MixedCaseITest.java
@@ -0,0 +1,230 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.jndi;
+
+
+import org.apache.directory.server.core.DefaultDirectoryService;
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.entry.DefaultServerEntry;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.integ.CiRunner;
+import org.apache.directory.server.core.integ.DirectoryServiceFactory;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getContext;
+import org.apache.directory.server.core.integ.Level;
+import org.apache.directory.server.core.integ.annotations.Factory;
+import org.apache.directory.server.core.integ.annotations.CleanupLevel;
+import org.apache.directory.server.core.partition.Partition;
+import org.apache.directory.server.core.partition.impl.btree.Index;
+import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmIndex;
+import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmPartition;
+import org.apache.directory.shared.ldap.exception.LdapNameNotFoundException;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.message.ModificationItemImpl;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.LdapContext;
+import java.util.HashSet;
+import java.util.Set;
+
+
+/**
+ * Tests various operations against a partition whose suffix contains both upper and lower case letters.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+@RunWith ( CiRunner.class )
+@CleanupLevel ( Level.CLASS )
+@Factory ( MixedCaseITest.MyFactory.class )
+public class MixedCaseITest
+{
+    public static DirectoryService service;
+
+    private static final String SUFFIX_DN = "dc=Apache,dc=Org";
+
+
+    public static class MyFactory implements DirectoryServiceFactory
+    {
+        public DirectoryService newInstance() throws NamingException
+        {
+            DirectoryService service = new DefaultDirectoryService();
+            service.getChangeLog().setEnabled( true );
+
+            JdbmPartition partition = new JdbmPartition();
+            partition.setId( "apache" );
+            partition.setSuffix( SUFFIX_DN );
+
+            HashSet<Index> indexedAttributes = new HashSet<Index>();
+            indexedAttributes.add( new JdbmIndex( "objectClass" ) );
+            indexedAttributes.add( new JdbmIndex( "ou" ) );
+            indexedAttributes.add( new JdbmIndex( "uid" ) );
+            partition.setIndexedAttributes( indexedAttributes );
+
+            ServerEntry serverEntry = new DefaultServerEntry( service.getRegistries(), new LdapDN( SUFFIX_DN ) );
+            serverEntry.put( "objectClass", "top", "domain", "extensibleObject" );
+            serverEntry.put( "dc", "Apache" );
+
+            partition.setContextEntry( serverEntry );
+
+            Set<Partition> partitions = new HashSet<Partition>();
+            partitions.add( partition );
+
+            service.setPartitions( partitions );
+            return service;
+        }
+    }
+
+    
+    @Test
+    public void testSearch() throws NamingException
+    {
+        LdapContext ctxRoot = getContext( "uid=admin,ou=system", service, SUFFIX_DN );
+
+        SearchControls sc = new SearchControls();
+        sc.setSearchScope( SearchControls.SUBTREE_SCOPE );
+
+        NamingEnumeration<SearchResult> ne = ctxRoot.search( "", "(objectClass=*)", sc );
+        assertTrue( "Search should return at least one entry.", ne.hasMore() );
+
+        SearchResult sr = ne.next();
+        assertEquals( "The entry returned should be the root entry.", SUFFIX_DN, sr.getName() );
+        assertFalse( "Search should return no more entries.", ne.hasMore() );
+    }
+
+
+    @Test
+    public void testAdd() throws NamingException
+    {
+        LdapContext ctxRoot = getContext( "uid=admin,ou=system", service, SUFFIX_DN );
+
+        String dn = "ou=Test";
+
+        Attributes attributes = new AttributesImpl( true );
+        Attribute attribute = new AttributeImpl( "objectClass" );
+        attribute.add( "top" );
+        attribute.add( "organizationalUnit" );
+        attributes.put( attribute );
+        attributes.put( "ou", "Test" );
+
+        DirContext ctx = ctxRoot.createSubcontext( dn, attributes );
+        assertNotNull( ctx );
+
+        SearchControls sc = new SearchControls();
+        sc.setSearchScope( SearchControls.OBJECT_SCOPE );
+
+        NamingEnumeration<SearchResult> ne = ctxRoot.search( dn, "(objectClass=*)", sc );
+        assertTrue( "Search should return at least one entry.", ne.hasMore() );
+
+        SearchResult sr = ne.next();
+        assertEquals( "The entry returned should be the entry added earlier.", dn + "," + SUFFIX_DN, sr.getName() );
+        assertFalse( "Search should return no more entries.", ne.hasMore() );
+    }
+
+
+    @Test
+    public void testModify() throws NamingException
+    {
+        LdapContext ctxRoot = getContext( "uid=admin,ou=system", service, SUFFIX_DN );
+
+        String dn = "ou=Test";
+        String description = "New Value";
+
+        Attributes attributes = new AttributesImpl( true );
+        Attribute attribute = new AttributeImpl( "objectClass" );
+        attribute.add( "top" );
+        attribute.add( "organizationalUnit" );
+        attributes.put( attribute );
+        attributes.put( "ou", "Test" );
+        attributes.put( "description", "Old Value" );
+
+        DirContext ctx = ctxRoot.createSubcontext( dn, attributes );
+        assertNotNull( ctx );
+
+        ModificationItemImpl[] mods = new ModificationItemImpl[1];
+        mods[0] = new ModificationItemImpl( DirContext.REPLACE_ATTRIBUTE, new AttributeImpl( "description", description ) );
+
+        ctxRoot.modifyAttributes( dn, mods );
+
+        SearchControls sc = new SearchControls();
+        sc.setSearchScope( SearchControls.OBJECT_SCOPE );
+
+        NamingEnumeration<SearchResult> ne = ctxRoot.search( dn, "(objectClass=*)", sc );
+        assertTrue( "Search should return at least one entry.", ne.hasMore() );
+
+        SearchResult sr = ( SearchResult ) ne.next();
+        assertEquals( "The entry returned should be the entry added earlier.", dn + "," + SUFFIX_DN, sr.getName() );
+
+        attributes = sr.getAttributes();
+        attribute = attributes.get( "description" );
+
+        assertEquals( "The description attribute should contain the new value.", description, attribute.get() );
+        assertFalse( "Search should return no more entries.", ne.hasMore() );
+    }
+
+
+    @Test
+    public void testDelete() throws NamingException
+    {
+        LdapContext ctxRoot = getContext( "uid=admin,ou=system", service, SUFFIX_DN );
+
+        String dn = "ou=Test";
+
+        Attributes attributes = new AttributesImpl( true );
+        Attribute attribute = new AttributeImpl( "objectClass" );
+        attribute.add( "top" );
+        attribute.add( "organizationalUnit" );
+        attributes.put( attribute );
+        attributes.put( "ou", "Test" );
+
+        DirContext ctx = ctxRoot.createSubcontext( dn, attributes );
+        assertNotNull( ctx );
+
+        ctxRoot.destroySubcontext( dn );
+
+        SearchControls sc = new SearchControls();
+        sc.setSearchScope( SearchControls.OBJECT_SCOPE );
+
+        try
+        {
+            ctxRoot.search( dn, "(objectClass=*)", sc );
+            fail( "Search should throw exception." );
+        }
+        catch ( LdapNameNotFoundException e )
+        {
+            // ignore
+        }
+    }
+}
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/jndi/ModifyContextIT.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/jndi/ModifyContextIT.java
new file mode 100644
index 0000000..0828d63
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/jndi/ModifyContextIT.java
@@ -0,0 +1,255 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.jndi;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.integ.CiRunner;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getRootContext;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getSystemContext;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getUserAddLdif;
+import org.apache.directory.shared.ldap.exception.LdapInvalidAttributeValueException;
+import org.apache.directory.shared.ldap.ldif.LdifEntry;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.ldap.LdapContext;
+
+
+/**
+ * Tests the methods on JNDI contexts that are analogous to entry modify
+ * operations in LDAP.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+@RunWith ( CiRunner.class )
+public class ModifyContextIT
+{
+    public static DirectoryService service;
+
+
+    /**
+     * @todo put this into an ldif annotation
+     *
+     * @throws NamingException on error
+     */
+    protected void createData() throws NamingException
+    {
+        LdifEntry akarasulu = getUserAddLdif();
+        getRootContext( service ).createSubcontext( akarasulu.getDn(), akarasulu.getAttributes() );
+        LdapContext sysRoot = getSystemContext( service );
+
+        /*
+         * create ou=testing00,ou=system
+         */
+        Attributes attributes = new AttributesImpl( true );
+        Attribute attribute = new AttributeImpl( "objectClass" );
+        attribute.add( "top" );
+        attribute.add( "organizationalUnit" );
+        attributes.put( attribute );
+        attributes.put( "ou", "testing00" );
+        DirContext ctx = sysRoot.createSubcontext( "ou=testing00", attributes );
+        assertNotNull( ctx );
+
+        ctx = ( DirContext ) sysRoot.lookup( "ou=testing00" );
+        assertNotNull( ctx );
+
+        attributes = ctx.getAttributes( "" );
+        assertNotNull( attributes );
+        assertEquals( "testing00", attributes.get( "ou" ).get() );
+        attribute = attributes.get( "objectClass" );
+        assertNotNull( attribute );
+        assertTrue( attribute.contains( "top" ) );
+        assertTrue( attribute.contains( "organizationalUnit" ) );
+
+        /*
+         * create ou=testing01,ou=system
+         */
+        attributes = new AttributesImpl( true );
+        attribute = new AttributeImpl( "objectClass" );
+        attribute.add( "top" );
+        attribute.add( "organizationalUnit" );
+        attributes.put( attribute );
+        attributes.put( "ou", "testing01" );
+        ctx = sysRoot.createSubcontext( "ou=testing01", attributes );
+        assertNotNull( ctx );
+
+        ctx = ( DirContext ) sysRoot.lookup( "ou=testing01" );
+        assertNotNull( ctx );
+
+        attributes = ctx.getAttributes( "" );
+        assertNotNull( attributes );
+        assertEquals( "testing01", attributes.get( "ou" ).get() );
+        attribute = attributes.get( "objectClass" );
+        assertNotNull( attribute );
+        assertTrue( attribute.contains( "top" ) );
+        assertTrue( attribute.contains( "organizationalUnit" ) );
+
+        /*
+         * create ou=testing02,ou=system
+         */
+        attributes = new AttributesImpl( true );
+        attribute = new AttributeImpl( "objectClass" );
+        attribute.add( "top" );
+        attribute.add( "organizationalUnit" );
+        attributes.put( attribute );
+        attributes.put( "ou", "testing02" );
+        ctx = sysRoot.createSubcontext( "ou=testing02", attributes );
+        assertNotNull( ctx );
+
+        ctx = ( DirContext ) sysRoot.lookup( "ou=testing02" );
+        assertNotNull( ctx );
+
+        attributes = ctx.getAttributes( "" );
+        assertNotNull( attributes );
+        assertEquals( "testing02", attributes.get( "ou" ).get() );
+        attribute = attributes.get( "objectClass" );
+        assertNotNull( attribute );
+        assertTrue( attribute.contains( "top" ) );
+        assertTrue( attribute.contains( "organizationalUnit" ) );
+
+        /*
+         * create ou=subtest,ou=testing01,ou=system
+         */
+        ctx = ( DirContext ) sysRoot.lookup( "ou=testing01" );
+
+        attributes = new AttributesImpl( true );
+        attribute = new AttributeImpl( "objectClass" );
+        attribute.add( "top" );
+        attribute.add( "organizationalUnit" );
+        attributes.put( attribute );
+        attributes.put( "ou", "subtest" );
+        ctx = ctx.createSubcontext( "ou=subtest", attributes );
+        assertNotNull( ctx );
+
+        ctx = ( DirContext ) sysRoot.lookup( "ou=subtest,ou=testing01" );
+        assertNotNull( ctx );
+
+        attributes = ctx.getAttributes( "" );
+        assertNotNull( attributes );
+        assertEquals( "subtest", attributes.get( "ou" ).get() );
+        attribute = attributes.get( "objectClass" );
+        assertNotNull( attribute );
+        assertTrue( attribute.contains( "top" ) );
+        assertTrue( attribute.contains( "organizationalUnit" ) );
+    }
+
+
+    /**
+     * Add a new attribute without any value to a person entry: testcase for
+     * http://issues.apache.org/jira/browse/DIRSERVER-630.
+     * 
+     * @throws NamingException on error
+     */
+    @Test
+    public void testIllegalModifyAdd() throws NamingException
+    {
+        createData();
+
+        LdapContext sysRoot = getSystemContext( service );
+
+        Attribute attr = new AttributeImpl( "description" );
+        Attributes attrs = new AttributesImpl();
+        attrs.put( attr );
+
+        try
+        {
+            sysRoot.modifyAttributes( "uid=akarasulu,ou=users", DirContext.ADD_ATTRIBUTE, attrs );
+            fail( "error expected due to empty attribute value" );
+        }
+        catch ( LdapInvalidAttributeValueException e )
+        {
+            // expected
+        }
+
+        // Check whether entry is unmodified, i.e. no description
+        Attributes entry = sysRoot.getAttributes( "uid=akarasulu,ou=users" );
+        assertNull( entry.get( "description" ) );
+    }
+
+
+    @Test
+    public void testModifyOperation() throws NamingException
+    {
+        createData();
+
+        LdapContext sysRoot = getSystemContext( service );
+        Attributes attributes = new AttributesImpl( true );
+        attributes.put( "ou", "testCases" );
+        sysRoot.modifyAttributes( "ou=testing00", DirContext.ADD_ATTRIBUTE, attributes );
+
+        DirContext ctx = ( DirContext ) sysRoot.lookup( "ou=testing00" );
+        attributes = ctx.getAttributes( "" );
+        assertTrue( attributes.get( "ou" ).contains( "testCases" ) );
+
+        Attribute attribute = attributes.get( "creatorsName" );
+        assertNull( attribute );
+
+        attribute = attributes.get( "createTimestamp" );
+        assertNull( attribute );
+
+        attribute = attributes.get( "modifiersName" );
+        assertNull( attribute );
+
+        attributes.get( "modifyTimestamp" );
+        assertNull( attribute );
+    }
+
+
+    @Test
+    public void testRemoveNonExistingValue() throws NamingException
+    {
+        createData();
+
+        LdapContext sysRoot = getSystemContext( service );
+        Attributes attributes = new AttributesImpl( true );
+        attributes.put( "ou", "testCases" );
+        sysRoot.modifyAttributes( "ou=testing00", DirContext.REMOVE_ATTRIBUTE, attributes );
+
+        DirContext ctx = ( DirContext ) sysRoot.lookup( "ou=testing00" );
+        attributes = ctx.getAttributes( "" );
+        assertFalse( attributes.get( "ou" ).contains( "testCases" ) );
+
+        Attribute attribute = attributes.get( "creatorsName" );
+        assertNull( attribute );
+
+        attribute = attributes.get( "createTimestamp" );
+        assertNull( attribute );
+
+        attribute = attributes.get( "modifiersName" );
+        assertNull( attribute );
+
+        attributes.get( "modifyTimestamp" );
+        assertNull( attribute );
+    }
+}
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/jndi/ObjStateFactoryIT.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/jndi/ObjStateFactoryIT.java
new file mode 100644
index 0000000..679dc16
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/jndi/ObjStateFactoryIT.java
@@ -0,0 +1,275 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.jndi;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.integ.CiRunner;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getSystemContext;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getUserAddLdif;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getRootContext;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.util.ArrayUtils;
+import org.apache.directory.shared.ldap.util.StringTools;
+import org.apache.directory.shared.ldap.ldif.LdifEntry;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertNull;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.naming.Context;
+import javax.naming.Name;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.SchemaViolationException;
+import javax.naming.ldap.LdapContext;
+import javax.naming.spi.DirObjectFactory;
+import javax.naming.spi.DirStateFactory;
+import java.util.Hashtable;
+
+
+/**
+ * Tests to make sure that object and state factories work.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+@RunWith ( CiRunner.class )
+public class ObjStateFactoryIT
+{
+    public static DirectoryService service;
+
+
+    @Test
+    public void testObjectFactory() throws NamingException
+    {
+        LdifEntry akarasulu = getUserAddLdif();
+        getRootContext( service ).createSubcontext( akarasulu.getDn(), akarasulu.getAttributes() );
+
+        LdapContext sysRoot = getSystemContext( service );
+        sysRoot.addToEnvironment( Context.OBJECT_FACTORIES, PersonObjectFactory.class.getName() );
+        Object obj = sysRoot.lookup( "uid=akarasulu, ou=users" );
+        Attributes attrs = sysRoot.getAttributes( "uid=akarasulu, ou=users" );
+        assertEquals( Person.class, obj.getClass() );
+        Person me = ( Person ) obj;
+        assertEquals( attrs.get( "sn" ).get(), me.getLastname() );
+        assertEquals( attrs.get( "cn" ).get(), me.getCn() );
+        assertTrue( ArrayUtils.isEquals( attrs.get( "userPassword" ).get(), StringTools.getBytesUtf8( "test" ) ) );
+        assertEquals( attrs.get( "telephonenumber" ).get(), me.getTelephoneNumber() );
+        assertNull( me.getSeealso() );
+        assertNull( me.getDescription() );
+    }
+
+
+    @Test
+    public void testStateFactory() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+
+        sysRoot.addToEnvironment( Context.STATE_FACTORIES, PersonStateFactory.class.getName() );
+        Person p = new Person( "Rodriguez", "Mr. Kerberos", "noices", "555-1212", "sn=erodriguez", "committer" );
+        sysRoot.bind( "uid=erodriguez, ou=users", p );
+        Attributes attrs = sysRoot.getAttributes( "uid=erodriguez, ou=users" );
+        assertEquals( "Rodriguez", attrs.get( "sn" ).get() );
+        assertEquals( "Mr. Kerberos", attrs.get( "cn" ).get() );
+        assertTrue( ArrayUtils.isEquals( attrs.get( "userPassword" ).get(), StringTools.getBytesUtf8( "noices" ) ) );
+        assertEquals( "555-1212", attrs.get( "telephonenumber" ).get() );
+        assertEquals( "sn=erodriguez", attrs.get( "seealso" ).get() );
+        assertEquals( "committer", attrs.get( "description" ).get() );
+    }
+
+    
+    public static class PersonStateFactory implements DirStateFactory
+    {
+        public Result getStateToBind( Object obj, Name name, Context nameCtx, Hashtable environment, Attributes inAttrs )
+            throws NamingException
+        {
+            // Only interested in Person objects
+            if ( obj instanceof Person )
+            {
+
+                Attributes outAttrs;
+
+                if ( inAttrs == null )
+                {
+                    outAttrs = new AttributesImpl( true );
+                }
+                else
+                {
+                    outAttrs = ( Attributes ) inAttrs.clone();
+                }
+
+                // Set up object class
+                if ( outAttrs.get( "objectclass" ) == null )
+                {
+                    Attribute oc = new AttributeImpl( "objectclass", "person" );
+                    oc.add( "top" );
+                    outAttrs.put( oc );
+                }
+
+                Person per = ( Person ) obj;
+
+                // mandatory attributes
+                if ( per.getLastname() != null )
+                {
+                    outAttrs.put( "sn", per.getLastname() );
+                }
+                else
+                {
+                    throw new SchemaViolationException( "Person must have surname" );
+                }
+
+                if ( per.getCn() != null )
+                {
+                    outAttrs.put( "cn", per.getCn() );
+                }
+                else
+                {
+                    throw new SchemaViolationException( "Person must have common name" );
+                }
+
+                // optional attributes
+                if ( per.getPassword() != null )
+                {
+                    outAttrs.put( "userPassword", per.getPassword() );
+                }
+                if ( per.getTelephoneNumber() != null )
+                {
+                    outAttrs.put( "telephoneNumber", per.getTelephoneNumber() );
+                }
+                if ( per.getSeealso() != null )
+                {
+                    outAttrs.put( "seeAlso", per.getSeealso() );
+                }
+                if ( per.getDescription() != null )
+                {
+                    outAttrs.put( "description", per.getDescription() );
+                }
+
+                return new DirStateFactory.Result( null, outAttrs );
+            }
+
+            return null;
+        }
+
+
+        public Object getStateToBind( Object obj, Name name, Context nameCtx, Hashtable environment )
+            throws NamingException
+        {
+            throw new UnsupportedOperationException( "Please use directory support overload with Attributes argument." );
+        }
+    }
+
+
+    public static class PersonObjectFactory implements DirObjectFactory
+    {
+        public Object getObjectInstance( Object obj, Name name, Context nameCtx, Hashtable environment, Attributes attrs )
+            throws Exception
+        {
+            // Only interested in Attributes with "person" objectclass
+            Attribute oc = ( attrs != null ? attrs.get( "objectclass" ) : null );
+            if ( oc != null && oc.contains( "person" ) )
+            {
+                Attribute attr;
+                String passwd = null;
+
+                // Extract the password
+                attr = attrs.get( "userPassword" );
+                if ( attr != null )
+                {
+                    Object pw = attr.get();
+
+                    if ( pw instanceof String )
+                        passwd = ( String ) pw;
+                    else
+                        passwd = new String( ( byte[] ) pw );
+                }
+
+                return new Person( ( String ) attrs.get( "sn" ).get(), ( String ) attrs.get( "cn" ).get(), passwd,
+                    ( attr = attrs.get( "telephoneNumber" ) ) != null ? ( String ) attr.get() : null, ( attr = attrs
+                        .get( "seealso" ) ) != null ? ( String ) attr.get() : null,
+                    ( attr = attrs.get( "description" ) ) != null ? ( String ) attr.get() : null );
+            }
+            return null;
+        }
+
+
+        public Object getObjectInstance( Object obj, Name name, Context nameCtx, Hashtable environment )
+            throws Exception
+        {
+            throw new UnsupportedOperationException( "Please use directory support overload with Attributes argument." );
+        }
+    }
+
+    public static class Person
+    {
+        private String sn, cn, pwd, tele, seealso, desc;
+
+
+        public Person(String sn, String cn, String pwd, String tele, String seealso, String desc)
+        {
+            this.sn = sn;
+            this.cn = cn;
+            this.pwd = pwd;
+            this.tele = tele;
+            this.seealso = seealso;
+            this.desc = desc;
+        }
+
+
+        public String getLastname()
+        {
+            return sn;
+        }
+
+
+        public String getCn()
+        {
+            return cn;
+        }
+
+
+        public String getPassword()
+        {
+            return pwd;
+        }
+
+
+        public String getTelephoneNumber()
+        {
+            return tele;
+        }
+
+
+        public String getSeealso()
+        {
+            return seealso;
+        }
+
+
+        public String getDescription()
+        {
+            return desc;
+        }
+    }
+}
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/jndi/RFC2713IT.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/jndi/RFC2713IT.java
new file mode 100644
index 0000000..0fcc359
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/jndi/RFC2713IT.java
@@ -0,0 +1,88 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.jndi;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.integ.CiRunner;
+import static org.apache.directory.server.core.integ.IntegrationUtils.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertNotNull;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.ldap.LdapContext;
+import java.util.ArrayList;
+
+
+/**
+ * Tests to validate whatever functionality we have for complying with
+ * <a href="http://www.faqs.org/rfcs/rfc2713.html">RFC 2713</a>.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+@RunWith ( CiRunner.class )
+public class RFC2713IT
+{
+    public static DirectoryService service;
+    
+
+    @Test
+    @SuppressWarnings("unchecked")
+    public void testSerializatin() throws Exception
+    {
+        LdapContext sysRoot = getSystemContext( service );
+
+        ArrayList<String> colors = new ArrayList<String>();
+        colors.add( "red" );
+        colors.add( "white" );
+        colors.add( "blue" );
+        sysRoot.bind( "cn=colors", colors );
+
+        Object obj = sysRoot.lookup( "cn=colors" );
+        assertTrue( obj instanceof ArrayList );
+        colors = ( ArrayList<String> ) obj;
+        assertEquals( 3, colors.size() );
+        assertTrue( colors.contains( "red" ) );
+        assertTrue( colors.contains( "white" ) );
+        assertTrue( colors.contains( "blue" ) );
+
+        Attributes attrs = sysRoot.getAttributes( "cn=colors" );
+        Attribute attr = attrs.get( "objectClass" );
+        assertNotNull( attr );
+        assertEquals( 4, attr.size() );
+        assertTrue( attr.contains( "top" ) );
+        assertTrue( attr.contains( "javaObject" ) );
+        assertTrue( attr.contains( "javaContainer" ) );
+        assertTrue( attr.contains( "javaSerializedObject" ) );
+        attr = attrs.get( "javaClassName" );
+        assertNotNull( attr );
+        assertEquals( 1, attr.size() );
+        assertTrue( attr.contains( "java.util.ArrayList" ) );
+
+        attr = attrs.get( "javaSerializedData" );
+        assertNotNull( attr );
+        assertEquals( 1, attr.size() );
+    }
+}
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/jndi/ReferralIT.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/jndi/ReferralIT.java
new file mode 100644
index 0000000..ed9335b
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/jndi/ReferralIT.java
@@ -0,0 +1,1266 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.jndi;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.integ.CiRunner;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getSystemContext;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getUserAddLdif;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getRootContext;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.exception.LdapNamingException;
+import org.apache.directory.shared.ldap.ldif.LdifEntry;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.message.ModificationItemImpl;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.naming.Context;
+import javax.naming.Name;
+import javax.naming.NameAlreadyBoundException;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.ReferralException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.LdapContext;
+import java.util.HashMap;
+import java.util.Map;
+
+
+/**
+ * Tests the referral handling functionality within the server's core.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+@RunWith ( CiRunner.class )
+public class ReferralIT
+{
+    public static DirectoryService service;
+    private TestData td = new TestData();
+
+
+    /*
+     * NOTE: We may encounter conflicting circumstances where the ManageDsaIT control
+     * is included in the request controls yet the Context.REFERRAL is set to 
+     * something other than ignore: throw or follow.  We have to figure out what to
+     * do in these cases.
+     * 
+     * Simply throw an illegal state exception when this happens?
+     * 
+     * NOTE: Need to figure out the behavoir of referral handling during search
+     * when aliases are being dereferenced.
+     */
+
+    private class TestData
+    {
+        LdapContext rootCtx;
+        Name ctxDn;
+        LdapContext refCtx;
+    }
+
+
+    private void addReferralEntry() throws NamingException
+    {
+        String ref0 = "ldap://fermi:10389/ou=users,ou=system";
+        String ref1 = "ldap://hertz:10389/ou=users,dc=example,dc=com";
+        String ref2 = "ldap://maxwell:10389/ou=users,ou=system";
+        td.rootCtx = getSystemContext( service );
+
+        LdifEntry akarasulu = getUserAddLdif();
+        getRootContext( service ).createSubcontext( akarasulu.getDn(), akarasulu.getAttributes() );
+
+        // -------------------------------------------------------------------
+        // Adds a referral entry regardless of referral handling settings
+        // -------------------------------------------------------------------
+
+        // Add a referral entry ( should be fine with or without the control )
+        Attributes referral = new AttributesImpl( "objectClass", "top", true );
+        referral.get( "objectClass" ).add( "referral" );
+        referral.get( "objectClass" ).add( "extensibleObject" );
+        referral.put( "ref", ref0 );
+        referral.get( "ref" ).add( ref1 );
+        referral.get( "ref" ).add( ref2 );
+        referral.put( "ou", "users" );
+
+        // Just in case if server is a remote server destroy remaing referral
+        td.rootCtx.addToEnvironment( Context.REFERRAL, "ignore" );
+        try
+        {
+            td.rootCtx.destroySubcontext( "uid=akarasulu,ou=users" );
+        }
+        catch ( NameNotFoundException e )
+        {
+            e.printStackTrace();
+        }
+        try
+        {
+            td.rootCtx.destroySubcontext( "ou=users" );
+        }
+        catch ( NameNotFoundException e )
+        {
+            e.printStackTrace();
+        }
+        try
+        {
+            td.refCtx = ( LdapContext ) td.rootCtx.createSubcontext( "ou=users", referral );
+        }
+        catch ( NameAlreadyBoundException e )
+        {
+            td.refCtx = ( LdapContext ) td.rootCtx.lookup( "ou=users" );
+        }
+        referral = td.refCtx.getAttributes( "" );
+        assertTrue( referral.get( "ou" ).contains( "users" ) );
+        assertTrue( referral.get( "objectClass" ).contains( "referral" ) );
+    }
+
+
+    private void checkAncestorReferrals( ReferralException e ) throws Exception
+    {
+        assertEquals( "ldap://fermi:10389", e.getReferralInfo() );
+        assertTrue( e.skipReferral() );
+        assertEquals( "ldap://hertz:10389/cn=alex%20karasulu,ou=apache,ou=users,dc=example,dc=com", e.getReferralInfo() );
+        assertTrue( e.skipReferral() );
+        assertEquals( "ldap://maxwell:10389", e.getReferralInfo() );
+        assertFalse( e.skipReferral() );
+    }
+
+
+    private void checkParentReferrals( ReferralException e ) throws Exception
+    {
+        assertEquals( "ldap://fermi:10389", e.getReferralInfo() );
+        assertTrue( e.skipReferral() );
+        assertEquals( "ldap://hertz:10389/cn=alex%20karasulu,ou=users,dc=example,dc=com", e.getReferralInfo() );
+        assertTrue( e.skipReferral() );
+        assertEquals( "ldap://maxwell:10389", e.getReferralInfo() );
+        assertFalse( e.skipReferral() );
+    }
+
+
+    /**
+     * Checks for correct core behavior when Context.REFERRAL is set to <b>throw</b>
+     * for an add operation with the parent context being a referral.
+     * 
+     * @throws Exception if something goes wrong.
+     */
+    @Test
+    public void testAddWithReferralParent() throws Exception
+    {
+        addReferralEntry();
+
+        // -------------------------------------------------------------------
+        // Attempt to add a normal entry below the referral parent. We should
+        // encounter referral errors with referral setting set to throw.
+        // -------------------------------------------------------------------
+
+        td.refCtx.addToEnvironment( Context.REFERRAL, "throw" );
+        Attributes userEntry = new AttributesImpl( "objectClass", "top", true );
+        userEntry.get( "objectClass" ).add( "person" );
+        userEntry.put( "sn", "karasulu" );
+        userEntry.put( "cn", "alex karasulu" );
+
+        try
+        {
+            td.refCtx.createSubcontext( "cn=alex karasulu", userEntry );
+            fail( "Should fail here throwing a ReferralException" );
+        }
+        catch ( ReferralException e )
+        {
+            checkParentReferrals( e );
+        }
+    }
+
+
+    /**
+     * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
+     * for an add operation with an ancestor context being a referral.
+     * 
+     * @throws Exception if something goes wrong.
+     */
+    @Test
+    public void testAddWithReferralAncestor() throws Exception
+    {
+        addReferralEntry();
+
+        // -------------------------------------------------------------------
+        // Attempt to add a normal entry below the referral ancestor. We should
+        // encounter referral errors with referral setting set to throw.
+        // -------------------------------------------------------------------
+
+        td.refCtx.addToEnvironment( Context.REFERRAL, "throw" );
+        Attributes userEntry = new AttributesImpl( "objectClass", "top", true );
+        userEntry.get( "objectClass" ).add( "person" );
+        userEntry.put( "sn", "karasulu" );
+        userEntry.put( "cn", "alex karasulu" );
+
+        try
+        {
+            td.refCtx.createSubcontext( "cn=alex karasulu,ou=apache", userEntry );
+            fail( "Should fail here throwing a ReferralException" );
+        }
+        catch ( ReferralException e )
+        {
+            checkAncestorReferrals( e );
+        }
+    }
+
+
+    /**
+     * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
+     * for an delete operation with the parent context being a referral.
+     * 
+     * @throws Exception if something goes wrong.
+     */
+    @Test
+    public void testDeleteWithReferralParent() throws Exception
+    {
+        addReferralEntry();
+
+        // -------------------------------------------------------------------
+        // Attempt to delete a non-existent entry below the referral parent. 
+        // We should encounter referral errors with referral setting set to 
+        // throw.
+        // -------------------------------------------------------------------
+
+        td.refCtx.addToEnvironment( Context.REFERRAL, "throw" );
+        try
+        {
+            td.refCtx.destroySubcontext( "cn=alex karasulu" );
+            fail( "Should fail here throwing a ReferralException" );
+        }
+        catch ( ReferralException e )
+        {
+            checkParentReferrals( e );
+        }
+    }
+
+
+    /**
+     * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
+     * for a delete operation with an ancestor context being a referral.
+     * 
+     * @throws Exception if something goes wrong.
+     */
+    @Test
+    public void testDeleteWithReferralAncestor() throws Exception
+    {
+        addReferralEntry();
+
+        // -------------------------------------------------------------------
+        // Attempt to delete a non-existent entry below the referral ancestor. 
+        // We should encounter referral errors when referral setting is set to 
+        // throw.
+        // -------------------------------------------------------------------
+
+        td.refCtx.addToEnvironment( Context.REFERRAL, "throw" );
+        try
+        {
+            td.refCtx.destroySubcontext( "cn=alex karasulu,ou=apache" );
+            fail( "Should fail here throwing a ReferralException" );
+        }
+        catch ( ReferralException e )
+        {
+            checkAncestorReferrals( e );
+        }
+    }
+
+
+    /**
+     * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
+     * for an delete operation with the parent context being a referral.
+     * 
+     * @throws Exception if something goes wrong.
+     */
+    @Test
+    public void testCompareWithReferralParent() throws Exception
+    {
+        addReferralEntry();
+
+        // -------------------------------------------------------------------
+        // Attempt to compare attributes in an entry below the referral parent. 
+        // We should encounter referral errors with referral setting set to 
+        // throw.
+        // -------------------------------------------------------------------
+
+        td.refCtx.addToEnvironment( Context.REFERRAL, "throw" );
+        try
+        {
+            if ( td.refCtx instanceof ServerLdapContext )
+            {
+                LdapDN dn = new LdapDN( "cn=alex karasulu,ou=users,ou=system" );
+                ( ( ServerLdapContext ) td.refCtx ).compare( dn, "sn", "karasulu" );
+            }
+            else
+            {
+                // abort the test because we're using the sun jdni provider
+                return;
+            }
+            fail( "Should fail here throwing a ReferralException" );
+        }
+        catch ( ReferralException e )
+        {
+            checkParentReferrals( e );
+        }
+    }
+
+
+    /**
+     * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
+     * for a compare operation with an ancestor context being a referral.
+     * 
+     * @throws Exception if something goes wrong.
+     */
+    @Test
+    public void testCompareWithReferralAncestor() throws Exception
+    {
+        addReferralEntry();
+
+        // -------------------------------------------------------------------
+        // Attempt to compare attributes in an entry below the referral ancestor. 
+        // We should encounter referral errors when referral setting is set to 
+        // throw.
+        // -------------------------------------------------------------------
+
+        td.refCtx.addToEnvironment( Context.REFERRAL, "throw" );
+        try
+        {
+            if ( td.refCtx instanceof ServerLdapContext )
+            {
+                LdapDN dn = new LdapDN( "cn=alex karasulu,ou=apache,ou=users,ou=system" );
+                ( ( ServerLdapContext ) td.refCtx ).compare( dn, "sn", "karasulu" );
+            }
+            else
+            {
+                // abort the test because we're using the sun jdni provider
+                return;
+            }
+            fail( "Should fail here throwing a ReferralException" );
+        }
+        catch ( ReferralException e )
+        {
+            checkAncestorReferrals( e );
+        }
+    }
+
+
+    /**
+     * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
+     * for a modify operation with the parent context being a referral.
+     * 
+     * @throws Exception if something goes wrong.
+     */
+    @Test
+    public void testModifyWithReferralParent() throws Exception
+    {
+        addReferralEntry();
+
+        // -------------------------------------------------------------------
+        // Attempt to modify the attributes of an entry below the referral 
+        // parent.  We should encounter referral errors with referral setting 
+        // set to throw.
+        // -------------------------------------------------------------------
+
+        td.refCtx.addToEnvironment( Context.REFERRAL, "throw" );
+        try
+        {
+            td.refCtx.modifyAttributes( "cn=alex karasulu", DirContext.ADD_ATTRIBUTE, new AttributesImpl(
+                "description", "just some text", true ) );
+            fail( "Should fail here throwing a ReferralException" );
+        }
+        catch ( ReferralException e )
+        {
+            checkParentReferrals( e );
+        }
+    }
+
+
+    /**
+     * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
+     * for a modify operation with an ancestor context being a referral.
+     * 
+     * @throws Exception if something goes wrong.
+     */
+    @Test
+    public void testModifyWithReferralAncestor() throws Exception
+    {
+        addReferralEntry();
+
+        // -------------------------------------------------------------------
+        // Attempt to modify the attributes of an entry below the referral 
+        // ancestor. We should encounter referral errors when referral setting 
+        // is set to throw.
+        // -------------------------------------------------------------------
+
+        td.refCtx.addToEnvironment( Context.REFERRAL, "throw" );
+        try
+        {
+            td.refCtx.modifyAttributes( "cn=alex karasulu,ou=apache", DirContext.ADD_ATTRIBUTE, new AttributesImpl(
+                "description", "just some text", true ) );
+            fail( "Should fail here throwing a ReferralException" );
+        }
+        catch ( ReferralException e )
+        {
+            checkAncestorReferrals( e );
+        }
+    }
+
+
+    /**
+     * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
+     * for a modify operation with the parent context being a referral.
+     * 
+     * @throws Exception if something goes wrong.
+     */
+    @Test
+    public void testModifyWithReferralParent2() throws Exception
+    {
+        addReferralEntry();
+
+        // -------------------------------------------------------------------
+        // Attempt to modify the attributes of an entry below the referral 
+        // parent.  We should encounter referral errors with referral setting 
+        // set to throw.
+        // -------------------------------------------------------------------
+
+        td.refCtx.addToEnvironment( Context.REFERRAL, "throw" );
+        try
+        {
+            ModificationItemImpl[] mods = new ModificationItemImpl[]
+                { new ModificationItemImpl( DirContext.ADD_ATTRIBUTE, new AttributeImpl( "description", "just some text" ) ) };
+            td.refCtx.modifyAttributes( "cn=alex karasulu", mods );
+            fail( "Should fail here throwing a ReferralException" );
+        }
+        catch ( ReferralException e )
+        {
+            checkParentReferrals( e );
+        }
+    }
+
+
+    /**
+     * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
+     * for a modify operation with an ancestor context being a referral.
+     * 
+     * @throws Exception if something goes wrong.
+     */
+    @Test
+    public void testModifyWithReferralAncestor2() throws Exception
+    {
+        addReferralEntry();
+
+        // -------------------------------------------------------------------
+        // Attempt to modify the attributes of an entry below the referral 
+        // ancestor. We should encounter referral errors when referral setting 
+        // is set to throw.
+        // -------------------------------------------------------------------
+
+        td.refCtx.addToEnvironment( Context.REFERRAL, "throw" );
+        try
+        {
+            ModificationItemImpl[] mods = new ModificationItemImpl[]
+                { new ModificationItemImpl( DirContext.ADD_ATTRIBUTE, new AttributeImpl( "description", "just some text" ) ) };
+            td.refCtx.modifyAttributes( "cn=alex karasulu,ou=apache", mods );
+            fail( "Should fail here throwing a ReferralException" );
+        }
+        catch ( ReferralException e )
+        {
+            checkAncestorReferrals( e );
+        }
+    }
+
+
+    /**
+     * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
+     * for a modify rdn interceptor operation (corresponds to a subset of the modify 
+     * dn operation) with the parent context being a referral.
+     * 
+     * @throws Exception if something goes wrong.
+     */
+    @Test
+    public void testModifyRdnWithReferralParent() throws Exception
+    {
+        addReferralEntry();
+
+        // -------------------------------------------------------------------
+        // Attempt to modify the last component of the entry's name which 
+        // resides below an parent which is a referral. We should encounter 
+        // referral errors when referral setting is set to throw.
+        // -------------------------------------------------------------------
+
+        td.refCtx.addToEnvironment( Context.REFERRAL, "throw" );
+        try
+        {
+            td.refCtx.rename( "cn=alex karasulu", "cn=aok" );
+            fail( "Should fail here throwing a ReferralException" );
+        }
+        catch ( ReferralException e )
+        {
+            checkParentReferrals( e );
+        }
+    }
+
+
+    /**
+     * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
+     * for a modify rdn interceptor operation (corresponds to a subset of the modify 
+     * dn operation) with an ancestor context being a referral.
+     * 
+     * @throws Exception if something goes wrong.
+     */
+    @Test
+    public void testModifyRdnWithReferralAncestor() throws Exception
+    {
+        addReferralEntry();
+
+        // -------------------------------------------------------------------
+        // Attempt to modify the last component of the entry's name which 
+        // resides below an ancestor which is a referral. We should encounter 
+        // referral errors when referral setting is set to throw.
+        // -------------------------------------------------------------------
+
+        td.refCtx.addToEnvironment( Context.REFERRAL, "throw" );
+        try
+        {
+            td.refCtx.rename( "cn=alex karasulu,ou=apache", "cn=aok,ou=apache" );
+            fail( "Should fail here throwing a ReferralException" );
+        }
+        catch ( ReferralException e )
+        {
+            checkAncestorReferrals( e );
+        }
+    }
+
+
+    /**
+     * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
+     * for a move interceptor operation (corresponds to a subset of the modify 
+     * dn operation) with the parent context being a referral.
+     * 
+     * @throws Exception if something goes wrong.
+     */
+    @Test
+    public void testMoveWithReferralParent() throws Exception
+    {
+        addReferralEntry();
+
+        // -------------------------------------------------------------------
+        // Attempt to modify the last component of the entry's name which 
+        // resides below an parent which is a referral. We should encounter 
+        // referral errors when referral setting is set to throw.
+        // -------------------------------------------------------------------
+
+        td.refCtx.addToEnvironment( Context.REFERRAL, "throw" );
+        try
+        {
+            td.refCtx.rename( "cn=alex karasulu", "cn=alex karasulu,ou=groups" );
+            fail( "Should fail here throwing a ReferralException" );
+        }
+        catch ( ReferralException e )
+        {
+            checkParentReferrals( e );
+        }
+    }
+
+
+    /**
+     * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
+     * for a move interceptor operation (corresponds to a subset of the modify 
+     * dn operation) with an ancestor context being a referral.
+     * 
+     * @throws Exception if something goes wrong.
+     */
+    @Test
+    public void testMoveWithReferralAncestor() throws Exception
+    {
+        addReferralEntry();
+
+        // -------------------------------------------------------------------
+        // Attempt to modify the last component of the entry's name which 
+        // resides below an ancestor which is a referral. We should encounter 
+        // referral errors when referral setting is set to throw.
+        // -------------------------------------------------------------------
+
+        td.refCtx.addToEnvironment( Context.REFERRAL, "throw" );
+        try
+        {
+            td.refCtx.rename( "cn=alex karasulu,ou=apache", "cn=alex karasulu,ou=groups" );
+            fail( "Should fail here throwing a ReferralException" );
+        }
+        catch ( ReferralException e )
+        {
+            checkAncestorReferrals( e );
+        }
+    }
+
+
+    /**
+     * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
+     * for a move interceptor operation (corresponds to a subset of the modify 
+     * dn operation) with the parent context being a referral.
+     * 
+     * @throws Exception if something goes wrong.
+     */
+    @Test
+    public void testMoveWithReferralParent2() throws Exception
+    {
+        addReferralEntry();
+
+        // -------------------------------------------------------------------
+        // Attempt to modify the last component of the entry's name which 
+        // resides below an parent which is a referral. We should encounter 
+        // referral errors when referral setting is set to throw.
+        // -------------------------------------------------------------------
+
+        td.refCtx.addToEnvironment( Context.REFERRAL, "throw" );
+        try
+        {
+            td.refCtx.rename( "cn=alex karasulu", "cn=aok,ou=groups" );
+            fail( "Should fail here throwing a ReferralException" );
+        }
+        catch ( ReferralException e )
+        {
+            checkParentReferrals( e );
+        }
+    }
+
+
+    /**
+     * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
+     * for a move interceptor operation (corresponds to a subset of the modify 
+     * dn operation) with an ancestor context being a referral.
+     * 
+     * @throws Exception if something goes wrong.
+     */
+    @Test
+    public void testMoveWithReferralAncestor2() throws Exception
+    {
+        addReferralEntry();
+
+        // -------------------------------------------------------------------
+        // Attempt to modify the last component of the entry's name which 
+        // resides below an ancestor which is a referral. We should encounter 
+        // referral errors when referral setting is set to throw.
+        // -------------------------------------------------------------------
+
+        td.refCtx.addToEnvironment( Context.REFERRAL, "throw" );
+        try
+        {
+            td.refCtx.rename( "cn=alex karasulu,ou=apache", "cn=aok,ou=groups" );
+            fail( "Should fail here throwing a ReferralException" );
+        }
+        catch ( ReferralException e )
+        {
+            checkAncestorReferrals( e );
+        }
+    }
+
+
+    /**
+     * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
+     * for a move interceptor operation (corresponds to a subset of the modify 
+     * dn operation) with the parent context being a referral.
+     * 
+     * @throws Exception if something goes wrong.
+     */
+    @Test
+    public void testMoveWithReferralParentDest() throws Exception
+    {
+        addReferralEntry();
+
+        // -------------------------------------------------------------------
+        // Attempt to modify the last component of the entry's name which 
+        // resides below an parent which is a referral. We should encounter 
+        // referral errors when referral setting is set to throw.
+        // -------------------------------------------------------------------
+
+        createLocalUser();
+        td.rootCtx.addToEnvironment( Context.REFERRAL, "throw" );
+        try
+        {
+            td.rootCtx.rename( "cn=akarasulu", "cn=akarasulu,ou=users" );
+            fail( "Should fail here throwing a LdapNamingException with ResultCodeEnum = AFFECTSMULTIPLEDSAS" );
+        }
+        catch ( LdapNamingException e )
+        {
+            assertTrue( e.getResultCode() == ResultCodeEnum.AFFECTS_MULTIPLE_DSAS );
+        }
+    }
+
+
+    /**
+     * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
+     * for a move interceptor operation (corresponds to a subset of the modify 
+     * dn operation) with an ancestor context being a referral.
+     * 
+     * @throws Exception if something goes wrong.
+     */
+    @Test
+    public void testMoveWithReferralAncestorDest() throws Exception
+    {
+        addReferralEntry();
+
+        // -------------------------------------------------------------------
+        // Attempt to modify the last component of the entry's name which 
+        // resides below an ancestor which is a referral. We should encounter 
+        // referral errors when referral setting is set to throw.
+        // -------------------------------------------------------------------
+
+        createDeepLocalUser();
+        td.rootCtx.addToEnvironment( Context.REFERRAL, "throw" );
+        try
+        {
+            td.rootCtx.rename( "cn=akarasulu,ou=deep", "cn=akarasulu,ou=users" );
+            fail( "Should fail here throwing a LdapNamingException with ResultCodeEnum = AFFECTSMULTIPLEDSAS" );
+        }
+        catch ( LdapNamingException e )
+        {
+            assertTrue( e.getResultCode() == ResultCodeEnum.AFFECTS_MULTIPLE_DSAS );
+        }
+    }
+
+
+    /**
+     * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
+     * for a move interceptor operation (corresponds to a subset of the modify 
+     * dn operation) with the parent context being a referral.
+     * 
+     * @throws Exception if something goes wrong.
+     */
+    @Test
+    public void testMoveWithReferralParent2Dest() throws Exception
+    {
+        addReferralEntry();
+
+        // -------------------------------------------------------------------
+        // Attempt to modify the last component of the entry's name which 
+        // resides below an parent which is a referral. We should encounter 
+        // referral errors when referral setting is set to throw.
+        // -------------------------------------------------------------------
+
+        createLocalUser();
+        td.rootCtx.addToEnvironment( Context.REFERRAL, "throw" );
+        try
+        {
+            td.rootCtx.rename( "cn=akarasulu", "cn=aok,ou=users" );
+            fail( "Should fail here throwing a LdapNamingException with ResultCodeEnum = AFFECTSMULTIPLEDSAS" );
+        }
+        catch ( LdapNamingException e )
+        {
+            assertTrue( e.getResultCode() == ResultCodeEnum.AFFECTS_MULTIPLE_DSAS );
+        }
+    }
+
+
+    /**
+     * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
+     * for a move interceptor operation (corresponds to a subset of the modify 
+     * dn operation) with an ancestor context being a referral.
+     * 
+     * @throws Exception if something goes wrong.
+     */
+    @Test
+    public void testMoveWithReferralAncestor2Dest() throws Exception
+    {
+        addReferralEntry();
+
+        // -------------------------------------------------------------------
+        // Attempt to modify the last component of the entry's name which 
+        // resides below an ancestor which is a referral. We should encounter 
+        // referral errors when referral setting is set to throw.
+        // -------------------------------------------------------------------
+
+        createDeepLocalUser();
+        td.rootCtx.addToEnvironment( Context.REFERRAL, "throw" );
+        try
+        {
+            td.rootCtx.rename( "cn=akarasulu,ou=deep", "cn=aok,ou=users" );
+            fail( "Should fail here throwing a LdapNamingException with ResultCodeEnum = AFFECTSMULTIPLEDSAS" );
+        }
+        catch ( LdapNamingException e )
+        {
+            assertTrue( e.getResultCode() == ResultCodeEnum.AFFECTS_MULTIPLE_DSAS );
+        }
+    }
+
+
+    private void createLocalUser() throws Exception
+    {
+        addReferralEntry();
+
+        LdapContext userCtx;
+        Attributes referral = new AttributesImpl( "objectClass", "top", true );
+        referral.get( "objectClass" ).add( "person" );
+        referral.put( "cn", "akarasulu" );
+        referral.put( "sn", "karasulu" );
+
+        //noinspection EmptyCatchBlock
+        try
+        {
+            td.rootCtx.destroySubcontext( "uid=akarasulu" );
+        }
+        catch ( NameNotFoundException e )
+        {
+        }
+        
+        try
+        {
+            userCtx = ( LdapContext ) td.rootCtx.createSubcontext( "cn=akarasulu", referral );
+        }
+        catch ( NameAlreadyBoundException e )
+        {
+            userCtx = ( LdapContext ) td.rootCtx.lookup( "cn=akarasulu" );
+        }
+        
+        referral = userCtx.getAttributes( "" );
+        assertTrue( referral.get( "cn" ).contains( "akarasulu" ) );
+        assertTrue( referral.get( "sn" ).contains( "karasulu" ) );
+    }
+
+
+    private void createDeepLocalUser() throws Exception
+    {
+        addReferralEntry();
+
+        LdapContext userCtx = null;
+        Attributes referral = new AttributesImpl( "objectClass", "top", true );
+        referral.get( "objectClass" ).add( "person" );
+        referral.get( "objectClass" ).add( SchemaConstants.EXTENSIBLE_OBJECT_OC );
+        referral.put( "cn", "akarasulu" );
+        referral.put( "sn", "karasulu" );
+        referral.put( "ou", "deep" );
+
+        //noinspection EmptyCatchBlock
+        try
+        {
+            td.rootCtx.destroySubcontext( "uid=akarasulu,ou=deep" );
+        }
+        catch ( NameNotFoundException e )
+        {
+        }
+        //noinspection EmptyCatchBlock
+        try
+        {
+            td.rootCtx.destroySubcontext( "ou=deep" );
+        }
+        catch ( NameNotFoundException e )
+        {
+        }
+        try
+        {
+            Attributes attrs = new AttributesImpl( "ou", "deep" );
+            Attribute oc = new AttributeImpl( "ObjectClass" );
+            oc.add( "top" );
+            oc.add( "organizationalUnit" );
+            attrs.put( oc );
+
+            td.rootCtx.createSubcontext( "ou=deep", attrs );
+            userCtx = ( LdapContext ) td.rootCtx.createSubcontext( "cn=akarasulu,ou=deep", referral );
+        }
+        catch ( NameAlreadyBoundException e )
+        {
+            td.refCtx = ( LdapContext ) td.rootCtx.lookup( "cn=akarasulu,ou=deep" );
+        }
+        referral = userCtx.getAttributes( "" );
+        assertTrue( referral.get( "cn" ).contains( "akarasulu" ) );
+        assertTrue( referral.get( "sn" ).contains( "karasulu" ) );
+    }
+
+
+    @Test
+    public void testSearchBaseIsReferral() throws Exception
+    {
+        addReferralEntry();
+
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        td.rootCtx.addToEnvironment( Context.REFERRAL, "throw" );
+        try
+        {
+            td.rootCtx.search( "ou=users", "(objectClass=*)", controls );
+            fail( "should never get here" );
+        }
+        catch ( ReferralException e )
+        {
+            assertEquals( "ldap://fermi:10389/ou=users,ou=system??sub", e.getReferralInfo() );
+            assertTrue( e.skipReferral() );
+            assertEquals( "ldap://hertz:10389/ou=users,dc=example,dc=com??sub", e.getReferralInfo() );
+            assertTrue( e.skipReferral() );
+            assertEquals( "ldap://maxwell:10389/ou=users,ou=system??sub", e.getReferralInfo() );
+            assertFalse( e.skipReferral() );
+        }
+    }
+
+
+    @Test
+    public void testSearchBaseParentIsReferral() throws Exception
+    {
+        addReferralEntry();
+
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.OBJECT_SCOPE );
+        td.refCtx.addToEnvironment( Context.REFERRAL, "throw" );
+        try
+        {
+            td.refCtx.search( "cn=alex karasulu", "(objectClass=*)", controls );
+        }
+        catch ( ReferralException e )
+        {
+            assertEquals( "ldap://fermi:10389/cn=alex%20karasulu,ou=users,ou=system??base", e.getReferralInfo() );
+            assertTrue( e.skipReferral() );
+            assertEquals( "ldap://hertz:10389/cn=alex%20karasulu,ou=users,dc=example,dc=com??base", e.getReferralInfo() );
+            assertTrue( e.skipReferral() );
+            assertEquals( "ldap://maxwell:10389/cn=alex%20karasulu,ou=users,ou=system??base", e.getReferralInfo() );
+            assertFalse( e.skipReferral() );
+        }
+    }
+
+
+    @Test
+    public void testSearchBaseAncestorIsReferral() throws Exception
+    {
+        addReferralEntry();
+
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.OBJECT_SCOPE );
+        td.refCtx.addToEnvironment( Context.REFERRAL, "throw" );
+        try
+        {
+            td.refCtx.search( "cn=alex karasulu,ou=apache", "(objectClass=*)", controls );
+        }
+        catch ( ReferralException e )
+        {
+            assertEquals( "ldap://fermi:10389/cn=alex%20karasulu,ou=apache,ou=users,ou=system??base", e.getReferralInfo() );
+            assertTrue( e.skipReferral() );
+            assertEquals( "ldap://hertz:10389/cn=alex%20karasulu,ou=apache,ou=users,dc=example,dc=com??base", e
+                .getReferralInfo() );
+            assertTrue( e.skipReferral() );
+            assertEquals( "ldap://maxwell:10389/cn=alex%20karasulu,ou=apache,ou=users,ou=system??base", e
+                .getReferralInfo() );
+            assertFalse( e.skipReferral() );
+        }
+    }
+
+
+    @Test
+    public void testSearchContinuations() throws Exception
+    {
+        addReferralEntry();
+
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        NamingEnumeration<SearchResult> list = td.rootCtx.search( "", "(objectClass=*)", controls );
+        Map<String, SearchResult> results = new HashMap<String, SearchResult>();
+        
+        while ( list.hasMore() )
+        {
+            SearchResult result = list.next();
+            results.put( result.getName(), result );
+        }
+
+        assertNotNull( results.get( "ou=users,ou=system" ) );
+
+        // -------------------------------------------------------------------
+        // Now we will throw exceptions when searching for referrals 
+        // -------------------------------------------------------------------
+
+        td.rootCtx.addToEnvironment( Context.REFERRAL, "throw" );
+        list = td.rootCtx.search( "", "(objectClass=*)", controls );
+        results = new HashMap<String, SearchResult>();
+
+        try
+        {
+            while ( list.hasMore() )
+            {
+                SearchResult result = ( SearchResult ) list.next();
+                results.put( result.getName(), result );
+            }
+        }
+        catch ( ReferralException e )
+        {
+            assertEquals( "ldap://fermi:10389/ou=users,ou=system??sub", e.getReferralInfo() );
+            assertTrue( e.skipReferral() );
+            assertEquals( "ldap://hertz:10389/ou=users,dc=example,dc=com??sub", e.getReferralInfo() );
+            assertTrue( e.skipReferral() );
+            assertEquals( "ldap://maxwell:10389/ou=users,ou=system??sub", e.getReferralInfo() );
+            assertFalse( e.skipReferral() );
+        }
+
+        assertNull( results.get( "ou=users" ) );
+
+        // try again but this time with single level scope
+
+        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+        list = td.rootCtx.search( "", "(objectClass=*)", controls );
+        results = new HashMap<String, SearchResult>();
+
+        try
+        {
+            while ( list.hasMore() )
+            {
+                SearchResult result = ( SearchResult ) list.next();
+                results.put( result.getName(), result );
+            }
+        }
+        catch ( ReferralException e )
+        {
+            assertEquals( "ldap://fermi:10389/ou=users,ou=system??base", e.getReferralInfo() );
+            assertTrue( e.skipReferral() );
+            assertEquals( "ldap://hertz:10389/ou=users,dc=example,dc=com??base", e.getReferralInfo() );
+            assertTrue( e.skipReferral() );
+            assertEquals( "ldap://maxwell:10389/ou=users,ou=system??base", e.getReferralInfo() );
+            assertFalse( e.skipReferral() );
+        }
+
+        assertNull( results.get( "ou=users" ) );
+    }
+
+
+    /**
+     * Checks that when injecting LDAP ref with an empty DN we get an exception
+     *
+     * @throws Exception on error
+     */
+    @Test
+    public void testAddWReferralWithEmptyDN() throws Exception
+    {
+        addReferralEntry();
+
+        Attributes attrs = new AttributesImpl( true );
+
+        Attribute oc = new AttributeImpl( "ObjectClass", "top" );
+        oc.add( "extensibleObject" );
+        oc.add( "referral" );
+        attrs.put( oc );
+        
+        Attribute ref = new AttributeImpl( "ref", "ldap://" );
+        attrs.put( ref );
+
+        attrs.put( "cn", "refWithEmptyDN" );
+
+        String base = "cn=refWithEmptyDN";
+
+        //create subcontext
+        try
+        {
+            getSystemContext( service ).createSubcontext( base, attrs );
+            fail( "Should not reach this state" );
+        }
+        catch ( NamingException ne )
+        {
+            assertTrue( true );
+        }
+    }
+
+
+    /**
+     * Checks that when injecting LDAP ref with attribuutes we get an exception
+     *
+     * @throws Exception on error
+     */
+    @Test
+    public void testAddWReferralWithAttrs() throws Exception
+    {
+        addReferralEntry();
+
+        Attributes attrs = new AttributesImpl( true );
+
+        Attribute oc = new AttributeImpl( "ObjectClass", "top" );
+        oc.add( "extensibleObject" );
+        oc.add( "referral" );
+        attrs.put( oc );
+        
+        Attribute ref = new AttributeImpl( "ref", "ldap://localhost/cn=RefWithAttributes?cn" );
+        attrs.put( ref );
+
+        attrs.put( "cn", "RefWithAttributes" );
+
+        String base = "cn=RefWithAttributes";
+
+        //create subcontext
+        try
+        {
+            getSystemContext( service ).createSubcontext( base, attrs );
+            fail( "Should not reach this state" );
+        }
+        catch ( NamingException ne )
+        {
+            assertTrue( true );
+        }
+    }
+
+
+    /**
+     * Checks that when injecting LDAP ref with a scope we get an exception
+     *
+     * @throws Exception on error
+     */
+    @Test
+    public void testAddWReferralWithScope() throws Exception
+    {
+        addReferralEntry();
+
+        Attributes attrs = new AttributesImpl( true );
+        Attribute oc = new AttributeImpl( "ObjectClass", "top" );
+        oc.add( "extensibleObject" );
+        oc.add( "referral" );
+        attrs.put( oc );
+        
+        Attribute ref = new AttributeImpl( "ref", "ldap://localhost/cn=RefWithScope??sub" );
+        attrs.put( ref );
+
+        attrs.put( "cn", "RefWithScope" );
+
+        String base = "cn=RefWithScope";
+
+        //create subcontext
+        try
+        {
+            getSystemContext( service ).createSubcontext( base, attrs );
+            fail( "Should not reach this state" );
+        }
+        catch ( NamingException ne )
+        {
+            assertTrue( true );
+        }
+    }
+
+
+    /**
+     * Checks that when injecting LDAP ref with a filter we get an exception
+     *
+     * @throws Exception on error
+     */
+    @Test
+    public void testAddWReferralWithFilters() throws Exception
+    {
+        addReferralEntry();
+
+        Attributes attrs = new AttributesImpl( true );
+        Attribute oc = new AttributeImpl( "ObjectClass", "top" );
+        oc.add( "extensibleObject" );
+        oc.add( "referral" );
+        attrs.put( oc );
+        
+        Attribute ref = new AttributeImpl( "ref", "ldap://localhost/cn=RefWithFilter???(cn=*)" );
+        attrs.put( ref );
+
+        attrs.put( "cn", "RefWithFilter" );
+
+        String base = "cn=RefWithFilter";
+
+        //create subcontext
+        try
+        {
+            getSystemContext( service ).createSubcontext( base, attrs );
+            fail( "Should not reach this state" );
+        }
+        catch ( NamingException ne )
+        {
+            assertTrue( true );
+        }
+    }
+
+
+    /**
+     * Checks that when injecting LDAP ref with an extension we get an exception
+     *
+     * @throws Exception on error
+     */
+    @Test
+    public void testAddWReferralWithExtension() throws Exception
+    {
+        addReferralEntry();
+
+        Attributes attrs = new AttributesImpl( true );
+        Attribute oc = new AttributeImpl( "ObjectClass", "top" );
+        oc.add( "extensibleObject" );
+        oc.add( "referral" );
+        attrs.put( oc );
+        
+        Attribute ref = new AttributeImpl( "ref", "ldap://localhost/cn=RefWithExtension????x-extension=1.2.3.4" );
+        attrs.put( ref );
+
+        attrs.put( "cn", "RefWithExtension" );
+
+        String base = "cn=RefWithExtension";
+
+        //create subcontext
+        try
+        {
+            getSystemContext( service ).createSubcontext( base, attrs );
+            fail( "Should not reach this state" );
+        }
+        catch ( NamingException ne )
+        {
+            assertTrue( true );
+        }
+    }
+
+
+    /**
+     * Checks that when injecting LDAP ref with a critical extension we get an exception
+     *
+     * @throws Exception on error
+     */
+    @Test
+    public void testAddWReferralWithCriticalExtension() throws Exception
+    {
+        addReferralEntry();
+
+        Attributes attrs = new AttributesImpl( true );
+        Attribute oc = new AttributeImpl( "ObjectClass", "top" );
+        oc.add( "extensibleObject" );
+        oc.add( "referral" );
+        attrs.put( oc );
+        
+        Attribute ref = new AttributeImpl( "ref", "ldap://localhost/cn=RefWithCriticalExtension????!x-extension=1.2.3.4" );
+        attrs.put( ref );
+
+        attrs.put( "cn", "RefWithCriticalExtension" );
+
+        String base = "cn=RefWithCriticalExtension";
+
+        //create subcontext
+        try
+        {
+            getSystemContext( service ).createSubcontext( base, attrs );
+            fail( "Should not reach this state" );
+        }
+        catch ( NamingException ne )
+        {
+            assertTrue( true );
+        }
+    }
+}
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/jndi/RootDSEIT.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/jndi/RootDSEIT.java
new file mode 100644
index 0000000..8e67dfb
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/jndi/RootDSEIT.java
@@ -0,0 +1,303 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.jndi;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.integ.CiRunner;
+import org.apache.directory.shared.ldap.exception.LdapNoPermissionException;
+import org.apache.directory.shared.ldap.message.ModificationItemImpl;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import java.util.Hashtable;
+
+
+/**
+ * Testing RootDSE lookups and context creation using the empty string.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+@RunWith ( CiRunner.class )
+public class RootDSEIT
+{
+    public static DirectoryService service;
+
+
+    /**
+     * Creates an initial context using the empty string for the provider URL.
+     * This should work.
+     *
+     * @throws NamingException if there are any problems
+     */
+    @Test
+    public void testGetInitialContext() throws NamingException
+    {
+        Hashtable<String,Object> env = new Hashtable<String,Object>();
+        env.put( DirectoryService.JNDI_KEY, service );
+        env.put( Context.PROVIDER_URL, "" );
+        env.put( Context.SECURITY_PRINCIPAL, "uid=admin,ou=system" );
+        env.put( Context.SECURITY_CREDENTIALS, "secret" );
+        env.put( Context.SECURITY_AUTHENTICATION, "simple" );
+        env.put( Context.INITIAL_CONTEXT_FACTORY, CoreContextFactory.class.getName() );
+
+        InitialContext initCtx = new InitialContext( env );
+        assertNotNull( initCtx );
+    }
+
+
+    /**
+     * Gets a DirContext from the InitialContext for the empty string or RootDSE
+     * and checks that none of the operational attributes are returned.
+     *
+     * @throws NamingException if there are any problems
+     */
+    @Test
+    public void testGetInitialContextLookupAttributes() throws NamingException
+    {
+        Hashtable<String,Object> env = new Hashtable<String,Object>();
+        env.put( DirectoryService.JNDI_KEY, service );
+        env.put( Context.PROVIDER_URL, "" );
+        env.put( Context.SECURITY_PRINCIPAL, "uid=admin,ou=system" );
+        env.put( Context.SECURITY_CREDENTIALS, "secret" );
+        env.put( Context.SECURITY_AUTHENTICATION, "simple" );
+        env.put( Context.INITIAL_CONTEXT_FACTORY, CoreContextFactory.class.getName() );
+
+        InitialContext initCtx = new InitialContext( env );
+        assertNotNull( initCtx );
+
+        DirContext ctx = ( DirContext ) initCtx.lookup( "" );
+        Attributes attributes = ctx.getAttributes( "" );
+
+        // Added some objectClass attributes to the rootDSE
+        assertEquals( 1, attributes.size() );
+    }
+
+
+    /**
+     * Checks for namingContexts and vendorName attributes.
+     *
+     * @throws NamingException if there are any problems
+     */
+    @Test
+    public void testGetInitialContextLookupAttributesByName() throws NamingException
+    {
+        Hashtable<String,Object> env = new Hashtable<String,Object>();
+        env.put( DirectoryService.JNDI_KEY, service );
+        env.put( Context.PROVIDER_URL, "" );
+        env.put( Context.SECURITY_PRINCIPAL, "uid=admin,ou=system" );
+        env.put( Context.SECURITY_CREDENTIALS, "secret" );
+        env.put( Context.SECURITY_AUTHENTICATION, "simple" );
+        env.put( Context.INITIAL_CONTEXT_FACTORY, CoreContextFactory.class.getName() );
+
+        InitialContext initCtx = new InitialContext( env );
+        assertNotNull( initCtx );
+        DirContext ctx = ( DirContext ) initCtx.lookup( "" );
+
+        Attributes attributes = ctx.getAttributes( "", new String[]
+            { "namingContexts", "VENDORNAME" } );
+        assertEquals( 2, attributes.size() );
+        assertEquals( "Apache Software Foundation", attributes.get( "vendorName" ).get() );
+        assertTrue( attributes.get( "namingContexts" ).contains( "ou=system" ) );
+    }
+
+
+    /**
+     * Checks for ObjectClass, namingContexts and vendorName attributes.
+     *
+     * @throws NamingException if there are any problems
+     */
+    @Test
+    public void testGetInitialContextLookupAttributesByNameWithOC() throws NamingException
+    {
+        Hashtable<String,Object> env = new Hashtable<String,Object>();
+        env.put( DirectoryService.JNDI_KEY, service );
+        env.put( Context.PROVIDER_URL, "" );
+        env.put( Context.SECURITY_PRINCIPAL, "uid=admin,ou=system" );
+        env.put( Context.SECURITY_CREDENTIALS, "secret" );
+        env.put( Context.SECURITY_AUTHENTICATION, "simple" );
+        env.put( Context.INITIAL_CONTEXT_FACTORY, CoreContextFactory.class.getName() );
+
+        InitialContext initCtx = new InitialContext( env );
+        assertNotNull( initCtx );
+        DirContext ctx = ( DirContext ) initCtx.lookup( "" );
+
+        Attributes attributes = ctx.getAttributes( "", new String[]
+            { "ObjectClass", "namingContexts", "VENDORNAME" } );
+        assertEquals( 3, attributes.size() );
+        assertEquals( "Apache Software Foundation", attributes.get( "vendorName" ).get() );
+        assertTrue( attributes.get( "namingContexts" ).contains( "ou=system" ) );
+    }
+
+
+    /**
+     * Checks for lack of permissions to delete this entry.
+     *
+     * @throws NamingException if there are any problems
+     */
+    @Test
+    public void testDelete() throws NamingException
+    {
+        Hashtable<String,Object> env = new Hashtable<String,Object>();
+        env.put( DirectoryService.JNDI_KEY, service );
+        env.put( Context.PROVIDER_URL, "" );
+        env.put( Context.SECURITY_PRINCIPAL, "uid=admin,ou=system" );
+        env.put( Context.SECURITY_CREDENTIALS, "secret" );
+        env.put( Context.SECURITY_AUTHENTICATION, "simple" );
+        env.put( Context.INITIAL_CONTEXT_FACTORY, CoreContextFactory.class.getName() );
+
+        InitialContext initCtx = new InitialContext( env );
+        assertNotNull( initCtx );
+        DirContext ctx = ( DirContext ) initCtx.lookup( "" );
+        LdapNoPermissionException notNull = null;
+
+        try
+        {
+            ctx.destroySubcontext( "" );
+            fail( "we should never get here" );
+        }
+        catch ( LdapNoPermissionException e )
+        {
+            notNull = e;
+        }
+
+        assertNotNull( notNull );
+    }
+
+
+    /**
+     * Checks for lack of permissions to rename or move this entry.
+     *
+     * @throws NamingException if there are any problems
+     */
+    @Test
+    public void testRename() throws NamingException
+    {
+        Hashtable<String,Object> env = new Hashtable<String,Object>();
+        env.put( DirectoryService.JNDI_KEY, service );
+        env.put( Context.PROVIDER_URL, "" );
+        env.put( Context.SECURITY_PRINCIPAL, "uid=admin,ou=system" );
+        env.put( Context.SECURITY_CREDENTIALS, "secret" );
+        env.put( Context.SECURITY_AUTHENTICATION, "simple" );
+        env.put( Context.INITIAL_CONTEXT_FACTORY, CoreContextFactory.class.getName() );
+
+        InitialContext initCtx = new InitialContext( env );
+        assertNotNull( initCtx );
+        DirContext ctx = ( DirContext ) initCtx.lookup( "" );
+        LdapNoPermissionException notNull = null;
+
+        try
+        {
+            ctx.rename( "", "ou=system" );
+            fail( "we should never get here" );
+        }
+        catch ( LdapNoPermissionException e )
+        {
+            notNull = e;
+        }
+
+        assertNotNull( notNull );
+    }
+
+
+    /**
+     * Checks for lack of permissions to modify this entry.
+     *
+     * @throws NamingException if there are any problems
+     */
+    @Test
+    public void testModify() throws NamingException
+    {
+        Hashtable<String,Object> env = new Hashtable<String,Object>();
+        env.put( DirectoryService.JNDI_KEY, service );
+        env.put( Context.PROVIDER_URL, "" );
+        env.put( Context.SECURITY_PRINCIPAL, "uid=admin,ou=system" );
+        env.put( Context.SECURITY_CREDENTIALS, "secret" );
+        env.put( Context.SECURITY_AUTHENTICATION, "simple" );
+        env.put( Context.INITIAL_CONTEXT_FACTORY, CoreContextFactory.class.getName() );
+
+        InitialContext initCtx = new InitialContext( env );
+        assertNotNull( initCtx );
+        DirContext ctx = ( DirContext ) initCtx.lookup( "" );
+        LdapNoPermissionException notNull = null;
+
+        try
+        {
+            ctx.modifyAttributes( "", DirContext.ADD_ATTRIBUTE, null );
+            fail( "we should never get here" );
+        }
+        catch ( LdapNoPermissionException e )
+        {
+            notNull = e;
+        }
+
+        assertNotNull( notNull );
+    }
+
+
+    /**
+     * Checks for lack of permissions to modify this entry.
+     *
+     * @throws NamingException if there are any problems
+     */
+    @Test
+    public void testModify2() throws NamingException
+    {
+        Hashtable<String,Object> env = new Hashtable<String,Object>();
+        env.put( DirectoryService.JNDI_KEY, service );
+        env.put( Context.PROVIDER_URL, "" );
+        env.put( Context.SECURITY_PRINCIPAL, "uid=admin,ou=system" );
+        env.put( Context.SECURITY_CREDENTIALS, "secret" );
+        env.put( Context.SECURITY_AUTHENTICATION, "simple" );
+        env.put( Context.INITIAL_CONTEXT_FACTORY, CoreContextFactory.class.getName() );
+
+        InitialContext initCtx = new InitialContext( env );
+
+        assertNotNull( initCtx );
+
+        DirContext ctx = ( DirContext ) initCtx.lookup( "" );
+
+        LdapNoPermissionException notNull = null;
+
+        try
+        {
+            ctx.modifyAttributes( "", new ModificationItemImpl[]
+                {} );
+
+            fail( "we should never get here" );
+        }
+        catch ( LdapNoPermissionException e )
+        {
+            notNull = e;
+        }
+
+        assertNotNull( notNull );
+    }
+}
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/jndi/SearchIT.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/jndi/SearchIT.java
new file mode 100644
index 0000000..964e1dd
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/jndi/SearchIT.java
@@ -0,0 +1,1305 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.jndi;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.integ.CiRunner;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getSystemContext;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getSchemaContext;
+import org.apache.directory.shared.ldap.constants.JndiPropertyConstants;
+import org.apache.directory.shared.ldap.exception.LdapSizeLimitExceededException;
+import org.apache.directory.shared.ldap.exception.LdapTimeLimitExceededException;
+import org.apache.directory.shared.ldap.message.AliasDerefMode;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.message.ModificationItemImpl;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.LdapContext;
+import java.util.HashMap;
+import java.util.Set;
+import java.util.HashSet;
+
+
+/**
+ * Tests the search() methods of the provider.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+@RunWith ( CiRunner.class )
+public class SearchIT
+{
+    private static final String RDN = "cn=Heather Nova";
+    private static final String FILTER = "(objectclass=*)";
+
+    public static DirectoryService service;
+
+    /**
+     * @todo put this into ldif and use ldif annotation to import
+     *
+     * @param sysRoot the system root to add entries to
+     * @throws NamingException on errors
+     */
+    protected void createData( LdapContext sysRoot ) throws NamingException
+    {
+        /*
+         * create ou=testing00,ou=system
+         */
+        Attributes attributes = new AttributesImpl( true );
+        Attribute attribute = new AttributeImpl( "objectClass" );
+        attribute.add( "top" );
+        attribute.add( "organizationalUnit" );
+        attributes.put( attribute );
+        attributes.put( "ou", "testing00" );
+
+        DirContext ctx = sysRoot.createSubcontext( "ou=testing00", attributes );
+        assertNotNull( ctx );
+
+        ctx = ( DirContext ) sysRoot.lookup( "ou=testing00" );
+        assertNotNull( ctx );
+        attributes = ctx.getAttributes( "" );
+        assertNotNull( attributes );
+        assertEquals( "testing00", attributes.get( "ou" ).get() );
+        attribute = attributes.get( "objectClass" );
+        assertNotNull( attribute );
+        assertTrue( attribute.contains( "top" ) );
+        assertTrue( attribute.contains( "organizationalUnit" ) );
+
+        /*
+         * create ou=testing01,ou=system
+         */
+        attributes = new AttributesImpl( true );
+        attribute = new AttributeImpl( "objectClass" );
+        attribute.add( "top" );
+        attribute.add( "organizationalUnit" );
+        attributes.put( attribute );
+        attributes.put( "ou", "testing01" );
+
+        ctx = sysRoot.createSubcontext( "ou=testing01", attributes );
+        assertNotNull( ctx );
+
+        ctx = ( DirContext ) sysRoot.lookup( "ou=testing01" );
+        assertNotNull( ctx );
+        attributes = ctx.getAttributes( "" );
+        assertNotNull( attributes );
+        assertEquals( "testing01", attributes.get( "ou" ).get() );
+        attribute = attributes.get( "objectClass" );
+        assertNotNull( attribute );
+        assertTrue( attribute.contains( "top" ) );
+        assertTrue( attribute.contains( "organizationalUnit" ) );
+
+        /*
+         * create ou=testing02,ou=system
+         */
+        attributes = new AttributesImpl( true );
+        attribute = new AttributeImpl( "objectClass" );
+        attribute.add( "top" );
+        attribute.add( "organizationalUnit" );
+        attributes.put( attribute );
+        attributes.put( "ou", "testing02" );
+        ctx = sysRoot.createSubcontext( "ou=testing02", attributes );
+        assertNotNull( ctx );
+
+        ctx = ( DirContext ) sysRoot.lookup( "ou=testing02" );
+        assertNotNull( ctx );
+
+        attributes = ctx.getAttributes( "" );
+        assertNotNull( attributes );
+        assertEquals( "testing02", attributes.get( "ou" ).get() );
+
+        attribute = attributes.get( "objectClass" );
+        assertNotNull( attribute );
+        assertTrue( attribute.contains( "top" ) );
+        assertTrue( attribute.contains( "organizationalUnit" ) );
+
+        /*
+         * create ou=subtest,ou=testing01,ou=system
+         */
+        ctx = ( DirContext ) sysRoot.lookup( "ou=testing01" );
+
+        attributes = new AttributesImpl( true );
+        attribute = new AttributeImpl( "objectClass" );
+        attribute.add( "top" );
+        attribute.add( "organizationalUnit" );
+        attributes.put( attribute );
+        attributes.put( "ou", "subtest" );
+
+        ctx = ctx.createSubcontext( "ou=subtest", attributes );
+        assertNotNull( ctx );
+
+        ctx = ( DirContext ) sysRoot.lookup( "ou=subtest,ou=testing01" );
+        assertNotNull( ctx );
+
+        attributes = ctx.getAttributes( "" );
+        assertNotNull( attributes );
+        assertEquals( "subtest", attributes.get( "ou" ).get() );
+
+        attribute = attributes.get( "objectClass" );
+        assertNotNull( attribute );
+        assertTrue( attribute.contains( "top" ) );
+        assertTrue( attribute.contains( "organizationalUnit" ) );
+
+        // Create entry cn=Heather Nova, ou=system
+        Attributes heather = new AttributesImpl();
+        Attribute ocls = new AttributeImpl( "objectClass" );
+        ocls.add( "top" );
+        ocls.add( "person" );
+        heather.put( ocls );
+        heather.put( "cn", "Heather Nova" );
+        heather.put( "sn", "Nova" );
+        ctx = sysRoot.createSubcontext( RDN, heather );
+        assertNotNull( ctx );
+
+        ctx = ( DirContext ) sysRoot.lookup( RDN );
+        assertNotNull( ctx );
+
+
+        // -------------------------------------------------------------------
+        // Enable the nis schema
+        // -------------------------------------------------------------------
+
+        // check if nis is disabled
+        LdapContext schemaRoot = getSchemaContext( service );
+        Attributes nisAttrs = schemaRoot.getAttributes( "cn=nis" );
+        boolean isNisDisabled = false;
+        if ( nisAttrs.get( "m-disabled" ) != null )
+        {
+            isNisDisabled = ( ( String ) nisAttrs.get( "m-disabled" ).get() ).equalsIgnoreCase( "TRUE" );
+        }
+
+        // if nis is disabled then enable it
+        if ( isNisDisabled )
+        {
+            Attribute disabled = new AttributeImpl( "m-disabled" );
+            ModificationItemImpl[] mods = new ModificationItemImpl[] {
+                new ModificationItemImpl( DirContext.REMOVE_ATTRIBUTE, disabled ) };
+            schemaRoot.modifyAttributes( "cn=nis", mods );
+        }
+
+        // -------------------------------------------------------------------
+        // Add a bunch of nis groups
+        // -------------------------------------------------------------------
+
+        addNisPosixGroup( "testGroup0", 0 );
+        addNisPosixGroup( "testGroup1", 1 );
+        addNisPosixGroup( "testGroup2", 2 );
+        addNisPosixGroup( "testGroup4", 4 );
+        addNisPosixGroup( "testGroup5", 5 );
+    }
+
+
+    private DirContext addNisPosixGroup( String name, int gid ) throws NamingException
+    {
+        Attributes attrs = new AttributesImpl( "objectClass", "top", true );
+        attrs.get( "objectClass" ).add( "posixGroup" );
+        attrs.put( "cn", name );
+        attrs.put( "gidNumber", String.valueOf( gid ) );
+        return getSystemContext( service ).createSubcontext( "cn="+name+",ou=groups", attrs );
+    }
+
+
+    @Test
+    public void testSearchOneLevel() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        createData( sysRoot );
+
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+        controls.setDerefLinkFlag( false );
+        sysRoot.addToEnvironment( JndiPropertyConstants.JNDI_LDAP_DAP_DEREF_ALIASES,
+                AliasDerefMode.NEVER_DEREF_ALIASES.getJndiValue() );
+        HashMap<String,Attributes> map = new HashMap<String,Attributes>();
+
+        NamingEnumeration<SearchResult> list = sysRoot.search( "", "(ou=*)", controls );
+        
+        while ( list.hasMore() )
+        {
+            SearchResult result = list.next();
+            map.put( result.getName(), result.getAttributes() );
+        }
+
+        assertEquals( "Expected number of results returned was incorrect!", 6, map.size() );
+        assertTrue( map.containsKey( "ou=testing00,ou=system" ) );
+        assertTrue( map.containsKey( "ou=testing01,ou=system" ) );
+        assertTrue( map.containsKey( "ou=testing02,ou=system" ) );
+    }
+
+
+    @Test
+    public void testSearchSubTreeLevel() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        createData( sysRoot );
+
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        controls.setDerefLinkFlag( false );
+        sysRoot.addToEnvironment( JndiPropertyConstants.JNDI_LDAP_DAP_DEREF_ALIASES,
+                AliasDerefMode.NEVER_DEREF_ALIASES.getJndiValue() );
+
+        HashMap<String, Attributes> map = new HashMap<String, Attributes>();
+        NamingEnumeration<SearchResult> list = sysRoot.search( "", "(ou=*)", controls );
+        
+        while ( list.hasMore() )
+        {
+            SearchResult result = list.next();
+            map.put( result.getName(), result.getAttributes() );
+        }
+
+        assertEquals( "Expected number of results returned was incorrect", 11, map.size() );
+        assertTrue( map.containsKey( "ou=system" ) );
+        assertTrue( map.containsKey( "ou=testing00,ou=system" ) );
+        assertTrue( map.containsKey( "ou=testing01,ou=system" ) );
+        assertTrue( map.containsKey( "ou=testing02,ou=system" ) );
+        assertTrue( map.containsKey( "ou=subtest,ou=testing01,ou=system" ) );
+    }
+
+
+    @Test
+    public void testSearchSubTreeLevelNoAttributes() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        createData( sysRoot );
+
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        controls.setDerefLinkFlag( false );
+        controls.setReturningAttributes( new String[]{ "1.1" } );
+        
+        sysRoot.addToEnvironment( JndiPropertyConstants.JNDI_LDAP_DAP_DEREF_ALIASES,
+                AliasDerefMode.NEVER_DEREF_ALIASES.getJndiValue() );
+
+        HashMap<String, Attributes> map = new HashMap<String, Attributes>();
+        NamingEnumeration<SearchResult> list = sysRoot.search( "", "(ou=testing02)", controls );
+        
+        while ( list.hasMore() )
+        {
+            SearchResult result = list.next();
+            map.put( result.getName(), result.getAttributes() );
+        }
+
+        assertEquals( "Expected number of results returned was incorrect", 1, map.size() );
+        assertTrue( map.containsKey( "ou=testing02,ou=system" ) );
+        Attributes attrs = map.get( "ou=testing02,ou=system" );
+        
+        assertEquals( 0, attrs.size() );
+    }
+
+
+    @Test
+    public void testSearchSubstringSubTreeLevel() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        createData( sysRoot );
+
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        controls.setDerefLinkFlag( false );
+        sysRoot.addToEnvironment( JndiPropertyConstants.JNDI_LDAP_DAP_DEREF_ALIASES,
+                AliasDerefMode.NEVER_DEREF_ALIASES.getJndiValue() );
+
+        HashMap<String, Attributes> map = new HashMap<String, Attributes>();
+        NamingEnumeration<SearchResult> list = sysRoot.search( "", "(objectClass=organ*)", controls );
+        
+        while ( list.hasMore() )
+        {
+            SearchResult result = list.next();
+            map.put( result.getName(), result.getAttributes() );
+        }
+
+        // 13 because it also matches organizationalPerson which the admin is
+        assertEquals( "Expected number of results returned was incorrect", 13, map.size() );
+        assertTrue( map.containsKey( "ou=system" ) );
+        assertTrue( map.containsKey( "ou=configuration,ou=system" ) );
+        assertTrue( map.containsKey( "ou=interceptors,ou=configuration,ou=system" ) );
+        assertTrue( map.containsKey( "ou=partitions,ou=configuration,ou=system" ) );
+        assertTrue( map.containsKey( "ou=services,ou=configuration,ou=system" ) );
+        assertTrue( map.containsKey( "ou=groups,ou=system" ) );
+        assertTrue( map.containsKey( "ou=testing00,ou=system" ) );
+        assertTrue( map.containsKey( "ou=testing01,ou=system" ) );
+        assertTrue( map.containsKey( "ou=subtest,ou=testing01,ou=system" ) );
+        assertTrue( map.containsKey( "ou=testing02,ou=system" ) );
+        assertTrue( map.containsKey( "ou=users,ou=system" ) );
+        assertTrue( map.containsKey( "prefNodeName=sysPrefRoot,ou=system" ) );
+        assertTrue( map.containsKey( "uid=admin,ou=system" ) );
+    }
+
+
+    @Test
+    public void testSearchFilterArgs() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        createData( sysRoot );
+
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+        controls.setDerefLinkFlag( false );
+        sysRoot.addToEnvironment( JndiPropertyConstants.JNDI_LDAP_DAP_DEREF_ALIASES,
+                AliasDerefMode.NEVER_DEREF_ALIASES.getJndiValue() );
+        HashMap<String, Attributes> map = new HashMap<String, Attributes>();
+
+        NamingEnumeration<SearchResult> list = sysRoot.search( "", "(|(ou={0})(ou={1}))", new Object[]
+            { "testing00", "testing01" }, controls );
+        
+        while ( list.hasMore() )
+        {
+            SearchResult result = list.next();
+            map.put( result.getName(), result.getAttributes() );
+        }
+
+        assertEquals( "Expected number of results returned was incorrect!", 2, map.size() );
+        assertTrue( map.containsKey( "ou=testing00,ou=system" ) );
+        assertTrue( map.containsKey( "ou=testing01,ou=system" ) );
+    }
+
+
+    @Test
+    public void testSearchSizeLimit() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        createData( sysRoot );
+
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        controls.setDerefLinkFlag( false );
+        controls.setCountLimit( 7 );
+        sysRoot.addToEnvironment( JndiPropertyConstants.JNDI_LDAP_DAP_DEREF_ALIASES,
+                AliasDerefMode.NEVER_DEREF_ALIASES.getJndiValue() );
+
+        HashMap<String, Attributes> map = new HashMap<String, Attributes>();
+        NamingEnumeration<SearchResult> list = sysRoot.search( "", "(ou=*)", controls );
+
+        try
+        {
+            while ( list.hasMore() )
+            {
+                SearchResult result = list.next();
+                map.put( result.getName(), result.getAttributes() );
+            }
+            
+            fail( "Should not get here due to a SizeLimitExceededException" );
+        }
+        catch ( LdapSizeLimitExceededException e )
+        {
+        }
+        assertEquals( "Expected number of results returned was incorrect", 7, map.size() );
+    }
+
+
+    @Test
+    public void testSearchTimeLimit() throws NamingException, InterruptedException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        createData( sysRoot );
+
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        controls.setDerefLinkFlag( false );
+        controls.setTimeLimit( 200 );
+        sysRoot.addToEnvironment( JndiPropertyConstants.JNDI_LDAP_DAP_DEREF_ALIASES,
+                AliasDerefMode.NEVER_DEREF_ALIASES.getJndiValue() );
+
+        HashMap<String, Attributes> map = new HashMap<String, Attributes>();
+        NamingEnumeration<SearchResult> list = sysRoot.search( "", "(ou=*)", controls );
+
+        try
+        {
+            while ( list.hasMore() )
+            {
+                SearchResult result = ( SearchResult ) list.next();
+                
+                // leep 201 ms before fetching the next element ...
+                Thread.sleep( 201 );
+                map.put( result.getName(), result.getAttributes() );
+            }
+            
+            fail( "Should not get here due to a TimeLimitExceededException" );
+        }
+        catch ( LdapTimeLimitExceededException e )
+        {
+        }
+        
+        assertEquals( "Expected number of results returned was incorrect", 1, map.size() );
+    }
+    
+
+    @Test
+    public void testFilterExpansion0() throws Exception
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        createData( sysRoot );
+
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        controls.setDerefLinkFlag( false );
+        sysRoot.addToEnvironment( JndiPropertyConstants.JNDI_LDAP_DAP_DEREF_ALIASES,
+                AliasDerefMode.NEVER_DEREF_ALIASES.getJndiValue() );
+        
+        HashMap<String, Attributes> map = new HashMap<String, Attributes>();
+        NamingEnumeration<SearchResult> list = sysRoot.search( "", "(name=testing00)", controls );
+        
+        while ( list.hasMore() )
+        {
+            SearchResult result = list.next();
+            map.put( result.getName(), result.getAttributes() );
+        }
+        
+        assertEquals( "size of results", 1, map.size() );
+        assertTrue( "contains ou=testing00,ou=system", map.containsKey( "ou=testing00,ou=system" ) ); 
+    }
+    
+
+    @Test
+    public void testFilterExpansion1() throws Exception
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        createData( sysRoot );
+
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        controls.setDerefLinkFlag( false );
+        sysRoot.addToEnvironment( JndiPropertyConstants.JNDI_LDAP_DAP_DEREF_ALIASES,
+                AliasDerefMode.NEVER_DEREF_ALIASES.getJndiValue() );
+        
+        HashMap<String, Attributes> map = new HashMap<String, Attributes>();
+        NamingEnumeration<SearchResult> list = sysRoot.search( "", "(name=*)", controls );
+        
+        while ( list.hasMore() )
+        {
+            SearchResult result = list.next();
+            map.put( result.getName(), result.getAttributes() );
+        }
+        
+        assertEquals( "size of results", 19, map.size() );
+        assertTrue( "contains ou=testing00,ou=system", map.containsKey( "ou=testing00,ou=system" ) ); 
+        assertTrue( "contains ou=testing01,ou=system", map.containsKey( "ou=testing01,ou=system" ) ); 
+        assertTrue( "contains ou=testing02,ou=system", map.containsKey( "ou=testing01,ou=system" ) ); 
+        assertTrue( "contains ou=configuration,ou=system", map.containsKey( "ou=configuration,ou=system" ) ); 
+        assertTrue( "contains ou=groups,ou=system", map.containsKey( "ou=groups,ou=system" ) ); 
+        assertTrue( "contains ou=interceptors,ou=configuration,ou=system", map.containsKey( "ou=interceptors,ou=configuration,ou=system" ) ); 
+        assertTrue( "contains ou=partitions,ou=configuration,ou=system", map.containsKey( "ou=partitions,ou=configuration,ou=system" ) ); 
+        assertTrue( "contains ou=services,ou=configuration,ou=system", map.containsKey( "ou=services,ou=configuration,ou=system" ) ); 
+        assertTrue( "contains ou=subtest,ou=testing01,ou=system", map.containsKey( "ou=subtest,ou=testing01,ou=system" ) ); 
+        assertTrue( "contains ou=system", map.containsKey( "ou=system" ) ); 
+        assertTrue( "contains ou=users,ou=system", map.containsKey( "ou=users,ou=system" ) ); 
+        assertTrue( "contains uid=admin,ou=system", map.containsKey( "uid=admin,ou=system" ) ); 
+        assertTrue( "contains cn=administrators,ou=groups,ou=system", map.containsKey( "cn=Administrators,ou=groups,ou=system" ) ); 
+    }
+    
+    
+    @Test
+    public void testFilterExpansion2() throws Exception
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        createData( sysRoot );
+
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        controls.setDerefLinkFlag( false );
+        sysRoot.addToEnvironment( JndiPropertyConstants.JNDI_LDAP_DAP_DEREF_ALIASES,
+                AliasDerefMode.NEVER_DEREF_ALIASES.getJndiValue() );
+        
+        HashMap<String, Attributes> map = new HashMap<String, Attributes>();
+        NamingEnumeration<SearchResult> list = sysRoot.search( "", "(|(name=testing00)(name=testing01))", controls );
+        
+        while ( list.hasMore() )
+        {
+            SearchResult result = list.next();
+            map.put( result.getName(), result.getAttributes() );
+        }
+        
+        assertEquals( "size of results", 2, map.size() );
+        assertTrue( "contains ou=testing00,ou=system", map.containsKey( "ou=testing00,ou=system" ) ); 
+        assertTrue( "contains ou=testing01,ou=system", map.containsKey( "ou=testing01,ou=system" ) ); 
+    }
+
+
+    @Test
+    public void testFilterExpansion4() throws Exception
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        createData( sysRoot );
+
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        controls.setDerefLinkFlag( false );
+        sysRoot.addToEnvironment( JndiPropertyConstants.JNDI_LDAP_DAP_DEREF_ALIASES,
+                AliasDerefMode.NEVER_DEREF_ALIASES.getJndiValue() );
+        
+        HashMap<String, Attributes> map = new HashMap<String, Attributes>();
+        NamingEnumeration<SearchResult> list = sysRoot.search( "", "(name=testing*)", controls );
+        
+        while ( list.hasMore() )
+        {
+            SearchResult result = list.next();
+            map.put( result.getName(), result.getAttributes() );
+        }
+        
+        assertEquals( "size of results", 3, map.size() );
+        assertTrue( "contains ou=testing00,ou=system", map.containsKey( "ou=testing00,ou=system" ) ); 
+        assertTrue( "contains ou=testing01,ou=system", map.containsKey( "ou=testing01,ou=system" ) ); 
+        assertTrue( "contains ou=testing02,ou=system", map.containsKey( "ou=testing01,ou=system" ) ); 
+    }
+
+
+    @Test
+    public void testFilterExpansion5() throws Exception
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        createData( sysRoot );
+
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        controls.setDerefLinkFlag( false );
+        sysRoot.addToEnvironment( JndiPropertyConstants.JNDI_LDAP_DAP_DEREF_ALIASES,
+                AliasDerefMode.NEVER_DEREF_ALIASES.getJndiValue() );
+        
+        HashMap<String, Attributes> map = new HashMap<String, Attributes>();
+        String filter = "(|(2.5.4.11.1=testing*)(2.5.4.54=testing*)(2.5.4.10=testing*)" +
+            "(2.5.4.6=testing*)(2.5.4.43=testing*)(2.5.4.7.1=testing*)(2.5.4.10.1=testing*)" +
+            "(2.5.4.44=testing*)(2.5.4.11=testing*)(2.5.4.4=testing*)(2.5.4.8.1=testing*)" +
+            "(2.5.4.12=testing*)(1.3.6.1.4.1.18060.0.4.1.2.3=testing*)" +
+            "(2.5.4.7=testing*)(2.5.4.3=testing*)(2.5.4.8=testing*)(2.5.4.42=testing*))";
+        NamingEnumeration<SearchResult> list = sysRoot.search( "", filter, controls );
+        
+        while ( list.hasMore() )
+        {
+            SearchResult result = list.next();
+            map.put( result.getName(), result.getAttributes() );
+        }
+        
+        assertEquals( "size of results", 3, map.size() );
+        assertTrue( "contains ou=testing00,ou=system", map.containsKey( "ou=testing00,ou=system" ) ); 
+        assertTrue( "contains ou=testing01,ou=system", map.containsKey( "ou=testing01,ou=system" ) ); 
+        assertTrue( "contains ou=testing02,ou=system", map.containsKey( "ou=testing01,ou=system" ) ); 
+    }
+    
+
+    @Test
+    public void testOpAttrDenormalizationOff() throws Exception
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        createData( sysRoot );
+
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+        controls.setDerefLinkFlag( false );
+        controls.setReturningAttributes( new String[] { "creatorsName" } );
+        sysRoot.addToEnvironment( JndiPropertyConstants.JNDI_LDAP_DAP_DEREF_ALIASES,
+                AliasDerefMode.NEVER_DEREF_ALIASES.getJndiValue() );
+        HashMap<String, Attributes> map = new HashMap<String, Attributes>();
+
+        NamingEnumeration<SearchResult> list = sysRoot.search( "", "(ou=testing00)", controls );
+        
+        while ( list.hasMore() )
+        {
+            SearchResult result = list.next();
+            map.put( result.getName(), result.getAttributes() );
+        }
+
+        assertEquals( "Expected number of results returned was incorrect!", 1, map.size() );
+        assertTrue( map.containsKey( "ou=testing00,ou=system" ) );
+        Attributes attrs = map.get( "ou=testing00,ou=system" );
+        assertEquals( "normalized creator's name", "0.9.2342.19200300.100.1.1=admin,2.5.4.11=system", 
+            attrs.get( "creatorsName" ).get() );
+    }
+
+
+    @Test
+    public void testOpAttrDenormalizationOn() throws Exception
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        createData( sysRoot );
+
+        service.setDenormalizeOpAttrsEnabled( true );
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+        controls.setDerefLinkFlag( false );
+        controls.setReturningAttributes( new String[] { "creatorsName" } );
+        sysRoot.addToEnvironment( JndiPropertyConstants.JNDI_LDAP_DAP_DEREF_ALIASES,
+                AliasDerefMode.NEVER_DEREF_ALIASES.getJndiValue() );
+        HashMap<String, Attributes> map = new HashMap<String, Attributes>();
+
+        NamingEnumeration<SearchResult> list = sysRoot.search( "", "(ou=testing00)", controls );
+        
+        while ( list.hasMore() )
+        {
+            SearchResult result = list.next();
+            map.put( result.getName(), result.getAttributes() );
+        }
+
+        assertEquals( "Expected number of results returned was incorrect!", 1, map.size() );
+        assertTrue( map.containsKey( "ou=testing00,ou=system" ) );
+        Attributes attrs = map.get( "ou=testing00,ou=system" );
+        assertEquals( "normalized creator's name", "uid=admin,ou=system", 
+            attrs.get( "creatorsName" ).get() );
+    }
+
+    
+    /**
+     * Creation of required attributes of a person entry.
+     *
+     * @param cn the commonName of the person
+     * @param sn the surName of the person
+     * @return the attributes of a new person entry
+     */
+    protected Attributes getPersonAttributes( String sn, String cn )
+    {
+        Attributes attributes = new AttributesImpl();
+        Attribute attribute = new AttributeImpl( "objectClass" );
+        attribute.add( "top" );
+        attribute.add( "person" );
+        attributes.put( attribute );
+        attributes.put( "cn", cn );
+        attributes.put( "sn", sn );
+
+        return attributes;
+    }
+
+
+    @Test
+    public void testBinaryAttributesInFilter() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        createData( sysRoot );
+
+        byte[] certData = new byte[] { 0x34, 0x56, 0x4e, 0x5f };
+        
+        // First let's add a some binary data representing a userCertificate
+        Attributes attrs = getPersonAttributes( "Bush", "Kate Bush" );
+        attrs.put( "userCertificate", certData );
+
+        Attribute objectClasses = attrs.get( "objectClass" );
+        objectClasses.add( "strongAuthenticationUser" );
+
+        sysRoot.createSubcontext( "cn=Kate Bush", attrs );
+
+        // Search for kate by cn first
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+        NamingEnumeration<SearchResult> enm = sysRoot.search( "", "(cn=Kate Bush)", controls );
+        assertTrue( enm.hasMore() );
+        SearchResult sr = enm.next();
+        assertNotNull( sr );
+        assertFalse( enm.hasMore() );
+        assertEquals( "cn=Kate Bush,ou=system", sr.getName() );
+
+        enm = sysRoot.search( "", "(userCertificate=\\34\\56\\4E\\5F)", controls );
+        assertTrue( enm.hasMore() );
+        sr = ( SearchResult ) enm.next();
+        assertNotNull( sr );
+        assertFalse( enm.hasMore() );
+        assertEquals( "cn=Kate Bush,ou=system", sr.getName() );
+    }
+
+
+    @Test
+    public void testSearchOperationalAttr() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        createData( sysRoot );
+
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+        controls.setDerefLinkFlag( false );
+        controls.setReturningAttributes( new String[] { "+" } );
+        sysRoot.addToEnvironment( JndiPropertyConstants.JNDI_LDAP_DAP_DEREF_ALIASES,
+                AliasDerefMode.NEVER_DEREF_ALIASES.getJndiValue() );
+        HashMap<String, Attributes> map = new HashMap<String, Attributes>();
+
+        NamingEnumeration<SearchResult> list = sysRoot.search( "", "(ou=testing01)", controls );
+        
+        while ( list.hasMore() )
+        {
+            SearchResult result = list.next();
+            map.put( result.getName(), result.getAttributes() );
+        }
+
+        assertEquals( "Expected number of results returned was incorrect!", 1, map.size() );
+
+        Attributes attrs = map.get( "ou=testing01,ou=system" );
+
+        assertNotNull( attrs.get( "createTimestamp" ) );
+        assertNotNull( attrs.get( "creatorsName" ) );
+        assertNull( attrs.get( "objectClass" ) );
+        assertNull( attrs.get( "ou" ) );
+    }
+
+
+    @Test
+    public void testSearchUserAttr() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        createData( sysRoot );
+
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+        controls.setDerefLinkFlag( false );
+        controls.setReturningAttributes( new String[] { "*" } );
+        sysRoot.addToEnvironment( JndiPropertyConstants.JNDI_LDAP_DAP_DEREF_ALIASES,
+                AliasDerefMode.NEVER_DEREF_ALIASES.getJndiValue() );
+        HashMap<String, Attributes> map = new HashMap<String, Attributes>();
+
+        NamingEnumeration<SearchResult> list = sysRoot.search( "", "(ou=testing01)", controls );
+        
+        while ( list.hasMore() )
+        {
+            SearchResult result = list.next();
+            map.put( result.getName(), result.getAttributes() );
+        }
+
+        assertEquals( "Expected number of results returned was incorrect!", 1, map.size() );
+
+        Attributes attrs = map.get( "ou=testing01,ou=system" );
+
+        assertNotNull( attrs.get( "objectClass" ) );
+        assertNotNull( attrs.get( "ou" ) );
+        assertNull( attrs.get( "createTimestamp" ) );
+        assertNull( attrs.get( "creatorsName" ) );
+    }
+
+
+    @Test
+    public void testSearchUserAttrAndOpAttr() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        createData( sysRoot );
+
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+        controls.setDerefLinkFlag( false );
+        controls.setReturningAttributes( new String[] { "*", "creatorsName" } );
+        sysRoot.addToEnvironment( JndiPropertyConstants.JNDI_LDAP_DAP_DEREF_ALIASES,
+                AliasDerefMode.NEVER_DEREF_ALIASES.getJndiValue() );
+        HashMap<String, Attributes> map = new HashMap<String, Attributes>();
+
+        NamingEnumeration<SearchResult> list = sysRoot.search( "", "(ou=testing01)", controls );
+        
+        while ( list.hasMore() )
+        {
+            SearchResult result = list.next();
+            map.put( result.getName(), result.getAttributes() );
+        }
+
+        assertEquals( "Expected number of results returned was incorrect!", 1, map.size() );
+
+        Attributes attrs = map.get( "ou=testing01,ou=system" );
+
+        assertNotNull( attrs.get( "objectClass" ) );
+        assertNotNull( attrs.get( "ou" ) );
+        assertNotNull( attrs.get( "creatorsName" ) );
+        assertNull( attrs.get( "createTimestamp" ) );
+    }
+
+
+    @Test
+    public void testSearchUserAttrAndNoAttr() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        createData( sysRoot );
+
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+        controls.setDerefLinkFlag( false );
+        controls.setReturningAttributes( new String[] { "1.1", "ou" } );
+        sysRoot.addToEnvironment( JndiPropertyConstants.JNDI_LDAP_DAP_DEREF_ALIASES,
+                AliasDerefMode.NEVER_DEREF_ALIASES.getJndiValue() );
+        HashMap<String, Attributes> map = new HashMap<String, Attributes>();
+
+        NamingEnumeration<SearchResult> list = sysRoot.search( "", "(ou=testing01)", controls );
+        
+        while ( list.hasMore() )
+        {
+            SearchResult result = list.next();
+            map.put( result.getName(), result.getAttributes() );
+        }
+
+        assertEquals( "Expected number of results returned was incorrect!", 1, map.size() );
+
+        Attributes attrs = map.get( "ou=testing01,ou=system" );
+
+        assertNull( attrs.get( "objectClass" ) );
+        assertNotNull( attrs.get( "ou" ) );
+        assertNull( attrs.get( "creatorsName" ) );
+        assertNull( attrs.get( "createTimestamp" ) );
+    }
+
+
+    @Test
+    public void testSearchNoAttr() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        createData( sysRoot );
+
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+        controls.setDerefLinkFlag( false );
+        controls.setReturningAttributes( new String[] { "1.1" } );
+        sysRoot.addToEnvironment( JndiPropertyConstants.JNDI_LDAP_DAP_DEREF_ALIASES,
+                AliasDerefMode.NEVER_DEREF_ALIASES.getJndiValue() );
+        HashMap<String, Attributes> map = new HashMap<String, Attributes>();
+
+        NamingEnumeration<SearchResult> list = sysRoot.search( "", "(ou=testing01)", controls );
+        
+        while ( list.hasMore() )
+        {
+            SearchResult result = list.next();
+            map.put( result.getName(), result.getAttributes() );
+        }
+
+        assertEquals( "Expected number of results returned was incorrect!", 1, map.size() );
+
+        Attributes attrs = map.get( "ou=testing01,ou=system" );
+
+        assertNull( attrs.get( "objectClass" ) );
+        assertNull( attrs.get( "ou" ) );
+        assertNull( attrs.get( "creatorsName" ) );
+        assertNull( attrs.get( "createTimestamp" ) );
+    }
+
+
+    @Test
+    public void testSearchAllAttr() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        createData( sysRoot );
+
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+        controls.setDerefLinkFlag( false );
+        controls.setReturningAttributes( new String[] { "+", "*" } );
+        sysRoot.addToEnvironment( JndiPropertyConstants.JNDI_LDAP_DAP_DEREF_ALIASES,
+                AliasDerefMode.NEVER_DEREF_ALIASES.getJndiValue() );
+        HashMap<String, Attributes> map = new HashMap<String, Attributes>();
+
+        NamingEnumeration<SearchResult> list = sysRoot.search( "", "(ou=testing01)", controls );
+        
+
+        while ( list.hasMore() )
+        {
+            SearchResult result = list.next();
+            map.put( result.getName(), result.getAttributes() );
+        }
+
+        assertEquals( "Expected number of results returned was incorrect!", 1, map.size() );
+
+        Attributes attrs = map.get( "ou=testing01,ou=system" );
+
+        assertNotNull( attrs.get( "createTimestamp" ) );
+        assertNotNull( attrs.get( "creatorsName" ) );
+        assertNotNull( attrs.get( "objectClass" ) );
+        assertNotNull( attrs.get( "ou" ) );
+    }
+
+
+    /**
+     * Search an entry and fetch an attribute with unknown option
+     * @throws NamingException if there are errors
+     */
+    @Test
+    public void testSearchFetchNonExistingAttributeOption() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        createData( sysRoot );
+
+        SearchControls ctls = new SearchControls();
+        ctls.setSearchScope( SearchControls.OBJECT_SCOPE );
+        ctls.setReturningAttributes( new String[]
+            { "cn", "sn;unknownOption" } );
+
+        NamingEnumeration<SearchResult> result = sysRoot.search( RDN, FILTER, ctls );
+
+        if ( result.hasMore() )
+        {
+            SearchResult entry = result.next();
+            Attributes attrs = entry.getAttributes();
+            Attribute cn = attrs.get( "cn" );
+
+            assertNotNull( cn );
+            assertEquals( "Heather Nova", cn.get().toString() );
+
+            Attribute sn = attrs.get( "sn" );
+            assertNull( sn );
+        }
+        else
+        {
+            fail( "entry " + RDN + " not found" );
+        }
+
+        result.close();
+    }
+
+
+    /**
+     * Search an entry and fetch an attribute with twice the same attributeType
+     * @throws NamingException if there are errors
+     */
+    @Test
+    public void testSearchFetchTwiceSameAttribute() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        createData( sysRoot );
+
+        SearchControls ctls = new SearchControls();
+        ctls.setSearchScope( SearchControls.OBJECT_SCOPE );
+        ctls.setReturningAttributes( new String[]
+            { "cn", "cn" } );
+
+        NamingEnumeration<SearchResult> result = sysRoot.search( RDN, FILTER, ctls );
+
+        if ( result.hasMore() )
+        {
+            SearchResult entry = result.next();
+            Attributes attrs = entry.getAttributes();
+            Attribute cn = attrs.get( "cn" );
+
+            assertNotNull( cn );
+            assertEquals( "Heather Nova", cn.get().toString() );
+        }
+        else
+        {
+            fail( "entry " + RDN + " not found" );
+        }
+
+        result.close();
+    }
+
+
+    // this one is failing because it returns the admin user twice: count = 15
+//    public void testFilterExpansion3() throws Exception
+//    {
+//        SearchControls controls = new SearchControls();
+//        controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+//        controls.setDerefLinkFlag( false );
+//        sysRoot.addToEnvironment( JndiPropertyConstants.JNDI_LDAP_DAP_DEREF_ALIASES, AliasDerefMode.NEVER_DEREF_ALIASES );
+//        
+//        List map = new ArrayList();
+//        NamingEnumeration list = sysRoot.search( "", "(name=*)", controls );
+//        while ( list.hasMore() )
+//        {
+//            SearchResult result = ( SearchResult ) list.next();
+//            map.add( result.getName() );
+//        }
+//        assertEquals( "size of results", 14, map.size() );
+//        assertTrue( "contains ou=testing00,ou=system", map.contains( "ou=testing00,ou=system" ) ); 
+//        assertTrue( "contains ou=testing01,ou=system", map.contains( "ou=testing01,ou=system" ) ); 
+//        assertTrue( "contains ou=testing02,ou=system", map.contains( "ou=testing01,ou=system" ) ); 
+//        assertTrue( "contains uid=akarasulu,ou=users,ou=system", map.contains( "uid=akarasulu,ou=users,ou=system" ) ); 
+//        assertTrue( "contains ou=configuration,ou=system", map.contains( "ou=configuration,ou=system" ) ); 
+//        assertTrue( "contains ou=groups,ou=system", map.contains( "ou=groups,ou=system" ) ); 
+//        assertTrue( "contains ou=interceptors,ou=configuration,ou=system", map.contains( "ou=interceptors,ou=configuration,ou=system" ) ); 
+//        assertTrue( "contains ou=partitions,ou=configuration,ou=system", map.contains( "ou=partitions,ou=configuration,ou=system" ) ); 
+//        assertTrue( "contains ou=services,ou=configuration,ou=system", map.contains( "ou=services,ou=configuration,ou=system" ) ); 
+//        assertTrue( "contains ou=subtest,ou=testing01,ou=system", map.contains( "ou=subtest,ou=testing01,ou=system" ) ); 
+//        assertTrue( "contains ou=system", map.contains( "ou=system" ) ); 
+//        assertTrue( "contains ou=users,ou=system", map.contains( "ou=users,ou=system" ) ); 
+//        assertTrue( "contains uid=admin,ou=system", map.contains( "uid=admin,ou=system" ) ); 
+//        assertTrue( "contains cn=administrators,ou=groups,ou=system", map.contains( "cn=administrators,ou=groups,ou=system" ) ); 
+//    }
+
+
+
+    /**
+     *  Convenience method that performs a one level search using the
+     *  specified filter returning their DNs as Strings in a set.
+     *
+     * @param controls the search controls
+     * @param filter the filter expression
+     * @return the set of groups
+     * @throws NamingException if there are problems conducting the search
+     */
+    public Set<String> searchGroups( String filter, SearchControls controls ) throws NamingException
+    {
+        if ( controls == null )
+        {
+            controls = new SearchControls();
+        }
+
+        Set<String> results = new HashSet<String>();
+        NamingEnumeration<SearchResult> list = getSystemContext( service ).search( "ou=groups", filter, controls );
+
+        while( list.hasMore() )
+        {
+            SearchResult result = list.next();
+            results.add( result.getName() );
+        }
+
+        return results;
+    }
+
+
+    /**
+     *  Convenience method that performs a one level search using the
+     *  specified filter returning their DNs as Strings in a set.
+     *
+     * @param filter the filter expression
+     * @return the set of group names
+     * @throws NamingException if there are problems conducting the search
+     */
+    public Set<String> searchGroups( String filter ) throws NamingException
+    {
+        return searchGroups( filter, null );
+    }
+
+
+    @Test
+    public void testSetup() throws Exception
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        createData( sysRoot );
+
+        Set<String> results = searchGroups( "(objectClass=posixGroup)" );
+        assertTrue( results.contains( "cn=testGroup0,ou=groups,ou=system" ) );
+        assertTrue( results.contains( "cn=testGroup1,ou=groups,ou=system" ) );
+        assertTrue( results.contains( "cn=testGroup2,ou=groups,ou=system" ) );
+        assertFalse( results.contains( "cn=testGroup3,ou=groups,ou=system" ) );
+        assertTrue( results.contains( "cn=testGroup4,ou=groups,ou=system" ) );
+        assertTrue( results.contains( "cn=testGroup5,ou=groups,ou=system" ) );
+    }
+
+
+    @Test
+    public void testLessThanSearch() throws Exception
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        createData( sysRoot );
+
+        Set<String> results = searchGroups( "(gidNumber<=5)" );
+        assertTrue( results.contains( "cn=testGroup0,ou=groups,ou=system" ) );
+        assertTrue( results.contains( "cn=testGroup1,ou=groups,ou=system" ) );
+        assertTrue( results.contains( "cn=testGroup2,ou=groups,ou=system" ) );
+        assertFalse( results.contains( "cn=testGroup3,ou=groups,ou=system" ) );
+        assertTrue( results.contains( "cn=testGroup4,ou=groups,ou=system" ) );
+        assertTrue( results.contains( "cn=testGroup5,ou=groups,ou=system" ) );
+
+        results = searchGroups( "(gidNumber<=4)" );
+        assertTrue( results.contains( "cn=testGroup0,ou=groups,ou=system" ) );
+        assertTrue( results.contains( "cn=testGroup1,ou=groups,ou=system" ) );
+        assertTrue( results.contains( "cn=testGroup2,ou=groups,ou=system" ) );
+        assertFalse( results.contains( "cn=testGroup3,ou=groups,ou=system" ) );
+        assertTrue( results.contains( "cn=testGroup4,ou=groups,ou=system" ) );
+        assertFalse( results.contains( "cn=testGroup5,ou=groups,ou=system" ) );
+
+        results = searchGroups( "(gidNumber<=3)" );
+        assertTrue( results.contains( "cn=testGroup0,ou=groups,ou=system" ) );
+        assertTrue( results.contains( "cn=testGroup1,ou=groups,ou=system" ) );
+        assertTrue( results.contains( "cn=testGroup2,ou=groups,ou=system" ) );
+        assertFalse( results.contains( "cn=testGroup3,ou=groups,ou=system" ) );
+        assertFalse( results.contains( "cn=testGroup4,ou=groups,ou=system" ) );
+        assertFalse( results.contains( "cn=testGroup5,ou=groups,ou=system" ) );
+
+        results = searchGroups( "(gidNumber<=0)" );
+        assertTrue( results.contains( "cn=testGroup0,ou=groups,ou=system" ) );
+        assertFalse( results.contains( "cn=testGroup1,ou=groups,ou=system" ) );
+        assertFalse( results.contains( "cn=testGroup2,ou=groups,ou=system" ) );
+        assertFalse( results.contains( "cn=testGroup3,ou=groups,ou=system" ) );
+        assertFalse( results.contains( "cn=testGroup4,ou=groups,ou=system" ) );
+        assertFalse( results.contains( "cn=testGroup5,ou=groups,ou=system" ) );
+
+        results = searchGroups( "(gidNumber<=-1)" );
+        assertFalse( results.contains( "cn=testGroup0,ou=groups,ou=system" ) );
+        assertFalse( results.contains( "cn=testGroup1,ou=groups,ou=system" ) );
+        assertFalse( results.contains( "cn=testGroup2,ou=groups,ou=system" ) );
+        assertFalse( results.contains( "cn=testGroup3,ou=groups,ou=system" ) );
+        assertFalse( results.contains( "cn=testGroup4,ou=groups,ou=system" ) );
+        assertFalse( results.contains( "cn=testGroup5,ou=groups,ou=system" ) );
+    }
+
+
+    @Test
+    public void testGreaterThanSearch() throws Exception
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        createData( sysRoot );
+
+        Set<String> results = searchGroups( "(gidNumber>=0)" );
+        assertTrue( results.contains( "cn=testGroup0,ou=groups,ou=system" ) );
+        assertTrue( results.contains( "cn=testGroup1,ou=groups,ou=system" ) );
+        assertTrue( results.contains( "cn=testGroup2,ou=groups,ou=system" ) );
+        assertFalse( results.contains( "cn=testGroup3,ou=groups,ou=system" ) );
+        assertTrue( results.contains( "cn=testGroup4,ou=groups,ou=system" ) );
+        assertTrue( results.contains( "cn=testGroup5,ou=groups,ou=system" ) );
+
+        results = searchGroups( "(gidNumber>=1)" );
+        assertFalse( results.contains( "cn=testGroup0,ou=groups,ou=system" ) );
+        assertTrue( results.contains( "cn=testGroup1,ou=groups,ou=system" ) );
+        assertTrue( results.contains( "cn=testGroup2,ou=groups,ou=system" ) );
+        assertFalse( results.contains( "cn=testGroup3,ou=groups,ou=system" ) );
+        assertTrue( results.contains( "cn=testGroup4,ou=groups,ou=system" ) );
+        assertTrue( results.contains( "cn=testGroup5,ou=groups,ou=system" ) );
+
+        results = searchGroups( "(gidNumber>=3)" );
+        assertFalse( results.contains( "cn=testGroup0,ou=groups,ou=system" ) );
+        assertFalse( results.contains( "cn=testGroup1,ou=groups,ou=system" ) );
+        assertFalse( results.contains( "cn=testGroup2,ou=groups,ou=system" ) );
+        assertFalse( results.contains( "cn=testGroup3,ou=groups,ou=system" ) );
+        assertTrue( results.contains( "cn=testGroup4,ou=groups,ou=system" ) );
+        assertTrue( results.contains( "cn=testGroup5,ou=groups,ou=system" ) );
+
+        results = searchGroups( "(gidNumber>=6)" );
+        assertFalse( results.contains( "cn=testGroup0,ou=groups,ou=system" ) );
+        assertFalse( results.contains( "cn=testGroup1,ou=groups,ou=system" ) );
+        assertFalse( results.contains( "cn=testGroup2,ou=groups,ou=system" ) );
+        assertFalse( results.contains( "cn=testGroup3,ou=groups,ou=system" ) );
+        assertFalse( results.contains( "cn=testGroup4,ou=groups,ou=system" ) );
+        assertFalse( results.contains( "cn=testGroup5,ou=groups,ou=system" ) );
+    }
+
+
+    @Test
+    public void testNotOperator() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        createData( sysRoot );
+
+        Set<String> results = searchGroups( "(!(gidNumber=4))" );
+        assertTrue( results.contains( "cn=testGroup0,ou=groups,ou=system" ) );
+        assertTrue( results.contains( "cn=testGroup1,ou=groups,ou=system" ) );
+        assertTrue( results.contains( "cn=testGroup2,ou=groups,ou=system" ) );
+        assertFalse( results.contains( "cn=testGroup3,ou=groups,ou=system" ) );
+        assertFalse( results.contains( "cn=testGroup4,ou=groups,ou=system" ) );
+        assertTrue( results.contains( "cn=testGroup5,ou=groups,ou=system" ) );
+    }
+
+
+    @Test
+    public void testNotOperatorSubtree() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        createData( sysRoot );
+
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+
+        Set<String> results = searchGroups( "(!(gidNumber=4))", controls );
+        assertTrue( results.contains( "cn=testGroup0,ou=groups,ou=system" ) );
+        assertTrue( results.contains( "cn=testGroup1,ou=groups,ou=system" ) );
+        assertTrue( results.contains( "cn=testGroup2,ou=groups,ou=system" ) );
+        assertFalse( results.contains( "cn=testGroup3,ou=groups,ou=system" ) );
+        assertFalse( results.contains( "cn=testGroup4,ou=groups,ou=system" ) );
+        assertTrue( results.contains( "cn=testGroup5,ou=groups,ou=system" ) );
+    }
+    
+    
+    @Test
+    public void testSearchWithEscapedCharsInFilter() throws NamingException
+    {
+        // Create an entry with special chars in the description attribute
+        LdapContext sysRoot = getSystemContext( service );
+        // Create entry cn=Sid Vicious, ou=system
+        Attributes vicious = new AttributesImpl();
+        Attribute ocls = new AttributeImpl( "objectClass" );
+        ocls.add( "top" );
+        ocls.add( "person" );
+        vicious.put( ocls );
+        vicious.put( "cn", "Sid Vicious" );
+        vicious.put( "sn", "Vicious" );
+        vicious.put(  "description", "(sex pistols)" );
+        DirContext ctx = sysRoot.createSubcontext( "cn=Sid Vicious", vicious );
+        assertNotNull( ctx );
+
+        ctx = ( DirContext ) sysRoot.lookup( "cn=Sid Vicious" );
+        assertNotNull( ctx );
+        
+        Attributes attributes = ctx.getAttributes( "" );
+        
+        assertEquals( "(sex pistols)", attributes.get( "description" ).get() );
+
+        // Now, search for the description
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        controls.setDerefLinkFlag( false );
+        controls.setReturningAttributes( new String[] { "*" } );
+        sysRoot.addToEnvironment( JndiPropertyConstants.JNDI_LDAP_DAP_DEREF_ALIASES,
+                AliasDerefMode.NEVER_DEREF_ALIASES.getJndiValue() );
+        HashMap<String, Attributes> map = new HashMap<String, Attributes>();
+
+        NamingEnumeration<SearchResult> list = sysRoot.search( "", "(description=\\28sex pistols\\29)", controls );
+        
+        while ( list.hasMore() )
+        {
+            SearchResult result = list.next();
+            map.put( result.getName(), result.getAttributes() );
+        }
+
+        assertEquals( "Expected number of results returned was incorrect!", 1, map.size() );
+
+        Attributes attrs = map.get( "cn=Sid Vicious,ou=system" );
+
+        assertNotNull( attrs.get( "objectClass" ) );
+        assertNotNull( attrs.get( "cn" ) );
+    }
+
+
+    /**
+     * Search operation with a base DN with quotes
+     * Commented as it's not valid by RFC 5514
+    @Test
+    public void testSearchWithQuotesInBase() throws NamingException 
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        createData( sysRoot );
+
+        SearchControls ctls = new SearchControls();
+        ctls.setSearchScope( SearchControls.OBJECT_SCOPE );
+        String filter = "(cn=Tori Amos)";
+        ctls.setReturningAttributes( new String[]
+            { "cn", "cn" } );
+
+        // Search for cn="Tori Amos" (with quotes)
+        String base = "cn=\"Tori Amos\"";
+
+        try {
+            // Check entry
+            NamingEnumeration<SearchResult> result = sysRoot.search( base, filter, ctls );
+            assertTrue( result.hasMore() );
+            
+            while ( result.hasMore() ) 
+            {
+                SearchResult sr = result.next();
+                Attributes attrs = sr.getAttributes();
+                Attribute sn = attrs.get( "cn" );
+                assertNotNull(sn);
+                assertTrue( sn.contains( "Amos" ) );
+            }
+        } catch (Exception e) 
+        {
+            fail( e.getMessage() );
+        }
+    }
+    */
+}
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/jndi/SearchWithIndicesITest.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/jndi/SearchWithIndicesITest.java
new file mode 100644
index 0000000..7ca3aac
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/jndi/SearchWithIndicesITest.java
@@ -0,0 +1,284 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.jndi;
+
+
+import org.apache.directory.server.core.DefaultDirectoryService;
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.entry.DefaultServerEntry;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.integ.CiRunner;
+import org.apache.directory.server.core.integ.DirectoryServiceFactory;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getSchemaContext;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getSystemContext;
+import org.apache.directory.server.core.integ.Level;
+import org.apache.directory.server.core.integ.annotations.Factory;
+import org.apache.directory.server.core.integ.annotations.CleanupLevel;
+import org.apache.directory.server.core.partition.impl.btree.Index;
+import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmIndex;
+import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmPartition;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.message.ModificationItemImpl;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.LdapContext;
+import java.util.HashSet;
+import java.util.Set;
+
+
+/**
+ * Tests various search scenarios.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+@RunWith ( CiRunner.class )
+@CleanupLevel ( Level.CLASS )
+@Factory ( SearchWithIndicesITest.MyFactory.class )
+public class SearchWithIndicesITest
+{
+    public static DirectoryService service;
+
+
+
+    private void createData() throws NamingException
+    {
+        // -------------------------------------------------------------------
+        // Enable the nis schema
+        // -------------------------------------------------------------------
+
+        // check if nis is disabled
+        LdapContext schemaRoot = getSchemaContext( service );
+        Attributes nisAttrs = schemaRoot.getAttributes( "cn=nis" );
+        boolean isNisDisabled = false;
+        if ( nisAttrs.get( "m-disabled" ) != null )
+        {
+            isNisDisabled = ( ( String ) nisAttrs.get( "m-disabled" ).get() ).equalsIgnoreCase( "TRUE" );
+        }
+
+        // if nis is disabled then enable it
+        if ( isNisDisabled )
+        {
+            Attribute disabled = new AttributeImpl( "m-disabled" );
+            ModificationItemImpl[] mods = new ModificationItemImpl[] {
+                new ModificationItemImpl( DirContext.REMOVE_ATTRIBUTE, disabled ) };
+            schemaRoot.modifyAttributes( "cn=nis", mods );
+        }
+
+        // -------------------------------------------------------------------
+        // Add a bunch of nis groups
+        // -------------------------------------------------------------------
+
+        addNisPosixGroup( "testGroup0", 0 );
+        addNisPosixGroup( "testGroup1", 1 );
+        addNisPosixGroup( "testGroup2", 2 );
+        addNisPosixGroup( "testGroup4", 4 );
+        addNisPosixGroup( "testGroup5", 5 );
+    }
+
+
+    private DirContext addNisPosixGroup( String name, int gid ) throws NamingException
+    {
+        Attributes attrs = new AttributesImpl( "objectClass", "top", true );
+        attrs.get( "objectClass" ).add( "posixGroup" );
+        attrs.put( "cn", name );
+        attrs.put( "gidNumber", String.valueOf( gid ) );
+        return getSystemContext( service ).createSubcontext( "cn="+name+",ou=groups", attrs );
+    }
+    
+    
+    public static class MyFactory implements DirectoryServiceFactory
+    {
+        public DirectoryService newInstance() 
+        {
+            DirectoryService service = new DefaultDirectoryService();
+            service.getChangeLog().setEnabled( true );
+
+            // -------------------------------------------------------------------
+            // Alter the partition configuration to index gidNumber
+            // -------------------------------------------------------------------
+
+            JdbmPartition partition = new JdbmPartition();
+            partition.setId( "system" );
+            
+            try
+            {
+                ServerEntry serverEntry = new DefaultServerEntry( service.getRegistries(), new LdapDN( "ou=system" ) );
+                serverEntry.put( "objectClass", "top", "organizationalUnit" );
+                serverEntry.put( "ou", "system" );
+                partition.setContextEntry( serverEntry );
+                partition.setSuffix( "ou=system" );
+
+            }
+            catch ( NamingException ne )
+            {
+                // Do nothing
+            }
+
+            Set<Index> indices = new HashSet<Index>();
+            indices.addAll( partition.getIndexedAttributes() );
+            indices.add( new JdbmIndex( "gidNumber" ) );
+            partition.setIndexedAttributes( indices );
+            service.setSystemPartition( partition );
+
+            return service;
+        }
+    }
+    
+    
+    /**
+     *  Convenience method that performs a one level search using the
+     *  specified filter returning their DNs as Strings in a set.
+     *
+     * @param controls the search controls
+     * @param filter the filter expression
+     * @return the set of groups
+     * @throws NamingException if there are problems conducting the search
+     */
+    public Set<String> searchGroups( String filter, SearchControls controls ) throws NamingException
+    {
+        if ( controls == null )
+        {
+            controls = new SearchControls();
+        }
+
+        Set<String> results = new HashSet<String>();
+        NamingEnumeration<SearchResult> list = getSystemContext( service ).search( "ou=groups", filter, controls );
+
+        while( list.hasMore() )
+        {
+            SearchResult result = list.next();
+            results.add( result.getName() );
+        }
+
+        return results;
+    }
+
+
+    /**
+     *  Convenience method that performs a one level search using the
+     *  specified filter returning their DNs as Strings in a set.
+     *
+     * @param filter the filter expression
+     * @return the set of group names
+     * @throws NamingException if there are problems conducting the search
+     */
+    public Set<String> searchGroups( String filter ) throws NamingException
+    {
+        return searchGroups( filter, null );
+    }
+
+
+    @Test
+    public void testLessThanSearchWithIndices() throws Exception
+    {
+        createData();
+        Set<String> results = searchGroups( "(gidNumber<=5)" );
+        assertTrue( results.contains( "cn=testGroup0,ou=groups,ou=system" ) );
+        assertTrue( results.contains( "cn=testGroup1,ou=groups,ou=system" ) );
+        assertTrue( results.contains( "cn=testGroup2,ou=groups,ou=system" ) );
+        assertFalse( results.contains( "cn=testGroup3,ou=groups,ou=system" ) );
+        assertTrue( results.contains( "cn=testGroup4,ou=groups,ou=system" ) );
+        assertTrue( results.contains( "cn=testGroup5,ou=groups,ou=system" ) );
+
+        results = searchGroups( "(gidNumber<=4)" );
+        assertTrue( results.contains( "cn=testGroup0,ou=groups,ou=system" ) );
+        assertTrue( results.contains( "cn=testGroup1,ou=groups,ou=system" ) );
+        assertTrue( results.contains( "cn=testGroup2,ou=groups,ou=system" ) );
+        assertFalse( results.contains( "cn=testGroup3,ou=groups,ou=system" ) );
+        assertTrue( results.contains( "cn=testGroup4,ou=groups,ou=system" ) );
+        assertFalse( results.contains( "cn=testGroup5,ou=groups,ou=system" ) );
+
+        results = searchGroups( "(gidNumber<=3)" );
+        assertTrue( results.contains( "cn=testGroup0,ou=groups,ou=system" ) );
+        assertTrue( results.contains( "cn=testGroup1,ou=groups,ou=system" ) );
+        assertTrue( results.contains( "cn=testGroup2,ou=groups,ou=system" ) );
+        assertFalse( results.contains( "cn=testGroup3,ou=groups,ou=system" ) );
+        assertFalse( results.contains( "cn=testGroup4,ou=groups,ou=system" ) );
+        assertFalse( results.contains( "cn=testGroup5,ou=groups,ou=system" ) );
+
+        results = searchGroups( "(gidNumber<=0)" );
+        assertTrue( results.contains( "cn=testGroup0,ou=groups,ou=system" ) );
+        assertFalse( results.contains( "cn=testGroup1,ou=groups,ou=system" ) );
+        assertFalse( results.contains( "cn=testGroup2,ou=groups,ou=system" ) );
+        assertFalse( results.contains( "cn=testGroup3,ou=groups,ou=system" ) );
+        assertFalse( results.contains( "cn=testGroup4,ou=groups,ou=system" ) );
+        assertFalse( results.contains( "cn=testGroup5,ou=groups,ou=system" ) );
+
+        results = searchGroups( "(gidNumber<=-1)" );
+        assertFalse( results.contains( "cn=testGroup0,ou=groups,ou=system" ) );
+        assertFalse( results.contains( "cn=testGroup1,ou=groups,ou=system" ) );
+        assertFalse( results.contains( "cn=testGroup2,ou=groups,ou=system" ) );
+        assertFalse( results.contains( "cn=testGroup3,ou=groups,ou=system" ) );
+        assertFalse( results.contains( "cn=testGroup4,ou=groups,ou=system" ) );
+        assertFalse( results.contains( "cn=testGroup5,ou=groups,ou=system" ) );
+    }
+
+    
+    @Test
+    public void testGreaterThanSearchWithIndices() throws Exception
+    {
+        createData();
+        Set<String> results = searchGroups( "(gidNumber>=0)" );
+        assertTrue( results.contains( "cn=testGroup0,ou=groups,ou=system" ) );
+        assertTrue( results.contains( "cn=testGroup1,ou=groups,ou=system" ) );
+        assertTrue( results.contains( "cn=testGroup2,ou=groups,ou=system" ) );
+        assertFalse( results.contains( "cn=testGroup3,ou=groups,ou=system" ) );
+        assertTrue( results.contains( "cn=testGroup4,ou=groups,ou=system" ) );
+        assertTrue( results.contains( "cn=testGroup5,ou=groups,ou=system" ) );
+
+        results = searchGroups( "(gidNumber>=1)" );
+        assertFalse( results.contains( "cn=testGroup0,ou=groups,ou=system" ) );
+        assertTrue( results.contains( "cn=testGroup1,ou=groups,ou=system" ) );
+        assertTrue( results.contains( "cn=testGroup2,ou=groups,ou=system" ) );
+        assertFalse( results.contains( "cn=testGroup3,ou=groups,ou=system" ) );
+        assertTrue( results.contains( "cn=testGroup4,ou=groups,ou=system" ) );
+        assertTrue( results.contains( "cn=testGroup5,ou=groups,ou=system" ) );
+
+        results = searchGroups( "(gidNumber>=3)" );
+        assertFalse( results.contains( "cn=testGroup0,ou=groups,ou=system" ) );
+        assertFalse( results.contains( "cn=testGroup1,ou=groups,ou=system" ) );
+        assertFalse( results.contains( "cn=testGroup2,ou=groups,ou=system" ) );
+        assertFalse( results.contains( "cn=testGroup3,ou=groups,ou=system" ) );
+        assertTrue( results.contains( "cn=testGroup4,ou=groups,ou=system" ) );
+        assertTrue( results.contains( "cn=testGroup5,ou=groups,ou=system" ) );
+
+        results = searchGroups( "(gidNumber>=6)" );
+        assertFalse( results.contains( "cn=testGroup0,ou=groups,ou=system" ) );
+        assertFalse( results.contains( "cn=testGroup1,ou=groups,ou=system" ) );
+        assertFalse( results.contains( "cn=testGroup2,ou=groups,ou=system" ) );
+        assertFalse( results.contains( "cn=testGroup3,ou=groups,ou=system" ) );
+        assertFalse( results.contains( "cn=testGroup4,ou=groups,ou=system" ) );
+        assertFalse( results.contains( "cn=testGroup5,ou=groups,ou=system" ) );
+    }
+}
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/jndi/UniqueMemberIT.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/jndi/UniqueMemberIT.java
new file mode 100644
index 0000000..e98e102
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/jndi/UniqueMemberIT.java
@@ -0,0 +1,468 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.
+ *
+ */
+package org.apache.directory.server.core.jndi;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.integ.CiRunner;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getSystemContext;
+import org.apache.directory.shared.ldap.constants.JndiPropertyConstants;
+import org.apache.directory.shared.ldap.message.AliasDerefMode;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.LdapContext;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Set;
+
+
+/**
+ * Test DIRSERVER-757 : a UniqueMember attribute should only contain a DN completed with an
+ * optional UID. 
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+@RunWith ( CiRunner.class )
+public class UniqueMemberIT
+{
+    public static DirectoryService service;
+
+    
+    /**
+     * Test a valid entry
+     *
+     * @throws Exception on error
+     */
+    @Test
+    public void testValidUniqueMember() throws Exception
+    {
+        LdapContext sysRoot = getSystemContext( service );
+
+        Attributes attrs = new AttributesImpl( true );
+        Attribute oc = new AttributeImpl( "ObjectClass", "top" );
+        oc.add( "groupOfUniqueNames" );
+        Attribute cn = new AttributeImpl( "cn", "kevin Spacey" );
+        Attribute dc = new AttributeImpl( "uniqueMember", "cn=kevin spacey, dc=example, dc=org" );
+        attrs.put( oc );
+        attrs.put( cn );
+        attrs.put( dc);
+
+        String base = "cn=kevin Spacey";
+
+        //create subcontext
+        try
+        {
+            sysRoot.createSubcontext( base, attrs );
+        }
+        catch ( NamingException ne )
+        {
+            fail();
+        }
+        
+        Attributes returned = sysRoot.getAttributes( "cn=kevin Spacey" );
+      
+        NamingEnumeration<? extends Attribute> attrList = returned.getAll();
+        
+        while ( attrList.hasMore() )
+        {
+            Attribute attr = attrList.next();
+          
+            if ( attr.getID().equalsIgnoreCase( "cn" ) )
+            {
+                assertEquals( "kevin Spacey", attr.get() );
+                continue;
+            }
+          
+            if ( attr.getID().equalsIgnoreCase( "objectClass" ) )
+            {
+                NamingEnumeration<?> values = attr.getAll();
+                Set<String> expectedValues = new HashSet<String>();
+                
+                expectedValues.add( "top" );
+                expectedValues.add( "groupofuniquenames" );
+                
+                while ( values.hasMoreElements() )
+                {
+                    String value = ( (String)values.nextElement() ).toLowerCase();
+                    assertTrue( expectedValues.contains( value ) );
+                    expectedValues.remove( value );
+                }
+                
+                assertEquals( 0, expectedValues.size() );
+                continue;
+            }
+          
+            if ( attr.getID().equalsIgnoreCase( "uniqueMember" ) )
+            {
+                assertEquals( "cn=kevin spacey, dc=example, dc=org", attr.get() );
+            }
+        }
+    }
+
+
+    /**
+     * Test a valid entry, with an optional UID
+     *
+     * @throws Exception on error
+     */
+    @Test
+    public void testValidUniqueMemberWithOptionnalUID() throws Exception
+    {
+        LdapContext sysRoot = getSystemContext( service );
+
+        Attributes attrs = new AttributesImpl( true );
+        Attribute oc = new AttributeImpl( "ObjectClass", "top" );
+        oc.add( "groupOfUniqueNames" );
+        Attribute cn = new AttributeImpl( "cn", "kevin Spacey 2" );
+        Attribute dc = new AttributeImpl( "uniqueMember", "cn=kevin spacey 2, dc=example, dc=org#'010101'B" );
+        attrs.put( oc );
+        attrs.put( cn );
+        attrs.put( dc);
+
+        String base = "cn=kevin Spacey 2";
+
+        //create subcontext
+        try
+        {
+            sysRoot.createSubcontext( base, attrs );
+        }
+        catch ( NamingException ne )
+        {
+            fail();
+        }
+        
+        Attributes returned = sysRoot.getAttributes( "cn=kevin Spacey 2" );
+      
+        NamingEnumeration<? extends Attribute> attrList = returned.getAll();
+        
+        while ( attrList.hasMore() )
+        {
+            Attribute attr = attrList.next();
+          
+            if ( attr.getID().equalsIgnoreCase( "cn" ) )
+            {
+                assertEquals( "kevin Spacey 2", attr.get() );
+                continue;
+            }
+          
+            if ( attr.getID().equalsIgnoreCase( "objectClass" ) )
+            {
+                NamingEnumeration<?> values = attr.getAll();
+                Set<String> expectedValues = new HashSet<String>();
+                
+                expectedValues.add( "top" );
+                expectedValues.add( "groupofuniquenames" );
+                
+                while ( values.hasMoreElements() )
+                {
+                    String value = ( (String)values.nextElement() ).toLowerCase();
+                    assertTrue( expectedValues.contains( value ) );
+                    expectedValues.remove( value );
+                }
+                
+                assertEquals( 0, expectedValues.size() );
+                continue;
+            }
+          
+            if ( attr.getID().equalsIgnoreCase( "uniqueMember" ) )
+            {
+                assertEquals( "cn=kevin spacey 2, dc=example, dc=org#'010101'B", attr.get() );
+            }
+        }
+    }
+
+
+    /**
+     * Test a valid entry, with an optional UID
+     *
+     * @throws Exception on error
+     */
+    @Test
+    public void testInvalidUniqueMemberBadDN() throws Exception
+    {
+        LdapContext sysRoot = getSystemContext( service );
+
+        Attributes attrs = new AttributesImpl( true );
+        Attribute oc = new AttributeImpl( "ObjectClass", "top" );
+        oc.add( "groupOfUniqueNames" );
+        Attribute cn = new AttributeImpl( "cn", "kevin Spacey bad" );
+        Attribute dc = new AttributeImpl( "uniqueMember", "kevin spacey bad, dc=example, dc=org#'010101'B" );
+        attrs.put( oc );
+        attrs.put( cn );
+        attrs.put( dc);
+
+        String base = "cn=kevin Spacey bad";
+
+        //create subcontext
+        try
+        {
+            sysRoot.createSubcontext( base, attrs );
+            fail();
+        }
+        catch ( NamingException ne )
+        {
+            assertTrue( true );
+        }
+    }
+
+
+    /**
+     * Test a valid entry, with an optional UID
+     *
+     * @throws Exception on error
+     */
+    @Test
+    public void testInvalidUniqueMemberBadUID() throws Exception
+    {
+        LdapContext sysRoot = getSystemContext( service );
+
+        Attributes attrs = new AttributesImpl( true );
+        Attribute oc = new AttributeImpl( "ObjectClass", "top" );
+        oc.add( "groupOfUniqueNames" );
+        Attribute cn = new AttributeImpl( "cn", "kevin Spacey bad 2" );
+        Attribute dc = new AttributeImpl( "uniqueMember", "cn=kevin spacey bad 2, dc=example, dc=org#'010101'" );
+        attrs.put( oc );
+        attrs.put( cn );
+        attrs.put( dc);
+
+        String base = "cn=kevin Spacey bad 2";
+
+        //create subcontext
+        try
+        {
+            sysRoot.createSubcontext( base, attrs );
+            fail();
+        }
+        catch ( NamingException ne )
+        {
+            assertTrue( true );
+        }
+    }
+    
+
+    @Test
+    public void testSearchUniqueMemberFilter() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+
+        Attributes attrs = new AttributesImpl( true );
+        Attribute oc = new AttributeImpl( "ObjectClass", "top" );
+        oc.add( "groupOfUniqueNames" );
+        Attribute cn = new AttributeImpl( "cn", "kevin Spacey" );
+        Attribute dc = new AttributeImpl( "uniqueMember", "cn=kevin spacey, dc=example, dc=org" );
+        attrs.put( oc );
+        attrs.put( cn );
+        attrs.put( dc);
+
+        String base = "cn=kevin Spacey";
+
+        //create subcontext
+        try
+        {
+            sysRoot.createSubcontext( base, attrs );
+        }
+        catch ( NamingException ne )
+        {
+            fail();
+        }
+
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        controls.setDerefLinkFlag( false );
+        controls.setReturningAttributes( new String[] { "*" } );
+        sysRoot.addToEnvironment( JndiPropertyConstants.JNDI_LDAP_DAP_DEREF_ALIASES,
+                AliasDerefMode.NEVER_DEREF_ALIASES.getJndiValue() );
+        HashMap<String, Attributes> map = new HashMap<String, Attributes>();
+
+        NamingEnumeration<SearchResult> list = sysRoot.search( "", "(uniqueMember=cn = kevin spacey, dc=example, dc=org)", controls );
+        
+        while ( list.hasMore() )
+        {
+            SearchResult result = list.next();
+            map.put( result.getName().toLowerCase(), result.getAttributes() );
+        }
+
+        assertEquals( "Expected number of results returned was incorrect!", 1, map.size() );
+        
+        attrs = map.get( "cn=kevin spacey,ou=system" );
+        
+        assertNotNull( attrs.get( "objectClass" ) );
+        assertNotNull( attrs.get( "cn" ) );
+        assertNotNull( attrs.get( "uniqueMember" ) );
+    }
+
+
+    @Test
+    public void testSearchUniqueMemberFilterWithSpaces() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+
+        Attributes attrs = new AttributesImpl( true );
+        Attribute oc = new AttributeImpl( "ObjectClass", "top" );
+        oc.add( "groupOfUniqueNames" );
+        Attribute cn = new AttributeImpl( "cn", "kevin Spacey" );
+        Attribute dc = new AttributeImpl( "uniqueMember", "cn=kevin spacey,dc=example,dc=org" );
+        attrs.put( oc );
+        attrs.put( cn );
+        attrs.put( dc);
+
+        String base = "cn=kevin Spacey";
+
+        //create subcontext
+        try
+        {
+            sysRoot.createSubcontext( base, attrs );
+        }
+        catch ( NamingException ne )
+        {
+            fail();
+        }
+
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        controls.setDerefLinkFlag( false );
+        controls.setReturningAttributes( new String[] { "*" } );
+        sysRoot.addToEnvironment( JndiPropertyConstants.JNDI_LDAP_DAP_DEREF_ALIASES,
+                AliasDerefMode.NEVER_DEREF_ALIASES.getJndiValue() );
+        HashMap<String, Attributes> map = new HashMap<String, Attributes>();
+
+        NamingEnumeration<SearchResult> list = sysRoot.search( "", "(uniqueMember=cn = Kevin  Spacey , dc = example , dc = ORG)", controls );
+        
+        while ( list.hasMore() )
+        {
+            SearchResult result = list.next();
+            map.put( result.getName().toLowerCase(), result.getAttributes() );
+        }
+
+        assertEquals( "Expected number of results returned was incorrect!", 1, map.size() );
+        
+        attrs = map.get( "cn=kevin spacey,ou=system" );
+        
+        assertNotNull( attrs.get( "objectClass" ) );
+        assertNotNull( attrs.get( "cn" ) );
+        assertNotNull( attrs.get( "uniqueMember" ) );
+    }
+
+
+    @Test
+    public void testSearchUniqueMemberFilterWithBadDN() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+
+        Attributes attrs = new AttributesImpl( true );
+        Attribute oc = new AttributeImpl( "ObjectClass", "top" );
+        oc.add( "groupOfUniqueNames" );
+        Attribute cn = new AttributeImpl( "cn", "kevin Spacey" );
+        Attribute dc = new AttributeImpl( "uniqueMember", "cn=kevin spacey,dc=example,dc=org" );
+        attrs.put( oc );
+        attrs.put( cn );
+        attrs.put( dc);
+
+        String base = "cn=kevin Spacey";
+
+        //create subcontext
+        try
+        {
+            sysRoot.createSubcontext( base, attrs );
+        }
+        catch ( NamingException ne )
+        {
+            fail();
+        }
+
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        controls.setDerefLinkFlag( false );
+        controls.setReturningAttributes( new String[] { "*" } );
+        sysRoot.addToEnvironment( JndiPropertyConstants.JNDI_LDAP_DAP_DEREF_ALIASES,
+                AliasDerefMode.NEVER_DEREF_ALIASES.getJndiValue() );
+
+        NamingEnumeration<SearchResult> list = sysRoot.search( "", "(uniqueMember=cn=cevin spacey,dc=example,dc=org)", controls );
+        
+        assertFalse( list.hasMore() );
+    }
+
+
+    @Test
+    public void testSearchUniqueMemberFilterWithUID() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+
+        Attributes attrs = new AttributesImpl( true );
+        Attribute oc = new AttributeImpl( "ObjectClass", "top" );
+        oc.add( "groupOfUniqueNames" );
+        Attribute cn = new AttributeImpl( "cn", "kevin Spacey" );
+        Attribute dc = new AttributeImpl( "uniqueMember", "cn=kevin spacey,dc=example,dc=org#'010101'B" );
+        attrs.put( oc );
+        attrs.put( cn );
+        attrs.put( dc);
+
+        String base = "cn=kevin Spacey";
+
+        //create subcontext
+        try
+        {
+            sysRoot.createSubcontext( base, attrs );
+        }
+        catch ( NamingException ne )
+        {
+            fail();
+        }
+
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        controls.setDerefLinkFlag( false );
+        controls.setReturningAttributes( new String[] { "*" } );
+        sysRoot.addToEnvironment( JndiPropertyConstants.JNDI_LDAP_DAP_DEREF_ALIASES,
+                AliasDerefMode.NEVER_DEREF_ALIASES.getJndiValue() );
+        HashMap<String, Attributes> map = new HashMap<String, Attributes>();
+
+        NamingEnumeration<SearchResult> list = sysRoot.search( "", "(uniqueMember=cn= Kevin Spacey, dc=example, dc=org #'010101'B)", controls );
+        
+        while ( list.hasMore() )
+        {
+            SearchResult result = list.next();
+            map.put( result.getName().toLowerCase(), result.getAttributes() );
+        }
+
+        assertEquals( "Expected number of results returned was incorrect!", 1, map.size() );
+        
+        attrs = map.get( "cn=kevin spacey,ou=system" );
+        
+        assertNotNull( attrs.get( "objectClass" ) );
+        assertNotNull( attrs.get( "cn" ) );
+        assertNotNull( attrs.get( "uniqueMember" ) );
+    }
+}
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/normalization/NormalizationServiceIT.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/normalization/NormalizationServiceIT.java
new file mode 100644
index 0000000..d9402ff
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/normalization/NormalizationServiceIT.java
@@ -0,0 +1,87 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.normalization;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.integ.CiRunner;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getSystemContext;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertNotNull;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.ldap.LdapContext;
+
+
+
+/**
+ * Test cases for the normalization service.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+@RunWith ( CiRunner.class )
+public final class NormalizationServiceIT
+{
+    public static DirectoryService service;
+
+
+    @Test
+    public void testDireve308Example() throws NamingException
+    {
+        /*
+
+        Use @Ldif to load this data but for now we can do it with code.
+
+dn: ou=direct report view,ou=system
+objectClass: organizationalUnit
+ou: direct report view
+
+dn: ou=corporate category\, operations,ou=direct report view,ou=system
+objectClass: organizationalUnit
+ou: corporate category\, operations
+
+         */
+
+        LdapContext sysRoot = getSystemContext( service );
+
+        Attributes attrs = new AttributesImpl( "objectClass", "organizationalUnit", true );
+        attrs.put( "ou", "direct report view" );
+        sysRoot.createSubcontext( "ou=direct report view", attrs );
+
+        attrs = new AttributesImpl( "objectClass", "organizationalUnit", true );
+        attrs.put( "ou", "corporate category\\, operations" );
+        sysRoot.createSubcontext( "ou=corporate category\\, operations,ou=direct report view", attrs );
+
+        attrs = sysRoot.getAttributes( "ou=corporate category\\, operations,ou=direct report view" );
+        assertNotNull( attrs );
+        Attribute ou = attrs.get( "ou" );
+        assertEquals( "corporate category, operations", ou.get() );
+        Attribute oc = attrs.get( "objectClass" );
+        assertTrue( oc.contains( "top" ) );
+        assertTrue( oc.contains( "organizationalUnit" ) );
+    }
+}
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/operational/OperationalAttributeServiceIT.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/operational/OperationalAttributeServiceIT.java
new file mode 100644
index 0000000..e48e445
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/operational/OperationalAttributeServiceIT.java
@@ -0,0 +1,460 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.operational;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.integ.CiRunner;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getRootContext;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getSystemContext;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getUserAddLdif;
+import org.apache.directory.shared.ldap.constants.JndiPropertyConstants;
+import org.apache.directory.shared.ldap.ldif.LdifEntry;
+import org.apache.directory.shared.ldap.message.AliasDerefMode;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.message.ModificationItemImpl;
+import org.apache.directory.shared.ldap.util.StringTools;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.NoPermissionException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.BasicAttribute;
+import javax.naming.directory.BasicAttributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.InvalidAttributeValueException;
+import javax.naming.directory.ModificationItem;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.LdapContext;
+
+
+/**
+ * Tests the methods on JNDI contexts that are analogous to entry modify
+ * operations in LDAP.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+@RunWith ( CiRunner.class )
+public class OperationalAttributeServiceIT
+{
+    private static final String BINARY_KEY = "java.naming.ldap.attributes.binary";
+    private static final String RDN_KATE_BUSH = "cn=Kate Bush";
+
+
+    public static DirectoryService service;
+
+
+    protected Attributes getPersonAttributes( String sn, String cn )
+    {
+        Attributes attrs = new BasicAttributes( true );
+        Attribute ocls = new BasicAttribute( "objectClass" );
+        ocls.add( "top" );
+        ocls.add( "person" );
+        attrs.put( ocls );
+        attrs.put( "cn", cn );
+        attrs.put( "sn", sn );
+
+        return attrs;
+    }
+
+
+    /**
+     * @todo add this to an LDIF annotation
+     *
+     * @param sysRoot the system root context at ou=system as the admin
+     * @throws NamingException on error
+     */
+    protected void createData( LdapContext sysRoot ) throws NamingException
+    {
+        // Create an entry for Kate Bush
+        Attributes attrs = getPersonAttributes( "Bush", "Kate Bush" );
+        DirContext ctx = sysRoot.createSubcontext( RDN_KATE_BUSH, attrs );
+        assertNotNull( ctx );
+    }
+
+
+    @Test
+    public void testBinaryAttributeFilterExtension() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        createData( sysRoot );
+
+        Attributes attributes = new AttributesImpl( true );
+        Attribute oc = new AttributeImpl( "objectClass", "top" );
+        oc.add( "person" );
+        oc.add( "organizationalPerson" );
+        oc.add( "inetOrgPerson" );
+        attributes.put( oc );
+
+        attributes.put( "ou", "test" );
+        attributes.put( "cn", "test" );
+        attributes.put( "sn", "test" );
+
+        sysRoot.createSubcontext( "ou=test", attributes );
+
+        // test without turning on the property
+        DirContext ctx = ( DirContext ) sysRoot.lookup( "ou=test" );
+        Attribute ou = ctx.getAttributes( "" ).get( "ou" );
+        Object value = ou.get();
+        assertTrue( value instanceof String );
+
+        // test with the property now making ou into a binary value
+        sysRoot.addToEnvironment( BINARY_KEY, "ou" );
+        ctx = ( DirContext ) sysRoot.lookup( "ou=test" );
+        ou = ctx.getAttributes( "" ).get( "ou" );
+        value = ou.get();
+        assertEquals( "test", value );
+
+        // try jpegPhoto which should be binary automatically - use ou as control
+        byte[] keyValue = new byte[]
+            { (byte)0xFF, (byte)0xD8, (byte)0xFF, (byte)0xE0, 0x01, 0x02, 'J', 'F', 'I', 'F', 0x00, 0x45, 0x23, 0x7d, 0x7f };
+        attributes.put( "jpegPhoto", keyValue );
+        sysRoot.createSubcontext( "ou=anothertest", attributes );
+        ctx = ( DirContext ) sysRoot.lookup( "ou=anothertest" );
+        ou = ctx.getAttributes( "" ).get( "ou" );
+        value = ou.get();
+        assertEquals( "anothertest", value );
+        Attribute jpegPhoto = ctx.getAttributes( "" ).get( "jpegPhoto" );
+        value = jpegPhoto.get();
+        assertTrue( value instanceof byte[] );
+        assertEquals( "0xFF 0xD8 0xFF 0xE0 0x01 0x02 0x4A 0x46 0x49 0x46 0x00 0x45 0x23 0x7D 0x7F ", StringTools.dumpBytes( ( byte[] ) value ) );
+
+        // try jpegPhoto which should be binary automatically but use String to
+        // create so we should still get back a byte[] - use ou as control
+        /*attributes.remove( "jpegPhoto" );
+        attributes.put( "jpegPhoto", "testing a string" );
+        sysRoot.createSubcontext( "ou=yetanothertest", attributes );
+        ctx = ( DirContext ) sysRoot.lookup( "ou=yetanothertest" );
+        ou = ctx.getAttributes( "" ).get( "ou" );
+        value = ou.get();
+        assertEquals( "yetanothertest", value );
+        jpegPhoto = ctx.getAttributes( "" ).get( "jpegPhoto" );
+        value = jpegPhoto.get();
+        assertTrue( value instanceof byte[] );*/
+    }
+
+
+    @Test
+    public void testModifyOperationalOpAttrs() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        createData( sysRoot );
+
+        /*
+         * create ou=testing00,ou=system
+         */
+        Attributes attributes = new AttributesImpl( true );
+        Attribute attribute = new AttributeImpl( "objectClass" );
+        attribute.add( "top" );
+        attribute.add( "organizationalUnit" );
+        attributes.put( attribute );
+        attributes.put( "ou", "testing00" );
+        DirContext ctx = sysRoot.createSubcontext( "ou=testing00", attributes );
+        assertNotNull( ctx );
+
+        ctx = ( DirContext ) sysRoot.lookup( "ou=testing00" );
+        assertNotNull( ctx );
+
+        attributes = ctx.getAttributes( "" );
+        assertNotNull( attributes );
+        assertEquals( "testing00", attributes.get( "ou" ).get() );
+        attribute = attributes.get( "objectClass" );
+        assertNotNull( attribute );
+        assertTrue( attribute.contains( "top" ) );
+        assertTrue( attribute.contains( "organizationalUnit" ) );
+        assertNull( attributes.get( "createTimestamp" ) );
+        assertNull( attributes.get( "creatorsName" ) );
+
+        SearchControls ctls = new SearchControls();
+        ctls.setReturningAttributes( new String[]
+            { "ou", "createTimestamp", "creatorsName" } );
+
+        sysRoot.addToEnvironment( JndiPropertyConstants.JNDI_LDAP_DAP_DEREF_ALIASES,
+                AliasDerefMode.NEVER_DEREF_ALIASES.getJndiValue() );
+        NamingEnumeration<SearchResult> list;
+        list = sysRoot.search( "", "(ou=testing00)", ctls );
+        SearchResult result = list.next();
+        list.close();
+
+        assertNotNull( result.getAttributes().get( "ou" ) );
+        assertNotNull( result.getAttributes().get( "creatorsName" ) );
+        assertNotNull( result.getAttributes().get( "createTimestamp" ) );
+    }
+
+
+    /**
+     * Checks to confirm that the system context root ou=system has the
+     * required operational attributes.  Since this is created automatically
+     * on system database creation properties the create attributes must be
+     * specified.  There are no interceptors in effect when this happens so
+     * we must test explicitly.
+     *
+     *
+     * @see <a href="http://nagoya.apache.org/jira/browse/DIREVE-57">DIREVE-57:
+     * ou=system does not contain operational attributes</a>
+     *
+     * @throws NamingException on error
+     */
+    @Test
+    public void testSystemContextRoot() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        createData( sysRoot );
+
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.OBJECT_SCOPE );
+        NamingEnumeration<SearchResult> list;
+        list = sysRoot.search( "", "(objectClass=*)", controls );
+        SearchResult result = list.next();
+
+        // test to make sure op attribute do not occur - this is the control
+        Attributes attributes = result.getAttributes();
+        assertNull( attributes.get( "creatorsName" ) );
+        assertNull( attributes.get( "createTimestamp" ) );
+
+        // now we ask for all the op attributes and check to get them
+        String[] ids = new String[]
+            { "creatorsName", "createTimestamp" };
+        controls.setReturningAttributes( ids );
+        list = sysRoot.search( "", "(objectClass=*)", controls );
+        result = list.next();
+        attributes = result.getAttributes();
+        assertNotNull( attributes.get( "creatorsName" ) );
+        assertNotNull( attributes.get( "createTimestamp" ) );
+    }
+
+
+    /**
+     * Test which confirms that all new users created under the user's dn
+     * (ou=users,ou=system) have the creatorsName set to the DN of the new
+     * user even though the admin is creating the user.  This is the basis
+     * for some authorization rules to protect passwords.
+     *
+     * NOTE THIS CHANGE WAS REVERTED SO WE ADAPTED THE TEST TO MAKE SURE THE
+     * CHANGE DOES NOT PERSIST!
+     *
+     * @see <a href="http://nagoya.apache.org/jira/browse/DIREVE-67">JIRA Issue DIREVE-67</a>
+     *
+     * @throws NamingException on error
+     */
+    @Test
+    public void testConfirmNonAdminUserDnIsCreatorsName() throws NamingException
+    {
+        LdifEntry akarasulu = getUserAddLdif();
+        getRootContext( service ).createSubcontext( akarasulu.getDn(), akarasulu.getAttributes() );
+
+        LdapContext sysRoot = getSystemContext( service );
+        createData( sysRoot );
+
+        Attributes attributes = sysRoot.getAttributes( "uid=akarasulu,ou=users", new String[]
+            { "creatorsName" } );
+
+        assertFalse( "uid=akarasulu,ou=users,ou=system".equals( attributes.get( "creatorsName" ).get() ) );
+    }
+
+    
+    /**
+     * Modify an entry and check whether attributes modifiersName and modifyTimestamp are present.
+     *
+     * @throws NamingException on error
+     */
+    @Test
+    public void testModifyShouldLeadToModifiersAttributes() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        createData( sysRoot );
+
+        ModificationItem modifyOp = new ModificationItem( DirContext.ADD_ATTRIBUTE, new BasicAttribute( "description",
+            "Singer Songwriter" ) );
+
+        sysRoot.modifyAttributes( RDN_KATE_BUSH, new ModificationItem[]
+            { modifyOp } );
+
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.OBJECT_SCOPE );
+        String[] ids = new String[]
+            { "modifiersName", "modifyTimestamp" };
+        controls.setReturningAttributes( ids );
+
+        NamingEnumeration<SearchResult> list = sysRoot.search( RDN_KATE_BUSH, "(objectClass=*)", controls );
+        SearchResult result = list.next();
+        Attributes attributes = result.getAttributes();
+        assertNotNull( attributes.get( "modifiersName" ) );
+        assertNotNull( attributes.get( "modifyTimestamp" ) );
+    }
+    
+    
+    /**
+     * Modify an entry and check whether attribute modifyTimestamp changes.
+     *
+     * @throws NamingException on error
+     * @throws InterruptedException on error
+     */
+    @Test
+    public void testModifyShouldChangeModifyTimestamp() throws NamingException, InterruptedException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        createData( sysRoot );
+
+        // Add attribute description to entry
+        ModificationItem modifyAddOp = new ModificationItem( DirContext.ADD_ATTRIBUTE, new BasicAttribute(
+            "description", "an English singer, songwriter, musician" ) );
+        sysRoot.modifyAttributes( RDN_KATE_BUSH, new ModificationItem[]
+            { modifyAddOp } );
+
+        // Determine modifyTimestamp
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.OBJECT_SCOPE );
+        String[] ids = new String[]
+            { "modifyTimestamp" };
+        controls.setReturningAttributes( ids );
+        NamingEnumeration<SearchResult> list = sysRoot.search( RDN_KATE_BUSH, "(objectClass=*)", controls );
+        SearchResult result = list.next();
+        Attributes attributes = result.getAttributes();
+        Attribute modifyTimestamp = attributes.get( "modifyTimestamp" );
+        assertNotNull( modifyTimestamp );
+        String oldTimestamp = modifyTimestamp.get().toString();
+        
+        // Wait two seconds
+        Thread.sleep( 2000 );
+
+        // Change value of attribute description
+        ModificationItem modifyOp = new ModificationItem( DirContext.REPLACE_ATTRIBUTE, new BasicAttribute(
+            "description", "one of England's most successful solo female performers" ) );
+        sysRoot.modifyAttributes( RDN_KATE_BUSH, new ModificationItem[]
+            { modifyOp } );
+
+        // Determine modifyTimestamp after modification
+        list = sysRoot.search( RDN_KATE_BUSH, "(objectClass=*)", controls );
+        result = list.next();
+        attributes = result.getAttributes();
+        modifyTimestamp = attributes.get( "modifyTimestamp" );
+        assertNotNull( modifyTimestamp );
+        String newTimestamp = modifyTimestamp.get().toString();
+        
+        // assert the value has changed
+        assertFalse( oldTimestamp.equals( newTimestamp ) );
+    }
+
+
+    /**
+     * Try to add modifiersName attribute to an entry
+     *
+     * @throws NamingException on error
+     */
+    @Test
+    public void testModifyOperationalAttributeAdd() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        createData( sysRoot );
+
+        ModificationItem modifyOp = new ModificationItem( DirContext.ADD_ATTRIBUTE, new BasicAttribute(
+            "modifiersName", "cn=Tori Amos,dc=example,dc=com" ) );
+
+        try
+        {
+            sysRoot.modifyAttributes( RDN_KATE_BUSH, new ModificationItem[]
+                { modifyOp } );
+            fail( "modification of entry should fail" );
+        }
+        catch ( InvalidAttributeValueException e )
+        {
+            // expected
+        }
+        catch ( NoPermissionException e )
+        {
+            // expected
+        }
+    }
+
+
+    /**
+     * Try to remove creatorsName attribute from an entry.
+     *
+     * @throws NamingException on error
+     */
+    @Test
+    public void testModifyOperationalAttributeRemove() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        createData( sysRoot );
+
+        ModificationItem modifyOp = new ModificationItem( DirContext.REMOVE_ATTRIBUTE, new BasicAttribute(
+            "creatorsName" ) );
+
+        try
+        {
+            sysRoot.modifyAttributes( RDN_KATE_BUSH, new ModificationItem[]
+                { modifyOp } );
+            fail( "modification of entry should fail" );
+        }
+        catch ( InvalidAttributeValueException e )
+        {
+            // expected
+        }
+        catch ( NoPermissionException e )
+        {
+            // expected
+        }
+    }
+
+
+    /**
+     * Try to replace creatorsName attribute on an entry.
+     *
+     * @throws NamingException on error
+     */
+    @Test
+    public void testModifyOperationalAttributeReplace() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        createData( sysRoot );
+
+        ModificationItem modifyOp = new ModificationItemImpl( DirContext.REPLACE_ATTRIBUTE, new AttributeImpl(
+            "creatorsName", "cn=Tori Amos,dc=example,dc=com" ) );
+
+        try
+        {
+            sysRoot.modifyAttributes( RDN_KATE_BUSH, new ModificationItem[]
+                { modifyOp } );
+            fail( "modification of entry should fail" );
+        }
+        catch ( InvalidAttributeValueException e )
+        {
+            // expected
+        }
+        catch ( NoPermissionException e )
+        {
+            // expected
+        }
+    }
+}
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/partition/PartitionIT.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/partition/PartitionIT.java
new file mode 100644
index 0000000..d412a08
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/partition/PartitionIT.java
@@ -0,0 +1,177 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition;
+
+
+import java.util.HashMap;
+
+import org.apache.directory.server.core.DefaultDirectoryService;
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.entry.DefaultServerEntry;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.integ.CiRunner;
+import org.apache.directory.server.core.integ.DirectoryServiceFactory;
+import org.apache.directory.server.core.integ.annotations.Factory;
+import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmPartition;
+
+import static org.apache.directory.server.core.integ.IntegrationUtils.getRootContext;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.ldap.LdapContext;
+
+
+
+/**
+ * Test cases for partition handling.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+@RunWith ( CiRunner.class )
+@Factory ( PartitionIT.Factory.class )
+public final class PartitionIT
+{
+    private static final Logger LOG = LoggerFactory.getLogger( PartitionIT.class );
+    public static DirectoryService service;
+
+    
+    /**
+     * Creates a DirectoryService configured with two separate dc=com based 
+     * domains to test multiple partitions.
+     */
+    public static class Factory implements DirectoryServiceFactory
+    {
+		public DirectoryService newInstance() throws NamingException 
+		{
+            DirectoryService service = new DefaultDirectoryService();
+            service.getChangeLog().setEnabled( true );
+            
+            Partition foo = new JdbmPartition();
+            foo.setId( "foo" );
+            foo.setSuffix( "dc=foo,dc=com" );
+            LdapDN contextDn = new LdapDN( "dc=foo,dc=com" );
+            contextDn.normalize( service.getRegistries().getAttributeTypeRegistry().getNormalizerMapping() );
+            ServerEntry contextEntry = new DefaultServerEntry( service.getRegistries(), contextDn );
+            contextEntry.add( "objectClass", "top", "domain" );
+            contextEntry.add( "dc", "foo" );
+            foo.setContextEntry( contextEntry );
+            service.addPartition( foo );
+            
+            Partition bar = new JdbmPartition();
+            bar.setId( "bar" );
+            bar.setSuffix( "dc=bar,dc=com" );
+            contextDn = new LdapDN( "dc=bar,dc=com" );
+            contextDn.normalize( service.getRegistries().getAttributeTypeRegistry().getNormalizerMapping() );
+            contextEntry = new DefaultServerEntry( service.getRegistries(), contextDn );
+            contextEntry.add( "objectClass", "top", "domain" );
+            contextEntry.add( "dc", "bar" );
+            bar.setContextEntry( contextEntry );
+            service.addPartition( bar );
+            
+            return service;
+		}
+    }
+    
+
+    /**
+     * Test case to weed out issue in DIRSERVER-1118.
+     *
+     * @see https://issues.apache.org/jira/browse/DIRSERVER-1118
+     */
+    @Test
+    public void testDIRSERVER_1118() throws Exception
+    {
+        /*
+         * Confirm the presence of the partitions foo and bar through DS API
+         */
+        HashMap<String, Partition> partitionMap = new HashMap<String, Partition>();
+        
+        for ( Partition partition : service.getPartitions() )
+        {
+            LOG.debug( "partition id = {}", partition.getId() );
+            partitionMap.put( partition.getId(), partition );
+        }
+        
+        assertNotNull( partitionMap.containsKey( "foo" ) );
+        assertNotNull( partitionMap.containsKey( "bar" ) );
+
+        /*
+         * Confirm presence and publishing of foo and bar partitions as 
+         * namingContexts as values innamingContexts attribute of the rootDSE
+         */
+        LdapContext rootDSE = getRootContext( service );
+        Attribute namingContexts = rootDSE.getAttributes( "", 
+            new String[] { "namingContexts" } ).get( "namingContexts" );
+        assertTrue( namingContexts.contains( "dc=foo,dc=com" ) );
+        assertTrue( namingContexts.contains( "dc=bar,dc=com" ) );
+        LOG.debug( "Found both dc=foo,dc=com and dc=bar,dc=com in namingContexts" );
+        
+        /*
+         * Add, lookup, then delete entry in both foo and bar partitions
+         */
+        addLookupDelete( "dc=foo,dc=com" );
+        addLookupDelete( "dc=bar,dc=com" );
+    }
+    
+
+    /**
+     * Given the suffix DN of a partition this method will add an entry, look 
+     * it up, then delete it making sure all checks out.
+     *
+     * @param partitionSuffix the DN of the partition suffix
+     */
+    public void addLookupDelete( String partitionSuffix ) throws Exception
+    {
+        LdapContext rootDSE = getRootContext( service );
+        Attributes attrs = new AttributesImpl( "objectClass", "organizationalUnit", true );
+        attrs.put( "ou", "people" );
+        String entryDn = "ou=people," + partitionSuffix;
+        rootDSE.createSubcontext( entryDn, attrs );
+        LOG.debug( "added entry {} to partition {}", entryDn, partitionSuffix );
+        
+        Attributes reloaded = rootDSE.getAttributes( entryDn );
+        assertNotNull( reloaded );
+        assertTrue( reloaded.get( "ou" ).contains( "people" ) );
+        LOG.debug( "looked up entry {} from partition {}", entryDn, partitionSuffix );
+        
+        rootDSE.destroySubcontext( entryDn );
+        try
+        {
+            rootDSE.getAttributes( entryDn );
+            fail( "should never get here" );
+        }
+        catch ( Exception e )
+        {
+            LOG.debug( "Successfully deleted entry {} from partition {}", entryDn, partitionSuffix );
+        }
+    }
+}
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/prefs/PreferencesIT.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/prefs/PreferencesIT.java
new file mode 100644
index 0000000..a0d0723
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/prefs/PreferencesIT.java
@@ -0,0 +1,214 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.prefs;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.integ.CiRunner;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertNotNull;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.prefs.BackingStoreException;
+import java.util.prefs.Preferences;
+
+
+/**
+ * Tests the ServerSystemPreferences class.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+@RunWith ( CiRunner.class )
+public class PreferencesIT
+{
+    public static DirectoryService service;
+
+
+    @Test
+    public void testSystemRoot()
+    {
+        ServerPreferencesFactory factory = new ServerPreferencesFactory( service );
+        Preferences prefs = factory.systemRoot();
+
+        assertNotNull( prefs );
+        assertEquals( "sysPrefRoot", prefs.get( "prefNodeName", "default value" ) );
+    }
+
+
+    /**
+     * Tests to make sure the system preferences root has entry (test, abc123).
+     *
+     * @throws Exception if there are failures with the store
+     */
+    @Test
+    public void testRoot() throws Exception
+    {
+        ServerSystemPreferences prefs = new ServerSystemPreferences( service );
+        assertEquals( "sysPrefRoot", prefs.get( "prefNodeName", "not the value" ) );
+    }
+
+
+    /**
+     * Tests the creation and use of a new preferences node.
+     *
+     * @throws BackingStoreException if there are failures with the store
+     */
+    @Test
+    public void testCreate() throws BackingStoreException
+    {
+        ServerSystemPreferences prefs = new ServerSystemPreferences( service );
+        Preferences testNode = prefs.node( "testNode" );
+
+        testNode.put( "cn", "testNodeValue" );
+        testNode.sync();
+    }
+
+
+    /**
+     * Tests the creation and use of a new preferences node.
+     *
+     * @throws BackingStoreException if there are failures with the store
+     */
+    @Test
+    public void testCreateAndSetBoolean() throws BackingStoreException
+    {
+        ServerSystemPreferences prefs = new ServerSystemPreferences( service );
+        Preferences testNode = prefs.node( "testNode" );
+        testNode.putBoolean( "cn", false );
+        testNode.sync();
+        testNode = prefs.node( "testNode" );
+        assertEquals( false, testNode.getBoolean( "cn", false ) );
+    }
+
+
+    /**
+     * Tests the creation and use of a new preferences node.
+     *
+     * @throws BackingStoreException if there are failures with the store
+     */
+    /* TODO: Temporarily commented until we get a clear status about this package
+    public void testCreateAndSetByteArray() throws BackingStoreException
+    {
+        byte[] jpegValue = new byte[]
+                                   { (byte)0xFF, (byte)0xD8, (byte)0xFF, (byte)0xE0, 0x01, 0x02, 'J', 'F', 'I', 'F', 0x00, 0x45, 0x23, 0x7d, 0x7f };
+        Preferences testNode = prefs.node( "testNode" );
+        testNode.putByteArray( "jpegPhoto", jpegValue );
+        testNode.sync();
+        testNode = prefs.node( "testNode" );
+    }
+    */
+
+
+    /**
+     * Tests the creation and use of a new preferences node.
+     *
+     * @throws BackingStoreException if there are failures with the store
+     */
+    @Test
+    public void testCreateAndSetDouble() throws BackingStoreException
+    {
+        ServerSystemPreferences prefs = new ServerSystemPreferences( service );
+        Preferences testNode = prefs.node( "testNode" );
+        testNode.putDouble( "cn", 3.14 );
+        testNode.sync();
+        testNode = prefs.node( "testNode" );
+        assertTrue( 3.14 == testNode.getDouble( "cn", 9.20 ) );
+    }
+
+
+    /**
+     * Tests the creation and use of a new preferences node.
+     *
+     * @throws BackingStoreException if there are failures with the store
+     */
+    @Test
+    public void testCreateAndSetFloat() throws BackingStoreException
+    {
+        ServerSystemPreferences prefs = new ServerSystemPreferences( service );
+        Preferences testNode = prefs.node( "testNode" );
+        testNode.putFloat( "cn", ( float ) 9.20 );
+        testNode.sync();
+        testNode = prefs.node( "testNode" );
+        assertTrue( ( float ) 9.20 == testNode.getFloat( "cn", ( float ) 9.20 ) );
+    }
+
+
+    /**
+     * Tests the creation and use of a new preferences node.
+     *
+     * @throws BackingStoreException if there are failures with the store
+     */
+    @Test
+    public void testCreateAndSetInt() throws BackingStoreException
+    {
+        ServerSystemPreferences prefs = new ServerSystemPreferences( service );
+        Preferences testNode = prefs.node( "testNode" );
+        testNode.putInt( "cn", 345 );
+        testNode.sync();
+        testNode = prefs.node( "testNode" );
+        assertTrue( 345 == testNode.getInt( "cn", 345 ) );
+    }
+
+
+    /**
+     * Tests the creation and use of a new preferences node.
+     *
+     * @throws BackingStoreException if there are failures with the store
+     */
+    @Test
+    public void testCreateAndSetLong() throws BackingStoreException
+    {
+        ServerSystemPreferences prefs = new ServerSystemPreferences( service );
+        Preferences testNode = prefs.node( "testNode" );
+        testNode.putLong( "cn", 75449559185447L );
+        testNode.sync();
+        testNode = prefs.node( "testNode" );
+        assertTrue( 75449559185447L == testNode.getLong( "cn", 75449559185447L ) );
+    }
+
+
+    /**
+     * Tests the creation and use of a new preferences node.
+     *
+     * @throws BackingStoreException if there are failures with the store
+     */
+    @Test
+    public void testCreateAndRemove() throws BackingStoreException
+    {
+        ServerSystemPreferences prefs = new ServerSystemPreferences( service );
+        Preferences testNode = prefs.node( "testNode" );
+
+        testNode.put( "cn", "testNodeValue" );
+        testNode.putInt( "roomNumber", 345 );
+        testNode.sync();
+
+        testNode = prefs.node( "testNode" );
+        assertEquals( 345, testNode.getInt( "roomNumber", 87 ) );
+        testNode.remove( "cn" );
+        testNode.remove( "roomNumber" );
+        testNode.sync();
+
+        assertEquals( "no value", testNode.get( "cn", "no value" ) );
+        assertEquals( "no value", testNode.get( "roomNumber", "no value" ) );
+    }
+}
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/schema/MetaAttributeTypeHandlerIT.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/schema/MetaAttributeTypeHandlerIT.java
new file mode 100644
index 0000000..a535247
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/schema/MetaAttributeTypeHandlerIT.java
@@ -0,0 +1,534 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.schema;
+
+
+import org.apache.directory.server.constants.MetaSchemaConstants;
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.integ.CiRunner;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getSchemaContext;
+import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.exception.LdapInvalidNameException;
+import org.apache.directory.shared.ldap.exception.LdapOperationNotSupportedException;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.message.ModificationItemImpl;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.fail;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.ldap.LdapContext;
+
+
+/**
+ * A test case which tests the addition of various schema elements
+ * to the ldap server.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+@RunWith ( CiRunner.class )
+public class MetaAttributeTypeHandlerIT
+{
+    private static final String DESCRIPTION0 = "A test attributeType";
+    private static final String DESCRIPTION1 = "An alternate description";
+
+    private static final String INTEGER_SYNTAX_OID = "1.3.6.1.4.1.1466.115.121.1.27";
+    private static final String DIRSTR_SYNTAX_OID = "1.3.6.1.4.1.1466.115.121.1.15";
+    
+    private static final String OID = "1.3.6.1.4.1.18060.0.4.0.2.100000";
+    private static final String NEW_OID = "1.3.6.1.4.1.18060.0.4.0.2.100001";
+    private static final String DEPENDEE_OID = "1.3.6.1.4.1.18060.0.4.0.2.100002";
+
+
+    public static DirectoryService service;
+
+    
+    /**
+     * Gets relative DN to ou=schema.
+     *
+     * @param schemaName the name of the schema
+     * @return the dn of the a schema's attributeType entity container
+     * @throws NamingException on failure
+     */
+    private LdapDN getAttributeTypeContainer( String schemaName ) throws NamingException
+    {
+        return new LdapDN( "ou=attributeTypes,cn=" + schemaName );
+    }
+
+
+    private static AttributeTypeRegistry getAttributeTypeRegistry()
+    {
+        return service.getRegistries().getAttributeTypeRegistry();
+    }
+    
+    
+    // ----------------------------------------------------------------------
+    // Test all core methods with normal operational pathways
+    // ----------------------------------------------------------------------
+
+    
+    @Test
+    public void testAddAttributeType() throws NamingException
+    {
+        Attributes attrs = new AttributesImpl();
+        Attribute oc = new AttributeImpl( SchemaConstants.OBJECT_CLASS_AT, "top" );
+        oc.add( MetaSchemaConstants.META_TOP_OC );
+        oc.add( MetaSchemaConstants.META_ATTRIBUTE_TYPE_OC );
+        attrs.put( oc );
+        attrs.put( MetaSchemaConstants.M_OID_AT, OID );
+        attrs.put( MetaSchemaConstants.M_SYNTAX_AT, INTEGER_SYNTAX_OID );
+        attrs.put( MetaSchemaConstants.M_DESCRIPTION_AT, DESCRIPTION0 );
+        attrs.put( MetaSchemaConstants.M_EQUALITY_AT, "caseIgnoreMatch" );
+        attrs.put( MetaSchemaConstants.M_SINGLE_VALUE_AT, "FALSE" );
+        attrs.put( MetaSchemaConstants.M_USAGE_AT, "directoryOperation" );
+        
+        LdapDN dn = getAttributeTypeContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        getSchemaContext( service ).createSubcontext( dn, attrs );
+        
+        assertTrue( service.getRegistries().getAttributeTypeRegistry().hasAttributeType( OID ) );
+        assertEquals( getAttributeTypeRegistry().getSchemaName( OID ), "apachemeta" );
+    }
+    
+    
+    @Test
+    public void testDeleteAttributeType() throws NamingException
+    {
+        LdapDN dn = getAttributeTypeContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        testAddAttributeType();
+        
+        getSchemaContext( service ).destroySubcontext( dn );
+
+        assertFalse( "attributeType should be removed from the registry after being deleted", 
+            getAttributeTypeRegistry().hasAttributeType( OID ) );
+        
+        try
+        {
+            getAttributeTypeRegistry().lookup( OID );
+            fail( "attributeType lookup should fail after deleting it" );
+        }
+        catch( NamingException e )
+        {
+        }
+    }
+
+
+    @Test
+    public void testRenameAttributeType() throws NamingException
+    {
+        LdapContext schemaRoot = getSchemaContext( service );
+        LdapDN dn = getAttributeTypeContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        testAddAttributeType();
+        
+        LdapDN newdn = getAttributeTypeContainer( "apachemeta" );
+        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + NEW_OID );
+        schemaRoot.rename( dn, newdn );
+
+        assertFalse( "old attributeType OID should be removed from the registry after being renamed", 
+            getAttributeTypeRegistry().hasAttributeType( OID ) );
+        
+        try
+        {
+            getAttributeTypeRegistry().lookup( OID );
+            fail( "attributeType lookup should fail after renaming the attributeType" );
+        }
+        catch( NamingException e )
+        {
+        }
+
+        assertTrue( getAttributeTypeRegistry().hasAttributeType( NEW_OID ) );
+    }
+
+
+    @Test
+    public void testMoveAttributeType() throws NamingException
+    {
+        testAddAttributeType();
+        
+        LdapDN dn = getAttributeTypeContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+
+        LdapDN newdn = getAttributeTypeContainer( "apache" );
+        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        
+        getSchemaContext( service ).rename( dn, newdn );
+
+        assertTrue( "attributeType OID should still be present",
+                getAttributeTypeRegistry().hasAttributeType( OID ) );
+        
+        assertEquals( "attributeType schema should be set to apache not apachemeta", 
+            getAttributeTypeRegistry().getSchemaName( OID ), "apache" );
+    }
+
+
+    @Test
+    public void testMoveAttributeTypeAndChangeRdn() throws NamingException
+    {
+        testAddAttributeType();
+        
+        LdapDN dn = getAttributeTypeContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+
+        LdapDN newdn = getAttributeTypeContainer( "apache" );
+        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + NEW_OID );
+        
+        getSchemaContext( service ).rename( dn, newdn );
+
+        assertFalse( "old attributeType OID should NOT be present", 
+            getAttributeTypeRegistry().hasAttributeType( OID ) );
+        
+        assertTrue( "new attributeType OID should be present", 
+            getAttributeTypeRegistry().hasAttributeType( NEW_OID ) );
+        
+        assertEquals( "attributeType with new oid should have schema set to apache NOT apachemeta", 
+            getAttributeTypeRegistry().getSchemaName( NEW_OID ), "apache" );
+    }
+
+    
+    @Test
+    public void testModifyAttributeTypeWithModificationItems() throws NamingException
+    {
+        testAddAttributeType();
+        
+        AttributeType at = getAttributeTypeRegistry().lookup( OID );
+        assertEquals( at.getDescription(), DESCRIPTION0 );
+        assertEquals( at.getSyntax().getOid(), INTEGER_SYNTAX_OID );
+
+        LdapDN dn = getAttributeTypeContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        
+        ModificationItemImpl[] mods = new ModificationItemImpl[2];
+        Attribute attr = new AttributeImpl( MetaSchemaConstants.M_DESCRIPTION_AT, DESCRIPTION1 );
+        mods[0] = new ModificationItemImpl( DirContext.REPLACE_ATTRIBUTE, attr );
+        attr = new AttributeImpl( MetaSchemaConstants.M_SYNTAX_AT, DIRSTR_SYNTAX_OID );
+        mods[1] = new ModificationItemImpl( DirContext.REPLACE_ATTRIBUTE, attr );
+        getSchemaContext( service ).modifyAttributes( dn, mods );
+
+        assertTrue( "attributeType OID should still be present", 
+            getAttributeTypeRegistry().hasAttributeType( OID ) );
+        
+        assertEquals( "attributeType schema should be set to apachemeta", 
+            getAttributeTypeRegistry().getSchemaName( OID ), "apachemeta" );
+        
+        at = getAttributeTypeRegistry().lookup( OID );
+        assertEquals( at.getDescription(), DESCRIPTION1 );
+        assertEquals( at.getSyntax().getOid(), DIRSTR_SYNTAX_OID );
+    }
+
+    
+    @Test
+    public void testModifyAttributeTypeWithAttributes() throws NamingException
+    {
+        testAddAttributeType();
+        
+        AttributeType at = getAttributeTypeRegistry().lookup( OID );
+        assertEquals( at.getDescription(), DESCRIPTION0 );
+        assertEquals( at.getSyntax().getOid(), INTEGER_SYNTAX_OID );
+
+        LdapDN dn = getAttributeTypeContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        
+        Attributes mods = new AttributesImpl();
+        mods.put( MetaSchemaConstants.M_DESCRIPTION_AT, DESCRIPTION1 );
+        mods.put( MetaSchemaConstants.M_SYNTAX_AT, DIRSTR_SYNTAX_OID );
+        getSchemaContext( service ).modifyAttributes( dn, DirContext.REPLACE_ATTRIBUTE, mods );
+
+        assertTrue( "attributeType OID should still be present", 
+            getAttributeTypeRegistry().hasAttributeType( OID ) );
+        
+        assertEquals( "attributeType schema should be set to apachemeta", 
+            getAttributeTypeRegistry().getSchemaName( OID ), "apachemeta" );
+
+        at = getAttributeTypeRegistry().lookup( OID );
+        assertEquals( at.getDescription(), DESCRIPTION1 );
+        assertEquals( at.getSyntax().getOid(), DIRSTR_SYNTAX_OID );
+    }
+    
+
+    // ----------------------------------------------------------------------
+    // Test move, rename, and delete when a MR exists and uses the Normalizer
+    // ----------------------------------------------------------------------
+
+    
+    private void addDependeeAttributeType() throws NamingException
+    {
+        Attributes attrs = new AttributesImpl();
+        Attribute oc = new AttributeImpl( SchemaConstants.OBJECT_CLASS_AT, "top" );
+        oc.add( MetaSchemaConstants.META_TOP_OC );
+        oc.add( MetaSchemaConstants.META_ATTRIBUTE_TYPE_OC );
+        attrs.put( oc );
+        attrs.put( MetaSchemaConstants.M_OID_AT, DEPENDEE_OID );
+        attrs.put( MetaSchemaConstants.M_SYNTAX_AT, INTEGER_SYNTAX_OID );
+        attrs.put( MetaSchemaConstants.M_DESCRIPTION_AT, DESCRIPTION0 );
+        attrs.put( MetaSchemaConstants.M_EQUALITY_AT, "caseIgnoreMatch" );
+        attrs.put( MetaSchemaConstants.M_SINGLE_VALUE_AT, "FALSE" );
+        attrs.put( MetaSchemaConstants.M_USAGE_AT, "directoryOperation" );
+        attrs.put( MetaSchemaConstants.M_SUP_ATTRIBUTE_TYPE_AT, OID );
+        
+        LdapDN dn = getAttributeTypeContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + DEPENDEE_OID );
+        getSchemaContext( service ).createSubcontext( dn, attrs );
+        
+        assertTrue( getAttributeTypeRegistry().hasAttributeType( DEPENDEE_OID ) );
+        assertEquals( getAttributeTypeRegistry().getSchemaName( DEPENDEE_OID ), "apachemeta" );
+    }
+
+
+    @Test
+    public void testDeleteAttributeTypeWhenInUse() throws NamingException
+    {
+        LdapDN dn = getAttributeTypeContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        testAddAttributeType();
+        addDependeeAttributeType();
+        
+        try
+        {
+            getSchemaContext( service ).destroySubcontext( dn );
+            fail( "should not be able to delete a attributeType in use" );
+        }
+        catch( LdapOperationNotSupportedException e ) 
+        {
+            assertEquals( e.getResultCode(), ResultCodeEnum.UNWILLING_TO_PERFORM );
+        }
+
+        assertTrue( "attributeType should still be in the registry after delete failure", 
+            getAttributeTypeRegistry().hasAttributeType( OID ) );
+    }
+    
+    
+    @Test
+    public void testMoveAttributeTypeWhenInUse() throws NamingException
+    {
+        testAddAttributeType();
+        addDependeeAttributeType();
+        
+        LdapDN dn = getAttributeTypeContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+
+        LdapDN newdn = getAttributeTypeContainer( "apache" );
+        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        
+        try
+        {
+            getSchemaContext( service ).rename( dn, newdn );
+            fail( "should not be able to move a attributeType in use" );
+        }
+        catch( LdapOperationNotSupportedException e ) 
+        {
+            assertEquals( e.getResultCode(), ResultCodeEnum.UNWILLING_TO_PERFORM );
+        }
+
+        assertTrue( "attributeType should still be in the registry after move failure", 
+            getAttributeTypeRegistry().hasAttributeType( OID ) );
+    }
+
+
+    @Test
+    public void testMoveAttributeTypeAndChangeRdnWhenInUse() throws NamingException
+    {
+        testAddAttributeType();
+        addDependeeAttributeType();
+        
+        LdapDN dn = getAttributeTypeContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+
+        LdapDN newdn = getAttributeTypeContainer( "apache" );
+        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + NEW_OID );
+        
+        try
+        {
+            getSchemaContext( service ).rename( dn, newdn );
+            fail( "should not be able to move a attributeType in use" );
+        }
+        catch( LdapOperationNotSupportedException e ) 
+        {
+            assertEquals( e.getResultCode(), ResultCodeEnum.UNWILLING_TO_PERFORM );
+        }
+
+        assertTrue( "attributeType should still be in the registry after move failure", 
+            getAttributeTypeRegistry().hasAttributeType( OID ) );
+    }
+
+    
+    @Test
+    public void testRenameAttributeTypeWhenInUse() throws NamingException
+    {
+        LdapDN dn = getAttributeTypeContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        testAddAttributeType();
+        addDependeeAttributeType();
+        
+        LdapDN newdn = getAttributeTypeContainer( "apachemeta" );
+        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + NEW_OID );
+        
+        try
+        {
+            getSchemaContext( service ).rename( dn, newdn );
+            fail( "should not be able to rename a attributeType in use" );
+        }
+        catch( LdapOperationNotSupportedException e ) 
+        {
+            assertEquals( e.getResultCode(), ResultCodeEnum.UNWILLING_TO_PERFORM );
+        }
+
+        assertTrue( "attributeType should still be in the registry after rename failure", 
+            getAttributeTypeRegistry().hasAttributeType( OID ) );
+    }
+
+
+    // ----------------------------------------------------------------------
+    // Let's try some freaky stuff
+    // ----------------------------------------------------------------------
+
+
+    @Test
+    public void testMoveAttributeTypeToTop() throws NamingException
+    {
+        testAddAttributeType();
+        
+        LdapDN dn = getAttributeTypeContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+
+        LdapDN top = new LdapDN();
+        top.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        
+        try
+        {
+            getSchemaContext( service ).rename( dn, top );
+            fail( "should not be able to move a attributeType up to ou=schema" );
+        }
+        catch( LdapInvalidNameException e ) 
+        {
+            assertEquals( e.getResultCode(), ResultCodeEnum.NAMING_VIOLATION );
+        }
+
+        assertTrue( "attributeType should still be in the registry after move failure", 
+            getAttributeTypeRegistry().hasAttributeType( OID ) );
+    }
+
+
+    @Test
+    public void testMoveAttributeTypeToComparatorContainer() throws NamingException
+    {
+        testAddAttributeType();
+        
+        LdapDN dn = getAttributeTypeContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+
+        LdapDN newdn = new LdapDN( "ou=comparators,cn=apachemeta" );
+        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        
+        try
+        {
+            getSchemaContext( service ).rename( dn, newdn );
+            fail( "should not be able to move a attributeType into comparators container" );
+        }
+        catch( LdapInvalidNameException e ) 
+        {
+            assertEquals( e.getResultCode(), ResultCodeEnum.NAMING_VIOLATION );
+        }
+
+        assertTrue( "attributeType should still be in the registry after move failure", 
+            getAttributeTypeRegistry().hasAttributeType( OID ) );
+    }
+    
+    
+    @Test
+    public void testAddAttributeTypeToDisabledSchema() throws NamingException
+    {
+        Attributes attrs = new AttributesImpl();
+        Attribute oc = new AttributeImpl( SchemaConstants.OBJECT_CLASS_AT, "top" );
+        oc.add( MetaSchemaConstants.META_TOP_OC );
+        oc.add( MetaSchemaConstants.META_ATTRIBUTE_TYPE_OC );
+        attrs.put( oc );
+        attrs.put( MetaSchemaConstants.M_OID_AT, OID );
+        attrs.put( MetaSchemaConstants.M_SYNTAX_AT, INTEGER_SYNTAX_OID );
+        attrs.put( MetaSchemaConstants.M_DESCRIPTION_AT, DESCRIPTION0 );
+        attrs.put( MetaSchemaConstants.M_EQUALITY_AT, "caseIgnoreMatch" );
+        attrs.put( MetaSchemaConstants.M_SINGLE_VALUE_AT, "FALSE" );
+        attrs.put( MetaSchemaConstants.M_USAGE_AT, "directoryOperation" );
+        
+        LdapDN dn = getAttributeTypeContainer( "nis" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        getSchemaContext( service ).createSubcontext( dn, attrs );
+        
+        assertFalse( "adding new attributeType to disabled schema should not register it into the registries", 
+            getAttributeTypeRegistry().hasAttributeType( OID ) );
+    }
+
+
+    @Test
+    public void testMoveAttributeTypeToDisabledSchema() throws NamingException
+    {
+        testAddAttributeType();
+        
+        LdapDN dn = getAttributeTypeContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+
+        // nis is inactive by default
+        LdapDN newdn = getAttributeTypeContainer( "nis" );
+        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        
+        getSchemaContext( service ).rename( dn, newdn );
+
+        assertFalse( "attributeType OID should no longer be present", 
+            getAttributeTypeRegistry().hasAttributeType( OID ) );
+    }
+
+
+    @Test
+    public void testMoveMatchingRuleToEnabledSchema() throws NamingException
+    {
+        testAddAttributeTypeToDisabledSchema();
+        
+        // nis is inactive by default
+        LdapDN dn = getAttributeTypeContainer( "nis" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+
+        assertFalse( "attributeType OID should NOT be present when added to disabled nis schema", 
+            getAttributeTypeRegistry().hasAttributeType( OID ) );
+
+        LdapDN newdn = getAttributeTypeContainer( "apachemeta" );
+        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        
+        getSchemaContext( service ).rename( dn, newdn );
+
+        assertTrue( "attributeType OID should be present when moved to enabled schema", 
+            getAttributeTypeRegistry().hasAttributeType( OID ) );
+        
+        assertEquals( "attributeType should be in apachemeta schema after move", 
+            getAttributeTypeRegistry().getSchemaName( OID ), "apachemeta" );
+    }
+}
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/schema/MetaComparatorHandlerIT.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/schema/MetaComparatorHandlerIT.java
new file mode 100644
index 0000000..77f9448
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/schema/MetaComparatorHandlerIT.java
@@ -0,0 +1,608 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.schema;
+
+
+import jdbm.helper.IntegerComparator;
+import jdbm.helper.StringComparator;
+import org.apache.directory.server.constants.MetaSchemaConstants;
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.integ.CiRunner;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getSchemaContext;
+import org.apache.directory.server.schema.registries.ComparatorRegistry;
+import org.apache.directory.server.schema.registries.MatchingRuleRegistry;
+import org.apache.directory.server.schema.registries.OidRegistry;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.exception.LdapInvalidNameException;
+import org.apache.directory.shared.ldap.exception.LdapOperationNotSupportedException;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.message.ModificationItemImpl;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.schema.MatchingRule;
+import org.apache.directory.shared.ldap.schema.Normalizer;
+import org.apache.directory.shared.ldap.schema.Syntax;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.fail;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.util.Comparator;
+
+
+/**
+ * A test case which tests the addition of various schema elements
+ * to the ldap server.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+@RunWith ( CiRunner.class )
+public class MetaComparatorHandlerIT
+{
+    private static final String OID = "1.3.6.1.4.1.18060.0.4.0.1.100000";
+    private static final String NEW_OID = "1.3.6.1.4.1.18060.0.4.0.1.100001";
+
+
+    public static DirectoryService service;
+
+    
+    /**
+     * Gets relative DN to ou=schema.
+     *
+     * @param schemaName the name of the schema
+     * @return the dn to the ou underwhich comparators are found for a schmea
+     * @throws NamingException if there are dn construction issues
+     */
+    private LdapDN getComparatorContainer( String schemaName ) throws NamingException
+    {
+        return new LdapDN( "ou=comparators,cn=" + schemaName );
+    }
+
+
+    private static ComparatorRegistry getComparatorRegistry()
+    {
+        return service.getRegistries().getComparatorRegistry();
+    }
+    
+
+    private static MatchingRuleRegistry getMatchingRuleRegistry()
+    {
+        return service.getRegistries().getMatchingRuleRegistry();
+    }
+
+
+    private static OidRegistry getOidRegistry()
+    {
+        return service.getRegistries().getOidRegistry();
+    }
+
+
+    // ----------------------------------------------------------------------
+    // Test all core methods with normal operational pathways
+    // ----------------------------------------------------------------------
+
+
+    @Test
+    public void testAddComparator() throws NamingException
+    {
+        Attributes attrs = new AttributesImpl();
+        Attribute oc = new AttributeImpl( SchemaConstants.OBJECT_CLASS_AT, "top" );
+        oc.add( MetaSchemaConstants.META_TOP_OC );
+        oc.add( MetaSchemaConstants.META_COMPARATOR_OC );
+        attrs.put( oc );
+        attrs.put( MetaSchemaConstants.M_FQCN_AT, StringComparator.class.getName() );
+        attrs.put( MetaSchemaConstants.M_OID_AT, OID );
+        attrs.put( MetaSchemaConstants.M_DESCRIPTION_AT, "A test comparator" );
+        
+        LdapDN dn = getComparatorContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        getSchemaContext( service ).createSubcontext( dn, attrs );
+        
+        assertTrue( getComparatorRegistry().hasComparator( OID ) );
+        assertEquals( getComparatorRegistry().getSchemaName( OID ), "apachemeta" );
+        Class<?> clazz = getComparatorRegistry().lookup( OID ).getClass();
+        assertEquals( clazz, StringComparator.class );
+    }
+    
+
+    @Test
+    public void testAddComparatorWithByteCode() throws Exception
+    {
+        InputStream in = getClass().getResourceAsStream( "DummyComparator.bytecode" );
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        while ( in.available() > 0 )
+        {
+            out.write( in.read() );
+        }
+        
+        Attributes attrs = new AttributesImpl();
+        Attribute oc = new AttributeImpl( SchemaConstants.OBJECT_CLASS_AT, "top" );
+        oc.add( MetaSchemaConstants.META_TOP_OC );
+        oc.add( MetaSchemaConstants.META_COMPARATOR_OC );
+        attrs.put( oc );
+        attrs.put( MetaSchemaConstants.M_FQCN_AT, "DummyComparator" );
+        attrs.put( MetaSchemaConstants.M_BYTECODE_AT, out.toByteArray() );
+        attrs.put( MetaSchemaConstants.M_OID_AT, OID );
+        attrs.put( MetaSchemaConstants.M_DESCRIPTION_AT, "A test comparator" );
+        
+        LdapDN dn = getComparatorContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        getSchemaContext( service ).createSubcontext( dn, attrs );
+        
+        assertTrue( getComparatorRegistry().hasComparator( OID ) );
+        assertEquals( getComparatorRegistry().getSchemaName( OID ), "apachemeta" );
+        Class<?> clazz = getComparatorRegistry().lookup( OID ).getClass();
+        assertEquals( clazz.getName(), "DummyComparator" );
+    }
+    
+
+    @Test
+    public void testDeleteComparator() throws NamingException
+    {
+        LdapDN dn = getComparatorContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        testAddComparator();
+        
+        getSchemaContext( service ).destroySubcontext( dn );
+
+        assertFalse( "comparator should be removed from the registry after being deleted", 
+            getComparatorRegistry().hasComparator( OID ) );
+        
+        try
+        {
+            getComparatorRegistry().lookup( OID );
+            fail( "comparator lookup should fail after deleting the comparator" );
+        }
+        catch( NamingException e )
+        {
+        }
+    }
+
+
+    @Test
+    public void testRenameComparator() throws NamingException
+    {
+        LdapDN dn = getComparatorContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        testAddComparator();
+        
+        LdapDN newdn = getComparatorContainer( "apachemeta" );
+        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + NEW_OID );
+        getSchemaContext( service ).rename( dn, newdn );
+
+        assertFalse( "old comparator OID should be removed from the registry after being renamed", 
+            getComparatorRegistry().hasComparator( OID ) );
+        
+        try
+        {
+            getComparatorRegistry().lookup( OID );
+            fail( "comparator lookup should fail after deleting the comparator" );
+        }
+        catch( NamingException e )
+        {
+        }
+
+        assertTrue( getComparatorRegistry().hasComparator( NEW_OID ) );
+        Class<?> clazz = getComparatorRegistry().lookup( NEW_OID ).getClass();
+        assertEquals( clazz, StringComparator.class );
+    }
+
+
+    @Test
+    public void testMoveComparator() throws NamingException
+    {
+        testAddComparator();
+        
+        LdapDN dn = getComparatorContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+
+        LdapDN newdn = getComparatorContainer( "apache" );
+        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        
+        getSchemaContext( service ).rename( dn, newdn );
+
+        assertTrue( "comparator OID should still be present", 
+            getComparatorRegistry().hasComparator( OID ) );
+        
+        assertEquals( "comparator schema should be set to apache not apachemeta", 
+            getComparatorRegistry().getSchemaName( OID ), "apache" );
+
+        Class<?> clazz = getComparatorRegistry().lookup( OID ).getClass();
+        assertEquals( clazz, StringComparator.class );
+    }
+
+
+    @Test
+    public void testMoveComparatorAndChangeRdn() throws NamingException
+    {
+        testAddComparator();
+        
+        LdapDN dn = getComparatorContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+
+        LdapDN newdn = getComparatorContainer( "apache" );
+        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + NEW_OID );
+        
+        getSchemaContext( service ).rename( dn, newdn );
+
+        assertFalse( "old comparator OID should NOT be present", 
+            getComparatorRegistry().hasComparator( OID ) );
+        
+        assertTrue( "new comparator OID should be present", 
+            getComparatorRegistry().hasComparator( NEW_OID ) );
+        
+        assertEquals( "comparator with new oid should have schema set to apache NOT apachemeta", 
+            getComparatorRegistry().getSchemaName( NEW_OID ), "apache" );
+
+        Class<?> clazz = getComparatorRegistry().lookup( NEW_OID ).getClass();
+        assertEquals( clazz, StringComparator.class );
+    }
+
+
+    @Test
+    public void testModifyComparatorWithModificationItems() throws NamingException
+    {
+        testAddComparator();
+        
+        LdapDN dn = getComparatorContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        
+        ModificationItemImpl[] mods = new ModificationItemImpl[1];
+        Attribute attr = new AttributeImpl( MetaSchemaConstants.M_FQCN_AT, IntegerComparator.class.getName() );
+        mods[0] = new ModificationItemImpl( DirContext.REPLACE_ATTRIBUTE, attr );
+        getSchemaContext( service ).modifyAttributes( dn, mods );
+
+        assertTrue( "comparator OID should still be present", 
+            getComparatorRegistry().hasComparator( OID ) );
+        
+        assertEquals( "comparator schema should be set to apachemeta", 
+            getComparatorRegistry().getSchemaName( OID ), "apachemeta" );
+
+        Class<?> clazz = getComparatorRegistry().lookup( OID ).getClass();
+        assertEquals( clazz, IntegerComparator.class );
+    }
+
+
+    @Test
+    public void testModifyComparatorWithAttributes() throws NamingException
+    {
+        testAddComparator();
+        
+        LdapDN dn = getComparatorContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        
+        Attributes mods = new AttributesImpl();
+        mods.put( MetaSchemaConstants.M_FQCN_AT, IntegerComparator.class.getName() );
+        getSchemaContext( service ).modifyAttributes( dn, DirContext.REPLACE_ATTRIBUTE, mods );
+
+        assertTrue( "comparator OID should still be present", 
+            getComparatorRegistry().hasComparator( OID ) );
+        
+        assertEquals( "comparator schema should be set to apachemeta", 
+            getComparatorRegistry().getSchemaName( OID ), "apachemeta" );
+
+        Class<?> clazz = getComparatorRegistry().lookup( OID ).getClass();
+        assertEquals( clazz, IntegerComparator.class );
+    }
+    
+
+    // ----------------------------------------------------------------------
+    // Test move, rename, and delete when a MR exists and uses the Comparator
+    // ----------------------------------------------------------------------
+
+    
+    @Test
+    public void testDeleteComparatorWhenInUse() throws NamingException
+    {
+        LdapDN dn = getComparatorContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        testAddComparator();
+        getMatchingRuleRegistry().register( new DummyMR() );
+        
+        try
+        {
+            getSchemaContext( service ).destroySubcontext( dn );
+            fail( "should not be able to delete a comparator in use" );
+        }
+        catch( LdapOperationNotSupportedException e ) 
+        {
+            assertEquals( e.getResultCode(), ResultCodeEnum.UNWILLING_TO_PERFORM );
+        }
+
+        assertTrue( "comparator should still be in the registry after delete failure", 
+            getComparatorRegistry().hasComparator( OID ) );
+        getMatchingRuleRegistry().unregister( OID );
+        getOidRegistry().unregister( OID );
+    }
+    
+    
+    @Test
+    public void testMoveComparatorWhenInUse() throws NamingException
+    {
+        testAddComparator();
+        getMatchingRuleRegistry().register( new DummyMR() );
+        
+        LdapDN dn = getComparatorContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+
+        LdapDN newdn = getComparatorContainer( "apache" );
+        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        
+        try
+        {
+            getSchemaContext( service ).rename( dn, newdn );
+            fail( "should not be able to move a comparator in use" );
+        }
+        catch( LdapOperationNotSupportedException e ) 
+        {
+            assertEquals( e.getResultCode(), ResultCodeEnum.UNWILLING_TO_PERFORM );
+        }
+
+        assertTrue( "comparator should still be in the registry after move failure", 
+            getComparatorRegistry().hasComparator( OID ) );
+        getMatchingRuleRegistry().unregister( OID );
+        getOidRegistry().unregister( OID );
+    }
+
+
+    @Test
+    public void testMoveComparatorAndChangeRdnWhenInUse() throws NamingException
+    {
+        testAddComparator();
+        getMatchingRuleRegistry().register( new DummyMR() );
+        
+        LdapDN dn = getComparatorContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+
+        LdapDN newdn = getComparatorContainer( "apache" );
+        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + NEW_OID );
+        
+        try
+        {
+            getSchemaContext( service ).rename( dn, newdn );
+            fail( "should not be able to move a comparator in use" );
+        }
+        catch( LdapOperationNotSupportedException e ) 
+        {
+            assertEquals( e.getResultCode(), ResultCodeEnum.UNWILLING_TO_PERFORM );
+        }
+
+        assertTrue( "comparator should still be in the registry after move failure", 
+            getComparatorRegistry().hasComparator( OID ) );
+        getMatchingRuleRegistry().unregister( OID );
+        getOidRegistry().unregister( OID );
+    }
+
+    
+    @Test
+    public void testRenameComparatorWhenInUse() throws NamingException
+    {
+        LdapDN dn = getComparatorContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        testAddComparator();
+        getMatchingRuleRegistry().register( new DummyMR() );
+        
+        LdapDN newdn = getComparatorContainer( "apachemeta" );
+        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + NEW_OID );
+        
+        try
+        {
+            getSchemaContext( service ).rename( dn, newdn );
+            fail( "should not be able to rename a comparator in use" );
+        }
+        catch( LdapOperationNotSupportedException e ) 
+        {
+            assertEquals( e.getResultCode(), ResultCodeEnum.UNWILLING_TO_PERFORM );
+        }
+
+        assertTrue( "comparator should still be in the registry after rename failure", 
+            getComparatorRegistry().hasComparator( OID ) );
+        getMatchingRuleRegistry().unregister( OID );
+        getOidRegistry().unregister( OID );
+    }
+
+
+    // ----------------------------------------------------------------------
+    // Let's try some freaky stuff
+    // ----------------------------------------------------------------------
+
+
+    @Test
+    public void testMoveComparatorToTop() throws NamingException
+    {
+        testAddComparator();
+        
+        LdapDN dn = getComparatorContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+
+        LdapDN top = new LdapDN();
+        top.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        
+        try
+        {
+            getSchemaContext( service ).rename( dn, top );
+            fail( "should not be able to move a comparator up to ou=schema" );
+        }
+        catch( LdapInvalidNameException e ) 
+        {
+            assertEquals( e.getResultCode(), ResultCodeEnum.NAMING_VIOLATION );
+        }
+
+        assertTrue( "comparator should still be in the registry after move failure", 
+            getComparatorRegistry().hasComparator( OID ) );
+    }
+
+
+    @Test
+    public void testMoveComparatorToNormalizers() throws NamingException
+    {
+        testAddComparator();
+        
+        LdapDN dn = getComparatorContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+
+        LdapDN newdn = new LdapDN( "ou=normalizers,cn=apachemeta" );
+        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        
+        try
+        {
+            getSchemaContext( service ).rename( dn, newdn );
+            fail( "should not be able to move a comparator up to normalizers container" );
+        }
+        catch( LdapInvalidNameException e ) 
+        {
+            assertEquals( e.getResultCode(), ResultCodeEnum.NAMING_VIOLATION );
+        }
+
+        assertTrue( "comparator should still be in the registry after move failure", 
+            getComparatorRegistry().hasComparator( OID ) );
+    }
+    
+    
+    @Test
+    public void testAddComparatorToDisabledSchema() throws NamingException
+    {
+        Attributes attrs = new AttributesImpl();
+        Attribute oc = new AttributeImpl( SchemaConstants.OBJECT_CLASS_AT, "top" );
+        oc.add( MetaSchemaConstants.META_TOP_OC );
+        oc.add( MetaSchemaConstants.META_COMPARATOR_OC );
+        attrs.put( oc );
+        attrs.put( MetaSchemaConstants.M_FQCN_AT, StringComparator.class.getName() );
+        attrs.put( MetaSchemaConstants.M_OID_AT, OID );
+        attrs.put( MetaSchemaConstants.M_DESCRIPTION_AT, "A test comparator" );
+        
+        // nis is by default inactive
+        LdapDN dn = getComparatorContainer( "nis" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        getSchemaContext( service ).createSubcontext( dn, attrs );
+        
+        assertFalse( "adding new comparator to disabled schema should not register it into the registries", 
+            getComparatorRegistry().hasComparator( OID ) );
+    }
+
+
+    @Test
+    public void testMoveComparatorToDisabledSchema() throws NamingException
+    {
+        testAddComparator();
+        
+        LdapDN dn = getComparatorContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+
+        // nis is inactive by default
+        LdapDN newdn = getComparatorContainer( "nis" );
+        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        
+        getSchemaContext( service ).rename( dn, newdn );
+
+        assertFalse( "comparator OID should no longer be present", 
+            getComparatorRegistry().hasComparator( OID ) );
+    }
+
+
+    @Test
+    public void testMoveComparatorToEnabledSchema() throws NamingException
+    {
+        testAddComparatorToDisabledSchema();
+        
+        // nis is inactive by default
+        LdapDN dn = getComparatorContainer( "nis" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+
+        assertFalse( "comparator OID should NOT be present when added to disabled nis schema", 
+            getComparatorRegistry().hasComparator( OID ) );
+
+        LdapDN newdn = getComparatorContainer( "apachemeta" );
+        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        
+        getSchemaContext( service ).rename( dn, newdn );
+
+        assertTrue( "comparator OID should be present when moved to enabled schema", 
+            getComparatorRegistry().hasComparator( OID ) );
+        
+        assertEquals( "comparator should be in apachemeta schema after move", 
+            getComparatorRegistry().getSchemaName( OID ), "apachemeta" );
+    }
+
+
+    class DummyMR implements MatchingRule
+    {
+        private static final long serialVersionUID = 1L;
+
+        public Comparator getComparator() throws NamingException
+        {
+            return null;
+        }
+
+        public Normalizer getNormalizer() throws NamingException
+        {
+            return null;
+        }
+
+        public Syntax getSyntax() throws NamingException
+        {
+            return null;
+        }
+
+        public String getDescription()
+        {
+            return null;
+        }
+
+        public String getName()
+        {
+            return "dummy";
+        }
+
+        public String[] getNamesRef()
+        {
+            return new String[] { "dummy" };
+        }
+
+        public String getOid()
+        {
+            return OID;
+        }
+
+        public boolean isObsolete()
+        {
+            return false;
+        }
+
+        public String getSchema()
+        {
+            return null;
+        }
+
+        public void setSchema( String schemaName )
+        {
+        }
+    }
+}
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/schema/MetaMatchingRuleHandlerIT.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/schema/MetaMatchingRuleHandlerIT.java
new file mode 100644
index 0000000..0737d4a
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/schema/MetaMatchingRuleHandlerIT.java
@@ -0,0 +1,505 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.schema;
+
+
+import org.apache.directory.server.constants.MetaSchemaConstants;
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.integ.CiRunner;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getSchemaContext;
+import org.apache.directory.server.schema.registries.MatchingRuleRegistry;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.exception.LdapInvalidNameException;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.message.ModificationItemImpl;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.schema.MatchingRule;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.fail;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+
+
+/**
+ * A test case which tests the addition of various schema elements
+ * to the ldap server.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+@RunWith ( CiRunner.class )
+public class MetaMatchingRuleHandlerIT
+{
+    private static final String DESCRIPTION0 = "A test matchingRule";
+    private static final String DESCRIPTION1 = "An alternate description";
+
+    private static final String INTEGER_SYNTAX_OID = "1.3.6.1.4.1.1466.115.121.1.27";
+    private static final String DIRSTR_SYNTAX_OID = "1.3.6.1.4.1.1466.115.121.1.15";
+    
+    private static final String OID = "1.3.6.1.4.1.18060.0.4.0.1.100000";
+    private static final String NEW_OID = "1.3.6.1.4.1.18060.0.4.0.1.100001";
+
+
+    public static DirectoryService service;
+
+
+    private static MatchingRuleRegistry getMatchingRuleRegistry()
+    {
+        return service.getRegistries().getMatchingRuleRegistry();
+    }
+    
+    
+    /**
+     * Gets relative DN to ou=schema.
+     * 
+     * @param schemaName the name of the schema
+     * @return  the dn of the container of matchingRules for a schema
+     * @throws NamingException on error
+     */
+    private LdapDN getMatchingRuleContainer( String schemaName ) throws NamingException
+    {
+        return new LdapDN( "ou=matchingRules,cn=" + schemaName );
+    }
+    
+    
+    // ----------------------------------------------------------------------
+    // Test all core methods with normal operational pathways
+    // ----------------------------------------------------------------------
+
+
+    @Test
+    public void testAddMatchingRule() throws NamingException
+    {
+        Attributes attrs = new AttributesImpl();
+        Attribute oc = new AttributeImpl( SchemaConstants.OBJECT_CLASS_AT, "top" );
+        oc.add( MetaSchemaConstants.META_TOP_OC );
+        oc.add( MetaSchemaConstants.META_MATCHING_RULE_OC );
+        attrs.put( oc );
+        attrs.put( MetaSchemaConstants.M_OID_AT, OID );
+        attrs.put( MetaSchemaConstants.M_SYNTAX_AT, INTEGER_SYNTAX_OID );
+        attrs.put( MetaSchemaConstants.M_DESCRIPTION_AT, DESCRIPTION0 );
+        
+        LdapDN dn = getMatchingRuleContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        getSchemaContext( service ).createSubcontext( dn, attrs );
+        
+        assertTrue( getMatchingRuleRegistry().hasMatchingRule( OID ) );
+        assertEquals( getMatchingRuleRegistry().getSchemaName( OID ), "apachemeta" );
+    }
+    
+
+    @Test
+    public void testDeleteMatchingRule() throws NamingException
+    {
+        LdapDN dn = getMatchingRuleContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        testAddMatchingRule();
+        
+        getSchemaContext( service ).destroySubcontext( dn );
+
+        assertFalse( "matchingRule should be removed from the registry after being deleted", 
+            getMatchingRuleRegistry().hasMatchingRule( OID ) );
+        
+        try
+        {
+            getMatchingRuleRegistry().lookup( OID );
+            fail( "matchingRule lookup should fail after deleting it" );
+        }
+        catch( NamingException e )
+        {
+        }
+    }
+
+
+    @Test
+    public void testRenameMatchingRule() throws NamingException
+    {
+        LdapDN dn = getMatchingRuleContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        testAddMatchingRule();
+        
+        LdapDN newdn = getMatchingRuleContainer( "apachemeta" );
+        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + NEW_OID );
+        getSchemaContext( service ).rename( dn, newdn );
+
+        assertFalse( "old matchingRule OID should be removed from the registry after being renamed", 
+            getMatchingRuleRegistry().hasMatchingRule( OID ) );
+        
+        try
+        {
+            getMatchingRuleRegistry().lookup( OID );
+            fail( "matchingRule lookup should fail after renaming the matchingRule" );
+        }
+        catch( NamingException e )
+        {
+        }
+
+        assertTrue( getMatchingRuleRegistry().hasMatchingRule( NEW_OID ) );
+    }
+
+
+    @Test
+    public void testMoveMatchingRule() throws NamingException
+    {
+        testAddMatchingRule();
+        
+        LdapDN dn = getMatchingRuleContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+
+        LdapDN newdn = getMatchingRuleContainer( "apache" );
+        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        
+        getSchemaContext( service ).rename( dn, newdn );
+
+        assertTrue( "matchingRule OID should still be present", 
+            getMatchingRuleRegistry().hasMatchingRule( OID ) );
+        
+        assertEquals( "matchingRule schema should be set to apache not apachemeta", 
+            getMatchingRuleRegistry().getSchemaName( OID ), "apache" );
+    }
+
+
+    @Test
+    public void testMoveMatchingRuleAndChangeRdn() throws NamingException
+    {
+        testAddMatchingRule();
+        
+        LdapDN dn = getMatchingRuleContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+
+        LdapDN newdn = getMatchingRuleContainer( "apache" );
+        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + NEW_OID );
+        
+        getSchemaContext( service ).rename( dn, newdn );
+
+        assertFalse( "old matchingRule OID should NOT be present", 
+            getMatchingRuleRegistry().hasMatchingRule( OID ) );
+        
+        assertTrue( "new matchingRule OID should be present", 
+            getMatchingRuleRegistry().hasMatchingRule( NEW_OID ) );
+        
+        assertEquals( "matchingRule with new oid should have schema set to apache NOT apachemeta", 
+            getMatchingRuleRegistry().getSchemaName( NEW_OID ), "apache" );
+    }
+
+
+    @Test
+    public void testModifyMatchingRuleWithModificationItems() throws NamingException
+    {
+        testAddMatchingRule();
+        
+        MatchingRule mr = getMatchingRuleRegistry().lookup( OID );
+        assertEquals( mr.getDescription(), DESCRIPTION0 );
+        assertEquals( mr.getSyntax().getOid(), INTEGER_SYNTAX_OID );
+
+        LdapDN dn = getMatchingRuleContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        
+        ModificationItemImpl[] mods = new ModificationItemImpl[2];
+        Attribute attr = new AttributeImpl( MetaSchemaConstants.M_DESCRIPTION_AT, DESCRIPTION1 );
+        mods[0] = new ModificationItemImpl( DirContext.REPLACE_ATTRIBUTE, attr );
+        attr = new AttributeImpl( MetaSchemaConstants.M_SYNTAX_AT, DIRSTR_SYNTAX_OID );
+        mods[1] = new ModificationItemImpl( DirContext.REPLACE_ATTRIBUTE, attr );
+        getSchemaContext( service ).modifyAttributes( dn, mods );
+
+        assertTrue( "matchingRule OID should still be present", 
+            getMatchingRuleRegistry().hasMatchingRule( OID ) );
+        
+        assertEquals( "matchingRule schema should be set to apachemeta", 
+            getMatchingRuleRegistry().getSchemaName( OID ), "apachemeta" );
+        
+        mr = getMatchingRuleRegistry().lookup( OID );
+        assertEquals( mr.getDescription(), DESCRIPTION1 );
+        assertEquals( mr.getSyntax().getOid(), DIRSTR_SYNTAX_OID );
+    }
+
+    
+    @Test
+    public void testModifyMatchingRuleWithAttributes() throws NamingException
+    {
+        testAddMatchingRule();
+        
+        MatchingRule mr = getMatchingRuleRegistry().lookup( OID );
+        assertEquals( mr.getDescription(), DESCRIPTION0 );
+        assertEquals( mr.getSyntax().getOid(), INTEGER_SYNTAX_OID );
+
+        LdapDN dn = getMatchingRuleContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        
+        Attributes mods = new AttributesImpl();
+        mods.put( MetaSchemaConstants.M_DESCRIPTION_AT, DESCRIPTION1 );
+        mods.put( MetaSchemaConstants.M_SYNTAX_AT, DIRSTR_SYNTAX_OID );
+        getSchemaContext( service ).modifyAttributes( dn, DirContext.REPLACE_ATTRIBUTE, mods );
+
+        assertTrue( "matchingRule OID should still be present", 
+            getMatchingRuleRegistry().hasMatchingRule( OID ) );
+        
+        assertEquals( "matchingRule schema should be set to apachemeta", 
+            getMatchingRuleRegistry().getSchemaName( OID ), "apachemeta" );
+
+        mr = getMatchingRuleRegistry().lookup( OID );
+        assertEquals( mr.getDescription(), DESCRIPTION1 );
+        assertEquals( mr.getSyntax().getOid(), DIRSTR_SYNTAX_OID );
+    }
+    
+
+    // ----------------------------------------------------------------------
+    // Test move, rename, and delete when a MR exists and uses the Normalizer
+    // ----------------------------------------------------------------------
+
+    
+//    public void testDeleteSyntaxWhenInUse() throws NamingException
+//    {
+//        LdapDN dn = getSyntaxContainer( "apachemeta" );
+//        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+//        testAddSyntax();
+//        addDependeeMatchingRule();
+//        
+//        try
+//        {
+//            super.schemaRoot.destroySubcontext( dn );
+//            fail( "should not be able to delete a syntax in use" );
+//        }
+//        catch( LdapOperationNotSupportedException e ) 
+//        {
+//            assertEquals( e.getResultCode(), ResultCodeEnum.UNWILLING_TO_PERFORM );
+//        }
+//
+//        assertTrue( "syntax should still be in the registry after delete failure", 
+//            registries.getSyntaxRegistry().hasSyntax( OID ) );
+//    }
+//    
+//    
+//    public void testMoveSyntaxWhenInUse() throws NamingException
+//    {
+//        testAddSyntax();
+//        addDependeeMatchingRule();
+//        
+//        LdapDN dn = getSyntaxContainer( "apachemeta" );
+//        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+//
+//        LdapDN newdn = getSyntaxContainer( "apache" );
+//        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+//        
+//        try
+//        {
+//            super.schemaRoot.rename( dn, newdn );
+//            fail( "should not be able to move a syntax in use" );
+//        }
+//        catch( LdapOperationNotSupportedException e ) 
+//        {
+//            assertEquals( e.getResultCode(), ResultCodeEnum.UNWILLING_TO_PERFORM );
+//        }
+//
+//        assertTrue( "syntax should still be in the registry after move failure", 
+//            registries.getSyntaxRegistry().hasSyntax( OID ) );
+//    }
+//
+//
+//    public void testMoveSyntaxAndChangeRdnWhenInUse() throws NamingException
+//    {
+//        testAddSyntax();
+//        addDependeeMatchingRule()
+//        
+//        LdapDN dn = getSyntaxContainer( "apachemeta" );
+//        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+//
+//        LdapDN newdn = getSyntaxContainer( "apache" );
+//        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + NEW_OID );
+//        
+//        try
+//        {
+//            super.schemaRoot.rename( dn, newdn );
+//            fail( "should not be able to move a syntax in use" );
+//        }
+//        catch( LdapOperationNotSupportedException e ) 
+//        {
+//            assertEquals( e.getResultCode(), ResultCodeEnum.UNWILLING_TO_PERFORM );
+//        }
+//
+//        assertTrue( "syntax should still be in the registry after move failure", 
+//            registries.getSyntaxRegistry().hasSyntax( OID ) );
+//    }
+//
+//    
+
+    // Need to add body to this method which creates a new matchingRule after 
+    // the matchingRule addition code has been added.
+    
+//    private void addDependeeMatchingRule()
+//    {
+//        throw new NotImplementedException();
+//    }
+//    
+//    public void testRenameNormalizerWhenInUse() throws NamingException
+//    {
+//        LdapDN dn = getSyntaxContainer( "apachemeta" );
+//        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+//        testAddSyntax();
+//        addDependeeMatchingRule();
+//        
+//        LdapDN newdn = getSyntaxContainer( "apachemeta" );
+//        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + NEW_OID );
+//        
+//        try
+//        {
+//            super.schemaRoot.rename( dn, newdn );
+//            fail( "should not be able to rename a syntax in use" );
+//        }
+//        catch( LdapOperationNotSupportedException e ) 
+//        {
+//            assertEquals( e.getResultCode(), ResultCodeEnum.UNWILLING_TO_PERFORM );
+//        }
+//
+//        assertTrue( "syntax should still be in the registry after rename failure", 
+//            registries.getSyntaxRegistry().hasSyntax( OID ) );
+//    }
+
+
+    // ----------------------------------------------------------------------
+    // Let's try some freaky stuff
+    // ----------------------------------------------------------------------
+
+
+    @Test
+    public void testMoveMatchingRuleToTop() throws NamingException
+    {
+        testAddMatchingRule();
+        
+        LdapDN dn = getMatchingRuleContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+
+        LdapDN top = new LdapDN();
+        top.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        
+        try
+        {
+            getSchemaContext( service ).rename( dn, top );
+            fail( "should not be able to move a matchingRule up to ou=schema" );
+        }
+        catch( LdapInvalidNameException e ) 
+        {
+            assertEquals( e.getResultCode(), ResultCodeEnum.NAMING_VIOLATION );
+        }
+
+        assertTrue( "matchingRule should still be in the registry after move failure", 
+            getMatchingRuleRegistry().hasMatchingRule( OID ) );
+    }
+
+
+    @Test
+    public void testMoveMatchingRuleToComparatorContainer() throws NamingException
+    {
+        testAddMatchingRule();
+        
+        LdapDN dn = getMatchingRuleContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+
+        LdapDN newdn = new LdapDN( "ou=comparators,cn=apachemeta" );
+        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        
+        try
+        {
+            getSchemaContext( service ).rename( dn, newdn );
+            fail( "should not be able to move a matchingRule into comparators container" );
+        }
+        catch( LdapInvalidNameException e ) 
+        {
+            assertEquals( e.getResultCode(), ResultCodeEnum.NAMING_VIOLATION );
+        }
+
+        assertTrue( "matchingRule should still be in the registry after move failure", 
+            getMatchingRuleRegistry().hasMatchingRule( OID ) );
+    }
+    
+    
+    @Test
+    public void testAddMatchingRuleToDisabledSchema() throws NamingException
+    {
+        Attributes attrs = new AttributesImpl();
+        Attribute oc = new AttributeImpl( SchemaConstants.OBJECT_CLASS_AT, "top" );
+        oc.add( MetaSchemaConstants.META_TOP_OC );
+        oc.add( MetaSchemaConstants.META_MATCHING_RULE_OC );
+        attrs.put( oc );
+        attrs.put( MetaSchemaConstants.M_OID_AT, OID );
+        attrs.put( MetaSchemaConstants.M_SYNTAX_AT, INTEGER_SYNTAX_OID );
+        attrs.put( MetaSchemaConstants.M_DESCRIPTION_AT, DESCRIPTION0 );
+        
+        LdapDN dn = getMatchingRuleContainer( "nis" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        getSchemaContext( service ).createSubcontext( dn, attrs );
+        
+        assertFalse( "adding new matchingRule to disabled schema should not register it into the registries", 
+            getMatchingRuleRegistry().hasMatchingRule( OID ) );
+    }
+
+
+    @Test
+    public void testMoveMatchingRuleToDisabledSchema() throws NamingException
+    {
+        testAddMatchingRule();
+        
+        LdapDN dn = getMatchingRuleContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+
+        // nis is inactive by default
+        LdapDN newdn = getMatchingRuleContainer( "nis" );
+        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        
+        getSchemaContext( service ).rename( dn, newdn );
+
+        assertFalse( "matchingRule OID should no longer be present", 
+            getMatchingRuleRegistry().hasMatchingRule( OID ) );
+    }
+
+
+    @Test
+    public void testMoveMatchingRuleToEnabledSchema() throws NamingException
+    {
+        testAddMatchingRuleToDisabledSchema();
+        
+        // nis is inactive by default
+        LdapDN dn = getMatchingRuleContainer( "nis" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+
+        assertFalse( "matchingRule OID should NOT be present when added to disabled nis schema", 
+            getMatchingRuleRegistry().hasMatchingRule( OID ) );
+
+        LdapDN newdn = getMatchingRuleContainer( "apachemeta" );
+        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        
+        getSchemaContext( service ).rename( dn, newdn );
+
+        assertTrue( "matchingRule OID should be present when moved to enabled schema", 
+            getMatchingRuleRegistry().hasMatchingRule( OID ) );
+        
+        assertEquals( "matchingRule should be in apachemeta schema after move", 
+            getMatchingRuleRegistry().getSchemaName( OID ), "apachemeta" );
+    }
+}
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/schema/MetaNormalizerHandlerIT.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/schema/MetaNormalizerHandlerIT.java
new file mode 100644
index 0000000..f2457c3
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/schema/MetaNormalizerHandlerIT.java
@@ -0,0 +1,611 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.schema;
+
+
+import org.apache.directory.server.constants.MetaSchemaConstants;
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.integ.CiRunner;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getSchemaContext;
+import org.apache.directory.server.schema.registries.MatchingRuleRegistry;
+import org.apache.directory.server.schema.registries.NormalizerRegistry;
+import org.apache.directory.server.schema.registries.OidRegistry;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.exception.LdapInvalidNameException;
+import org.apache.directory.shared.ldap.exception.LdapOperationNotSupportedException;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.message.ModificationItemImpl;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.schema.DeepTrimNormalizer;
+import org.apache.directory.shared.ldap.schema.MatchingRule;
+import org.apache.directory.shared.ldap.schema.NoOpNormalizer;
+import org.apache.directory.shared.ldap.schema.Normalizer;
+import org.apache.directory.shared.ldap.schema.Syntax;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.fail;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.util.Comparator;
+
+
+/**
+ * A test case which tests the addition of various schema elements
+ * to the ldap server.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+@RunWith ( CiRunner.class )
+public class MetaNormalizerHandlerIT
+{
+    private static final String OID = "1.3.6.1.4.1.18060.0.4.0.1.100000";
+    private static final String NEW_OID = "1.3.6.1.4.1.18060.0.4.0.1.100001";
+
+
+    public static DirectoryService service;
+
+
+    /**
+     * Gets relative DN to ou=schema.
+     *
+     * @param schemaName the name of the schema
+     * @return  the name of the container with normalizer entries in it
+     * @throws NamingException on error
+     */
+    private LdapDN getNormalizerContainer( String schemaName ) throws NamingException
+    {
+        return new LdapDN( "ou=normalizers,cn=" + schemaName );
+    }
+    
+
+    private static NormalizerRegistry getNormalizerRegistry()
+    {
+        return service.getRegistries().getNormalizerRegistry();
+    }
+
+
+    private static MatchingRuleRegistry getMatchingRuleRegistry()
+    {
+        return service.getRegistries().getMatchingRuleRegistry();
+    }
+
+
+    private static OidRegistry getOidRegistry()
+    {
+        return service.getRegistries().getOidRegistry();
+    }
+
+
+    // ----------------------------------------------------------------------
+    // Test all core methods with normal operational pathways
+    // ----------------------------------------------------------------------
+
+
+    @Test
+    public void testAddNormalizer() throws NamingException
+    {
+        Attributes attrs = new AttributesImpl();
+        Attribute oc = new AttributeImpl( SchemaConstants.OBJECT_CLASS_AT, "top" );
+        oc.add( MetaSchemaConstants.META_TOP_OC );
+        oc.add( MetaSchemaConstants.META_NORMALIZER_OC );
+        attrs.put( oc );
+        attrs.put( MetaSchemaConstants.M_FQCN_AT, NoOpNormalizer.class.getName() );
+        attrs.put( MetaSchemaConstants.M_OID_AT, OID );
+        attrs.put( MetaSchemaConstants.M_DESCRIPTION_AT, "A test normalizer" );
+        
+        LdapDN dn = getNormalizerContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        getSchemaContext( service ).createSubcontext( dn, attrs );
+        
+        assertTrue( getNormalizerRegistry().hasNormalizer( OID ) );
+        assertEquals( getNormalizerRegistry().getSchemaName( OID ), "apachemeta" );
+        Class<?> clazz = getNormalizerRegistry().lookup( OID ).getClass();
+        assertEquals( clazz, NoOpNormalizer.class );
+    }
+    
+    
+    @Test
+    public void testAddNormalizerWithByteCode() throws Exception
+    {
+        InputStream in = getClass().getResourceAsStream( "DummyNormalizer.bytecode" );
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        while ( in.available() > 0 )
+        {
+            out.write( in.read() );
+        }
+        
+        Attributes attrs = new AttributesImpl();
+        Attribute oc = new AttributeImpl( SchemaConstants.OBJECT_CLASS_AT, "top" );
+        oc.add( MetaSchemaConstants.META_TOP_OC );
+        oc.add( MetaSchemaConstants.META_NORMALIZER_OC );
+        attrs.put( oc );
+        attrs.put( MetaSchemaConstants.M_FQCN_AT, "DummyNormalizer" );
+        attrs.put( MetaSchemaConstants.M_BYTECODE_AT, out.toByteArray() );
+        attrs.put( MetaSchemaConstants.M_OID_AT, OID );
+        attrs.put( MetaSchemaConstants.M_DESCRIPTION_AT, "A test normalizer" );
+        
+        LdapDN dn = getNormalizerContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        getSchemaContext( service ).createSubcontext( dn, attrs );
+        
+        assertTrue( getNormalizerRegistry().hasNormalizer( OID ) );
+        assertEquals( getNormalizerRegistry().getSchemaName( OID ), "apachemeta" );
+        Class<?> clazz = getNormalizerRegistry().lookup( OID ).getClass();
+        assertEquals( clazz.getName(), "DummyNormalizer" );
+    }
+    
+    
+    @Test
+    public void testDeleteNormalizer() throws NamingException
+    {
+        LdapDN dn = getNormalizerContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        testAddNormalizer();
+        
+        getSchemaContext( service ).destroySubcontext( dn );
+
+        assertFalse( "normalizer should be removed from the registry after being deleted", 
+            getNormalizerRegistry().hasNormalizer( OID ) );
+
+        //noinspection EmptyCatchBlock
+        try
+        {
+            getNormalizerRegistry().lookup( OID );
+            fail( "normalizer lookup should fail after deleting the normalizer" );
+        }
+        catch( NamingException e )
+        {
+        }
+    }
+
+
+    @Test
+    public void testRenameNormalizer() throws NamingException
+    {
+        LdapDN dn = getNormalizerContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        testAddNormalizer();
+        
+        LdapDN newdn = getNormalizerContainer( "apachemeta" );
+        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + NEW_OID );
+        getSchemaContext( service ).rename( dn, newdn );
+
+        assertFalse( "old normalizer OID should be removed from the registry after being renamed", 
+            getNormalizerRegistry().hasNormalizer( OID ) );
+
+        //noinspection EmptyCatchBlock
+        try
+        {
+            getNormalizerRegistry().lookup( OID );
+            fail( "normalizer lookup should fail after deleting the normalizer" );
+        }
+        catch( NamingException e )
+        {
+        }
+
+        assertTrue( getNormalizerRegistry().hasNormalizer( NEW_OID ) );
+        Class<?> clazz = getNormalizerRegistry().lookup( NEW_OID ).getClass();
+        assertEquals( clazz, NoOpNormalizer.class );
+    }
+
+
+    @Test
+    public void testMoveNormalizer() throws NamingException
+    {
+        testAddNormalizer();
+        
+        LdapDN dn = getNormalizerContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+
+        LdapDN newdn = getNormalizerContainer( "apache" );
+        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        
+        getSchemaContext( service ).rename( dn, newdn );
+
+        assertTrue( "normalizer OID should still be present", 
+            getNormalizerRegistry().hasNormalizer( OID ) );
+        
+        assertEquals( "normalizer schema should be set to apache not apachemeta", 
+            getNormalizerRegistry().getSchemaName( OID ), "apache" );
+
+        Class<?> clazz = getNormalizerRegistry().lookup( OID ).getClass();
+        assertEquals( clazz, NoOpNormalizer.class );
+    }
+
+
+    @Test
+    public void testMoveNormalizerAndChangeRdn() throws NamingException
+    {
+        testAddNormalizer();
+        
+        LdapDN dn = getNormalizerContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+
+        LdapDN newdn = getNormalizerContainer( "apache" );
+        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + NEW_OID );
+        
+        getSchemaContext( service ).rename( dn, newdn );
+
+        assertFalse( "old normalizer OID should NOT be present", 
+            getNormalizerRegistry().hasNormalizer( OID ) );
+        
+        assertTrue( "new normalizer OID should be present", 
+            getNormalizerRegistry().hasNormalizer( NEW_OID ) );
+        
+        assertEquals( "normalizer with new oid should have schema set to apache NOT apachemeta", 
+            getNormalizerRegistry().getSchemaName( NEW_OID ), "apache" );
+
+        Class<?> clazz = getNormalizerRegistry().lookup( NEW_OID ).getClass();
+        assertEquals( clazz, NoOpNormalizer.class );
+    }
+
+    
+    @Test
+    public void testModifyNormalizerWithModificationItems() throws NamingException
+    {
+        testAddNormalizer();
+        
+        LdapDN dn = getNormalizerContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        
+        ModificationItemImpl[] mods = new ModificationItemImpl[1];
+        Attribute attr = new AttributeImpl( MetaSchemaConstants.M_FQCN_AT, DeepTrimNormalizer.class.getName() );
+        mods[0] = new ModificationItemImpl( DirContext.REPLACE_ATTRIBUTE, attr );
+        getSchemaContext( service ).modifyAttributes( dn, mods );
+
+        assertTrue( "normalizer OID should still be present", 
+            getNormalizerRegistry().hasNormalizer( OID ) );
+        
+        assertEquals( "normalizer schema should be set to apachemeta", 
+            getNormalizerRegistry().getSchemaName( OID ), "apachemeta" );
+
+        Class<?> clazz = getNormalizerRegistry().lookup( OID ).getClass();
+        assertEquals( clazz, DeepTrimNormalizer.class );
+    }
+
+    
+    @Test
+    public void testModifyNormalizerWithAttributes() throws NamingException
+    {
+        testAddNormalizer();
+        
+        LdapDN dn = getNormalizerContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        
+        Attributes mods = new AttributesImpl();
+        mods.put( MetaSchemaConstants.M_FQCN_AT, DeepTrimNormalizer.class.getName() );
+        getSchemaContext( service ).modifyAttributes( dn, DirContext.REPLACE_ATTRIBUTE, mods );
+
+        assertTrue( "normalizer OID should still be present", 
+            getNormalizerRegistry().hasNormalizer( OID ) );
+        
+        assertEquals( "normalizer schema should be set to apachemeta", 
+            getNormalizerRegistry().getSchemaName( OID ), "apachemeta" );
+
+        Class<?> clazz = getNormalizerRegistry().lookup( OID ).getClass();
+        assertEquals( clazz, DeepTrimNormalizer.class );
+    }
+    
+
+    // ----------------------------------------------------------------------
+    // Test move, rename, and delete when a MR exists and uses the Normalizer
+    // ----------------------------------------------------------------------
+
+    
+    @Test
+    public void testDeleteNormalizerWhenInUse() throws NamingException
+    {
+        LdapDN dn = getNormalizerContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        testAddNormalizer();
+        getMatchingRuleRegistry().register( new DummyMR() );
+        
+        try
+        {
+            getSchemaContext( service ).destroySubcontext( dn );
+            fail( "should not be able to delete a normalizer in use" );
+        }
+        catch( LdapOperationNotSupportedException e ) 
+        {
+            assertEquals( e.getResultCode(), ResultCodeEnum.UNWILLING_TO_PERFORM );
+        }
+
+        assertTrue( "normalizer should still be in the registry after delete failure", 
+            getNormalizerRegistry().hasNormalizer( OID ) );
+        getMatchingRuleRegistry().unregister( OID );
+        getOidRegistry().unregister( OID );
+    }
+    
+    
+    @Test
+    public void testMoveNormalizerWhenInUse() throws NamingException
+    {
+        testAddNormalizer();
+        getMatchingRuleRegistry().register( new DummyMR() );
+        
+        LdapDN dn = getNormalizerContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+
+        LdapDN newdn = getNormalizerContainer( "apache" );
+        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        
+        try
+        {
+            getSchemaContext( service ).rename( dn, newdn );
+            fail( "should not be able to move a normalizer in use" );
+        }
+        catch( LdapOperationNotSupportedException e ) 
+        {
+            assertEquals( e.getResultCode(), ResultCodeEnum.UNWILLING_TO_PERFORM );
+        }
+
+        assertTrue( "normalizer should still be in the registry after move failure", 
+            getNormalizerRegistry().hasNormalizer( OID ) );
+        getMatchingRuleRegistry().unregister( OID );
+        getOidRegistry().unregister( OID );
+    }
+
+
+    @Test
+    public void testMoveNormalizerAndChangeRdnWhenInUse() throws NamingException
+    {
+        testAddNormalizer();
+        getMatchingRuleRegistry().register( new DummyMR() );
+        
+        LdapDN dn = getNormalizerContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+
+        LdapDN newdn = getNormalizerContainer( "apache" );
+        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + NEW_OID );
+        
+        try
+        {
+            getSchemaContext( service ).rename( dn, newdn );
+            fail( "should not be able to move a normalizer in use" );
+        }
+        catch( LdapOperationNotSupportedException e ) 
+        {
+            assertEquals( e.getResultCode(), ResultCodeEnum.UNWILLING_TO_PERFORM );
+        }
+
+        assertTrue( "normalizer should still be in the registry after move failure", 
+            getNormalizerRegistry().hasNormalizer( OID ) );
+        getMatchingRuleRegistry().unregister( OID );
+        getOidRegistry().unregister( OID );
+    }
+
+    
+    @Test
+    public void testRenameNormalizerWhenInUse() throws NamingException
+    {
+        LdapDN dn = getNormalizerContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        testAddNormalizer();
+        getMatchingRuleRegistry().register( new DummyMR() );
+        
+        LdapDN newdn = getNormalizerContainer( "apachemeta" );
+        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + NEW_OID );
+        
+        try
+        {
+            getSchemaContext( service ).rename( dn, newdn );
+            fail( "should not be able to rename a normalizer in use" );
+        }
+        catch( LdapOperationNotSupportedException e ) 
+        {
+            assertEquals( e.getResultCode(), ResultCodeEnum.UNWILLING_TO_PERFORM );
+        }
+
+        assertTrue( "normalizer should still be in the registry after rename failure", 
+            getNormalizerRegistry().hasNormalizer( OID ) );
+        getMatchingRuleRegistry().unregister( OID );
+        getOidRegistry().unregister( OID );
+    }
+
+
+    // ----------------------------------------------------------------------
+    // Let's try some freaky stuff
+    // ----------------------------------------------------------------------
+
+
+    @Test
+    public void testMoveNormalizerToTop() throws NamingException
+    {
+        testAddNormalizer();
+        
+        LdapDN dn = getNormalizerContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+
+        LdapDN top = new LdapDN();
+        top.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        
+        try
+        {
+            getSchemaContext( service ).rename( dn, top );
+            fail( "should not be able to move a normalizer up to ou=schema" );
+        }
+        catch( LdapInvalidNameException e ) 
+        {
+            assertEquals( e.getResultCode(), ResultCodeEnum.NAMING_VIOLATION );
+        }
+
+        assertTrue( "normalizer should still be in the registry after move failure", 
+            getNormalizerRegistry().hasNormalizer( OID ) );
+    }
+
+
+    @Test
+    public void testMoveNormalizerToComparatorContainer() throws NamingException
+    {
+        testAddNormalizer();
+        
+        LdapDN dn = getNormalizerContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+
+        LdapDN newdn = new LdapDN( "ou=comparators,cn=apachemeta" );
+        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        
+        try
+        {
+            getSchemaContext( service ).rename( dn, newdn );
+            fail( "should not be able to move a normalizer into comparators container" );
+        }
+        catch( LdapInvalidNameException e ) 
+        {
+            assertEquals( e.getResultCode(), ResultCodeEnum.NAMING_VIOLATION );
+        }
+
+        assertTrue( "normalizer should still be in the registry after move failure", 
+            getNormalizerRegistry().hasNormalizer( OID ) );
+    }
+    
+    
+    @Test
+    public void testAddNormalizerToDisabledSchema() throws NamingException
+    {
+        Attributes attrs = new AttributesImpl();
+        Attribute oc = new AttributeImpl( SchemaConstants.OBJECT_CLASS_AT, "top" );
+        oc.add( MetaSchemaConstants.META_TOP_OC );
+        oc.add( MetaSchemaConstants.META_NORMALIZER_OC );
+        attrs.put( oc );
+        attrs.put( MetaSchemaConstants.M_FQCN_AT, NoOpNormalizer.class.getName() );
+        attrs.put( MetaSchemaConstants.M_OID_AT, OID );
+        attrs.put( MetaSchemaConstants.M_DESCRIPTION_AT, "A test normalizer" );
+        
+        // nis is by default inactive
+        LdapDN dn = getNormalizerContainer( "nis" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        getSchemaContext( service ).createSubcontext( dn, attrs );
+        
+        assertFalse( "adding new normalizer to disabled schema should not register it into the registries", 
+            getNormalizerRegistry().hasNormalizer( OID ) );
+    }
+
+
+    @Test
+    public void testMoveNormalizerToDisabledSchema() throws NamingException
+    {
+        testAddNormalizer();
+        
+        LdapDN dn = getNormalizerContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+
+        // nis is inactive by default
+        LdapDN newdn = getNormalizerContainer( "nis" );
+        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        
+        getSchemaContext( service ).rename( dn, newdn );
+
+        assertFalse( "normalizer OID should no longer be present", 
+            getNormalizerRegistry().hasNormalizer( OID ) );
+    }
+
+
+    @Test
+    public void testMoveNormalizerToEnabledSchema() throws NamingException
+    {
+        testAddNormalizerToDisabledSchema();
+        
+        // nis is inactive by default
+        LdapDN dn = getNormalizerContainer( "nis" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+
+        assertFalse( "normalizer OID should NOT be present when added to disabled nis schema", 
+            getNormalizerRegistry().hasNormalizer( OID ) );
+
+        LdapDN newdn = getNormalizerContainer( "apachemeta" );
+        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        
+        getSchemaContext( service ).rename( dn, newdn );
+
+        assertTrue( "normalizer OID should be present when moved to enabled schema", 
+            getNormalizerRegistry().hasNormalizer( OID ) );
+        
+        assertEquals( "normalizer should be in apachemeta schema after move", 
+            getNormalizerRegistry().getSchemaName( OID ), "apachemeta" );
+    }
+
+
+    class DummyMR implements MatchingRule
+    {
+        private static final long serialVersionUID = 1L;
+
+        public Comparator getComparator() throws NamingException
+        {
+            return null;
+        }
+
+        public Normalizer getNormalizer() throws NamingException
+        {
+            return null;
+        }
+
+        public Syntax getSyntax() throws NamingException
+        {
+            return null;
+        }
+
+        public String getDescription()
+        {
+            return null;
+        }
+
+        public String getName()
+        {
+            return "dummy";
+        }
+
+        public String[] getNamesRef()
+        {
+            return new String[] { "dummy" };
+        }
+
+        public String getOid()
+        {
+            return OID;
+        }
+
+        public boolean isObsolete()
+        {
+            return false;
+        }
+
+        public String getSchema()
+        {
+            return null;
+        }
+
+        public void setSchema( String schemaName )
+        {
+        }
+    }
+}
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/schema/MetaObjectClassHandlerIT.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/schema/MetaObjectClassHandlerIT.java
new file mode 100644
index 0000000..ffcf235
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/schema/MetaObjectClassHandlerIT.java
@@ -0,0 +1,535 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.schema;
+
+
+import org.apache.directory.server.constants.MetaSchemaConstants;
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.integ.CiRunner;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getSchemaContext;
+import org.apache.directory.server.schema.registries.ObjectClassRegistry;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.exception.LdapInvalidNameException;
+import org.apache.directory.shared.ldap.exception.LdapOperationNotSupportedException;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.message.ModificationItemImpl;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.schema.ObjectClass;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.fail;
+import org.junit.runner.RunWith;
+
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+
+
+/**
+ * A test case which tests the addition of various schema elements
+ * to the ldap server.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+@RunWith ( CiRunner.class )
+public class MetaObjectClassHandlerIT
+{
+    private static final String NAME = "testObjectClass";
+    private static final String NEW_NAME = "alternateName";
+    private static final String DEPENDEE_NAME = "dependeeName";
+
+    private static final String DESCRIPTION0 = "A test objectClass";
+    private static final String DESCRIPTION1 = "An alternate description";
+    
+    private static final String OID = "1.3.6.1.4.1.18060.0.4.0.3.100000";
+    private static final String NEW_OID = "1.3.6.1.4.1.18060.0.4.0.3.100001";
+    private static final String DEPENDEE_OID = "1.3.6.1.4.1.18060.0.4.0.3.100002";
+
+
+    public static DirectoryService service;
+
+    
+    /**
+     * Gets relative DN to ou=schema.
+     *
+     * @param schemaName the name of the schema
+     * @return the dn of the container which contains objectClasses
+     * @throws NamingException on error
+     */
+    private LdapDN getObjectClassContainer( String schemaName ) throws NamingException
+    {
+        return new LdapDN( "ou=objectClasses,cn=" + schemaName );
+    }
+
+
+    private static ObjectClassRegistry getObjectClassRegistry()
+    {
+        return service.getRegistries().getObjectClassRegistry();
+    }
+
+    
+    // ----------------------------------------------------------------------
+    // Test all core methods with normal operational pathways
+    // ----------------------------------------------------------------------
+
+
+    @Test
+    public void testAddObjectClass() throws NamingException
+    {
+        Attributes attrs = new AttributesImpl();
+        Attribute oc = new AttributeImpl( SchemaConstants.OBJECT_CLASS_AT, "top" );
+        oc.add( MetaSchemaConstants.META_TOP_OC );
+        oc.add( MetaSchemaConstants.META_OBJECT_CLASS_OC );
+        attrs.put( oc );
+        attrs.put( MetaSchemaConstants.M_OID_AT, OID );
+        attrs.put( MetaSchemaConstants.M_NAME_AT, NAME);
+        attrs.put( MetaSchemaConstants.M_DESCRIPTION_AT, DESCRIPTION0 );
+        attrs.put( MetaSchemaConstants.M_TYPE_OBJECT_CLASS_AT, "AUXILIARY" );
+        attrs.put( MetaSchemaConstants.M_MUST_AT, "cn" );
+        attrs.put( MetaSchemaConstants.M_MAY_AT, "ou" );
+        
+        LdapDN dn = getObjectClassContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        getSchemaContext( service ).createSubcontext( dn, attrs );
+        
+        assertTrue( getObjectClassRegistry().hasObjectClass( OID ) );
+        assertEquals( getObjectClassRegistry().getSchemaName( OID ), "apachemeta" );
+    }
+    
+    
+    @Test
+    public void testDeleteAttributeType() throws NamingException
+    {
+        LdapDN dn = getObjectClassContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        testAddObjectClass();
+        
+        getSchemaContext( service ).destroySubcontext( dn );
+
+        assertFalse( "objectClass should be removed from the registry after being deleted", 
+            getObjectClassRegistry().hasObjectClass( OID ) );
+
+        //noinspection EmptyCatchBlock
+        try
+        {
+            getObjectClassRegistry().lookup( OID );
+            fail( "objectClass lookup should fail after deleting it" );
+        }
+        catch( NamingException e )
+        {
+        }
+    }
+
+
+    @Test
+    public void testRenameAttributeType() throws NamingException
+    {
+        LdapDN dn = getObjectClassContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        testAddObjectClass();
+        
+        LdapDN newdn = getObjectClassContainer( "apachemeta" );
+        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + NEW_OID );
+        getSchemaContext( service ).rename( dn, newdn );
+
+        assertFalse( "old objectClass OID should be removed from the registry after being renamed", 
+            getObjectClassRegistry().hasObjectClass( OID ) );
+
+        //noinspection EmptyCatchBlock
+        try
+        {
+            getObjectClassRegistry().lookup( OID );
+            fail( "objectClass lookup should fail after renaming the objectClass" );
+        }
+        catch( NamingException e )
+        {
+        }
+
+        assertTrue( getObjectClassRegistry().hasObjectClass( NEW_OID ) );
+    }
+
+
+    @Test
+    public void testMoveAttributeType() throws NamingException
+    {
+        testAddObjectClass();
+        
+        LdapDN dn = getObjectClassContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+
+        LdapDN newdn = getObjectClassContainer( "apache" );
+        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        
+        getSchemaContext( service ).rename( dn, newdn );
+
+        assertTrue( "objectClass OID should still be present", 
+            getObjectClassRegistry().hasObjectClass( OID ) );
+        
+        assertEquals( "objectClass schema should be set to apache not apachemeta", 
+            getObjectClassRegistry().getSchemaName( OID ), "apache" );
+    }
+
+
+    @Test
+    public void testMoveObjectClassAndChangeRdn() throws NamingException
+    {
+        testAddObjectClass();
+        
+        LdapDN dn = getObjectClassContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+
+        LdapDN newdn = getObjectClassContainer( "apache" );
+        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + NEW_OID );
+        
+        getSchemaContext( service ).rename( dn, newdn );
+
+        assertFalse( "old objectClass OID should NOT be present", 
+            getObjectClassRegistry().hasObjectClass( OID ) );
+        
+        assertTrue( "new objectClass OID should be present", 
+            getObjectClassRegistry().hasObjectClass( NEW_OID ) );
+        
+        assertEquals( "objectClass with new oid should have schema set to apache NOT apachemeta", 
+            getObjectClassRegistry().getSchemaName( NEW_OID ), "apache" );
+    }
+
+    
+    @Test
+    public void testModifyAttributeTypeWithModificationItems() throws NamingException
+    {
+        testAddObjectClass();
+        
+        ObjectClass oc = getObjectClassRegistry().lookup( OID );
+        assertEquals( oc.getDescription(), DESCRIPTION0 );
+        assertEquals( oc.getName(), NAME );
+
+        LdapDN dn = getObjectClassContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        
+        ModificationItemImpl[] mods = new ModificationItemImpl[2];
+        Attribute attr = new AttributeImpl( MetaSchemaConstants.M_DESCRIPTION_AT, DESCRIPTION1 );
+        mods[0] = new ModificationItemImpl( DirContext.REPLACE_ATTRIBUTE, attr );
+        attr = new AttributeImpl( MetaSchemaConstants.M_NAME_AT, NEW_NAME );
+        mods[1] = new ModificationItemImpl( DirContext.REPLACE_ATTRIBUTE, attr );
+        getSchemaContext( service ).modifyAttributes( dn, mods );
+
+        assertTrue( "objectClass OID should still be present", 
+            getObjectClassRegistry().hasObjectClass( OID ) );
+        
+        assertEquals( "objectClass schema should be set to apachemeta", 
+            getObjectClassRegistry().getSchemaName( OID ), "apachemeta" );
+        
+        oc = getObjectClassRegistry().lookup( OID );
+        assertEquals( oc.getDescription(), DESCRIPTION1 );
+        assertEquals( oc.getName(), NEW_NAME );
+    }
+
+    
+    @Test
+    public void testModifyAttributeTypeWithAttributes() throws NamingException
+    {
+        testAddObjectClass();
+        
+        ObjectClass oc = getObjectClassRegistry().lookup( OID );
+        assertEquals( oc.getDescription(), DESCRIPTION0 );
+        assertEquals( oc.getName(), NAME );
+
+        LdapDN dn = getObjectClassContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        
+        Attributes mods = new AttributesImpl();
+        mods.put( MetaSchemaConstants.M_DESCRIPTION_AT, DESCRIPTION1 );
+        mods.put( MetaSchemaConstants.M_NAME_AT, NEW_NAME );
+        getSchemaContext( service ).modifyAttributes( dn, DirContext.REPLACE_ATTRIBUTE, mods );
+
+        assertTrue( "objectClass OID should still be present", 
+            getObjectClassRegistry().hasObjectClass( OID ) );
+        
+        assertEquals( "objectClass schema should be set to apachemeta", 
+            getObjectClassRegistry().getSchemaName( OID ), "apachemeta" );
+
+        oc = getObjectClassRegistry().lookup( OID );
+        assertEquals( oc.getDescription(), DESCRIPTION1 );
+        assertEquals( oc.getName(), NEW_NAME );
+    }
+    
+
+    // ----------------------------------------------------------------------
+    // Test move, rename, and delete when a OC exists and uses the OC as sup
+    // ----------------------------------------------------------------------
+
+    
+    private void addDependeeObjectClass() throws NamingException
+    {
+        Attributes attrs = new AttributesImpl();
+        Attribute oc = new AttributeImpl( SchemaConstants.OBJECT_CLASS_AT, "top" );
+        oc.add( MetaSchemaConstants.META_TOP_OC );
+        oc.add( MetaSchemaConstants.META_OBJECT_CLASS_OC );
+        attrs.put( oc );
+        attrs.put( MetaSchemaConstants.M_OID_AT, DEPENDEE_OID );
+        attrs.put( MetaSchemaConstants.M_NAME_AT, DEPENDEE_NAME );
+        attrs.put( MetaSchemaConstants.M_DESCRIPTION_AT, DESCRIPTION0 );
+        attrs.put( MetaSchemaConstants.M_TYPE_OBJECT_CLASS_AT, "AUXILIARY" );
+        attrs.put( MetaSchemaConstants.M_MUST_AT, "cn" );
+        attrs.put( MetaSchemaConstants.M_MAY_AT, "ou" );
+        attrs.put( MetaSchemaConstants.M_SUP_OBJECT_CLASS_AT, OID );
+        
+        LdapDN dn = getObjectClassContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + DEPENDEE_OID );
+        getSchemaContext( service ).createSubcontext( dn, attrs );
+        
+        assertTrue( getObjectClassRegistry().hasObjectClass( DEPENDEE_OID ) );
+        assertEquals( getObjectClassRegistry().getSchemaName( DEPENDEE_OID ), "apachemeta" );
+    }
+
+    
+    @Test
+    public void testDeleteObjectClassWhenInUse() throws NamingException
+    {
+        LdapDN dn = getObjectClassContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        testAddObjectClass();
+        addDependeeObjectClass();
+        
+        try
+        {
+            getSchemaContext( service ).destroySubcontext( dn );
+            fail( "should not be able to delete a objectClass in use" );
+        }
+        catch( LdapOperationNotSupportedException e ) 
+        {
+            assertEquals( e.getResultCode(), ResultCodeEnum.UNWILLING_TO_PERFORM );
+        }
+
+        assertTrue( "objectClass should still be in the registry after delete failure", 
+            getObjectClassRegistry().hasObjectClass( OID ) );
+    }
+    
+    
+    @Test
+    public void testMoveObjectClassWhenInUse() throws NamingException
+    {
+        testAddObjectClass();
+        addDependeeObjectClass();
+        
+        LdapDN dn = getObjectClassContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+
+        LdapDN newdn = getObjectClassContainer( "apache" );
+        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        
+        try
+        {
+            getSchemaContext( service ).rename( dn, newdn );
+            fail( "should not be able to move a objectClass in use" );
+        }
+        catch( LdapOperationNotSupportedException e ) 
+        {
+            assertEquals( e.getResultCode(), ResultCodeEnum.UNWILLING_TO_PERFORM );
+        }
+
+        assertTrue( "objectClass should still be in the registry after move failure", 
+            getObjectClassRegistry().hasObjectClass( OID ) );
+    }
+
+
+    @Test
+    public void testMoveObjectClassAndChangeRdnWhenInUse() throws NamingException
+    {
+        testAddObjectClass();
+        addDependeeObjectClass();
+        
+        LdapDN dn = getObjectClassContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+
+        LdapDN newdn = getObjectClassContainer( "apache" );
+        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + NEW_OID );
+        
+        try
+        {
+            getSchemaContext( service ).rename( dn, newdn );
+            fail( "should not be able to move an objectClass in use" );
+        }
+        catch( LdapOperationNotSupportedException e ) 
+        {
+            assertEquals( e.getResultCode(), ResultCodeEnum.UNWILLING_TO_PERFORM );
+        }
+
+        assertTrue( "ObjectClass should still be in the registry after move failure", 
+            getObjectClassRegistry().hasObjectClass( OID ) );
+    }
+
+    
+    @Test
+    public void testRenameObjectClassWhenInUse() throws NamingException
+    {
+        LdapDN dn = getObjectClassContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        testAddObjectClass();
+        addDependeeObjectClass();
+        
+        LdapDN newdn = getObjectClassContainer( "apachemeta" );
+        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + NEW_OID );
+        
+        try
+        {
+            getSchemaContext( service ).rename( dn, newdn );
+            fail( "should not be able to rename an objectClass in use" );
+        }
+        catch( LdapOperationNotSupportedException e ) 
+        {
+            assertEquals( e.getResultCode(), ResultCodeEnum.UNWILLING_TO_PERFORM );
+        }
+
+        assertTrue( "objectClass should still be in the registry after rename failure", 
+            getObjectClassRegistry().hasObjectClass( OID ) );
+    }
+
+
+    // ----------------------------------------------------------------------
+    // Let's try some freaky stuff
+    // ----------------------------------------------------------------------
+
+
+    @Test
+    public void testMoveObjectClassToTop() throws NamingException
+    {
+        testAddObjectClass();
+        
+        LdapDN dn = getObjectClassContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+
+        LdapDN top = new LdapDN();
+        top.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        
+        try
+        {
+            getSchemaContext( service ).rename( dn, top );
+            fail( "should not be able to move a objectClass up to ou=schema" );
+        }
+        catch( LdapInvalidNameException e ) 
+        {
+            assertEquals( e.getResultCode(), ResultCodeEnum.NAMING_VIOLATION );
+        }
+
+        assertTrue( "objectClass should still be in the registry after move failure", 
+            getObjectClassRegistry().hasObjectClass( OID ) );
+    }
+
+
+    @Test
+    public void testMoveObjectClassToComparatorContainer() throws NamingException
+    {
+        testAddObjectClass();
+        
+        LdapDN dn = getObjectClassContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+
+        LdapDN newdn = new LdapDN( "ou=comparators,cn=apachemeta" );
+        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        
+        try
+        {
+            getSchemaContext( service ).rename( dn, newdn );
+            fail( "should not be able to move a objectClass into comparators container" );
+        }
+        catch( LdapInvalidNameException e ) 
+        {
+            assertEquals( e.getResultCode(), ResultCodeEnum.NAMING_VIOLATION );
+        }
+
+        assertTrue( "objectClass should still be in the registry after move failure", 
+            getObjectClassRegistry().hasObjectClass( OID ) );
+    }
+    
+    
+    @Test
+    public void testAddObjectClassToDisabledSchema() throws NamingException
+    {
+        Attributes attrs = new AttributesImpl();
+        Attribute oc = new AttributeImpl( SchemaConstants.OBJECT_CLASS_AT, "top" );
+        oc.add( MetaSchemaConstants.META_TOP_OC );
+        oc.add( MetaSchemaConstants.META_OBJECT_CLASS_OC );
+        attrs.put( oc );
+        attrs.put( MetaSchemaConstants.M_OID_AT, OID );
+        attrs.put( MetaSchemaConstants.M_NAME_AT, NAME);
+        attrs.put( MetaSchemaConstants.M_DESCRIPTION_AT, DESCRIPTION0 );
+        attrs.put( MetaSchemaConstants.M_TYPE_OBJECT_CLASS_AT, "AUXILIARY" );
+        attrs.put( MetaSchemaConstants.M_MUST_AT, "cn" );
+        attrs.put( MetaSchemaConstants.M_MAY_AT, "ou" );
+        
+        LdapDN dn = getObjectClassContainer( "nis" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        getSchemaContext( service ).createSubcontext( dn, attrs );
+        
+        assertFalse( "adding new objectClass to disabled schema should not register it into the registries", 
+            getObjectClassRegistry().hasObjectClass( OID ) );
+    }
+
+
+    @Test
+    public void testMoveObjectClassToDisabledSchema() throws NamingException
+    {
+        testAddObjectClass();
+        
+        LdapDN dn = getObjectClassContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+
+        // nis is inactive by default
+        LdapDN newdn = getObjectClassContainer( "nis" );
+        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        
+        getSchemaContext( service ).rename( dn, newdn );
+
+        assertFalse( "objectClass OID should no longer be present", 
+            getObjectClassRegistry().hasObjectClass( OID ) );
+    }
+
+
+    @Test
+    public void testMoveObjectClassToEnabledSchema() throws NamingException
+    {
+        testAddObjectClassToDisabledSchema();
+        
+        // nis is inactive by default
+        LdapDN dn = getObjectClassContainer( "nis" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+
+        assertFalse( "objectClass OID should NOT be present when added to disabled nis schema", 
+            getObjectClassRegistry().hasObjectClass( OID ) );
+
+        LdapDN newdn = getObjectClassContainer( "apachemeta" );
+        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        
+        getSchemaContext( service ).rename( dn, newdn );
+
+        assertTrue( "objectClass OID should be present when moved to enabled schema", 
+            getObjectClassRegistry().hasObjectClass( OID ) );
+        
+        assertEquals( "objectClass should be in apachemeta schema after move", 
+            getObjectClassRegistry().getSchemaName( OID ), "apachemeta" );
+    }
+}
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/schema/MetaSchemaHandlerIT.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/schema/MetaSchemaHandlerIT.java
new file mode 100644
index 0000000..71d2168
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/schema/MetaSchemaHandlerIT.java
@@ -0,0 +1,711 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.schema;
+
+
+import org.apache.directory.server.constants.MetaSchemaConstants;
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.integ.CiRunner;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getSchemaContext;
+import org.apache.directory.server.schema.bootstrap.Schema;
+import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
+import org.apache.directory.shared.ldap.exception.LdapNameNotFoundException;
+import org.apache.directory.shared.ldap.exception.LdapOperationNotSupportedException;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.message.ModificationItemImpl;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.ldap.LdapContext;
+import java.util.Map;
+
+
+/**
+ * A test case which tests the correct operation of the schema 
+ * entity handler.  
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+@RunWith ( CiRunner.class )
+public class MetaSchemaHandlerIT
+{
+    /** the schema to use for this test: one that is not loaded by default */
+    private static final String TEST_SCHEMA = "nis";
+    /** a test attribute in the test schema: uidNumber in nis schema */
+    private static final String TEST_ATTR_OID = "1.3.6.1.1.1.1.0";
+    /** the name of the dummy schema to test metaSchema adds/deletes with */
+    private static final String DUMMY_SCHEMA = "dummy";
+    
+
+    public static DirectoryService service;
+
+
+    private static AttributeTypeRegistry getAttributeTypeRegistry()
+    {
+        return service.getRegistries().getAttributeTypeRegistry();
+    }
+
+
+    private static Map<String, Schema> getLoadedSchemas()
+    {
+        return service.getRegistries().getLoadedSchemas();
+    }
+
+
+    @Before
+    public void checkSambaSchema() throws NamingException
+    {
+        LdapContext schemaRoot = getSchemaContext( service );
+
+        // check that there is a samba schema installed and that is is disabled
+        Attributes attributes = schemaRoot.getAttributes( "cn=samba" );
+        assertNotNull( attributes );
+        assertTrue( attributes.get( MetaSchemaConstants.M_DISABLED_AT ).contains( "TRUE" ) );
+        attributes = schemaRoot.getAttributes( "ou=attributeTypes,cn=samba" );
+        assertNotNull( attributes );
+        assertTrue( attributes.get( SchemaConstants.OU_AT ).contains( "attributetypes" ) );
+    }
+
+
+    // -----------------------------------------------------------------------
+    // Schema Add Tests
+    // -----------------------------------------------------------------------
+
+    
+    /**
+     * Tests the addition of a new metaSchema object that is disabled 
+     * on addition and has no dependencies.
+     *
+     * @throws Exception on error
+     */
+    @Test
+    public void testAddDisabledSchemaNoDeps() throws Exception
+    {
+        LdapContext schemaRoot = getSchemaContext( service );
+        Attributes dummySchema = new AttributesImpl( "objectClass", "top" );
+        dummySchema.get( "objectClass" ).add( MetaSchemaConstants.META_SCHEMA_OC );
+        dummySchema.put( "cn", DUMMY_SCHEMA );
+        dummySchema.put( MetaSchemaConstants.M_DISABLED_AT, "TRUE" );
+        schemaRoot.createSubcontext( "cn=" + DUMMY_SCHEMA, dummySchema );
+        
+        assertNull( getLoadedSchemas().get( DUMMY_SCHEMA ) );
+        assertNotNull( schemaRoot.lookup( "cn=" + DUMMY_SCHEMA ) );
+    }
+    
+    
+    /**
+     * Tests the addition of a new metaSchema object that is disabled 
+     * on addition and has dependencies.
+     *
+     * @throws Exception on error
+     */
+    @Test
+    public void testAddDisabledSchemaWithDeps() throws Exception
+    {
+        LdapContext schemaRoot = getSchemaContext( service );
+        Attributes dummySchema = new AttributesImpl( "objectClass", "top" );
+        dummySchema.get( "objectClass" ).add( MetaSchemaConstants.META_SCHEMA_OC );
+        dummySchema.put( "cn", DUMMY_SCHEMA );
+        dummySchema.put( MetaSchemaConstants.M_DISABLED_AT, "TRUE" );
+        dummySchema.put( MetaSchemaConstants.M_DEPENDENCIES_AT, TEST_SCHEMA );
+        dummySchema.get( MetaSchemaConstants.M_DEPENDENCIES_AT ).add( "core" );
+        schemaRoot.createSubcontext( "cn=" + DUMMY_SCHEMA, dummySchema );
+        
+        assertNull( getLoadedSchemas().get( DUMMY_SCHEMA ) );
+        assertNotNull( schemaRoot.lookup( "cn=" + DUMMY_SCHEMA ) );
+    }
+    
+    
+    /**
+     * Tests the rejection of a new metaSchema object that is disabled 
+     * on addition and has missing dependencies.
+     *
+     * @throws Exception on error
+     */
+    @Test
+    public void testRejectDisabledSchemaAddWithMissingDeps() throws Exception
+    {
+        LdapContext schemaRoot = getSchemaContext( service );
+        Attributes dummySchema = new AttributesImpl( "objectClass", "top" );
+        dummySchema.get( "objectClass" ).add( MetaSchemaConstants.META_SCHEMA_OC );
+        dummySchema.put( "cn", DUMMY_SCHEMA );
+        dummySchema.put( MetaSchemaConstants.M_DISABLED_AT, "TRUE" );
+        dummySchema.put( MetaSchemaConstants.M_DEPENDENCIES_AT, "missing" );
+        dummySchema.get( MetaSchemaConstants.M_DEPENDENCIES_AT ).add( "core" );
+        
+        try
+        {
+            schemaRoot.createSubcontext( "cn=" + DUMMY_SCHEMA, dummySchema );
+        } 
+        catch( LdapOperationNotSupportedException e )
+        {
+            assertTrue( e.getResultCode().equals( ResultCodeEnum.UNWILLING_TO_PERFORM ) );
+        }
+        
+        assertNull( getLoadedSchemas().get( DUMMY_SCHEMA ) );
+
+        //noinspection EmptyCatchBlock
+        try
+        {
+            schemaRoot.lookup( "cn=" + DUMMY_SCHEMA );
+            fail( "schema should not be added to schema partition" );
+        }
+        catch( NamingException e )
+        {
+        }
+    }
+    
+    
+    /**
+     * Tests the addition of a new metaSchema object that is enabled 
+     * on addition and has no dependencies.
+     *
+     * @throws Exception on error
+     */
+    @Test
+    public void testAddEnabledSchemaNoDeps() throws Exception
+    {
+        LdapContext schemaRoot = getSchemaContext( service );
+        Attributes dummySchema = new AttributesImpl( "objectClass", "top" );
+        dummySchema.get( "objectClass" ).add( MetaSchemaConstants.META_SCHEMA_OC );
+        dummySchema.put( "cn", DUMMY_SCHEMA );
+        schemaRoot.createSubcontext( "cn=" + DUMMY_SCHEMA, dummySchema );
+        
+        assertNotNull( getLoadedSchemas().get( DUMMY_SCHEMA ) );
+        assertNotNull( schemaRoot.lookup( "cn=" + DUMMY_SCHEMA ) );
+    }
+    
+    
+    /**
+     * Tests the rejection of a metaSchema object add that is enabled 
+     * on addition yet has disabled dependencies.
+     *
+     * @throws Exception on error
+     */
+    @Test
+    public void testRejectEnabledSchemaAddWithDisabledDeps() throws Exception
+    {
+        LdapContext schemaRoot = getSchemaContext( service );
+        Attributes dummySchema = new AttributesImpl( "objectClass", "top" );
+        dummySchema.get( "objectClass" ).add( MetaSchemaConstants.META_SCHEMA_OC );
+        dummySchema.put( "cn", DUMMY_SCHEMA );
+        dummySchema.put( MetaSchemaConstants.M_DEPENDENCIES_AT, TEST_SCHEMA );
+        
+        try
+        {
+            schemaRoot.createSubcontext( "cn=" + DUMMY_SCHEMA, dummySchema );
+            fail( "should not be able to add enabled schema with deps on disabled schemas" );
+        }
+        catch( LdapOperationNotSupportedException e )
+        {
+            assertTrue( e.getResultCode().equals( ResultCodeEnum.UNWILLING_TO_PERFORM ) );
+        }
+        
+        assertNull( getLoadedSchemas().get( DUMMY_SCHEMA ) );
+
+        //noinspection EmptyCatchBlock
+        try
+        {
+            schemaRoot.lookup( "cn=" + DUMMY_SCHEMA );
+            fail( "schema should not be added to schema partition" );
+        }
+        catch( NamingException e )
+        {
+        }
+    }
+
+    
+    // -----------------------------------------------------------------------
+    // Schema Delete Tests
+    // -----------------------------------------------------------------------
+
+    
+    /**
+     * Makes sure we can delete schemas that have no dependents.
+     *
+     * @throws Exception on error
+     */
+    @Test
+    public void testDeleteSchemaNoDependents() throws Exception
+    {
+        LdapContext schemaRoot = getSchemaContext( service );
+
+        // add the dummy schema enabled 
+        testAddEnabledSchemaNoDeps();
+        assertNotNull( getLoadedSchemas().get( DUMMY_SCHEMA ) );
+        
+        // delete it now
+        schemaRoot.destroySubcontext( "cn=" + DUMMY_SCHEMA );
+        assertNull( getLoadedSchemas().get( DUMMY_SCHEMA ) );
+    }
+    
+    
+    /**
+     * Makes sure we can NOT delete schemas that have dependents.
+     *
+     * @throws Exception on error
+     */
+    @Test
+    public void testRejectSchemaDeleteWithDependents() throws Exception
+    {
+        LdapContext schemaRoot = getSchemaContext( service );
+
+        // add the dummy schema enabled
+        testAddEnabledSchemaNoDeps();
+        assertNotNull( getLoadedSchemas().get( DUMMY_SCHEMA ) );
+        
+        // make the nis schema depend on the dummy schema
+        ModificationItemImpl[] mods = new ModificationItemImpl[1];
+        mods[0] = new ModificationItemImpl( DirContext.ADD_ATTRIBUTE,
+                new AttributeImpl( MetaSchemaConstants.M_DEPENDENCIES_AT, DUMMY_SCHEMA ) );
+        schemaRoot.modifyAttributes( "cn=" + TEST_SCHEMA, mods );
+        
+        // attempt to delete it now & it should fail
+        try
+        {
+            schemaRoot.destroySubcontext( "cn=" + DUMMY_SCHEMA );
+            fail( "should not be able to delete a schema with dependents" );
+        }
+        catch ( LdapOperationNotSupportedException e )
+        {
+            assertTrue( e.getResultCode().equals( ResultCodeEnum.UNWILLING_TO_PERFORM ) );
+        }
+
+        assertNotNull( getLoadedSchemas().get( DUMMY_SCHEMA ) );
+    }
+    
+    
+    /**
+     * Tests the rejection of a new metaSchema object that is enabled 
+     * on addition and missing dependencies.
+     *
+     * @throws Exception on error
+     */
+    @Test
+    public void testRejectEnabledSchemaAddWithMisingDeps() throws Exception
+    {
+        LdapContext schemaRoot = getSchemaContext( service );
+
+        Attributes dummySchema = new AttributesImpl( "objectClass", "top" );
+        dummySchema.get( "objectClass" ).add( MetaSchemaConstants.META_SCHEMA_OC );
+        dummySchema.put( "cn", DUMMY_SCHEMA );
+        dummySchema.put( MetaSchemaConstants.M_DEPENDENCIES_AT, "missing" );
+        
+        try
+        {
+            schemaRoot.createSubcontext( "cn=" + DUMMY_SCHEMA, dummySchema );
+            fail( "should not be able to add enabled schema with deps on missing schemas" );
+        }
+        catch( LdapOperationNotSupportedException e )
+        {
+            assertTrue( e.getResultCode().equals( ResultCodeEnum.UNWILLING_TO_PERFORM ) );
+        }
+        
+        assertNull( getLoadedSchemas().get( DUMMY_SCHEMA ) );
+
+        //noinspection EmptyCatchBlock
+        try
+        {
+            schemaRoot.lookup( "cn=" + DUMMY_SCHEMA );
+            fail( "schema should not be added to schema partition" );
+        }
+        catch( NamingException e )
+        {
+        }
+    }
+
+    
+    // -----------------------------------------------------------------------
+    // Enable/Disable Schema Tests
+    // -----------------------------------------------------------------------
+
+    
+    private void enableSchema( String schemaName ) throws NamingException
+    {
+        LdapContext schemaRoot = getSchemaContext( service );
+
+        // now enable the test schema
+        ModificationItemImpl[] mods = new ModificationItemImpl[1];
+        Attribute attr = new AttributeImpl( "m-disabled", "FALSE" );
+        mods[0] = new ModificationItemImpl( DirContext.REPLACE_ATTRIBUTE, attr );
+        schemaRoot.modifyAttributes( "cn=" + schemaName, mods );
+    }
+    
+    
+    private void disableSchema( String schemaName ) throws NamingException
+    {
+        LdapContext schemaRoot = getSchemaContext( service );
+
+        // now enable the test schema
+        ModificationItemImpl[] mods = new ModificationItemImpl[1];
+        Attribute attr = new AttributeImpl( "m-disabled", "TRUE" );
+        mods[0] = new ModificationItemImpl( DirContext.REPLACE_ATTRIBUTE, attr );
+        schemaRoot.modifyAttributes( "cn=" + schemaName, mods );
+    }
+    
+    
+    /**
+     * Checks to make sure updates enabling a metaSchema object in
+     * the schema partition triggers the loading of that schema into
+     * the global registries.
+     *
+     * @throws Exception on error
+     */
+    @Test
+    public void testEnableSchema() throws Exception
+    {
+        AttributeTypeRegistry atr = getAttributeTypeRegistry();
+        
+        // check that the nis schema is not loaded
+        assertNull( getLoadedSchemas().get( TEST_SCHEMA ) );
+        
+        // double check and make sure an attribute from that schema is 
+        // not in the AttributeTypeRegistry
+        assertFalse( atr.hasAttributeType( TEST_ATTR_OID ) );
+        
+        // now enable the test schema
+        enableSchema( "nis" );
+        
+        // now test that the schema is loaded 
+        assertNotNull( getLoadedSchemas().get( TEST_SCHEMA ) );
+        
+        // double check and make sure the test attribute from the 
+        // test schema is now loaded and present within the attr registry
+        assertTrue( atr.hasAttributeType( TEST_ATTR_OID ) );
+    }
+
+
+    /**
+     * Checks to make sure an attempt to disable a metaSchema fails if 
+     * that schema has dependents which are enabled.
+     *
+     * @throws Exception on error
+     */
+    @Test
+    public void testDisableSchema() throws Exception
+    {
+        // let's enable the test schema
+        testEnableSchema();
+        
+        AttributeTypeRegistry atr = getAttributeTypeRegistry();
+        
+        // check that the nis schema is loaded
+        assertNotNull( getLoadedSchemas().get( TEST_SCHEMA ) );
+        
+        // double check and make sure an attribute from that schema is 
+        // in the AttributeTypeRegistry
+        assertTrue( atr.hasAttributeType( TEST_ATTR_OID ) );
+        
+        // now disable the test schema 
+        disableSchema( "samba" );
+        disableSchema( "nis" );
+        
+        // now test that the schema is NOT loaded 
+        assertNull( getLoadedSchemas().get( TEST_SCHEMA ) );
+        
+        // double check and make sure the test attribute from the test  
+        // schema is now NOT loaded and present within the attr registry
+        assertFalse( atr.hasAttributeType( TEST_ATTR_OID ) );
+    }
+
+    
+    /**
+     * Checks to make sure updates disabling a metaSchema object in
+     * the schema partition triggers the unloading of that schema from
+     * the global registries.
+     *
+     * @throws Exception on error
+     */
+    @Test
+    public void testDisableSchemaWithEnabledDependents() throws Exception
+    {
+        LdapContext schemaRoot = getSchemaContext( service );
+
+        // let's enable the test schema and add the dummy schema
+        // as enabled by default and dependends on the test schema
+        
+//      // enables the test schema and samba
+        testEnableSchema(); 
+        
+        // adds enabled dummy schema that depends on the test schema  
+        Attributes dummySchema = new AttributesImpl( "objectClass", "top" );
+        dummySchema.get( "objectClass" ).add( MetaSchemaConstants.META_SCHEMA_OC );
+        dummySchema.put( "cn", DUMMY_SCHEMA );
+        dummySchema.put( MetaSchemaConstants.M_DEPENDENCIES_AT, TEST_SCHEMA );
+        schemaRoot.createSubcontext( "cn=" + DUMMY_SCHEMA, dummySchema );
+        
+        // check that the nis schema is loaded and the dummy schema is loaded
+        assertNotNull( getLoadedSchemas().get( TEST_SCHEMA ) );
+        assertNotNull( getLoadedSchemas().get( DUMMY_SCHEMA ) );
+        
+        AttributeTypeRegistry atr = getAttributeTypeRegistry();
+        
+        // double check and make sure an attribute from that schema is 
+        // in the AttributeTypeRegistry
+        assertTrue( atr.hasAttributeType( TEST_ATTR_OID ) );
+        
+        // now try to disable the test schema which should fail 
+        // since it's dependent, the dummy schema, is enabled
+        ModificationItemImpl[] mods = new ModificationItemImpl[1];
+        Attribute attr = new AttributeImpl( "m-disabled", "TRUE" );
+        mods[0] = new ModificationItemImpl( DirContext.REPLACE_ATTRIBUTE, attr );
+        
+        try
+        {
+            schemaRoot.modifyAttributes( "cn=nis", mods );
+            fail( "attempt to disable schema with enabled dependents should fail" );
+        }
+        catch ( LdapOperationNotSupportedException e )
+        {
+            assertTrue( e.getResultCode().equals( ResultCodeEnum.UNWILLING_TO_PERFORM ) );
+        }
+        
+        // now test that both schema are still loaded 
+        assertNotNull( getLoadedSchemas().get( TEST_SCHEMA ) );
+        assertNotNull( getLoadedSchemas().get( DUMMY_SCHEMA ) );
+        
+        // double check and make sure the test attribute from the test  
+        // schema is still loaded and present within the attr registry
+        assertTrue( atr.hasAttributeType( TEST_ATTR_OID ) );
+    }
+    
+    
+    // -----------------------------------------------------------------------
+    // Schema Rename Tests
+    // -----------------------------------------------------------------------
+
+
+    /**
+     * Makes sure we can change the name of a schema with entities in it.
+     * Will use the samba schema which comes out of the box and nothing 
+     * depends on.
+     *
+     * @throws Exception on error
+     */
+    @Test
+    public void testSchemaRenameDisabledSchema() throws Exception
+    {
+        LdapContext schemaRoot = getSchemaContext( service );
+        schemaRoot.rename( "cn=samba", "cn=foo" );
+        assertNotNull( schemaRoot.lookup( "cn=foo" ) );
+
+        // check that there is a samba schema installed and that is is disabled
+        Attributes attributes = schemaRoot.getAttributes( "cn=foo" );
+        assertNotNull( attributes );
+        assertTrue( attributes.get( MetaSchemaConstants.M_DISABLED_AT ).contains( "TRUE" ) );
+        attributes = schemaRoot.getAttributes( "ou=attributeTypes,cn=foo" );
+        assertNotNull( attributes );
+        assertTrue( attributes.get( SchemaConstants.OU_AT ).contains( "attributetypes" ) );
+
+        //noinspection EmptyCatchBlock
+        try
+        {
+            schemaRoot.lookup( "cn=samba" );
+            fail( "the samba schema should not be present after a rename to foo" );
+        }
+        catch( LdapNameNotFoundException e )
+        {
+        }
+    }
+
+
+    /**
+     * Makes sure we can NOT change the name of a schema that has dependents.
+     * Will use the nis schema which comes out of the box and has samba as
+     * it's dependent.
+     *
+     * @throws Exception on error
+     */
+    @Test
+    public void testRejectSchemaRenameWithDeps() throws Exception
+    {
+        LdapContext schemaRoot = getSchemaContext( service );
+        try
+        {
+            schemaRoot.rename( "cn=nis", "cn=foo" );
+            fail( "should not be able to rename nis which has samba as it's dependent" );
+        }
+        catch ( LdapOperationNotSupportedException e )
+        {
+            assertEquals( ResultCodeEnum.UNWILLING_TO_PERFORM, e.getResultCode() );
+        }
+        
+        assertNotNull( schemaRoot.lookup( "cn=nis" ) );
+
+        //noinspection EmptyCatchBlock
+        try
+        {
+            schemaRoot.lookup( "cn=foo" );
+            fail( "the foo schema should not be present after rejecting the rename" );
+        }
+        catch( LdapNameNotFoundException e )
+        {
+        }
+    }
+
+
+    /**
+     * Makes sure we can change the name of a schema with entities in it.
+     * Will use the samba schema which comes out of the box and nothing 
+     * depends on.
+     *
+     * @throws Exception on error
+     */
+    @Test
+    public void testSchemaRenameEnabledSchema() throws Exception
+    {
+        LdapContext schemaRoot = getSchemaContext( service );
+
+        enableSchema( "samba" );
+        assertTrue( getAttributeTypeRegistry().hasAttributeType( "sambaNTPassword" ) );
+        assertEquals( "samba", getAttributeTypeRegistry().getSchemaName( "sambaNTPassword" ) );
+        
+        schemaRoot.rename( "cn=samba", "cn=foo" );
+        assertNotNull( schemaRoot.lookup( "cn=foo" ) );
+        assertTrue( getAttributeTypeRegistry().hasAttributeType( "sambaNTPassword" ) );
+        assertEquals( "foo", getAttributeTypeRegistry().getSchemaName( "sambaNTPassword" ) );
+
+        //noinspection EmptyCatchBlock
+        try
+        {
+            schemaRoot.lookup( "cn=samba" );
+            fail( "the samba schema should not be present after a rename to foo" );
+        }
+        catch( LdapNameNotFoundException e )
+        {
+        }
+    }
+
+
+    // -----------------------------------------------------------------------
+    // Dependency Modify Tests
+    // -----------------------------------------------------------------------
+
+
+    /**
+     * Checks to make sure the addition of an undefined schema to the dependencies 
+     * of an existing schema fail with an UNWILLING_TO_PERFORM result code.
+     *
+     * @throws Exception on error
+     */
+    @Test
+    public void testRejectAddBogusDependency() throws Exception
+    {
+        LdapContext schemaRoot = getSchemaContext( service );
+
+        ModificationItemImpl[] mods = new ModificationItemImpl[1];
+        Attribute attr = new AttributeImpl( "m-dependencies", "bogus" );
+        mods[0] = new ModificationItemImpl( DirContext.ADD_ATTRIBUTE, attr );
+        
+        try
+        {
+            schemaRoot.modifyAttributes( "cn=" + TEST_SCHEMA, mods );
+            fail( "Should not be able to add bogus dependency to schema" );
+        }
+        catch ( LdapOperationNotSupportedException e )
+        {
+            assertEquals( ResultCodeEnum.UNWILLING_TO_PERFORM, e.getResultCode() );
+        }
+    }
+
+
+    /**
+     * Checks to make sure the addition of an defined yet disabled schema to the 
+     * dependencies of an existing enabled schema fails with an UNWILLING_TO_PERFORM 
+     * result code.  You must enable the dependency to add it or disable the schema 
+     * depending on it to add it.
+     *
+     * @throws Exception on error
+     */
+    @Test
+    public void testRejectAddOfDisabledDependencyToEnabledSchema() throws Exception
+    {
+        LdapContext schemaRoot = getSchemaContext( service );
+        enableSchema( TEST_SCHEMA );
+        ModificationItemImpl[] mods = new ModificationItemImpl[1];
+        Attribute attr = new AttributeImpl( "m-dependencies", "mozilla" );
+        mods[0] = new ModificationItemImpl( DirContext.ADD_ATTRIBUTE, attr );
+        
+        try
+        {
+            schemaRoot.modifyAttributes( "cn=" + TEST_SCHEMA, mods );
+            fail( "Should not be able to add disabled dependency to schema" );
+        }
+        catch ( LdapOperationNotSupportedException e )
+        {
+            assertEquals( ResultCodeEnum.UNWILLING_TO_PERFORM, e.getResultCode() );
+        }
+    }
+
+
+    /**
+     * Checks to make sure the addition of an defined yet disabled schema to the 
+     * dependencies of an existing disabled schema succeeds. 
+     *
+     * @throws Exception on error
+     */
+    @Test
+    public void testAddOfDisabledDependencyToDisabledSchema() throws Exception
+    {
+        LdapContext schemaRoot = getSchemaContext( service );
+        ModificationItemImpl[] mods = new ModificationItemImpl[1];
+        Attribute attr = new AttributeImpl( "m-dependencies", "mozilla" );
+        mods[0] = new ModificationItemImpl( DirContext.ADD_ATTRIBUTE, attr );
+        schemaRoot.modifyAttributes( "cn=" + TEST_SCHEMA, mods );
+        Attributes attrs = schemaRoot.getAttributes( "cn=" + TEST_SCHEMA );
+        Attribute dependencies = attrs.get( "m-dependencies" );
+        assertTrue( dependencies.contains( "mozilla" ) );
+    }
+
+
+    /**
+     * Checks to make sure the addition of an defined yet enabled schema to the 
+     * dependencies of an existing disabled schema succeeds.
+     *
+     * @throws Exception on error
+     */
+    @Test
+    public void testAddOfEnabledDependencyToDisabledSchema() throws Exception
+    {
+        LdapContext schemaRoot = getSchemaContext( service );
+        ModificationItemImpl[] mods = new ModificationItemImpl[1];
+        Attribute attr = new AttributeImpl( "m-dependencies", "java" );
+        mods[0] = new ModificationItemImpl( DirContext.ADD_ATTRIBUTE, attr );
+        schemaRoot.modifyAttributes( "cn=" + TEST_SCHEMA, mods );
+        Attributes attrs = schemaRoot.getAttributes( "cn=" + TEST_SCHEMA );
+        Attribute dependencies = attrs.get( "m-dependencies" );
+        assertTrue( dependencies.contains( "java" ) );
+    }
+}
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/schema/MetaSyntaxCheckerHandlerIT.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/schema/MetaSyntaxCheckerHandlerIT.java
new file mode 100644
index 0000000..13145cf
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/schema/MetaSyntaxCheckerHandlerIT.java
@@ -0,0 +1,625 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.schema;
+
+
+import org.apache.directory.server.constants.MetaSchemaConstants;
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.integ.CiRunner;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getSchemaContext;
+import org.apache.directory.server.schema.registries.OidRegistry;
+import org.apache.directory.server.schema.registries.SyntaxCheckerRegistry;
+import org.apache.directory.server.schema.registries.SyntaxRegistry;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.exception.LdapInvalidNameException;
+import org.apache.directory.shared.ldap.exception.LdapOperationNotSupportedException;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.message.ModificationItemImpl;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.schema.Syntax;
+import org.apache.directory.shared.ldap.schema.syntax.AcceptAllSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.SyntaxChecker;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.fail;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+
+
+/**
+ * A test case which tests the addition of various schema elements
+ * to the ldap server.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+@RunWith ( CiRunner.class )
+public class MetaSyntaxCheckerHandlerIT
+{
+    private static final String OID = "1.3.6.1.4.1.18060.0.4.0.0.100000";
+    private static final String NEW_OID = "1.3.6.1.4.1.18060.0.4.0.0.100001";
+
+
+    public static DirectoryService service;
+
+
+    private static SyntaxCheckerRegistry getSyntaxCheckerRegistry()
+    {
+        return service.getRegistries().getSyntaxCheckerRegistry();
+    }
+
+
+    private static SyntaxRegistry getSyntaxRegistry()
+    {
+        return service.getRegistries().getSyntaxRegistry();
+    }
+
+
+    private static OidRegistry getOidRegistry()
+    {
+        return service.getRegistries().getOidRegistry();
+    }
+
+
+    /**
+     * Gets relative DN to ou=schema.
+     *
+     * @param schemaName the name of the schema
+     * @return the dn of the container holding syntax checkers for the schema
+     * @throws NamingException on dn parse errors
+     */
+    private LdapDN getSyntaxCheckerContainer( String schemaName ) throws NamingException
+    {
+        return new LdapDN( "ou=syntaxCheckers,cn=" + schemaName );
+    }
+    
+    
+    // ----------------------------------------------------------------------
+    // Test all core methods with normal operational pathways
+    // ----------------------------------------------------------------------
+
+
+    @Test
+    public void testAddSyntaxChecker() throws NamingException
+    {
+        Attributes attrs = new AttributesImpl();
+        Attribute oc = new AttributeImpl( SchemaConstants.OBJECT_CLASS_AT, "top" );
+        oc.add( MetaSchemaConstants.META_TOP_OC );
+        oc.add( MetaSchemaConstants.META_SYNTAX_CHECKER_OC );
+        attrs.put( oc );
+        attrs.put( MetaSchemaConstants.M_FQCN_AT, AcceptAllSyntaxChecker.class.getName() );
+        attrs.put( MetaSchemaConstants.M_OID_AT, OID );
+        attrs.put( MetaSchemaConstants.M_DESCRIPTION_AT, "A test syntaxChecker" );
+        
+        LdapDN dn = getSyntaxCheckerContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        getSchemaContext( service ).createSubcontext( dn, attrs );
+        
+        assertTrue( getSyntaxCheckerRegistry().hasSyntaxChecker( OID ) );
+        assertEquals( getSyntaxCheckerRegistry().getSchemaName( OID ), "apachemeta" );
+        Class<?> clazz = getSyntaxCheckerRegistry().lookup( OID ).getClass();
+        assertEquals( clazz, AcceptAllSyntaxChecker.class );
+    }
+    
+    
+    @Test
+    public void testAddSyntaxCheckerWithByteCode() throws Exception
+    {
+        InputStream in = getClass().getResourceAsStream( "DummySyntaxChecker.bytecode" );
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        while ( in.available() > 0 )
+        {
+            out.write( in.read() );
+        }
+        
+        Attributes attrs = new AttributesImpl();
+        Attribute oc = new AttributeImpl( SchemaConstants.OBJECT_CLASS_AT, "top" );
+        oc.add( MetaSchemaConstants.META_TOP_OC );
+        oc.add( MetaSchemaConstants.META_SYNTAX_CHECKER_OC );
+        attrs.put( oc );
+        attrs.put( MetaSchemaConstants.M_FQCN_AT, "DummySyntaxChecker" );
+        attrs.put( MetaSchemaConstants.M_BYTECODE_AT, out.toByteArray() );
+        attrs.put( MetaSchemaConstants.M_OID_AT, OID );
+        attrs.put( MetaSchemaConstants.M_DESCRIPTION_AT, "A test syntaxChecker" );
+        
+        LdapDN dn = getSyntaxCheckerContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        getSchemaContext( service ).createSubcontext( dn, attrs );
+        
+        assertTrue( getSyntaxCheckerRegistry().hasSyntaxChecker( OID ) );
+        assertEquals( getSyntaxCheckerRegistry().getSchemaName( OID ), "apachemeta" );
+        Class<?> clazz = getSyntaxCheckerRegistry().lookup( OID ).getClass();
+        assertEquals( clazz.getName(), "DummySyntaxChecker" );
+    }
+    
+    
+    @Test
+    public void testDeleteSyntaxChecker() throws NamingException
+    {
+        LdapDN dn = getSyntaxCheckerContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        testAddSyntaxChecker();
+        
+        getSchemaContext( service ).destroySubcontext( dn );
+
+        assertFalse( "syntaxChecker should be removed from the registry after being deleted", 
+            getSyntaxCheckerRegistry().hasSyntaxChecker( OID ) );
+
+        //noinspection EmptyCatchBlock
+        try
+        {
+            getSyntaxCheckerRegistry().lookup( OID );
+            fail( "syntaxChecker lookup should fail after deleting the syntaxChecker" );
+        }
+        catch( NamingException e )
+        {
+        }
+    }
+
+
+    @Test
+    public void testRenameSyntaxChecker() throws NamingException
+    {
+        LdapDN dn = getSyntaxCheckerContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        testAddSyntaxChecker();
+        
+        LdapDN newdn = getSyntaxCheckerContainer( "apachemeta" );
+        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + NEW_OID );
+        getSchemaContext( service ).rename( dn, newdn );
+
+        assertFalse( "old syntaxChecker OID should be removed from the registry after being renamed", 
+            getSyntaxCheckerRegistry().hasSyntaxChecker( OID ) );
+
+        //noinspection EmptyCatchBlock
+        try
+        {
+            getSyntaxCheckerRegistry().lookup( OID );
+            fail( "syntaxChecker lookup should fail after deleting the syntaxChecker" );
+        }
+        catch( NamingException e )
+        {
+        }
+
+        assertTrue( getSyntaxCheckerRegistry().hasSyntaxChecker( NEW_OID ) );
+        Class<?> clazz = getSyntaxCheckerRegistry().lookup( NEW_OID ).getClass();
+        assertEquals( clazz, AcceptAllSyntaxChecker.class );
+    }
+
+
+    @Test
+    public void testMoveSyntaxChecker() throws NamingException
+    {
+        testAddSyntaxChecker();
+        
+        LdapDN dn = getSyntaxCheckerContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+
+        LdapDN newdn = getSyntaxCheckerContainer( "apache" );
+        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        
+        getSchemaContext( service ).rename( dn, newdn );
+
+        assertTrue( "syntaxChecker OID should still be present", 
+            getSyntaxCheckerRegistry().hasSyntaxChecker( OID ) );
+        
+        assertEquals( "syntaxChecker schema should be set to apache not apachemeta", 
+            getSyntaxCheckerRegistry().getSchemaName( OID ), "apache" );
+
+        Class<?> clazz = getSyntaxCheckerRegistry().lookup( OID ).getClass();
+        assertEquals( clazz, AcceptAllSyntaxChecker.class );
+    }
+
+
+    @Test
+    public void testMoveSyntaxCheckerAndChangeRdn() throws NamingException
+    {
+        testAddSyntaxChecker();
+        
+        LdapDN dn = getSyntaxCheckerContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+
+        LdapDN newdn = getSyntaxCheckerContainer( "apache" );
+        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + NEW_OID );
+        
+        getSchemaContext( service ).rename( dn, newdn );
+
+        assertFalse( "old syntaxChecker OID should NOT be present", 
+            getSyntaxCheckerRegistry().hasSyntaxChecker( OID ) );
+        
+        assertTrue( "new syntaxChecker OID should be present", 
+            getSyntaxCheckerRegistry().hasSyntaxChecker( NEW_OID ) );
+        
+        assertEquals( "syntaxChecker with new oid should have schema set to apache NOT apachemeta", 
+            getSyntaxCheckerRegistry().getSchemaName( NEW_OID ), "apache" );
+
+        Class<?> clazz = getSyntaxCheckerRegistry().lookup( NEW_OID ).getClass();
+        assertEquals( clazz, AcceptAllSyntaxChecker.class );
+    }
+
+    
+    @Test
+    public void testModifySyntaxCheckerWithModificationItems() throws NamingException
+    {
+        testAddSyntaxChecker();
+        
+        LdapDN dn = getSyntaxCheckerContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        
+        ModificationItemImpl[] mods = new ModificationItemImpl[1];
+        Attribute attr = new AttributeImpl( MetaSchemaConstants.M_FQCN_AT, BogusSyntaxChecker.class.getName() );
+        mods[0] = new ModificationItemImpl( DirContext.REPLACE_ATTRIBUTE, attr );
+        getSchemaContext( service ).modifyAttributes( dn, mods );
+
+        assertTrue( "syntaxChecker OID should still be present", 
+            getSyntaxCheckerRegistry().hasSyntaxChecker( OID ) );
+        
+        assertEquals( "syntaxChecker schema should be set to apachemeta", 
+            getSyntaxCheckerRegistry().getSchemaName( OID ), "apachemeta" );
+
+        Class<?> clazz = getSyntaxCheckerRegistry().lookup( OID ).getClass();
+        assertEquals( clazz, BogusSyntaxChecker.class );
+    }
+
+    
+    @Test
+    public void testModifySyntaxCheckerWithAttributes() throws NamingException
+    {
+        testAddSyntaxChecker();
+        
+        LdapDN dn = getSyntaxCheckerContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        
+        Attributes mods = new AttributesImpl();
+        mods.put( MetaSchemaConstants.M_FQCN_AT, BogusSyntaxChecker.class.getName() );
+        getSchemaContext( service ).modifyAttributes( dn, DirContext.REPLACE_ATTRIBUTE, mods );
+
+        assertTrue( "syntaxChecker OID should still be present", 
+            getSyntaxCheckerRegistry().hasSyntaxChecker( OID ) );
+        
+        assertEquals( "syntaxChecker schema should be set to apachemeta", 
+            getSyntaxCheckerRegistry().getSchemaName( OID ), "apachemeta" );
+
+        Class<?> clazz = getSyntaxCheckerRegistry().lookup( OID ).getClass();
+        assertEquals( clazz, BogusSyntaxChecker.class );
+    }
+    
+
+    // ----------------------------------------------------------------------
+    // Test move, rename, and delete when a MR exists and uses the Normalizer
+    // ----------------------------------------------------------------------
+
+    
+    @Test
+    public void testDeleteSyntaxCheckerWhenInUse() throws NamingException
+    {
+        LdapDN dn = getSyntaxCheckerContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        testAddSyntaxChecker();
+        getSyntaxRegistry().register( new DummySyntax() );
+        
+        try
+        {
+            getSchemaContext( service ).destroySubcontext( dn );
+            fail( "should not be able to delete a syntaxChecker in use" );
+        }
+        catch( LdapOperationNotSupportedException e ) 
+        {
+            assertEquals( e.getResultCode(), ResultCodeEnum.UNWILLING_TO_PERFORM );
+        }
+
+        assertTrue( "syntaxChecker should still be in the registry after delete failure", 
+            getSyntaxCheckerRegistry().hasSyntaxChecker( OID ) );
+        getSyntaxRegistry().unregister( OID );
+        getOidRegistry().unregister( OID );
+    }
+    
+    
+    @Test
+    public void testMoveSyntaxCheckerWhenInUse() throws NamingException
+    {
+        testAddSyntaxChecker();
+        getSyntaxRegistry().register( new DummySyntax() );
+        
+        LdapDN dn = getSyntaxCheckerContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+
+        LdapDN newdn = getSyntaxCheckerContainer( "apache" );
+        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        
+        try
+        {
+            getSchemaContext( service ).rename( dn, newdn );
+            fail( "should not be able to move a syntaxChecker in use" );
+        }
+        catch( LdapOperationNotSupportedException e ) 
+        {
+            assertEquals( e.getResultCode(), ResultCodeEnum.UNWILLING_TO_PERFORM );
+        }
+
+        assertTrue( "syntaxChecker should still be in the registry after move failure", 
+            getSyntaxCheckerRegistry().hasSyntaxChecker( OID ) );
+        getSyntaxRegistry().unregister( OID );
+        getOidRegistry().unregister( OID );
+    }
+
+
+    @Test
+    public void testMoveSyntaxCheckerAndChangeRdnWhenInUse() throws NamingException
+    {
+        testAddSyntaxChecker();
+        getSyntaxRegistry().register( new DummySyntax() );
+        
+        LdapDN dn = getSyntaxCheckerContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+
+        LdapDN newdn = getSyntaxCheckerContainer( "apache" );
+        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + NEW_OID );
+        
+        try
+        {
+            getSchemaContext( service ).rename( dn, newdn );
+            fail( "should not be able to move a syntaxChecker in use" );
+        }
+        catch( LdapOperationNotSupportedException e ) 
+        {
+            assertEquals( e.getResultCode(), ResultCodeEnum.UNWILLING_TO_PERFORM );
+        }
+
+        assertTrue( "syntaxChecker should still be in the registry after move failure", 
+            getSyntaxCheckerRegistry().hasSyntaxChecker( OID ) );
+        getSyntaxRegistry().unregister( OID );
+        getOidRegistry().unregister( OID );
+    }
+
+    
+    @Test
+    public void testRenameSyntaxCheckerWhenInUse() throws NamingException
+    {
+        LdapDN dn = getSyntaxCheckerContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        testAddSyntaxChecker();
+        getSyntaxRegistry().register( new DummySyntax() );
+        
+        LdapDN newdn = getSyntaxCheckerContainer( "apachemeta" );
+        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + NEW_OID );
+        
+        try
+        {
+            getSchemaContext( service ).rename( dn, newdn );
+            fail( "should not be able to rename a syntaxChecker in use" );
+        }
+        catch( LdapOperationNotSupportedException e ) 
+        {
+            assertEquals( e.getResultCode(), ResultCodeEnum.UNWILLING_TO_PERFORM );
+        }
+
+        assertTrue( "syntaxChecker should still be in the registry after rename failure", 
+            getSyntaxCheckerRegistry().hasSyntaxChecker( OID ) );
+        getSyntaxRegistry().unregister( OID );
+        getOidRegistry().unregister( OID );
+    }
+
+
+    // ----------------------------------------------------------------------
+    // Let's try some freaky stuff
+    // ----------------------------------------------------------------------
+
+
+    @Test
+    public void testMoveSyntaxCheckerToTop() throws NamingException
+    {
+        testAddSyntaxChecker();
+        
+        LdapDN dn = getSyntaxCheckerContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+
+        LdapDN top = new LdapDN();
+        top.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        
+        try
+        {
+            getSchemaContext( service ).rename( dn, top );
+            fail( "should not be able to move a syntaxChecker up to ou=schema" );
+        }
+        catch( LdapInvalidNameException e ) 
+        {
+            assertEquals( e.getResultCode(), ResultCodeEnum.NAMING_VIOLATION );
+        }
+
+        assertTrue( "syntaxChecker should still be in the registry after move failure", 
+            getSyntaxCheckerRegistry().hasSyntaxChecker( OID ) );
+    }
+
+
+    @Test
+    public void testMoveSyntaxCheckerToComparatorContainer() throws NamingException
+    {
+        testAddSyntaxChecker();
+        
+        LdapDN dn = getSyntaxCheckerContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+
+        LdapDN newdn = new LdapDN( "ou=comparators,cn=apachemeta" );
+        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        
+        try
+        {
+            getSchemaContext( service ).rename( dn, newdn );
+            fail( "should not be able to move a syntaxChecker into comparators container" );
+        }
+        catch( LdapInvalidNameException e ) 
+        {
+            assertEquals( e.getResultCode(), ResultCodeEnum.NAMING_VIOLATION );
+        }
+
+        assertTrue( "syntaxChecker should still be in the registry after move failure", 
+            getSyntaxCheckerRegistry().hasSyntaxChecker( OID ) );
+    }
+    
+    
+    @Test
+    public void testAddSyntaxCheckerToDisabledSchema() throws NamingException
+    {
+        Attributes attrs = new AttributesImpl();
+        Attribute oc = new AttributeImpl( SchemaConstants.OBJECT_CLASS_AT, "top" );
+        oc.add( MetaSchemaConstants.META_TOP_OC );
+        oc.add( MetaSchemaConstants.META_SYNTAX_CHECKER_OC );
+        attrs.put( oc );
+        attrs.put( MetaSchemaConstants.M_FQCN_AT, AcceptAllSyntaxChecker.class.getName() );
+        attrs.put( MetaSchemaConstants.M_OID_AT, OID );
+        attrs.put( MetaSchemaConstants.M_DESCRIPTION_AT, "A test syntaxChecker" );
+        
+        // nis is by default inactive
+        LdapDN dn = getSyntaxCheckerContainer( "nis" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        getSchemaContext( service ).createSubcontext( dn, attrs );
+        
+        assertFalse( "adding new syntaxChecker to disabled schema should not register it into the registries", 
+            getSyntaxCheckerRegistry().hasSyntaxChecker( OID ) );
+    }
+
+
+    @Test
+    public void testMoveSyntaxCheckerToDisabledSchema() throws NamingException
+    {
+        testAddSyntaxChecker();
+        
+        LdapDN dn = getSyntaxCheckerContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+
+        // nis is inactive by default
+        LdapDN newdn = getSyntaxCheckerContainer( "nis" );
+        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        
+        getSchemaContext( service ).rename( dn, newdn );
+
+        assertFalse( "syntaxChecker OID should no longer be present", 
+            getSyntaxCheckerRegistry().hasSyntaxChecker( OID ) );
+    }
+
+
+    @Test
+    public void testMoveSyntaxCheckerToEnabledSchema() throws NamingException
+    {
+        testAddSyntaxCheckerToDisabledSchema();
+        
+        // nis is inactive by default
+        LdapDN dn = getSyntaxCheckerContainer( "nis" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+
+        assertFalse( "syntaxChecker OID should NOT be present when added to disabled nis schema", 
+            getSyntaxCheckerRegistry().hasSyntaxChecker( OID ) );
+
+        LdapDN newdn = getSyntaxCheckerContainer( "apachemeta" );
+        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        
+        getSchemaContext( service ).rename( dn, newdn );
+
+        assertTrue( "syntaxChecker OID should be present when moved to enabled schema", 
+            getSyntaxCheckerRegistry().hasSyntaxChecker( OID ) );
+        
+        assertEquals( "syntaxChecker should be in apachemeta schema after move", 
+            getSyntaxCheckerRegistry().getSchemaName( OID ), "apachemeta" );
+    }
+
+    
+    public static class BogusSyntaxChecker implements SyntaxChecker
+    {
+        public BogusSyntaxChecker()
+        {
+        }
+        
+        public void assertSyntax( Object value ) throws NamingException
+        {
+        }
+
+        public String getSyntaxOid()
+        {
+            return OID;
+        }
+
+        public boolean isValidSyntax( Object value )
+        {
+            return false;
+        }
+    }
+
+    
+    class DummySyntax implements Syntax
+    {
+        private static final long serialVersionUID = 1L;
+
+
+        public String getDescription()
+        {
+            return null;
+        }
+
+        public String getName()
+        {
+            return "dummy";
+        }
+
+        public String[] getNamesRef()
+        {
+            return new String[] { "dummy" };
+        }
+
+        public String getOid()
+        {
+            return OID;
+        }
+
+        public boolean isObsolete()
+        {
+            return false;
+        }
+
+        public SyntaxChecker getSyntaxChecker() throws NamingException
+        {
+            return null;
+        }
+
+        public boolean isHumanReadable()
+        {
+            return false;
+        }
+
+        public String getSchema()
+        {
+            return null;
+        }
+
+        public void setSchema( String schemaName )
+        {
+        }
+    }
+}
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/schema/MetaSyntaxHandlerIT.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/schema/MetaSyntaxHandlerIT.java
new file mode 100644
index 0000000..4bcc4f0
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/schema/MetaSyntaxHandlerIT.java
@@ -0,0 +1,593 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.schema;
+
+
+import org.apache.directory.server.constants.MetaSchemaConstants;
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.integ.CiRunner;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getRootContext;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getSchemaContext;
+import org.apache.directory.server.schema.registries.MatchingRuleRegistry;
+import org.apache.directory.server.schema.registries.SyntaxRegistry;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.exception.LdapInvalidNameException;
+import org.apache.directory.shared.ldap.exception.LdapOperationNotSupportedException;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.message.ModificationItemImpl;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.schema.Syntax;
+import org.apache.directory.shared.ldap.schema.syntax.AcceptAllSyntaxChecker;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.fail;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * A test case which tests the addition of various schema elements
+ * to the ldap server.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+@RunWith ( CiRunner.class )
+public class MetaSyntaxHandlerIT
+{
+    private static final String DESCRIPTION0 = "A test normalizer";
+    private static final String DESCRIPTION1 = "An alternate description";
+
+    private static final String OID = "1.3.6.1.4.1.18060.0.4.0.0.100000";
+    private static final String NEW_OID = "1.3.6.1.4.1.18060.0.4.0.0.100001";
+
+    private static final String MR_OID = "1.3.6.1.4.1.18060.0.4.0.1.100000";
+    private static final String MR_DESCRIPTION = "A test matchingRule";
+
+    private static final String SUBSCHEMA_SUBENTRY = "subschemaSubentry";
+
+
+    public static DirectoryService service;
+
+
+    private static SyntaxRegistry getSyntaxRegistry()
+    {
+        return service.getRegistries().getSyntaxRegistry();
+    }
+
+
+    private static MatchingRuleRegistry getMatchingRuleRegistry()
+    {
+        return service.getRegistries().getMatchingRuleRegistry();
+    }
+
+    
+    /**
+     * Gets relative DN to ou=schema.
+     *
+     * @param schemaName the name of the schema
+     * @return the dn of the container for the syntax entities
+     * @throws NamingException on error
+     */
+    private LdapDN getSyntaxContainer( String schemaName ) throws NamingException
+    {
+        return new LdapDN( "ou=syntaxes,cn=" + schemaName );
+    }
+    
+    
+    // ----------------------------------------------------------------------
+    // Test all core methods with normal operational pathways
+    // ----------------------------------------------------------------------
+
+    
+    @Test
+    public void testAddSyntax() throws NamingException
+    {
+        Attributes attrs = new AttributesImpl();
+        Attribute oc = new AttributeImpl( SchemaConstants.OBJECT_CLASS_AT, "top" );
+        oc.add( MetaSchemaConstants.META_TOP_OC );
+        oc.add( MetaSchemaConstants.META_SYNTAX_OC );
+        attrs.put( oc );
+        attrs.put( MetaSchemaConstants.M_OID_AT, OID );
+        attrs.put( MetaSchemaConstants.M_DESCRIPTION_AT, DESCRIPTION0 );
+        
+        LdapDN dn = getSyntaxContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        createDummySyntaxChecker( OID, "apachemeta" );
+        getSchemaContext( service ).createSubcontext( dn, attrs );
+        
+        assertTrue( getSyntaxRegistry().hasSyntax( OID ) );
+        assertEquals( getSyntaxRegistry().getSchemaName( OID ), "apachemeta" );
+    }
+    
+    
+    @Test
+    public void testDeleteSyntax() throws NamingException
+    {
+        LdapDN dn = getSyntaxContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        testAddSyntax();
+        
+        getSchemaContext( service ).destroySubcontext( dn );
+
+        assertFalse( "syntax should be removed from the registry after being deleted", 
+            getSyntaxRegistry().hasSyntax( OID ) );
+
+        //noinspection EmptyCatchBlock
+        try
+        {
+            getSyntaxRegistry().lookup( OID );
+            fail( "syntax lookup should fail after deleting it" );
+        }
+        catch( NamingException e )
+        {
+        }
+    }
+
+
+    @Test
+    public void testRenameSyntax() throws NamingException
+    {
+        LdapDN dn = getSyntaxContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        testAddSyntax();
+        
+        LdapDN newdn = getSyntaxContainer( "apachemeta" );
+        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + NEW_OID );
+        getSchemaContext( service ).rename( dn, newdn );
+
+        assertFalse( "old syntax OID should be removed from the registry after being renamed", 
+            getSyntaxRegistry().hasSyntax( OID ) );
+
+        //noinspection EmptyCatchBlock
+        try
+        {
+            getSyntaxRegistry().lookup( OID );
+            fail( "syntax lookup should fail after deleting the syntax" );
+        }
+        catch( NamingException e )
+        {
+        }
+
+        assertTrue( getSyntaxRegistry().hasSyntax( NEW_OID ) );
+    }
+
+
+    @Test
+    public void testMoveSyntax() throws NamingException
+    {
+        testAddSyntax();
+        
+        LdapDN dn = getSyntaxContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+
+        LdapDN newdn = getSyntaxContainer( "apache" );
+        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        
+        getSchemaContext( service ).rename( dn, newdn );
+
+        assertTrue( "syntax OID should still be present", 
+            getSyntaxRegistry().hasSyntax( OID ) );
+        
+        assertEquals( "syntax schema should be set to apache not apachemeta", 
+            getSyntaxRegistry().getSchemaName( OID ), "apache" );
+    }
+
+
+    @Test
+    public void testMoveSyntaxAndChangeRdn() throws NamingException
+    {
+        testAddSyntax();
+        
+        LdapDN dn = getSyntaxContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+
+        LdapDN newdn = getSyntaxContainer( "apache" );
+        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + NEW_OID );
+        
+        getSchemaContext( service ).rename( dn, newdn );
+
+        assertFalse( "old syntax OID should NOT be present", 
+            getSyntaxRegistry().hasSyntax( OID ) );
+        
+        assertTrue( "new syntax OID should be present", 
+            getSyntaxRegistry().hasSyntax( NEW_OID ) );
+        
+        assertEquals( "syntax with new oid should have schema set to apache NOT apachemeta", 
+            getSyntaxRegistry().getSchemaName( NEW_OID ), "apache" );
+    }
+
+    
+    @Test
+    public void testModifySyntaxWithModificationItems() throws NamingException
+    {
+        testAddSyntax();
+        
+        Syntax syntax = getSyntaxRegistry().lookup( OID );
+        assertEquals( syntax.getDescription(), DESCRIPTION0 );
+
+        LdapDN dn = getSyntaxContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        
+        ModificationItemImpl[] mods = new ModificationItemImpl[1];
+        Attribute attr = new AttributeImpl( MetaSchemaConstants.M_DESCRIPTION_AT, DESCRIPTION1 );
+        mods[0] = new ModificationItemImpl( DirContext.REPLACE_ATTRIBUTE, attr );
+        getSchemaContext( service ).modifyAttributes( dn, mods );
+
+        assertTrue( "syntax OID should still be present", 
+            getSyntaxRegistry().hasSyntax( OID ) );
+        
+        assertEquals( "syntax schema should be set to apachemeta", 
+            getSyntaxRegistry().getSchemaName( OID ), "apachemeta" );
+        
+        syntax = getSyntaxRegistry().lookup( OID );
+        assertEquals( syntax.getDescription(), DESCRIPTION1 );
+    }
+
+    
+    @Test
+    public void testModifySyntaxWithAttributes() throws NamingException
+    {
+        testAddSyntax();
+        
+        Syntax syntax = getSyntaxRegistry().lookup( OID );
+        assertEquals( syntax.getDescription(), DESCRIPTION0 );
+
+        LdapDN dn = getSyntaxContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        
+        Attributes mods = new AttributesImpl();
+        mods.put( MetaSchemaConstants.M_DESCRIPTION_AT, DESCRIPTION1 );
+        getSchemaContext( service ).modifyAttributes( dn, DirContext.REPLACE_ATTRIBUTE, mods );
+
+        assertTrue( "syntax OID should still be present", 
+            getSyntaxRegistry().hasSyntax( OID ) );
+        
+        assertEquals( "syntax schema should be set to apachemeta", 
+            getSyntaxRegistry().getSchemaName( OID ), "apachemeta" );
+
+        syntax = getSyntaxRegistry().lookup( OID );
+        assertEquals( syntax.getDescription(), DESCRIPTION1 );
+    }
+    
+
+    // ----------------------------------------------------------------------
+    // Test move, rename, and delete when a MR exists and uses the Normalizer
+    // ----------------------------------------------------------------------
+
+    
+    @Test
+    public void testDeleteSyntaxWhenInUse() throws NamingException
+    {
+        LdapDN dn = getSyntaxContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        testAddSyntax();
+        addDependeeMatchingRule( OID );
+        
+        try
+        {
+            getSchemaContext( service ).destroySubcontext( dn );
+            fail( "should not be able to delete a syntax in use" );
+        }
+        catch( LdapOperationNotSupportedException e ) 
+        {
+            assertEquals( e.getResultCode(), ResultCodeEnum.UNWILLING_TO_PERFORM );
+        }
+
+        assertTrue( "syntax should still be in the registry after delete failure", 
+            getSyntaxRegistry().hasSyntax( OID ) );
+    }
+    
+    
+    @Test
+    public void testMoveSyntaxWhenInUse() throws NamingException
+    {
+        testAddSyntax();
+        addDependeeMatchingRule( OID );
+        
+        LdapDN dn = getSyntaxContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+
+        LdapDN newdn = getSyntaxContainer( "apache" );
+        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        
+        try
+        {
+            getSchemaContext( service ).rename( dn, newdn );
+            fail( "should not be able to move a syntax in use" );
+        }
+        catch( LdapOperationNotSupportedException e ) 
+        {
+            assertEquals( e.getResultCode(), ResultCodeEnum.UNWILLING_TO_PERFORM );
+        }
+
+        assertTrue( "syntax should still be in the registry after move failure", 
+            getSyntaxRegistry().hasSyntax( OID ) );
+    }
+
+
+    @Test
+    public void testMoveSyntaxAndChangeRdnWhenInUse() throws NamingException
+    {
+        testAddSyntax();
+        addDependeeMatchingRule( OID );
+        
+        LdapDN dn = getSyntaxContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+
+        LdapDN newdn = getSyntaxContainer( "apache" );
+        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + NEW_OID );
+        
+        try
+        {
+            getSchemaContext( service ).rename( dn, newdn );
+            fail( "should not be able to move a syntax in use" );
+        }
+        catch( LdapOperationNotSupportedException e ) 
+        {
+            assertEquals( e.getResultCode(), ResultCodeEnum.UNWILLING_TO_PERFORM );
+        }
+
+        assertTrue( "syntax should still be in the registry after move failure", 
+            getSyntaxRegistry().hasSyntax( OID ) );
+    }
+
+    
+    /**
+     * Gets relative DN to ou=schema.
+     *
+     * @param schemaName the name of the schmea
+     * @return the dn of the container entry holding matchingRules
+     * @throws NamingException on parse errors
+     */
+    private LdapDN getMatchingRuleContainer( String schemaName ) throws NamingException
+    {
+        return new LdapDN( "ou=matchingRules,cn=" + schemaName );
+    }
+    
+    
+    private void addDependeeMatchingRule( String oid ) throws NamingException
+    {
+        Attributes attrs = new AttributesImpl();
+        Attribute oc = new AttributeImpl( SchemaConstants.OBJECT_CLASS_AT, "top" );
+        oc.add( MetaSchemaConstants.META_TOP_OC );
+        oc.add( MetaSchemaConstants.META_MATCHING_RULE_OC );
+        attrs.put( oc );
+        attrs.put( MetaSchemaConstants.M_OID_AT, MR_OID );
+        attrs.put( MetaSchemaConstants.M_SYNTAX_AT, OID );
+        attrs.put( MetaSchemaConstants.M_DESCRIPTION_AT, MR_DESCRIPTION );
+        
+        LdapDN dn = getMatchingRuleContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + MR_OID );
+        getSchemaContext( service ).createSubcontext( dn, attrs );
+        
+        assertTrue( getMatchingRuleRegistry().hasMatchingRule( MR_OID ) );
+        assertEquals( getMatchingRuleRegistry().getSchemaName( MR_OID ), "apachemeta" );
+    }
+
+    
+    @Test
+    public void testRenameNormalizerWhenInUse() throws NamingException
+    {
+        LdapDN dn = getSyntaxContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        testAddSyntax();
+        addDependeeMatchingRule( OID );
+        
+        LdapDN newdn = getSyntaxContainer( "apachemeta" );
+        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + NEW_OID );
+        
+        try
+        {
+            getSchemaContext( service ).rename( dn, newdn );
+            fail( "should not be able to rename a syntax in use" );
+        }
+        catch( LdapOperationNotSupportedException e ) 
+        {
+            assertEquals( e.getResultCode(), ResultCodeEnum.UNWILLING_TO_PERFORM );
+        }
+
+        assertTrue( "syntax should still be in the registry after rename failure", 
+            getSyntaxRegistry().hasSyntax( OID ) );
+    }
+
+
+    // ----------------------------------------------------------------------
+    // Let's try some freaky stuff
+    // ----------------------------------------------------------------------
+
+
+    @Test
+    public void testMoveSyntaxToTop() throws NamingException
+    {
+        testAddSyntax();
+        
+        LdapDN dn = getSyntaxContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+
+        LdapDN top = new LdapDN();
+        top.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        
+        try
+        {
+            getSchemaContext( service ).rename( dn, top );
+            fail( "should not be able to move a syntax up to ou=schema" );
+        }
+        catch( LdapInvalidNameException e ) 
+        {
+            assertEquals( e.getResultCode(), ResultCodeEnum.NAMING_VIOLATION );
+        }
+
+        assertTrue( "syntax should still be in the registry after move failure", 
+            getSyntaxRegistry().hasSyntax( OID ) );
+    }
+
+
+    @Test
+    public void testMoveSyntaxToComparatorContainer() throws NamingException
+    {
+        testAddSyntax();
+        
+        LdapDN dn = getSyntaxContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+
+        LdapDN newdn = new LdapDN( "ou=comparators,cn=apachemeta" );
+        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        
+        try
+        {
+            getSchemaContext( service ).rename( dn, newdn );
+            fail( "should not be able to move a syntax into comparators container" );
+        }
+        catch( LdapInvalidNameException e ) 
+        {
+            assertEquals( e.getResultCode(), ResultCodeEnum.NAMING_VIOLATION );
+        }
+
+        assertTrue( "syntax should still be in the registry after move failure", 
+            getSyntaxRegistry().hasSyntax( OID ) );
+    }
+    
+    
+    @Test
+    public void testAddSyntaxToDisabledSchema() throws NamingException
+    {
+        Attributes attrs = new AttributesImpl();
+        Attribute oc = new AttributeImpl( SchemaConstants.OBJECT_CLASS_AT, "top" );
+        oc.add( MetaSchemaConstants.META_TOP_OC );
+        oc.add( MetaSchemaConstants.META_SYNTAX_OC );
+        attrs.put( oc );
+        attrs.put( MetaSchemaConstants.M_OID_AT, OID );
+        attrs.put( MetaSchemaConstants.M_DESCRIPTION_AT, DESCRIPTION0 );
+        
+        // nis is by default inactive
+        LdapDN dn = getSyntaxContainer( "nis" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        createDummySyntaxChecker( OID, "nis" );
+        getSchemaContext( service ).createSubcontext( dn, attrs );
+        
+        assertFalse( "adding new syntax to disabled schema should not register it into the registries", 
+            getSyntaxRegistry().hasSyntax( OID ) );
+    }
+
+
+    @Test
+    public void testMoveSyntaxToDisabledSchema() throws NamingException
+    {
+        testAddSyntax();
+        
+        LdapDN dn = getSyntaxContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+
+        // nis is inactive by default
+        LdapDN newdn = getSyntaxContainer( "nis" );
+        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        
+        getSchemaContext( service ).rename( dn, newdn );
+
+        assertFalse( "syntax OID should no longer be present", 
+            getSyntaxRegistry().hasSyntax( OID ) );
+    }
+
+
+    @Test
+    public void testMoveSyntaxToEnabledSchema() throws NamingException
+    {
+        testAddSyntaxToDisabledSchema();
+        
+        // nis is inactive by default
+        LdapDN dn = getSyntaxContainer( "nis" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+
+        assertFalse( "syntax OID should NOT be present when added to disabled nis schema", 
+            getSyntaxRegistry().hasSyntax( OID ) );
+
+        LdapDN newdn = getSyntaxContainer( "apachemeta" );
+        newdn.add( MetaSchemaConstants.M_OID_AT + "=" + OID );
+        
+        getSchemaContext( service ).rename( dn, newdn );
+
+        assertTrue( "syntax OID should be present when moved to enabled schema", 
+            getSyntaxRegistry().hasSyntax( OID ) );
+        
+        assertEquals( "syntax should be in apachemeta schema after move", 
+            getSyntaxRegistry().getSchemaName( OID ), "apachemeta" );
+    }
+
+
+    private void createDummySyntaxChecker( String oid, String schema ) throws NamingException
+    {
+        List<String> descriptions = new ArrayList<String>();
+        descriptions.add( "( " + oid + " DESC 'bogus desc' FQCN " + AcceptAllSyntaxChecker.class.getName() 
+            + " X-SCHEMA '" + schema + "' )" );
+        modify( DirContext.ADD_ATTRIBUTE, descriptions, "syntaxCheckers" );
+    }
+    
+    
+    private void modify( int op, List<String> descriptions, String opAttr ) throws NamingException
+    {
+        LdapDN dn = new LdapDN( getSubschemaSubentryDN() );
+        Attribute attr = new AttributeImpl( opAttr );
+        for ( String description : descriptions )
+        {
+            attr.add( description );
+        }
+        
+        Attributes mods = new AttributesImpl();
+        mods.put( attr );
+        
+        getRootContext( service ).modifyAttributes( dn, op, mods );
+    }
+    
+    
+    /**
+     * Get's the subschemaSubentry attribute value from the rootDSE.
+     * 
+     * @return the subschemaSubentry distinguished name
+     * @throws NamingException if there are problems accessing the RootDSE
+     */
+    private String getSubschemaSubentryDN() throws NamingException
+    {
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.OBJECT_SCOPE );
+        controls.setReturningAttributes( new String[]{ SUBSCHEMA_SUBENTRY } );
+        
+        NamingEnumeration<SearchResult> results = getRootContext( service ).search(
+                "", "(objectClass=*)", controls );
+        SearchResult result = results.next();
+        results.close();
+        Attribute subschemaSubentry = result.getAttributes().get( SUBSCHEMA_SUBENTRY );
+        return ( String ) subschemaSubentry.get();
+    }
+}
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/schema/ObjectClassCreateIT.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/schema/ObjectClassCreateIT.java
new file mode 100644
index 0000000..0e27971
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/schema/ObjectClassCreateIT.java
@@ -0,0 +1,130 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.
+ *
+ */
+package org.apache.directory.server.core.schema;
+
+
+import org.apache.directory.server.constants.MetaSchemaConstants;
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.integ.CiRunner;
+import static org.apache.directory.server.core.integ.IntegrationUtils.*;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.BasicAttribute;
+import javax.naming.directory.BasicAttributes;
+
+
+@RunWith ( CiRunner.class )
+public class ObjectClassCreateIT
+{
+    private String testOID = "1.3.6.1.4.1.18060.0.4.0.3.1.555555.5555.5555555";
+
+
+    public static DirectoryService service;
+
+
+    /**
+     * Gets relative DN to ou=schema.
+     *
+     * @param schemaName the name of the schema
+     * @return the dn of the objectClass container
+     * @throws NamingException on error
+     */
+    private LdapDN getObjectClassContainer( String schemaName ) throws NamingException
+    {
+        return new LdapDN( "ou=objectClasses,cn=" + schemaName );
+    }
+
+
+    /*
+     * Test that I can create an ObjectClass entry with an invalid
+     */
+    @Test
+    public void testCreateObjectClassWithInvalidNameAttribute() throws NamingException
+    {
+        Attributes attributes = new BasicAttributes();
+        Attribute  objectClassAttribute = new BasicAttribute( "objectClass" );
+        
+        objectClassAttribute.add( "top" );
+        objectClassAttribute.add( "metaTop" );
+        objectClassAttribute.add( "metaObjectClass" );
+        
+        attributes.put( objectClassAttribute );
+        
+        attributes.put( "m-oid", "testOID" );
+        
+        // This name is invalid
+        attributes.put( "m-name", "http://example.com/users/accounts/L0" );
+        
+        LdapDN dn = getObjectClassContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + testOID );
+        
+        try
+        {
+            getSchemaContext( service ).createSubcontext( dn, attributes );
+            fail(); // Should not reach this point
+        }
+        catch ( NamingException ne )
+        {
+            assertTrue( true );
+        }
+    }
+
+    /*
+     * Test that I can create an ObjectClass entry with an invalid
+     */
+    @Test
+    public void testCreateObjectClassWithNoObjectClass() throws NamingException
+    {
+        Attributes attributes = new BasicAttributes();
+        Attribute  objectClassAttribute = new BasicAttribute( "objectClass" );
+        
+        objectClassAttribute.add( "top" );
+        objectClassAttribute.add( "metaTop" );
+        objectClassAttribute.add( "metaObjectClass" );
+        
+        // Don't put the objectclasses in the entry : this is in purpose !
+        // attributes.put( objectClassAttribute );
+        
+        attributes.put( "m-oid", "testOID" );
+        
+        // This name is invalid
+        attributes.put( "m-name", "no-objectClasses" );
+        
+        LdapDN dn = getObjectClassContainer( "apachemeta" );
+        dn.add( MetaSchemaConstants.M_OID_AT + "=" + testOID );
+        
+        try
+        {
+            getSchemaContext( service ).createSubcontext( dn, attributes );
+            fail(); // Should not reach this point
+        }
+        catch ( NamingException ne )
+        {
+            assertTrue( true );
+        }
+    }
+}
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/schema/SchemaISuite.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/schema/SchemaISuite.java
new file mode 100644
index 0000000..9af5d2e
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/schema/SchemaISuite.java
@@ -0,0 +1,53 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements.  See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership.  The ASF licenses this file

+ * to you 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.

+ */

+package org.apache.directory.server.core.schema;

+

+

+import org.apache.directory.server.core.integ.CiSuite;

+import org.apache.directory.server.core.integ.Level;

+import org.apache.directory.server.core.integ.annotations.*;

+import org.junit.runner.RunWith;

+import org.junit.runners.Suite;

+

+

+/**

+ * Document me!

+ *

+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>

+ * @version $Rev$, $Date$

+ */

+@RunWith ( CiSuite.class )

+@Suite.SuiteClasses ( {

+        MetaAttributeTypeHandlerIT.class,

+        MetaComparatorHandlerIT.class,

+        MetaMatchingRuleHandlerIT.class,

+        MetaNormalizerHandlerIT.class,

+        MetaObjectClassHandlerIT.class,

+        MetaSchemaHandlerIT.class,

+        MetaSyntaxCheckerHandlerIT.class,

+        MetaSyntaxHandlerIT.class,

+        ObjectClassCreateIT.class,

+        SchemaPersistenceIT.class,

+        SubschemaSubentryIT.class,

+        SchemaServiceIT.class

+        } )

+@CleanupLevel ( Level.SUITE )

+public class SchemaISuite

+{

+}

diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/schema/SchemaPersistenceIT.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/schema/SchemaPersistenceIT.java
new file mode 100755
index 0000000..f433e24
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/schema/SchemaPersistenceIT.java
@@ -0,0 +1,263 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.schema;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.integ.CiRunner;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getRootContext;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getSchemaContext;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.message.ModificationItemImpl;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.schema.syntax.AttributeTypeDescription;
+import org.apache.directory.shared.ldap.schema.syntax.parser.AttributeTypeDescriptionSchemaParser;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * An integration test class for testing persistence for various operations 
+ * on the subschemaSubentry with server restarts.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+@RunWith(CiRunner.class)
+public class SchemaPersistenceIT
+{
+    private static final String SUBSCHEMA_SUBENTRY = "subschemaSubentry";
+    private static final AttributeTypeDescriptionSchemaParser ATTRIBUTE_TYPE_DESCRIPTION_SCHEMA_PARSER = new AttributeTypeDescriptionSchemaParser();
+
+    public static DirectoryService service;
+
+
+    /**
+     * Tests to see if an attributeType is persisted when added, then server 
+     * is shutdown, then restarted again.
+     *
+     * @throws Exception on error
+     */
+    @Test
+    public void testAddAttributeTypePersistence() throws Exception
+    {
+        try
+        {
+            enableSchema( "nis" );
+            List<String> descriptions = new ArrayList<String>();
+
+            // -------------------------------------------------------------------
+            // test successful add with everything
+            // -------------------------------------------------------------------
+
+            descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.2.10000 NAME 'type0' " + "OBSOLETE SUP 2.5.4.41 "
+                + "EQUALITY caseExactIA5Match " + "ORDERING octetStringOrderingMatch "
+                + "SUBSTR caseExactIA5SubstringsMatch COLLECTIVE " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 "
+                + "SINGLE-VALUE USAGE userApplications X-SCHEMA 'nis' )" );
+            descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.2.10001 NAME ( 'type1' 'altName' ) "
+                + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SUP 2.5.4.41 "
+                + "NO-USER-MODIFICATION USAGE directoryOperation X-SCHEMA 'nis' )" );
+
+            modify( DirContext.ADD_ATTRIBUTE, descriptions, "attributeTypes" );
+
+            checkAttributeTypePresent( "1.3.6.1.4.1.18060.0.4.1.2.10000", "nis", true );
+            checkAttributeTypePresent( "1.3.6.1.4.1.18060.0.4.1.2.10001", "nis", true );
+
+            // sync operation happens anyway on shutdowns but just to make sure we can do it again
+            service.sync();
+
+            service.shutdown();
+            service.startup();
+
+            AttributesImpl attrs = new AttributesImpl( "objectClass", "metaSchema" );
+            attrs.put( "cn", "blah" );
+            getSchemaContext( service ).createSubcontext( "cn=blah", attrs );
+
+            checkAttributeTypePresent( "1.3.6.1.4.1.18060.0.4.1.2.10000", "nis", true );
+            checkAttributeTypePresent( "1.3.6.1.4.1.18060.0.4.1.2.10001", "nis", true );
+        }
+        catch ( Exception e )
+        {
+            e.printStackTrace();
+            throw e;
+        }
+    }
+
+
+    // -----------------------------------------------------------------------
+    // Private Utility Methods 
+    // -----------------------------------------------------------------------
+
+    private void modify( int op, List<String> descriptions, String opAttr ) throws Exception
+    {
+        LdapDN dn = new LdapDN( getSubschemaSubentryDN() );
+        Attribute attr = new AttributeImpl( opAttr );
+        for ( String description : descriptions )
+        {
+            attr.add( description );
+        }
+
+        Attributes mods = new AttributesImpl();
+        mods.put( attr );
+
+        getRootContext( service ).modifyAttributes( dn, op, mods );
+    }
+
+
+    private void enableSchema( String schemaName ) throws NamingException
+    {
+        // now enable the test schema
+        ModificationItemImpl[] mods = new ModificationItemImpl[1];
+        Attribute attr = new AttributeImpl( "m-disabled", "FALSE" );
+        mods[0] = new ModificationItemImpl( DirContext.REPLACE_ATTRIBUTE, attr );
+        getSchemaContext( service ).modifyAttributes( "cn=" + schemaName, mods );
+    }
+
+
+    /**
+     * Get's the subschemaSubentry attribute value from the rootDSE.
+     * 
+     * @return the subschemaSubentry distinguished name
+     * @throws NamingException if there are problems accessing the RootDSE
+     */
+    private String getSubschemaSubentryDN() throws NamingException
+    {
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.OBJECT_SCOPE );
+        controls.setReturningAttributes( new String[]
+            { SUBSCHEMA_SUBENTRY } );
+
+        NamingEnumeration<SearchResult> results = getRootContext( service ).search( "", "(objectClass=*)", controls );
+        SearchResult result = results.next();
+        results.close();
+        Attribute subschemaSubentry = result.getAttributes().get( SUBSCHEMA_SUBENTRY );
+        return ( String ) subschemaSubentry.get();
+    }
+
+
+    /**
+     * Gets the subschemaSubentry attributes for the global schema.
+     * 
+     * @return all operational attributes of the subschemaSubentry 
+     * @throws NamingException if there are problems accessing this entry
+     */
+    private Attributes getSubschemaSubentryAttributes() throws NamingException
+    {
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.OBJECT_SCOPE );
+        controls.setReturningAttributes( new String[]
+            { "+", "*" } );
+
+        NamingEnumeration<SearchResult> results = getRootContext( service ).search( getSubschemaSubentryDN(),
+            "(objectClass=*)", controls );
+        SearchResult result = results.next();
+        results.close();
+        return result.getAttributes();
+    }
+
+
+    private void checkAttributeTypePresent( String oid, String schemaName, boolean isPresent ) throws Exception
+    {
+        // -------------------------------------------------------------------
+        // check first to see if it is present in the subschemaSubentry
+        // -------------------------------------------------------------------
+
+        Attributes attrs = getSubschemaSubentryAttributes();
+        Attribute attrTypes = attrs.get( "attributeTypes" );
+        AttributeTypeDescription attributeTypeDescription = null;
+        for ( int ii = 0; ii < attrTypes.size(); ii++ )
+        {
+            String desc = ( String ) attrTypes.get( ii );
+            if ( desc.indexOf( oid ) != -1 )
+            {
+                attributeTypeDescription = ATTRIBUTE_TYPE_DESCRIPTION_SCHEMA_PARSER
+                    .parseAttributeTypeDescription( desc );
+                break;
+            }
+        }
+
+        if ( isPresent )
+        {
+            assertNotNull( attributeTypeDescription );
+            assertEquals( oid, attributeTypeDescription.getNumericOid() );
+        }
+        else
+        {
+            assertNull( attributeTypeDescription );
+        }
+
+        // -------------------------------------------------------------------
+        // check next to see if it is present in the schema partition
+        // -------------------------------------------------------------------
+
+        attrs = null;
+
+        if ( isPresent )
+        {
+            attrs = getSchemaContext( service ).getAttributes( "m-oid=" + oid + ",ou=attributeTypes,cn=" + schemaName );
+            assertNotNull( attrs );
+        }
+        else
+        {
+            //noinspection EmptyCatchBlock
+            try
+            {
+                attrs = getSchemaContext( service ).getAttributes(
+                    "m-oid=" + oid + ",ou=attributeTypes,cn=" + schemaName );
+                fail( "should never get here" );
+            }
+            catch ( NamingException e )
+            {
+            }
+            assertNull( attrs );
+        }
+
+        // -------------------------------------------------------------------
+        // check to see if it is present in the attributeTypeRegistry
+        // -------------------------------------------------------------------
+
+        if ( isPresent )
+        {
+            assertTrue( service.getRegistries().getAttributeTypeRegistry().hasAttributeType( oid ) );
+        }
+        else
+        {
+            assertFalse( service.getRegistries().getAttributeTypeRegistry().hasAttributeType( oid ) );
+        }
+    }
+}
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/schema/SchemaServiceIT.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/schema/SchemaServiceIT.java
new file mode 100644
index 0000000..8f419ae
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/schema/SchemaServiceIT.java
@@ -0,0 +1,905 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.schema;
+
+
+import java.io.StringReader;
+import java.util.HashMap;
+import java.util.Map;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.LdapContext;
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.integ.CiRunner;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getRootContext;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getSystemContext;
+import org.apache.directory.server.core.integ.annotations.ApplyLdifs;
+
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.exception.LdapSchemaViolationException;
+import org.apache.directory.shared.ldap.ldif.LdifEntry;
+import org.apache.directory.shared.ldap.ldif.LdifReader;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+
+/**
+ * Test cases for the schema service.  This is for 
+ * <a href="http://issues.apache.org/jira/browse/DIREVE-276">DIREVE-276</a>.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+@ApplyLdifs( {
+    // Entry # 1
+    "dn: cn=person0,ou=system\n" +
+    "objectClass: person\n" +
+    "cn: person0\n" +
+    "sn: sn_person0\n",
+    // Entry # 2
+    "dn: cn=person1,ou=system\n" +
+    "objectClass: organizationalPerson\n" +
+    "cn: person1\n" +
+    "sn: sn_person1\n" +
+    "seealso: cn=Good One,ou=people,o=sevenSeas\n" +
+    "seealso:: Y249QmFkIEXDqWvDoCxvdT1wZW9wbGUsbz1zZXZlblNlYXM=\n",
+    // Entry # 3
+    "dn: cn=person2,ou=system\n" +
+    "objectClass: inetOrgPerson\n" +
+    "cn: person2\n" +
+    "sn: sn_person2\n" }
+    )
+@RunWith ( CiRunner.class )
+public class SchemaServiceIT
+{
+    /** The Directory service */
+    public static DirectoryService service;
+
+
+    /**
+     * For <a href="https://issues.apache.org/jira/browse/DIRSERVER-925">DIRSERVER-925</a>.
+     *
+     * @throws NamingException on error
+     */
+    @Test
+    public void testNoStructuralObjectClass() throws NamingException
+    {
+        Attributes attrs = new AttributesImpl( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC );
+        attrs.get( SchemaConstants.OBJECT_CLASS_AT ).add( "uidObject" );
+        attrs.put( SchemaConstants.UID_AT, "invalid" );
+        
+        try
+        {
+            getSystemContext( service ).createSubcontext( "uid=invalid", attrs );
+        }
+        catch ( LdapSchemaViolationException e )
+        {
+            assertEquals( ResultCodeEnum.OBJECT_CLASS_VIOLATION, e.getResultCode() );
+        }
+    }
+    
+    
+    /**
+     * For <a href="https://issues.apache.org/jira/browse/DIRSERVER-925">DIRSERVER-925</a>.
+     *
+     * @throws NamingException on error
+     */
+    @Test
+    public void testMultipleStructuralObjectClasses() throws NamingException
+    {
+        Attributes attrs = new AttributesImpl( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC );
+        attrs.get( SchemaConstants.OBJECT_CLASS_AT ).add( SchemaConstants.ORGANIZATIONAL_UNIT_OC );
+        attrs.get( SchemaConstants.OBJECT_CLASS_AT ).add( SchemaConstants.PERSON_OC );
+        attrs.put( SchemaConstants.OU_AT, "comedy" );
+        attrs.put( SchemaConstants.CN_AT, "Jack Black" );
+        attrs.put( SchemaConstants.SN_AT, "Black" );
+        
+        try
+        {
+            getSystemContext( service ).createSubcontext( "cn=Jack Black", attrs );
+        }
+        catch ( LdapSchemaViolationException e )
+        {
+            assertEquals( ResultCodeEnum.OBJECT_CLASS_VIOLATION, e.getResultCode() );
+        }
+    }
+    
+    
+    /**
+     * For <a href="https://issues.apache.org/jira/browse/DIRSERVER-904">DIRSERVER-904</a>.
+     *
+     * @throws NamingException on error
+     */
+    @Test
+    public void testAddingTwoDifferentEntitiesWithSameOid() throws NamingException
+    {
+        String numberOfGunsAttrLdif = "dn: m-oid=1.3.6.1.4.1.18060.0.4.1.2.999,ou=attributeTypes,cn=other,ou=schema\n" +
+            "m-usage: USER_APPLICATIONS\n" +
+            "m-equality: integerOrderingMatch\n" +
+            "objectClass: metaAttributeType\n" +
+            "objectClass: metaTop\n" +
+            "objectClass: top\n" +
+            "m-name: numberOfGuns\n" +
+            "m-oid: 1.3.6.1.4.1.18060.0.4.1.2.999\n" +
+            "m-singleValue: TRUE\n" +
+            "m-description: Number of guns of a ship\n" +
+            "m-collective: FALSE\n" +
+            "m-obsolete: FALSE\n" +
+            "m-noUserModification: FALSE\n" +
+            "m-syntax: 1.3.6.1.4.1.1466.115.121.1.27\n";
+        String shipOCLdif = "dn: m-oid=1.3.6.1.4.1.18060.0.4.1.2.999,ou=objectClasses,cn=other,ou=schema\n" +
+            "objectClass: top\n" +
+            "objectClass: metaTop\n" +
+            "objectClass: metaObjectclass\n" +
+            "m-supObjectClass: top\n" +
+            "m-oid: 1.3.6.1.4.1.18060.0.4.1.2.999\n" +
+            "m-name: ship\n" +
+            "m-must: cn\n" +
+            "m-may: numberOfGuns\n" +
+            "m-may: description\n" +
+            "m-typeObjectClass: STRUCTURAL\n" +
+            "m-obsolete: FALSE\n" +
+            "m-description: A ship\n";
+
+        StringReader in = new StringReader( numberOfGunsAttrLdif + "\n\n" + shipOCLdif );
+        LdifReader ldifReader = new LdifReader( in );
+        LdifEntry numberOfGunsAttrEntry = ldifReader.next();
+        LdifEntry shipOCEntry = ldifReader.next();
+        assertFalse( ldifReader.hasNext() );
+        
+        // should be fine with unique OID
+        LdapContext root = getRootContext( service );
+        root.createSubcontext( numberOfGunsAttrEntry.getDn(), numberOfGunsAttrEntry.getAttributes() );
+         
+        // should blow chuncks using same OID
+        try
+        {
+            root.createSubcontext( shipOCEntry.getDn(), shipOCEntry.getAttributes() );
+            fail( "Should not be possible to create two schema entities with the same OID." );
+        }
+        catch( NamingException e )
+        {
+            assertTrue( true );
+        }
+    }
+    
+    
+    /**
+     * Test that we have all the needed ObjectClasses
+     * 
+     * @throws NamingException on error
+     */
+    @Test
+    public void testFillInObjectClasses() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        Attribute ocs = sysRoot.getAttributes( "cn=person0" ).get( "objectClass" );
+        assertEquals( 2, ocs.size() );
+        assertTrue( ocs.contains( "top" ) );
+        assertTrue( ocs.contains( "person" ) );
+
+        ocs = sysRoot.getAttributes( "cn=person1" ).get( "objectClass" );
+        assertEquals( 3, ocs.size() );
+        assertTrue( ocs.contains( "top" ) );
+        assertTrue( ocs.contains( "person" ) );
+        assertTrue( ocs.contains( "organizationalPerson" ) );
+
+        ocs = sysRoot.getAttributes( "cn=person2" ).get( "objectClass" );
+        assertEquals( 4, ocs.size() );
+        assertTrue( ocs.contains( "top" ) );
+        assertTrue( ocs.contains( "person" ) );
+        assertTrue( ocs.contains( "organizationalPerson" ) );
+        assertTrue( ocs.contains( "inetOrgPerson" ) );
+    }
+
+
+    /**
+     * Search all the entries with a 'person' ObjectClass, or an ObjectClass
+     * inheriting from 'person' 
+     *
+     * @throws NamingException on error
+     */
+    @Test
+    public void testSearchForPerson() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+        Map<String, Attributes> persons = new HashMap<String, Attributes>();
+        NamingEnumeration<SearchResult> results = sysRoot.search( "", "(objectClass=*person)", controls );
+        
+        while ( results.hasMore() )
+        {
+            SearchResult result = results.next();
+            persons.put( result.getName(), result.getAttributes() );
+        }
+
+        // admin is extra
+        assertEquals( 4, persons.size() );
+
+        Attributes person = persons.get( "cn=person0,ou=system" );
+        assertNotNull( person );
+        Attribute ocs = person.get( "objectClass" );
+        assertEquals( 2, ocs.size() );
+        assertTrue( ocs.contains( "top" ) );
+        assertTrue( ocs.contains( "person" ) );
+
+        person = persons.get( "cn=person1,ou=system" );
+        assertNotNull( person );
+        ocs = person.get( "objectClass" );
+        assertEquals( 3, ocs.size() );
+        assertTrue( ocs.contains( "top" ) );
+        assertTrue( ocs.contains( "person" ) );
+        assertTrue( ocs.contains( "organizationalPerson" ) );
+
+        person = persons.get( "cn=person2,ou=system" );
+        assertNotNull( person );
+        ocs = person.get( "objectClass" );
+        assertEquals( 4, ocs.size() );
+        assertTrue( ocs.contains( "top" ) );
+        assertTrue( ocs.contains( "person" ) );
+        assertTrue( ocs.contains( "organizationalPerson" ) );
+        assertTrue( ocs.contains( "inetOrgPerson" ) );
+    }
+
+
+    @Test
+    public void testSearchForOrgPerson() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+        Map<String, Attributes> orgPersons = new HashMap<String, Attributes>();
+        NamingEnumeration<SearchResult> results = sysRoot.search( "", "(objectClass=organizationalPerson)", controls );
+        
+        while ( results.hasMore() )
+        {
+            SearchResult result = results.next();
+            orgPersons.put( result.getName(), result.getAttributes() );
+        }
+
+        // admin is extra
+        assertEquals( 3, orgPersons.size() );
+
+        Attributes orgPerson = orgPersons.get( "cn=person1,ou=system" );
+        assertNotNull( orgPerson );
+        Attribute ocs = orgPerson.get( "objectClass" );
+        assertEquals( 3, ocs.size() );
+        assertTrue( ocs.contains( "top" ) );
+        assertTrue( ocs.contains( "person" ) );
+        assertTrue( ocs.contains( "organizationalPerson" ) );
+
+        orgPerson = orgPersons.get( "cn=person2,ou=system" );
+        assertNotNull( orgPerson );
+        ocs = orgPerson.get( "objectClass" );
+        assertEquals( 4, ocs.size() );
+        assertTrue( ocs.contains( "top" ) );
+        assertTrue( ocs.contains( "person" ) );
+        assertTrue( ocs.contains( "organizationalPerson" ) );
+        assertTrue( ocs.contains( "inetOrgPerson" ) );
+    }
+
+
+    @Test
+    public void testSearchForInetOrgPerson() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+        Map<String, Attributes> inetOrgPersons = new HashMap<String, Attributes>();
+        NamingEnumeration<SearchResult> results = sysRoot.search( "", "(objectClass=inetOrgPerson)", controls );
+        
+        while ( results.hasMore() )
+        {
+            SearchResult result = results.next();
+            inetOrgPersons.put( result.getName(), result.getAttributes() );
+        }
+
+        // admin is extra
+        assertEquals( 2, inetOrgPersons.size() );
+
+        Attributes inetOrgPerson = inetOrgPersons.get( "cn=person2,ou=system" );
+        assertNotNull( inetOrgPerson );
+        Attribute ocs = inetOrgPerson.get( "objectClass" );
+        assertEquals( 4, ocs.size() );
+        assertTrue( ocs.contains( "top" ) );
+        assertTrue( ocs.contains( "person" ) );
+        assertTrue( ocs.contains( "organizationalPerson" ) );
+        assertTrue( ocs.contains( "inetOrgPerson" ) );
+    }
+
+
+    @Test
+    public void testSearchForSubSchemaSubEntryUserAttrsOnly() throws NamingException
+    {
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.OBJECT_SCOPE );
+        
+        Map<String, Attributes> subSchemaEntry = new HashMap<String, Attributes>();
+        NamingEnumeration<SearchResult> results = getRootContext( service ).search( "cn=schema", "(objectClass=*)", controls );
+
+        while ( results.hasMore() )
+        {
+            SearchResult result = results.next();
+            subSchemaEntry.put( result.getName(), result.getAttributes() );
+        }
+
+        // We should have only one entry in the result
+        assertEquals( 1, subSchemaEntry.size() );
+        
+        // It should be the normalized form of cn=schema
+        Attributes attrs = subSchemaEntry.get( "cn=schema" );
+        
+        assertNotNull( attrs );
+        
+        // We should have 2 attributes in the result :
+        // - attributeTypes
+        // - cn
+        // - objectClass
+        assertEquals( 2, attrs.size() );
+        
+        assertNotNull( attrs.get( "cn" ) );
+        assertNotNull( attrs.get( "objectClass" ) );
+    }
+
+
+    @Test
+    public void testSearchForSubSchemaSubEntryAllAttrs() throws NamingException
+    {
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.OBJECT_SCOPE );
+        controls.setReturningAttributes( new String[]{ "*", "+" } );
+        
+        Map<String, Attributes> subSchemaEntry = new HashMap<String, Attributes>();
+        NamingEnumeration<SearchResult> results = getRootContext( service ).search(
+                "cn=schema", "(objectClass=*)", controls );
+
+        while ( results.hasMore() )
+        {
+            SearchResult result = results.next();
+            subSchemaEntry.put( result.getName(), result.getAttributes() );
+        }
+
+        // We should have only one entry in the result
+        assertEquals( 1, subSchemaEntry.size() );
+        
+        // It should be the normalized form of cn=schema
+        Attributes attrs = subSchemaEntry.get( "cn=schema" );
+        
+        assertNotNull( attrs );
+        
+    }
+
+    
+    @Test
+    public void testSearchForSubSchemaSubEntrySingleAttributeSelected() throws NamingException
+    {
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.OBJECT_SCOPE );
+        controls.setReturningAttributes( new String[]{ "nameForms" } );
+        
+        Map<String, Attributes> subSchemaEntry = new HashMap<String, Attributes>();
+        NamingEnumeration<SearchResult> results = getRootContext( service )
+                .search( "cn=schema", "(objectClass=*)", controls );
+
+        while ( results.hasMore() )
+        {
+            SearchResult result = results.next();
+            subSchemaEntry.put( result.getName(), result.getAttributes() );
+        }
+
+        // We should have only one entry in the result
+        assertEquals( 1, subSchemaEntry.size() );
+        
+        // It should be the normalized form of cn=schema
+        Attributes attrs = subSchemaEntry.get( "cn=schema" );
+        
+        assertNotNull( attrs );
+        
+        // We should have 1 attribute in the result :
+        // - nameForms
+        assertEquals( 1, attrs.size() );
+        
+        assertNull( attrs.get( "attributeTypes" ) );
+        assertNull( attrs.get( "cn" ) );
+        assertNull( attrs.get( "creatorsName" ) );
+        assertNull( attrs.get( "createTimestamp" ) );
+        assertNull( attrs.get( "dITContentRules" ) );
+        assertNull( attrs.get( "dITStructureRules" ) );
+        assertNull( attrs.get( "ldapSyntaxes" ) );
+        assertNull( attrs.get( "matchingRules" ) );
+        assertNull( attrs.get( "matchingRuleUse" ) );
+        assertNull( attrs.get( "modifiersName" ) );
+        assertNull( attrs.get( "modifyTimestamp" ) );
+        assertNotNull( attrs.get( "nameForms" ) );
+        assertNull( attrs.get( "objectClass" ) );
+        assertNull( attrs.get( "objectClasses" ) );
+    }
+
+    
+    /**
+     * Test for DIRSERVER-1055.
+     * Check if modifyTimestamp and createTimestamp are present in the search result,
+     * if they are requested.
+     */
+    @Test
+    public void testSearchForSubSchemaSubEntryOperationalAttributesSelected() throws NamingException
+    {
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.OBJECT_SCOPE );
+        controls.setReturningAttributes( new String[]
+            { "creatorsName", "createTimestamp", "modifiersName", "modifyTimestamp" } );
+        
+        Map<String, Attributes> subSchemaEntry = new HashMap<String, Attributes>();
+        NamingEnumeration<SearchResult> results = getRootContext( service )
+        .search( "cn=schema", "(objectClass=subschema)", controls );
+        
+        while ( results.hasMore() )
+        {
+            SearchResult result = results.next();
+            subSchemaEntry.put( result.getName(), result.getAttributes() );
+        }
+        
+        // We should have only one entry in the result
+        assertEquals( 1, subSchemaEntry.size() );
+        
+        // It should be the normalized form of cn=schema
+        Attributes attrs = subSchemaEntry.get( "cn=schema" );
+        
+        assertNotNull( attrs );
+        
+        // We should have 4 attribute in the result :
+        assertEquals( 4, attrs.size() );
+        
+        assertNull( attrs.get( "attributeTypes" ) );
+        assertNull( attrs.get( "cn" ) );
+        assertNotNull( attrs.get( "creatorsName" ) );
+        assertNotNull( attrs.get( "createTimestamp" ) );
+        assertNull( attrs.get( "dITContentRules" ) );
+        assertNull( attrs.get( "dITStructureRules" ) );
+        assertNull( attrs.get( "ldapSyntaxes" ) );
+        assertNull( attrs.get( "matchingRules" ) );
+        assertNull( attrs.get( "matchingRuleUse" ) );
+        assertNotNull( attrs.get( "modifiersName" ) );
+        assertNotNull( attrs.get( "modifyTimestamp" ) );
+        assertNull( attrs.get( "nameForms" ) );
+        assertNull( attrs.get( "objectClass" ) );
+        assertNull( attrs.get( "objectClasses" ) );
+    }
+
+
+    @Test
+    public void testSearchForSubSchemaSubEntryBadFilter() throws NamingException
+    {
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.OBJECT_SCOPE );
+        controls.setReturningAttributes( new String[]{ "+" } );
+        
+        Map<String, Attributes> subSchemaEntry = new HashMap<String, Attributes>();
+        NamingEnumeration<SearchResult> results = getRootContext( service )
+                .search( "cn=schema", "(objectClass=nothing)", controls );
+
+        while ( results.hasMore() )
+        {
+            SearchResult result = results.next();
+            subSchemaEntry.put( result.getName(), result.getAttributes() );
+        }
+
+        // We should have no entry in the result
+        assertEquals( 0, subSchemaEntry.size() );
+    }
+
+
+    @Test
+    public void testSearchForSubSchemaSubEntryFilterEqualTop() throws NamingException
+    {
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.OBJECT_SCOPE );
+        controls.setReturningAttributes( new String[]{ "+" } );
+        
+        Map<String, Attributes> subSchemaEntry = new HashMap<String, Attributes>();
+        NamingEnumeration<SearchResult> results = getRootContext( service )
+                .search( "cn=schema", "(objectClass=top)", controls );
+
+        while ( results.hasMore() )
+        {
+            SearchResult result = results.next();
+            subSchemaEntry.put( result.getName(), result.getAttributes() );
+        }
+
+        // We should have only one entry in the result
+        assertEquals( 1, subSchemaEntry.size() );
+        
+        // It should be the normalized form of cn=schema
+        Attributes attrs = subSchemaEntry.get( "cn=schema" );
+        
+        assertNotNull( attrs );
+        
+        // We should have 18 attribute in the result :
+        // - nameForms
+        // - comparators
+        // - normalizers
+        // - syntaxCheckers
+        assertEquals( 18, attrs.size() );
+        
+        assertNotNull( attrs.get( "attributeTypes" ) );
+        assertNotNull( attrs.get( "cn" ) );
+        assertNotNull( attrs.get( "subtreeSpecification" ) );
+        assertNotNull( attrs.get( "creatorsName" ) );
+        assertNotNull( attrs.get( "createTimestamp" ) );
+        assertNotNull( attrs.get( "dITContentRules" ) );
+        assertNotNull( attrs.get( "dITStructureRules" ) );
+        assertNotNull( attrs.get( "ldapSyntaxes" ) );
+        assertNotNull( attrs.get( "matchingRules" ) );
+        assertNotNull( attrs.get( "matchingRuleUse" ) );
+        assertNotNull( attrs.get( "modifiersName" ) );
+        assertNotNull( attrs.get( "modifyTimestamp" ) );
+        assertNotNull( attrs.get( "nameForms" ) );
+        assertNotNull( attrs.get( "objectClass" ) );
+        assertNotNull( attrs.get( "objectClasses" ) );
+    }
+
+
+    @Test
+    public void testSearchForSubSchemaSubEntryFilterEqualSubSchema() throws NamingException
+    {
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.OBJECT_SCOPE );
+        controls.setReturningAttributes( new String[]{ "+" } );
+        
+        Map<String, Attributes> subSchemaEntry = new HashMap<String, Attributes>();
+        NamingEnumeration<SearchResult> results = getRootContext( service )
+                .search( "cn=schema", "(objectClass=subSchema)", controls );
+
+        while ( results.hasMore() )
+        {
+            SearchResult result = results.next();
+            subSchemaEntry.put( result.getName(), result.getAttributes() );
+        }
+
+        // We should have only one entry in the result
+        assertEquals( 1, subSchemaEntry.size() );
+        
+        // It should be the normalized form of cn=schema
+        Attributes attrs = subSchemaEntry.get( "cn=schema" );
+        
+        assertNotNull( attrs );
+        
+        // We should have 18 attribute in the result :
+        // - nameForms
+        // - comparators
+        // - normalizers
+        // - syntaxCheckers
+        assertEquals( 18, attrs.size() );
+        
+        assertNotNull( attrs.get( "attributeTypes" ) );
+        assertNotNull( attrs.get( "cn" ) );
+        assertNotNull( attrs.get( "subtreeSpecification" ) );
+        assertNotNull( attrs.get( "creatorsName" ) );
+        assertNotNull( attrs.get( "createTimestamp" ) );
+        assertNotNull( attrs.get( "dITContentRules" ) );
+        assertNotNull( attrs.get( "dITStructureRules" ) );
+        assertNotNull( attrs.get( "ldapSyntaxes" ) );
+        assertNotNull( attrs.get( "matchingRules" ) );
+        assertNotNull( attrs.get( "matchingRuleUse" ) );
+        assertNotNull( attrs.get( "modifiersName" ) );
+        assertNotNull( attrs.get( "modifyTimestamp" ) );
+        assertNotNull( attrs.get( "nameForms" ) );
+        assertNotNull( attrs.get( "objectClass" ) );
+        assertNotNull( attrs.get( "objectClasses" ) );
+    }
+
+
+    @Test
+    public void testSearchForSubSchemaSubEntryNotObjectScope() throws NamingException
+    {
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+        controls.setReturningAttributes( new String[]{ "+" } );
+        
+        Map<String, Attributes> subSchemaEntry = new HashMap<String, Attributes>();
+        NamingEnumeration<SearchResult> results = getRootContext( service )
+                .search( "cn=schema", "(objectClass=nothing)", controls );
+
+        while ( results.hasMore() )
+        {
+            SearchResult result = results.next();
+            subSchemaEntry.put( result.getName(), result.getAttributes() );
+        }
+
+        // We should have no entry in the result
+        assertEquals( 0, subSchemaEntry.size() );
+    }
+
+
+    @Test
+    public void testSearchForSubSchemaSubEntryComposedFilters() throws NamingException
+    {
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+        controls.setReturningAttributes( new String[]{ "+" } );
+        
+        Map<String, Attributes> subSchemaEntry = new HashMap<String, Attributes>();
+        NamingEnumeration<SearchResult> results = getRootContext( service )
+                .search( "cn=schema", "(&(objectClass=*)(objectClass=top))", controls );
+
+        while ( results.hasMore() )
+        {
+            SearchResult result = results.next();
+            subSchemaEntry.put( result.getName(), result.getAttributes() );
+        }
+
+        // We should have no entry in the result
+        assertEquals( 0, subSchemaEntry.size() );
+    }
+
+
+    /**
+     * Test for DIRSERVER-844: storing of base 64 encoded values into H-R attributes
+     *
+     * @throws NamingException on error
+     */
+    @Test
+    public void testSearchSeeAlso() throws NamingException
+    {
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+        Map<String, Attributes> persons = new HashMap<String, Attributes>();
+        NamingEnumeration<SearchResult> results = getSystemContext( service )
+                    .search( "", "(seeAlso=cn=Good One,ou=people,o=sevenSeas)", controls );
+
+        while ( results.hasMore() )
+        {
+            SearchResult result = results.next();
+            persons.put( result.getName(), result.getAttributes() );
+        }
+
+        // admin is extra
+        assertEquals( 1, persons.size() );
+
+        Attributes person;
+        Attribute ocs;
+
+        person = persons.get( "cn=person1,ou=system" );
+        assertNotNull( person );
+        ocs = person.get( "objectClass" );
+        assertEquals( 3, ocs.size() );
+        assertTrue( ocs.contains( "top" ) );
+        assertTrue( ocs.contains( "person" ) );
+        assertTrue( ocs.contains( "organizationalPerson" ) );
+
+        Attribute seeAlso = person.get(  "seeAlso"  );
+        assertTrue( seeAlso.contains( "cn=Good One,ou=people,o=sevenSeas" ) );
+        assertTrue( seeAlso.contains( "cn=Bad E\u00e9k\u00e0,ou=people,o=sevenSeas" ) );
+    }
+
+
+    /**
+     * Doing a search with filtering attributes should work even if the attribute
+     * is not valid 
+     *
+     * @throws NamingException on error
+     */
+    @Test
+    public void testSearchForUnknownAttributes() throws NamingException
+    {
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+        Map<String, Attributes> persons = new HashMap<String, Attributes>();
+        controls.setReturningAttributes( new String[] { "9.9.9" } );
+
+        NamingEnumeration<SearchResult> results = getSystemContext( service )
+                .search( "", "(objectClass=person)", controls );
+        
+        while ( results.hasMore() )
+        {
+            SearchResult result = results.next();
+            persons.put( result.getName(), result.getAttributes() );
+        }
+
+        // admin is extra
+        assertEquals( 4, persons.size() );
+
+        Attributes person;
+        Attribute ocs;
+
+        person = persons.get( "cn=person0,ou=system" );
+        assertNotNull( person );
+        ocs = person.get( "objectClass" );
+        assertNull( ocs );
+        
+        ocs = person.get( "9.9.9" );
+        assertNull( ocs );
+
+        person = persons.get( "cn=person1,ou=system" );
+        assertNotNull( person );
+        ocs = person.get( "objectClass" );
+        assertNull( ocs );
+
+        person = persons.get( "cn=person2,ou=system" );
+        assertNotNull( person );
+        ocs = person.get( "objectClass" );
+        assertNull( ocs );
+    }
+
+    
+    /**
+     * Check that if we request a Attribute which is not an AttributeType,
+     * we still get a result
+     *
+     * @throws NamingException on error
+     */
+    @Test
+    public void testSearchAttributesOIDObjectClass() throws NamingException
+    {
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+        Map<String, Attributes> persons = new HashMap<String, Attributes>();
+        controls.setReturningAttributes( new String[] { "2.5.6.6" } );
+
+        NamingEnumeration<SearchResult> results = getSystemContext( service )
+                .search( "", "(objectClass=person)", controls );
+        
+        while ( results.hasMore() )
+        {
+            SearchResult result = results.next();
+            persons.put( result.getName(), result.getAttributes() );
+        }
+
+        // admin is extra
+        assertEquals( 4, persons.size() );
+
+        Attributes person;
+        Attribute ocs;
+
+        person = persons.get( "cn=person0,ou=system" );
+        assertNotNull( person );
+        ocs = person.get( "objectClass" );
+        assertNull( ocs );
+        
+        // We should not get this attribute (it's an ObjectClass)
+        ocs = person.get( "2.5.6.6" );
+        assertNull( ocs );
+
+        person = persons.get( "cn=person1,ou=system" );
+        assertNotNull( person );
+        ocs = person.get( "objectClass" );
+        assertNull( ocs );
+
+        person = persons.get( "cn=person2,ou=system" );
+        assertNotNull( person );
+        ocs = person.get( "objectClass" );
+        assertNull( ocs );
+    }
+
+    
+    /**
+     * Check that if we request a Attribute which is an ObjectClass.
+     *
+     * @throws NamingException on error
+     */
+    @Test
+    public void testSearchAttributesOIDObjectClassName() throws NamingException
+    {
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+        Map<String, Attributes> persons = new HashMap<String, Attributes>();
+        controls.setReturningAttributes( new String[] { "person" } );
+
+        NamingEnumeration<SearchResult> results = getSystemContext( service )
+                .search( "", "(objectClass=person)", controls );
+        
+        while ( results.hasMore() )
+        {
+            SearchResult result = results.next();
+            persons.put( result.getName(), result.getAttributes() );
+        }
+
+        // admin is extra
+        assertEquals( 4, persons.size() );
+
+        Attributes person;
+        Attribute ocs;
+
+        person = persons.get( "cn=person0,ou=system" );
+        assertNotNull( person );
+        ocs = person.get( "objectClass" );
+        assertNull( ocs );
+        
+        // We should not get this attrinute (it's an ObjectClass)
+        ocs = person.get( "2.5.4.46" );
+        assertNull( ocs );
+
+        person = persons.get( "cn=person1,ou=system" );
+        assertNotNull( person );
+        ocs = person.get( "objectClass" );
+        assertNull( ocs );
+
+        person = persons.get( "cn=person2,ou=system" );
+        assertNotNull( person );
+        ocs = person.get( "objectClass" );
+        assertNull( ocs );
+    }
+
+
+    /**
+     * Check that if we search for an attribute using its inherited
+     * AttributeType (ie, looking for name instead of givenName, surname, 
+     * commonName), we find all the entries.
+     *
+     * @throws NamingException
+     */
+    @Test
+    public void testSearchForName() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+        Map<String, Attributes> persons = new HashMap<String, Attributes>();
+
+        NamingEnumeration<SearchResult> results = sysRoot.search( "", "(name=person*)", controls );
+        
+        while ( results.hasMore() )
+        {
+            SearchResult result = results.next();
+            persons.put( result.getName(), result.getAttributes() );
+        }
+
+        assertEquals( 3, persons.size() );
+
+        Attributes person = persons.get( "cn=person0,ou=system" );
+        assertNotNull( person );
+        Attribute ocs = person.get( "objectClass" );
+        assertEquals( 2, ocs.size() );
+        assertTrue( ocs.contains( "top" ) );
+        assertTrue( ocs.contains( "person" ) );
+
+        person = persons.get( "cn=person1,ou=system" );
+        assertNotNull( person );
+        ocs = person.get( "objectClass" );
+        assertEquals( 3, ocs.size() );
+        assertTrue( ocs.contains( "top" ) );
+        assertTrue( ocs.contains( "person" ) );
+        assertTrue( ocs.contains( "organizationalPerson" ) );
+
+        person = persons.get( "cn=person2,ou=system" );
+        assertNotNull( person );
+        ocs = person.get( "objectClass" );
+        assertEquals( 4, ocs.size() );
+        assertTrue( ocs.contains( "top" ) );
+        assertTrue( ocs.contains( "person" ) );
+        assertTrue( ocs.contains( "organizationalPerson" ) );
+        assertTrue( ocs.contains( "inetOrgPerson" ) );
+    }
+}
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/schema/SubschemaSubentryIT.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/schema/SubschemaSubentryIT.java
new file mode 100644
index 0000000..fecc7c2
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/schema/SubschemaSubentryIT.java
@@ -0,0 +1,2063 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.schema;
+
+
+import jdbm.helper.IntegerComparator;
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerEntryUtils;
+import org.apache.directory.server.core.integ.CiRunner;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getSchemaContext;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getRootContext;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getSystemContext;
+import org.apache.directory.shared.ldap.exception.LdapNameAlreadyBoundException;
+import org.apache.directory.shared.ldap.exception.LdapOperationNotSupportedException;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.message.ModificationItemImpl;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.schema.DeepTrimNormalizer;
+import org.apache.directory.shared.ldap.schema.syntax.AcceptAllSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.AttributeTypeDescription;
+import org.apache.directory.shared.ldap.schema.syntax.ComparatorDescription;
+import org.apache.directory.shared.ldap.schema.syntax.LdapSyntaxDescription;
+import org.apache.directory.shared.ldap.schema.syntax.MatchingRuleDescription;
+import org.apache.directory.shared.ldap.schema.syntax.NormalizerDescription;
+import org.apache.directory.shared.ldap.schema.syntax.ObjectClassDescription;
+import org.apache.directory.shared.ldap.schema.syntax.SyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.SyntaxCheckerDescription;
+import org.apache.directory.shared.ldap.schema.syntax.parser.AttributeTypeDescriptionSchemaParser;
+import org.apache.directory.shared.ldap.schema.syntax.parser.ComparatorDescriptionSchemaParser;
+import org.apache.directory.shared.ldap.schema.syntax.parser.LdapSyntaxDescriptionSchemaParser;
+import org.apache.directory.shared.ldap.schema.syntax.parser.MatchingRuleDescriptionSchemaParser;
+import org.apache.directory.shared.ldap.schema.syntax.parser.NormalizerDescriptionSchemaParser;
+import org.apache.directory.shared.ldap.schema.syntax.parser.ObjectClassDescriptionSchemaParser;
+import org.apache.directory.shared.ldap.schema.syntax.parser.SyntaxCheckerDescriptionSchemaParser;
+import org.apache.directory.shared.ldap.util.Base64;
+import org.apache.directory.shared.ldap.util.DateUtils;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.naming.Context;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.InitialDirContext;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.LdapContext;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.TimeZone;
+
+
+/**
+ * An integration test class for performing various operations on the 
+ * subschemaSubentry as listed in the rootDSE.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+@RunWith ( CiRunner.class )
+public class SubschemaSubentryIT 
+{
+    private static final String GLOBAL_SUBSCHEMA_DN = "cn=schema";
+    private static final String SUBSCHEMA_SUBENTRY = "subschemaSubentry";
+    
+    private static final SyntaxCheckerDescriptionSchemaParser SYNTAX_CHECKER_DESCRIPTION_SCHEMA_PARSER =
+        new SyntaxCheckerDescriptionSchemaParser();
+    private static final AttributeTypeDescriptionSchemaParser ATTRIBUTE_TYPE_DESCRIPTION_SCHEMA_PARSER =
+        new AttributeTypeDescriptionSchemaParser();
+    private ComparatorDescriptionSchemaParser comparatorDescriptionSchemaParser =
+        new ComparatorDescriptionSchemaParser();
+    private NormalizerDescriptionSchemaParser normalizerDescriptionSchemaParser =
+        new NormalizerDescriptionSchemaParser();
+    private LdapSyntaxDescriptionSchemaParser ldapSyntaxDescriptionSchemaParser =
+        new LdapSyntaxDescriptionSchemaParser();
+    private MatchingRuleDescriptionSchemaParser matchingRuleDescriptionSchemaParser =
+        new MatchingRuleDescriptionSchemaParser();
+    private ObjectClassDescriptionSchemaParser objectClassDescriptionSchemaParser =
+        new ObjectClassDescriptionSchemaParser();
+
+
+    public static DirectoryService service;
+
+    
+    /**
+     * Make sure the global subschemaSubentry is where it is expected to be.
+     *
+     * @throws NamingException on error
+     */
+    @Test
+    public void testRootDSEsSubschemaSubentry() throws NamingException
+    {
+        assertEquals( GLOBAL_SUBSCHEMA_DN, getSubschemaSubentryDN() );
+        Attributes subschemaSubentryAttrs = getSubschemaSubentryAttributes();
+        assertNotNull( subschemaSubentryAttrs );
+    }
+    
+    
+    /**
+     * Tests the rejection of a delete operation on the SubschemaSubentry (SSSE).
+     *
+     * @throws NamingException on error
+     */
+    @Test
+    public void testSSSEDeleteRejection() throws NamingException
+    {
+        try
+        {
+            getRootContext( service ).destroySubcontext( getSubschemaSubentryDN() );
+            fail( "You are not allowed to delete the global schema subentry" );
+        }
+        catch( LdapOperationNotSupportedException e )
+        {
+            assertEquals( ResultCodeEnum.UNWILLING_TO_PERFORM, e.getResultCode() );
+        }
+    }
+
+
+    /**
+     * Tests the rejection of an add operation for the SubschemaSubentry (SSSE).
+     *
+     * @throws NamingException on error
+     */
+    @Test
+    public void testSSSEAddRejection() throws NamingException
+    {
+        try
+        {
+            getRootContext( service ).createSubcontext( getSubschemaSubentryDN(), getSubschemaSubentryAttributes() );
+            fail( "You are not allowed to add the global schema subentry which exists by default" );
+        }
+        catch( LdapNameAlreadyBoundException e )
+        {
+            assertEquals( ResultCodeEnum.ENTRY_ALREADY_EXISTS, e.getResultCode() );
+        }
+    }
+
+
+    /**
+     * Tests the rejection of rename (modifyDn) operation for the SubschemaSubentry (SSSE).
+     *
+     * @throws NamingException on error
+     */
+    @Test
+    public void testSSSERenameRejection() throws NamingException
+    {
+        try
+        {
+            getRootContext( service ).rename( getSubschemaSubentryDN(), "cn=schema,ou=system" );
+            fail( "You are not allowed to rename the global schema subentry which is fixed" );
+        }
+        catch( LdapOperationNotSupportedException e )
+        {
+            assertEquals( ResultCodeEnum.UNWILLING_TO_PERFORM, e.getResultCode() );
+        }
+    }
+
+
+    /**
+     * Tests the rejection of move operation for the SubschemaSubentry (SSSE).
+     *
+     * @throws NamingException on error
+     */
+    @Test
+    public void testSSSEMoveRejection() throws NamingException
+    {
+        try
+        {
+            getRootContext( service ).rename( getSubschemaSubentryDN(), "cn=blah,ou=schema" );
+            fail( "You are not allowed to move the global schema subentry which is fixed" );
+        }
+        catch( LdapOperationNotSupportedException e )
+        {
+            assertEquals( ResultCodeEnum.UNWILLING_TO_PERFORM, e.getResultCode() );
+        }
+
+        try
+        {
+            getRootContext( service ).rename( getSubschemaSubentryDN(), "cn=schema,ou=schema" );
+            fail( "You are not allowed to move the global schema subentry which is fixed" );
+        }
+        catch( LdapOperationNotSupportedException e )
+        {
+            assertEquals( ResultCodeEnum.UNWILLING_TO_PERFORM, e.getResultCode() );
+        }
+    }
+    
+    
+    // -----------------------------------------------------------------------
+    // SyntaxChecker Tests
+    // -----------------------------------------------------------------------
+
+    
+    private void checkSyntaxCheckerPresent( String oid, String schemaName, boolean isPresent ) throws Exception
+    {
+        // -------------------------------------------------------------------
+        // check first to see if it is present in the subschemaSubentry
+        // -------------------------------------------------------------------
+        
+        Attributes attrs = getSubschemaSubentryAttributes();
+        Attribute attrTypes = attrs.get( "syntaxCheckers" );
+        SyntaxCheckerDescription syntaxCheckerDescription = null; 
+        for ( int ii = 0; ii < attrTypes.size(); ii++ )
+        {
+            String desc = ( String ) attrTypes.get( ii );
+            if ( desc.indexOf( oid ) != -1 )
+            {
+                syntaxCheckerDescription = SYNTAX_CHECKER_DESCRIPTION_SCHEMA_PARSER.parseSyntaxCheckerDescription( desc );
+                break;
+            }
+        }
+     
+        if ( isPresent )
+        {
+            assertNotNull( syntaxCheckerDescription );
+            assertEquals( oid, syntaxCheckerDescription.getNumericOid() );
+        }
+        else
+        {
+            assertNull( syntaxCheckerDescription );
+        }
+
+        // -------------------------------------------------------------------
+        // check next to see if it is present in the schema partition
+        // -------------------------------------------------------------------
+        
+        attrs = null;
+        
+        if ( isPresent )
+        {
+            attrs = getSchemaContext( service ).getAttributes( "m-oid=" + oid + ",ou=syntaxCheckers,cn=" + schemaName );
+            assertNotNull( attrs );
+            SchemaEntityFactory factory = new SchemaEntityFactory( service.getRegistries() );
+            
+            ServerEntry serverEntry = ServerEntryUtils.toServerEntry( attrs, LdapDN.EMPTY_LDAPDN, service.getRegistries() );
+
+            SyntaxChecker syntaxChecker = factory.getSyntaxChecker( serverEntry, service.getRegistries() );
+            assertEquals( oid, syntaxChecker.getSyntaxOid() );
+        }
+        else
+        {
+            //noinspection EmptyCatchBlock
+            try
+            {
+                attrs = getSchemaContext( service ).getAttributes( "m-oid=" + oid + ",ou=syntaxCheckers,cn=" + schemaName );
+                fail( "should never get here" );
+            }
+            catch( NamingException e )
+            {
+            }
+            
+            assertNull( attrs );
+        }
+        
+        // -------------------------------------------------------------------
+        // check to see if it is present in the syntaxCheckerRegistry
+        // -------------------------------------------------------------------
+
+        if ( isPresent )
+        {
+            assertTrue( service.getRegistries().getSyntaxCheckerRegistry().hasSyntaxChecker( oid ) );
+        }
+        else
+        {
+            assertFalse( service.getRegistries().getSyntaxCheckerRegistry().hasSyntaxChecker( oid ) );
+        }
+    }
+
+
+    /**
+     * Tests a number of modify add, remove and replace operation combinations for
+     * a syntaxChecker on the schema subentry.
+     *
+     * @throws Exception on error
+     */
+    @Test
+    public void testAddRemoveReplaceSyntaxCheckers() throws Exception
+    {
+        // 1st change
+        enableSchema( "nis" );
+        List<String> descriptions = new ArrayList<String>();
+        
+        // ( 1.3.6.1.4.1.18060.0.4.0.2.10000 DESC 'bogus desc' FQCN org.foo.Bar BYTECODE 14561234 )
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.0.10000 DESC 'bogus desc' FQCN " 
+            + AcceptAllSyntaxChecker.class.getName() + " X-SCHEMA 'nis' )" );
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.0.10001 DESC 'bogus desc' FQCN " 
+            + AcceptAllSyntaxChecker.class.getName() + " X-SCHEMA 'nis' )" );
+
+        // -------------------------------------------------------------------
+        // add and check
+        // -------------------------------------------------------------------
+        
+        // 2nd change
+        modify( DirContext.ADD_ATTRIBUTE, descriptions, "syntaxCheckers" );
+        checkSyntaxCheckerPresent( "1.3.6.1.4.1.18060.0.4.1.0.10000", "nis", true );
+        checkSyntaxCheckerPresent( "1.3.6.1.4.1.18060.0.4.1.0.10001", "nis", true );
+
+        // -------------------------------------------------------------------
+        // remove and check
+        // -------------------------------------------------------------------
+
+        // 3rd change
+        modify( DirContext.REMOVE_ATTRIBUTE, descriptions, "syntaxCheckers" );
+        checkSyntaxCheckerPresent( "1.3.6.1.4.1.18060.0.4.1.0.10000", "nis", false );
+        checkSyntaxCheckerPresent( "1.3.6.1.4.1.18060.0.4.1.0.10001", "nis", false );
+        
+        // -------------------------------------------------------------------
+        // test failure to replace
+        // -------------------------------------------------------------------
+        
+        try
+        {
+            modify( DirContext.REPLACE_ATTRIBUTE, descriptions, "syntaxCheckers" );
+            fail( "modify REPLACE operations should not be allowed" );
+        }
+        catch ( LdapOperationNotSupportedException e )
+        {
+            assertEquals( ResultCodeEnum.UNWILLING_TO_PERFORM, e.getResultCode() );
+        }
+
+        // -------------------------------------------------------------------
+        // check add with valid bytecode
+        // -------------------------------------------------------------------
+        
+        descriptions.clear();
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.0.10002 DESC 'bogus desc' FQCN DummySyntaxChecker BYTECODE " 
+            +  getByteCode( "DummySyntaxChecker.bytecode" ) + " X-SCHEMA 'nis' )" );
+
+        // 4th change
+        modify( DirContext.ADD_ATTRIBUTE, descriptions, "syntaxCheckers" );
+        checkSyntaxCheckerPresent( "1.3.6.1.4.1.18060.0.4.1.0.10002", "nis", true );
+
+        // -------------------------------------------------------------------
+        // check remove
+        // -------------------------------------------------------------------
+
+        // 5th change
+        modify( DirContext.REMOVE_ATTRIBUTE, descriptions, "syntaxCheckers" );
+        checkSyntaxCheckerPresent( "1.3.6.1.4.1.18060.0.4.1.0.10002", "nis", false );
+
+        // -------------------------------------------------------------------
+        // check add no schema info
+        // -------------------------------------------------------------------
+        
+        descriptions.clear();
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.0.10002 DESC 'bogus desc' FQCN DummySyntaxChecker BYTECODE " 
+            +  getByteCode( "DummySyntaxChecker.bytecode" ) + " )" );
+
+        // 6th change
+        modify( DirContext.ADD_ATTRIBUTE, descriptions, "syntaxCheckers" );
+        checkSyntaxCheckerPresent( "1.3.6.1.4.1.18060.0.4.1.0.10002", "other", true );
+
+        // after a total of 6 changes 
+        if ( service.getChangeLog().getLatest() != null )
+        {
+            assertEquals( service.getChangeLog().getLatest().getRevision() + 6,
+                    service.getChangeLog().getCurrentRevision() );
+        }
+    }
+    
+    
+    // -----------------------------------------------------------------------
+    // Comparator Tests
+    // -----------------------------------------------------------------------
+    
+    
+    private void checkComparatorPresent( String oid, String schemaName, boolean isPresent ) throws Exception
+    {
+        // -------------------------------------------------------------------
+        // check first to see if it is present in the subschemaSubentry
+        // -------------------------------------------------------------------
+        
+        Attributes attrs = getSubschemaSubentryAttributes();
+        Attribute attrTypes = attrs.get( "comparators" );
+        ComparatorDescription comparatorDescription = null; 
+        for ( int ii = 0; ii < attrTypes.size(); ii++ )
+        {
+            String desc = ( String ) attrTypes.get( ii );
+            if ( desc.indexOf( oid ) != -1 )
+            {
+                comparatorDescription = comparatorDescriptionSchemaParser.parseComparatorDescription( desc );
+                break;
+            }
+        }
+     
+        if ( isPresent )
+        {
+            assertNotNull( comparatorDescription );
+            assertEquals( oid, comparatorDescription.getNumericOid() );
+        }
+        else
+        {
+            assertNull( comparatorDescription );
+        }
+
+        // -------------------------------------------------------------------
+        // check next to see if it is present in the schema partition
+        // -------------------------------------------------------------------
+        
+        attrs = null;
+
+        LdapContext schemaRoot = getSchemaContext( service );
+        if ( isPresent )
+        {
+            attrs = schemaRoot.getAttributes( "m-oid=" + oid + ",ou=comparators,cn=" + schemaName );
+            assertNotNull( attrs );
+        }
+        else
+        {
+            //noinspection EmptyCatchBlock
+            try
+            {
+                attrs = schemaRoot.getAttributes( "m-oid=" + oid + ",ou=comparators,cn=" + schemaName );
+                fail( "should never get here" );
+            }
+            catch( NamingException e )
+            {
+            }
+            assertNull( attrs );
+        }
+        
+        // -------------------------------------------------------------------
+        // check to see if it is present in the comparatorRegistry
+        // -------------------------------------------------------------------
+
+        if ( isPresent )
+        {
+            assertTrue( service.getRegistries().getComparatorRegistry().hasComparator( oid ) );
+        }
+        else
+        {
+            assertFalse( service.getRegistries().getComparatorRegistry().hasComparator( oid ) );
+        }
+    }
+    
+    
+    /**
+     * Tests a number of modify add, remove and replace operation combinations for
+     * comparators on the schema subentry.
+     *
+     * @throws Exception on error
+     */
+    @Test
+    public void testAddRemoveReplaceComparators() throws Exception
+    {
+        enableSchema( "nis" );
+        List<String> descriptions = new ArrayList<String>();
+        
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.0.10000 DESC 'bogus desc' FQCN " 
+            + IntegerComparator.class.getName() + " X-SCHEMA 'nis' )" );
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.0.10001 DESC 'bogus desc' FQCN " 
+            + IntegerComparator.class.getName() + " X-SCHEMA 'nis' )" );
+
+        // -------------------------------------------------------------------
+        // add and check
+        // -------------------------------------------------------------------
+        
+        modify( DirContext.ADD_ATTRIBUTE, descriptions, "comparators" );
+        checkComparatorPresent( "1.3.6.1.4.1.18060.0.4.1.0.10000", "nis", true );
+        checkComparatorPresent( "1.3.6.1.4.1.18060.0.4.1.0.10001", "nis", true );
+
+        // -------------------------------------------------------------------
+        // remove and check
+        // -------------------------------------------------------------------
+        
+        modify( DirContext.REMOVE_ATTRIBUTE, descriptions, "comparators" );
+        checkComparatorPresent( "1.3.6.1.4.1.18060.0.4.1.0.10000", "nis", false );
+        checkComparatorPresent( "1.3.6.1.4.1.18060.0.4.1.0.10001", "nis", false );
+        
+        // -------------------------------------------------------------------
+        // test failure to replace
+        // -------------------------------------------------------------------
+        
+        try
+        {
+            modify( DirContext.REPLACE_ATTRIBUTE, descriptions, "comparators" );
+            fail( "modify REPLACE operations should not be allowed" );
+        }
+        catch ( LdapOperationNotSupportedException e )
+        {
+            assertEquals( ResultCodeEnum.UNWILLING_TO_PERFORM, e.getResultCode() );
+        }
+
+        // -------------------------------------------------------------------
+        // check add with valid bytecode
+        // -------------------------------------------------------------------
+        
+        descriptions.clear();
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.0.10002 DESC 'bogus desc' FQCN DummyComparator BYTECODE " 
+            +  getByteCode( "DummyComparator.bytecode" ) + " X-SCHEMA 'nis' )" );
+
+        modify( DirContext.ADD_ATTRIBUTE, descriptions, "comparators" );
+        checkComparatorPresent( "1.3.6.1.4.1.18060.0.4.1.0.10002", "nis", true );
+
+        // -------------------------------------------------------------------
+        // check remove with valid bytecode
+        // -------------------------------------------------------------------
+        
+        modify( DirContext.REMOVE_ATTRIBUTE, descriptions, "comparators" );
+        checkComparatorPresent( "1.3.6.1.4.1.18060.0.4.1.0.10002", "nis", false );
+
+        // -------------------------------------------------------------------
+        // check add no schema info
+        // -------------------------------------------------------------------
+        
+        descriptions.clear();
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.0.10002 DESC 'bogus desc' FQCN DummyComparator BYTECODE " 
+            +  getByteCode( "DummyComparator.bytecode" ) + " )" );
+
+        modify( DirContext.ADD_ATTRIBUTE, descriptions, "comparators" );
+        checkComparatorPresent( "1.3.6.1.4.1.18060.0.4.1.0.10002", "other", true );
+    }
+
+    
+    // -----------------------------------------------------------------------
+    // Normalizer Tests
+    // -----------------------------------------------------------------------
+    
+    
+    private void checkNormalizerPresent( String oid, String schemaName, boolean isPresent ) throws Exception
+    {
+        // -------------------------------------------------------------------
+        // check first to see if it is present in the subschemaSubentry
+        // -------------------------------------------------------------------
+        
+        Attributes attrs = getSubschemaSubentryAttributes();
+        Attribute attrTypes = attrs.get( "normalizers" );
+        NormalizerDescription normalizerDescription = null; 
+        for ( int ii = 0; ii < attrTypes.size(); ii++ )
+        {
+            String desc = ( String ) attrTypes.get( ii );
+            if ( desc.indexOf( oid ) != -1 )
+            {
+                normalizerDescription = normalizerDescriptionSchemaParser.parseNormalizerDescription( desc );
+                break;
+            }
+        }
+     
+        if ( isPresent )
+        {
+            assertNotNull( normalizerDescription );
+            assertEquals( oid, normalizerDescription.getNumericOid() );
+        }
+        else
+        {
+            assertNull( normalizerDescription );
+        }
+
+        // -------------------------------------------------------------------
+        // check next to see if it is present in the schema partition
+        // -------------------------------------------------------------------
+        
+        attrs = null;
+
+        LdapContext schemaRoot = getSchemaContext( service );
+        if ( isPresent )
+        {
+            attrs = schemaRoot.getAttributes( "m-oid=" + oid + ",ou=normalizers,cn=" + schemaName );
+            assertNotNull( attrs );
+        }
+        else
+        {
+            //noinspection EmptyCatchBlock
+            try
+            {
+                attrs = schemaRoot.getAttributes( "m-oid=" + oid + ",ou=normalizers,cn=" + schemaName );
+                fail( "should never get here" );
+            }
+            catch( NamingException e )
+            {
+            }
+            assertNull( attrs );
+        }
+        
+        // -------------------------------------------------------------------
+        // check to see if it is present in the normalizerRegistry
+        // -------------------------------------------------------------------
+        
+        if ( isPresent ) 
+        { 
+            assertTrue( service.getRegistries().getNormalizerRegistry().hasNormalizer( oid ) );
+        }
+        else
+        {
+            assertFalse( service.getRegistries().getNormalizerRegistry().hasNormalizer( oid ) );
+        }
+    }
+    
+    
+    /**
+     * Tests a number of modify add, remove and replace operation combinations for
+     * normalizers on the schema subentry.
+     *
+     * @throws Exception on error
+     */
+    @Test
+    public void testAddRemoveReplaceNormalizers() throws Exception
+    {
+        enableSchema( "nis" );
+        List<String> descriptions = new ArrayList<String>();
+        
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.0.10000 DESC 'bogus desc' FQCN " 
+            + DeepTrimNormalizer.class.getName() + " X-SCHEMA 'nis' )" );
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.0.10001 DESC 'bogus desc' FQCN " 
+            + DeepTrimNormalizer.class.getName() + " X-SCHEMA 'nis' )" );
+
+        // -------------------------------------------------------------------
+        // add and check
+        // -------------------------------------------------------------------
+        
+        modify( DirContext.ADD_ATTRIBUTE, descriptions, "normalizers" );
+        checkNormalizerPresent( "1.3.6.1.4.1.18060.0.4.1.0.10000", "nis", true );
+        checkNormalizerPresent( "1.3.6.1.4.1.18060.0.4.1.0.10001", "nis", true );
+
+        // -------------------------------------------------------------------
+        // remove and check
+        // -------------------------------------------------------------------
+        
+        modify( DirContext.REMOVE_ATTRIBUTE, descriptions, "normalizers" );
+        checkNormalizerPresent( "1.3.6.1.4.1.18060.0.4.1.0.10000", "nis", false );
+        checkNormalizerPresent( "1.3.6.1.4.1.18060.0.4.1.0.10001", "nis", false );
+        
+        // -------------------------------------------------------------------
+        // test failure to replace
+        // -------------------------------------------------------------------
+        
+        try
+        {
+            modify( DirContext.REPLACE_ATTRIBUTE, descriptions, "normalizers" );
+            fail( "modify REPLACE operations should not be allowed" );
+        }
+        catch ( LdapOperationNotSupportedException e )
+        {
+            assertEquals( ResultCodeEnum.UNWILLING_TO_PERFORM, e.getResultCode() );
+        }
+
+        // -------------------------------------------------------------------
+        // check add with valid bytecode
+        // -------------------------------------------------------------------
+        
+        descriptions.clear();
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.0.10002 DESC 'bogus desc' FQCN DummyNormalizer BYTECODE " 
+            +  getByteCode( "DummyNormalizer.bytecode" ) + " X-SCHEMA 'nis' )" );
+
+        modify( DirContext.ADD_ATTRIBUTE, descriptions, "normalizers" );
+        checkNormalizerPresent( "1.3.6.1.4.1.18060.0.4.1.0.10002", "nis", true );
+
+        // -------------------------------------------------------------------
+        // check remove with valid bytecode
+        // -------------------------------------------------------------------
+        
+        modify( DirContext.REMOVE_ATTRIBUTE, descriptions, "normalizers" );
+        checkNormalizerPresent( "1.3.6.1.4.1.18060.0.4.1.0.10002", "nis", false );
+
+        // -------------------------------------------------------------------
+        // check add no schema info
+        // -------------------------------------------------------------------
+        
+        descriptions.clear();
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.0.10002 DESC 'bogus desc' FQCN DummyNormalizer BYTECODE " 
+            +  getByteCode( "DummyNormalizer.bytecode" ) + " )" );
+
+        modify( DirContext.ADD_ATTRIBUTE, descriptions, "normalizers" );
+        checkNormalizerPresent( "1.3.6.1.4.1.18060.0.4.1.0.10002", "other", true );
+    }
+
+    
+    // -----------------------------------------------------------------------
+    // Syntax Tests
+    // -----------------------------------------------------------------------
+    
+    
+    private void checkSyntaxPresent( String oid, String schemaName, boolean isPresent ) throws Exception
+    {
+        // -------------------------------------------------------------------
+        // check first to see if it is present in the subschemaSubentry
+        // -------------------------------------------------------------------
+        
+        Attributes attrs = getSubschemaSubentryAttributes();
+        Attribute attrTypes = attrs.get( "ldapSyntaxes" );
+        LdapSyntaxDescription syntaxDescription = null; 
+        for ( int ii = 0; ii < attrTypes.size(); ii++ )
+        {
+            String desc = ( String ) attrTypes.get( ii );
+            if ( desc.indexOf( oid ) != -1 )
+            {
+                syntaxDescription = ldapSyntaxDescriptionSchemaParser.parseLdapSyntaxDescription( desc );
+                break;
+            }
+        }
+     
+        if ( isPresent )
+        {
+            assertNotNull( syntaxDescription );
+            assertEquals( oid, syntaxDescription.getNumericOid() );
+        }
+        else
+        {
+            assertNull( syntaxDescription );
+        }
+
+        // -------------------------------------------------------------------
+        // check next to see if it is present in the schema partition
+        // -------------------------------------------------------------------
+        
+        attrs = null;
+
+        LdapContext schemaRoot = getSchemaContext( service );
+        if ( isPresent )
+        {
+            attrs = schemaRoot.getAttributes( "m-oid=" + oid + ",ou=syntaxes,cn=" + schemaName );
+            assertNotNull( attrs );
+        }
+        else
+        {
+            //noinspection EmptyCatchBlock
+            try
+            {
+                attrs = schemaRoot.getAttributes( "m-oid=" + oid + ",ou=syntaxes,cn=" + schemaName );
+                fail( "should never get here" );
+            }
+            catch( NamingException e )
+            {
+            }
+            assertNull( attrs );
+        }
+        
+        // -------------------------------------------------------------------
+        // check to see if it is present in the syntaxRegistry
+        // -------------------------------------------------------------------
+        
+        if ( isPresent ) 
+        { 
+            assertTrue( service.getRegistries().getSyntaxRegistry().hasSyntax( oid ) );
+        }
+        else
+        {
+            assertFalse( service.getRegistries().getSyntaxRegistry().hasSyntax( oid ) );
+        }
+    }
+    
+    
+    /**
+     * Tests a number of modify add, remove and replace operation combinations for
+     * syntaxes on the schema subentry.
+     *
+     * @throws Exception on error
+     */
+    @Test
+    public void testAddRemoveReplaceSyntaxes() throws Exception
+    {
+        enableSchema( "nis" );
+        List<String> descriptions = new ArrayList<String>();
+        
+        // -------------------------------------------------------------------
+        // add of syntaxes without their syntax checkers should fail
+        // -------------------------------------------------------------------
+        
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.0.10000 DESC 'bogus desc' X-SCHEMA 'nis' )" );
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.0.10001 DESC 'bogus desc' X-SCHEMA 'nis' )" );
+        
+        try
+        {
+            modify( DirContext.ADD_ATTRIBUTE, descriptions, "ldapSyntaxes" );
+            fail( "should not be able to add syntaxes without their syntaxCheckers" );
+        }
+        catch( LdapOperationNotSupportedException e )
+        {
+            assertEquals( ResultCodeEnum.UNWILLING_TO_PERFORM, e.getResultCode() );
+        }
+        
+        // none of the syntaxes should be present
+        checkSyntaxPresent( "1.3.6.1.4.1.18060.0.4.1.0.10000", "nis", false );
+        checkSyntaxPresent( "1.3.6.1.4.1.18060.0.4.1.0.10001", "nis", false );
+
+        // -------------------------------------------------------------------
+        // first in order to add syntaxes we need their syntax checkers 
+        // -------------------------------------------------------------------
+
+        descriptions.clear();
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.0.10000 DESC 'bogus desc' FQCN " 
+            + AcceptAllSyntaxChecker.class.getName() + " X-SCHEMA 'nis' )" );
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.0.10001 DESC 'bogus desc' FQCN " 
+            + AcceptAllSyntaxChecker.class.getName() + " X-SCHEMA 'nis' )" );
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.0.10002 DESC 'bogus desc' FQCN " 
+            + AcceptAllSyntaxChecker.class.getName() + " X-SCHEMA 'nis' )" );
+
+        modify( DirContext.ADD_ATTRIBUTE, descriptions, "syntaxCheckers" );
+        checkSyntaxCheckerPresent( "1.3.6.1.4.1.18060.0.4.1.0.10000", "nis", true );
+        checkSyntaxCheckerPresent( "1.3.6.1.4.1.18060.0.4.1.0.10001", "nis", true );
+        checkSyntaxCheckerPresent( "1.3.6.1.4.1.18060.0.4.1.0.10002", "nis", true );
+
+        // -------------------------------------------------------------------
+        // add and check
+        // -------------------------------------------------------------------
+        
+        descriptions.clear();
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.0.10000 DESC 'bogus desc' X-SCHEMA 'nis' )" );
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.0.10001 DESC 'bogus desc' X-SCHEMA 'nis' )" );
+
+        modify( DirContext.ADD_ATTRIBUTE, descriptions, "ldapSyntaxes" );
+        checkSyntaxPresent( "1.3.6.1.4.1.18060.0.4.1.0.10000", "nis", true );
+        checkSyntaxPresent( "1.3.6.1.4.1.18060.0.4.1.0.10001", "nis", true );
+
+        // -------------------------------------------------------------------
+        // remove and check
+        // -------------------------------------------------------------------
+        
+        modify( DirContext.REMOVE_ATTRIBUTE, descriptions, "ldapSyntaxes" );
+        checkSyntaxPresent( "1.3.6.1.4.1.18060.0.4.1.0.10000", "nis", false );
+        checkSyntaxPresent( "1.3.6.1.4.1.18060.0.4.1.0.10001", "nis", false );
+        
+        // -------------------------------------------------------------------
+        // test failure to replace
+        // -------------------------------------------------------------------
+        
+        try
+        {
+            modify( DirContext.REPLACE_ATTRIBUTE, descriptions, "ldapSyntaxes" );
+            fail( "modify REPLACE operations should not be allowed" );
+        }
+        catch ( LdapOperationNotSupportedException e )
+        {
+            assertEquals( ResultCodeEnum.UNWILLING_TO_PERFORM, e.getResultCode() );
+        }
+
+        // -------------------------------------------------------------------
+        // check add no schema info
+        // -------------------------------------------------------------------
+        
+        descriptions.clear();
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.0.10002 DESC 'bogus desc' )" );
+        modify( DirContext.ADD_ATTRIBUTE, descriptions, "ldapSyntaxes" );
+        checkSyntaxPresent( "1.3.6.1.4.1.18060.0.4.1.0.10002", "other", true );
+    }
+    
+    
+    // -----------------------------------------------------------------------
+    // MatchingRule Tests
+    // -----------------------------------------------------------------------
+    
+    
+    private void checkMatchingRulePresent( String oid, String schemaName, boolean isPresent ) throws Exception
+    {
+        // -------------------------------------------------------------------
+        // check first to see if it is present in the subschemaSubentry
+        // -------------------------------------------------------------------
+        
+        Attributes attrs = getSubschemaSubentryAttributes();
+        Attribute attrTypes = attrs.get( "matchingRules" );
+        MatchingRuleDescription matchingRuleDescription = null; 
+        for ( int ii = 0; ii < attrTypes.size(); ii++ )
+        {
+            String desc = ( String ) attrTypes.get( ii );
+            if ( desc.indexOf( oid ) != -1 )
+            {
+                matchingRuleDescription = matchingRuleDescriptionSchemaParser.parseMatchingRuleDescription( desc );
+                break;
+            }
+        }
+     
+        if ( isPresent )
+        {
+            assertNotNull( matchingRuleDescription );
+            assertEquals( oid, matchingRuleDescription.getNumericOid() );
+        }
+        else
+        {
+            assertNull( matchingRuleDescription );
+        }
+
+        // -------------------------------------------------------------------
+        // check next to see if it is present in the schema partition
+        // -------------------------------------------------------------------
+        
+        attrs = null;
+
+        LdapContext schemaRoot = getSchemaContext( service );
+        if ( isPresent )
+        {
+            attrs = schemaRoot.getAttributes( "m-oid=" + oid + ",ou=matchingRules,cn=" + schemaName );
+            assertNotNull( attrs );
+        }
+        else
+        {
+            //noinspection EmptyCatchBlock
+            try
+            {
+                attrs = schemaRoot.getAttributes( "m-oid=" + oid + ",ou=matchingRules,cn=" + schemaName );
+                fail( "should never get here" );
+            }
+            catch( NamingException e )
+            {
+            }
+            assertNull( attrs );
+        }
+        
+        // -------------------------------------------------------------------
+        // check to see if it is present in the matchingRuleRegistry
+        // -------------------------------------------------------------------
+        
+        if ( isPresent ) 
+        { 
+            assertTrue( service.getRegistries().getMatchingRuleRegistry().hasMatchingRule( oid ) );
+        }
+        else
+        {
+            assertFalse( service.getRegistries().getMatchingRuleRegistry().hasMatchingRule( oid ) );
+        }
+    }
+    
+    
+    /**
+     * Tests a number of modify add, remove and replace operation combinations for
+     * matchingRules on the schema subentry.
+     *
+     * @throws Exception on error
+     */
+    @Test
+    public void testAddRemoveReplaceMatchingRules() throws Exception
+    {
+        enableSchema( "nis" );
+        List<String> descriptions = new ArrayList<String>();
+
+        // -------------------------------------------------------------------
+        // test rejection with non-existant syntax
+        // -------------------------------------------------------------------
+        
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.1.10000 DESC 'bogus desc' SYNTAX 1.2.3.4 X-SCHEMA 'nis' )" );
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.1.10001 DESC 'bogus desc' SYNTAX 1.2.3.4 X-SCHEMA 'nis' )" );
+
+        try
+        {
+            modify( DirContext.ADD_ATTRIBUTE, descriptions, "matchingRules" );
+            fail( "Cannot add matchingRule with bogus non-existant syntax" );
+        }
+        catch( LdapOperationNotSupportedException e )
+        {
+            assertEquals( ResultCodeEnum.UNWILLING_TO_PERFORM, e.getResultCode() );
+        }
+        
+        checkMatchingRulePresent( "1.3.6.1.4.1.18060.0.4.1.1.10000", "nis", false );
+        checkMatchingRulePresent( "1.3.6.1.4.1.18060.0.4.1.1.10001", "nis", false );
+
+        // -------------------------------------------------------------------
+        // test add with existant syntax but no name and no desc
+        // -------------------------------------------------------------------
+
+        descriptions.clear();
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.1.10000 " +
+                "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-SCHEMA 'nis' )" );
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.1.10001 " +
+                "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-SCHEMA 'nis' )" );
+        
+        modify( DirContext.ADD_ATTRIBUTE, descriptions, "matchingRules" );
+        
+        checkMatchingRulePresent( "1.3.6.1.4.1.18060.0.4.1.1.10000", "nis", true );
+        checkMatchingRulePresent( "1.3.6.1.4.1.18060.0.4.1.1.10001", "nis", true );
+
+        // -------------------------------------------------------------------
+        // test add with existant syntax but no name 
+        // -------------------------------------------------------------------
+        
+        // clear the matchingRules out now
+        modify( DirContext.REMOVE_ATTRIBUTE, descriptions, "matchingRules" );
+        checkMatchingRulePresent( "1.3.6.1.4.1.18060.0.4.1.1.10000", "nis", false );
+        checkMatchingRulePresent( "1.3.6.1.4.1.18060.0.4.1.1.10001", "nis", false );
+
+        descriptions.clear();
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.1.10000 DESC 'bogus desc' " +
+                "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-SCHEMA 'nis' )" );
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.1.10001 DESC 'bogus desc' " +
+                "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-SCHEMA 'nis' )" );
+        
+        modify( DirContext.ADD_ATTRIBUTE, descriptions, "matchingRules" );
+        
+        checkMatchingRulePresent( "1.3.6.1.4.1.18060.0.4.1.1.10000", "nis", true );
+        checkMatchingRulePresent( "1.3.6.1.4.1.18060.0.4.1.1.10001", "nis", true );
+
+        // -------------------------------------------------------------------
+        // test add success with name
+        // -------------------------------------------------------------------
+        
+        modify( DirContext.REMOVE_ATTRIBUTE, descriptions, "matchingRules" );
+        checkMatchingRulePresent( "1.3.6.1.4.1.18060.0.4.1.1.10000", "nis", false );
+        checkMatchingRulePresent( "1.3.6.1.4.1.18060.0.4.1.1.10001", "nis", false );
+        
+        descriptions.clear();
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.1.10000 NAME 'blah0' DESC 'bogus desc' " +
+                "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-SCHEMA 'nis' )" );
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.1.10001 NAME ( 'blah1' 'othername1' ) DESC 'bogus desc' " +
+                "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-SCHEMA 'nis' )" );
+        
+        modify( DirContext.ADD_ATTRIBUTE, descriptions, "matchingRules" );
+        
+        checkMatchingRulePresent( "1.3.6.1.4.1.18060.0.4.1.1.10000", "nis", true );
+        checkMatchingRulePresent( "1.3.6.1.4.1.18060.0.4.1.1.10001", "nis", true );
+
+        // -------------------------------------------------------------------
+        // test add success full (with obsolete)
+        // -------------------------------------------------------------------
+        
+        modify( DirContext.REMOVE_ATTRIBUTE, descriptions, "matchingRules" );
+        checkMatchingRulePresent( "1.3.6.1.4.1.18060.0.4.1.1.10000", "nis", false );
+        checkMatchingRulePresent( "1.3.6.1.4.1.18060.0.4.1.1.10001", "nis", false );
+        
+        descriptions.clear();
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.1.10000 NAME 'blah0' DESC 'bogus desc' " +
+                "OBSOLETE SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-SCHEMA 'nis' )" );
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.1.10001 NAME ( 'blah1' 'othername1' ) DESC 'bogus desc' " +
+                "OBSOLETE SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-SCHEMA 'nis' )" );
+        
+        modify( DirContext.ADD_ATTRIBUTE, descriptions, "matchingRules" );
+        
+        checkMatchingRulePresent( "1.3.6.1.4.1.18060.0.4.1.1.10000", "nis", true );
+        checkMatchingRulePresent( "1.3.6.1.4.1.18060.0.4.1.1.10001", "nis", true );
+
+        // -------------------------------------------------------------------
+        // test failure to replace
+        // -------------------------------------------------------------------
+        
+        modify( DirContext.REMOVE_ATTRIBUTE, descriptions, "matchingRules" );
+        checkMatchingRulePresent( "1.3.6.1.4.1.18060.0.4.1.1.10000", "nis", false );
+        checkMatchingRulePresent( "1.3.6.1.4.1.18060.0.4.1.1.10001", "nis", false );
+        
+        try
+        {
+            modify( DirContext.REPLACE_ATTRIBUTE, descriptions, "matchingRules" );
+            fail( "modify REPLACE operations should not be allowed" );
+        }
+        catch ( LdapOperationNotSupportedException e )
+        {
+            assertEquals( ResultCodeEnum.UNWILLING_TO_PERFORM, e.getResultCode() );
+        }
+
+        // -------------------------------------------------------------------
+        // check add no schema info
+        // -------------------------------------------------------------------
+        
+        descriptions.clear();
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.1.10002 DESC 'bogus desc' " +
+        "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )" );
+        modify( DirContext.ADD_ATTRIBUTE, descriptions, "matchingRules" );
+        checkMatchingRulePresent( "1.3.6.1.4.1.18060.0.4.1.1.10002", "other", true );
+    }
+
+    
+    // -----------------------------------------------------------------------
+    // AttributeType Tests
+    // -----------------------------------------------------------------------
+
+    
+    private void checkAttributeTypePresent( String oid, String schemaName, boolean isPresent ) throws Exception
+    {
+        // -------------------------------------------------------------------
+        // check first to see if it is present in the subschemaSubentry
+        // -------------------------------------------------------------------
+        
+        Attributes attrs = getSubschemaSubentryAttributes();
+        Attribute attrTypes = attrs.get( "attributeTypes" );
+        AttributeTypeDescription attributeTypeDescription = null; 
+        for ( int ii = 0; ii < attrTypes.size(); ii++ )
+        {
+            String desc = ( String ) attrTypes.get( ii );
+            if ( desc.indexOf( oid ) != -1 )
+            {
+                attributeTypeDescription = ATTRIBUTE_TYPE_DESCRIPTION_SCHEMA_PARSER.parseAttributeTypeDescription( desc );
+                break;
+            }
+        }
+     
+        if ( isPresent )
+        {
+            assertNotNull( attributeTypeDescription );
+            assertEquals( oid, attributeTypeDescription.getNumericOid() );
+        }
+        else
+        {
+            assertNull( attributeTypeDescription );
+        }
+
+        // -------------------------------------------------------------------
+        // check next to see if it is present in the schema partition
+        // -------------------------------------------------------------------
+
+        //noinspection UnusedAssignment
+        attrs = null;
+
+        LdapContext schemaRoot = getSchemaContext( service );
+        if ( isPresent )
+        {
+            attrs = schemaRoot.getAttributes( "m-oid=" + oid + ",ou=attributeTypes,cn=" + schemaName );
+            assertNotNull( attrs );
+        }
+        else
+        {
+            //noinspection EmptyCatchBlock
+            try
+            {
+                attrs = schemaRoot.getAttributes( "m-oid=" + oid + ",ou=attributeTypes,cn=" + schemaName );
+                fail( "should never get here" );
+            }
+            catch( NamingException e )
+            {
+            }
+            assertNull( attrs );
+        }
+        
+        // -------------------------------------------------------------------
+        // check to see if it is present in the attributeTypeRegistry
+        // -------------------------------------------------------------------
+        
+        if ( isPresent ) 
+        { 
+            assertTrue( service.getRegistries().getAttributeTypeRegistry().hasAttributeType( oid ) );
+        }
+        else
+        {
+            assertFalse( service.getRegistries().getAttributeTypeRegistry().hasAttributeType( oid ) );
+        }
+    }
+    
+    
+    /**
+     * Tests a number of modify add, remove and replace operation combinations for
+     * attributeTypes on the schema subentry.
+     *
+     * @throws Exception on error
+     */
+    @Test
+    public void testAddRemoveReplaceAttributeTypes() throws Exception
+    {
+        enableSchema( "nis" );
+        List<String> descriptions = new ArrayList<String>();
+
+        // -------------------------------------------------------------------
+        // test rejection with non-existant syntax
+        // -------------------------------------------------------------------
+        
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.2.10000 DESC 'bogus desc' " +
+                "SYNTAX 1.2.3.4 X-SCHEMA 'nis' )" );
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.2.10001 DESC 'bogus desc' " +
+                "SYNTAX 1.2.3.4 X-SCHEMA 'nis' )" );
+
+        try
+        {
+            modify( DirContext.ADD_ATTRIBUTE, descriptions, "attributeTypes" );
+            fail( "Cannot add attributeType with bogus non-existant syntax" );
+        }
+        catch( LdapOperationNotSupportedException e )
+        {
+            assertEquals( ResultCodeEnum.UNWILLING_TO_PERFORM, e.getResultCode() );
+        }
+        
+        checkAttributeTypePresent( "1.3.6.1.4.1.18060.0.4.1.2.10000", "nis", false );
+        checkAttributeTypePresent( "1.3.6.1.4.1.18060.0.4.1.2.10001", "nis", false );
+
+        // -------------------------------------------------------------------
+        // test reject with non-existant super type
+        // -------------------------------------------------------------------
+
+        descriptions.clear();
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.2.10000 " +
+                "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SUP 1.2.3.4 X-SCHEMA 'nis' )" );
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.2.10001 " +
+                "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SUP 1.2.3.4 X-SCHEMA 'nis' )" );
+        
+        try
+        {
+            modify( DirContext.ADD_ATTRIBUTE, descriptions, "attributeTypes" );
+            fail( "Cannot add attributeType with bogus non-existant syntax" );
+        }
+        catch( LdapOperationNotSupportedException e )
+        {
+            assertEquals( ResultCodeEnum.UNWILLING_TO_PERFORM, e.getResultCode() );
+        }
+        
+        checkAttributeTypePresent( "1.3.6.1.4.1.18060.0.4.1.2.10000", "nis", false );
+        checkAttributeTypePresent( "1.3.6.1.4.1.18060.0.4.1.2.10001", "nis", false );
+
+        // -------------------------------------------------------------------
+        // test reject with non-existant equality matchingRule
+        // -------------------------------------------------------------------
+
+        descriptions.clear();
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.2.10000 " +
+                "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 EQUALITY 1.2.3.4 X-SCHEMA 'nis' )" );
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.2.10001 " +
+                "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 EQUALITY 1.2.3.4 X-SCHEMA 'nis' )" );
+        
+        try
+        {
+            modify( DirContext.ADD_ATTRIBUTE, descriptions, "attributeTypes" );
+            fail( "Cannot add attributeType with bogus non-existant equality MatchingRule" );
+        }
+        catch( LdapOperationNotSupportedException e )
+        {
+            assertEquals( ResultCodeEnum.UNWILLING_TO_PERFORM, e.getResultCode() );
+        }
+        
+        checkAttributeTypePresent( "1.3.6.1.4.1.18060.0.4.1.2.10000", "nis", false );
+        checkAttributeTypePresent( "1.3.6.1.4.1.18060.0.4.1.2.10001", "nis", false );
+
+        // -------------------------------------------------------------------
+        // test reject with non-existant ordering matchingRule
+        // -------------------------------------------------------------------
+
+        descriptions.clear();
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.2.10000 " +
+                "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ORDERING 1.2.3.4 X-SCHEMA 'nis' )" );
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.2.10001 " +
+                "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ORDERING 1.2.3.4 X-SCHEMA 'nis' )" );
+        
+        try
+        {
+            modify( DirContext.ADD_ATTRIBUTE, descriptions, "attributeTypes" );
+            fail( "Cannot add attributeType with bogus non-existant ordering MatchingRule" );
+        }
+        catch( LdapOperationNotSupportedException e )
+        {
+            assertEquals( ResultCodeEnum.UNWILLING_TO_PERFORM, e.getResultCode() );
+        }
+        
+        checkAttributeTypePresent( "1.3.6.1.4.1.18060.0.4.1.2.10000", "nis", false );
+        checkAttributeTypePresent( "1.3.6.1.4.1.18060.0.4.1.2.10001", "nis", false );
+
+        // -------------------------------------------------------------------
+        // test reject with non-existant substring matchingRule
+        // -------------------------------------------------------------------
+
+        descriptions.clear();
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.2.10000 " +
+                "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SUBSTR 1.2.3.4 X-SCHEMA 'nis' )" );
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.2.10001 " +
+                "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SUBSTR 1.2.3.4 X-SCHEMA 'nis' )" );
+        
+        try
+        {
+            modify( DirContext.ADD_ATTRIBUTE, descriptions, "attributeTypes" );
+            fail( "Cannot add attributeType with bogus non-existant substrings MatchingRule" );
+        }
+        catch( LdapOperationNotSupportedException e )
+        {
+            assertEquals( ResultCodeEnum.UNWILLING_TO_PERFORM, e.getResultCode() );
+        }
+        
+        checkAttributeTypePresent( "1.3.6.1.4.1.18060.0.4.1.2.10000", "nis", false );
+        checkAttributeTypePresent( "1.3.6.1.4.1.18060.0.4.1.2.10001", "nis", false );
+
+        // -------------------------------------------------------------------
+        // test success with valid superior, valid syntax but no name
+        // -------------------------------------------------------------------
+
+        descriptions.clear();
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.2.10000 " +
+                "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SUP 2.5.4.41 X-SCHEMA 'nis' )" );
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.2.10001 " +
+                "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SUP 2.5.4.41 X-SCHEMA 'nis' )" );
+        
+        modify( DirContext.ADD_ATTRIBUTE, descriptions, "attributeTypes" );
+        
+        checkAttributeTypePresent( "1.3.6.1.4.1.18060.0.4.1.2.10000", "nis", true );
+        checkAttributeTypePresent( "1.3.6.1.4.1.18060.0.4.1.2.10001", "nis", true );
+
+        // -------------------------------------------------------------------
+        // test success with valid superior, valid syntax and names
+        // -------------------------------------------------------------------
+
+        modify( DirContext.REMOVE_ATTRIBUTE, descriptions, "attributeTypes" );
+        
+        descriptions.clear();
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.2.10000 NAME 'type0' " +
+                "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SUP 2.5.4.41 X-SCHEMA 'nis' )" );
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.2.10001 NAME ( 'type1' 'altName' ) " +
+                "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SUP 2.5.4.41 X-SCHEMA 'nis' )" );
+        
+        modify( DirContext.ADD_ATTRIBUTE, descriptions, "attributeTypes" );
+        
+        checkAttributeTypePresent( "1.3.6.1.4.1.18060.0.4.1.2.10000", "nis", true );
+        checkAttributeTypePresent( "1.3.6.1.4.1.18060.0.4.1.2.10001", "nis", true );
+
+        // -------------------------------------------------------------------
+        // test success with everything
+        // -------------------------------------------------------------------
+
+        modify( DirContext.REMOVE_ATTRIBUTE, descriptions, "attributeTypes" );
+        
+        descriptions.clear();
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.2.10000 NAME 'type0' " +
+                "OBSOLETE SUP 2.5.4.41 " +
+                "EQUALITY caseExactIA5Match " +
+                "ORDERING octetStringOrderingMatch " +
+                "SUBSTR caseExactIA5SubstringsMatch COLLECTIVE " +
+                "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 " +
+                "SINGLE-VALUE USAGE userApplications X-SCHEMA 'nis' )" );
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.2.10001 NAME ( 'type1' 'altName' ) " +
+                "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SUP 2.5.4.41 " +
+                "NO-USER-MODIFICATION USAGE directoryOperation X-SCHEMA 'nis' )" );
+        
+        modify( DirContext.ADD_ATTRIBUTE, descriptions, "attributeTypes" );
+        
+        checkAttributeTypePresent( "1.3.6.1.4.1.18060.0.4.1.2.10000", "nis", true );
+        checkAttributeTypePresent( "1.3.6.1.4.1.18060.0.4.1.2.10001", "nis", true );
+
+        // -------------------------------------------------------------------
+        // test failure to replace
+        // -------------------------------------------------------------------
+        
+        modify( DirContext.REMOVE_ATTRIBUTE, descriptions, "attributeTypes" );
+        checkMatchingRulePresent( "1.3.6.1.4.1.18060.0.4.1.2.10000", "nis", false );
+        checkMatchingRulePresent( "1.3.6.1.4.1.18060.0.4.1.2.10001", "nis", false );
+        
+        try
+        {
+            modify( DirContext.REPLACE_ATTRIBUTE, descriptions, "attributeTypes" );
+            fail( "modify REPLACE operations should not be allowed" );
+        }
+        catch ( LdapOperationNotSupportedException e )
+        {
+            assertEquals( ResultCodeEnum.UNWILLING_TO_PERFORM, e.getResultCode() );
+        }
+
+        // -------------------------------------------------------------------
+        // check add no schema info
+        // -------------------------------------------------------------------
+        
+        descriptions.clear();
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.2.10000 NAME 'type0' " +
+                "OBSOLETE SUP 2.5.4.41 " +
+                "EQUALITY caseExactIA5Match " +
+                "ORDERING octetStringOrderingMatch " +
+                "SUBSTR caseExactIA5SubstringsMatch COLLECTIVE " +
+                "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 " +
+                "SINGLE-VALUE USAGE userApplications )" );
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.2.10001 NAME ( 'type1' 'altName' ) " +
+                "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SUP 2.5.4.41 " +
+                "NO-USER-MODIFICATION USAGE directoryOperation )" );
+        
+        modify( DirContext.ADD_ATTRIBUTE, descriptions, "attributeTypes" );
+        
+        checkAttributeTypePresent( "1.3.6.1.4.1.18060.0.4.1.2.10000", "other", true );
+        checkAttributeTypePresent( "1.3.6.1.4.1.18060.0.4.1.2.10001", "other", true );
+    }
+
+    
+    /**
+     * Tests the addition of a new attributeType via a modify ADD on the SSSE to disabled schema.
+     *
+     * @throws Exception on error
+     */
+    @Test
+    public void testAddAttributeTypeOnDisabledSchema() throws Exception
+    {
+        disableSchema( "nis" );
+        LdapDN dn = new LdapDN( getSubschemaSubentryDN() );
+        String substrate = "( 1.3.6.1.4.1.18060.0.4.0.2.10000 NAME ( 'bogus' 'bogusName' ) " +
+            "DESC 'bogus description' SUP name SINGLE-VALUE X-SCHEMA 'nis' )";
+        ModificationItemImpl[] mods = new ModificationItemImpl[1];
+        mods[0] = new ModificationItemImpl( DirContext.ADD_ATTRIBUTE, 
+            new AttributeImpl( "attributeTypes", substrate ) );
+        
+        getRootContext( service ).modifyAttributes( dn, mods );
+        
+        Attributes attrs = getSubschemaSubentryAttributes();
+        Attribute attrTypes = attrs.get( "attributeTypes" );
+        AttributeTypeDescription attributeTypeDescription = null;
+        
+        for ( int ii = 0; ii < attrTypes.size(); ii++ )
+        {
+            String desc = ( String ) attrTypes.get( ii );
+            
+            if ( desc.indexOf( "1.3.6.1.4.1.18060.0.4.0.2.10000" ) != -1 )
+            {
+                attributeTypeDescription = ATTRIBUTE_TYPE_DESCRIPTION_SCHEMA_PARSER.parseAttributeTypeDescription( desc );
+                break;
+            }
+        }
+        
+        assertNull( attributeTypeDescription );
+
+        attrs = getSchemaContext( service ).getAttributes( "m-oid=1.3.6.1.4.1.18060.0.4.0.2.10000,ou=attributeTypes,cn=nis" );
+        assertNotNull( attrs );
+        SchemaEntityFactory factory = new SchemaEntityFactory( service.getRegistries() );
+        
+        ServerEntry serverEntry = ServerEntryUtils.toServerEntry( attrs, LdapDN.EMPTY_LDAPDN, service.getRegistries() );
+        
+        AttributeType at = factory.getAttributeType( serverEntry, service.getRegistries(), "nis" );
+        assertEquals( "1.3.6.1.4.1.18060.0.4.0.2.10000", at.getOid() );
+        assertEquals( "name", at.getSuperior().getName() );
+        assertEquals( "bogus description", at.getDescription() );
+        assertEquals( "bogus", at.getNamesRef()[0] );
+        assertEquals( "bogusName", at.getNamesRef()[1] );
+        assertEquals( true, at.isCanUserModify() );
+        assertEquals( false, at.isCollective() );
+        assertEquals( false, at.isObsolete() );
+        assertEquals( true, at.isSingleValue() );
+    }
+
+    
+    /**
+     * Tests the addition of a new attributeType via a modify ADD on the SSSE to enabled schema.
+     *
+     * @throws Exception on error
+     */
+    @Test
+    public void testAddAttributeTypeOnEnabledSchema() throws Exception
+    {
+        enableSchema( "nis" );
+        LdapDN dn = new LdapDN( getSubschemaSubentryDN() );
+        String substrate = "( 1.3.6.1.4.1.18060.0.4.0.2.10000 NAME ( 'bogus' 'bogusName' ) " +
+            "DESC 'bogus description' SUP name SINGLE-VALUE X-SCHEMA 'nis' )";
+        ModificationItemImpl[] mods = new ModificationItemImpl[1];
+        mods[0] = new ModificationItemImpl( DirContext.ADD_ATTRIBUTE, 
+            new AttributeImpl( "attributeTypes", substrate ) );
+        
+        getRootContext( service ).modifyAttributes( dn, mods );
+        
+        Attributes attrs = getSubschemaSubentryAttributes();
+        Attribute attrTypes = attrs.get( "attributeTypes" );
+        AttributeTypeDescription attributeTypeDescription = null; 
+        
+        for ( int ii = 0; ii < attrTypes.size(); ii++ )
+        {
+            String desc = ( String ) attrTypes.get( ii );
+            if ( desc.indexOf( "1.3.6.1.4.1.18060.0.4.0.2.10000" ) != -1 )
+            {
+                attributeTypeDescription = ATTRIBUTE_TYPE_DESCRIPTION_SCHEMA_PARSER.parseAttributeTypeDescription( desc );
+                break;
+            }
+        }
+        
+        assertNotNull( attributeTypeDescription );
+        assertEquals( true, attributeTypeDescription.isSingleValued() );
+        assertEquals( false, attributeTypeDescription.isCollective() );
+        assertEquals( false, attributeTypeDescription.isObsolete() );
+        assertEquals( true, attributeTypeDescription.isUserModifiable() );
+        assertEquals( "bogus description", attributeTypeDescription.getDescription() );
+        assertEquals( "bogus", attributeTypeDescription.getNames().get( 0 ) );
+        assertEquals( "bogusName", attributeTypeDescription.getNames().get( 1 ) );
+        assertEquals( "name", attributeTypeDescription.getSuperType() );
+        
+        attrs = getSchemaContext( service ).getAttributes(
+                "m-oid=1.3.6.1.4.1.18060.0.4.0.2.10000,ou=attributeTypes,cn=nis" );
+        assertNotNull( attrs );
+        SchemaEntityFactory factory = new SchemaEntityFactory( service.getRegistries() );
+        
+        ServerEntry serverEntry = ServerEntryUtils.toServerEntry( attrs, LdapDN.EMPTY_LDAPDN, service.getRegistries() );
+
+        AttributeType at = factory.getAttributeType( serverEntry, service.getRegistries(), "nis" );
+        assertEquals( "1.3.6.1.4.1.18060.0.4.0.2.10000", at.getOid() );
+        assertEquals( "name", at.getSuperior().getName() );
+        assertEquals( "bogus description", at.getDescription() );
+        assertEquals( "bogus", at.getNamesRef()[0] );
+        assertEquals( "bogusName", at.getNamesRef()[1] );
+        assertEquals( true, at.isCanUserModify() );
+        assertEquals( false, at.isCollective() );
+        assertEquals( false, at.isObsolete() );
+        assertEquals( true, at.isSingleValue() );
+    }
+
+
+    // -----------------------------------------------------------------------
+    // ObjectClass Tests
+    // -----------------------------------------------------------------------
+    
+    
+    private void checkObjectClassPresent( String oid, String schemaName, boolean isPresent ) throws Exception
+    {
+        // -------------------------------------------------------------------
+        // check first to see if it is present in the subschemaSubentry
+        // -------------------------------------------------------------------
+        
+        Attributes attrs = getSubschemaSubentryAttributes();
+        Attribute attrTypes = attrs.get( "objectClasses" );
+        ObjectClassDescription objectClassDescription = null; 
+        for ( int ii = 0; ii < attrTypes.size(); ii++ )
+        {
+            String desc = ( String ) attrTypes.get( ii );
+            if ( desc.indexOf( oid ) != -1 )
+            {
+                objectClassDescription = objectClassDescriptionSchemaParser.parseObjectClassDescription( desc );
+                break;
+            }
+        }
+     
+        if ( isPresent )
+        {
+            assertNotNull( objectClassDescription );
+            assertEquals( oid, objectClassDescription.getNumericOid() );
+        }
+        else
+        {
+            assertNull( objectClassDescription );
+        }
+
+        // -------------------------------------------------------------------
+        // check next to see if it is present in the schema partition
+        // -------------------------------------------------------------------
+
+        //noinspection UnusedAssignment
+        attrs = null;
+        
+        if ( isPresent )
+        {
+            attrs = getSchemaContext( service ).getAttributes( "m-oid=" + oid
+                    + ",ou=objectClasses,cn=" + schemaName );
+            assertNotNull( attrs );
+        }
+        else
+        {
+            //noinspection EmptyCatchBlock
+            try
+            {
+                attrs = getSchemaContext( service ).getAttributes( "m-oid=" +
+                        oid + ",ou=objectClasses,cn=" + schemaName );
+                fail( "should never get here" );
+            }
+            catch( NamingException e )
+            {
+            }
+            assertNull( attrs );
+        }
+        
+        // -------------------------------------------------------------------
+        // check to see if it is present in the matchingRuleRegistry
+        // -------------------------------------------------------------------
+        
+        if ( isPresent ) 
+        { 
+            assertTrue( service.getRegistries().getObjectClassRegistry().hasObjectClass( oid ) );
+        }
+        else
+        {
+            assertFalse( service.getRegistries().getObjectClassRegistry().hasObjectClass( oid ) );
+        }
+    }
+    
+    
+    /**
+     * Tests a number of modify add, remove and replace operation combinations for
+     * objectClasses on the schema subentry.
+     *
+     * @throws Exception on error
+     */
+    @Test
+    public void testAddRemoveReplaceObjectClasses() throws Exception
+    {
+        enableSchema( "nis" );
+        List<String> descriptions = new ArrayList<String>();
+
+        // -------------------------------------------------------------------
+        // test rejection with non-existant superclass
+        // -------------------------------------------------------------------
+        
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.3.10000 SUP 1.2.3 X-SCHEMA 'nis' )" );
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.3.10001 SUP ( 1.2.3 $ 4.5.6 ) X-SCHEMA 'nis' )" );
+
+        try
+        {
+            modify( DirContext.ADD_ATTRIBUTE, descriptions, "objectClasses" );
+            fail( "Cannot add objectClass with bogus non-existant super" );
+        }
+        catch( LdapOperationNotSupportedException e )
+        {
+            assertEquals( ResultCodeEnum.UNWILLING_TO_PERFORM, e.getResultCode() );
+        }
+        
+        checkObjectClassPresent( "1.3.6.1.4.1.18060.0.4.1.3.10000", "nis", false );
+        checkObjectClassPresent( "1.3.6.1.4.1.18060.0.4.1.3.10001", "nis", false );
+
+        // -------------------------------------------------------------------
+        // test add with existant superiors but no name and no desc
+        // -------------------------------------------------------------------
+
+        descriptions.clear();
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.3.10000 " +
+                "SUP 2.5.6.0 X-SCHEMA 'nis' )" );
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.3.10001 " +
+                "SUP 2.5.6.0 X-SCHEMA 'nis' )" );
+        
+        modify( DirContext.ADD_ATTRIBUTE, descriptions, "objectClasses" );
+        
+        checkObjectClassPresent( "1.3.6.1.4.1.18060.0.4.1.3.10000", "nis", true );
+        checkObjectClassPresent( "1.3.6.1.4.1.18060.0.4.1.3.10001", "nis", true );
+
+        // -------------------------------------------------------------------
+        // test add with existant superiors with names and no desc
+        // -------------------------------------------------------------------
+
+        modify( DirContext.REMOVE_ATTRIBUTE, descriptions, "objectClasses" );
+        checkObjectClassPresent( "1.3.6.1.4.1.18060.0.4.1.3.10000", "nis", false );
+        checkObjectClassPresent( "1.3.6.1.4.1.18060.0.4.1.3.10001", "nis", false );
+        
+        descriptions.clear();
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.3.10000 " +
+                "NAME ( 'blah0' 'altname0' ) SUP 2.5.6.0 X-SCHEMA 'nis' )" );
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.3.10001 " +
+                "NAME ( 'blah1' 'altname1' ) SUP 2.5.6.0 X-SCHEMA 'nis' )" );
+        
+        modify( DirContext.ADD_ATTRIBUTE, descriptions, "objectClasses" );
+        
+        checkObjectClassPresent( "1.3.6.1.4.1.18060.0.4.1.3.10000", "nis", true );
+        checkObjectClassPresent( "1.3.6.1.4.1.18060.0.4.1.3.10001", "nis", true );
+
+        // -------------------------------------------------------------------
+        // test add with existant superiors with names and desc
+        // -------------------------------------------------------------------
+
+        modify( DirContext.REMOVE_ATTRIBUTE, descriptions, "objectClasses" );
+        checkObjectClassPresent( "1.3.6.1.4.1.18060.0.4.1.3.10000", "nis", false );
+        checkObjectClassPresent( "1.3.6.1.4.1.18060.0.4.1.3.10001", "nis", false );
+        
+        descriptions.clear();
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.3.10000 " +
+                "NAME ( 'blah0' 'altname0' ) DESC 'bogus' SUP 2.5.6.0 X-SCHEMA 'nis' )" );
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.3.10001 " +
+                "NAME ( 'blah1' 'altname1' ) DESC 'bogus' SUP 2.5.6.0 X-SCHEMA 'nis' )" );
+        
+        modify( DirContext.ADD_ATTRIBUTE, descriptions, "objectClasses" );
+        
+        checkObjectClassPresent( "1.3.6.1.4.1.18060.0.4.1.3.10000", "nis", true );
+        checkObjectClassPresent( "1.3.6.1.4.1.18060.0.4.1.3.10001", "nis", true );
+
+        // -------------------------------------------------------------------
+        // test add with many existant superiors with names and desc
+        // -------------------------------------------------------------------
+
+        modify( DirContext.REMOVE_ATTRIBUTE, descriptions, "objectClasses" );
+        checkObjectClassPresent( "1.3.6.1.4.1.18060.0.4.1.3.10000", "nis", false );
+        checkObjectClassPresent( "1.3.6.1.4.1.18060.0.4.1.3.10001", "nis", false );
+        
+        descriptions.clear();
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.3.10000 " +
+                "NAME ( 'blah0' 'altname0' ) DESC 'bogus' SUP ( 2.5.6.0 $ dynamicObject ) X-SCHEMA 'nis' )" );
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.3.10001 " +
+                "NAME ( 'blah1' 'altname1' ) DESC 'bogus' SUP ( 2.5.6.0 $ domain ) X-SCHEMA 'nis' )" );
+        
+        modify( DirContext.ADD_ATTRIBUTE, descriptions, "objectClasses" );
+        
+        checkObjectClassPresent( "1.3.6.1.4.1.18060.0.4.1.3.10000", "nis", true );
+        checkObjectClassPresent( "1.3.6.1.4.1.18060.0.4.1.3.10001", "nis", true );
+
+        // -------------------------------------------------------------------
+        // test reject with non-existant attributeType in may list
+        // -------------------------------------------------------------------
+
+        modify( DirContext.REMOVE_ATTRIBUTE, descriptions, "objectClasses" );
+        checkObjectClassPresent( "1.3.6.1.4.1.18060.0.4.1.3.10000", "nis", false );
+        checkObjectClassPresent( "1.3.6.1.4.1.18060.0.4.1.3.10001", "nis", false );
+        
+        descriptions.clear();
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.3.10000 " +
+                "NAME ( 'blah0' 'altname0' ) DESC 'bogus' SUP ( 2.5.6.0 $ dynamicObject ) " +
+                "MAY ( blah0 $ cn ) X-SCHEMA 'nis' )" );
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.3.10001 " +
+                "NAME ( 'blah1' 'altname1' ) DESC 'bogus' SUP ( 2.5.6.0 $ domain ) " +
+                "MAY ( sn $ blah1 ) X-SCHEMA 'nis' )" );
+        
+        try
+        {
+            modify( DirContext.ADD_ATTRIBUTE, descriptions, "objectClasses" );
+            fail( "Cannot add objectClass with bogus non-existant attributeTypes" );
+        }
+        catch( LdapOperationNotSupportedException e )
+        {
+            assertEquals( ResultCodeEnum.UNWILLING_TO_PERFORM, e.getResultCode() );
+        }
+        
+        checkObjectClassPresent( "1.3.6.1.4.1.18060.0.4.1.3.10000", "nis", false );
+        checkObjectClassPresent( "1.3.6.1.4.1.18060.0.4.1.3.10001", "nis", false );
+
+        // -------------------------------------------------------------------
+        // test reject with non-existant attributeType in must list
+        // -------------------------------------------------------------------
+
+        descriptions.clear();
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.3.10000 " +
+                "NAME ( 'blah0' 'altname0' ) DESC 'bogus' SUP ( 2.5.6.0 $ dynamicObject ) " +
+                "MUST ( blah0 $ cn ) X-SCHEMA 'nis' )" );
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.3.10001 " +
+                "NAME ( 'blah1' 'altname1' ) DESC 'bogus' SUP ( 2.5.6.0 $ domain ) " +
+                "MUST ( sn $ blah1 ) X-SCHEMA 'nis' )" );
+        
+        try
+        {
+            modify( DirContext.ADD_ATTRIBUTE, descriptions, "objectClasses" );
+            fail( "Cannot add objectClass with bogus non-existant attributeTypes" );
+        }
+        catch( LdapOperationNotSupportedException e )
+        {
+            assertEquals( ResultCodeEnum.UNWILLING_TO_PERFORM, e.getResultCode() );
+        }
+        
+        checkObjectClassPresent( "1.3.6.1.4.1.18060.0.4.1.3.10000", "nis", false );
+        checkObjectClassPresent( "1.3.6.1.4.1.18060.0.4.1.3.10001", "nis", false );
+
+        // -------------------------------------------------------------------
+        // test add with valid attributeTypes in may list
+        // -------------------------------------------------------------------
+
+        descriptions.clear();
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.3.10000 " +
+                "NAME ( 'blah0' 'altname0' ) DESC 'bogus' SUP ( 2.5.6.0 $ dynamicObject ) " +
+                "MAY ( sn $ cn ) X-SCHEMA 'nis' )" );
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.3.10001 " +
+                "NAME ( 'blah1' 'altname1' ) DESC 'bogus' SUP ( 2.5.6.0 $ domain ) " +
+                "MAY ( sn $ ou ) X-SCHEMA 'nis' )" );
+        
+        modify( DirContext.ADD_ATTRIBUTE, descriptions, "objectClasses" );
+        
+        checkObjectClassPresent( "1.3.6.1.4.1.18060.0.4.1.3.10000", "nis", true );
+        checkObjectClassPresent( "1.3.6.1.4.1.18060.0.4.1.3.10001", "nis", true );
+
+        // -------------------------------------------------------------------
+        // test add with valid attributeTypes in must list
+        // -------------------------------------------------------------------
+
+        modify( DirContext.REMOVE_ATTRIBUTE, descriptions, "objectClasses" );
+        checkObjectClassPresent( "1.3.6.1.4.1.18060.0.4.1.3.10000", "nis", false );
+        checkObjectClassPresent( "1.3.6.1.4.1.18060.0.4.1.3.10001", "nis", false );
+        
+        descriptions.clear();
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.3.10000 " +
+                "NAME ( 'blah0' 'altname0' ) DESC 'bogus' SUP ( 2.5.6.0 $ dynamicObject ) " +
+                "MUST ( sn $ cn ) X-SCHEMA 'nis' )" );
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.3.10001 " +
+                "NAME ( 'blah1' 'altname1' ) DESC 'bogus' SUP ( 2.5.6.0 $ domain ) " +
+                "MUST ( sn $ ou ) X-SCHEMA 'nis' )" );
+        
+        modify( DirContext.ADD_ATTRIBUTE, descriptions, "objectClasses" );
+        
+        checkObjectClassPresent( "1.3.6.1.4.1.18060.0.4.1.3.10000", "nis", true );
+        checkObjectClassPresent( "1.3.6.1.4.1.18060.0.4.1.3.10001", "nis", true );
+
+        // -------------------------------------------------------------------
+        // test add success full (with obsolete)
+        // -------------------------------------------------------------------
+        
+        modify( DirContext.REMOVE_ATTRIBUTE, descriptions, "objectClasses" );
+        checkObjectClassPresent( "1.3.6.1.4.1.18060.0.4.1.3.10000", "nis", false );
+        checkObjectClassPresent( "1.3.6.1.4.1.18060.0.4.1.3.10001", "nis", false );
+        
+        descriptions.clear();
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.3.10000 " +
+                "NAME ( 'blah0' 'altname0' ) DESC 'bogus' OBSOLETE SUP ( 2.5.6.0 $ dynamicObject ) STRUCTURAL " +
+                "MUST ( sn $ cn ) " +
+                "MAY ( sn $ ou ) " +
+                "X-SCHEMA 'nis' ) " );
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.3.10001 " +
+                "NAME ( 'blah1' 'altname1' ) DESC 'bogus' OBSOLETE SUP ( 2.5.6.0 $ domain ) STRUCTURAL " +
+                "MUST ( sn $ ou ) " +
+                "MAY ( sn $ ou ) " +
+                "X-SCHEMA 'nis' )" );
+        
+        modify( DirContext.ADD_ATTRIBUTE, descriptions, "objectClasses" );
+        
+        checkObjectClassPresent( "1.3.6.1.4.1.18060.0.4.1.3.10000", "nis", true );
+        checkObjectClassPresent( "1.3.6.1.4.1.18060.0.4.1.3.10001", "nis", true );
+
+        // -------------------------------------------------------------------
+        // test failure to replace
+        // -------------------------------------------------------------------
+        
+        modify( DirContext.REMOVE_ATTRIBUTE, descriptions, "objectClasses" );
+        checkMatchingRulePresent( "1.3.6.1.4.1.18060.0.4.1.3.10000", "nis", false );
+        checkMatchingRulePresent( "1.3.6.1.4.1.18060.0.4.1.3.10001", "nis", false );
+        
+        try
+        {
+            modify( DirContext.REPLACE_ATTRIBUTE, descriptions, "objectClasses" );
+            fail( "modify REPLACE operations should not be allowed" );
+        }
+        catch ( LdapOperationNotSupportedException e )
+        {
+            assertEquals( ResultCodeEnum.UNWILLING_TO_PERFORM, e.getResultCode() );
+        }
+
+        // -------------------------------------------------------------------
+        // check add no schema info
+        // -------------------------------------------------------------------
+        
+        descriptions.clear();
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.3.10000 " +
+            "NAME ( 'blah0' 'altname0' ) DESC 'bogus' OBSOLETE SUP ( 2.5.6.0 $ dynamicObject ) STRUCTURAL " +
+            "MUST ( sn $ cn ) " +
+            "MAY ( sn $ ou ) )" );
+        descriptions.add( "( 1.3.6.1.4.1.18060.0.4.1.3.10001 " +
+            "NAME ( 'blah1' 'altname1' ) DESC 'bogus' OBSOLETE SUP ( 2.5.6.0 $ domain ) STRUCTURAL " +
+            "MUST ( sn $ ou ) " +
+            "MAY ( sn $ ou ) )" );
+
+        modify( DirContext.ADD_ATTRIBUTE, descriptions, "objectClasses" );
+        checkObjectClassPresent( "1.3.6.1.4.1.18060.0.4.1.3.10000", "other", true );
+        checkObjectClassPresent( "1.3.6.1.4.1.18060.0.4.1.3.10001", "other", true );
+    }
+
+    
+    // -----------------------------------------------------------------------
+    // Test Modifier and Timestamp Updates 
+    // -----------------------------------------------------------------------
+    
+    
+    /**
+     * This method checks the modifiersName, and the modifyTimestamp on the schema
+     * subentry then modifies a schema.  It then checks it again to make sure these
+     * values have been updated properly to reflect the modification time and the
+     * modifier.
+     *
+     * @throws InterruptedException on error
+     * @throws NamingException on error
+     */
+    @Test
+    @Ignore ( "Don't know why but this is causing intermittant failures in assertions" )
+    public void testTimestampAndModifierUpdates() throws NamingException, InterruptedException
+    {
+        TimeZone tz = TimeZone.getTimeZone( "GMT" );
+        
+        Attributes subentry = this.getSubschemaSubentryAttributes();
+        
+        // check first that everything that is required is present
+        
+        Attribute creatorsNameAttr = subentry.get( "creatorsName" );
+        Attribute createTimestampAttr = subentry.get( "createTimestamp" );
+        assertNotNull( creatorsNameAttr );
+        assertNotNull( createTimestampAttr );
+
+        Attribute modifiersNameAttr = subentry.get( "modifiersName" );
+        Attribute modifyTimestampAttr = subentry.get( "modifyTimestamp" );
+        assertNotNull( modifiersNameAttr );
+        LdapDN expectedDN = new LdapDN( "uid=admin,ou=system" );
+        expectedDN.normalize( service.getRegistries().getAttributeTypeRegistry().getNormalizerMapping() );
+        assertEquals( expectedDN.getNormName(), modifiersNameAttr.get() );
+        assertNotNull( modifyTimestampAttr );
+
+        Calendar cal = Calendar.getInstance( tz );
+        String modifyTimestampStr = ( String ) modifyTimestampAttr.get();
+        Date modifyTimestamp = DateUtils.getDate( modifyTimestampStr );
+        Date currentTimestamp = cal.getTime();
+
+        assertFalse( modifyTimestamp.after( currentTimestamp ) );
+        
+        // now update the schema information: add a new attribute type
+        
+        enableSchema( "nis" );
+        LdapDN dn = new LdapDN( getSubschemaSubentryDN() );
+        String substrate = "( 1.3.6.1.4.1.18060.0.4.0.2.10000 NAME ( 'bogus' 'bogusName' ) " +
+            "DESC 'bogus description' SUP name SINGLE-VALUE X-SCHEMA 'nis' )";
+        ModificationItemImpl[] mods = new ModificationItemImpl[1];
+        mods[0] = new ModificationItemImpl( DirContext.ADD_ATTRIBUTE, 
+            new AttributeImpl( "attributeTypes", substrate ) );
+        
+        getRootContext( service ).modifyAttributes( dn, mods );
+
+        // now check the modification timestamp and the modifiers name
+
+        subentry = this.getSubschemaSubentryAttributes();
+        
+        // check first that everything that is required is present
+        
+        Attribute creatorsNameAttrAfter = subentry.get( "creatorsName" );
+        Attribute createTimestampAttrAfter = subentry.get( "createTimestamp" );
+        assertNotNull( creatorsNameAttrAfter );
+        assertNotNull( createTimestampAttrAfter );
+
+        Attribute modifiersNameAttrAfter = subentry.get( "modifiersName" );
+        Attribute modifiersTimestampAttrAfter = subentry.get( "modifyTimestamp" );
+        assertNotNull( modifiersNameAttrAfter );
+        expectedDN = new LdapDN( "uid=admin,ou=system" );
+        expectedDN.normalize( service.getRegistries().getAttributeTypeRegistry().getNormalizerMapping() );
+        assertEquals( expectedDN.getNormName(), modifiersNameAttrAfter.get() );
+        assertNotNull( modifiersTimestampAttrAfter );
+        
+        cal = Calendar.getInstance( tz );
+        Date modifyTimestampAfter = DateUtils.getDate( ( String ) modifiersTimestampAttrAfter.get() );
+        assertTrue( modifyTimestampAfter.getTime() <= cal.getTime().getTime() );
+
+
+        assertTrue( modifyTimestampAfter.getTime() >= modifyTimestamp.getTime() );
+
+        // now let's test the modifiersName update with another user besides
+        // the administrator - we'll create a dummy user for that ...
+        
+        AttributesImpl user = new AttributesImpl( "objectClass", "person", true );
+        user.put( "sn", "bogus" );
+        user.put( "cn", "bogus user" );
+        user.put( "userPassword", "secret" );
+        getSystemContext( service ).createSubcontext( "cn=bogus user", user );
+        
+        // now let's get a context for this user
+        
+        Hashtable<String,Object> env = new Hashtable<String,Object>();
+        env.put( Context.INITIAL_CONTEXT_FACTORY, "org.apache.directory.server.core.jndi.CoreContextFactory" );
+        env.put( Context.PROVIDER_URL, "" );
+        env.put( Context.SECURITY_AUTHENTICATION, "simple" );
+        env.put( Context.SECURITY_CREDENTIALS, "secret" );
+        env.put( Context.SECURITY_PRINCIPAL, "cn=bogus user,ou=system" );
+        env.put( DirectoryService.JNDI_KEY, service );
+        InitialDirContext ctx = new InitialDirContext( env );
+        
+        // now let's add another attribute type definition to the schema but 
+        // with this newly created user and check that the modifiers name is his
+
+        substrate = "( 1.3.6.1.4.1.18060.0.4.0.2.10001 NAME ( 'bogus2' 'bogusName2' ) " +
+            "DESC 'bogus description' SUP name SINGLE-VALUE X-SCHEMA 'nis' )";
+        mods[0] = new ModificationItemImpl( DirContext.ADD_ATTRIBUTE, 
+            new AttributeImpl( "attributeTypes", substrate ) );
+        ctx.modifyAttributes( dn, mods );
+        
+        // now let's verify the new values for the modification attributes
+
+        subentry = this.getSubschemaSubentryAttributes();
+
+        creatorsNameAttrAfter = subentry.get( "creatorsName" );
+        createTimestampAttrAfter = subentry.get( "createTimestamp" );
+        assertNotNull( creatorsNameAttrAfter );
+        assertNotNull( createTimestampAttrAfter );
+
+        modifiersNameAttrAfter = subentry.get( "modifiersName" );
+        modifiersTimestampAttrAfter = subentry.get( "modifyTimestamp" );
+        assertNotNull( modifiersNameAttrAfter );
+        expectedDN = new LdapDN( "cn=bogus user,ou=system" );
+        expectedDN.normalize( service.getRegistries().getAttributeTypeRegistry().getNormalizerMapping() );
+        assertEquals( expectedDN.getNormName(), modifiersNameAttrAfter.get() );
+        assertNotNull( modifiersTimestampAttrAfter );
+        
+        cal = Calendar.getInstance( tz );
+        modifyTimestamp = DateUtils.getDate( ( String ) modifyTimestampAttr.get() );
+        modifyTimestampAfter = DateUtils.getDate( ( String ) modifiersTimestampAttrAfter.get() );
+        assertTrue( modifyTimestampAfter.getTime() <= cal.getTime().getTime() );
+        assertTrue( modifyTimestampAfter.getTime() >= modifyTimestamp.getTime() );
+    }
+
+
+    // -----------------------------------------------------------------------
+    // Private Utility Methods 
+    // -----------------------------------------------------------------------
+    
+
+    private void modify( int op, List<String> descriptions, String opAttr ) throws Exception
+    {
+        LdapDN dn = new LdapDN( getSubschemaSubentryDN() );
+        Attribute attr = new AttributeImpl( opAttr );
+        for ( String description : descriptions )
+        {
+            attr.add( description );
+        }
+        
+        Attributes mods = new AttributesImpl();
+        mods.put( attr );
+        
+        getRootContext( service ).modifyAttributes( dn, op, mods );
+    }
+    
+    
+    private void enableSchema( String schemaName ) throws NamingException
+    {
+        // now enable the test schema
+        ModificationItemImpl[] mods = new ModificationItemImpl[1];
+        Attribute attr = new AttributeImpl( "m-disabled", "FALSE" );
+        mods[0] = new ModificationItemImpl( DirContext.REPLACE_ATTRIBUTE, attr );
+        getSchemaContext( service ).modifyAttributes( "cn=" + schemaName, mods );
+    }
+    
+    
+    private void disableSchema( String schemaName ) throws NamingException
+    {
+        // now enable the test schema
+        ModificationItemImpl[] mods = new ModificationItemImpl[1];
+        Attribute attr = new AttributeImpl( "m-disabled", "TRUE" );
+        mods[0] = new ModificationItemImpl( DirContext.REPLACE_ATTRIBUTE, attr );
+        getSchemaContext( service ).modifyAttributes( "cn=" + schemaName, mods );
+    }
+    
+    
+    private String getByteCode( String resource ) throws IOException
+    {
+        InputStream in = getClass().getResourceAsStream( resource );
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        while ( in.available() > 0 )
+        {
+            out.write( in.read() );
+        }
+        
+        return new String( Base64.encode( out.toByteArray() ) );
+    }
+
+
+    /**
+     * Get's the subschemaSubentry attribute value from the rootDSE.
+     * 
+     * @return the subschemaSubentry distinguished name
+     * @throws NamingException if there are problems accessing the RootDSE
+     */
+    private String getSubschemaSubentryDN() throws NamingException
+    {
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.OBJECT_SCOPE );
+        controls.setReturningAttributes( new String[]{ SUBSCHEMA_SUBENTRY } );
+        
+        NamingEnumeration<SearchResult> results = getRootContext( service )
+                .search( "", "(objectClass=*)", controls );
+        SearchResult result = results.next();
+        results.close();
+        Attribute subschemaSubentry = result.getAttributes().get( SUBSCHEMA_SUBENTRY );
+        return ( String ) subschemaSubentry.get();
+    }
+
+    
+    /**
+     * Gets the subschemaSubentry attributes for the global schema.
+     *
+     * @return all operational attributes of the subschemaSubentry
+     * @throws NamingException if there are problems accessing this entry
+     */
+    private Attributes getSubschemaSubentryAttributes() throws NamingException
+    {
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.OBJECT_SCOPE );
+        controls.setReturningAttributes( new String[]{ "+", "*" } );
+
+        NamingEnumeration<SearchResult> results = getRootContext( service )
+                .search( getSubschemaSubentryDN(), "(objectClass=*)", controls );
+        SearchResult result = results.next();
+        results.close();
+        return result.getAttributes();
+    }
+}
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/sp/LdapClassLoaderIT.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/sp/LdapClassLoaderIT.java
new file mode 100644
index 0000000..10fc723
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/sp/LdapClassLoaderIT.java
@@ -0,0 +1,139 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.core.sp;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.integ.CiRunner;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getRootContext;
+import org.apache.directory.server.core.jndi.ServerLdapContext;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.util.Base64;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.ldap.LdapContext;
+
+
+/**
+ * Test case for LdapClassLoader.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$ $Date$
+ */
+@RunWith ( CiRunner.class )
+public class LdapClassLoaderIT
+{
+    private static final String HELLOWORLD_CLASS_BASE64 = "yv66vgAAADEAHQoABgAPCQAQABEIABIKABMAFAcAFQcAFgEABjxpbml0PgEAAygpV"
+        + "gEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAARtYWluAQAWKFtMamF2YS9sYW5nL1N0cmluZzsp"
+        + "VgEAClNvdXJjZUZpbGUBAA9IZWxsb1dvcmxkLmphdmEMAAcACAcAFwwAGAAZAQAMSGVsbG8gV29"
+        + "ybGQhBwAaDAAbABwBAApIZWxsb1dvcmxkAQAQamF2YS9sYW5nL09iamVjdAEAEGphdmEvbGFuZy"
+        + "9TeXN0ZW0BAANvdXQBABVMamF2YS9pby9QcmludFN0cmVhbTsBABNqYXZhL2lvL1ByaW50U3RyZ"
+        + "WFtAQAHcHJpbnRsbgEAFShMamF2YS9sYW5nL1N0cmluZzspVgAhAAUABgAAAAAAAgABAAcACAAB"
+        + "AAkAAAAdAAEAAQAAAAUqtwABsQAAAAEACgAAAAYAAQAAAAEACQALAAwAAQAJAAAAJQACAAEAAAA"
+        + "JsgACEgO2AASxAAAAAQAKAAAACgACAAAABQAIAAYAAQANAAAAAgAO";
+
+    private static final byte[] HELLOWORLD_CLASS_BYTES = Base64.decode( HELLOWORLD_CLASS_BASE64.toCharArray() );
+
+
+    public static DirectoryService service;
+
+
+    @Test
+    public void testLdapClassLoaderWithClassLoadedAnywhere() throws Exception
+    {
+        LdapContext root = getRootContext( service );
+
+        // get default naming context to work on
+        ServerLdapContext defaultContext = ( ServerLdapContext ) root.lookup( "ou=system" );
+
+        // set up
+        Attributes attributes = new AttributesImpl( "objectClass", "top", true );
+        attributes.get( "objectClass" ).add( "javaClass" );
+        attributes.put( "fullyQualifiedJavaClassName", "HelloWorld" );
+        attributes.put( "javaClassByteCode", HELLOWORLD_CLASS_BYTES );
+        defaultContext.createSubcontext( "fullyQualifiedJavaClassName=HelloWorld", attributes );
+
+        // assert set up successfull
+        assertNotNull( defaultContext.lookup( "fullyQualifiedJavaClassName=HelloWorld" ) );
+
+        // load the class
+        LdapClassLoader loader = new LdapClassLoader( ( ServerLdapContext ) ( root.lookup( "" ) ) );
+        Class<?> clazz = loader.loadClass( "HelloWorld" );
+
+        // assert class loaded successfully
+        assertEquals( clazz.getName(), "HelloWorld" );
+    }
+
+
+    @Test
+    public void testLdapClassLoaderWithClassLoadedAtDefaultSearchSubtree() throws Exception
+    {
+        LdapContext root = getRootContext( service );
+
+        // get default naming context to work on
+        ServerLdapContext defaultContext = ( ServerLdapContext ) root.lookup( "ou=system" );
+
+        // create an extensible object for holding custom config data
+        Attributes classLoaderDefaultSearchContextConfig = new AttributesImpl();
+        Attribute objectClass = new AttributeImpl( "objectClass" );
+        objectClass.add( "top" );
+        objectClass.add( "javaContainer" );
+        
+        // We need to ad this extensibleObject OC in order to avoid an error
+        // with the classLoaderDefaultSearchContext AT used later (no OC contains
+        // this AT)
+        objectClass.add( "extensibleObject" );
+
+        // create custom config entry
+        classLoaderDefaultSearchContextConfig.put( objectClass );
+        classLoaderDefaultSearchContextConfig.put( new AttributeImpl( "cn", "classLoaderDefaultSearchContext" ) );
+
+        // add a default search context to the configuration
+        classLoaderDefaultSearchContextConfig.put( new AttributeImpl( "classLoaderDefaultSearchContext", "ou=system" ) );
+
+        // add the configuration entry to the DIT
+        ServerLdapContext configContext = ( ServerLdapContext ) defaultContext.lookup( "ou=configuration" );
+        configContext.createSubcontext( "cn=classLoaderDefaultSearchContext", classLoaderDefaultSearchContextConfig );
+
+        // create a class holder entry and add it to the DIT
+        Attributes attributes = new AttributesImpl( "objectClass", "top", true );
+        attributes.get( "objectClass" ).add( "javaClass" );
+        attributes.put( "fullyQualifiedJavaClassName", "HelloWorld" );
+        attributes.put( "javaClassByteCode", HELLOWORLD_CLASS_BYTES );
+        defaultContext.createSubcontext( "fullyQualifiedJavaClassName=HelloWorld", attributes );
+
+        // assert set up successfull
+        assertNotNull( defaultContext.lookup( "fullyQualifiedJavaClassName=HelloWorld" ) );
+
+        // load the class
+        LdapClassLoader loader = new LdapClassLoader( ( ServerLdapContext ) ( root.lookup( "" ) ) );
+        Class clazz = loader.loadClass( "HelloWorld" );
+
+        // assert class loaded successfully
+        assertEquals( clazz.getName(), "HelloWorld" );
+    }
+}
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/subtree/BadSubentryServiceIT.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/subtree/BadSubentryServiceIT.java
new file mode 100644
index 0000000..0d15f9d
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/subtree/BadSubentryServiceIT.java
@@ -0,0 +1,207 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.core.subtree;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.integ.CiRunner;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getSystemContext;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.message.ModificationItemImpl;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.LdapContext;
+import java.util.HashMap;
+import java.util.Map;
+
+
+/**
+ * Testcases for the SubentryInterceptor. Investigation on some serious problems.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+@RunWith ( CiRunner.class )
+public class BadSubentryServiceIT
+{
+    public static DirectoryService service;
+
+
+    public Attributes getTestEntry( String cn )
+    {
+        Attributes entry = new AttributesImpl();
+        Attribute objectClass = new AttributeImpl( "objectClass" );
+        objectClass.add( "top" );
+        objectClass.add( "person" );
+        entry.put( objectClass );
+        entry.put( "cn", cn );
+        entry.put( "sn", cn );
+        return entry;
+    }
+
+
+    public Attributes getCollectiveAttributeTestSubentry( String cn )
+    {
+        Attributes subentry = new AttributesImpl();
+        Attribute objectClass = new AttributeImpl( "objectClass" );
+        objectClass.add( "top" );
+        objectClass.add( SchemaConstants.SUBENTRY_OC );
+        objectClass.add( "collectiveAttributeSubentry" );
+        subentry.put( objectClass );
+        subentry.put( "subtreeSpecification", "{ }" );
+        subentry.put( "c-o", "Test Org" );
+        subentry.put( "cn", cn );
+        return subentry;
+    }
+    
+    
+    public Attributes getAccessControlTestSubentry( String cn )
+    {
+        Attributes subentry = new AttributesImpl();
+        Attribute objectClass = new AttributeImpl( "objectClass" );
+        objectClass.add( "top" );
+        objectClass.add( SchemaConstants.SUBENTRY_OC );
+        objectClass.add( "accessControlSubentry" );
+        subentry.put( objectClass );
+        subentry.put( "subtreeSpecification", "{ }" );
+        subentry.put( "prescriptiveACI",
+            "{ " +
+            "identificationTag \"alllUsersFullAccessACI\", " +
+            "precedence 14, " +
+            "authenticationLevel none, " +
+            "itemOrUserFirst userFirst: " +
+            "{ " +
+              "userClasses " +
+              "{ " +
+                "allUsers " +
+              "}, " +
+              "userPermissions " +
+              "{ " + 
+                "{ " +
+                  "protectedItems " +
+                  "{ " +
+                    "entry, allUserAttributeTypesAndValues " +
+                  "}, " +
+                  "grantsAndDenials " +
+                  "{ " +
+                    "grantAdd, grantDiscloseOnError, grantRead, " +
+                    "grantRemove, grantBrowse, grantExport, grantImport, " +
+                    "grantModify, grantRename, grantReturnDN, " +
+                    "grantCompare, grantFilterMatch, grantInvoke " +
+                  "} " + 
+                "} " +
+              "} " +
+            "} " + 
+          "} "
+           );
+        subentry.put( "cn", cn );
+        return subentry;
+    }
+
+
+    public void addAdministrativeRoles() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        Attribute attribute = new AttributeImpl( "administrativeRole" );
+        attribute.add( "autonomousArea" );
+        attribute.add( "collectiveAttributeSpecificArea" );
+        attribute.add( "accessControlSpecificArea" );
+        ModificationItemImpl item = new ModificationItemImpl( DirContext.ADD_ATTRIBUTE, attribute );
+        sysRoot.modifyAttributes( "", new ModificationItemImpl[] { item } );
+    }
+
+
+    public Map<String, Attributes> getAllEntries() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        Map<String, Attributes> resultMap = new HashMap<String, Attributes>();
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        controls.setReturningAttributes( new String[] { "+", "*" } );
+        NamingEnumeration<SearchResult> results = sysRoot.search( "", "(objectClass=*)", controls );
+        
+        while ( results.hasMore() )
+        {
+            SearchResult result = results.next();
+            resultMap.put( result.getName(), result.getAttributes() );
+        }
+        
+        return resultMap;
+    }
+    
+
+    /*
+     * FIXME: The test fails badly.
+     */
+    @Test
+    public void testTrackingOfSubentryOperationals() throws NamingException
+    {
+        
+        LdapContext sysRoot = getSystemContext( service );
+        addAdministrativeRoles();
+        sysRoot.createSubcontext( "cn=collectiveAttributeTestSubentry",
+            getCollectiveAttributeTestSubentry( "collectiveAttributeTestSubentry" ) );
+        sysRoot.createSubcontext( "cn=accessControlTestSubentry",
+            getAccessControlTestSubentry( "accessControlTestSubentry" ) );
+        sysRoot.createSubcontext( "cn=testEntry", getTestEntry( "testEntry" ) );
+        
+        Map<String, Attributes> results = getAllEntries();
+        Attributes testEntry = results.get( "cn=testEntry,ou=system" );
+        
+        //----------------------------------------------------------------------
+        
+        Attribute collectiveAttributeSubentries = testEntry.get( "collectiveAttributeSubentries" );
+        
+        assertTrue( collectiveAttributeSubentries.contains( "2.5.4.3=collectiveattributetestsubentry,2.5.4.11=system" ) );
+        
+        assertFalse( "'collectiveAttributeSubentries' operational attribute SHOULD NOT " + 
+            "contain references to non-'collectiveAttributeSubentry's like 'accessControlSubentry's", 
+            collectiveAttributeSubentries.contains( "2.5.4.3=accesscontroltestsubentry,2.5.4.11=system" ) );
+        
+        assertEquals( 1, collectiveAttributeSubentries.size() );
+        
+        //----------------------------------------------------------------------
+        
+        Attribute accessControlSubentries = testEntry.get( "accessControlSubentries" );
+        
+        assertTrue( accessControlSubentries.contains( "2.5.4.3=accesscontroltestsubentry,2.5.4.11=system" ) );
+        
+        assertFalse( "'accessControlSubentries' operational attribute SHOULD NOT " + 
+            "contain references to non-'accessControlSubentry's like 'collectiveAttributeSubentry's", 
+            accessControlSubentries.contains( "2.5.4.3=collectiveattributetestsubentry,2.5.4.11=system" ) );
+        
+        assertEquals( 1, accessControlSubentries.size() );
+        
+    }
+}
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/subtree/SubentryServiceEntryModificationHandlingIT.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/subtree/SubentryServiceEntryModificationHandlingIT.java
new file mode 100644
index 0000000..7852d53
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/subtree/SubentryServiceEntryModificationHandlingIT.java
@@ -0,0 +1,157 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.core.subtree;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.integ.CiRunner;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getSystemContext;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.message.ModificationItemImpl;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.LdapContext;
+import java.util.HashMap;
+import java.util.Map;
+
+
+/**
+ * Testcases for the SubentryInterceptor. Investigation on handling Subtree Refinement
+ * Selection Membership upon entry modifications. As we allow any LDAP filter to be
+ * specified as specificationFilter in subtreeSpecifications, any modification on
+ * entries can cause changes on subentry operational attributes.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+@RunWith ( CiRunner.class )
+public class SubentryServiceEntryModificationHandlingIT
+{
+    public static DirectoryService service;
+
+
+    public Attributes getTestEntry( String cn )
+    {
+        Attributes entry = new AttributesImpl();
+        Attribute objectClass = new AttributeImpl( "objectClass" );
+        objectClass.add( "top" );
+        objectClass.add( "person" );
+        entry.put( objectClass );
+        entry.put( "cn", cn );
+        entry.put( "sn", cn );
+        return entry;
+    }
+
+
+    public Attributes getCollectiveAttributeTestSubentryWithLDAPFilter( String cn, String sn )
+    {
+        Attributes subentry = new AttributesImpl();
+        Attribute objectClass = new AttributeImpl( "objectClass" );
+        objectClass.add( "top" );
+        objectClass.add( SchemaConstants.SUBENTRY_OC );
+        objectClass.add( "collectiveAttributeSubentry" );
+        subentry.put( objectClass );
+        subentry.put( "subtreeSpecification", "{ specificationFilter (sn=" + sn + ") }" );
+        subentry.put( "c-o", "Test Org" );
+        subentry.put( "cn", cn );
+        return subentry;
+    }
+
+
+    public void addAdministrativeRoles() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        Attribute attribute = new AttributeImpl( "administrativeRole" );
+        attribute.add( "autonomousArea" );
+        attribute.add( "collectiveAttributeSpecificArea" );
+        ModificationItemImpl item = new ModificationItemImpl( DirContext.ADD_ATTRIBUTE, attribute );
+        sysRoot.modifyAttributes( "", new ModificationItemImpl[]
+            { item } );
+    }
+
+
+    public Map<String, Attributes> getAllEntries() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        Map<String, Attributes> resultMap = new HashMap<String, Attributes>();
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        controls.setReturningAttributes( new String[]
+            { "+", "*" } );
+        NamingEnumeration<SearchResult> results = sysRoot.search( "", "(objectClass=*)", controls );
+        
+        while ( results.hasMore() )
+        {
+            SearchResult result = results.next();
+            resultMap.put( result.getName(), result.getAttributes() );
+        }
+        
+        return resultMap;
+    }
+    
+
+    @Test
+    public void testTrackingOfEntryModificationsInSubentryServiceModifyRoutine() throws Exception
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        addAdministrativeRoles();
+        sysRoot.createSubcontext( "cn=collectiveAttributeTestSubentry",
+            getCollectiveAttributeTestSubentryWithLDAPFilter( "collectiveAttributeTestSubentry", "testEntry" ) );
+        sysRoot.createSubcontext( "cn=testEntry", getTestEntry( "testEntry" ) );
+
+        //----------------------------------------------------------------------
+
+        Map<String, Attributes> results = getAllEntries();
+        Attributes testEntry = results.get( "cn=testEntry,ou=system" );
+
+        Attribute collectiveAttributeSubentries = testEntry.get( "collectiveAttributeSubentries" );
+
+        assertNotNull( collectiveAttributeSubentries );
+
+        //----------------------------------------------------------------------
+
+        AttributeImpl attr = new AttributeImpl( "sn", "changedSn");
+        ModificationItemImpl mod = new ModificationItemImpl(DirContext.REPLACE_ATTRIBUTE, attr);
+        ModificationItemImpl[] mods = new ModificationItemImpl[] { mod };
+        
+        sysRoot.modifyAttributes( "cn=testEntry", mods );
+
+        results = getAllEntries();
+        testEntry = ( Attributes ) results.get( "cn=testEntry,ou=system" );
+
+        collectiveAttributeSubentries = testEntry.get( "collectiveAttributeSubentries" );
+
+        assertNull( collectiveAttributeSubentries );
+    }
+
+}
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/subtree/SubentryServiceIT.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/subtree/SubentryServiceIT.java
new file mode 100644
index 0000000..161d7fb
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/subtree/SubentryServiceIT.java
@@ -0,0 +1,932 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.subtree;
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.integ.CiRunner;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getSystemContext;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.exception.LdapNoSuchAttributeException;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.message.ModificationItemImpl;
+import org.apache.directory.shared.ldap.message.SubentriesControl;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.Control;
+import javax.naming.ldap.LdapContext;
+import java.util.HashMap;
+import java.util.Map;
+
+
+/**
+ * Testcases for the SubentryInterceptor.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+@RunWith ( CiRunner.class )
+@Ignore
+public class SubentryServiceIT
+{
+    public static DirectoryService service;
+
+
+    public Attributes getTestEntry( String cn )
+    {
+        Attributes subentry = new AttributesImpl();
+        Attribute objectClass = new AttributeImpl( "objectClass" );
+        objectClass.add( "top" );
+        objectClass.add( "person" );
+        subentry.put( objectClass );
+        subentry.put( "cn", cn );
+        subentry.put( "sn", "testentry" );
+        return subentry;
+    }
+    
+
+    public Attributes getTestSubentry()
+    {
+        Attributes subentry = new AttributesImpl();
+        Attribute objectClass = new AttributeImpl( "objectClass" );
+        objectClass.add( "top" );
+        objectClass.add( SchemaConstants.SUBENTRY_OC );
+        objectClass.add( "collectiveAttributeSubentry" );
+        subentry.put( objectClass );
+        subentry.put( "subtreeSpecification", "{ base \"ou=configuration\" }" );
+        subentry.put( "c-o", "Test Org" );
+        subentry.put( "cn", "testsubentry" );
+        return subentry;
+    }
+
+
+    public Attributes getTestSubentryWithExclusion()
+    {
+        Attributes subentry = new AttributesImpl();
+        Attribute objectClass = new AttributeImpl( "objectClass" );
+        objectClass.add( "top" );
+        objectClass.add( SchemaConstants.SUBENTRY_OC );
+        objectClass.add( "collectiveAttributeSubentry" );
+        subentry.put( objectClass );
+        String spec = "{ base \"ou=configuration\", specificExclusions { chopBefore:\"cn=unmarked\" } }";
+        subentry.put( "subtreeSpecification", spec );
+        subentry.put( "c-o", "Test Org" );
+        subentry.put( "cn", "testsubentry" );
+        return subentry;
+    }
+
+
+    public void addAdministrativeRole( String role ) throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        Attribute attribute = new AttributeImpl( "administrativeRole" );
+        attribute.add( role );
+        ModificationItemImpl item = new ModificationItemImpl( DirContext.ADD_ATTRIBUTE, attribute );
+        sysRoot.modifyAttributes( "", new ModificationItemImpl[]
+            { item } );
+    }
+
+
+    public Map<String, Attributes> getAllEntries() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        Map<String, Attributes> resultMap = new HashMap<String, Attributes>();
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        controls.setReturningAttributes( new String[]
+            { "+", "*" } );
+        NamingEnumeration<SearchResult> results = sysRoot.search( "", "(objectClass=*)", controls );
+        
+        while ( results.hasMore() )
+        {
+            SearchResult result = results.next();
+            resultMap.put( result.getName(), result.getAttributes() );
+        }
+        
+        return resultMap;
+    }
+
+
+    @Test
+    public void testEntryAdd() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        addAdministrativeRole( "collectiveArributeSpecificArea" );
+        sysRoot.createSubcontext( "cn=testsubentry", getTestSubentry() );
+        sysRoot.createSubcontext( "cn=unmarked", getTestEntry( "unmarked" ) );
+        sysRoot.createSubcontext( "cn=marked,ou=configuration", getTestEntry( "marked" ) );
+        Map<String, Attributes> results = getAllEntries();
+
+        // --------------------------------------------------------------------
+        // Make sure entries selected by the subentry do have the mark
+        // --------------------------------------------------------------------
+
+        Attributes marked = results.get( "cn=marked,ou=configuration,ou=system" );
+        Attribute collectiveAttributeSubentries = marked.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT );
+        assertNotNull( "ou=marked,ou=configuration,ou=system should be marked", collectiveAttributeSubentries );
+        assertEquals( "2.5.4.3=testsubentry,2.5.4.11=system", collectiveAttributeSubentries.get() );
+        assertEquals( 1, collectiveAttributeSubentries.size() );
+
+        // --------------------------------------------------------------------
+        // Make sure entries not selected by subentry do not have the mark
+        // --------------------------------------------------------------------
+
+        Attributes unmarked = results.get( "cn=unmarked,ou=system" );
+        assertNull( "cn=unmarked,ou=system should not be marked", unmarked
+            .get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
+    }
+
+
+    @Test
+    public void testSubentryAdd() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        try
+        {
+            sysRoot.createSubcontext( "cn=testsubentry", getTestSubentry() );
+            fail( "should never get here: cannot create subentry under regular entries" );
+        }
+        catch ( LdapNoSuchAttributeException e )
+        {
+        }
+
+        addAdministrativeRole( "collectiveArributeSpecificArea" );
+        sysRoot.createSubcontext( "cn=testsubentry", getTestSubentry() );
+        Map<String, Attributes> results = getAllEntries();
+
+        // --------------------------------------------------------------------
+        // Make sure entries selected by the subentry do have the mark
+        // --------------------------------------------------------------------
+
+        Attributes configuration = results.get( "ou=configuration,ou=system" );
+        Attribute collectiveAttributeSubentries = configuration.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT );
+        assertNotNull( "ou=configuration,ou=system should be marked", collectiveAttributeSubentries );
+        assertEquals( "2.5.4.3=testsubentry,2.5.4.11=system", collectiveAttributeSubentries.get() );
+        assertEquals( 1, collectiveAttributeSubentries.size() );
+
+        Attributes interceptors = results.get( "ou=interceptors,ou=configuration,ou=system" );
+        collectiveAttributeSubentries = interceptors.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT );
+        assertNotNull( "ou=interceptors,ou=configuration,ou=system should be marked", collectiveAttributeSubentries );
+        assertEquals( "2.5.4.3=testsubentry,2.5.4.11=system", collectiveAttributeSubentries.get() );
+        assertEquals( 1, collectiveAttributeSubentries.size() );
+
+        Attributes partitions = results.get( "ou=partitions,ou=configuration,ou=system" );
+        collectiveAttributeSubentries = partitions.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT );
+        assertNotNull( "ou=partitions,ou=configuration,ou=system should be marked", collectiveAttributeSubentries );
+        assertEquals( "2.5.4.3=testsubentry,2.5.4.11=system", collectiveAttributeSubentries.get() );
+        assertEquals( 1, collectiveAttributeSubentries.size() );
+
+        Attributes services = results.get( "ou=services,ou=configuration,ou=system" );
+        collectiveAttributeSubentries = services.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT );
+        assertNotNull( "ou=services,ou=configuration,ou=system should be marked", collectiveAttributeSubentries );
+        assertEquals( "2.5.4.3=testsubentry,2.5.4.11=system", collectiveAttributeSubentries.get() );
+        assertEquals( 1, collectiveAttributeSubentries.size() );
+
+        // --------------------------------------------------------------------
+        // Make sure entries not selected by subentry do not have the mark
+        // --------------------------------------------------------------------
+
+        Attributes system = results.get( "ou=system" );
+        assertNull( "ou=system should not be marked", system.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
+
+        Attributes users = results.get( "ou=users,ou=system" );
+        assertNull( "ou=users,ou=system should not be marked", users.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
+
+        Attributes groups = results.get( "ou=groups,ou=system" );
+        assertNull( "ou=groups,ou=system should not be marked", groups.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
+
+        Attributes admin = results.get( "uid=admin,ou=system" );
+        assertNull( "uid=admin,ou=system should not be marked", admin.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
+
+        Attributes sysPrefRoot = results.get( "prefNodeName=sysPrefRoot,ou=system" );
+        assertNull( "prefNode=sysPrefRoot,ou=system should not be marked", sysPrefRoot
+            .get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
+
+    }
+
+
+    @Test
+    public void testSubentryModify() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        addAdministrativeRole( "collectiveArributeSpecificArea" );
+        sysRoot.createSubcontext( "cn=testsubentry", getTestSubentry() );
+        Map<String, Attributes> results = getAllEntries();
+
+        // --------------------------------------------------------------------
+        // Make sure entries selected by the subentry do have the mark
+        // --------------------------------------------------------------------
+
+        Attributes configuration = results.get( "ou=configuration,ou=system" );
+        Attribute collectiveAttributeSubentries = configuration.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT );
+        assertNotNull( "ou=configuration,ou=system should be marked", collectiveAttributeSubentries );
+        assertEquals( "2.5.4.3=testsubentry,2.5.4.11=system", collectiveAttributeSubentries.get() );
+        assertEquals( 1, collectiveAttributeSubentries.size() );
+
+        Attributes interceptors = results.get( "ou=interceptors,ou=configuration,ou=system" );
+        collectiveAttributeSubentries = interceptors.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT );
+        assertNotNull( "ou=interceptors,ou=configuration,ou=system should be marked", collectiveAttributeSubentries );
+        assertEquals( "2.5.4.3=testsubentry,2.5.4.11=system", collectiveAttributeSubentries.get() );
+        assertEquals( 1, collectiveAttributeSubentries.size() );
+
+        Attributes partitions = results.get( "ou=partitions,ou=configuration,ou=system" );
+        collectiveAttributeSubentries = partitions.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT );
+        assertNotNull( "ou=partitions,ou=configuration,ou=system should be marked", collectiveAttributeSubentries );
+        assertEquals( "2.5.4.3=testsubentry,2.5.4.11=system", collectiveAttributeSubentries.get() );
+        assertEquals( 1, collectiveAttributeSubentries.size() );
+
+        Attributes services = results.get( "ou=services,ou=configuration,ou=system" );
+        collectiveAttributeSubentries = services.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT );
+        assertNotNull( "ou=services,ou=configuration,ou=system should be marked", collectiveAttributeSubentries );
+        assertEquals( "2.5.4.3=testsubentry,2.5.4.11=system", collectiveAttributeSubentries.get() );
+        assertEquals( 1, collectiveAttributeSubentries.size() );
+
+        // --------------------------------------------------------------------
+        // Make sure entries not selected by subentry do not have the mark
+        // --------------------------------------------------------------------
+
+        Attributes system = results.get( "ou=system" );
+        assertNull( "ou=system should not be marked", system.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
+
+        Attributes users = results.get( "ou=users,ou=system" );
+        assertNull( "ou=users,ou=system should not be marked", users.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
+
+        Attributes groups = results.get( "ou=groups,ou=system" );
+        assertNull( "ou=groups,ou=system should not be marked", groups.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
+
+        Attributes admin = results.get( "uid=admin,ou=system" );
+        assertNull( "uid=admin,ou=system should not be marked", admin.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
+
+        Attributes sysPrefRoot = results.get( "prefNodeName=sysPrefRoot,ou=system" );
+        assertNull( "prefNode=sysPrefRoot,ou=system should not be marked", sysPrefRoot
+            .get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
+
+        // --------------------------------------------------------------------
+        // Now modify the subentry by introducing an exclusion
+        // --------------------------------------------------------------------
+
+        Attribute subtreeSpecification = new AttributeImpl( "subtreeSpecification" );
+        subtreeSpecification.add( "{ base \"ou=configuration\", specificExclusions { chopBefore:\"ou=services\" } }" );
+        ModificationItemImpl item = new ModificationItemImpl( DirContext.REPLACE_ATTRIBUTE, subtreeSpecification );
+        sysRoot.modifyAttributes( "cn=testsubentry", new ModificationItemImpl[]
+            { item } );
+        results = getAllEntries();
+
+        // --------------------------------------------------------------------
+        // Make sure entries selected by the subentry do have the mark
+        // --------------------------------------------------------------------
+
+        configuration = results.get( "ou=configuration,ou=system" );
+        collectiveAttributeSubentries = configuration.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT );
+        assertNotNull( "ou=configuration,ou=system should be marked", collectiveAttributeSubentries );
+        assertEquals( "2.5.4.3=testsubentry,2.5.4.11=system", collectiveAttributeSubentries.get() );
+        assertEquals( 1, collectiveAttributeSubentries.size() );
+
+        interceptors = results.get( "ou=interceptors,ou=configuration,ou=system" );
+        collectiveAttributeSubentries = interceptors.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT );
+        assertNotNull( "ou=interceptors,ou=configuration,ou=system should be marked", collectiveAttributeSubentries );
+        assertEquals( "2.5.4.3=testsubentry,2.5.4.11=system", collectiveAttributeSubentries.get() );
+        assertEquals( 1, collectiveAttributeSubentries.size() );
+
+        partitions = results.get( "ou=partitions,ou=configuration,ou=system" );
+        collectiveAttributeSubentries = partitions.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT );
+        assertNotNull( "ou=partitions,ou=configuration,ou=system should be marked", collectiveAttributeSubentries );
+        assertEquals( "2.5.4.3=testsubentry,2.5.4.11=system", collectiveAttributeSubentries.get() );
+        assertEquals( 1, collectiveAttributeSubentries.size() );
+
+        // --------------------------------------------------------------------
+        // Make sure entries not selected by subentry do not have the mark
+        // --------------------------------------------------------------------
+
+        system = results.get( "ou=system" );
+        assertNull( "ou=system should not be marked", system.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
+
+        users = results.get( "ou=users,ou=system" );
+        assertNull( "ou=users,ou=system should not be marked", users.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
+
+        groups = results.get( "ou=groups,ou=system" );
+        assertNull( "ou=groups,ou=system should not be marked", groups.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
+
+        admin = results.get( "uid=admin,ou=system" );
+        assertNull( "uid=admin,ou=system should not be marked", admin.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
+
+        sysPrefRoot = results.get( "prefNodeName=sysPrefRoot,ou=system" );
+        assertNull( "prefNode=sysPrefRoot,ou=system should not be marked", sysPrefRoot
+            .get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
+
+        services = results.get( "ou=services,ou=configuration,ou=system" );
+        collectiveAttributeSubentries = services.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT );
+        if ( collectiveAttributeSubentries != null )
+        {
+            assertEquals( "ou=services,ou=configuration,ou=system should not be marked", 0, collectiveAttributeSubentries.size() );
+        }
+    }
+
+
+    @Test
+    public void testSubentryModify2() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        addAdministrativeRole( "collectiveArributeSpecificArea" );
+        sysRoot.createSubcontext( "cn=testsubentry", getTestSubentry() );
+        Map<String, Attributes> results = getAllEntries();
+
+        // --------------------------------------------------------------------
+        // Make sure entries selected by the subentry do have the mark
+        // --------------------------------------------------------------------
+
+        Attributes configuration = results.get( "ou=configuration,ou=system" );
+        Attribute collectiveAttributeSubentries = configuration.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT );
+        assertNotNull( "ou=configuration,ou=system should be marked", collectiveAttributeSubentries );
+        assertEquals( "2.5.4.3=testsubentry,2.5.4.11=system", collectiveAttributeSubentries.get() );
+        assertEquals( 1, collectiveAttributeSubentries.size() );
+
+        Attributes interceptors = results.get( "ou=interceptors,ou=configuration,ou=system" );
+        collectiveAttributeSubentries = interceptors.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT );
+        assertNotNull( "ou=interceptors,ou=configuration,ou=system should be marked", collectiveAttributeSubentries );
+        assertEquals( "2.5.4.3=testsubentry,2.5.4.11=system", collectiveAttributeSubentries.get() );
+        assertEquals( 1, collectiveAttributeSubentries.size() );
+
+        Attributes partitions = results.get( "ou=partitions,ou=configuration,ou=system" );
+        collectiveAttributeSubentries = partitions.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT );
+        assertNotNull( "ou=partitions,ou=configuration,ou=system should be marked", collectiveAttributeSubentries );
+        assertEquals( "2.5.4.3=testsubentry,2.5.4.11=system", collectiveAttributeSubentries.get() );
+        assertEquals( 1, collectiveAttributeSubentries.size() );
+
+        Attributes services = results.get( "ou=services,ou=configuration,ou=system" );
+        collectiveAttributeSubentries = services.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT );
+        assertNotNull( "ou=services,ou=configuration,ou=system should be marked", collectiveAttributeSubentries );
+        assertEquals( "2.5.4.3=testsubentry,2.5.4.11=system", collectiveAttributeSubentries.get() );
+        assertEquals( 1, collectiveAttributeSubentries.size() );
+
+        // --------------------------------------------------------------------
+        // Make sure entries not selected by subentry do not have the mark
+        // --------------------------------------------------------------------
+
+        Attributes system = results.get( "ou=system" );
+        assertNull( "ou=system should not be marked", system.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
+
+        Attributes users = results.get( "ou=users,ou=system" );
+        assertNull( "ou=users,ou=system should not be marked", users.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
+
+        Attributes groups = results.get( "ou=groups,ou=system" );
+        assertNull( "ou=groups,ou=system should not be marked", groups.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
+
+        Attributes admin = results.get( "uid=admin,ou=system" );
+        assertNull( "uid=admin,ou=system should not be marked", admin.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
+
+        Attributes sysPrefRoot = results.get( "prefNodeName=sysPrefRoot,ou=system" );
+        assertNull( "prefNode=sysPrefRoot,ou=system should not be marked", sysPrefRoot
+            .get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
+
+        // --------------------------------------------------------------------
+        // Now modify the subentry by introducing an exclusion
+        // --------------------------------------------------------------------
+
+        Attributes changes = new AttributesImpl();
+        changes.put( "subtreeSpecification",
+            "{ base \"ou=configuration\", specificExclusions { chopBefore:\"ou=services\" } }" );
+        sysRoot.modifyAttributes( "cn=testsubentry", DirContext.REPLACE_ATTRIBUTE, changes );
+        results = getAllEntries();
+
+        // --------------------------------------------------------------------
+        // Make sure entries selected by the subentry do have the mark
+        // --------------------------------------------------------------------
+
+        configuration = results.get( "ou=configuration,ou=system" );
+        collectiveAttributeSubentries = configuration.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT );
+        assertNotNull( "ou=configuration,ou=system should be marked", collectiveAttributeSubentries );
+        assertEquals( "2.5.4.3=testsubentry,2.5.4.11=system", collectiveAttributeSubentries.get() );
+        assertEquals( 1, collectiveAttributeSubentries.size() );
+
+        interceptors = results.get( "ou=interceptors,ou=configuration,ou=system" );
+        collectiveAttributeSubentries = interceptors.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT );
+        assertNotNull( "ou=interceptors,ou=configuration,ou=system should be marked", collectiveAttributeSubentries );
+        assertEquals( "2.5.4.3=testsubentry,2.5.4.11=system", collectiveAttributeSubentries.get() );
+        assertEquals( 1, collectiveAttributeSubentries.size() );
+
+        partitions = results.get( "ou=partitions,ou=configuration,ou=system" );
+        collectiveAttributeSubentries = partitions.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT );
+        assertNotNull( "ou=partitions,ou=configuration,ou=system should be marked", collectiveAttributeSubentries );
+        assertEquals( "2.5.4.3=testsubentry,2.5.4.11=system", collectiveAttributeSubentries.get() );
+        assertEquals( 1, collectiveAttributeSubentries.size() );
+
+        // --------------------------------------------------------------------
+        // Make sure entries not selected by subentry do not have the mark
+        // --------------------------------------------------------------------
+
+        system = results.get( "ou=system" );
+        assertNull( "ou=system should not be marked", system.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
+
+        users = results.get( "ou=users,ou=system" );
+        assertNull( "ou=users,ou=system should not be marked", users.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
+
+        groups = results.get( "ou=groups,ou=system" );
+        assertNull( "ou=groups,ou=system should not be marked", groups.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
+
+        admin = results.get( "uid=admin,ou=system" );
+        assertNull( "uid=admin,ou=system should not be marked", admin.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
+
+        sysPrefRoot = results.get( "prefNodeName=sysPrefRoot,ou=system" );
+        assertNull( "prefNode=sysPrefRoot,ou=system should not be marked", sysPrefRoot
+            .get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
+
+        services = results.get( "ou=services,ou=configuration,ou=system" );
+        collectiveAttributeSubentries = services.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT );
+        if ( collectiveAttributeSubentries != null )
+        {
+            assertEquals( "ou=services,ou=configuration,ou=system should not be marked", 0, collectiveAttributeSubentries.size() );
+        }
+    }
+
+
+    @Test
+    public void testSubentryDelete() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        addAdministrativeRole( "collectiveArributeSpecificArea" );
+        sysRoot.createSubcontext( "cn=testsubentry", getTestSubentry() );
+        sysRoot.destroySubcontext( "cn=testsubentry" );
+
+        Map<String, Attributes> results = getAllEntries();
+
+        // --------------------------------------------------------------------
+        // Make sure entries not selected by subentry do not have the mark
+        // --------------------------------------------------------------------
+
+        Attributes configuration = results.get( "ou=configuration,ou=system" );
+        Attribute collectiveAttributeSubentries = configuration.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT );
+        if ( collectiveAttributeSubentries != null )
+        {
+            assertEquals( "ou=configuration,ou=system should not be marked", 0, collectiveAttributeSubentries.size() );
+        }
+
+        Attributes interceptors = results.get( "ou=interceptors,ou=configuration,ou=system" );
+        collectiveAttributeSubentries = interceptors.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT );
+        if ( collectiveAttributeSubentries != null )
+        {
+            assertEquals( "ou=interceptors,ou=configuration,ou=system should not be marked", 0, collectiveAttributeSubentries
+                .size() );
+        }
+
+        Attributes partitions = results.get( "ou=partitions,ou=configuration,ou=system" );
+        collectiveAttributeSubentries = partitions.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT );
+        if ( collectiveAttributeSubentries != null )
+        {
+            assertEquals( "ou=partitions,ou=configuration,ou=system should not be marked", 0, collectiveAttributeSubentries.size() );
+        }
+
+        Attributes services = results.get( "ou=services,ou=configuration,ou=system" );
+        collectiveAttributeSubentries = services.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT );
+        if ( collectiveAttributeSubentries != null )
+        {
+            assertEquals( "ou=services,ou=configuration,ou=system should not be marked", 0, collectiveAttributeSubentries.size() );
+        }
+
+        Attributes system = results.get( "ou=system" );
+        assertNull( "ou=system should not be marked", system.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
+
+        Attributes users = results.get( "ou=users,ou=system" );
+        assertNull( "ou=users,ou=system should not be marked", users.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
+
+        Attributes admin = results.get( "uid=admin,ou=system" );
+        assertNull( "uid=admin,ou=system should not be marked", admin.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
+
+        Attributes sysPrefRoot = results.get( "prefNodeName=sysPrefRoot,ou=system" );
+        assertNull( "prefNode=sysPrefRoot,ou=system should not be marked", sysPrefRoot
+            .get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
+
+    }
+
+
+    @Test
+    public void testSubentryModifyRdn() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        addAdministrativeRole( "collectiveArributeSpecificArea" );
+        sysRoot.createSubcontext( "cn=testsubentry", getTestSubentry() );
+        sysRoot.rename( "cn=testsubentry", "cn=newname" );
+        Map<String, Attributes> results = getAllEntries();
+
+        // --------------------------------------------------------------------
+        // Make sure entries selected by the subentry do have the mark
+        // --------------------------------------------------------------------
+
+        Attributes configuration = results.get( "ou=configuration,ou=system" );
+        Attribute collectiveAttributeSubentries = configuration.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT );
+        assertNotNull( "ou=configuration,ou=system should be marked", collectiveAttributeSubentries );
+        assertEquals( "2.5.4.3=newname,2.5.4.11=system", collectiveAttributeSubentries.get() );
+        assertEquals( 1, collectiveAttributeSubentries.size() );
+
+        Attributes interceptors = results.get( "ou=interceptors,ou=configuration,ou=system" );
+        collectiveAttributeSubentries = interceptors.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT );
+        assertNotNull( "ou=interceptors,ou=configuration,ou=system should be marked", collectiveAttributeSubentries );
+        assertEquals( "2.5.4.3=newname,2.5.4.11=system", collectiveAttributeSubentries.get() );
+        assertEquals( 1, collectiveAttributeSubentries.size() );
+
+        Attributes partitions = results.get( "ou=partitions,ou=configuration,ou=system" );
+        collectiveAttributeSubentries = partitions.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT );
+        assertNotNull( "ou=partitions,ou=configuration,ou=system should be marked", collectiveAttributeSubentries );
+        assertEquals( "2.5.4.3=newname,2.5.4.11=system", collectiveAttributeSubentries.get() );
+        assertEquals( 1, collectiveAttributeSubentries.size() );
+
+        Attributes services = results.get( "ou=services,ou=configuration,ou=system" );
+        collectiveAttributeSubentries = services.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT );
+        assertNotNull( "ou=services,ou=configuration,ou=system should be marked", collectiveAttributeSubentries );
+        assertEquals( "2.5.4.3=newname,2.5.4.11=system", collectiveAttributeSubentries.get() );
+        assertEquals( 1, collectiveAttributeSubentries.size() );
+
+        // --------------------------------------------------------------------
+        // Make sure entries not selected by subentry do not have the mark
+        // --------------------------------------------------------------------
+
+        Attributes system = results.get( "ou=system" );
+        assertNull( "ou=system should not be marked", system.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
+
+        Attributes users = results.get( "ou=users,ou=system" );
+        assertNull( "ou=users,ou=system should not be marked", users.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
+
+        Attributes groups = results.get( "ou=groups,ou=system" );
+        assertNull( "ou=groups,ou=system should not be marked", groups.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
+
+        Attributes admin = results.get( "uid=admin,ou=system" );
+        assertNull( "uid=admin,ou=system should not be marked", admin.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
+
+        Attributes sysPrefRoot = results.get( "prefNodeName=sysPrefRoot,ou=system" );
+        assertNull( "prefNode=sysPrefRoot,ou=system should not be marked", sysPrefRoot
+            .get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
+
+    }
+
+
+    @Test
+    public void testEntryModifyRdn() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        addAdministrativeRole( "collectiveArributeSpecificArea" );
+        sysRoot.createSubcontext( "cn=testsubentry", getTestSubentryWithExclusion() );
+        sysRoot.createSubcontext( "cn=unmarked,ou=configuration", getTestEntry( "unmarked" ) );
+        sysRoot.createSubcontext( "cn=marked,ou=configuration", getTestEntry( "marked" ) );
+        Map<String, Attributes> results = getAllEntries();
+
+        // --------------------------------------------------------------------
+        // Make sure entries selected by the subentry do have the mark
+        // --------------------------------------------------------------------
+
+        Attributes configuration = results.get( "ou=configuration,ou=system" );
+        Attribute collectiveAttributeSubentries = configuration.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT );
+        assertNotNull( "ou=configuration,ou=system should be marked", collectiveAttributeSubentries );
+        assertEquals( "2.5.4.3=testsubentry,2.5.4.11=system", collectiveAttributeSubentries.get() );
+        assertEquals( 1, collectiveAttributeSubentries.size() );
+
+        Attributes interceptors = results.get( "ou=interceptors,ou=configuration,ou=system" );
+        collectiveAttributeSubentries = interceptors.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT );
+        assertNotNull( "ou=interceptors,ou=configuration,ou=system should be marked", collectiveAttributeSubentries );
+        assertEquals( "2.5.4.3=testsubentry,2.5.4.11=system", collectiveAttributeSubentries.get() );
+        assertEquals( 1, collectiveAttributeSubentries.size() );
+
+        Attributes partitions = results.get( "ou=partitions,ou=configuration,ou=system" );
+        collectiveAttributeSubentries = partitions.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT );
+        assertNotNull( "ou=partitions,ou=configuration,ou=system should be marked", collectiveAttributeSubentries );
+        assertEquals( "2.5.4.3=testsubentry,2.5.4.11=system", collectiveAttributeSubentries.get() );
+        assertEquals( 1, collectiveAttributeSubentries.size() );
+
+        Attributes services = results.get( "ou=services,ou=configuration,ou=system" );
+        collectiveAttributeSubentries = services.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT );
+        assertNotNull( "ou=services,ou=configuration,ou=system should be marked", collectiveAttributeSubentries );
+        assertEquals( "2.5.4.3=testsubentry,2.5.4.11=system", collectiveAttributeSubentries.get() );
+        assertEquals( 1, collectiveAttributeSubentries.size() );
+
+        Attributes marked = results.get( "cn=marked,ou=configuration,ou=system" );
+        collectiveAttributeSubentries = marked.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT );
+        assertNotNull( "cn=marked,ou=configuration,ou=system should be marked", collectiveAttributeSubentries );
+        assertEquals( "2.5.4.3=testsubentry,2.5.4.11=system", collectiveAttributeSubentries.get() );
+        assertEquals( 1, collectiveAttributeSubentries.size() );
+
+        // --------------------------------------------------------------------
+        // Make sure entries not selected by subentry do not have the mark
+        // --------------------------------------------------------------------
+
+        Attributes system = results.get( "ou=system" );
+        assertNull( "ou=system should not be marked", system.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
+
+        Attributes users = results.get( "ou=users,ou=system" );
+        assertNull( "ou=users,ou=system should not be marked", users.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
+
+        Attributes groups = results.get( "ou=groups,ou=system" );
+        assertNull( "ou=groups,ou=system should not be marked", groups.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
+
+        Attributes admin = results.get( "uid=admin,ou=system" );
+        assertNull( "uid=admin,ou=system should not be marked", admin.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
+
+        Attributes sysPrefRoot = results.get( "prefNodeName=sysPrefRoot,ou=system" );
+        assertNull( "prefNode=sysPrefRoot,ou=system should not be marked", sysPrefRoot
+            .get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
+
+        Attributes unmarked = results.get( "cn=unmarked,ou=configuration,ou=system" );
+        assertNull( "cn=unmarked,ou=configuration,ou=system should not be marked", unmarked
+            .get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
+
+        // --------------------------------------------------------------------
+        // Now destry one of the marked/unmarked and rename to deleted entry
+        // --------------------------------------------------------------------
+
+        sysRoot.destroySubcontext( "cn=unmarked,ou=configuration" );
+        sysRoot.rename( "cn=marked,ou=configuration", "cn=unmarked,ou=configuration" );
+        results = getAllEntries();
+
+        unmarked = results.get( "cn=unmarked,ou=configuration,ou=system" );
+        assertNull( "cn=unmarked,ou=configuration,ou=system should not be marked", unmarked
+            .get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
+        assertNull( results.get( "cn=marked,ou=configuration,ou=system" ) );
+
+        // --------------------------------------------------------------------
+        // Now rename unmarked to marked and see that subentry op attr is there
+        // --------------------------------------------------------------------
+
+        sysRoot.rename( "cn=unmarked,ou=configuration", "cn=marked,ou=configuration" );
+        results = getAllEntries();
+        assertNull( results.get( "cn=unmarked,ou=configuration,ou=system" ) );
+        marked = results.get( "cn=marked,ou=configuration,ou=system" );
+        assertNotNull( marked );
+        collectiveAttributeSubentries = marked.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT );
+        assertNotNull( "cn=marked,ou=configuration,ou=system should be marked", collectiveAttributeSubentries );
+        assertEquals( "2.5.4.3=testsubentry,2.5.4.11=system", collectiveAttributeSubentries.get() );
+        assertEquals( 1, collectiveAttributeSubentries.size() );
+    }
+
+
+    @Test
+    public void testEntryMoveWithRdnChange() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        addAdministrativeRole( "collectiveArributeSpecificArea" );
+        sysRoot.createSubcontext( "cn=testsubentry", getTestSubentryWithExclusion() );
+        sysRoot.createSubcontext( "cn=unmarked", getTestEntry( "unmarked" ) );
+        sysRoot.createSubcontext( "cn=marked,ou=configuration", getTestEntry( "marked" ) );
+        Map<String, Attributes> results = getAllEntries();
+
+        // --------------------------------------------------------------------
+        // Make sure entries selected by the subentry do have the mark
+        // --------------------------------------------------------------------
+
+        Attributes configuration = results.get( "ou=configuration,ou=system" );
+        Attribute collectiveAttributeSubentries = configuration.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT );
+        assertNotNull( "ou=configuration,ou=system should be marked", collectiveAttributeSubentries );
+        assertEquals( "2.5.4.3=testsubentry,2.5.4.11=system", collectiveAttributeSubentries.get() );
+        assertEquals( 1, collectiveAttributeSubentries.size() );
+
+        Attributes interceptors = results.get( "ou=interceptors,ou=configuration,ou=system" );
+        collectiveAttributeSubentries = interceptors.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT );
+        assertNotNull( "ou=interceptors,ou=configuration,ou=system should be marked", collectiveAttributeSubentries );
+        assertEquals( "2.5.4.3=testsubentry,2.5.4.11=system", collectiveAttributeSubentries.get() );
+        assertEquals( 1, collectiveAttributeSubentries.size() );
+
+        Attributes partitions = results.get( "ou=partitions,ou=configuration,ou=system" );
+        collectiveAttributeSubentries = partitions.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT );
+        assertNotNull( "ou=partitions,ou=configuration,ou=system should be marked", collectiveAttributeSubentries );
+        assertEquals( "2.5.4.3=testsubentry,2.5.4.11=system", collectiveAttributeSubentries.get() );
+        assertEquals( 1, collectiveAttributeSubentries.size() );
+
+        Attributes services = results.get( "ou=services,ou=configuration,ou=system" );
+        collectiveAttributeSubentries = services.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT );
+        assertNotNull( "ou=services,ou=configuration,ou=system should be marked", collectiveAttributeSubentries );
+        assertEquals( "2.5.4.3=testsubentry,2.5.4.11=system", collectiveAttributeSubentries.get() );
+        assertEquals( 1, collectiveAttributeSubentries.size() );
+
+        Attributes marked = results.get( "cn=marked,ou=configuration,ou=system" );
+        collectiveAttributeSubentries = marked.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT );
+        assertNotNull( "cn=marked,ou=configuration,ou=system should be marked", collectiveAttributeSubentries );
+        assertEquals( "2.5.4.3=testsubentry,2.5.4.11=system", collectiveAttributeSubentries.get() );
+        assertEquals( 1, collectiveAttributeSubentries.size() );
+
+        // --------------------------------------------------------------------
+        // Make sure entries not selected by subentry do not have the mark
+        // --------------------------------------------------------------------
+
+        Attributes system = results.get( "ou=system" );
+        assertNull( "ou=system should not be marked", system.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
+
+        Attributes users = results.get( "ou=users,ou=system" );
+        assertNull( "ou=users,ou=system should not be marked", users.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
+
+        Attributes groups = results.get( "ou=groups,ou=system" );
+        assertNull( "ou=groups,ou=system should not be marked", groups.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
+
+        Attributes admin = results.get( "uid=admin,ou=system" );
+        assertNull( "uid=admin,ou=system should not be marked", admin.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
+
+        Attributes sysPrefRoot = results.get( "prefNodeName=sysPrefRoot,ou=system" );
+        assertNull( "prefNode=sysPrefRoot,ou=system should not be marked", sysPrefRoot
+            .get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
+
+        Attributes unmarked = results.get( "cn=unmarked,ou=system" );
+        assertNull( "cn=unmarked,ou=system should not be marked", unmarked
+            .get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
+
+        // --------------------------------------------------------------------
+        // Now destry one of the marked/unmarked and rename to deleted entry
+        // --------------------------------------------------------------------
+
+        sysRoot.destroySubcontext( "cn=unmarked" );
+        sysRoot.rename( "cn=marked,ou=configuration", "cn=unmarked" );
+        results = getAllEntries();
+
+        unmarked = results.get( "cn=unmarked,ou=system" );
+        assertNull( "cn=unmarked,ou=system should not be marked", unmarked
+            .get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
+        assertNull( results.get( "cn=marked,ou=configuration,ou=system" ) );
+
+        // --------------------------------------------------------------------
+        // Now rename unmarked to marked and see that subentry op attr is there
+        // --------------------------------------------------------------------
+
+        sysRoot.rename( "cn=unmarked", "cn=marked,ou=configuration" );
+        results = getAllEntries();
+        assertNull( results.get( "cn=unmarked,ou=system" ) );
+        marked = results.get( "cn=marked,ou=configuration,ou=system" );
+        assertNotNull( marked );
+        collectiveAttributeSubentries = marked.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT );
+        assertNotNull( "cn=marked,ou=configuration,ou=system should be marked", collectiveAttributeSubentries );
+        assertEquals( "2.5.4.3=testsubentry,2.5.4.11=system", collectiveAttributeSubentries.get() );
+        assertEquals( 1, collectiveAttributeSubentries.size() );
+    }
+
+
+    @Test
+    public void testEntryMove() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        addAdministrativeRole( "collectiveArributeSpecificArea" );
+        sysRoot.createSubcontext( "cn=testsubentry", getTestSubentryWithExclusion() );
+        sysRoot.createSubcontext( "cn=unmarked", getTestEntry( "unmarked" ) );
+        sysRoot.createSubcontext( "cn=marked,ou=configuration", getTestEntry( "marked" ) );
+        Map<String, Attributes> results = getAllEntries();
+
+        // --------------------------------------------------------------------
+        // Make sure entries selected by the subentry do have the mark
+        // --------------------------------------------------------------------
+
+        Attributes configuration = results.get( "ou=configuration,ou=system" );
+        Attribute collectiveAttributeSubentries = configuration.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT );
+        assertNotNull( "ou=configuration,ou=system should be marked", collectiveAttributeSubentries );
+        assertEquals( "2.5.4.3=testsubentry,2.5.4.11=system", collectiveAttributeSubentries.get() );
+        assertEquals( 1, collectiveAttributeSubentries.size() );
+
+        Attributes interceptors = results.get( "ou=interceptors,ou=configuration,ou=system" );
+        collectiveAttributeSubentries = interceptors.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT );
+        assertNotNull( "ou=interceptors,ou=configuration,ou=system should be marked", collectiveAttributeSubentries );
+        assertEquals( "2.5.4.3=testsubentry,2.5.4.11=system", collectiveAttributeSubentries.get() );
+        assertEquals( 1, collectiveAttributeSubentries.size() );
+
+        Attributes partitions = results.get( "ou=partitions,ou=configuration,ou=system" );
+        collectiveAttributeSubentries = partitions.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT );
+        assertNotNull( "ou=partitions,ou=configuration,ou=system should be marked", collectiveAttributeSubentries );
+        assertEquals( "2.5.4.3=testsubentry,2.5.4.11=system", collectiveAttributeSubentries.get() );
+        assertEquals( 1, collectiveAttributeSubentries.size() );
+
+        Attributes services = results.get( "ou=services,ou=configuration,ou=system" );
+        collectiveAttributeSubentries = services.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT );
+        assertNotNull( "ou=services,ou=configuration,ou=system should be marked", collectiveAttributeSubentries );
+        assertEquals( "2.5.4.3=testsubentry,2.5.4.11=system", collectiveAttributeSubentries.get() );
+        assertEquals( 1, collectiveAttributeSubentries.size() );
+
+        Attributes marked = results.get( "cn=marked,ou=configuration,ou=system" );
+        collectiveAttributeSubentries = marked.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT );
+        assertNotNull( "cn=marked,ou=configuration,ou=system should be marked", collectiveAttributeSubentries );
+        assertEquals( "2.5.4.3=testsubentry,2.5.4.11=system", collectiveAttributeSubentries.get() );
+        assertEquals( 1, collectiveAttributeSubentries.size() );
+
+        // --------------------------------------------------------------------
+        // Make sure entries not selected by subentry do not have the mark
+        // --------------------------------------------------------------------
+
+        Attributes system = results.get( "ou=system" );
+        assertNull( "ou=system should not be marked", system.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
+
+        Attributes users = results.get( "ou=users,ou=system" );
+        assertNull( "ou=users,ou=system should not be marked", users.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
+
+        Attributes groups = results.get( "ou=groups,ou=system" );
+        assertNull( "ou=groups,ou=system should not be marked", groups.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
+
+        Attributes admin = results.get( "uid=admin,ou=system" );
+        assertNull( "uid=admin,ou=system should not be marked", admin.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
+
+        Attributes sysPrefRoot = results.get( "prefNodeName=sysPrefRoot,ou=system" );
+        assertNull( "prefNode=sysPrefRoot,ou=system should not be marked", sysPrefRoot
+            .get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
+
+        Attributes unmarked = results.get( "cn=unmarked,ou=system" );
+        assertNull( "cn=unmarked,ou=system should not be marked", unmarked
+            .get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
+
+        // --------------------------------------------------------------------
+        // Now destry one of the marked/unmarked and rename to deleted entry
+        // --------------------------------------------------------------------
+
+        sysRoot.destroySubcontext( "cn=unmarked" );
+        sysRoot.rename( "cn=marked,ou=configuration", "cn=marked,ou=services,ou=configuration" );
+        results = getAllEntries();
+
+        unmarked = results.get( "cn=unmarked,ou=system" );
+        assertNull( "cn=unmarked,ou=system should not be marked", unmarked );
+        assertNull( results.get( "cn=marked,ou=configuration,ou=system" ) );
+
+        marked = results.get( "cn=marked,ou=services,ou=configuration,ou=system" );
+        assertNotNull( marked );
+        collectiveAttributeSubentries = marked.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT );
+        assertNotNull( "cn=marked,ou=services,ou=configuration should be marked", collectiveAttributeSubentries );
+        assertEquals( "2.5.4.3=testsubentry,2.5.4.11=system", collectiveAttributeSubentries.get() );
+        assertEquals( 1, collectiveAttributeSubentries.size() );
+    }
+
+
+    @Test
+    public void testSubentriesControl() throws Exception
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        addAdministrativeRole( "collectiveArributeSpecificArea" );
+        sysRoot.createSubcontext( "cn=testsubentry", getTestSubentryWithExclusion() );
+        SearchControls searchControls = new SearchControls();
+        searchControls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+
+        // perform the search without the control
+        Map<String, SearchResult> entries = new HashMap<String, SearchResult>();
+        NamingEnumeration<SearchResult> list = sysRoot.search( "", "(objectClass=*)", searchControls );
+        
+        while ( list.hasMore() )
+        {
+            SearchResult result = ( SearchResult ) list.next();
+            entries.put( result.getName(), result );
+        }
+        
+        assertTrue( entries.size() > 1 );
+        assertNull( entries.get( "cn=testsubentry,ou=system" ) );
+
+        // now add the control with visibility set to true where all entries 
+        // except subentries disappear
+        SubentriesControl ctl = new SubentriesControl();
+        ctl.setVisibility( true );
+        sysRoot.setRequestControls( new Control[] { ctl } );
+        list = sysRoot.search( "", "(objectClass=*)", searchControls );
+        SearchResult result = ( SearchResult ) list.next();
+        assertFalse( list.hasMore() );
+        assertEquals( "cn=testsubentry,ou=system", result.getName() );
+    }
+    
+
+    @Test
+    public void testBaseScopeSearchSubentryVisibilityWithoutTheControl() throws Exception
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        addAdministrativeRole( "collectiveArributeSpecificArea" );
+        sysRoot.createSubcontext( "cn=testsubentry", getTestSubentryWithExclusion() );
+        SearchControls searchControls = new SearchControls();
+        searchControls.setSearchScope( SearchControls.OBJECT_SCOPE );
+
+        Map<String, SearchResult> entries = new HashMap<String, SearchResult>();
+        NamingEnumeration<SearchResult> list = sysRoot.search( "cn=testsubentry", "(objectClass=subentry)", searchControls );
+        
+        while ( list.hasMore() )
+        {
+            SearchResult result = list.next();
+            entries.put( result.getName(), result );
+        }
+        
+        assertEquals( 1, entries.size() );
+        assertNotNull( entries.get( "cn=testsubentry,ou=system" ) );
+    }
+}
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/subtree/SubentryServiceObjectClassChangeHandlingIT.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/subtree/SubentryServiceObjectClassChangeHandlingIT.java
new file mode 100644
index 0000000..c9fc06e
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/subtree/SubentryServiceObjectClassChangeHandlingIT.java
@@ -0,0 +1,160 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.core.subtree;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.integ.CiRunner;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getSystemContext;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.message.ModificationItemImpl;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.LdapContext;
+import java.util.HashMap;
+import java.util.Map;
+
+
+/**
+ * Testcases for the SubentryInterceptor. Investigation on handling Subtree Refinement
+ * Selection Membership upon objectClass attribute value changes.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+@RunWith ( CiRunner.class )
+public class SubentryServiceObjectClassChangeHandlingIT 
+{
+    public static DirectoryService service;
+
+
+    public Attributes getTestEntry( String cn )
+    {
+        Attributes entry = new AttributesImpl();
+        Attribute objectClass = new AttributeImpl( "objectClass" );
+        objectClass.add( "top" );
+        objectClass.add( "person" );
+        entry.put( objectClass );
+        entry.put( "cn", cn );
+        entry.put( "sn", cn );
+        return entry;
+    }
+
+
+    public Attributes getModsForIntroducingNewOC() throws NamingException
+    {
+        Attributes changes = new AttributesImpl();
+        Attribute objectClass = new AttributeImpl( "objectClass" );
+        objectClass.add( "organizationalPerson" );
+        changes.put( objectClass );
+        changes.put( "ou", "Test Organizational Unit" );
+        return changes;
+    }
+
+
+    public Attributes getCollectiveAttributeTestSubentry( String cn )
+    {
+        Attributes subentry = new AttributesImpl();
+        Attribute objectClass = new AttributeImpl( "objectClass" );
+        objectClass.add( "top" );
+        objectClass.add( SchemaConstants.SUBENTRY_OC );
+        objectClass.add( "collectiveAttributeSubentry" );
+        subentry.put( objectClass );
+        subentry.put( "subtreeSpecification", "{ specificationFilter item:organizationalPerson }" );
+        subentry.put( "c-o", "Test Org" );
+        subentry.put( "cn", cn );
+        return subentry;
+    }
+
+
+    public void addAdministrativeRoles() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        Attribute attribute = new AttributeImpl( "administrativeRole" );
+        attribute.add( "autonomousArea" );
+        attribute.add( "collectiveAttributeSpecificArea" );
+        ModificationItemImpl item = new ModificationItemImpl( DirContext.ADD_ATTRIBUTE, attribute );
+        sysRoot.modifyAttributes( "", new ModificationItemImpl[]
+            { item } );
+    }
+
+
+    public Map<String, Attributes> getAllEntries() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        Map<String, Attributes> resultMap = new HashMap<String, Attributes>();
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        controls.setReturningAttributes( new String[]
+            { "+", "*" } );
+        NamingEnumeration<SearchResult> results = sysRoot.search( "", "(objectClass=*)", controls );
+        
+        while ( results.hasMore() )
+        {
+            SearchResult result = results.next();
+            resultMap.put( result.getName(), result.getAttributes() );
+        }
+        return resultMap;
+    }
+    
+
+    @Test
+    public void testTrackingOfOCChangesInSubentryServiceModifyRoutine() throws Exception
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        addAdministrativeRoles();
+        sysRoot.createSubcontext( "cn=collectiveAttributeTestSubentry",
+            getCollectiveAttributeTestSubentry( "collectiveAttributeTestSubentry" ) );
+        sysRoot.createSubcontext( "cn=testEntry", getTestEntry( "testEntry" ) );
+
+        //----------------------------------------------------------------------
+
+        Map<String, Attributes> results = getAllEntries();
+        Attributes testEntry = results.get( "cn=testEntry,ou=system" );
+
+        Attribute collectiveAttributeSubentries = testEntry.get( "collectiveAttributeSubentries" );
+        
+        assertNull( collectiveAttributeSubentries );
+
+        //----------------------------------------------------------------------
+
+        sysRoot.modifyAttributes( "cn=testEntry", DirContext.ADD_ATTRIBUTE, getModsForIntroducingNewOC() );
+
+        results = getAllEntries();
+        testEntry = ( Attributes ) results.get( "cn=testEntry,ou=system" );
+
+        collectiveAttributeSubentries = testEntry.get( "collectiveAttributeSubentries" );
+        assertNotNull( collectiveAttributeSubentries );
+    }
+
+}
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/trigger/BackupUtilities.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/trigger/BackupUtilities.java
new file mode 100644
index 0000000..2c7014a
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/trigger/BackupUtilities.java
@@ -0,0 +1,59 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.
+ *
+ */
+package org.apache.directory.server.core.trigger;
+
+import javax.naming.Name;
+import javax.naming.NamingException;
+import javax.naming.directory.Attributes;
+import javax.naming.ldap.LdapContext;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev:$
+ */
+public class BackupUtilities
+{
+    private static final Logger LOG = LoggerFactory.getLogger( BackupUtilities.class );
+
+
+    public static void backupDeleted( LdapContext ctx, Name deletedEntryName,
+                                      Name operationPrincipal, Attributes deletedEntry ) throws NamingException
+    {
+        LOG.info( "User \"" + operationPrincipal + "\" has deleted entry \"" + deletedEntryName + "\"" );
+        LOG.info( "Entry content was: " + deletedEntry );
+        LdapContext backupCtx = ( LdapContext ) ctx.lookup( "ou=backupContext,ou=system" );
+        String deletedEntryRdn = deletedEntryName.get( deletedEntryName.size() - 1 );
+        backupCtx.createSubcontext( deletedEntryRdn, deletedEntry );
+        LOG.info( "Backed up deleted entry to \"" +
+                ( ( LdapContext ) backupCtx.lookup( deletedEntryRdn ) ).getNameInNamespace() + "\"" );
+    }
+    
+    public static void duplicateDeletedEntry( LdapContext ctx, Name deletedEntryName, Name operationPrincipal,
+                                              Attributes deletedEntry ) throws NamingException
+    {
+        LdapContext backupCtx = ( LdapContext ) ctx.lookup( "ou=backupContext,ou=system" );
+        String deletedEntryRdn = deletedEntryName.get( deletedEntryName.size() - 1 );
+        backupCtx.createSubcontext( deletedEntryRdn + "," + deletedEntryRdn, deletedEntry );
+    }
+}
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/trigger/BackupUtilitiesSP.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/trigger/BackupUtilitiesSP.java
new file mode 100644
index 0000000..a5f2b81
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/trigger/BackupUtilitiesSP.java
@@ -0,0 +1,48 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.trigger;
+
+import javax.naming.NamingException;
+import javax.naming.ldap.LdapContext;
+
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerEntryUtils;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+public class BackupUtilitiesSP
+{
+    private static final Logger LOG = LoggerFactory.getLogger( BackupUtilitiesSP.class );
+
+
+    public static void backupDeleted( LdapContext ctx, LdapDN deletedEntryName,
+                                      LdapDN operationPrincipal, ServerEntry deletedEntry ) throws NamingException
+    {
+        LOG.info( "User \"" + operationPrincipal + "\" has deleted entry \"" + deletedEntryName + "\"" );
+        LOG.info( "Entry content was: " + deletedEntry );
+        LdapContext backupCtx = ( LdapContext ) ctx.lookup( "ou=backupContext,ou=system" );
+        String deletedEntryRdn = deletedEntryName.get( deletedEntryName.size() - 1 );
+        backupCtx.createSubcontext( deletedEntryRdn, ServerEntryUtils.toAttributesImpl( deletedEntry ) );
+        LOG.info( "Backed up deleted entry to \"" +
+                ( ( LdapContext ) backupCtx.lookup( deletedEntryRdn ) ).getNameInNamespace() + "\"" );
+    }
+}
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/trigger/ListUtilsSP.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/trigger/ListUtilsSP.java
new file mode 100644
index 0000000..f8f2baa
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/trigger/ListUtilsSP.java
@@ -0,0 +1,45 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.trigger;
+
+import javax.naming.NamingException;
+import javax.naming.directory.BasicAttributes;
+import javax.naming.directory.DirContext;
+import javax.naming.ldap.LdapContext;
+
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+public class ListUtilsSP
+{
+    private static final Logger LOG = LoggerFactory.getLogger( ListUtilsSP.class );
+
+
+    public static void subscribeToGroup( LdapDN addedEntryName, LdapContext groupCtx ) throws NamingException
+    {
+        LOG.info( "User \"" + addedEntryName + "\" will be subscribed to \"" + groupCtx + "\"" );
+        groupCtx.modifyAttributes("", DirContext.ADD_ATTRIBUTE,
+                new BasicAttributes( SchemaConstants.UNIQUE_MEMBER_AT, addedEntryName.toString(), true ) );
+        LOG.info( "Subscription OK." );
+    }
+}
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/trigger/LoggingUtilities.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/trigger/LoggingUtilities.java
new file mode 100644
index 0000000..25a0845
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/trigger/LoggingUtilities.java
@@ -0,0 +1,43 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.
+ *
+ */
+package org.apache.directory.server.core.trigger;
+
+
+import javax.naming.Name;
+import javax.naming.NamingException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev:$
+ */
+public class LoggingUtilities
+{
+    private static final Logger LOG = LoggerFactory.getLogger( LoggingUtilities.class );
+    
+    public static void logWarningForDeletedEntry( Name deletedEntryName,
+                                                  Name operationPrincipal ) throws NamingException
+    {
+        LOG.info( "User \"" + operationPrincipal + "\" is about to delete entry \"" + deletedEntryName + "\"." );
+    }
+}
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/trigger/SubentryServiceForTriggersIT.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/trigger/SubentryServiceForTriggersIT.java
new file mode 100644
index 0000000..24d2593
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/trigger/SubentryServiceForTriggersIT.java
@@ -0,0 +1,580 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.core.trigger;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.integ.CiRunner;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getSystemContext;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.exception.LdapNoSuchAttributeException;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.message.ModificationItemImpl;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.LdapContext;
+import java.util.HashMap;
+import java.util.Map;
+
+
+/**
+ * Testcases for the SubentryInterceptor for Triggers.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev:$
+ */
+@RunWith ( CiRunner.class )
+@Ignore ( "Reverts are failing to delete marked entries. Fixing this " +
+        "problem in testEntryAdd() will fix it all over." )
+public class SubentryServiceForTriggersIT
+{
+    public static DirectoryService service;
+
+
+    public Attributes getTestEntry( String cn )
+    {
+        Attributes subentry = new AttributesImpl();
+        Attribute objectClass = new AttributeImpl( "objectClass" );
+        objectClass.add( "top" );
+        objectClass.add( "person" );
+        subentry.put( objectClass );
+        subentry.put( "cn", cn );
+        subentry.put( "sn", "testentry" );
+        return subentry;
+    }
+
+
+    public Attributes getTestSubentry()
+    {
+        Attributes subentry = new AttributesImpl();
+        Attribute objectClass = new AttributeImpl( "objectClass" );
+        objectClass.add( "top" );
+        objectClass.add( SchemaConstants.SUBENTRY_OC );
+        objectClass.add( "triggerExecutionSubentry" );
+        subentry.put( objectClass );
+        subentry.put( "subtreeSpecification", "{ base \"ou=configuration\" }" );
+        subentry.put( "prescriptiveTriggerSpecification", "AFTER Delete CALL \"LogUtils.logDelete\"($name);" );
+        subentry.put( "cn", "testsubentry" );
+        return subentry;
+    }
+    
+    public Attributes getTestSubentryWithExclusion()
+    {
+        Attributes subentry = new AttributesImpl();
+        Attribute objectClass = new AttributeImpl( "objectClass" );
+        objectClass.add( "top" );
+        objectClass.add( SchemaConstants.SUBENTRY_OC );
+        objectClass.add( "triggerExecutionSubentry" );
+        subentry.put( objectClass );
+        String spec = "{ base \"ou=configuration\", specificExclusions { chopBefore:\"cn=unmarked\" } }";
+        subentry.put( "subtreeSpecification", spec );
+        subentry.put( "prescriptiveTriggerSpecification", "AFTER Delete CALL \"LogUtils.logDelete\"($name);" );
+        subentry.put( "cn", "testsubentry" );
+        return subentry;
+    }
+
+
+    public void addTheAdministrativeRole() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        Attribute attribute = new AttributeImpl( "administrativeRole" );
+        attribute.add( "autonomousArea" );
+        attribute.add( "triggerSpecificArea" );
+        ModificationItemImpl item = new ModificationItemImpl( DirContext.ADD_ATTRIBUTE, attribute );
+        sysRoot.modifyAttributes( "", new ModificationItemImpl[] { item } );
+    }
+
+
+    public Map<String, Attributes> getAllEntries() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        Map<String, Attributes> resultMap = new HashMap<String, Attributes>();
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        controls.setReturningAttributes( new String[]
+            { "+", "*" } );
+        NamingEnumeration<SearchResult> results = sysRoot.search( "", "(objectClass=*)", controls );
+        
+        while ( results.hasMore() )
+        {
+            SearchResult result = results.next();
+            resultMap.put( result.getName(), result.getAttributes() );
+        }
+        return resultMap;
+    }
+
+
+    @Test
+    public void testEntryAdd() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        addTheAdministrativeRole();        
+        sysRoot.createSubcontext( "cn=testsubentry", getTestSubentry() );
+        sysRoot.createSubcontext( "cn=unmarked", getTestEntry( "unmarked" ) );
+        sysRoot.createSubcontext( "cn=marked,ou=configuration", getTestEntry( "marked" ) );
+        Map<String, Attributes> results = getAllEntries();
+
+        // --------------------------------------------------------------------
+        // Make sure entries selected by the subentry do have the mark
+        // --------------------------------------------------------------------
+
+        Attributes marked = results.get( "cn=marked,ou=configuration,ou=system" );
+        Attribute triggerSubentries = marked.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT );
+        assertNotNull( "cn=marked,ou=configuration,ou=system should be marked", triggerSubentries );
+        assertEquals( "2.5.4.3=testsubentry,2.5.4.11=system", triggerSubentries.get() );
+        assertEquals( 1, triggerSubentries.size() );
+
+        // --------------------------------------------------------------------
+        // Make sure entries not selected by subentry do not have the mark
+        // --------------------------------------------------------------------
+
+        Attributes unmarked = results.get( "cn=unmarked,ou=system" );
+        assertNull( "cn=unmarked,ou=system should not be marked", unmarked
+            .get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ) );
+
+        // @todo attempts to delete this entry cause an StringIndexOutOfBoundsException
+        sysRoot.destroySubcontext( "cn=marked,ou=configuration" );
+    }
+
+
+    @Test
+    public void testSubentryAdd() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+
+        //noinspection EmptyCatchBlock
+        try
+        {
+            sysRoot.createSubcontext( "cn=testsubentry", getTestSubentry() );
+            fail( "should never get here: cannot create subentry under regular entries" );
+        }
+        catch ( LdapNoSuchAttributeException e )
+        {
+        }
+
+        addTheAdministrativeRole();
+        sysRoot.createSubcontext( "cn=testsubentry", getTestSubentry() );
+        Map<String, Attributes> results = getAllEntries();
+
+        // --------------------------------------------------------------------
+        // Make sure entries selected by the subentry do have the mark
+        // --------------------------------------------------------------------
+
+        Attributes configuration = results.get( "ou=configuration,ou=system" );
+        Attribute triggerSubentries = configuration.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT );
+        assertNotNull( "ou=configuration,ou=system should be marked", triggerSubentries );
+        assertEquals( "2.5.4.3=testsubentry,2.5.4.11=system", triggerSubentries.get() );
+        assertEquals( 1, triggerSubentries.size() );
+
+        Attributes interceptors = results.get( "ou=interceptors,ou=configuration,ou=system" );
+        triggerSubentries = interceptors.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT );
+        assertNotNull( "ou=interceptors,ou=configuration,ou=system should be marked", triggerSubentries );
+        assertEquals( "2.5.4.3=testsubentry,2.5.4.11=system", triggerSubentries.get() );
+        assertEquals( 1, triggerSubentries.size() );
+
+        // --------------------------------------------------------------------
+        // Make sure entries not selected by subentry do not have the mark
+        // --------------------------------------------------------------------
+
+        Attributes system = results.get( "ou=system" );
+        assertNull( "ou=system should not be marked", system.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ) );
+
+        Attributes users = results.get( "ou=users,ou=system" );
+        assertNull( "ou=users,ou=system should not be marked", users.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ) );
+    }
+
+
+    @Test
+    public void testSubentryModify() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        addTheAdministrativeRole();
+        sysRoot.createSubcontext( "cn=testsubentry", getTestSubentry() );
+        Map<String, Attributes> results = getAllEntries();
+
+        // --------------------------------------------------------------------
+        // Make sure entries selected by the subentry do have the mark
+        // --------------------------------------------------------------------
+
+        Attributes configuration = results.get( "ou=configuration,ou=system" );
+        Attribute triggerSubentries = configuration.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT );
+        assertNotNull( "ou=configuration,ou=system should be marked", triggerSubentries );
+        assertEquals( "2.5.4.3=testsubentry,2.5.4.11=system", triggerSubentries.get() );
+        assertEquals( 1, triggerSubentries.size() );
+
+        Attributes interceptors = results.get( "ou=interceptors,ou=configuration,ou=system" );
+        triggerSubentries = interceptors.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT );
+        assertNotNull( "ou=interceptors,ou=configuration,ou=system should be marked", triggerSubentries );
+        assertEquals( "2.5.4.3=testsubentry,2.5.4.11=system", triggerSubentries.get() );
+        assertEquals( 1, triggerSubentries.size() );
+
+        // --------------------------------------------------------------------
+        // Make sure entries not selected by subentry do not have the mark
+        // --------------------------------------------------------------------
+
+        Attributes system = results.get( "ou=system" );
+        assertNull( "ou=system should not be marked", system.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ) );
+
+        Attributes users = results.get( "ou=users,ou=system" );
+        assertNull( "ou=users,ou=system should not be marked", users.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ) );
+
+        // --------------------------------------------------------------------
+        // Now modify the subentry by introducing an exclusion
+        // --------------------------------------------------------------------
+
+        Attribute subtreeSpecification = new AttributeImpl( "subtreeSpecification" );
+        subtreeSpecification.add( "{ base \"ou=configuration\", specificExclusions { chopBefore:\"ou=interceptors\" } }" );
+        ModificationItemImpl item = new ModificationItemImpl( DirContext.REPLACE_ATTRIBUTE, subtreeSpecification );
+        sysRoot.modifyAttributes( "cn=testsubentry", new ModificationItemImpl[] { item } );
+        results = getAllEntries();
+
+        // --------------------------------------------------------------------
+        // Make sure entries selected by the subentry do have the mark
+        // --------------------------------------------------------------------
+
+        configuration = results.get( "ou=configuration,ou=system" );
+        triggerSubentries = configuration.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT );
+        assertNotNull( "ou=configuration,ou=system should be marked", triggerSubentries );
+        assertEquals( "2.5.4.3=testsubentry,2.5.4.11=system", triggerSubentries.get() );
+        assertEquals( 1, triggerSubentries.size() );
+
+        // --------------------------------------------------------------------
+        // Make sure entries not selected by subentry do not have the mark
+        // --------------------------------------------------------------------
+
+        system = results.get( "ou=system" );
+        assertNull( "ou=system should not be marked", system.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ) );
+
+        users = results.get( "ou=users,ou=system" );
+        assertNull( "ou=users,ou=system should not be marked", users.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ) );
+
+        interceptors = results.get( "ou=interceptors,ou=configuration,ou=system" );
+        triggerSubentries = interceptors.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT );
+        if ( triggerSubentries != null )
+        {
+            assertEquals( "ou=interceptors,ou=configuration,ou=system should not be marked", 0, triggerSubentries.size() );
+        }
+    }
+
+
+    @Test
+    public void testSubentryDelete() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        addTheAdministrativeRole();
+        sysRoot.createSubcontext( "cn=testsubentry", getTestSubentry() );
+        sysRoot.destroySubcontext( "cn=testsubentry" );
+        Map<String, Attributes> results = getAllEntries();
+
+        // --------------------------------------------------------------------
+        // Make sure entries not selected by subentry do not have the mark
+        // --------------------------------------------------------------------
+
+        Attributes configuration = results.get( "ou=configuration,ou=system" );
+        Attribute triggerSubentries = configuration.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT );
+        if ( triggerSubentries != null )
+        {
+            assertEquals( "ou=configuration,ou=system should not be marked", 0, triggerSubentries.size() );
+        }
+
+        Attributes interceptors = results.get( "ou=interceptors,ou=configuration,ou=system" );
+        triggerSubentries = interceptors.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT );
+        if ( triggerSubentries != null )
+        {
+            assertEquals( "ou=interceptors,ou=configuration,ou=system should not be marked", 0, triggerSubentries.size() );
+        }
+
+        Attributes system = results.get( "ou=system" );
+        assertNull( "ou=system should not be marked", system.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ) );
+
+        Attributes users = results.get( "ou=users,ou=system" );
+        assertNull( "ou=users,ou=system should not be marked", users.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ) );
+    }
+
+
+    @Test
+    public void testSubentryModifyRdn() throws NamingException
+    {
+        addTheAdministrativeRole();
+        LdapContext sysRoot = getSystemContext( service );
+        sysRoot.createSubcontext( "cn=testsubentry", getTestSubentry() );
+        sysRoot.rename( "cn=testsubentry", "cn=newname" );
+        Map<String, Attributes> results = getAllEntries();
+
+        // --------------------------------------------------------------------
+        // Make sure entries selected by the subentry do have the mark
+        // --------------------------------------------------------------------
+
+        Attributes configuration = results.get( "ou=configuration,ou=system" );
+        Attribute triggerSubentries = configuration.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT );
+        assertNotNull( "ou=configuration,ou=system should be marked", triggerSubentries );
+        assertEquals( "2.5.4.3=newname,2.5.4.11=system", triggerSubentries.get() );
+        assertEquals( 1, triggerSubentries.size() );
+
+        Attributes interceptors = results.get( "ou=interceptors,ou=configuration,ou=system" );
+        triggerSubentries = interceptors.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT );
+        assertNotNull( "ou=interceptors,ou=configuration,ou=system should be marked", triggerSubentries );
+        assertEquals( "2.5.4.3=newname,2.5.4.11=system", triggerSubentries.get() );
+        assertEquals( 1, triggerSubentries.size() );
+
+        // --------------------------------------------------------------------
+        // Make sure entries not selected by subentry do not have the mark
+        // --------------------------------------------------------------------
+
+        Attributes system = results.get( "ou=system" );
+        assertNull( "ou=system should not be marked", system.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ) );
+
+        Attributes users = results.get( "ou=users,ou=system" );
+        assertNull( "ou=users,ou=system should not be marked", users.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ) );
+    }
+
+
+    @Test
+    public void testEntryModifyRdn() throws NamingException
+    {
+        addTheAdministrativeRole();
+        LdapContext sysRoot = getSystemContext( service );
+        sysRoot.createSubcontext( "cn=testsubentry", getTestSubentryWithExclusion() );
+        sysRoot.createSubcontext( "cn=unmarked,ou=configuration", getTestEntry( "unmarked" ) );
+        sysRoot.createSubcontext( "cn=marked,ou=configuration", getTestEntry( "marked" ) );
+        Map<String, Attributes> results = getAllEntries();
+
+        // --------------------------------------------------------------------
+        // Make sure entries selected by the subentry do have the mark
+        // --------------------------------------------------------------------
+
+        Attributes configuration = results.get( "ou=configuration,ou=system" );
+        Attribute triggerSubentries = configuration.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT );
+        assertNotNull( "ou=configuration,ou=system should be marked", triggerSubentries );
+        assertEquals( "2.5.4.3=testsubentry,2.5.4.11=system", triggerSubentries.get() );
+        assertEquals( 1, triggerSubentries.size() );
+
+        Attributes interceptors = results.get( "ou=interceptors,ou=configuration,ou=system" );
+        triggerSubentries = interceptors.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT );
+        assertNotNull( "ou=interceptors,ou=configuration,ou=system should be marked", triggerSubentries );
+        assertEquals( "2.5.4.3=testsubentry,2.5.4.11=system", triggerSubentries.get() );
+        assertEquals( 1, triggerSubentries.size() );
+
+        Attributes marked = results.get( "cn=marked,ou=configuration,ou=system" );
+        triggerSubentries = marked.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT );
+        assertNotNull( "cn=marked,ou=configuration,ou=system should be marked", triggerSubentries );
+        assertEquals( "2.5.4.3=testsubentry,2.5.4.11=system", triggerSubentries.get() );
+        assertEquals( 1, triggerSubentries.size() );
+
+        // --------------------------------------------------------------------
+        // Make sure entries not selected by subentry do not have the mark
+        // --------------------------------------------------------------------
+
+        Attributes system = results.get( "ou=system" );
+        assertNull( "ou=system should not be marked", system.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ) );
+
+        Attributes users = results.get( "ou=users,ou=system" );
+        assertNull( "ou=users,ou=system should not be marked", users.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ) );
+
+        Attributes unmarked = results.get( "cn=unmarked,ou=configuration,ou=system" );
+        assertNull( "cn=unmarked,ou=configuration,ou=system should not be marked", unmarked
+            .get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ) );
+
+        // --------------------------------------------------------------------
+        // Now destry one of the marked/unmarked and rename to deleted entry
+        // --------------------------------------------------------------------
+
+        sysRoot.destroySubcontext( "cn=unmarked,ou=configuration" );
+        sysRoot.rename( "cn=marked,ou=configuration", "cn=unmarked,ou=configuration" );
+        results = getAllEntries();
+
+        unmarked = results.get( "cn=unmarked,ou=configuration,ou=system" );
+        assertNull( "cn=unmarked,ou=configuration,ou=system should not be marked", unmarked
+            .get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ) );
+        assertNull( results.get( "cn=marked,ou=configuration,ou=system" ) );
+
+        // --------------------------------------------------------------------
+        // Now rename unmarked to marked and see that subentry op attr is there
+        // --------------------------------------------------------------------
+
+        sysRoot.rename( "cn=unmarked,ou=configuration", "cn=marked,ou=configuration" );
+        results = getAllEntries();
+        assertNull( results.get( "cn=unmarked,ou=configuration,ou=system" ) );
+        marked = results.get( "cn=marked,ou=configuration,ou=system" );
+        assertNotNull( marked );
+        triggerSubentries = marked.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT );
+        assertNotNull( "cn=marked,ou=configuration,ou=system should be marked", triggerSubentries );
+        assertEquals( "2.5.4.3=testsubentry,2.5.4.11=system", triggerSubentries.get() );
+        assertEquals( 1, triggerSubentries.size() );
+    }
+
+
+    @Test
+    public void testEntryMoveWithRdnChange() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        addTheAdministrativeRole();
+        sysRoot.createSubcontext( "cn=testsubentry", getTestSubentryWithExclusion() );
+        sysRoot.createSubcontext( "cn=unmarked", getTestEntry( "unmarked" ) );
+        sysRoot.createSubcontext( "cn=marked,ou=configuration", getTestEntry( "marked" ) );
+        Map<String, Attributes> results = getAllEntries();
+
+        // --------------------------------------------------------------------
+        // Make sure entries selected by the subentry do have the mark
+        // --------------------------------------------------------------------
+
+        Attributes configuration = results.get( "ou=configuration,ou=system" );
+        Attribute triggerSubentries = configuration.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT );
+        assertNotNull( "ou=configuration,ou=system should be marked", triggerSubentries );
+        assertEquals( "2.5.4.3=testsubentry,2.5.4.11=system", triggerSubentries.get() );
+        assertEquals( 1, triggerSubentries.size() );
+
+        Attributes interceptors = results.get( "ou=interceptors,ou=configuration,ou=system" );
+        triggerSubentries = interceptors.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT );
+        assertNotNull( "ou=interceptors,ou=configuration,ou=system should be marked", triggerSubentries );
+        assertEquals( "2.5.4.3=testsubentry,2.5.4.11=system", triggerSubentries.get() );
+        assertEquals( 1, triggerSubentries.size() );
+
+        Attributes marked = results.get( "cn=marked,ou=configuration,ou=system" );
+        triggerSubentries = marked.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT );
+        assertNotNull( "cn=marked,ou=configuration,ou=system should be marked", triggerSubentries );
+        assertEquals( "2.5.4.3=testsubentry,2.5.4.11=system", triggerSubentries.get() );
+        assertEquals( 1, triggerSubentries.size() );
+
+        // --------------------------------------------------------------------
+        // Make sure entries not selected by subentry do not have the mark
+        // --------------------------------------------------------------------
+
+        Attributes system = results.get( "ou=system" );
+        assertNull( "ou=system should not be marked", system.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ) );
+
+        Attributes users = results.get( "ou=users,ou=system" );
+        assertNull( "ou=users,ou=system should not be marked", users.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ) );
+
+        Attributes unmarked = results.get( "cn=unmarked,ou=system" );
+        assertNull( "cn=unmarked,ou=system should not be marked", unmarked
+            .get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ) );
+
+        // --------------------------------------------------------------------
+        // Now destry one of the marked/unmarked and rename to deleted entry
+        // --------------------------------------------------------------------
+
+        sysRoot.destroySubcontext( "cn=unmarked" );
+        sysRoot.rename( "cn=marked,ou=configuration", "cn=unmarked" );
+        results = getAllEntries();
+
+        unmarked = results.get( "cn=unmarked,ou=system" );
+        assertNull( "cn=unmarked,ou=system should not be marked", unmarked
+            .get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ) );
+        assertNull( results.get( "cn=marked,ou=configuration,ou=system" ) );
+
+        // --------------------------------------------------------------------
+        // Now rename unmarked to marked and see that subentry op attr is there
+        // --------------------------------------------------------------------
+
+        sysRoot.rename( "cn=unmarked", "cn=marked,ou=configuration" );
+        results = getAllEntries();
+        assertNull( results.get( "cn=unmarked,ou=system" ) );
+        marked = results.get( "cn=marked,ou=configuration,ou=system" );
+        assertNotNull( marked );
+        triggerSubentries = marked.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT );
+        assertNotNull( "cn=marked,ou=configuration,ou=system should be marked", triggerSubentries );
+        assertEquals( "2.5.4.3=testsubentry,2.5.4.11=system", triggerSubentries.get() );
+        assertEquals( 1, triggerSubentries.size() );
+    }
+
+
+    @Test
+    public void testEntryMove() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        addTheAdministrativeRole();
+        sysRoot.createSubcontext( "cn=testsubentry", getTestSubentryWithExclusion() );
+        sysRoot.createSubcontext( "cn=unmarked", getTestEntry( "unmarked" ) );
+        sysRoot.createSubcontext( "cn=marked,ou=configuration", getTestEntry( "marked" ) );
+        Map<String, Attributes> results = getAllEntries();
+
+        // --------------------------------------------------------------------
+        // Make sure entries selected by the subentry do have the mark
+        // --------------------------------------------------------------------
+
+        Attributes configuration = results.get( "ou=configuration,ou=system" );
+        Attribute triggerSubentries = configuration.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT );
+        assertNotNull( "ou=configuration,ou=system should be marked", triggerSubentries );
+        assertEquals( "2.5.4.3=testsubentry,2.5.4.11=system", triggerSubentries.get() );
+        assertEquals( 1, triggerSubentries.size() );
+
+        Attributes interceptors = results.get( "ou=interceptors,ou=configuration,ou=system" );
+        triggerSubentries = interceptors.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT );
+        assertNotNull( "ou=interceptors,ou=configuration,ou=system should be marked", triggerSubentries );
+        assertEquals( "2.5.4.3=testsubentry,2.5.4.11=system", triggerSubentries.get() );
+        assertEquals( 1, triggerSubentries.size() );
+
+        Attributes marked = results.get( "cn=marked,ou=configuration,ou=system" );
+        triggerSubentries = marked.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT );
+        assertNotNull( "cn=marked,ou=configuration,ou=system should be marked", triggerSubentries );
+        assertEquals( "2.5.4.3=testsubentry,2.5.4.11=system", triggerSubentries.get() );
+        assertEquals( 1, triggerSubentries.size() );
+
+        // --------------------------------------------------------------------
+        // Make sure entries not selected by subentry do not have the mark
+        // --------------------------------------------------------------------
+
+        Attributes system = results.get( "ou=system" );
+        assertNull( "ou=system should not be marked", system.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ) );
+
+        Attributes users = results.get( "ou=users,ou=system" );
+        assertNull( "ou=users,ou=system should not be marked", users.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ) );
+
+        Attributes unmarked = results.get( "cn=unmarked,ou=system" );
+        assertNull( "cn=unmarked,ou=system should not be marked", unmarked
+            .get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ) );
+
+        // --------------------------------------------------------------------
+        // Now destry one of the marked/unmarked and rename to deleted entry
+        // --------------------------------------------------------------------
+
+        sysRoot.destroySubcontext( "cn=unmarked" );
+        sysRoot.rename( "cn=marked,ou=configuration", "cn=marked,ou=interceptors,ou=configuration" );
+        results = getAllEntries();
+
+        unmarked = results.get( "cn=unmarked,ou=system" );
+        assertNull( "cn=unmarked,ou=system should not be marked", unmarked );
+        assertNull( results.get( "cn=marked,ou=configuration,ou=system" ) );
+
+        marked = results.get( "cn=marked,ou=interceptors,ou=configuration,ou=system" );
+        assertNotNull( marked );
+        triggerSubentries = marked.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT );
+        assertNotNull( "cn=marked,ou=interceptors,ou=configuration should be marked", triggerSubentries );
+        assertEquals( "2.5.4.3=testsubentry,2.5.4.11=system", triggerSubentries.get() );
+        assertEquals( 1, triggerSubentries.size() );
+    }
+
+}
diff --git a/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/trigger/TriggerInterceptorIT.java b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/trigger/TriggerInterceptorIT.java
new file mode 100644
index 0000000..eda11b4
--- /dev/null
+++ b/old_trunk/core-integ/src/test/java/org/apache/directory/server/core/trigger/TriggerInterceptorIT.java
@@ -0,0 +1,248 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.core.trigger;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.integ.CiRunner;
+import static org.apache.directory.server.core.integ.IntegrationUtils.getSystemContext;
+import static org.apache.directory.server.core.integ.IntegrationUtils.injectEntries;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.sp.JavaStoredProcUtils;
+import org.apache.directory.shared.ldap.trigger.TriggerUtils;
+import org.apache.directory.shared.ldap.util.AttributeUtils;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.naming.NamingException;
+import javax.naming.directory.Attributes;
+import javax.naming.ldap.LdapContext;
+
+
+/**
+ * Integration tests for TriggerInterceptor.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev:$
+ */
+@RunWith ( CiRunner.class )
+public class TriggerInterceptorIT
+{
+    public static DirectoryService service;
+
+    LdapContext spCtx;
+
+
+    /*
+     * @todo replace this with an ldif annotation
+     */
+    public void createData( LdapContext ctx ) throws NamingException
+    {
+        Attributes spContainer = new AttributesImpl( "objectClass", "top", true );
+        spContainer.get( "objectClass" ).add( "organizationalUnit" );
+        spContainer.put( "ou", "Stored Procedures" );
+        spCtx = ( LdapContext ) ctx.createSubcontext( "ou=Stored Procedures", spContainer );
+    }
+    
+
+    @Test
+    public void testAfterDeleteBackupDeletedEntryEntryTrigger() throws NamingException
+    {
+        String ldif  = 
+            "version: 1\n" +
+            "\n" +
+            "dn: ou=backupContext, ou=system\n"+
+            "objectClass: top\n" +
+            "objectClass: organizationalUnit\n" +
+            "ou: backupContext\n" +
+            "\n" +
+            "dn: ou=testEntry, ou=system\n" +
+            "objectClass: top\n" +
+            "objectClass: organizationalUnit\n" +
+            "ou: testEntry\n";
+
+        LdapContext sysRoot = getSystemContext( service );
+        createData( sysRoot );
+
+        // Inject the ldif file into the server.
+        injectEntries( service, ldif );
+        
+        // Load the stored procedure unit which has the stored procedure to be triggered.
+        JavaStoredProcUtils.loadStoredProcedureClass( spCtx, BackupUtilitiesSP.class );
+        
+        // Create the Entry Trigger Specification.
+        TriggerUtils.defineTriggerExecutionSpecificPoint( sysRoot );
+        LdapContext entry = ( LdapContext ) sysRoot.lookup( "ou=testEntry" );
+        String triggerSpec = "AFTER Delete CALL \"" + BackupUtilitiesSP.class.getName() +
+            ":backupDeleted\" ( $ldapContext \"\", $name, $operationPrincipal, $deletedEntry );";
+        TriggerUtils.loadEntryTriggerSpecification( entry, triggerSpec );
+        
+        // Delete the test entry in order to fire the Trigger.
+        sysRoot.destroySubcontext( "ou=testEntry" );
+        
+        // ------------------------------------------
+        // The trigger should be fired at this point.
+        // ------------------------------------------
+        
+        // Check if the Trigger really worked (backed up the deleted entry).
+        assertNotNull( sysRoot.lookup( "ou=testEntry,ou=backupContext" ) );
+    }
+    
+    
+    public void testAfterDeleteBackupDeletedEntryPrescriptiveTrigger() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        createData( sysRoot );
+
+        // Load the stored procedure unit which has the stored procedure to be triggered.
+        JavaStoredProcUtils.loadStoredProcedureClass( spCtx, BackupUtilitiesSP.class );
+        
+        // Create a container for backing up deleted entries.
+        String ldif  = 
+            "version: 1\n" +
+            "\n" +
+            "dn: ou=backupContext, ou=system\n"+
+            "objectClass: top\n" +
+            "objectClass: organizationalUnit\n" +
+            "ou: backupContext\n";
+        
+        // Inject the ldif file into the server.
+        injectEntries( service, ldif );
+        
+        // Create the Trigger Specification within a Trigger Subentry.
+        TriggerUtils.defineTriggerExecutionSpecificPoint( sysRoot );
+        TriggerUtils.createTriggerExecutionSubentry( sysRoot,
+                                                     "triggerSubentry1",
+                                                     "{}",
+                                                     "AFTER Delete " +
+                                                         "CALL \"" + BackupUtilitiesSP.class.getName() + ":backupDeleted\" " +
+                                                             " ( $ldapContext \"\", $name, $operationPrincipal, $deletedEntry );" );        
+
+        /**
+         * The Trigger Specification without Java clutter:
+         * 
+         * AFTER Delete
+         *     CALL "BackupUtilitiesSP.backupDeleted" ( $ldapContext "", $name, $operationPrincipal, $deletedEntry );
+         * 
+         */   
+        
+        // Create a test entry which is selected by the Trigger Subentry.
+        String ldif2  = 
+            "version: 1\n" +
+            "\n" +
+            "dn: ou=testou, ou=system\n" +
+            "objectClass: top\n" +
+            "objectClass: organizationalUnit\n" +
+            "ou: testou\n";
+        
+        // Inject the ldif file into the server.
+        injectEntries( service, ldif2 );
+        
+        // Delete the test entry in order to fire the Trigger.
+        sysRoot.destroySubcontext( "ou=testou" );
+        
+        // ------------------------------------------
+        // The trigger should be fired at this point.
+        // ------------------------------------------
+        
+        // Check if the Trigger really worked (backed up the deleted entry).
+        assertNotNull( sysRoot.lookup( "ou=testou,ou=backupContext" ) );
+    }
+    
+    
+    public void testAfterAddSubscribeUserToSomeGroupsPrescriptiveTrigger() throws NamingException
+    {
+        LdapContext sysRoot = getSystemContext( service );
+        createData( sysRoot );
+
+        // Create two groups to be subscribed to : staff and teachers.
+        String ldif  = 
+            "version: 1\n" +
+            "\n" +
+            "dn: cn=staff, ou=system\n"+
+            "objectClass: top\n" +
+            "objectClass: groupOfUniqueNames\n" +
+            "uniqueMember: cn=dummy\n"+
+            "cn: staff\n" +
+            "\n" +
+            "dn: cn=teachers, ou=system\n"+
+            "objectClass: top\n" +
+            "objectClass: groupOfUniqueNames\n" +
+            "uniqueMember: cn=dummy\n"+
+            "cn: teachers\n";
+        
+        // Load the stored procedure unit which has the stored procedure to be triggered.
+        JavaStoredProcUtils.loadStoredProcedureClass( spCtx, ListUtilsSP.class );
+
+        // Inject the ldif file into the server
+        injectEntries( service, ldif );
+            
+        // Create the Trigger Specification within a Trigger Subentry.
+        String staffDN = "cn=staff, ou=system";
+        String teachersDN = "cn=teachers, ou=system";
+
+        
+        // Create the Triger Specification within a Trigger Subentry.
+        TriggerUtils.defineTriggerExecutionSpecificPoint( sysRoot );
+        TriggerUtils.createTriggerExecutionSubentry( sysRoot,
+                                                     "triggerSubentry1",
+                                                     "{}",
+                                                     "AFTER Add " +
+                                                         "CALL \"" + ListUtilsSP.class.getName() + ":subscribeToGroup\" ( $entry , $ldapContext \"" + staffDN + "\" ); " +
+                                                         "CALL \"" + ListUtilsSP.class.getName() + ":subscribeToGroup\" ( $entry , $ldapContext \"" + teachersDN + "\" );" );
+        
+        /**
+         * The Trigger Specification without Java clutter:
+         * 
+         * AFTER Add
+         *     CALL "ListUtilsSP:subscribeToGroup" ( $entry , $ldapContext "cn=staff, ou=system" );
+         *     CALL "ListUtilsSP:subscribeToGroup" ( $entry , $ldapContext "cn=teachers, ou=system" );
+         * 
+         */
+
+        // Create a test entry which is selected by the Trigger Subentry.
+        String testEntry  = 
+            "version: 1\n" +
+            "\n" +
+            "dn: cn=The Teacher of All Times, ou=system\n"+
+            "objectClass: top\n" +
+            "objectClass: inetOrgPerson\n" +
+            "cn: The Teacher of All Times\n" +
+            "sn: TheTeacher";
+
+        // Inject the entry into the server
+        injectEntries( service, testEntry );
+
+        // ------------------------------------------
+        // The trigger should be fired at this point.
+        // ------------------------------------------
+        
+        // Check if the Trigger really worked (subscribed the user to the groups).
+        Attributes staff = sysRoot.getAttributes( "cn=staff" );
+        Attributes teachers = sysRoot.getAttributes( "cn=teachers" );
+        String testEntryName = ( ( LdapContext )sysRoot.lookup( "cn=The Teacher of All Times" ) ).getNameInNamespace();
+        assertTrue( AttributeUtils.containsValueCaseIgnore( staff.get( "uniqueMember" ), testEntryName ) );
+        assertTrue( AttributeUtils.containsValueCaseIgnore( teachers.get( "uniqueMember" ), testEntryName ) );
+    }
+ 
+}
diff --git a/old_trunk/core-integ/src/test/resources/log4j.properties b/old_trunk/core-integ/src/test/resources/log4j.properties
new file mode 100644
index 0000000..c78ea83
--- /dev/null
+++ b/old_trunk/core-integ/src/test/resources/log4j.properties
@@ -0,0 +1,24 @@
+#############################################################################
+#    Licensed to the Apache Software Foundation (ASF) under one or more
+#    contributor license agreements.  See the NOTICE file distributed with
+#    this work for additional information regarding copyright ownership.
+#    The ASF licenses this file to You 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.
+#############################################################################
+log4j.rootCategory=OFF, stdout
+
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=[%d{HH:mm:ss}] %p [%c] - %m%n
+log4j.logger.org.apache.directory.server.core.integ=ERROR
+
+#log4j.logger.org.apache.directory.server.core.partition.PartitionIT=DEBUG
diff --git a/old_trunk/core-integ/src/test/resources/org/apache/directory/server/core/normalization/testDireve308Example.ldif b/old_trunk/core-integ/src/test/resources/org/apache/directory/server/core/normalization/testDireve308Example.ldif
new file mode 100644
index 0000000..6406487
--- /dev/null
+++ b/old_trunk/core-integ/src/test/resources/org/apache/directory/server/core/normalization/testDireve308Example.ldif
@@ -0,0 +1,30 @@
+#
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you 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.
+#
+#   EXAMPLE.COM is reserved for testing according to this RFC:
+#
+#   http://www.rfc-editor.org/rfc/rfc2606.txt
+#
+
+dn: ou=direct report view,ou=system
+objectClass: organizationalUnit
+ou: direct report view
+
+dn: ou=corporate category\, operations,ou=direct report view,ou=system
+objectClass: organizationalUnit
+ou: corporate category\, operations
diff --git a/old_trunk/core-integ/src/test/resources/org/apache/directory/server/core/schema/DummyComparator.bytecode b/old_trunk/core-integ/src/test/resources/org/apache/directory/server/core/schema/DummyComparator.bytecode
new file mode 100644
index 0000000..e58ac17
--- /dev/null
+++ b/old_trunk/core-integ/src/test/resources/org/apache/directory/server/core/schema/DummyComparator.bytecode
Binary files differ
diff --git a/old_trunk/core-integ/src/test/resources/org/apache/directory/server/core/schema/DummyNormalizer.bytecode b/old_trunk/core-integ/src/test/resources/org/apache/directory/server/core/schema/DummyNormalizer.bytecode
new file mode 100644
index 0000000..38d77b3
--- /dev/null
+++ b/old_trunk/core-integ/src/test/resources/org/apache/directory/server/core/schema/DummyNormalizer.bytecode
Binary files differ
diff --git a/old_trunk/core-integ/src/test/resources/org/apache/directory/server/core/schema/DummySyntaxChecker.bytecode b/old_trunk/core-integ/src/test/resources/org/apache/directory/server/core/schema/DummySyntaxChecker.bytecode
new file mode 100644
index 0000000..318a7d2
--- /dev/null
+++ b/old_trunk/core-integ/src/test/resources/org/apache/directory/server/core/schema/DummySyntaxChecker.bytecode
Binary files differ
diff --git a/old_trunk/core-integ/src/test/resources/org/apache/directory/server/core/schema/nonspecific.ldif b/old_trunk/core-integ/src/test/resources/org/apache/directory/server/core/schema/nonspecific.ldif
new file mode 100644
index 0000000..701f341
--- /dev/null
+++ b/old_trunk/core-integ/src/test/resources/org/apache/directory/server/core/schema/nonspecific.ldif
@@ -0,0 +1,40 @@
+#
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you 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.
+#
+#   EXAMPLE.COM is reserved for testing according to this RFC:
+#
+#   http://www.rfc-editor.org/rfc/rfc2606.txt
+#
+
+dn: cn=person0,ou=system
+objectClass: person
+cn: person0
+sn: sn_person0
+
+dn: cn=person1,ou=system
+objectClass: organizationalPerson
+cn: person1
+sn: sn_person1
+seealso: cn=Good One,ou=people,o=sevenSeas
+seealso:: Y249QmFkIEXDqWvDoCxvdT1wZW9wbGUsbz1zZXZlblNlYXM=
+
+
+dn: cn=person2,ou=system
+objectClass: inetOrgPerson
+cn: person2
+sn: sn_person2
diff --git a/old_trunk/core-plugin/pom.xml b/old_trunk/core-plugin/pom.xml
new file mode 100644
index 0000000..3d03731
--- /dev/null
+++ b/old_trunk/core-plugin/pom.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you 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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.directory.server</groupId>
+    <artifactId>apacheds-parent</artifactId>
+    <version>1.5.4-SNAPSHOT</version>
+  </parent>
+  <artifactId>apacheds-core-plugin</artifactId>
+  <name>ApacheDS Core Plugin (Maven 2)</name>
+  <packaging>maven-plugin</packaging>
+
+  <description>
+    A collection of tools as plugins to manage various tasks associated with
+    the directory server.
+  </description>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.maven</groupId>
+      <artifactId>maven-plugin-api</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.maven</groupId>
+      <artifactId>maven-project</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-core-shared</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.velocity</groupId>
+      <artifactId>velocity</artifactId>
+    </dependency>
+  </dependencies>
+
+  <reporting>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-plugin-plugin</artifactId>
+      </plugin>
+    </plugins>
+  </reporting>
+
+</project>
+
diff --git a/old_trunk/core-plugin/src/main/appended-resources/META-INF/LICENSE b/old_trunk/core-plugin/src/main/appended-resources/META-INF/LICENSE
new file mode 100644
index 0000000..857d336
--- /dev/null
+++ b/old_trunk/core-plugin/src/main/appended-resources/META-INF/LICENSE
@@ -0,0 +1,21 @@
+--------------------------------------------------------------
+                            ANTLR 2 License
+
+ANTLR 2 License
+
+We reserve no legal rights to the ANTLR--it is fully in the public domain. An 
+individual or company may do whatever they wish with source code distributed 
+with ANTLR or the code generated by ANTLR, including the incorporation of 
+ANTLR, or its output, into commerical software.
+
+We encourage users to develop software with ANTLR. However, we do ask that 
+credit is given to us for developing ANTLR. By "credit", we mean that if you 
+use ANTLR or incorporate any source code into one of your programs (commercial 
+product, research project, or otherwise) that you acknowledge this fact 
+somewhere in the documentation, research report, etc... If you like ANTLR 
+and have developed a nice tool with the output, please mention that you 
+developed it using ANTLR. In addition, we ask that the headers remain intact 
+in our source code. As long as these guidelines are kept, we expect to 
+continue enhancing this system and expect to make other tools available as 
+they are completed. 
+
diff --git a/old_trunk/core-plugin/src/main/appended-resources/META-INF/NOTICE b/old_trunk/core-plugin/src/main/appended-resources/META-INF/NOTICE
new file mode 100644
index 0000000..c4d94e5
--- /dev/null
+++ b/old_trunk/core-plugin/src/main/appended-resources/META-INF/NOTICE
@@ -0,0 +1,3 @@
+
+This software includes code generated by ANTLR 2.
+
diff --git a/old_trunk/core-plugin/src/main/java/org/apache/directory/server/core/tools/schema/DirectorySchemaToolMojo.java b/old_trunk/core-plugin/src/main/java/org/apache/directory/server/core/tools/schema/DirectorySchemaToolMojo.java
new file mode 100644
index 0000000..9ce483e
--- /dev/null
+++ b/old_trunk/core-plugin/src/main/java/org/apache/directory/server/core/tools/schema/DirectorySchemaToolMojo.java
@@ -0,0 +1,478 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.tools.schema;
+
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.directory.server.schema.bootstrap.AbstractBootstrapSchema;
+import org.apache.directory.server.schema.bootstrap.BootstrapSchema;
+import org.apache.directory.server.schema.bootstrap.ProducerTypeEnum;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.schema.parser.AttributeTypeLiteral;
+import org.apache.directory.shared.ldap.schema.parser.ObjectClassLiteral;
+import org.apache.directory.shared.ldap.schema.parser.OpenLdapSchemaParser;
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.project.MavenProject;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.Velocity;
+
+
+/**
+ * Maven 2 plugin mojo wrapper for directory plugin.
+ * 
+ * @goal generate
+ * @description Generates ApacheDS schema classes from OpenLDAP schema files
+ * @phase generate-sources
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class DirectorySchemaToolMojo extends AbstractMojo
+{
+    /**
+     * The directory containing the OpenLDAP schema files.
+     * @parameter expression="src/main/schema"
+     */
+    private File sourceDirectory;
+
+    /**
+     * The target directory into which the plugin generates schema java sources.
+     * @parameter expression="target/generated-sources"
+     */
+    private File outputDirectory;
+
+    /**
+     * The default package to use for generated schema classes.
+     * @parameter expression="org.apache.directory.server.schema.bootstrap"
+     */
+    private String defaultPackage;
+
+    /**
+     * The distinguished name of the default schema owner.
+     * @parameter expression="uid=admin,ou=system"
+     */
+    private String defaultOwner;
+
+    /**
+     * The set of schemas to generate classes for.
+     * @parameter 
+     */
+    private Schema[] schemas;
+
+    /**
+     * Toggles verbose output.
+     * @parameter expression="true"
+     */
+    private boolean verboseOutput;
+
+    /**
+     * @parameter expression="${project}"
+     * @required
+     */
+    private MavenProject project;
+
+
+    public DirectorySchemaToolMojo() throws Exception
+    {
+        Velocity.init();
+    }
+
+
+    private void generate( BootstrapSchema schema ) throws Exception
+    {
+        if ( schema == null )
+        {
+            throw new NullPointerException( "the schema property must be set" );
+        }
+
+        String filePath = sourceDirectory + File.separator + schema.getSchemaName() + ".schema";
+        InputStream in = new FileInputStream( filePath );
+        OpenLdapSchemaParser parser = new OpenLdapSchemaParser();
+        parser.parse( in );
+        generateSchema( schema );
+        generateAttributeTypes( parser, schema );
+        generateObjectClasses( parser, schema );
+        generateRest( schema );
+    }
+
+
+    protected void generateSchema( BootstrapSchema schema ) throws Exception
+    {
+        StringBuffer schemaCapped = new StringBuffer();
+        schemaCapped.append( Character.toUpperCase( schema.getSchemaName().charAt( 0 ) ) );
+        schemaCapped.append( schema.getSchemaName().substring( 1, schema.getSchemaName().length() ) );
+
+        VelocityContext context = new VelocityContext();
+        context.put( "package", schema.getPackageName() );
+        context.put( "classname", schemaCapped.toString() + "Schema" );
+        context.put( "schema", schema.getSchemaName() );
+        context.put( "owner", schema.getOwner() );
+        context.put( "deps", schema.getDependencies() );
+
+        Reader fileIn = getResourceReader( "Schema.template" );
+        Writer writer = getResourceWriter( schema.getPackageName(), schema.getUnqualifiedClassName() );
+        Velocity.evaluate( context, writer, "LOG", fileIn );
+
+        writer.flush();
+        writer.close();
+    }
+
+
+    protected void generateRest( BootstrapSchema schema ) throws Exception
+    {
+        List types = new ArrayList();
+        types.addAll( ProducerTypeEnum.getList() );
+        types.remove( ProducerTypeEnum.ATTRIBUTE_TYPE_PRODUCER );
+        types.remove( ProducerTypeEnum.OBJECT_CLASS_PRODUCER );
+
+        ProducerTypeEnum type = null;
+        for ( int ii = 0; ii < types.size(); ii++ )
+        {
+            type = ( ProducerTypeEnum ) types.get( ii );
+
+            if ( exists( schema.getFullDefaultBaseClassName( type ), type ) )
+            {
+                continue;
+            }
+
+            VelocityContext context = new VelocityContext();
+            context.put( "package", schema.getPackageName() );
+            context.put( "classname", schema.getUnqualifiedClassName( type ) );
+            context.put( "schema", schema.getSchemaName() );
+            context.put( "owner", schema.getOwner() );
+            context.put( "type", type.getName().substring( 0, type.getName().length() - 8 ) );
+
+            String typeName = null;
+            
+            switch ( type )
+            {
+                case COMPARATOR_PRODUCER :
+                    typeName = "ProducerTypeEnum.COMPARATOR_PRODUCER";
+                    break;
+                    
+                case DIT_CONTENT_RULE_PRODUCER :
+                    typeName = "ProducerTypeEnum.DIT_CONTENT_RULE_PRODUCER";
+                    break;
+                    
+                case DIT_STRUCTURE_RULE_PRODUCER :
+                    typeName = "ProducerTypeEnum.DIT_STRUCTURE_RULE_PRODUCER";
+                    break;
+                    
+                case MATCHING_RULE_PRODUCER :
+                    typeName = "ProducerTypeEnum.MATCHING_RULE_PRODUCER";
+                    break;
+                    
+                case MATCHING_RULE_USE_PRODUCER :
+                    typeName = "ProducerTypeEnum.MATCHING_RULE_USE_PRODUCER";
+                    break;
+                    
+                case NAME_FORM_PRODUCER :
+                    typeName = "ProducerTypeEnum.NAME_FORM_PRODUCER";
+                    break;
+                    
+                case NORMALIZER_PRODUCER :
+                    typeName = "ProducerTypeEnum.NORMALIZER_PRODUCER";
+                    break;
+                    
+                case SYNTAX_CHECKER_PRODUCER :
+                    typeName = "ProducerTypeEnum.SYNTAX_CHECKER_PRODUCER";
+                    break;
+                    
+                case SYNTAX_PRODUCER :
+                    typeName = "ProducerTypeEnum.SYNTAX_PRODUCER";
+                    break;
+                    
+                default:
+                    throw new IllegalStateException( "Unexpected producer: " + type.getName() );
+            }
+
+            context.put( "typeName", typeName );
+            runVelocity( schema.getPackageName(), schema.getUnqualifiedClassName( type ), context, "typeless.template",
+                type );
+        }
+    }
+
+
+    protected void generateAttributeTypes( OpenLdapSchemaParser parser, BootstrapSchema schema ) throws Exception
+    {
+        final ProducerTypeEnum type = ProducerTypeEnum.ATTRIBUTE_TYPE_PRODUCER;
+
+        // check to see if the producer exists for this type
+        if ( exists( schema.getFullDefaultBaseClassName( type ), type ) )
+        {
+            return;
+        }
+
+        int size = parser.getAttributeTypes().size();
+        AttributeTypeLiteral[] attributeTypes = new AttributeTypeLiteral[size];
+        attributeTypes = ( AttributeTypeLiteral[] ) parser.getAttributeTypes().toArray( attributeTypes );
+
+        VelocityContext context = new VelocityContext();
+        context.put( "package", schema.getPackageName() );
+        context.put( "classname", schema.getUnqualifiedClassName( type ) );
+        context.put( "schema", schema.getSchemaName() );
+        context.put( "owner", schema.getOwner() );
+        context.put( "schemaDepCount", new Integer( schema.getDependencies().length ) );
+        context.put( "schemaDeps", new String[]
+            { "dep1", "dep2" } );
+        context.put( "attrTypes", attributeTypes );
+        runVelocity( schema.getPackageName(), schema.getUnqualifiedClassName( type ), context,
+            "AttributeTypes.template", type );
+    }
+
+
+    protected void generateObjectClasses( OpenLdapSchemaParser parser, BootstrapSchema schema ) throws Exception
+    {
+        final ProducerTypeEnum type = ProducerTypeEnum.OBJECT_CLASS_PRODUCER;
+
+        // check to see if the producer exists for this type
+        if ( exists( schema.getFullDefaultBaseClassName( type ), type ) )
+        {
+            return;
+        }
+
+        int size = parser.getObjectClassTypes().size();
+        ObjectClassLiteral[] objectClasses = new ObjectClassLiteral[size];
+        objectClasses = ( ObjectClassLiteral[] ) parser.getObjectClassTypes().toArray( objectClasses );
+
+        VelocityContext context = new VelocityContext();
+        context.put( "package", schema.getPackageName() );
+        context.put( "classname", schema.getUnqualifiedClassName( type ) );
+        context.put( "schema", schema.getSchemaName() );
+        context.put( "owner", schema.getOwner() );
+        context.put( "schemaDepCount", new Integer( schema.getDependencies().length ) );
+        context.put( "schemaDeps", new String[]
+            { "dep1", "dep2" } );
+        context.put( SchemaConstants.OBJECT_CLASSES_AT, objectClasses );
+        runVelocity( schema.getPackageName(), schema.getUnqualifiedClassName( type ), context,
+            "ObjectClasses.template", type );
+    }
+
+
+    protected void runVelocity( String pkg, String uqcn, VelocityContext context, String template, ProducerTypeEnum type )
+        throws Exception
+    {
+        Reader fileIn = getResourceReader( template );
+        Writer writer = getResourceWriter( pkg, uqcn );
+        Velocity.evaluate( context, writer, "LOG", fileIn );
+        writer.flush();
+        writer.close();
+    }
+
+
+    protected Reader getResourceReader( String res ) throws IOException
+    {
+        return new InputStreamReader( getClass().getResourceAsStream( res ) );
+    }
+
+
+    protected boolean mkdirs( String base, String path )
+    {
+        String[] comps = path.split( "/" );
+        File file = new File( base );
+
+        if ( !file.exists() )
+        {
+            file.mkdirs();
+        }
+
+        for ( int ii = 0; ii < comps.length; ii++ )
+        {
+            file = new File( file, comps[ii] );
+
+            if ( !file.exists() )
+            {
+                file.mkdirs();
+            }
+        }
+
+        return file.exists();
+    }
+
+
+    protected FileWriter getResourceWriter( String pkg, String classname ) throws IOException
+    {
+        mkdirs( outputDirectory.getPath(), pkg.replace( '.', File.separatorChar ) );
+        File base = outputDirectory;
+        String relativePath = pkg.replace( '.', File.separatorChar );
+        File dir = new File( base, relativePath );
+        return new FileWriter( new File( dir, classname + ".java" ) );
+    }
+
+
+    protected boolean exists( String defaultClass, ProducerTypeEnum type )
+    {
+        // check to see if any of the classes are available in the java 
+        // source directory, if so we return true
+        File defaultFile = new File( project.getBuild().getSourceDirectory() + File.separator
+            + getFilePath( defaultClass ) );
+        return defaultFile.exists();
+    }
+
+
+    private String getFilePath( String fqcn )
+    {
+        String path = fqcn.replace( '.', File.separatorChar );
+        path += ".java";
+        return path;
+    }
+
+
+    private boolean isStale( BootstrapSchema schema )
+    {
+        String pkgPath = schema.getPackageName().replace( '.', File.separatorChar );
+        File dir = new File( outputDirectory, pkgPath );
+        File schemaClassFile = new File( dir, schema.getUnqualifiedClassName() + ".java" );
+
+        if ( !schemaClassFile.exists() )
+        {
+            return true;
+        }
+
+        File schemaFile = new File( sourceDirectory, schema.getSchemaName() + ".schema" );
+        return schemaFile.lastModified() > schemaClassFile.lastModified();
+    }
+
+
+    public void execute() throws MojoExecutionException
+    {
+        // Bypass if no schemas have yet been defined 
+        if ( schemas == null || schemas.length == 0 )
+        {
+            getLog().warn( "No schemas defined for directory plugin!" );
+            return;
+        }
+
+        // Make sure schema configurations have a name field and set defaults
+        // for any other missing properties of the bean: pkg and owner.
+        for ( int ii = 0; ii < schemas.length; ii++ )
+        {
+            Schema schema = schemas[ii];
+
+            if ( schema.getName() == null )
+            {
+                String msg = ii + "th schema configuration element must specify a name.";
+                getLog().error( msg );
+                throw new MojoExecutionException( msg );
+            }
+
+            if ( schema.getPkg() == null )
+            {
+                schema.setPkg( defaultPackage );
+            }
+
+            if ( schema.getOwner() == null )
+            {
+                schema.setOwner( defaultOwner );
+            }
+        }
+
+        // Report configuration if verbose output is enabled
+        if ( verboseOutput )
+        {
+            report();
+        }
+
+        // Create output directory if it does not exist
+        if ( !outputDirectory.exists() )
+        {
+            outputDirectory.mkdirs();
+        }
+
+        // Generate for each schema 
+        for ( int ii = 0; ii < schemas.length; ii++ )
+        {
+            try
+            {
+                BootstrapSchema bootstrapSchema = new AbstractBootstrapSchema( schemas[ii].getOwner(), schemas[ii]
+                    .getName(), schemas[ii].getPkg(), schemas[ii].getDependencies() )
+                {
+                };
+
+                if ( isStale( bootstrapSchema ) )
+                {
+                    getLog().info( "Generating " + schemas[ii].getName() + " schema." );
+                    generate( bootstrapSchema );
+                }
+                else
+                {
+                    getLog().info( schemas[ii].getName() + " schema is up to date." );
+                }
+            }
+            catch ( Exception e )
+            {
+                throw new MojoExecutionException( "Failed while generating sources for " + schemas[ii].getName(), e );
+            }
+        }
+
+        project.addCompileSourceRoot( outputDirectory.getPath() );
+    }
+
+
+    private void report()
+    {
+        getLog().info( "===================================================================" );
+        getLog().info( "[directory:generate]" );
+        getLog().info( "sourceDirectory = " + sourceDirectory );
+        getLog().info( "outputDirectory = " + outputDirectory );
+        getLog().info( "defaultPackage  = " + defaultPackage );
+        getLog().info( "defaultOwner    = " + defaultOwner );
+        getLog().info( "----------------------------- schemas -----------------------------" );
+
+        if ( schemas != null )
+        {
+            for ( int ii = 0; ii < schemas.length; ii++ )
+            {
+                getLog().info( "SCHEMA: " + schemas[ii].getName() );
+
+                if ( schemas[ii].getDependencies() != null )
+                {
+                    StringBuffer buf = new StringBuffer();
+                    for ( int jj = 0; jj < schemas[ii].getDependencies().length; jj++ )
+                    {
+                        buf.append( schemas[ii].getDependencies()[jj] );
+                        buf.append( " " );
+                    }
+                    getLog().info( "DEPENDENCIES: " + buf.toString() );
+                }
+
+                getLog().info( "PACKAGE: " + schemas[ii].getPkg() );
+                getLog().info( "OWNER: " + schemas[ii].getOwner() );
+
+                if ( ii + 1 < schemas.length )
+                {
+                    getLog().info( "" );
+                }
+            }
+        }
+
+        getLog().info( "===================================================================" );
+    }
+}
diff --git a/old_trunk/core-plugin/src/main/java/org/apache/directory/server/core/tools/schema/Schema.java b/old_trunk/core-plugin/src/main/java/org/apache/directory/server/core/tools/schema/Schema.java
new file mode 100644
index 0000000..79f4c7a
--- /dev/null
+++ b/old_trunk/core-plugin/src/main/java/org/apache/directory/server/core/tools/schema/Schema.java
@@ -0,0 +1,77 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.tools.schema;
+
+
+public class Schema
+{
+    private String name;
+    private String[] dependencies;
+    private String pkg;
+    private String owner;
+
+
+    public void setDependencies( String[] dependencies )
+    {
+        this.dependencies = dependencies;
+    }
+
+
+    public String[] getDependencies()
+    {
+        return dependencies;
+    }
+
+
+    public void setPkg( String pkg )
+    {
+        this.pkg = pkg;
+    }
+
+
+    public String getPkg()
+    {
+        return pkg;
+    }
+
+
+    public void setOwner( String owner )
+    {
+        this.owner = owner;
+    }
+
+
+    public String getOwner()
+    {
+        return owner;
+    }
+
+
+    public void setName( String name )
+    {
+        this.name = name;
+    }
+
+
+    public String getName()
+    {
+        return name;
+    }
+}
diff --git a/old_trunk/core-plugin/src/main/resources/org/apache/directory/server/core/tools/schema/AttributeTypes.template b/old_trunk/core-plugin/src/main/resources/org/apache/directory/server/core/tools/schema/AttributeTypes.template
new file mode 100755
index 0000000..074bf21
--- /dev/null
+++ b/old_trunk/core-plugin/src/main/resources/org/apache/directory/server/core/tools/schema/AttributeTypes.template
@@ -0,0 +1,100 @@
+#*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ *#
+package $package;
+
+
+import java.util.ArrayList;
+import javax.naming.NamingException;
+
+import org.apache.directory.shared.ldap.schema.*;
+import org.apache.directory.server.schema.registries.*;
+
+#if ( $package != "org.apache.directory.server.schema.bootstrap" )
+import org.apache.directory.server.schema.bootstrap.*;
+#end
+
+#**
+ * A producer of schema attributeType definations for the $schema schema.  This
+ * code has been automatically generated using schema files in the OpenLDAP
+ * format along with the directory plugin for maven.  This has been done
+ * to facilitate Eve<->OpenLDAP schema interoperability.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ *#
+public class $classname extends AbstractBootstrapProducer
+{
+
+    public $classname()
+    {
+        super( ProducerTypeEnum.ATTRIBUTE_TYPE_PRODUCER );
+    }
+
+
+    ## ------------------------------------------------------------------------
+    ## BootstrapProducer Methods
+    ## ------------------------------------------------------------------------
+
+
+    #**
+     * @see BootstrapProducer#produce( Registries, ProducerCallback )
+     *#
+    public void produce( Registries registries, ProducerCallback cb )
+        throws NamingException
+    {
+        ArrayList<String> names = new ArrayList<String>();
+        BootstrapAttributeType attributeType;
+
+        #foreach ( $attrType in $attrTypes )
+
+        ## --------------------------------------------------------------------
+        ## AttributeType $attrType.getOid() $!attrType.getName()
+        ## --------------------------------------------------------------------
+
+        attributeType = newAttributeType( "$attrType.getOid()", registries );
+#if ( $attrType.getDescription() )
+        #set( $desc = $attrType.getDescription().replaceAll("\"", "\\\"") )
+        attributeType.setDescription( "$desc" );
+#end
+        attributeType.setCanUserModify( ! $attrType.isNoUserModification() );
+        attributeType.setSingleValue( $attrType.isSingleValue() );
+        attributeType.setCollective( $attrType.isCollective() );
+        attributeType.setObsolete( $attrType.isObsolete() );
+        attributeType.setLength( $attrType.getLength() );
+        attributeType.setUsage( UsageEnum.getUsage( "$attrType.getUsage()" ) );
+#if ( $attrType.getSuperior() )        attributeType.setSuperiorId( "$attrType.getSuperior()" ); #end
+#if ( $attrType.getEquality() )        attributeType.setEqualityId( "$attrType.getEquality()" );
+#end
+#if ( $attrType.getSubstr() )        attributeType.setSubstrId( "$attrType.getSubstr()" );#end
+#if ( $attrType.getOrdering() )        attributeType.setOrderingId( "$attrType.getOrdering()" );#end
+#if ( $attrType.getSyntax() )        attributeType.setSyntaxId( "$attrType.getSyntax()" );#end
+
+        #if ( $attrType.getNames() )
+names.clear();
+       #foreach ( $name in $attrType.getNames() ) names.add( "$name" );
+       #end
+ attributeType.setNames( names.toArray( EMPTY ) );
+        #else
+ attributeType.setNames( EMPTY );
+        #end
+cb.schemaObjectProduced( this, "$attrType.getOid()", attributeType );
+
+ #end   }
+}
diff --git a/old_trunk/core-plugin/src/main/resources/org/apache/directory/server/core/tools/schema/ObjectClasses.template b/old_trunk/core-plugin/src/main/resources/org/apache/directory/server/core/tools/schema/ObjectClasses.template
new file mode 100755
index 0000000..74d22c4
--- /dev/null
+++ b/old_trunk/core-plugin/src/main/resources/org/apache/directory/server/core/tools/schema/ObjectClasses.template
@@ -0,0 +1,128 @@
+#*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ *#
+package $package;
+
+
+import java.util.ArrayList;
+import javax.naming.NamingException;
+import org.apache.directory.shared.ldap.schema.ObjectClassTypeEnum;
+import org.apache.directory.server.schema.registries.*;
+
+#if ( $package != "org.apache.directory.server.schema.bootstrap" )
+import org.apache.directory.server.schema.bootstrap.*;
+#end
+
+#**
+ * A producer of schema objectClass definations for the $schema schema.  This
+ * code has been automatically generated using schema files in the OpenLDAP
+ * format along with the directory plugin for maven.  This has been done
+ * to facilitate OpenLDAP schema interoperability.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ *#
+public class $classname extends AbstractBootstrapProducer
+{
+
+    public $classname()
+    {
+        super( ProducerTypeEnum.OBJECT_CLASS_PRODUCER );
+    }
+
+
+    ## ------------------------------------------------------------------------
+    ## BootstrapProducer Methods
+    ## ------------------------------------------------------------------------
+
+
+    #**
+     * @see BootstrapProducer#produce(Registries, ProducerCallback)
+     *#
+    public void produce( Registries registries, ProducerCallback cb )
+        throws NamingException
+    {
+        ArrayList<String> array = new ArrayList<String>();
+        BootstrapObjectClass objectClass;
+
+        #foreach ( $objectClass in $objectClasses )
+
+        // --------------------------------------------------------------------
+        // ObjectClass $objectClass.getOid() $!objectClass.getName()
+        // --------------------------------------------------------------------
+
+        objectClass = newObjectClass( "$objectClass.getOid()", registries );
+        objectClass.setObsolete( $objectClass.isObsolete() );
+
+        #if ( $objectClass.getDescription() )
+#set( $desc = $objectClass.getDescription().replaceAll("\"", "\\\"") )
+objectClass.setDescription( "$desc" );
+        #end
+// set the objectclass type
+        #if ( $objectClass.getClassType().getValue() == 0 )
+objectClass.setType( ObjectClassTypeEnum.ABSTRACT );
+        #elseif ( $objectClass.getClassType().getValue() == 1 )
+objectClass.setType( ObjectClassTypeEnum.AUXILIARY );
+        #elseif ( $objectClass.getClassType().getValue() == 2 )
+objectClass.setType( ObjectClassTypeEnum.STRUCTURAL );
+        #end
+
+        // set superior objectClasses
+        #if ( $objectClass.getSuperiors() )
+array.clear();
+       #foreach ( $id in $objectClass.getSuperiors() ) array.add( "$id" );
+       #end
+ objectClass.setSuperClassIds( array.toArray( EMPTY ) );
+        #else
+ objectClass.setSuperClassIds( EMPTY );
+        #end
+
+        // set must list
+        #if ( $objectClass.getMust() )
+array.clear();
+       #foreach ( $id in $objectClass.getMust() ) array.add( "$id" );
+       #end
+ objectClass.setMustListIds( array.toArray( EMPTY ) );
+        #else
+ objectClass.setMustListIds( EMPTY );
+        #end
+
+        // set may list
+        #if ( $objectClass.getMay() )
+array.clear();
+       #foreach ( $id in $objectClass.getMay() ) array.add( "$id" );
+       #end
+ objectClass.setMayListIds( array.toArray( EMPTY ) );
+        #else
+ objectClass.setMayListIds( EMPTY );
+        #end
+
+        // set names
+        #if ( $objectClass.getNames() )
+array.clear();
+       #foreach ( $id in $objectClass.getNames() ) array.add( "$id" );
+       #end
+ objectClass.setNames( array.toArray( EMPTY ) );
+        #else
+ objectClass.setNames( EMPTY );
+        #end
+cb.schemaObjectProduced( this, "$objectClass.getOid()", objectClass );
+
+ #end   }
+}
diff --git a/old_trunk/core-plugin/src/main/resources/org/apache/directory/server/core/tools/schema/Schema.template b/old_trunk/core-plugin/src/main/resources/org/apache/directory/server/core/tools/schema/Schema.template
new file mode 100644
index 0000000..bbb3a96
--- /dev/null
+++ b/old_trunk/core-plugin/src/main/resources/org/apache/directory/server/core/tools/schema/Schema.template
@@ -0,0 +1,52 @@
+#*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ *#
+package $package;
+
+
+import java.util.ArrayList;
+
+#if ( $package != "org.apache.directory.server.schema.bootstrap" )
+import org.apache.directory.server.schema.bootstrap.*;
+#end
+
+
+#**
+ * Top level $schema schema class.  This code has been automatically generated
+ * using the directory plugin for maven.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ *#
+public class $classname extends AbstractBootstrapSchema
+{
+    public $classname()
+    {
+        super( "$owner", "$schema", "$package" );
+
+        ArrayList<String> list = new ArrayList<String>();
+        #if ( $deps )
+list.clear();
+       #foreach ( $name in $deps ) list.add( "$name" );
+       #end
+ setDependencies( list.toArray( DEFAULT_DEPS ) );#else
+ setDependencies( DEFAULT_DEPS );#end
+
+    }
+}
diff --git a/old_trunk/core-plugin/src/main/resources/org/apache/directory/server/core/tools/schema/typeless.template b/old_trunk/core-plugin/src/main/resources/org/apache/directory/server/core/tools/schema/typeless.template
new file mode 100644
index 0000000..45e0042
--- /dev/null
+++ b/old_trunk/core-plugin/src/main/resources/org/apache/directory/server/core/tools/schema/typeless.template
@@ -0,0 +1,60 @@
+#*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ *#
+package $package;
+
+
+import javax.naming.NamingException;
+import org.apache.directory.server.schema.registries.*;
+
+#if ( $package != "org.apache.directory.server.schema.bootstrap" )
+import org.apache.directory.server.schema.bootstrap.*;
+#end
+
+
+#**
+ * A producer of $type objects for the $schema schema.  This code has been
+ * automatically generated using schema files in the OpenLDAP format along with
+ * the directory plugin for maven.  This has been done to facilitate
+ * OpenLDAP schema interoperability.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ *#
+public class $classname extends AbstractBootstrapProducer
+{
+    public $classname()
+    {
+        super( $typeName );
+    }
+
+
+    ## ------------------------------------------------------------------------
+    ## BootstrapProducer Methods
+    ## ------------------------------------------------------------------------
+
+
+    #**
+     * @see BootstrapProducer#produce(Registries, ProducerCallback)
+     *#
+    public void produce( Registries registries, ProducerCallback cb )
+        throws NamingException
+    {
+    }
+}
diff --git a/old_trunk/core-plugin/src/site/site.xml b/old_trunk/core-plugin/src/site/site.xml
new file mode 100644
index 0000000..15d43f9
--- /dev/null
+++ b/old_trunk/core-plugin/src/site/site.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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.
+-->
+<!--
+  @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+-->
+<project name="${project.name}">
+  <bannerLeft>
+    <name>${project.name}</name>
+  </bannerLeft>
+  <publishDate position="navigation-bottom" format="dd-MM-yyyy HH:mm" />
+  <body>
+    <menu name="Parent">
+      <item name="Apache Directory ApacheDS" href="../index.html" />
+    </menu>
+    <menu name="Overview">
+      <item name="Goals" href="plugin-info.html" />
+    </menu>
+    <menu ref="reports" />
+  </body>
+</project>
diff --git a/old_trunk/core-shared/pom.xml b/old_trunk/core-shared/pom.xml
new file mode 100644
index 0000000..2eda51a
--- /dev/null
+++ b/old_trunk/core-shared/pom.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you 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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.directory.server</groupId>
+    <artifactId>apacheds-parent</artifactId>
+    <version>1.5.4-SNAPSHOT</version>
+  </parent>
+  <artifactId>apacheds-core-shared</artifactId>
+  <name>ApacheDS Core Shared</name>
+
+  <description>
+    Shared classes between the core plugin and the core to prevent cyclic
+    dependencies since the core uses the core plugin.
+  </description>
+  
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-core-constants</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
+  </dependencies>
+
+  <packaging>jar</packaging>
+</project>
+
diff --git a/old_trunk/core-shared/src/main/java/org/apache/directory/server/core/cursor/Cursor.java b/old_trunk/core-shared/src/main/java/org/apache/directory/server/core/cursor/Cursor.java
new file mode 100644
index 0000000..0114cb4
--- /dev/null
+++ b/old_trunk/core-shared/src/main/java/org/apache/directory/server/core/cursor/Cursor.java
@@ -0,0 +1,236 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements.  See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership.  The ASF licenses this file

+ * to you 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.

+ */

+package org.apache.directory.server.core.cursor;

+

+import java.io.IOException;

+

+

+/**

+ * A simple cursor concept for bidirectionally enumerating over elements.

+ * Cursors unlike iterators request to advance to an element by calling next()

+ * or previous() which returns true or false if the request succeeds.  Other

+ * operations for relative and absolute advances are provided.  If the cursor

+ * does not advance, then the Cursor is either positioned before the first

+ * element or after the last element in which case the user of the Cursor must

+ * stop advancing in the respective direction.  If an advance succeeds a get()

+ * operation retreives the current object at the Cursors position.

+ *

+ * Although this interface presumes Cursors can advance bidirectionally, one

+ * or more either direction may not be supported.  In this case

+ * implementations should throw UnsupportedOperationExceptions.

+ *

+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>

+ * @version $Rev$, $Date$

+ */

+public interface Cursor<E>

+{

+    /**

+     * Positions this Curser before the first element.

+     *

+     * @throws IOException if there are problems positioning this cursor or if

+     * this Cursor is closed

+     * @throws UnsupportedOperationException if this operation is not supported

+     */

+    void beforeFirst() throws IOException;

+

+

+    /**

+     * Positions this Curser after the last element.

+     *

+     * @throws IOException if there are problems positioning this Cursor or if

+     * this Cursor is closed

+     * @throws UnsupportedOperationException if this operation is not supported

+     */

+    void afterLast() throws IOException;

+

+

+    /**

+     * Positions this Curser at the nth element.  Zero based indexing is used.

+     *

+     * If the specified position is past the first or last element, the Cursor

+     * is positioned before the first or after the last element respectively.

+     *

+     * @param absolutePosition the absolute position to move this Cursor to

+     * @return true if the position has been successfully changed to the

+     * element at the specified position, false otherwise

+     * @throws IOException if there are problems positioning this Cursor or if

+     * this Cursor is closed

+     * @throws UnsupportedOperationException if this operation is not supported

+     */

+    boolean absolute( int absolutePosition ) throws IOException;

+

+

+    /**

+     * Positions this Curser n places relative to the present position.  Zero

+     * based indexing is used and negative index values may be provided for

+     * representing the direction.

+     *

+     * If the specified position is past the first or last element, the Cursor

+     * is positioned before the first or after the last element respectively.

+     *

+     * @param relativePosition the relative position to move this Cursor to

+     * @return true if the position has been successfully changed to the

+     * element relative to the current position, false otherwise

+     * @throws IOException if there are problems positioning this Cursor or if

+     * this Cursor is closed

+     * @throws UnsupportedOperationException if this operation is not supported

+     */

+    boolean relative( int relativePosition ) throws IOException;

+

+

+    /**

+     * Positions this Curser at the first element.

+     *

+     * @return true if the position has been successfully changed to the first

+     * element, false otherwise

+     * @throws IOException if there are problems positioning this Cursor or if

+     * this Cursor is closed

+     * @throws UnsupportedOperationException if this operation is not supported

+     */

+    boolean first() throws IOException;

+

+

+    /**

+     * Positions this Curser at the last element.

+     *

+     * @return true if the position has been successfully changed to the last

+     * element, false otherwise

+     * @throws IOException if there are problems positioning this Cursor or if

+     * this Cursor is closed

+     * @throws UnsupportedOperationException if this operation is not supported

+     */

+    boolean last() throws IOException;

+

+

+    /**

+     * Checks if this Curser is positioned at the first element.

+     *

+     * @return true if the current position is at the first element, false

+     * otherwise

+     * @throws IOException if there are problems determining this Cursor's

+     * position, or if this Cursor is closed

+     * @throws UnsupportedOperationException if this operation is not supported

+     */

+    boolean isFirst() throws IOException;

+

+

+    /**

+     * Checks if this Curser is positioned at the last element.

+     *

+     * @return true if the current position is at the last element, false

+     * otherwise

+     * @throws IOException if there are problems determining this Cursor's

+     * position, or if this Cursor is closed

+     * @throws UnsupportedOperationException if this operation is not supported

+     */

+    boolean isLast() throws IOException;

+

+

+    /**

+     * Checks if this Curser is positioned after the last element.

+     *

+     * @return true if the current position is after the last element, false

+     * otherwise

+     * @throws IOException if there are problems determining this Cursor's

+     * position, or if this Cursor is closed

+     * @throws UnsupportedOperationException if this operation is not supported

+     */

+    boolean isAfterLast() throws IOException;

+

+

+    /**

+     * Checks if this Curser is positioned before the first element.

+     *

+     * @return true if the current position is before the first element, false

+     * otherwise

+     * @throws IOException if there are problems determining this Cursor's

+     * position, or if this Cursor is closed

+     * @throws UnsupportedOperationException if this operation is not supported

+     */

+    boolean isBeforeFirst() throws IOException;

+

+

+    /**

+     * Checks if this Curser is closed.  Calls to this operation should not

+     * fail with exceptions if and only if the cursor is in the closed state.

+     *

+     * @return true if this Cursor is closed, false otherwise

+     * @throws IOException if there are problems determining the cursor's closed state

+     * @throws UnsupportedOperationException if this operation is not supported

+     */

+    boolean isClosed() throws IOException;

+

+

+    /**

+     * Advances this Cursor to the previous position.

+     *

+     * @return true if the advance succeeded, false otherwise

+     * @throws IOException if there are problems advancing to the next position

+     * @throws UnsupportedOperationException if advances in this direction are not supported

+     */

+    boolean previous() throws IOException;

+

+

+    /**

+     * Advances this Cursor to the next position.

+     *

+     * @return true if the advance succeeded, false otherwise

+     * @throws IOException if there are problems advancing to this Cursor to

+     * the next position, or if this Cursor is closed

+     * @throws UnsupportedOperationException if advances in this direction are not supported

+     */

+    boolean next() throws IOException;

+

+

+    /**

+     * Gets the object at the current position.  Cursor implementations may

+     * choose to reuse element objects by re-populating them on advances

+     * instead of creating new objects on each advance.

+     *

+     * @return the object at the current position

+     * @throws IOException if the object at this Cursor's current position

+     * cannot be retrieved, or if this Cursor is closed

+     */

+    E get() throws IOException;

+

+

+    /**

+     * Gets whether or not this Cursor will return the same element object

+     * instance on get() operations for any position of this Cursor.  Some

+     * Cursor implementations may reuse the same element copying values into

+     * it for every position rather than creating and emiting new element

+     * objects on each advance.  Some Cursor implementations may return

+     * different elements for each position yet the same element instance

+     * is returned for the same position. In these cases this method should

+     * return true.

+     *

+     * @return true if elements are reused by this Cursor

+     */

+    boolean isElementReused();

+

+

+    /**

+     * Closes this Cursor and frees any resources it my have allocated.

+     * Repeated calls to this method after this Cursor has already been

+     * called should not fail with exceptions.

+     *

+     * @throws IOException if this Cursor cannot be closed

+     */

+    void close() throws IOException;

+}

diff --git a/old_trunk/core-shared/src/main/java/org/apache/directory/server/core/cursor/ListCursor.java b/old_trunk/core-shared/src/main/java/org/apache/directory/server/core/cursor/ListCursor.java
new file mode 100644
index 0000000..73b112e
--- /dev/null
+++ b/old_trunk/core-shared/src/main/java/org/apache/directory/server/core/cursor/ListCursor.java
@@ -0,0 +1,358 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements.  See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership.  The ASF licenses this file

+ * to you 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.

+ */

+package org.apache.directory.server.core.cursor;

+

+

+import java.io.IOException;

+import java.util.Collections;

+import java.util.List;

+

+

+/**

+ * A simple implementation of a Cursor on a {@link List}.  Optionally, the

+ * Cursor may be limited to a specific range within the list.

+ *

+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>

+ * @version $Rev$, $Date$

+ */

+public class ListCursor<E> implements Cursor<E>

+{

+    private final List<E> list;

+    private final int start;

+    private final int end;

+    private boolean closed;

+    private int index = -1;

+

+

+    /**

+     * Creates a new ListCursor with lower (inclusive) and upper (exclusive)

+     * bounds.

+     *

+     * As with all Cursors, this ListCursor requires a successful return from

+     * advance operations (next() or previous()) to properly return values

+     * using the get() operation.

+     *

+     * @param start the lower bound index

+     * @param list the list this ListCursor operates on

+     * @param end the upper bound index

+     */

+    public ListCursor( int start, List<E> list, int end )

+    {

+        if ( start < 0 || start > list.size() )

+        {

+            throw new IllegalArgumentException( "start index '" + start + "' out of range" );

+        }

+

+        if ( end < 0 || end > list.size() )

+        {

+            throw new IllegalArgumentException( "end index '" + end + "' out of range" );

+        }

+

+        // check list is not empty list since the empty list is the only situation

+        // where we allow for start to equal the end: in other cases it makes no sense

+        if ( list.size() > 0 && start >= end )

+        {

+            throw new IllegalArgumentException( "start index '" + start + "' greater than or equal to end index '"

+                    + end + "' just does not make sense" );

+        }

+

+        //noinspection ConstantConditions

+        if ( list != null )

+        {

+            this.list = list;

+        }

+        else

+        {

+            //noinspection unchecked

+            this.list = Collections.EMPTY_LIST;

+        }

+

+        this.start = start;

+        this.end = end;

+    }

+

+

+    /**

+     * Creates a new ListCursor with a specific upper (exclusive) bound: the

+     * lower (inclusive) bound defaults to 0.

+     *

+     * @param list the backing for this ListCursor

+     * @param end the upper bound index representing the position after the

+     * last element

+     */

+    public ListCursor( List<E> list, int end )

+    {

+        this( 0, list, end );

+    }

+

+

+    /**

+     * Creates a new ListCursor with a lower (inclusive) bound: the upper

+     * (exclusive) bound is the size of the list.

+     *

+     * @param start the lower (inclusive) bound index: the position of the

+     * first entry

+     * @param list the backing for this ListCursor

+     */

+    public ListCursor( int start, List<E> list )

+    {

+        this( start, list, list.size() );

+    }

+

+

+    /**

+     * Creates a new ListCursor without specific bounds: the bounds are

+     * acquired from the size of the list.

+     *

+     * @param list the backing for this ListCursor

+     */

+    public ListCursor( List<E> list )

+    {

+        this( 0, list, list.size() );

+    }

+

+

+    /**

+     * Creates a new ListCursor without any elements.

+     */

+    public ListCursor()

+    {

+        //noinspection unchecked

+        this( 0, Collections.EMPTY_LIST, 0 );

+    }

+

+

+    protected void checkClosed( String operation ) throws IOException

+    {

+        if ( closed )

+        {

+            throw new IOException( "Attempting " + operation + " operation on a closed Cursor." );

+        }

+    }

+

+

+    public void beforeFirst() throws IOException

+    {

+        checkClosed( "beforeFirst()" );

+        this.index = -1;

+    }

+

+

+    public void afterLast() throws IOException

+    {

+        checkClosed( "afterLast()" );

+        this.index = end;

+    }

+

+

+    public boolean absolute( int index ) throws IOException

+    {

+        checkClosed( "absolute()" );

+

+        if ( index < start )

+        {

+            this.index = -1;

+            return false;

+        }

+

+        if ( index >= end )

+        {

+            this.index = end;

+            return false;

+        }

+

+        this.index = index;

+        return true;

+    }

+

+

+    public boolean relative( int index ) throws IOException

+    {

+        checkClosed( "relative()" );

+

+        if ( this.index + index < start )

+        {

+            this.index = -1;

+            return false;

+        }

+

+        if ( this.index + index >= end )

+        {

+            this.index = end;

+            return false;

+        }

+

+        this.index += index;

+        return true;

+    }

+

+

+    public boolean first() throws IOException

+    {

+        checkClosed( "first()" );

+

+        if ( list.size() > 0 )

+        {

+            index = start;

+            return true;

+        }

+

+        return false;

+    }

+

+

+    public boolean last() throws IOException

+    {

+        checkClosed( "last()" );

+

+        if ( list.size() > 0 )

+        {

+            index = end - 1;

+            return true;

+        }

+        

+        return false;

+    }

+

+

+    public boolean isFirst() throws IOException

+    {

+        checkClosed( "isFirst()" );

+        return list.size() > 0 && index == start;

+    }

+

+

+    public boolean isLast() throws IOException

+    {

+        checkClosed( "isLast()" );

+        return list.size() > 0 && index == end - 1;

+

+    }

+

+

+    public boolean isAfterLast() throws IOException

+    {

+        checkClosed( "isAfterLast()" );

+        return index == end;

+    }

+

+

+    public boolean isBeforeFirst() throws IOException

+    {

+        checkClosed( "isBeforeFirst()" );

+        return index == -1;

+    }

+

+

+    public boolean isClosed()

+    {

+        return closed;

+    }

+

+

+    public boolean previous() throws IOException

+    {

+        checkClosed( "previous()" );

+

+        // if parked at -1 we cannot go backwards

+        if ( index == -1 )

+        {

+            return false;

+        }

+

+        // if the index moved back is still greater than or eq to start then OK

+        if ( index - 1 >= start )

+        {

+            index--;

+            return true;

+        }

+

+        // if the index currently less than or equal to start we need to park it at -1 and return false

+        if ( index <= start )

+        {

+            index = -1;

+            return false;

+        }

+

+        if ( list.size() <= 0 )

+        {

+            index = -1;

+        }

+

+        return false;

+    }

+

+

+    public boolean next() throws IOException

+    {

+        checkClosed( "next()" );

+

+        // if parked at -1 we advance to the start index and return true

+        if ( list.size() > 0 && index == -1 )

+        {

+            index = start;

+            return true;

+        }

+

+        // if the index plus one is less than the end then increment and return true

+        if ( list.size() > 0 && index + 1 < end )

+        {

+            index++;

+            return true;

+        }

+

+        // if the index plus one is equal to the end then increment and return false

+        if ( list.size() > 0 && index + 1 == end )

+        {

+            index++;

+            return false;

+        }

+

+        if ( list.size() <= 0 )

+        {

+            index = end;

+        }

+

+        return false;

+    }

+

+

+    public E get() throws IOException

+    {

+        checkClosed( "get()" );

+        if ( index < start || index >= end )

+        {

+            throw new IOException( "Cursor not positioned at an element" );

+        }

+

+        return list.get( index );

+    }

+

+

+    public boolean isElementReused()

+    {

+        return true;

+    }

+

+

+    public void close()

+    {

+        closed = true;

+    }

+}

diff --git a/old_trunk/core-shared/src/main/java/org/apache/directory/server/schema/bootstrap/AbstractBootstrapSchema.java b/old_trunk/core-shared/src/main/java/org/apache/directory/server/schema/bootstrap/AbstractBootstrapSchema.java
new file mode 100755
index 0000000..1e7a9de
--- /dev/null
+++ b/old_trunk/core-shared/src/main/java/org/apache/directory/server/schema/bootstrap/AbstractBootstrapSchema.java
@@ -0,0 +1,201 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.bootstrap;
+
+
+import org.apache.directory.server.constants.ServerDNConstants;
+import org.apache.directory.shared.ldap.util.ArrayUtils;
+import org.apache.directory.shared.ldap.util.ClassUtils;
+
+
+/**
+ * Abstract bootstrap schema implementation.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class AbstractBootstrapSchema implements BootstrapSchema
+{
+    protected static final String[] DEFAULT_DEPS = ArrayUtils.EMPTY_STRING_ARRAY;
+    private static final String DEFAULT_OWNER = ServerDNConstants.ADMIN_SYSTEM_DN;
+    private static final String DEFAULT_SCHEMA_NAME = "default";
+    private static final String DEFAULT_PACKAGE_NAME = AbstractBootstrapSchema.class.getPackage().getName();
+
+    private final String owner;
+    private final String schemaName;
+    private final String packageName;
+    private String[] dependencies;
+
+    private transient String baseName;
+    private transient String defaultBaseName;
+
+    private transient String schemaNameCapped;
+
+
+    // ------------------------------------------------------------------------
+    // C O N S T R U C T O R S
+    // ------------------------------------------------------------------------
+
+    protected AbstractBootstrapSchema(String schemaName)
+    {
+        this( null, schemaName, null, null );
+    }
+
+
+    protected AbstractBootstrapSchema(String owner, String schemaName)
+    {
+        this( owner, schemaName, null, null );
+    }
+
+
+    protected AbstractBootstrapSchema(String owner, String schemaName, String packageName)
+    {
+        this( owner, schemaName, packageName, null );
+    }
+
+
+    protected AbstractBootstrapSchema(String owner, String schemaName, String packageName, String[] dependencies)
+    {
+        if ( owner == null )
+        {
+            this.owner = DEFAULT_OWNER;
+        }
+        else
+        {
+            this.owner = owner;
+        }
+
+        if ( schemaName == null )
+        {
+            this.schemaName = DEFAULT_SCHEMA_NAME;
+        }
+        else
+        {
+            this.schemaName = schemaName;
+        }
+
+        if ( packageName == null )
+        {
+            this.packageName = DEFAULT_PACKAGE_NAME;
+        }
+        else
+        {
+            this.packageName = packageName;
+        }
+
+        if ( dependencies == null )
+        {
+            this.dependencies = ArrayUtils.EMPTY_STRING_ARRAY;
+        }
+        else
+        {
+            this.dependencies = dependencies;
+        }
+
+        StringBuffer buf = new StringBuffer();
+        buf.append( Character.toUpperCase( schemaName.charAt( 0 ) ) );
+        buf.append( schemaName.substring( 1, schemaName.length() ) );
+        schemaNameCapped = buf.toString();
+
+        buf.setLength( 0 );
+        buf.append( DEFAULT_PACKAGE_NAME );
+        buf.append( ClassUtils.PACKAGE_SEPARATOR_CHAR );
+        buf.append( schemaNameCapped );
+        defaultBaseName = buf.toString();
+
+        buf.setLength( 0 );
+        buf.append( packageName );
+        buf.append( ClassUtils.PACKAGE_SEPARATOR_CHAR );
+        buf.append( schemaNameCapped );
+        baseName = buf.toString();
+    }
+
+
+    public final String getOwner()
+    {
+        return owner;
+    }
+
+
+    public final String getSchemaName()
+    {
+        return schemaName;
+    }
+
+
+    public final String[] getDependencies()
+    {
+        return dependencies;
+    }
+
+
+    protected final void setDependencies( String[] dependencies )
+    {
+        this.dependencies = dependencies;
+    }
+
+
+    public String getBaseClassName()
+    {
+        return baseName;
+    }
+
+
+    public String getDefaultBaseClassName()
+    {
+        return defaultBaseName;
+    }
+
+
+    public String getFullClassName( ProducerTypeEnum type )
+    {
+        return baseName + type.getName();
+    }
+
+
+    public String getFullDefaultBaseClassName( ProducerTypeEnum type )
+    {
+        return defaultBaseName + type.getName();
+    }
+
+
+    public String getUnqualifiedClassName( ProducerTypeEnum type )
+    {
+        return schemaNameCapped + type.getName();
+    }
+
+
+    public String getPackageName()
+    {
+        return packageName;
+    }
+
+
+    public String getUnqualifiedClassName()
+    {
+        return schemaNameCapped + "Schema";
+    }
+
+
+    public boolean isDisabled()
+    {
+        return false;
+    }
+}
diff --git a/old_trunk/core-shared/src/main/java/org/apache/directory/server/schema/bootstrap/BootstrapSchema.java b/old_trunk/core-shared/src/main/java/org/apache/directory/server/schema/bootstrap/BootstrapSchema.java
new file mode 100755
index 0000000..1cae4b4
--- /dev/null
+++ b/old_trunk/core-shared/src/main/java/org/apache/directory/server/schema/bootstrap/BootstrapSchema.java
@@ -0,0 +1,94 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.bootstrap;
+
+
+/**
+ * A configuration of related Schema objects bundled together and identified as
+ * a group.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public interface BootstrapSchema extends Schema
+{
+    /**
+     * Gets the package name of the schema's object factories.
+     *
+     * @return the name of the schema's package name
+     */
+    String getPackageName();
+
+
+    /**
+     * Gets the base class name for bootstrap Schema class files.  This name
+     * is the schema name with the first character capitalized and qualified
+     * by the package name.  So for a bootstrap schema name of 'bar' within
+     * the 'foo' package would return foo.Bar as the base class name.
+     *
+     * @return the base of all bootstrap schema class names for this schema
+     */
+    String getBaseClassName();
+
+
+    /**
+     * Gets the default base class name for bootstrap Schema class files.  This
+     * name is the schema name with the first character capitalized and qualified
+     * by the default package name.  So for a bootstrap schema name of 'bar'
+     * within the 'foo' package would return foo.Bar as the base class name.
+     *
+     * @return the default base of all bootstrap schema class names for this schema
+     */
+    String getDefaultBaseClassName();
+
+
+    /**
+     * Gets the class name for bootstrap Schema class producer type.
+     *
+     * @return the bootstrap schema class name for a producer type in this schema
+     */
+    String getFullClassName( ProducerTypeEnum type );
+
+
+    /**
+     * If the base class name for the target class does not resolve, we attempt
+     * to load another backup class using this default base class name which
+     * tries another package for the target class factory to load.
+     *
+     * @return the default base class name
+     */
+    String getFullDefaultBaseClassName( ProducerTypeEnum type );
+
+
+    /**
+     * Gets the unqualified class name for bootstrap Schema class producer type.
+     *
+     * @return the bootstrap schema class name for a producer type in this schema
+     */
+    String getUnqualifiedClassName( ProducerTypeEnum type );
+
+
+    /**
+     * Gets the unqualified class name for Schema class.
+     *
+     * @return the bootstrap schema class name
+     */
+    String getUnqualifiedClassName();
+}
diff --git a/old_trunk/core-shared/src/main/java/org/apache/directory/server/schema/bootstrap/ProducerTypeEnum.java b/old_trunk/core-shared/src/main/java/org/apache/directory/server/schema/bootstrap/ProducerTypeEnum.java
new file mode 100755
index 0000000..1623d91
--- /dev/null
+++ b/old_trunk/core-shared/src/main/java/org/apache/directory/server/schema/bootstrap/ProducerTypeEnum.java
@@ -0,0 +1,178 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.bootstrap;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * Type safe enum for an BootstrapProducer tyoes.  This can be take one of the
+ * following values:
+ * <ul>
+ * <li>NormalizerProducer</li>
+ * <li>ComparatorProducer</li>
+ * <li>SyntaxCheckerProducer</li>
+ * <li>SyntaxProducer</li>
+ * <li>MatchingRuleProducer</li>
+ * <li>AttributeTypeProducer</li>
+ * <li>ObjectClassProducer</li>
+ * <li>MatchingRuleUseProducer</li>
+ * <li>DitContentRuleProducer</li>
+ * <li>NameFormProducer</li>
+ * <li>DitStructureRuleProducer</li>
+ * </ul>
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public enum ProducerTypeEnum
+{
+    /** value for Normalizer BootstrapProducers */
+    NORMALIZER_PRODUCER( 0 ),
+
+    /** value for Comparator BootstrapProducers */
+    COMPARATOR_PRODUCER( 1 ),
+    
+    /** value for SyntaxChecker BootstrapProducers */
+    SYNTAX_CHECKER_PRODUCER( 2 ),
+    
+    /** value for Syntax BootstrapProducers */
+    SYNTAX_PRODUCER( 3 ),
+    
+    /** value for MatchingRule BootstrapProducers */
+    MATCHING_RULE_PRODUCER( 4 ),
+    
+    /** value for AttributeType BootstrapProducers */
+    ATTRIBUTE_TYPE_PRODUCER( 5 ),
+    
+    /** value for ObjectClass BootstrapProducers */
+    OBJECT_CLASS_PRODUCER( 6 ),
+    
+    /** value for MatchingRuleUse BootstrapProducers */
+    MATCHING_RULE_USE_PRODUCER( 7 ),
+    
+    /** value for DitContentRule BootstrapProducers */
+    DIT_CONTENT_RULE_PRODUCER( 8 ),
+    
+    /** value for NameForm BootstrapProducers */
+    NAME_FORM_PRODUCER( 9 ),
+    
+    /** value for DitStructureRule BootstrapProducers */
+    DIT_STRUCTURE_RULE_PRODUCER( 10 );
+    
+    private int value;
+
+    /**
+     * Private construct so no other instances can be created other than the
+     * public static constants in this class.
+     *
+     * @param value the integer value of the enumeration.
+     */
+    private ProducerTypeEnum( int value )
+    {
+        this.value = value;
+    }
+
+    /**
+     * @return return the value for this producer type
+     */
+    public int getValue()
+    {
+        return value;
+    }
+
+    /**
+     * Gets the enumeration type for the attributeType producerType string regardless
+     * of case.
+     * 
+     * @param producerType the producerType string
+     * @return the producerType enumeration type
+     */
+    public static ProducerTypeEnum getProducerType( String producerType )
+    {
+        return valueOf( producerType );
+    }
+    
+    /**
+     * 
+     * @return A list of Producer Type
+     */
+    public static List<ProducerTypeEnum> getList()
+    {
+        List<ProducerTypeEnum> list = new ArrayList<ProducerTypeEnum>();
+        
+        list.add(NORMALIZER_PRODUCER );
+        list.add(COMPARATOR_PRODUCER );
+        list.add(SYNTAX_CHECKER_PRODUCER );
+        list.add(SYNTAX_PRODUCER );
+        list.add(MATCHING_RULE_PRODUCER );
+        list.add(ATTRIBUTE_TYPE_PRODUCER );
+        list.add(OBJECT_CLASS_PRODUCER );
+        list.add(MATCHING_RULE_USE_PRODUCER );
+        list.add(DIT_CONTENT_RULE_PRODUCER );
+        list.add(NAME_FORM_PRODUCER );
+        list.add(DIT_STRUCTURE_RULE_PRODUCER );
+        
+        return list;
+    }
+    
+    public String getName()
+    {
+        switch ( this )
+        {
+            case NORMALIZER_PRODUCER :
+                return "NormalizerProducer";
+
+            case COMPARATOR_PRODUCER :
+                return "ComparatorProducer";
+            
+            case SYNTAX_CHECKER_PRODUCER :
+                return "SyntaxCheckerProducer";
+            
+            case SYNTAX_PRODUCER :
+                return "SyntaxProducer";
+            
+            case MATCHING_RULE_PRODUCER :
+                return "MatchingRuleProducer";
+            
+            case ATTRIBUTE_TYPE_PRODUCER :
+                return "AttributeTypeProducer";
+            
+            case OBJECT_CLASS_PRODUCER :
+                return "ObjectClassProducer";
+            
+            case MATCHING_RULE_USE_PRODUCER:
+                return "MatchingRuleUseProducer";
+            
+            case DIT_CONTENT_RULE_PRODUCER:
+                return "DitContentRuleProducer";
+            
+            case NAME_FORM_PRODUCER :
+                return "NameFormProducer";
+            
+            case DIT_STRUCTURE_RULE_PRODUCER :
+                return "DitStructureRuleProducer";
+                
+            default :
+                return "";
+        }
+    }
+}
diff --git a/old_trunk/core-shared/src/main/java/org/apache/directory/server/schema/bootstrap/Schema.java b/old_trunk/core-shared/src/main/java/org/apache/directory/server/schema/bootstrap/Schema.java
new file mode 100644
index 0000000..8f290c4
--- /dev/null
+++ b/old_trunk/core-shared/src/main/java/org/apache/directory/server/schema/bootstrap/Schema.java
@@ -0,0 +1,63 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.bootstrap;
+
+
+/**
+ * Base schema interface.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public interface Schema
+{
+    /**
+     * Checks whether or not this schema is enabled or disabled.
+     * 
+     * @return true if this schema is disabled, false otherwise
+     */
+    boolean isDisabled();
+    
+    /**
+     * Gets the name of the owner of the schema objects within this
+     * Schema.
+     *
+     * @return the identifier for the owner of this set's objects
+     */
+    String getOwner();
+
+
+    /**
+     * Gets the name of the logical schema the objects of this Schema
+     * belong to: e.g. krb5-kdc may be the logical LDAP schema name.
+     *
+     * @return the name of the logical schema
+     */
+    String getSchemaName();
+
+
+    /**
+     * Gets the names of other schemas that the objects within this
+     * Schema depends upon.
+     *
+     * @return the String names of schema dependencies
+     */
+    String[] getDependencies();
+}
diff --git a/old_trunk/core-shared/src/site/site.xml b/old_trunk/core-shared/src/site/site.xml
new file mode 100644
index 0000000..9ec7c16
--- /dev/null
+++ b/old_trunk/core-shared/src/site/site.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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.
+-->
+<!--
+  @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+-->
+<project name="${project.name}">
+    <bannerLeft>
+        <name>${project.name}</name>
+    </bannerLeft>
+    <publishDate position="navigation-bottom" format="dd-MM-yyyy HH:mm" />
+    <body>
+        <menu name="Parent">
+            <item name="Apache Directory ApacheDS" href="../index.html" />
+        </menu>
+        <menu ref="reports" />
+    </body>
+</project>
diff --git a/old_trunk/core-shared/src/test/java/org/apache/directory/server/core/cursor/ListCursorTest.java b/old_trunk/core-shared/src/test/java/org/apache/directory/server/core/cursor/ListCursorTest.java
new file mode 100755
index 0000000..5724cda
--- /dev/null
+++ b/old_trunk/core-shared/src/test/java/org/apache/directory/server/core/cursor/ListCursorTest.java
@@ -0,0 +1,589 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements.  See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership.  The ASF licenses this file

+ * to you 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.

+ */

+package org.apache.directory.server.core.cursor;

+

+

+import junit.framework.TestCase;

+

+import java.io.IOException;

+import java.util.ArrayList;

+import java.util.Collections;

+import java.util.List;

+

+

+/**

+ * Tests the ListCursor class.  The assertXxxx() methods defined in this class

+ * can be collected in an abstract test case class that can be used to test

+ * the behavior of any Cursor implementation down the line.

+ *

+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>

+ * @version $Rev$, $Date$

+ */

+public class ListCursorTest extends TestCase

+{

+    public void testEmptyList() throws IOException

+    {

+        ListCursor<String> cursor = new ListCursor<String>();

+

+        assertFirstLastOnNewCursor( cursor, 0, 0, 0 );

+        assertAbsolute( cursor, 0, 0, 0 );

+        assertRelative( cursor, 0, 0, 0 );

+

+        // close test

+        cursor.close();

+        assertClosed( cursor, "cursor.isCloased() should return true after closing the cursor", true );

+    }

+

+

+    public void testSingleElementList() throws IOException

+    {

+        ListCursor<String> cursor = new ListCursor<String>( Collections.singletonList( "singleton" ) );

+        assertFirstLastOnNewCursor( cursor, 1, 0, 1 );

+        assertAbsolute( cursor, 1, 0, 1 );

+        assertRelative( cursor, 1, 0, 1 );

+        cursor.close();

+

+        // close test

+        cursor.close();

+        assertClosed( cursor, "cursor.isCloased() should return true after closing the cursor", true );

+

+        // bad bounds: start = end is senseless

+        try

+        {

+            cursor = new ListCursor<String>( Collections.singletonList( "singleton" ), 0 );

+            cursor.close();

+            fail( "when the start = end bounds this is senseless and should complain" );

+        }

+        catch ( IllegalArgumentException e )

+        {

+            assertNotNull( e );

+        }

+

+        // bad bounds: start = end is senseless

+        try

+        {

+            cursor = new ListCursor<String>( 1, Collections.singletonList( "singleton" ) );

+            cursor.close();

+            fail( "when the start = end bounds this is senseless and should complain" );

+        }

+        catch ( IllegalArgumentException e )

+        {

+            assertNotNull( e );

+        }

+

+        // bad bounds: start > end is senseless

+        try

+        {

+            cursor = new ListCursor<String>( 5, Collections.singletonList( "singleton" ) );

+            cursor.close();

+            fail( "when the start = end bounds this is senseless and should complain" );

+        }

+        catch ( IllegalArgumentException e )

+        {

+            assertNotNull( e );

+        }

+

+        // bad bounds: end < start is senseless too in another way :)

+        try

+        {

+            cursor = new ListCursor<String>( Collections.singletonList( "singleton" ), -5 );

+            cursor.close();

+            fail( "when the start = end bounds this is senseless and should complain" );

+        }

+        catch ( IllegalArgumentException e )

+        {

+            assertNotNull( e );

+        }

+

+        // bad bounds: start out of range

+        try

+        {

+            cursor = new ListCursor<String>( -5, Collections.singletonList( "singleton" ) );

+            cursor.close();

+            fail( "when the start = end bounds this is senseless and should complain" );

+        }

+        catch ( IllegalArgumentException e )

+        {

+            assertNotNull( e );

+        }

+

+        // bad bounds: end out of range

+        try

+        {

+            cursor = new ListCursor<String>( Collections.singletonList( "singleton" ), 5 );

+            cursor.close();

+            fail( "when the start = end bounds this is senseless and should complain" );

+        }

+        catch ( IllegalArgumentException e )

+        {

+            assertNotNull( e );

+        }

+    }

+

+

+    public void testManyElementList() throws IOException

+    {

+        List<String> list = new ArrayList<String>();

+        list.add( "item 1" );

+        list.add( "item 2" );

+        list.add( "item 3" );

+        list.add( "item 4" );

+        list.add( "item 5" );

+

+        // test with bounds of the list itself

+        ListCursor<String> cursor = new ListCursor<String>( list );

+        assertFirstLastOnNewCursor( cursor, 5, 0, 5 );

+        assertAbsolute( cursor, 5, 0, 5 );

+        assertRelative( cursor, 5, 0, 5 );

+        cursor.close();

+

+        // test with nonzero lower bound

+        cursor = new ListCursor<String>( 1, list );

+        assertFirstLastOnNewCursor( cursor, 5, 1, 5 );

+        assertAbsolute( cursor, 5, 1, 5 );

+        assertRelative( cursor, 5, 1, 5 );

+        cursor.close();

+

+        // test with nonzero lower bound and upper bound

+        cursor = new ListCursor<String>( 1, list, 4 );

+        assertFirstLastOnNewCursor( cursor, 5, 1, 4 );

+        assertAbsolute( cursor, 5, 1, 4 );

+        assertRelative( cursor, 5, 1, 4 );

+

+        // close test

+        cursor.close();

+        assertClosed( cursor, "cursor.isCloased() should return true after closing the cursor", true );

+

+        // bad bounds: start = end is senseless

+        try

+        {

+            cursor = new ListCursor<String>( list, 0 );

+            cursor.close();

+            fail( "when the start = end bounds this is senseless and should complain" );

+        }

+        catch ( IllegalArgumentException e )

+        {

+            assertNotNull( e );

+        }

+

+        // bad bounds: start = end is senseless

+        try

+        {

+            cursor = new ListCursor<String>( 5, list );

+            cursor.close();

+            fail( "when the start = end bounds this is senseless and should complain" );

+        }

+        catch ( IllegalArgumentException e )

+        {

+            assertNotNull( e );

+        }

+

+        // bad bounds: start > end is senseless

+        try

+        {

+            cursor = new ListCursor<String>( 10, list );

+            cursor.close();

+            fail( "when the start = end bounds this is senseless and should complain" );

+        }

+        catch ( IllegalArgumentException e )

+        {

+            assertNotNull( e );

+        }

+

+        // bad bounds: end < start is senseless too in another way :)

+        try

+        {

+            cursor = new ListCursor<String>( list, -5 );

+            cursor.close();

+            fail( "when the start = end bounds this is senseless and should complain" );

+        }

+        catch ( IllegalArgumentException e )

+        {

+            assertNotNull( e );

+        }

+

+        // bad bounds: start out of range

+        try

+        {

+            cursor = new ListCursor<String>( -5, list );

+            cursor.close();

+            fail( "when the start = end bounds this is senseless and should complain" );

+        }

+        catch ( IllegalArgumentException e )

+        {

+            assertNotNull( e );

+        }

+

+        // bad bounds: end out of range

+        try

+        {

+            cursor = new ListCursor<String>( list, 10 );

+            cursor.close();

+            fail( "when the start = end bounds this is senseless and should complain" );

+        }

+        catch ( IllegalArgumentException e )

+        {

+            assertNotNull( e );

+        }

+    }

+

+

+    @SuppressWarnings ( { "ConstantConditions" } )

+    protected void assertFirstLastOnNewCursor( Cursor cursor, int listSize, int lowerBound, int upperBound )

+            throws IOException

+    {

+        assertNotNull( cursor );

+

+        String prefix = "[size, " + listSize + "] [lower, " + lowerBound + "] [upper, " + upperBound + "]: ";

+

+        assertFalse( prefix + "new cursor should not be positioned after last", cursor.isAfterLast() );

+        assertTrue( prefix + "new cursor should be positioned before first", cursor.isBeforeFirst() );

+        assertFalse( prefix + "new cursor should not be closed", cursor.isClosed() );

+        assertFalse( prefix + "new cursor should not be positioned at first", cursor.isFirst() );

+        assertFalse( prefix + "new cursor should not be positioned at last", cursor.isLast() );

+

+        // beforeFirst and afterLast tests

+        cursor.afterLast();

+        assertTrue( prefix + "cursor.afterLast() should return true on isAfterLast()", cursor.isAfterLast() );

+        assertFalse( prefix + "cursor.afterLast() should return false on isBeforeFirst()", cursor.isBeforeFirst() );

+        assertFalse( prefix + "cursor.afterLast() should return false on isFirst()", cursor.isFirst() );

+        assertFalse( prefix + "cursor.afterLast() should return false on isLast()", cursor.isLast() );

+

+        cursor.beforeFirst();

+        assertTrue( prefix + "cursor.beforeFirst() should return true on isBeforeFirst()", cursor.isBeforeFirst() );

+        assertFalse( prefix + "cursor.beforeFirst() should return false on isAfterLast()", cursor.isAfterLast() );

+        assertFalse( prefix + "cursor.beforeFirst() should return false on isFirst()", cursor.isFirst() );

+        assertFalse( prefix + "cursor.beforeFirst() should return false on isLast()", cursor.isLast() );

+

+        // first() tests

+        cursor.afterLast();

+        if ( listSize <= 0 )

+        {

+            assertFalse( "cursor.first() on empty cursor should return false", cursor.first() );

+            assertFalse( "cursor.first() on empty cursor should return false on isFirst()", cursor.isFirst() );

+            assertFalse( "cursor.first() on empty cursor should should change position state", cursor.isBeforeFirst() );

+            assertTrue( "cursor.first() on empty cursor should should change position state", cursor.isAfterLast() );

+        }

+        else

+        {

+            assertTrue( prefix + "cursor.first() should return true", cursor.first() );

+            assertTrue( prefix + "cursor.first() should return true on isFirst()", cursor.isFirst() );

+            assertFalse( prefix + "cursor.first() should change position", cursor.isBeforeFirst() );

+            assertFalse( prefix + "cursor.first() should change position", cursor.isAfterLast() );

+        }

+

+        // last() tests

+        cursor.beforeFirst();

+        if ( listSize <= 0 )

+        {

+            assertFalse( "cursor.last() on empty cursor should return false", cursor.last() );

+            assertFalse( "cursor.last() on empty cursor should return false on isLast()", cursor.isLast() );

+            assertFalse( "cursor.last() on empty cursor should should change position state", cursor.isAfterLast() );

+            assertTrue( "cursor.last() on empty cursor should should change position state", cursor.isBeforeFirst() );

+        }

+        else

+        {

+            assertTrue( prefix + "cursor.last() should return true", cursor.last() );

+            assertTrue( prefix + "cursor.last() should return true on isLast()", cursor.isLast() );

+            assertFalse( prefix + "cursor.last() should not park position after last", cursor.isAfterLast() );

+            assertFalse( prefix + "cursor.last() should not park position before first", cursor.isBeforeFirst() );

+        }

+

+        // next() tests

+        cursor.beforeFirst();

+        if ( listSize <= 0 )

+        {

+            assertFalse( "empty cursor.next() should return false", cursor.next() );

+            assertTrue( "empty cursor.next() should change pos to after last", cursor.isAfterLast() );

+            assertFalse( "empty cursor.next() should change pos to after last", cursor.isBeforeFirst() );

+        }

+        else

+        {

+            assertTrue( prefix + "cursor.next() should return true", cursor.next() );

+            assertTrue( prefix + "cursor.next() should change pos to first element", cursor.isFirst() );

+            assertFalse( prefix + "cursor.next() should not change pos to after last", cursor.isAfterLast() );

+            assertFalse( prefix + "cursor.next() should not change pos to before first", cursor.isBeforeFirst() );

+

+            while( cursor.next() )

+            {

+                assertFalse( prefix + "cursor.next() should not change pos to before first", cursor.isBeforeFirst() );

+                assertFalse( prefix + "cursor.next() should not change pos to first after first advance forward",

+                        cursor.isFirst() );

+            }

+

+            assertTrue( prefix + "cursor.next() failure should put pos to after last", cursor.isAfterLast() );

+        }

+

+        // previous() tests

+        cursor.afterLast();

+        if ( listSize <= 0 )

+        {

+            assertFalse( "empty cursor.previous() should return false", cursor.previous() );

+            assertTrue( "empty cursor.previous() should change pos to before first", cursor.isBeforeFirst() );

+            assertFalse( "empty cursor.previous() should change pos to before first", cursor.isAfterLast() );

+        }

+        else

+        {

+            assertTrue( prefix + "cursor.previous() should return true", cursor.previous() );

+            assertTrue( prefix + "cursor.previous() should change pos to last element", cursor.isLast() );

+            assertFalse( prefix + "cursor.previous() should not change pos to before first", cursor.isBeforeFirst() );

+            assertFalse( prefix + "cursor.previous() should not change pos to after last", cursor.isAfterLast() );

+

+            while( cursor.previous() )

+            {

+                assertFalse( prefix + "cursor.previous() should not change pos to after last", cursor.isAfterLast() );

+                assertFalse( prefix + "cursor.previous() should not change pos to last after first advance backward",

+                        cursor.isLast() );

+            }

+

+            assertTrue( prefix + "cursor.previous() failure should put pos to before first", cursor.isBeforeFirst() );

+        }

+    }

+

+

+    protected void assertAbsolute( Cursor cursor, int listSize, int lowerBound, int upperBound )

+            throws IOException

+    {

+        String prefix = "[size, " + listSize + "] [lower, " + lowerBound + "] [upper, " + upperBound + "]: ";

+

+        // test absolute() advance with change of position below lower bound

+        cursor.afterLast();

+        assertFalse( prefix + "cursor.absolute(" + ( lowerBound - 1 ) +

+                ") should return false and change state to before first", cursor.absolute( lowerBound - 1 ) );

+        assertTrue( prefix + "cursor.relative(" + ( lowerBound - 1 ) +

+                ") should change pos to before first", cursor.isBeforeFirst() );

+        assertFalse( prefix + "cursor.relative(" + ( lowerBound - 1 ) +

+                ") should --NOT-- change pos to after last", cursor.isAfterLast() );

+

+        if ( listSize == 0 )

+        {

+            // Corner case!!!  Technically the 0th index is the 1st element

+            // which is greater than 0 elements which is the size of the list

+            // so technically the observed state change for index = 0 should be

+            // the same as when index > 0.

+            cursor.beforeFirst();

+            assertFalse( "empty cursor.absolute(0) should fail but change state to after last", cursor.absolute( 0 ) );

+            assertFalse( "empty cursor.absolute(0) should change pos to after last", cursor.isBeforeFirst() );

+            assertTrue( "empty cursor.absolute(0) should change pos to after last", cursor.isAfterLast() );

+        }

+

+        // test absolute() advance with change of position above upper bound

+        cursor.beforeFirst();

+        assertFalse( prefix + "cursor.absolute(" + ( upperBound + 1 )

+                + ") should return false but change state to after last", cursor.absolute( upperBound + 1 ) );

+        assertFalse( prefix + "cursor.absolute(" + ( upperBound + 1 ) + ") should change pos to after last",

+                cursor.isBeforeFirst() );

+        assertTrue( prefix + "cursor.absolute(" + ( upperBound + 1 ) + ") should change pos to after last",

+                cursor.isAfterLast() );

+    }

+

+

+    protected void assertRelative( Cursor cursor, int listSize, int lowerBound, int upperBound )

+            throws IOException

+    {

+        String prefix = "[size, " + listSize + "] [lower, " + lowerBound + "] [upper, " + upperBound + "]: ";

+

+        // test relative() advance which changes position below lower bound

+        cursor.afterLast();

+        int relativePos = - ( upperBound - lowerBound + 1 );

+

+        assertFalse( prefix + "cursor.relative(" + relativePos +

+                ") should return false and change state to before first", cursor.relative( - ( upperBound + 1 ) ) );

+        assertTrue( prefix + "cursor.relative(" + relativePos +

+                ") should change pos to before first", cursor.isBeforeFirst() );

+        assertFalse( prefix + "cursor.relative(" + relativePos +

+                ") should --NOT-- change pos to after last", cursor.isAfterLast() );

+

+        // make sure relative(0) does not change pos if begin state is after last

+        cursor.afterLast();

+        assertFalse( prefix + "cursor.relative(0) should return false and have no effect on pos",

+                cursor.relative( 0 ) );

+        assertFalse( prefix + "cursor.relative(0) should have no effect on changing state", cursor.isBeforeFirst() );

+        assertTrue( prefix + "cursor.relative(0) should have no effect on changing state", cursor.isAfterLast() );

+

+        // make sure relative(0) does not change pos if begin state is before first

+        cursor.beforeFirst();

+        assertFalse( prefix + "cursor.relative(0) should return false and have no effect on pos",

+                cursor.relative( 0 ) );

+        assertTrue( prefix + "cursor.relative(0) should have no effect on changing state", cursor.isBeforeFirst() );

+        assertFalse( prefix + "cursor.relative(0) should have no effect on changing state", cursor.isAfterLast() );

+

+        // make relative() advance which changes position above upper bound

+        cursor.beforeFirst();

+        assertFalse( prefix + "cursor.relative(" + ( upperBound + 1 )

+                + ") should return false but change state to after last", cursor.relative( upperBound + 1 ) );

+        assertFalse( prefix + "cursor.relative(" + ( upperBound + 1 ) + ") should change pos to after last",

+                cursor.isBeforeFirst() );

+        assertTrue( prefix + "cursor.relative(" + ( upperBound + 1 ) + ") should change pos to after last",

+                cursor.isAfterLast() );

+    }

+

+

+    protected void assertClosed( Cursor cursor, String msg, boolean expected )

+    {

+        try

+        {

+            assertEquals( msg, expected, cursor.isClosed() );

+        }

+        catch ( IOException e )

+        {

+            fail( "cursor.isClosed() test should not fail after closing the cursor" );

+        }

+

+        try

+        {

+            cursor.close();

+        }

+        catch ( IOException e )

+        {

+            fail( "cursor.close() after closing the cursor should not fail with exceptions" );

+        }

+

+

+        try

+        {

+            cursor.absolute( 1 );

+            fail( "cursor.absolute() after closing the cursor should fail with an IOException" );

+        }

+        catch ( IOException e )

+        {

+            assertNotNull( e );

+        }

+

+        try

+        {

+            cursor.afterLast();

+            fail( "cursor.afterLast() after closing the cursor should fail with an IOException" );

+        }

+        catch ( IOException e )

+        {

+            assertNotNull( e );

+        }

+

+        try

+        {

+            cursor.beforeFirst();

+            fail( "cursor.beforeFirst() after closing the cursor should fail with an IOException" );

+        }

+        catch ( IOException e )

+        {

+            assertNotNull( e );

+        }

+

+        try

+        {

+            cursor.first();

+            fail( "cursor.first() after closing the cursor should fail with an IOException" );

+        }

+        catch ( IOException e )

+        {

+            assertNotNull( e );

+        }

+

+        try

+        {

+            cursor.get();

+            fail( "cursor.get() after closing the cursor should fail with an IOException" );

+        }

+        catch ( IOException e )

+        {

+            assertNotNull( e );

+        }

+

+        try

+        {

+            cursor.isAfterLast();

+            fail( "cursor.isAfterLast() after closing the cursor should fail with an IOException" );

+        }

+        catch ( IOException e )

+        {

+            assertNotNull( e );

+        }

+

+        try

+        {

+            cursor.isBeforeFirst();

+            fail( "cursor.isBeforeFirst() after closing the cursor should fail with an IOException" );

+        }

+        catch ( IOException e )

+        {

+            assertNotNull( e );

+        }

+

+        try

+        {

+            cursor.isFirst();

+            fail( "cursor.isFirst() after closing the cursor should fail with an IOException" );

+        }

+        catch ( IOException e )

+        {

+            assertNotNull( e );

+        }

+

+        try

+        {

+            cursor.isLast();

+            fail( "cursor.isLast() after closing the cursor should fail with an IOException" );

+        }

+        catch ( IOException e )

+        {

+            assertNotNull( e );

+        }

+

+        try

+        {

+            cursor.last();

+            fail( "cursor.last() after closing the cursor should fail with an IOException" );

+        }

+        catch ( IOException e )

+        {

+            assertNotNull( e );

+        }

+

+        try

+        {

+            cursor.next();

+            fail( "cursor.next() after closing the cursor should fail with an IOException" );

+        }

+        catch ( IOException e )

+        {

+            assertNotNull( e );

+        }

+

+        try

+        {

+            cursor.previous();

+            fail( "cursor.previous() after closing the cursor should fail with an IOException" );

+        }

+        catch ( IOException e )

+        {

+            assertNotNull( e );

+        }

+

+        try

+        {

+            cursor.relative( 1 );

+            fail( "cursor.relative() after closing the cursor should fail with an IOException" );

+        }

+        catch ( IOException e )

+        {

+            assertNotNull( e );

+        }

+    }

+}
\ No newline at end of file
diff --git a/old_trunk/core-unit/pom.xml b/old_trunk/core-unit/pom.xml
new file mode 100644
index 0000000..4b3d016
--- /dev/null
+++ b/old_trunk/core-unit/pom.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you 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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.directory.server</groupId>
+    <artifactId>apacheds-parent</artifactId>
+    <version>1.5.4-SNAPSHOT</version>
+  </parent>
+  <artifactId>apacheds-core-unit</artifactId>
+  <name>ApacheDS Core Unit</name>
+  <packaging>jar</packaging>  
+
+  <dependencies>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>provided</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-io</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-core</artifactId>
+      <version>${pom.version}</version>
+    </dependency>  
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-schema-extras</artifactId>
+      <version>${pom.version}</version>
+      <scope>test</scope>
+    </dependency>  
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-bootstrap-partition</artifactId>
+      <version>${pom.version}</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <configuration>
+          <forkMode>pertest</forkMode>
+          <excludes>
+            <exclude>**/*PTest.java</exclude>
+          </excludes>
+          <argLine>-Xmx1024m</argLine>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+
+</project>
+
diff --git a/old_trunk/core-unit/src/main/java/org/apache/directory/server/core/unit/AbstractAdminTestCase.java b/old_trunk/core-unit/src/main/java/org/apache/directory/server/core/unit/AbstractAdminTestCase.java
new file mode 100644
index 0000000..7029031
--- /dev/null
+++ b/old_trunk/core-unit/src/main/java/org/apache/directory/server/core/unit/AbstractAdminTestCase.java
@@ -0,0 +1,39 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.unit;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.constants.ServerDNConstants;
+
+
+/**
+ * A simple testcase for testing JNDI provider functionality.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public abstract class AbstractAdminTestCase extends AbstractTestCase
+{
+    protected AbstractAdminTestCase() throws NamingException
+    {
+        super( ServerDNConstants.ADMIN_SYSTEM_DN, "secret" );
+    }
+}
diff --git a/old_trunk/core-unit/src/main/java/org/apache/directory/server/core/unit/AbstractTestCase.java b/old_trunk/core-unit/src/main/java/org/apache/directory/server/core/unit/AbstractTestCase.java
new file mode 100644
index 0000000..f1d4f0b
--- /dev/null
+++ b/old_trunk/core-unit/src/main/java/org/apache/directory/server/core/unit/AbstractTestCase.java
@@ -0,0 +1,433 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.unit;
+
+
+import junit.framework.TestCase;
+import org.apache.commons.io.FileUtils;
+import org.apache.directory.server.constants.ServerDNConstants;
+import org.apache.directory.server.core.DefaultDirectoryService;
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.ldif.LdifEntry;
+import org.apache.directory.shared.ldap.ldif.LdifReader;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.naming.Context;
+import javax.naming.NamingException;
+import javax.naming.ldap.InitialLdapContext;
+import javax.naming.ldap.LdapContext;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+
+
+/**
+ * A simple testcase for testing JNDI provider functionality.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public abstract class AbstractTestCase extends TestCase
+{
+    public static final Logger LOG = LoggerFactory.getLogger( AbstractTestCase.class );
+
+    public static final String LDIF = 
+        "dn: uid=akarasulu,ou=users,ou=system\n" + 
+        "cn: Alex Karasulu\n" +
+        "sn: Karasulu\n" + 
+        "givenname: Alex\n" + 
+        "objectclass: top\n" + 
+        "objectclass: person\n" +
+        "objectclass: organizationalPerson\n" + 
+        "objectclass: inetOrgPerson\n" + 
+        "ou: Engineering\n" + 
+        "ou: People\n" +
+        "l: Bogusville\n" + 
+        "uid: akarasulu\n" + 
+        "mail: akarasulu@apache.org\n" +
+        "telephonenumber: +1 408 555 4798\n" + 
+        "facsimiletelephonenumber: +1 408 555 9751\n" + 
+        "roomnumber: 4612\n" +
+        "userpassword: test\n";
+
+    protected final String username;
+
+    protected final String password;
+
+    /** the context root for the system partition */
+    protected LdapContext sysRoot;
+
+    /** the context root for the schema partition */
+    protected LdapContext schemaRoot;
+    
+    /** the rootDSE context for the server */
+    protected LdapContext rootDSE;
+
+    /** flag whether to delete database files for each test or not */
+    protected boolean doDelete = true;
+
+    /** A testEntries of entries as Attributes to add to the DIT for testing */
+    protected List<LdifEntry> testEntries = new ArrayList<LdifEntry>();
+
+    /** An optional LDIF file path if set and present is read to add more test entries */
+    private String ldifPath;
+
+    /** Load resources relative to this class */
+    private Class<?> loadClass;
+
+    private Hashtable<String,Object> overrides = new Hashtable<String,Object>();
+
+    protected Registries registries;
+
+    protected DirectoryService service;
+
+
+    protected AbstractTestCase( String username, String password ) throws NamingException
+    {
+        if ( username == null || password == null )
+        {
+            throw new NullPointerException();
+        }
+
+        this.username = username;
+        this.password = password;
+        this.service = new DefaultDirectoryService();
+    }
+
+
+    /**
+     * Sets the LDIF path as a relative resource path to use with the
+     * loadClass parameter to load the resource.
+     *
+     * @param ldifPath the relative resource path to the LDIF file
+     * @param loadClass the class used to load the LDIF as a resource stream
+     */
+    protected void setLdifPath( String ldifPath, Class<?> loadClass )
+    {
+        this.loadClass = loadClass;
+        this.ldifPath = ldifPath;
+    }
+
+
+    /**
+     * Sets the LDIF path to use.  If the path is relative to this class then it
+     * is first tested
+     *
+     * @param ldifPath the path to the LDIF file
+     */
+    protected void setLdifPath( String ldifPath )
+    {
+        this.ldifPath = ldifPath;
+    }
+
+
+    /**
+     * Get's the initial context factory for the provider's ou=system context
+     * root.
+     *
+     * @see junit.framework.TestCase#setUp()
+     */
+    protected void setUp() throws Exception
+    {
+        super.setUp();
+
+        // -------------------------------------------------------------------
+        // Bypass normal startup if changelog has been enabled and the server
+        // has already started.
+        // -------------------------------------------------------------------
+
+        /// still working on it!!!
+//        if ( service.getChangeLog().isEnabled() )
+//        {
+//            startTag = service.getChangeLog().tag();
+//
+//            if ( service.isStarted() )
+//            {
+//                return;
+//            }
+//        }
+
+        // -------------------------------------------------------------------
+        // Add a single test entry
+        // -------------------------------------------------------------------
+
+        LdifReader reader = new LdifReader();
+        List<LdifEntry> entries = reader.parseLdif( LDIF );
+        LdifEntry entry = entries.get(0);
+        testEntries.add( entry );
+
+        // -------------------------------------------------------------------
+        // Add more from an optional LDIF file if they exist
+        // -------------------------------------------------------------------
+
+        InputStream in = null;
+        if ( loadClass != null && ldifPath == null )
+        {
+            in = loadClass.getResourceAsStream( getName() + ".ldif" );
+        }
+        else if ( loadClass == null && ldifPath != null )
+        {
+            File ldifFile = new File( ldifPath );
+            if ( ldifFile.exists() )
+            {
+                //noinspection UnusedAssignment
+                in = new FileInputStream( ldifPath );
+            }
+            else
+            {
+                //noinspection UnusedAssignment
+                in = getClass().getResourceAsStream( ldifPath );
+            }
+            throw new FileNotFoundException( ldifPath );
+        }
+        else if ( loadClass != null )
+        {
+            in = loadClass.getResourceAsStream( ldifPath );
+        }
+
+        if ( in != null )
+        {
+            for ( LdifEntry ldifEntry:new LdifReader( in ) )
+            {
+                testEntries.add( ldifEntry );
+            }
+        }
+
+        // -------------------------------------------------------------------
+        // Add key for extra entries to the testEntries of extras
+        // -------------------------------------------------------------------
+
+        service.setTestEntries( testEntries );
+        service.setShutdownHookEnabled( false );
+        doDelete( service.getWorkingDirectory() );
+        service.startup();
+        setContextRoots( username, password );
+        registries = service.getRegistries();
+    }
+
+    
+    /**
+     * Restarts the server without loading data when it has been shutdown.
+     * @throws NamingException if the restart fails
+     */
+    protected void restart() throws NamingException
+    {
+        if ( service == null )
+        {
+            service = new DefaultDirectoryService();
+        }
+        service.setShutdownHookEnabled( false );
+        service.startup();
+        setContextRoots( username, password );
+    }
+    
+
+    /**
+     * Deletes the working directory.
+     *
+     * @param wkdir the working directory to delete
+     * @throws IOException if the working directory cannot be deleted
+     */
+    protected void doDelete( File wkdir ) throws IOException
+    {
+        if ( doDelete )
+        {
+            if ( wkdir.exists() )
+            {
+                try
+                {
+                    FileUtils.deleteDirectory( wkdir );
+                }
+                catch ( IOException e )
+                {
+                    LOG.error( "Failed to delete the working directory.", e );
+                }
+            }
+            if ( wkdir.exists() )
+            {
+                throw new IOException( "Failed to delete: " + wkdir );
+            }
+        }
+    }
+
+
+    /**
+     * Sets and returns the system root.  Values of user and password used to
+     * set the respective JNDI properties.  These values can be overriden by the
+     * overrides properties.
+     *
+     * @param user the username for authenticating as this user
+     * @param passwd the password of the user
+     * @throws NamingException if there is a failure of any kind
+     */
+    protected void setContextRoots( String user, String passwd ) throws NamingException
+    {
+        Hashtable<String,Object> env = new Hashtable<String,Object>();
+        env.put(  DirectoryService.JNDI_KEY, service );
+        env.put( Context.SECURITY_PRINCIPAL, user );
+        env.put( Context.SECURITY_CREDENTIALS, passwd );
+        env.put( Context.SECURITY_AUTHENTICATION, "simple" );
+        setContextRoots( env );
+    }
+
+
+    /**
+     * Sets the system root taking into account the extras and overrides
+     * properties.  In between these it sets the properties for the working
+     * directory, the provider URL and the JNDI InitialContexFactory to use.
+     *
+     * @param env an environment to use while setting up the system root.
+     * @throws NamingException if there is a failure of any kind
+     */
+    protected void setContextRoots( Hashtable<String,Object> env ) throws NamingException
+    {
+        Hashtable<String,Object> envFinal = new Hashtable<String,Object>( env );
+        if ( !envFinal.containsKey( Context.PROVIDER_URL ) )
+        {
+            envFinal.put( Context.PROVIDER_URL, ServerDNConstants.SYSTEM_DN );
+        }
+
+        envFinal.put( Context.INITIAL_CONTEXT_FACTORY, "org.apache.directory.server.core.jndi.CoreContextFactory" );
+        envFinal.putAll( overrides );
+
+        // We have to initiate the first run as an admin at least.
+        Hashtable<String,Object> adminEnv = new Hashtable<String,Object>( envFinal );
+        adminEnv.put( Context.SECURITY_PRINCIPAL, ServerDNConstants.ADMIN_SYSTEM_DN );
+        adminEnv.put( Context.SECURITY_CREDENTIALS, "secret" );
+        adminEnv.put( Context.SECURITY_AUTHENTICATION, "simple" );
+        adminEnv.put( Context.INITIAL_CONTEXT_FACTORY, "org.apache.directory.server.core.jndi.CoreContextFactory" );
+        adminEnv.put( DirectoryService.JNDI_KEY, service );
+        new InitialLdapContext( adminEnv, null );
+
+        // OK, now let's get an appropriate context.
+        sysRoot = new InitialLdapContext( envFinal, null );
+
+        envFinal.put( Context.PROVIDER_URL, ServerDNConstants.OU_SCHEMA_DN );
+        schemaRoot = new InitialLdapContext( envFinal, null );
+        
+        envFinal.put( Context.PROVIDER_URL, "" );
+        rootDSE = new InitialLdapContext( envFinal, null );
+    }
+
+
+    /**
+     * Overrides default JNDI environment properties.  Please call this method
+     * to override any JNDI environment properties this test case will set.
+     * @param key the key of the hashtable entry to add to the environment
+     * @param value the value of the hashtable entry to add to the environment
+     */
+    protected void overrideEnvironment( String key, Object value )
+    {
+        overrides.put( key, value );
+    }
+
+
+    protected Hashtable getOverriddenEnvironment()
+    {
+        return ( Hashtable ) overrides.clone();
+    }
+
+
+    /**
+     * Issues a shutdown request to the server.
+     */
+    protected void shutdown()
+    {
+        try
+        {
+            service.shutdown();
+        }
+        catch ( Exception e )
+        {
+            LOG.error( "Encountered an error while shutting down directory service.", e );
+        } 
+        sysRoot = null;
+        Runtime.getRuntime().gc();
+    }
+
+
+    /**
+     * Issues a sync request to the server.
+     */
+    protected void sync()
+    {
+        try
+        {
+            service.sync();
+        }
+        catch ( Exception e )
+        {
+            LOG.warn( "Encountered error while syncing.", e );
+        } 
+    }
+
+    
+    /**
+     * Sets the system context root to null.
+     *
+     * @see junit.framework.TestCase#tearDown()
+     */
+    protected void tearDown() throws Exception
+    {
+        super.tearDown();
+        shutdown();
+        testEntries.clear();
+        ldifPath = null;
+        loadClass = null;
+        overrides.clear();
+        service = new DefaultDirectoryService();
+        doDelete( service.getWorkingDirectory() );
+    }
+
+
+    protected void setLoadClass( Class<?> loadClass )
+    {
+        this.loadClass = loadClass;
+    }
+    
+    
+    /**
+     * Inject an ldif String into the server. DN must be relative to the
+     * root.
+     * 
+     * @param ldif the ldif containing entries to add to the server.
+     * @throws NamingException if there is a problem adding the entries from the LDIF
+     */
+    protected void injectEntries( String ldif ) throws NamingException
+    {
+        LdifReader reader = new LdifReader();
+        List<LdifEntry> entries = reader.parseLdif( ldif );
+
+        for ( LdifEntry entry : entries )
+        {
+            rootDSE.createSubcontext( new LdapDN( entry.getDn() ), entry.getAttributes() );
+        }
+    }
+}
diff --git a/old_trunk/core-unit/src/main/java/org/apache/directory/server/core/unit/IntegrationUtils.java b/old_trunk/core-unit/src/main/java/org/apache/directory/server/core/unit/IntegrationUtils.java
new file mode 100644
index 0000000..fbd42ca
--- /dev/null
+++ b/old_trunk/core-unit/src/main/java/org/apache/directory/server/core/unit/IntegrationUtils.java
@@ -0,0 +1,175 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements.  See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership.  The ASF licenses this file

+ * to you 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.

+ */

+package org.apache.directory.server.core.unit;

+

+

+import org.apache.commons.io.FileUtils;

+import org.apache.directory.shared.ldap.ldif.ChangeType;

+import org.apache.directory.shared.ldap.ldif.LdifEntry;

+import org.apache.directory.shared.ldap.message.AttributeImpl;

+import org.apache.directory.shared.ldap.name.LdapDN;

+import org.slf4j.Logger;

+import org.slf4j.LoggerFactory;

+

+import javax.naming.InvalidNameException;

+import javax.naming.NamingException;

+import javax.naming.ldap.LdapContext;

+import java.io.File;

+import java.io.IOException;

+

+

+/**

+ * Integration test utility methods.

+ *

+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>

+ * @version $Rev$, $Date$

+ */

+public class IntegrationUtils

+{

+    private static final Logger LOG = LoggerFactory.getLogger( IntegrationUtils.class );

+

+

+    /**

+     * Deletes the working directory.

+     *

+     * @param wkdir the working directory to delete

+     * @throws IOException if the working directory cannot be deleted

+     */

+    public static void doDelete( File wkdir ) throws IOException

+    {

+        if ( wkdir.exists() )

+        {

+            try

+            {

+                FileUtils.deleteDirectory( wkdir );

+            }

+            catch ( IOException e )

+            {

+                LOG.error( "Failed to delete the working directory.", e );

+            }

+        }

+        if ( wkdir.exists() )

+        {

+            throw new IOException( "Failed to delete: " + wkdir );

+        }

+    }

+

+

+    public static LdifEntry getUserAddLdif() throws InvalidNameException

+    {

+        return getUserAddLdif( "uid=akarasulu,ou=users,ou=system", "test".getBytes(), "Alex Karasulu", "Karasulu" );

+    }

+

+

+

+    public static void apply( LdapContext root, LdifEntry entry ) throws NamingException

+    {

+        LdapDN dn = new LdapDN( entry.getDn() );

+

+        switch( entry.getChangeType().getChangeType() )

+        {

+            case( ChangeType.ADD_ORDINAL ):

+                root.createSubcontext( dn, entry.getAttributes() );

+                break;

+            case( ChangeType.DELETE_ORDINAL ):

+                root.destroySubcontext( entry.getDn() );

+                break;

+            case( ChangeType.MODDN_ORDINAL ):

+                LdapDN target = new LdapDN( entry.getNewSuperior() );

+                if ( entry.getNewRdn() != null )

+                {

+                    target.add( entry.getNewRdn() );

+                }

+                else

+                {

+                    target.add( dn.getRdn().toString() );

+                }

+

+                if ( entry.isDeleteOldRdn() )

+                {

+                    root.addToEnvironment( "java.naming.ldap.deleteRDN", "true" );

+                }

+                else

+                {

+                    root.addToEnvironment( "java.naming.ldap.deleteRDN", "false" );

+                }

+

+                root.rename( dn, target );

+                break;

+            case( ChangeType.MODRDN_ORDINAL ):

+                target = ( LdapDN ) dn.clone();

+                target.remove( dn.size() - 1 );

+                target.add( entry.getNewRdn() );

+

+                if ( entry.isDeleteOldRdn() )

+                {

+                    root.addToEnvironment( "java.naming.ldap.deleteRDN", "true" );

+                }

+                else

+                {

+                    root.addToEnvironment( "java.naming.ldap.deleteRDN", "false" );

+                }

+

+                root.rename( dn, target );

+                break;

+            case( ChangeType.MODIFY_ORDINAL ):

+                root.modifyAttributes( dn, entry.getModificationItemsArray() );

+                break;

+

+            default:

+                throw new IllegalStateException( "Unidentified change type value: " + entry.getChangeType() );

+        }

+    }

+

+

+    public static LdifEntry getUserAddLdif( String dnstr, byte[] password, String cn, String sn )

+            throws InvalidNameException

+    {

+        LdapDN dn = new LdapDN( dnstr );

+        LdifEntry ldif = new LdifEntry();

+        ldif.setDn( dnstr );

+        ldif.setChangeType( ChangeType.Add );

+

+        AttributeImpl attr = new AttributeImpl( "objectClass", "top" );

+        attr.add( "person" );

+        attr.add( "organizationalPerson" );

+        attr.add( "inetOrgPerson" );

+        ldif.addAttribute( attr );

+

+        attr = new AttributeImpl( "ou", "Engineering" );

+        attr.add( "People" );

+        ldif.addAttribute( attr );

+

+        String uid = ( String ) dn.getRdn().getValue();

+        ldif.putAttribute( "uid", uid );

+

+        ldif.putAttribute( "l", "Bogusville" );

+        ldif.putAttribute( "cn", cn );

+        ldif.putAttribute( "sn", sn );

+        ldif.putAttribute( "mail", uid + "@apache.org" );

+        ldif.putAttribute( "telephoneNumber", "+1 408 555 4798" );

+        ldif.putAttribute( "facsimileTelephoneNumber", "+1 408 555 9751" );

+        ldif.putAttribute( "roomnumber", "4612" );

+        ldif.putAttribute( "userPassword", password );

+

+        String givenName = cn.split( " " )[0];

+        ldif.putAttribute( "givenName", givenName );

+        return ldif;

+    }

+}

diff --git a/old_trunk/core-unit/src/site/site.xml b/old_trunk/core-unit/src/site/site.xml
new file mode 100644
index 0000000..9ec7c16
--- /dev/null
+++ b/old_trunk/core-unit/src/site/site.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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.
+-->
+<!--
+  @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+-->
+<project name="${project.name}">
+    <bannerLeft>
+        <name>${project.name}</name>
+    </bannerLeft>
+    <publishDate position="navigation-bottom" format="dd-MM-yyyy HH:mm" />
+    <body>
+        <menu name="Parent">
+            <item name="Apache Directory ApacheDS" href="../index.html" />
+        </menu>
+        <menu ref="reports" />
+    </body>
+</project>
diff --git a/old_trunk/core-unit/src/test/resources/log4j.properties b/old_trunk/core-unit/src/test/resources/log4j.properties
new file mode 100644
index 0000000..c3be193
--- /dev/null
+++ b/old_trunk/core-unit/src/test/resources/log4j.properties
@@ -0,0 +1,22 @@
+#############################################################################
+#    Licensed to the Apache Software Foundation (ASF) under one or more
+#    contributor license agreements.  See the NOTICE file distributed with
+#    this work for additional information regarding copyright ownership.
+#    The ASF licenses this file to You 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.
+#############################################################################
+log4j.rootCategory=OFF, stdout
+
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=[%d{HH:mm:ss}] %p [%c] - %m%n
+
diff --git a/old_trunk/core/pom.xml b/old_trunk/core/pom.xml
new file mode 100644
index 0000000..45de586
--- /dev/null
+++ b/old_trunk/core/pom.xml
@@ -0,0 +1,161 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you 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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.directory.server</groupId>
+    <artifactId>apacheds-parent</artifactId>
+    <version>1.5.4-SNAPSHOT</version>
+  </parent>
+  <artifactId>apacheds-core</artifactId>
+  <name>ApacheDS Core</name>
+  <packaging>jar</packaging>
+
+  <description>
+     Server's core contains the JNDI provider, interceptors, schema, and
+     database subsystems.  The core is the heart of the server without protocols
+     enabled.
+  </description>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-io</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-schema-bootstrap</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-core-entry</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-schema-registries</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-core-shared</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-core-constants</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-utils</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-jdbm-store</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-btree-base</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-bootstrap-extract</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-bootstrap-partition</artifactId>
+      <version>${pom.version}</version>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.directory.shared</groupId>
+      <artifactId>shared-bouncycastle-reduced</artifactId>
+    </dependency>
+
+<!--
+    <dependency>
+      <groupId>bouncycastle</groupId>
+      <artifactId>bcprov-jdk15</artifactId>
+    </dependency>
+-->
+
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <configuration> 
+          <systemProperties>
+            <property>
+              <name>workingDirectory</name>
+              <value>${basedir}/target/server-work</value>
+            </property>
+          </systemProperties>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-source-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>attach-sources</id>
+            <phase>verify</phase>
+            <goals>
+              <goal>jar</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+
+    <resources>
+      <resource>
+        <directory>src/main/resources</directory>
+        <filtering>true</filtering>
+        <excludes>
+          <exclude>**/*.gif</exclude>
+        </excludes>
+      </resource>
+    </resources>
+  </build>
+</project>
+
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/DefaultDirectoryService.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/DefaultDirectoryService.java
new file mode 100644
index 0000000..28e856e
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/DefaultDirectoryService.java
@@ -0,0 +1,1501 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core;
+
+
+import org.apache.directory.server.constants.ServerDNConstants;
+import org.apache.directory.server.core.authn.AuthenticationInterceptor;
+import org.apache.directory.server.core.authn.LdapPrincipal;
+import org.apache.directory.server.core.authz.AciAuthorizationInterceptor;
+import org.apache.directory.server.core.authz.DefaultAuthorizationInterceptor;
+import org.apache.directory.server.core.changelog.ChangeLog;
+import org.apache.directory.server.core.changelog.ChangeLogEvent;
+import org.apache.directory.server.core.changelog.ChangeLogInterceptor;
+import org.apache.directory.server.core.changelog.DefaultChangeLog;
+import org.apache.directory.server.core.changelog.Tag;
+import org.apache.directory.server.core.collective.CollectiveAttributeInterceptor;
+import org.apache.directory.server.core.cursor.Cursor;
+import org.apache.directory.server.core.entry.DefaultServerEntry;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerEntryUtils;
+import org.apache.directory.server.core.event.EventInterceptor;
+import org.apache.directory.server.core.exception.ExceptionInterceptor;
+import org.apache.directory.server.core.interceptor.Interceptor;
+import org.apache.directory.server.core.interceptor.InterceptorChain;
+import org.apache.directory.server.core.interceptor.context.AddContextPartitionOperationContext;
+import org.apache.directory.server.core.interceptor.context.AddOperationContext;
+import org.apache.directory.server.core.interceptor.context.EntryOperationContext;
+import org.apache.directory.server.core.interceptor.context.LookupOperationContext;
+import org.apache.directory.server.core.interceptor.context.RemoveContextPartitionOperationContext;
+import org.apache.directory.server.core.jndi.DeadContext;
+import org.apache.directory.server.core.jndi.ServerLdapContext;
+import org.apache.directory.server.core.normalization.NormalizationInterceptor;
+import org.apache.directory.server.core.operational.OperationalAttributeInterceptor;
+import org.apache.directory.server.core.partition.DefaultPartitionNexus;
+import org.apache.directory.server.core.partition.Partition;
+import org.apache.directory.server.core.partition.PartitionNexus;
+import org.apache.directory.server.core.partition.impl.btree.BTreePartition;
+import org.apache.directory.server.core.partition.impl.btree.Index;
+import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmIndex;
+import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmPartition;
+import org.apache.directory.server.core.referral.ReferralInterceptor;
+import org.apache.directory.server.core.schema.PartitionSchemaLoader;
+import org.apache.directory.server.core.schema.SchemaInterceptor;
+import org.apache.directory.server.core.schema.SchemaOperationControl;
+import org.apache.directory.server.core.schema.SchemaPartitionDao;
+import org.apache.directory.server.core.schema.SchemaService;
+import org.apache.directory.server.core.security.TlsKeyGenerator;
+import org.apache.directory.server.core.subtree.SubentryInterceptor;
+import org.apache.directory.server.core.trigger.TriggerInterceptor;
+import org.apache.directory.server.schema.SerializableComparator;
+import org.apache.directory.server.schema.bootstrap.ApacheSchema;
+import org.apache.directory.server.schema.bootstrap.ApachemetaSchema;
+import org.apache.directory.server.schema.bootstrap.BootstrapSchemaLoader;
+import org.apache.directory.server.schema.bootstrap.CoreSchema;
+import org.apache.directory.server.schema.bootstrap.Schema;
+import org.apache.directory.server.schema.bootstrap.SystemSchema;
+import org.apache.directory.server.schema.bootstrap.partition.DbFileListing;
+import org.apache.directory.server.schema.bootstrap.partition.SchemaPartitionExtractor;
+import org.apache.directory.server.schema.registries.DefaultOidRegistry;
+import org.apache.directory.server.schema.registries.DefaultRegistries;
+import org.apache.directory.server.schema.registries.OidRegistry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.NotImplementedException;
+import org.apache.directory.shared.ldap.constants.AuthenticationLevel;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.exception.LdapAuthenticationNotSupportedException;
+import org.apache.directory.shared.ldap.exception.LdapConfigurationException;
+import org.apache.directory.shared.ldap.exception.LdapNamingException;
+import org.apache.directory.shared.ldap.exception.LdapNoPermissionException;
+import org.apache.directory.shared.ldap.ldif.ChangeType;
+import org.apache.directory.shared.ldap.ldif.LdifEntry;
+import org.apache.directory.shared.ldap.ldif.LdifReader;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.schema.OidNormalizer;
+import org.apache.directory.shared.ldap.util.DateUtils;
+import org.apache.directory.shared.ldap.util.StringTools;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.naming.Context;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.ldap.LdapContext;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+
+/**
+ * Default implementation of {@link DirectoryService}.
+ * 
+ * @org.apache.xbean.XBean
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class DefaultDirectoryService implements DirectoryService
+{
+    private static final Logger LOG = LoggerFactory.getLogger( DefaultDirectoryService.class );
+
+    private static final String ILLEGAL_STATE_MSG = "Something has got to be severely " +
+    "wrong with the core packaging\nor the build to have " +
+    "resulted in this exception.";
+    
+    private SchemaService schemaService;
+
+    /** the registries for system schema objects */
+    private Registries registries;
+    
+    /** the root nexus */
+    private DefaultPartitionNexus partitionNexus;
+
+    /** whether or not server is started for the first time */
+    private boolean firstStart;
+
+    /** The interceptor (or interceptor chain) for this service */
+    private InterceptorChain interceptorChain;
+
+    /** whether or not this instance has been shutdown */
+    private boolean started;
+
+    /** the change log service */
+    private ChangeLog changeLog;
+
+    private LdapDN adminDn;
+
+    /** remove me after implementation is completed */
+    private static final String PARTIAL_IMPL_WARNING =
+            "WARNING: the changelog is only partially operational and will revert\n" +
+            "state without consideration of who made the original change.  All reverting " +
+            "changes are made by the admin user.\n Furthermore the used controls are not at " +
+            "all taken into account";
+
+    // ------------------------------------------------------------------------
+    // Constructor
+    // ------------------------------------------------------------------------
+
+
+    /**
+     * Creates a new instance.
+     */
+    public DefaultDirectoryService() 
+    {
+        setDefaultInterceptorConfigurations();
+        changeLog = new DefaultChangeLog();
+        
+        // --------------------------------------------------------------------
+        // Load the bootstrap schemas to start up the schema partition
+        // --------------------------------------------------------------------
+
+        // setup temporary loader and temp registry 
+        BootstrapSchemaLoader loader = new BootstrapSchemaLoader();
+        OidRegistry oidRegistry = new DefaultOidRegistry();
+        registries = new DefaultRegistries( "bootstrap", loader, oidRegistry );
+
+        // load essential bootstrap schemas 
+        Set<Schema> bootstrapSchemas = new HashSet<Schema>();
+        bootstrapSchemas.add( new ApachemetaSchema() );
+        bootstrapSchemas.add( new ApacheSchema() );
+        bootstrapSchemas.add( new CoreSchema() );
+        bootstrapSchemas.add( new SystemSchema() );
+        
+        
+        try
+        {
+            loader.loadWithDependencies( bootstrapSchemas, registries );
+        }
+        catch ( NamingException e )
+        {
+            throw new IllegalStateException( ILLEGAL_STATE_MSG, e );
+        }
+
+        // run referential integrity tests
+        List<Throwable> errors = registries.checkRefInteg();
+        if ( !errors.isEmpty() )
+        {
+            NamingException e = new NamingException();
+            e.setRootCause( errors.get( 0 ) );
+            throw new IllegalStateException( ILLEGAL_STATE_MSG, e );
+        }
+        
+        SerializableComparator.setRegistry( registries.getComparatorRegistry() );
+    }
+
+
+    // ------------------------------------------------------------------------
+    // C O N F I G U R A T I O N   M E T H O D S
+    // ------------------------------------------------------------------------
+
+
+    public static final int MAX_SIZE_LIMIT_DEFAULT = 100;
+    public static final int MAX_TIME_LIMIT_DEFAULT = 10000;
+
+    private String instanceId;
+    private File workingDirectory = new File( "server-work" );
+    private boolean exitVmOnShutdown = true; // allow by default
+    private boolean shutdownHookEnabled = true; // allow by default
+    private boolean allowAnonymousAccess = true; // allow by default
+    private boolean accessControlEnabled; // off by default
+    private boolean denormalizeOpAttrsEnabled; // off by default
+    private int maxSizeLimit = MAX_SIZE_LIMIT_DEFAULT; // set to default value
+    private int maxTimeLimit = MAX_TIME_LIMIT_DEFAULT; // set to default value (milliseconds)
+    private List<Interceptor> interceptors;
+    private Partition systemPartition;
+    private Set<Partition> partitions = new HashSet<Partition>();
+    private List<? extends LdifEntry> testEntries = new ArrayList<LdifEntry>(); // List<Attributes>
+
+
+
+    public void setInstanceId( String instanceId )
+    {
+        this.instanceId = instanceId;
+    }
+
+
+    public String getInstanceId()
+    {
+        return instanceId;
+    }
+
+
+    /**
+     * Gets the {@link Partition}s used by this DirectoryService.
+     *
+     * @org.apache.xbean.Property nestedType="org.apache.directory.server.core.partition.Partition"
+     * @return the set of partitions used
+     */
+    public Set<? extends Partition> getPartitions()
+    {
+        Set<Partition> cloned = new HashSet<Partition>();
+        cloned.addAll( partitions );
+        return cloned;
+    }
+
+
+    /**
+     * Sets {@link Partition}s used by this DirectoryService.
+     *
+     * @org.apache.xbean.Property nestedType="org.apache.directory.server.core.partition.Partition"
+     * @param partitions the partitions to used
+     */
+    public void setPartitions( Set<? extends Partition> partitions )
+    {
+        Set<Partition> cloned = new HashSet<Partition>();
+        cloned.addAll( partitions );
+        Set<String> names = new HashSet<String>();
+        for ( Partition partition : cloned )
+        {
+            String id = partition.getId();
+            if ( names.contains( id ) )
+            {
+                LOG.warn( "Encountered duplicate partition {} identifier.", id );
+            }
+            names.add( id );
+        }
+
+        this.partitions = cloned;
+    }
+
+
+    /**
+     * Returns <tt>true</tt> if access control checks are enabled.
+     *
+     * @return true if access control checks are enabled, false otherwise
+     */
+    public boolean isAccessControlEnabled()
+    {
+        return accessControlEnabled;
+    }
+
+
+    /**
+     * Sets whether to enable basic access control checks or not.
+     *
+     * @param accessControlEnabled true to enable access control checks, false otherwise
+     */
+    public void setAccessControlEnabled( boolean accessControlEnabled )
+    {
+        this.accessControlEnabled = accessControlEnabled;
+    }
+
+
+    /**
+     * Returns <tt>true</tt> if anonymous access is allowed on entries besides the RootDSE.
+     * If the access control subsystem is enabled then access to some entries may not be
+     * allowed even when full anonymous access is enabled.
+     *
+     * @return true if anonymous access is allowed on entries besides the RootDSE, false
+     * if anonymous access is allowed to all entries.
+     */
+    public boolean isAllowAnonymousAccess()
+    {
+        return allowAnonymousAccess;
+    }
+
+
+    /**
+     * Sets whether to allow anonymous access to entries other than the RootDSE.  If the
+     * access control subsystem is enabled then access to some entries may not be allowed
+     * even when full anonymous access is enabled.
+     *
+     * @param enableAnonymousAccess true to enable anonymous access, false to disable it
+     */
+    public void setAllowAnonymousAccess( boolean enableAnonymousAccess )
+    {
+        this.allowAnonymousAccess = enableAnonymousAccess;
+    }
+
+
+    /**
+     * Returns interceptors in the server.
+     *
+     * @return the interceptors in the server.
+     */
+    public List<Interceptor> getInterceptors()
+    {
+        List<Interceptor> cloned = new ArrayList<Interceptor>();
+        cloned.addAll( interceptors );
+        return cloned;
+    }
+
+
+    /**
+     * Sets the interceptors in the server.
+     *
+     * @org.apache.xbean.Property nestedType="org.apache.directory.server.core.interceptor.Interceptor"
+     * @param interceptors the interceptors to be used in the server.
+     */
+    public void setInterceptors( List<Interceptor> interceptors ) 
+    {
+        Set<String> names = new HashSet<String>();
+        for ( Interceptor interceptor : interceptors )
+        {
+            String name = interceptor.getName();
+            if ( names.contains( name ) )
+            {
+                LOG.warn( "Encountered duplicate definitions for {} interceptor", interceptor.getName() );
+            }
+            names.add( name );
+        }
+
+        this.interceptors = interceptors;
+    }
+
+
+    /**
+     * Returns test directory entries({@link LdifEntry}) to be loaded while
+     * bootstrapping.
+     *
+     * @org.apache.xbean.Property nestedType="org.apache.directory.shared.ldap.ldif.Entry"
+     * @return test entries to load during bootstrapping
+     */
+    public List<LdifEntry> getTestEntries()
+    {
+        List<LdifEntry> cloned = new ArrayList<LdifEntry>();
+        cloned.addAll( testEntries );
+        return cloned;
+    }
+
+
+    /**
+     * Sets test directory entries({@link Attributes}) to be loaded while
+     * bootstrapping.
+     *
+     * @org.apache.xbean.Property nestedType="org.apache.directory.shared.ldap.ldif.Entry"
+     * @param testEntries the test entries to load while bootstrapping
+     */
+    public void setTestEntries( List<? extends LdifEntry> testEntries )
+    {
+        //noinspection MismatchedQueryAndUpdateOfCollection
+        List<LdifEntry> cloned = new ArrayList<LdifEntry>();
+        cloned.addAll( testEntries );
+        this.testEntries = testEntries;
+    }
+
+
+    /**
+     * Returns working directory (counterpart of <tt>var/lib</tt>) where partitions are
+     * stored by default.
+     *
+     * @return the directory where partition's are stored.
+     */
+    public File getWorkingDirectory()
+    {
+        return workingDirectory;
+    }
+
+
+    /**
+     * Sets working directory (counterpart of <tt>var/lib</tt>) where partitions are stored
+     * by default.
+     *
+     * @param workingDirectory the directory where the server's partitions are stored by default.
+     */
+    public void setWorkingDirectory( File workingDirectory )
+    {
+        this.workingDirectory = workingDirectory;
+    }
+
+
+    public void setShutdownHookEnabled( boolean shutdownHookEnabled )
+    {
+        this.shutdownHookEnabled = shutdownHookEnabled;
+    }
+
+
+    public boolean isShutdownHookEnabled()
+    {
+        return shutdownHookEnabled;
+    }
+
+
+    public void setExitVmOnShutdown( boolean exitVmOnShutdown )
+    {
+        this.exitVmOnShutdown = exitVmOnShutdown;
+    }
+
+
+    public boolean isExitVmOnShutdown()
+    {
+        return exitVmOnShutdown;
+    }
+
+
+    public void setMaxSizeLimit( int maxSizeLimit )
+    {
+        this.maxSizeLimit = maxSizeLimit;
+    }
+
+
+    public int getMaxSizeLimit()
+    {
+        return maxSizeLimit;
+    }
+
+
+    public void setMaxTimeLimit( int maxTimeLimit )
+    {
+        this.maxTimeLimit = maxTimeLimit;
+    }
+
+
+    public int getMaxTimeLimit()
+    {
+        return maxTimeLimit;
+    }
+
+    public void setSystemPartition( Partition systemPartition )
+    {
+        this.systemPartition = systemPartition;
+    }
+
+
+    public Partition getSystemPartition()
+    {
+        return systemPartition;
+    }
+
+
+    public boolean isDenormalizeOpAttrsEnabled()
+    {
+        return denormalizeOpAttrsEnabled;
+    }
+
+
+    public void setDenormalizeOpAttrsEnabled( boolean denormalizeOpAttrsEnabled )
+    {
+        this.denormalizeOpAttrsEnabled = denormalizeOpAttrsEnabled;
+    }
+
+
+    public ChangeLog getChangeLog()
+    {
+        return changeLog;
+    }
+
+
+    public void setChangeLog( ChangeLog changeLog )
+    {
+        this.changeLog = changeLog;
+    }
+
+
+    public void addPartition( Partition parition ) throws NamingException
+    {
+        partitions.add( parition );
+
+        if ( ! started )
+        {
+            return;
+        }
+
+        AddContextPartitionOperationContext addPartitionCtx = new AddContextPartitionOperationContext( registries, parition );
+        partitionNexus.addContextPartition( addPartitionCtx );
+    }
+
+
+    public void removePartition( Partition partition ) throws NamingException
+    {
+        partitions.remove( partition );
+
+        if ( ! started )
+        {
+            return;
+        }
+
+        RemoveContextPartitionOperationContext removePartitionCtx =
+                new RemoveContextPartitionOperationContext( registries, partition.getSuffixDn() );
+        partitionNexus.removeContextPartition( removePartitionCtx );
+    }
+
+
+    // ------------------------------------------------------------------------
+    // BackendSubsystem Interface Method Implemetations
+    // ------------------------------------------------------------------------
+
+
+    private void setDefaultInterceptorConfigurations()
+    {
+        // Set default interceptor chains
+        List<Interceptor> list = new ArrayList<Interceptor>();
+
+        list.add( new NormalizationInterceptor() );
+        list.add( new AuthenticationInterceptor() );
+        list.add( new ReferralInterceptor() );
+        list.add( new AciAuthorizationInterceptor() );
+        list.add( new DefaultAuthorizationInterceptor() );
+        list.add( new ExceptionInterceptor() );
+        list.add( new ChangeLogInterceptor() );
+        list.add( new OperationalAttributeInterceptor() );
+        list.add( new SchemaInterceptor() );
+        list.add( new SubentryInterceptor() );
+        list.add( new CollectiveAttributeInterceptor() );
+        list.add( new EventInterceptor() );
+        list.add( new TriggerInterceptor() );
+
+        setInterceptors( list );
+    }
+
+
+    public LdapContext getJndiContext() throws NamingException
+    {
+        return this.getJndiContext( null, null, null, AuthenticationLevel.NONE.toString(), "" );
+    }
+
+
+    public LdapContext getJndiContext( String dn ) throws NamingException
+    {
+        return this.getJndiContext( null, null, null, AuthenticationLevel.NONE.toString(), dn );
+    }
+
+
+    public LdapContext getJndiContext( LdapPrincipal principal ) throws NamingException
+    {
+        return new ServerLdapContext( this, principal, new LdapDN() );
+    }
+
+
+    public LdapContext getJndiContext( LdapPrincipal principal, String dn ) throws NamingException
+    {
+        return new ServerLdapContext( this, principal, new LdapDN( dn ) );
+    }
+
+
+    public synchronized LdapContext getJndiContext( LdapDN principalDn, String principal, byte[] credential,
+        String authentication, String rootDN ) throws NamingException
+    {
+        checkSecuritySettings( principal, credential, authentication );
+
+        if ( !started )
+        {
+            return new DeadContext();
+        }
+
+        Hashtable<String, Object> environment = new Hashtable<String, Object>();
+
+        if ( principal != null )
+        {
+            environment.put( Context.SECURITY_PRINCIPAL, principal );
+        }
+
+        if ( credential != null )
+        {
+            environment.put( Context.SECURITY_CREDENTIALS, credential );
+        }
+
+        if ( authentication != null )
+        {
+            environment.put( Context.SECURITY_AUTHENTICATION, authentication );
+        }
+
+        if ( rootDN == null )
+        {
+            rootDN = "";
+        }
+        environment.put( Context.PROVIDER_URL, rootDN );
+        environment.put( DirectoryService.JNDI_KEY, this );
+
+        return new ServerLdapContext( this, environment );
+    }
+
+
+    public long revert() throws NamingException
+    {
+        if ( changeLog == null || ! changeLog.isEnabled() )
+        {
+            throw new IllegalStateException( "The change log must be enabled to revert to previous log revisions." );
+        }
+
+        Tag latest = changeLog.getLatest();
+        if ( null != latest )
+        {
+            if ( latest.getRevision() < changeLog.getCurrentRevision() )
+            {
+                return revert( latest.getRevision() );
+            }
+            else
+            {
+                LOG.info( "Ignoring request to revert without changes since the latest tag." );
+                return changeLog.getCurrentRevision();
+            }
+        }
+
+        throw new IllegalStateException( "There must be at least one tag to revert to the latest tag." );
+    }
+
+
+    public long revert( long revision ) throws NamingException
+    {
+        if ( changeLog == null || ! changeLog.isEnabled() )
+        {
+            throw new IllegalStateException( "The change log must be enabled to revert to previous log revisions." );
+        }
+
+        if ( revision < 0 )
+        {
+            throw new IllegalArgumentException( "revision must be greater than or equal to 0" );
+        }
+
+        if ( revision >= changeLog.getChangeLogStore().getCurrentRevision() )
+        {
+            throw new IllegalArgumentException( "revision must be less than the current revision" );
+        }
+
+        DirContext ctx = getJndiContext( new LdapPrincipal( adminDn, AuthenticationLevel.SIMPLE ) );
+        Cursor<ChangeLogEvent> cursor = changeLog.getChangeLogStore().findAfter( revision );
+
+        /*
+         * BAD, BAD, BAD!!!
+         *
+         * No synchronization no nothing.  Just getting this to work for now
+         * so we can revert tests.  Any production grade use of this feature
+         * needs to synchronize on all changes while the revert is in progress.
+         *
+         * How about making this operation transactional?
+         *
+         * First of all just stop using JNDI and construct the operations to
+         * feed into the interceptor pipeline.
+         */
+
+        try
+        {
+            LOG.warn( PARTIAL_IMPL_WARNING );
+            cursor.afterLast();
+            while ( cursor.previous() ) // apply ldifs in reverse order
+            {
+                ChangeLogEvent event = cursor.get();
+                LdifEntry reverse = event.getReverseLdif();
+                
+                switch( reverse.getChangeType().getChangeType() )
+                {
+                    case( ChangeType.ADD_ORDINAL ):
+                        ctx.createSubcontext( reverse.getDn(), reverse.getAttributes() );
+                        break;
+                    case( ChangeType.DELETE_ORDINAL ):
+                        ctx.destroySubcontext( reverse.getDn() );
+                        break;
+                    case( ChangeType.MODIFY_ORDINAL ):
+                        ctx.modifyAttributes( reverse.getDn(), reverse.getModificationItemsArray() );
+                        break;
+                    case( ChangeType.MODDN_ORDINAL ):
+                        // NOT BREAK - both ModDN and ModRDN handling is the same
+                    case( ChangeType.MODRDN_ORDINAL ):
+                        if ( reverse.isDeleteOldRdn() )
+                        {
+                            ctx.addToEnvironment( "java.naming.ldap.deleteRDN", "true" );
+                        }
+                        else
+                        {
+                            ctx.addToEnvironment( "java.naming.ldap.deleteRDN", "true" );
+                        }
+
+                        ctx.rename( reverse.getDn(), event.getForwardLdif().getDn() );
+                        break;
+                    default:
+                        throw new NotImplementedException( "Reverts of change type " + reverse.getChangeType()
+                                + " has not yet been implemented!");
+                }
+            }
+        }
+        catch ( IOException e )
+        {
+            throw new NamingException( "Encountered a failure while trying to revert to a previous revision: "
+                    + revision );
+        }
+
+        return changeLog.getCurrentRevision();
+    }
+
+
+    /**
+     * @throws NamingException if the LDAP server cannot be started
+     */
+    public synchronized void startup() throws NamingException
+    {
+        if ( started )
+        {
+            return;
+        }
+
+        if ( shutdownHookEnabled )
+        {
+            Runtime.getRuntime().addShutdownHook( new Thread( new Runnable()
+            {
+                public void run()
+                {
+                    try
+                    {
+                        shutdown();
+                    }
+                    catch ( NamingException e )
+                    {
+                        LOG.warn( "Failed to shut down the directory service: "
+                            + DefaultDirectoryService.this.instanceId, e );
+                    }
+                }
+            }, "ApacheDS Shutdown Hook (" + instanceId + ')' ) );
+
+            LOG.info( "ApacheDS shutdown hook has been registered with the runtime." );
+        }
+        else if ( LOG.isWarnEnabled() )
+        {
+            LOG.warn( "ApacheDS shutdown hook has NOT been registered with the runtime."
+                + "  This default setting for standalone operation has been overriden." );
+        }
+
+        initialize();
+        firstStart = createBootstrapEntries();
+        showSecurityWarnings();
+        started = true;
+        
+        adminDn = new LdapDN( ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED );
+        adminDn.normalize( registries.getAttributeTypeRegistry().getNormalizerMapping() );
+
+        if ( !testEntries.isEmpty() )
+        {
+            createTestEntries();
+        }
+    }
+
+
+    public synchronized void sync() throws NamingException
+    {
+        if ( !started )
+        {
+            return;
+        }
+
+        this.changeLog.sync();
+        this.partitionNexus.sync();
+    }
+
+
+    public synchronized void shutdown() throws NamingException
+    {
+        if ( !started )
+        {
+            return;
+        }
+
+        this.changeLog.sync();
+        this.changeLog.destroy();
+
+        this.partitionNexus.sync();
+        this.partitionNexus.destroy();
+        this.interceptorChain.destroy();
+        this.started = false;
+        setDefaultInterceptorConfigurations();
+    }
+
+
+    public Registries getRegistries()
+    {
+        return registries;
+    }
+
+
+    public void setRegistries( Registries registries )
+    {
+        this.registries = registries;
+    }
+
+
+    public SchemaService getSchemaService()
+    {
+        return schemaService;
+    }
+
+
+    public void setSchemaService( SchemaService schemaService )
+    {
+        this.schemaService = schemaService;
+    }
+
+
+    public PartitionNexus getPartitionNexus()
+    {
+        return partitionNexus;
+    }
+
+
+    public InterceptorChain getInterceptorChain()
+    {
+        return interceptorChain;
+    }
+
+
+    public boolean isFirstStart()
+    {
+        return firstStart;
+    }
+
+
+    public boolean isStarted()
+    {
+        return started;
+    }
+
+
+    public ServerEntry newEntry( LdapDN dn ) throws NamingException
+    {
+        return new DefaultServerEntry( registries, dn );
+    }
+    
+    
+    /**
+     * Checks to make sure security environment parameters are set correctly.
+     *
+     * @throws javax.naming.NamingException if the security settings are not correctly configured.
+     * @param authentication the mechanism for authentication
+     * @param credential the password
+     * @param principal the distinguished name of the principal
+     */
+    private void checkSecuritySettings( String principal, byte[] credential, String authentication )
+        throws NamingException
+    {
+        if ( authentication == null )
+        {
+            authentication = "";
+        }
+
+        /*
+         * If bind is strong make sure we have the principal name
+         * set within the environment, otherwise complain
+         */
+        if ( AuthenticationLevel.STRONG.toString().equalsIgnoreCase( authentication ) )
+        {
+            if ( principal == null )
+            {
+                throw new LdapConfigurationException( "missing required " + Context.SECURITY_PRINCIPAL
+                    + " property for strong authentication" );
+            }
+        }
+        /*
+         * If bind is simple make sure we have the credentials and the
+         * principal name set within the environment, otherwise complain
+         */
+        else if ( AuthenticationLevel.SIMPLE.toString().equalsIgnoreCase( authentication ) )
+        {
+            if ( credential == null )
+            {
+                throw new LdapConfigurationException( "missing required " + Context.SECURITY_CREDENTIALS
+                    + " property for simple authentication" );
+            }
+
+            if ( principal == null )
+            {
+                throw new LdapConfigurationException( "missing required " + Context.SECURITY_PRINCIPAL
+                    + " property for simple authentication" );
+            }
+        }
+        /*
+         * If bind is none make sure credentials and the principal
+         * name are NOT set within the environment, otherwise complain
+         */
+        else if ( AuthenticationLevel.NONE.toString().equalsIgnoreCase( authentication ) )
+        {
+            if ( credential != null )
+            {
+                throw new LdapConfigurationException( "ambiguous bind "
+                    + "settings encountered where bind is anonymous yet " + Context.SECURITY_CREDENTIALS
+                    + " property is set" );
+            }
+
+            if ( principal != null )
+            {
+                throw new LdapConfigurationException( "ambiguous bind "
+                    + "settings encountered where bind is anonymous yet " + Context.SECURITY_PRINCIPAL
+                    + " property is set" );
+            }
+
+            if ( !allowAnonymousAccess )
+            {
+                throw new LdapNoPermissionException( "Anonymous access disabled." );
+            }
+        }
+        else
+        {
+            /*
+             * If bind is anything other than strong, simple, or none we need to complain
+             */
+            throw new LdapAuthenticationNotSupportedException( "Unknown authentication type: '" + authentication + "'",
+                ResultCodeEnum.AUTH_METHOD_NOT_SUPPORTED );
+        }
+    }
+
+
+    /**
+     * Returns true if we had to create the bootstrap entries on the first
+     * start of the server.  Otherwise if all entries exist, meaning none
+     * had to be created, then we are not starting for the first time.
+     *
+     * @return true if the bootstrap entries had to be created, false otherwise
+     * @throws javax.naming.NamingException if entries cannot be created
+     */
+    private boolean createBootstrapEntries() throws NamingException
+    {
+        boolean firstStart = false;
+        
+        // -------------------------------------------------------------------
+        // create admin entry
+        // -------------------------------------------------------------------
+
+        /*
+         * If the admin entry is there, then the database was already created
+         */
+        if ( !partitionNexus.hasEntry( new EntryOperationContext( registries, PartitionNexus.getAdminName() ) ) )
+        {
+            firstStart = true;
+
+            ServerEntry serverEntry = new DefaultServerEntry( registries, PartitionNexus.getAdminName() );
+            
+            serverEntry.put( SchemaConstants.OBJECT_CLASS_AT, 
+                                SchemaConstants.TOP_OC,
+                                SchemaConstants.PERSON_OC,
+                                SchemaConstants.ORGANIZATIONAL_PERSON_OC,
+                                SchemaConstants.INET_ORG_PERSON_OC );
+
+            serverEntry.put( SchemaConstants.UID_AT, PartitionNexus.ADMIN_UID );
+            serverEntry.put( SchemaConstants.USER_PASSWORD_AT, PartitionNexus.ADMIN_PASSWORD_BYTES );
+            serverEntry.put( SchemaConstants.DISPLAY_NAME_AT, "Directory Superuser" );
+            serverEntry.put( SchemaConstants.CN_AT, "system administrator" );
+            serverEntry.put( SchemaConstants.SN_AT, "administrator" );
+            serverEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED );
+            serverEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() );
+            serverEntry.put( SchemaConstants.DISPLAY_NAME_AT, "Directory Superuser" );
+
+            TlsKeyGenerator.addKeyPair( serverEntry );
+            partitionNexus.add( new AddOperationContext( registries, serverEntry ) );
+        }
+
+        // -------------------------------------------------------------------
+        // create system users area
+        // -------------------------------------------------------------------
+
+        Map<String,OidNormalizer> oidsMap = registries.getAttributeTypeRegistry().getNormalizerMapping();
+        LdapDN userDn = new LdapDN( ServerDNConstants.USERS_SYSTEM_DN );
+        userDn.normalize( oidsMap );
+        
+        if ( !partitionNexus.hasEntry( new EntryOperationContext( registries, userDn ) ) )
+        {
+            firstStart = true;
+
+            ServerEntry serverEntry = new DefaultServerEntry( registries, userDn );
+            
+            serverEntry.put( SchemaConstants.OBJECT_CLASS_AT, 
+                                SchemaConstants.TOP_OC,
+                                SchemaConstants.ORGANIZATIONAL_UNIT_OC );
+
+            serverEntry.put( SchemaConstants.OU_AT, "users" );
+            serverEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED );
+            serverEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() );
+
+            partitionNexus.add( new AddOperationContext( registries, serverEntry ) );
+        }
+
+        // -------------------------------------------------------------------
+        // create system groups area
+        // -------------------------------------------------------------------
+
+        LdapDN groupDn = new LdapDN( ServerDNConstants.GROUPS_SYSTEM_DN );
+        groupDn.normalize( oidsMap );
+        
+        if ( !partitionNexus.hasEntry( new EntryOperationContext( registries, groupDn ) ) )
+        {
+            firstStart = true;
+
+            ServerEntry serverEntry = new DefaultServerEntry( registries, groupDn );
+            
+            serverEntry.put( SchemaConstants.OBJECT_CLASS_AT, 
+                                SchemaConstants.TOP_OC,
+                                SchemaConstants.ORGANIZATIONAL_UNIT_OC );
+
+            serverEntry.put( SchemaConstants.OU_AT, "groups" );
+            serverEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED );
+            serverEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() );
+
+            partitionNexus.add( new AddOperationContext( registries, serverEntry ) );
+        }
+
+        // -------------------------------------------------------------------
+        // create administrator group
+        // -------------------------------------------------------------------
+
+        LdapDN name = new LdapDN( ServerDNConstants.ADMINISTRATORS_GROUP_DN );
+        name.normalize( oidsMap );
+        
+        if ( !partitionNexus.hasEntry( new EntryOperationContext( registries, name ) ) )
+        {
+            firstStart = true;
+
+            ServerEntry serverEntry = new DefaultServerEntry( registries, name );
+            
+            serverEntry.put( SchemaConstants.OBJECT_CLASS_AT, 
+                                SchemaConstants.TOP_OC,
+                                SchemaConstants.GROUP_OF_UNIQUE_NAMES_OC );
+
+            serverEntry.put( SchemaConstants.CN_AT, "Administrators" );
+            serverEntry.put( SchemaConstants.UNIQUE_MEMBER_AT, ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED );
+            serverEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED );
+            serverEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() );
+
+            partitionNexus.add( new AddOperationContext( registries, serverEntry ) );
+            
+            Interceptor authzInterceptor = interceptorChain.get( AciAuthorizationInterceptor.class.getName() );
+            
+            if ( authzInterceptor == null )
+            {
+                LOG.error( "The Authorization service is null : this is not allowed" );
+                throw new NamingException( "The Authorization service is null" );
+            }
+            
+            if ( !( authzInterceptor instanceof AciAuthorizationInterceptor ) )
+            {
+                LOG.error( "The Authorization service is not set correctly : '{}' is an incorect interceptor",
+                    authzInterceptor.getClass().getName() );
+                throw new NamingException( "The Authorization service is incorrectly set" );
+                
+            }
+
+            AciAuthorizationInterceptor authzSrvc = ( AciAuthorizationInterceptor ) authzInterceptor;
+            authzSrvc.cacheNewGroup( name, serverEntry );
+
+        }
+
+        // -------------------------------------------------------------------
+        // create system configuration area
+        // -------------------------------------------------------------------
+
+        LdapDN configurationDn = new LdapDN( "ou=configuration,ou=system" );
+        configurationDn.normalize( oidsMap );
+        
+        if ( !partitionNexus.hasEntry( new EntryOperationContext( registries, configurationDn ) ) )
+        {
+            firstStart = true;
+
+            ServerEntry serverEntry = new DefaultServerEntry( registries, configurationDn );
+            serverEntry.put( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC, SchemaConstants.ORGANIZATIONAL_UNIT_OC );
+
+            serverEntry.put( SchemaConstants.OU_AT, "configuration" );
+            serverEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED );
+            serverEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() );
+
+            partitionNexus.add( new AddOperationContext( registries, serverEntry ) );
+        }
+
+        // -------------------------------------------------------------------
+        // create system configuration area for partition information
+        // -------------------------------------------------------------------
+
+        LdapDN partitionsDn = new LdapDN( "ou=partitions,ou=configuration,ou=system" );
+        partitionsDn.normalize( oidsMap );
+        
+        if ( !partitionNexus.hasEntry( new EntryOperationContext( registries, partitionsDn ) ) )
+        {
+            firstStart = true;
+
+            ServerEntry serverEntry = new DefaultServerEntry( registries, partitionsDn );
+            serverEntry.put( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC, SchemaConstants.ORGANIZATIONAL_UNIT_OC );
+
+            serverEntry.put( SchemaConstants.OU_AT, "partitions" );
+            serverEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED );
+            serverEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() );
+
+            partitionNexus.add( new AddOperationContext( registries, serverEntry ) );
+        }
+
+        // -------------------------------------------------------------------
+        // create system configuration area for services
+        // -------------------------------------------------------------------
+
+        LdapDN servicesDn = new LdapDN( "ou=services,ou=configuration,ou=system" );
+        servicesDn.normalize( oidsMap );
+        
+        if ( !partitionNexus.hasEntry( new EntryOperationContext( registries, servicesDn ) ) )
+        {
+            firstStart = true;
+
+            ServerEntry serverEntry = new DefaultServerEntry( registries, servicesDn );
+            serverEntry.put( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC, SchemaConstants.ORGANIZATIONAL_UNIT_OC );
+
+            serverEntry.put( SchemaConstants.OU_AT, "services" );
+            serverEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED );
+            serverEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() );
+
+            partitionNexus.add( new AddOperationContext( registries, serverEntry ) );
+        }
+
+        // -------------------------------------------------------------------
+        // create system configuration area for interceptors
+        // -------------------------------------------------------------------
+
+        LdapDN interceptorsDn = new LdapDN( "ou=interceptors,ou=configuration,ou=system" );
+        interceptorsDn.normalize( oidsMap );
+        
+        if ( !partitionNexus.hasEntry( new EntryOperationContext( registries, interceptorsDn ) ) )
+        {
+            firstStart = true;
+
+            ServerEntry serverEntry = new DefaultServerEntry( registries, interceptorsDn );
+            serverEntry.put( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC, SchemaConstants.ORGANIZATIONAL_UNIT_OC );
+
+            serverEntry.put( SchemaConstants.OU_AT, "interceptors" );
+            serverEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED );
+            serverEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() );
+
+            partitionNexus.add( new AddOperationContext( registries, serverEntry ) );
+        }
+
+        // -------------------------------------------------------------------
+        // create system preferences area
+        // -------------------------------------------------------------------
+
+        LdapDN sysPrefRootDn = new LdapDN( ServerDNConstants.SYSPREFROOT_SYSTEM_DN );
+        sysPrefRootDn.normalize( oidsMap );
+        
+        if ( !partitionNexus.hasEntry( new EntryOperationContext( registries, sysPrefRootDn ) ) )
+        {
+            firstStart = true;
+
+            ServerEntry serverEntry = new DefaultServerEntry( registries, sysPrefRootDn );
+            serverEntry.put( SchemaConstants.OBJECT_CLASS_AT, 
+                SchemaConstants.TOP_OC, 
+                SchemaConstants.ORGANIZATIONAL_UNIT_OC,
+                SchemaConstants.EXTENSIBLE_OBJECT_OC );
+
+            serverEntry.put( "prefNodeName", "sysPrefRoot" );
+            serverEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED );
+            serverEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() );
+
+            partitionNexus.add( new AddOperationContext( registries, serverEntry ) );
+        }
+
+        return firstStart;
+    }
+
+
+    /**
+     * Displays security warning messages if any possible secutiry issue is found.
+     * @throws NamingException if there are failures parsing and accessing internal structures
+     */
+    private void showSecurityWarnings() throws NamingException
+    {
+        // Warn if the default password is not changed.
+        boolean needToChangeAdminPassword = false;
+
+        LdapDN adminDn = new LdapDN( ServerDNConstants.ADMIN_SYSTEM_DN );
+        adminDn.normalize( registries.getAttributeTypeRegistry().getNormalizerMapping() );
+        
+        ServerEntry adminEntry = partitionNexus.lookup( new LookupOperationContext( registries, adminDn ) );
+        Object userPassword = adminEntry.get( SchemaConstants.USER_PASSWORD_AT ).get();
+        
+        if ( userPassword instanceof byte[] )
+        {
+            needToChangeAdminPassword = Arrays.equals( PartitionNexus.ADMIN_PASSWORD_BYTES, ( byte[] ) userPassword );
+        }
+        else if ( userPassword.toString().equals( PartitionNexus.ADMIN_PASSWORD_STRING ) )
+        {
+            needToChangeAdminPassword = PartitionNexus.ADMIN_PASSWORD_STRING.equals( userPassword.toString() );
+        }
+
+        if ( needToChangeAdminPassword )
+        {
+            LOG.warn( "You didn't change the admin password of directory service " + "instance '" + instanceId + "'.  "
+                + "Please update the admin password as soon as possible " + "to prevent a possible security breach." );
+        }
+    }
+
+
+    /**
+     * Adds test entries into the core.
+     *
+     * @todo this may no longer be needed when JNDI is not used for bootstrapping
+     * 
+     * @throws NamingException if the creation of test entries fails.
+     */
+    private void createTestEntries() throws NamingException
+    {
+        LdapPrincipal principal = new LdapPrincipal( adminDn, AuthenticationLevel.SIMPLE );
+        ServerLdapContext ctx = new ServerLdapContext( this, principal, new LdapDN() );
+
+        for ( LdifEntry testEntry : testEntries )
+        {
+            try
+            {
+                LdifEntry entry = testEntry.clone();
+                Attributes attributes = entry.getAttributes();
+                String dn = entry.getDn();
+
+                try
+                {
+                    ctx.createSubcontext( dn, attributes );
+                }
+                catch ( Exception e )
+                {
+                    LOG.warn( dn + " test entry already exists.", e );
+                }
+            }
+            catch ( CloneNotSupportedException cnse )
+            {
+                LOG.warn( "Cannot clone the entry ", cnse );
+            }
+        }
+    }
+
+
+    /**
+     * Kicks off the initialization of the entire system.
+     *
+     * @throws javax.naming.NamingException if there are problems along the way
+     */
+    private void initialize() throws NamingException
+    {
+        if ( LOG.isDebugEnabled() )
+        {
+            LOG.debug( "---> Initializing the DefaultDirectoryService " );
+        }
+
+        // --------------------------------------------------------------------
+        // If not present extract schema partition from jar
+        // --------------------------------------------------------------------
+
+        File schemaDirectory = new File( workingDirectory, "schema" );
+        SchemaPartitionExtractor extractor;
+        if ( ! schemaDirectory.exists() )
+        {
+            try
+            {
+                extractor = new SchemaPartitionExtractor( workingDirectory );
+                extractor.extract();
+            }
+            catch ( IOException e )
+            {
+                NamingException ne = new NamingException( "Failed to extract pre-loaded schema partition." );
+                ne.setRootCause( e );
+                throw ne;
+            }
+        }
+        
+        // --------------------------------------------------------------------
+        // Initialize schema partition
+        // --------------------------------------------------------------------
+
+        JdbmPartition schemaPartition = new JdbmPartition();
+        schemaPartition.setId( "schema" );
+        schemaPartition.setCacheSize( 1000 );
+
+        DbFileListing listing;
+        
+        try 
+        {
+            listing = new DbFileListing();
+        }
+        catch( IOException e )
+        {
+            throw new LdapNamingException( "Got IOException while trying to read DBFileListing: " + e.getMessage(), 
+                ResultCodeEnum.OTHER );
+        }
+
+        Set<Index> indexedAttributes = new HashSet<Index>();
+        
+        for ( String attributeId : listing.getIndexedAttributes() )
+        {
+            indexedAttributes.add( new JdbmIndex( attributeId ) );
+        }
+
+        schemaPartition.setIndexedAttributes( indexedAttributes );
+        schemaPartition.setSuffix( ServerDNConstants.OU_SCHEMA_DN );
+        
+        ServerEntry entry = new DefaultServerEntry( registries, new LdapDN( ServerDNConstants.OU_SCHEMA_DN ) );
+        entry.put( SchemaConstants.OBJECT_CLASS_AT, 
+                SchemaConstants.TOP_OC, 
+                SchemaConstants.ORGANIZATIONAL_UNIT_OC );
+        entry.put( SchemaConstants.OU_AT, "schema" );
+        schemaPartition.setContextEntry( entry );
+        schemaPartition.init( this );
+
+        // --------------------------------------------------------------------
+        // Enable schemas of all indices of partition configurations 
+        // --------------------------------------------------------------------
+
+        /*
+         * We need to make sure that every attribute indexed by a partition is
+         * loaded into the registries on the next step.  So here we must enable
+         * the schemas of those attributes so they are loaded into the global
+         * registries.
+         */
+        
+        SchemaPartitionDao dao = new SchemaPartitionDao( schemaPartition, registries );
+        Map<String,Schema> schemaMap = dao.getSchemas();
+        Set<Partition> partitions = new HashSet<Partition>();
+        partitions.add( systemPartition );
+        partitions.addAll( this.partitions );
+
+        for ( Partition partition : partitions )
+        {
+            if ( partition instanceof BTreePartition )
+            {
+                JdbmPartition btpconf = ( JdbmPartition ) partition;
+                for ( Index index : btpconf.getIndexedAttributes() )
+                {
+                    String schemaName = dao.findSchema( index.getAttributeId() );
+                    if ( schemaName == null )
+                    {
+                        throw new NamingException( "Index on unidentified attribute: " + index.toString() );
+                    }
+
+                    Schema schema = schemaMap.get( schemaName );
+                    if ( schema.isDisabled() )
+                    {
+                        dao.enableSchema( schemaName );
+                    }
+                }
+            }
+        }
+        
+        // --------------------------------------------------------------------
+        // Initialize schema subsystem and reset registries
+        // --------------------------------------------------------------------
+        
+        PartitionSchemaLoader schemaLoader = new PartitionSchemaLoader( schemaPartition, registries );
+        Registries globalRegistries = new DefaultRegistries( "global", schemaLoader, registries.getOidRegistry() );
+        schemaLoader.loadEnabled( globalRegistries );
+        registries = globalRegistries;
+        SerializableComparator.setRegistry( globalRegistries.getComparatorRegistry() );
+
+        SchemaOperationControl schemaControl = new SchemaOperationControl( registries, schemaLoader,
+            new SchemaPartitionDao( schemaPartition, registries ) );
+
+        schemaService = new SchemaService( registries, schemaPartition, schemaControl );
+
+
+        partitionNexus = new DefaultPartitionNexus( new DefaultServerEntry( registries, LdapDN.EMPTY_LDAPDN ) );
+        partitionNexus.init( this );
+        partitionNexus.addContextPartition( new AddContextPartitionOperationContext( registries, schemaPartition ) );
+
+        interceptorChain = new InterceptorChain();
+        interceptorChain.init( this );
+
+        if ( changeLog.isEnabled() )
+        {
+            changeLog.init( this );
+        }
+
+        if ( LOG.isDebugEnabled() )
+        {
+            LOG.debug( "<--- DefaultDirectoryService initialized" );
+        }
+    }
+    
+    /**
+     * Read an entry (without DN)
+     * 
+     * @param text The ldif format file
+     * @return An Attributes.
+     */
+    private Attributes readEntry( String text )
+    {
+        StringReader strIn = new StringReader( text );
+        BufferedReader in = new BufferedReader( strIn );
+
+        String line = null;
+        Attributes attributes = new AttributesImpl( true );
+
+        try
+        {
+            while ( ( line = in.readLine() ) != null )
+            {
+                if ( line.length() == 0 )
+                {
+                    continue;
+                }
+
+                String addedLine = line.trim();
+
+                if ( StringTools.isEmpty( addedLine ) )
+                {
+                    continue;
+                }
+
+                Attribute attribute = LdifReader.parseAttributeValue( addedLine );
+                Attribute oldAttribute = attributes.get( attribute.getID() );
+
+                if ( oldAttribute != null )
+                {
+                    try
+                    {
+                        oldAttribute.add( attribute.get() );
+                        attributes.put( oldAttribute );
+                    }
+                    catch (NamingException ne)
+                    {
+                        // Do nothing
+                    }
+                }
+                else
+                {
+                    attributes.put( attribute );
+                }
+            }
+        }
+        catch (IOException ioe)
+        {
+            // Do nothing : we can't reach this point !
+        }
+
+        return attributes;
+    }
+
+    
+    /**
+     * Create a new ServerEntry
+     * 
+     * @param ldif The String representing the attributes, as a LDIF file
+     * @param dn The DN for this new entry
+     */
+    public ServerEntry newEntry( String ldif, String dn )
+    {
+        try
+        {
+            Attributes entry = readEntry( ldif );
+            
+            ServerEntry serverEntry = ServerEntryUtils.toServerEntry( entry, new LdapDN( dn ), registries );
+            return serverEntry;
+        }
+        catch ( Exception e )
+        {
+            LOG.error( "Cannot build an entry for '{}' and this DN :'{}'", ldif, dn );
+            // do nothing
+            return null;
+        }
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/DirectoryService.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/DirectoryService.java
new file mode 100644
index 0000000..500971b
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/DirectoryService.java
@@ -0,0 +1,393 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core;
+
+
+import org.apache.directory.server.core.authn.LdapPrincipal;
+import org.apache.directory.server.core.changelog.ChangeLog;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerEntryFactory;
+import org.apache.directory.server.core.interceptor.Interceptor;
+import org.apache.directory.server.core.interceptor.InterceptorChain;
+import org.apache.directory.server.core.jndi.AbstractContextFactory;
+import org.apache.directory.server.core.partition.Partition;
+import org.apache.directory.server.core.partition.PartitionNexus;
+import org.apache.directory.server.core.schema.SchemaService;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.ldif.LdifEntry;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+import javax.naming.Context;
+import javax.naming.NamingException;
+import javax.naming.directory.Attributes;
+import javax.naming.ldap.LdapContext;
+import java.io.File;
+import java.util.List;
+import java.util.Set;
+
+
+/**
+ * Provides JNDI service to {@link AbstractContextFactory}.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public interface DirectoryService extends ServerEntryFactory
+{
+    String JNDI_KEY = DirectoryService.class.getName();
+
+    /**
+     * Reverts the server's state to an earlier revision.  Note that the revsion number
+     * still increases to revert back even though the state reverted to is the same.
+     * Note that implementations may lock the server from making changes or searching
+     * the directory until this operation has completed.
+     *
+     * @param revision the revision number to revert to
+     * @return the new revision reached by applying all changes needed to revert to the
+     * original state
+     * @throws NamingException if there are problems reverting back to the earlier state
+     * @throws IllegalArgumentException if the revision provided is greater than the current
+     * revision or less than 0
+     * @throws UnsupportedOperationException if this feature is not supported by the
+     * change log
+     */
+    long revert( long revision ) throws NamingException;
+
+
+    /**
+     * Reverts the server's state to the latest tagged snapshot if one was taken.  If
+     * there is no tag a illegal state exception will result.  If the latest revision
+     * is not earlier than the current revision (both are same), then no changes were
+     * made to the directory to be reverted.  In this case we return the current
+     * revision and do nothiig loggin the fact that we ignored the request to revert.
+     *
+     * @return the new revision reached by applying all changes needed to revert
+     * to the new state or the same version before this call if no revert actually
+     * took place
+     * @throws NamingException if there are problems reverting back to the earlier state
+     * @throws UnsupportedOperationException if this feature is not supported by the
+     * change log
+     */
+    long revert() throws NamingException;
+
+
+    PartitionNexus getPartitionNexus();
+
+
+    InterceptorChain getInterceptorChain();
+
+
+    void addPartition( Partition partition ) throws NamingException;
+    
+
+    void removePartition( Partition partition ) throws NamingException;
+
+
+    Registries getRegistries();
+
+
+    void setRegistries( Registries registries );
+
+
+    SchemaService getSchemaService();
+
+
+    void setSchemaService( SchemaService schemaService );
+
+
+    /**
+     * Starts up this service.
+     * 
+     * @throws NamingException if failed to start up
+     */
+    void startup() throws NamingException;
+
+
+    /**
+     * Shuts down this service.
+     * 
+     * @throws NamingException if failed to shut down
+     */
+    void shutdown() throws NamingException;
+
+
+    /**
+     * Calls {@link Partition#sync()} for all registered {@link Partition}s.
+     * @throws NamingException if synchronization failed
+     */
+    void sync() throws NamingException;
+
+
+    /**
+     * Returns <tt>true</tt> if this service is started.
+     * @return true if the service has started, false otherwise
+     */
+    boolean isStarted();
+
+
+    /**
+     * Gets a JNDI {@link Context} to the RootDSE as an anonymous user.
+     * This bypasses authentication within the server.
+     *
+     * @return a JNDI context to the RootDSE
+     * @throws NamingException if failed to create a context
+     */
+    LdapContext getJndiContext() throws NamingException;
+
+
+    /**
+     * Gets a JNDI {@link Context} to a specific entry as an anonymous user.
+     * This bypasses authentication within the server.
+     *
+     * @param dn the distinguished name of the entry
+     * @return a JNDI context to the entry at the specified DN
+     * @throws NamingException if failed to create a context
+     */
+    LdapContext getJndiContext( String dn ) throws NamingException;
+
+
+    /**
+     * Gets a JNDI {@link Context} to the RootDSE as a specific LDAP user principal.
+     * This bypasses authentication within the server.
+     *
+     * @param principal the user to associate with the context
+     * @return a JNDI context to the RootDSE as a specific user
+     * @throws NamingException if failed to create a context
+     */
+    LdapContext getJndiContext( LdapPrincipal principal ) throws NamingException;
+
+
+    /**
+     * Gets a JNDI {@link Context} to a specific entry as a specific LDAP user principal.
+     * This bypasses authentication within the server.
+     *
+     * @param principal the user to associate with the context
+     * @param dn the distinguished name of the entry
+     * @return a JNDI context to the specified entry as a specific user
+     * @throws NamingException if failed to create a context
+     */
+    LdapContext getJndiContext( LdapPrincipal principal, String dn ) throws NamingException;
+
+
+    /**
+     * Returns a JNDI {@link Context} with the specified authentication information
+     * (<tt>principal</tt>, <tt>credential</tt>, and <tt>authentication</tt>) and
+     * <tt>baseName</tt>.
+     * 
+     * @param principalDn the distinguished name of the bind principal
+     * @param principal {@link Context#SECURITY_PRINCIPAL} value
+     * @param credential {@link Context#SECURITY_CREDENTIALS} value
+     * @param authentication {@link Context#SECURITY_AUTHENTICATION} value
+     * @param dn the distinguished name of the entry
+     * @return a JNDI context to the specified entry as a specific user
+     * @throws NamingException if failed to create a context
+     */
+    LdapContext getJndiContext( LdapDN principalDn, String principal, byte[] credential,
+        String authentication, String dn ) throws NamingException;
+
+
+    void setInstanceId( String instanceId );
+
+
+    String getInstanceId();
+
+
+    /**
+     * Gets the {@link Partition}s used by this DirectoryService.
+     *
+     * @return the set of partitions used
+     */
+    Set<? extends Partition> getPartitions();
+
+
+    /**
+     * Sets {@link Partition}s used by this DirectoryService.
+     *
+     * @param partitions the partitions to used
+     */
+    void setPartitions( Set<? extends Partition> partitions );
+
+
+    /**
+     * Returns <tt>true</tt> if access control checks are enabled.
+     *
+     * @return true if access control checks are enabled, false otherwise
+     */
+    boolean isAccessControlEnabled();
+
+
+    /**
+     * Sets whether to enable basic access control checks or not.
+     *
+     * @param accessControlEnabled true to enable access control checks, false otherwise
+     */
+    void setAccessControlEnabled( boolean accessControlEnabled );
+
+
+    /**
+     * Returns <tt>true</tt> if anonymous access is allowed on entries besides the RootDSE.
+     * If the access control subsystem is enabled then access to some entries may not be
+     * allowed even when full anonymous access is enabled.
+     *
+     * @return true if anonymous access is allowed on entries besides the RootDSE, false
+     * if anonymous access is allowed to all entries.
+     */
+    boolean isAllowAnonymousAccess();
+
+
+    /**
+     * Sets whether to allow anonymous access to entries other than the RootDSE.  If the
+     * access control subsystem is enabled then access to some entries may not be allowed
+     * even when full anonymous access is enabled.
+     *
+     * @param enableAnonymousAccess true to enable anonymous access, false to disable it
+     */
+    void setAllowAnonymousAccess( boolean enableAnonymousAccess );
+
+
+    /**
+     * Returns interceptors in the server.
+     *
+     * @return the interceptors in the server.
+     */
+    List<Interceptor> getInterceptors();
+
+
+    /**
+     * Sets the interceptors in the server.
+     *
+     * @param interceptors the interceptors to be used in the server.
+     */
+    void setInterceptors( List<Interceptor> interceptors );
+
+
+    /**
+     * Returns test directory entries({@link LdifEntry}) to be loaded while
+     * bootstrapping.
+     *
+     * @return test entries to load during bootstrapping
+     */
+    List<LdifEntry> getTestEntries();
+
+
+    /**
+     * Sets test directory entries({@link Attributes}) to be loaded while
+     * bootstrapping.
+     *
+     * @param testEntries the test entries to load while bootstrapping
+     */
+    void setTestEntries( List<? extends LdifEntry> testEntries );
+
+
+    /**
+     * Returns working directory (counterpart of <tt>var/lib</tt>) where partitions are
+     * stored by default.
+     *
+     * @return the directory where partition's are stored.
+     */
+    File getWorkingDirectory();
+
+
+    /**
+     * Sets working directory (counterpart of <tt>var/lib</tt>) where partitions are stored
+     * by default.
+     *
+     * @param workingDirectory the directory where the server's partitions are stored by default.
+     */
+    void setWorkingDirectory( File workingDirectory );
+
+
+    /**
+     * Sets the shutdown hook flag which controls whether or not this DirectoryService
+     * registers a JVM shutdown hook to flush caches and synchronize to disk safely.  This is
+     * enabled by default.
+     *
+     * @param shutdownHookEnabled true to enable the shutdown hook, false to disable
+     */
+    void setShutdownHookEnabled( boolean shutdownHookEnabled );
+
+
+    /**
+     * Checks to see if this DirectoryService has registered a JVM shutdown hook
+     * to flush caches and synchronize to disk safely.  This is enabled by default.
+     *
+     * @return true if a shutdown hook is registered, false if it is not
+     */
+    boolean isShutdownHookEnabled();
+
+
+    void setExitVmOnShutdown( boolean exitVmOnShutdown );
+
+
+    boolean isExitVmOnShutdown();
+
+
+    void setMaxSizeLimit( int maxSizeLimit );
+
+
+    int getMaxSizeLimit();
+
+
+    void setMaxTimeLimit( int maxTimeLimit );
+
+
+    int getMaxTimeLimit();
+
+
+    void setSystemPartition( Partition systemPartition );
+
+
+    Partition getSystemPartition();
+
+
+    boolean isDenormalizeOpAttrsEnabled();
+
+
+    void setDenormalizeOpAttrsEnabled( boolean denormalizeOpAttrsEnabled );
+
+
+    /**
+     * Gets the ChangeLog service for this DirectoryService used for tracking
+     * changes (revisions) to the server and using them to revert the server
+     * to earier revisions.
+     *
+     * @return the change log service
+     */
+    ChangeLog getChangeLog();
+
+
+    /**
+     * Sets the ChangeLog service for this DirectoryService used for tracking
+     * changes (revisions) to the server and using them to revert the server
+     * to earier revisions.
+     *
+     * @param changeLog the change log service to set
+     */
+    void setChangeLog( ChangeLog changeLog );
+    
+
+    /**
+     * Create a new ServerEntry
+     * 
+     * @param ldif The String representing the attributes, as a LDIF file
+     * @param dn The DN for this new entry
+     */
+    ServerEntry newEntry( String ldif, String dn );
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/authn/AbstractAuthenticator.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/authn/AbstractAuthenticator.java
new file mode 100644
index 0000000..73be547
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/authn/AbstractAuthenticator.java
@@ -0,0 +1,128 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.authn;
+
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.jndi.ServerContext;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+
+/**
+ * Base class for all Authenticators.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public abstract class AbstractAuthenticator implements Authenticator
+{
+    private DirectoryService directoryService;
+    
+    /** authenticator type */
+    private final String authenticatorType;
+
+
+    /**
+     * Creates a new instance.
+     *
+     * @param type the type of this authenticator (e.g. <tt>'simple'</tt>, <tt>'none'</tt>...)
+     */
+    protected AbstractAuthenticator( String type )
+    {
+        this.authenticatorType = type;
+    }
+
+
+    /**
+     * Returns {@link DirectoryService} for this authenticator.
+     * @return the directory service core
+     */
+    public DirectoryService getDirectoryService()
+    {
+        return directoryService;
+    }
+    
+
+    public String getAuthenticatorType()
+    {
+        return authenticatorType;
+    }
+
+
+    /**
+     * Initializes (<tt>directoryService</tt> and and calls {@link #doInit()} method.
+     * Please put your initialization code into {@link #doInit()}.
+     * @param directoryService the directory core for this authenticator
+     * @throws NamingException if there is a problem starting up the authenticator
+     */
+    public final void init( DirectoryService directoryService ) throws NamingException
+    {
+        this.directoryService = directoryService;
+        doInit();
+    }
+
+
+    /**
+     * Implement your initialization code here.
+     */
+    protected void doInit()
+    {
+    }
+
+
+    /**
+     * Calls {@link #doDestroy()} method, and clears default properties
+     * (<tt>factoryConfiguration</tt> and <tt>configuration</tt>).
+     * Please put your deinitialization code into {@link #doDestroy()}. 
+     */
+    public final void destroy()
+    {
+        try
+        {
+            doDestroy();
+        }
+        finally
+        {
+            this.directoryService = null;
+        }
+    }
+
+
+    /**
+     * Implement your deinitialization code here.
+     */
+    protected void doDestroy()
+    {
+    }
+
+
+    public abstract LdapPrincipal authenticate( LdapDN bindDn, ServerContext ctx ) throws NamingException;
+
+    
+    /**
+     * Does nothing leaving it so subclasses can override.
+     */
+    public void invalidateCache( LdapDN bindDn )
+    {
+    }
+
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/authn/AnonymousAuthenticator.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/authn/AnonymousAuthenticator.java
new file mode 100644
index 0000000..22c17e5
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/authn/AnonymousAuthenticator.java
@@ -0,0 +1,63 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.authn;
+
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.core.jndi.ServerContext;
+import org.apache.directory.shared.ldap.constants.AuthenticationLevel;
+import org.apache.directory.shared.ldap.exception.LdapNoPermissionException;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+
+/**
+ * An {@link Authenticator} that handles anonymous connections
+ * (type <tt>'none'</tt>).
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class AnonymousAuthenticator extends AbstractAuthenticator
+{
+    /**
+     * Creates a new instance.
+     */
+    public AnonymousAuthenticator()
+    {
+        super( AuthenticationLevel.NONE.toString() );
+    }
+
+
+    /**
+     * If the context is not configured to allow anonymous connections,
+     * this method throws a {@link javax.naming.NoPermissionException}.
+     */
+    public LdapPrincipal authenticate( LdapDN bindDn, ServerContext ctx ) throws NamingException
+    {
+        if ( getDirectoryService().isAllowAnonymousAccess() )
+        {
+            return LdapPrincipal.ANONYMOUS;
+        }
+        else
+        {
+            throw new LdapNoPermissionException( "Anonymous bind NOT permitted!" );
+        }
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/authn/AuthenticationInterceptor.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/authn/AuthenticationInterceptor.java
new file mode 100644
index 0000000..25ad925
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/authn/AuthenticationInterceptor.java
@@ -0,0 +1,580 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.authn;
+
+
+import javax.naming.Context;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerSearchResult;
+import org.apache.directory.server.core.interceptor.BaseInterceptor;
+import org.apache.directory.server.core.interceptor.Interceptor;
+import org.apache.directory.server.core.interceptor.NextInterceptor;
+import org.apache.directory.server.core.interceptor.context.AddOperationContext;
+import org.apache.directory.server.core.interceptor.context.BindOperationContext;
+import org.apache.directory.server.core.interceptor.context.DeleteOperationContext;
+import org.apache.directory.server.core.interceptor.context.EntryOperationContext;
+import org.apache.directory.server.core.interceptor.context.GetMatchedNameOperationContext;
+import org.apache.directory.server.core.interceptor.context.GetRootDSEOperationContext;
+import org.apache.directory.server.core.interceptor.context.GetSuffixOperationContext;
+import org.apache.directory.server.core.interceptor.context.ListOperationContext;
+import org.apache.directory.server.core.interceptor.context.ListSuffixOperationContext;
+import org.apache.directory.server.core.interceptor.context.LookupOperationContext;
+import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
+import org.apache.directory.server.core.interceptor.context.MoveAndRenameOperationContext;
+import org.apache.directory.server.core.interceptor.context.MoveOperationContext;
+import org.apache.directory.server.core.interceptor.context.RenameOperationContext;
+import org.apache.directory.server.core.interceptor.context.SearchOperationContext;
+import org.apache.directory.server.core.invocation.InvocationStack;
+import org.apache.directory.server.core.jndi.LdapJndiProperties;
+import org.apache.directory.server.core.jndi.ServerContext;
+import org.apache.directory.shared.ldap.exception.LdapAuthenticationException;
+import org.apache.directory.shared.ldap.message.MessageTypeEnum;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.util.StringTools;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * An {@link Interceptor} that authenticates users.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ * @org.apache.xbean.XBean
+ */
+public class AuthenticationInterceptor extends BaseInterceptor
+{
+    private static final Logger LOG = LoggerFactory.getLogger( AuthenticationInterceptor.class );
+
+    /**
+     * Speedup for logs
+     */
+    private static final boolean IS_DEBUG = LOG.isDebugEnabled();
+
+    private Set<Authenticator> authenticators;
+    private final Map<String, Collection<Authenticator>> authenticatorsMapByType = new HashMap<String, Collection<Authenticator>>();
+
+    /**
+     * Creates an authentication service interceptor.
+     */
+    public AuthenticationInterceptor()
+    {
+    }
+
+    /**
+     * Registers and initializes all {@link Authenticator}s to this service.
+     */
+    public void init( DirectoryService directoryService ) throws NamingException
+    {
+
+        if ( authenticators == null )
+        {
+            setDefaultAuthenticators();
+        }
+        // Register all authenticators
+        for ( Authenticator authenticator : authenticators )
+        {
+            register( authenticator, directoryService );
+        }
+    }
+
+    private void setDefaultAuthenticators()
+    {
+        Set<Authenticator> set = new HashSet<Authenticator>();
+        set.add( new AnonymousAuthenticator() );
+        set.add( new SimpleAuthenticator() );
+        set.add( new StrongAuthenticator() );
+
+        setAuthenticators( set );
+    }
+
+
+    public Set<Authenticator> getAuthenticators()
+    {
+        return authenticators;
+    }
+
+    /**
+     * @param authenticators authenticators to be used by this AuthenticationInterceptor
+     * @org.apache.xbean.Property nestedType="org.apache.directory.server.core.authn.Authenticator"
+     */
+    public void setAuthenticators( Set<Authenticator> authenticators )
+    {
+        this.authenticators = authenticators;
+    }
+
+    /**
+     * Deinitializes and deregisters all {@link Authenticator}s from this service.
+     */
+    public void destroy()
+    {
+        authenticatorsMapByType.clear();
+        Set<Authenticator> copy = new HashSet<Authenticator>( authenticators );
+        authenticators = null;
+        for ( Authenticator authenticator : copy )
+        {
+            authenticator.destroy();
+        }
+    }
+
+    /**
+     * Initializes the specified {@link Authenticator} and registers it to
+     * this service.
+     *
+     * @param authenticator Authenticator to initialize and register by type
+     * @param directoryService configuration info to supply to the Authenticator during initialization
+     * @throws javax.naming.NamingException if initialization fails.
+     */
+    private void register( Authenticator authenticator, DirectoryService directoryService ) throws NamingException
+    {
+        authenticator.init( directoryService );
+
+        Collection<Authenticator> authenticatorList = getAuthenticators( authenticator.getAuthenticatorType() );
+
+        if ( authenticatorList == null )
+        {
+            authenticatorList = new ArrayList<Authenticator>();
+            authenticatorsMapByType.put( authenticator.getAuthenticatorType(), authenticatorList );
+        }
+
+        authenticatorList.add( authenticator );
+    }
+
+
+    /**
+     * Returns the list of {@link Authenticator}s with the specified type.
+     *
+     * @param type type of Authenticator sought
+     * @return A list of Authenticators of the requested type or <tt>null</tt> if no authenticator is found.
+     */
+    private Collection<Authenticator> getAuthenticators( String type )
+    {
+        Collection<Authenticator> result = authenticatorsMapByType.get( type );
+
+        if ( ( result != null ) && ( result.size() > 0 ) )
+        {
+            return result;
+        } else
+        {
+            return null;
+        }
+    }
+
+
+    public void add( NextInterceptor next, AddOperationContext opContext ) throws NamingException
+    {
+        if ( IS_DEBUG )
+        {
+            LOG.debug( "Adding the entry " +
+                    opContext.getEntry() +
+                    " for DN = '" + opContext.getDn().getUpName() + "'" );
+        }
+
+        checkAuthenticated( MessageTypeEnum.ADD_REQUEST );
+        next.add( opContext );
+    }
+
+
+    public void delete( NextInterceptor next, DeleteOperationContext opContext ) throws NamingException
+    {
+        if ( IS_DEBUG )
+        {
+            LOG.debug( "Deleting name = '" + opContext.getDn().getUpName() + "'" );
+        }
+
+        checkAuthenticated( MessageTypeEnum.DEL_REQUEST );
+        next.delete( opContext );
+        invalidateAuthenticatorCaches( opContext.getDn() );
+    }
+
+
+    public LdapDN getMatchedName( NextInterceptor next, GetMatchedNameOperationContext opContext ) throws NamingException
+    {
+        if ( IS_DEBUG )
+        {
+            LOG.debug( "Matching name = '" + opContext.getDn().getUpName() + "'" );
+        }
+
+        checkAuthenticated( "getMatchedName" );
+        return next.getMatchedName( opContext );
+    }
+
+
+    public ServerEntry getRootDSE( NextInterceptor next, GetRootDSEOperationContext opContext ) throws NamingException
+    {
+        if ( IS_DEBUG )
+        {
+            LOG.debug( "Getting root DSE" );
+        }
+
+        checkAuthenticated( "getRootDSE" );
+        return next.getRootDSE( opContext );
+    }
+
+
+    public LdapDN getSuffix( NextInterceptor next, GetSuffixOperationContext opContext ) throws NamingException
+    {
+        if ( IS_DEBUG )
+        {
+            LOG.debug( "Getting suffix for name = '" + opContext.getDn().getUpName() + "'" );
+        }
+
+        checkAuthenticated( "getSuffix" );
+        return next.getSuffix( opContext );
+    }
+
+
+    public boolean hasEntry( NextInterceptor next, EntryOperationContext opContext ) throws NamingException
+    {
+        if ( IS_DEBUG )
+        {
+            LOG.debug( "Testing if entry name = '" + opContext.getDn().getUpName() + "' exists" );
+        }
+
+        checkAuthenticated( "hasEntry" );
+        return next.hasEntry( opContext );
+    }
+
+
+    public NamingEnumeration<ServerSearchResult> list( NextInterceptor next, ListOperationContext opContext ) throws NamingException
+    {
+        if ( IS_DEBUG )
+        {
+            LOG.debug( "Listing base = '" + opContext.getDn().getUpName() + "'" );
+        }
+
+        checkAuthenticated( "list" );
+        return next.list( opContext );
+    }
+
+
+    public Iterator<String> listSuffixes( NextInterceptor next, ListSuffixOperationContext opContext ) throws NamingException
+    {
+        if ( IS_DEBUG )
+        {
+            LOG.debug( "Listing suffixes" );
+        }
+
+        checkAuthenticated( "listSuffixes" );
+        return next.listSuffixes( opContext );
+    }
+
+
+    public ServerEntry lookup( NextInterceptor next, LookupOperationContext opContext ) throws NamingException
+    {
+        if ( IS_DEBUG )
+        {
+            List<String> attrIds = opContext.getAttrsId();
+
+            if ( ( attrIds != null ) && ( attrIds.size() != 0 ) )
+            {
+                String attrs = StringTools.listToString( attrIds );
+                LOG.debug( "Lookup name = '" + opContext.getDn().getUpName() + "', attributes = " + attrs );
+            } else
+            {
+                LOG.debug( "Lookup name = '" + opContext.getDn().getUpName() + "', no attributes " );
+            }
+        }
+
+        checkAuthenticated( "lookup" );
+        return next.lookup( opContext );
+    }
+
+    private void invalidateAuthenticatorCaches( LdapDN principalDn )
+    {
+        for ( String authMech : authenticatorsMapByType.keySet() )
+        {
+            Collection<Authenticator> authenticators = getAuthenticators( authMech );
+
+            // try each authenticator
+            for ( Authenticator authenticator : authenticators )
+            {
+                authenticator.invalidateCache( principalDn );
+            }
+        }
+    }
+
+
+    public void modify( NextInterceptor next, ModifyOperationContext opContext ) throws NamingException
+    {
+        if ( IS_DEBUG )
+        {
+            LOG.debug( opContext.toString() );
+        }
+
+        checkAuthenticated( MessageTypeEnum.MODIFY_REQUEST );
+        next.modify( opContext );
+        invalidateAuthenticatorCaches( opContext.getDn() );
+    }
+
+
+    public void rename( NextInterceptor next, RenameOperationContext opContext ) throws NamingException
+    {
+        if ( IS_DEBUG )
+        {
+            LOG.debug( "Modifying name = '" + opContext.getDn().getUpName() + "', new RDN = '" +
+                    opContext.getNewRdn() + "', " +
+                    "oldRDN = '" + opContext.getDelOldDn() + "'" );
+        }
+
+        checkAuthenticated( MessageTypeEnum.MOD_DN_REQUEST );
+        next.rename( opContext );
+        invalidateAuthenticatorCaches( opContext.getDn() );
+    }
+
+
+    public void moveAndRename( NextInterceptor next, MoveAndRenameOperationContext opContext )
+            throws NamingException
+    {
+        if ( IS_DEBUG )
+        {
+            LOG.debug( "Moving name = '" + opContext.getDn().getUpName() + "' to name = '" +
+                    opContext.getParent() + "', new RDN = '" +
+                    opContext.getNewRdn() + "', oldRDN = '" +
+                    opContext.getDelOldDn() + "'" );
+        }
+
+        checkAuthenticated( MessageTypeEnum.MOD_DN_REQUEST );
+        next.moveAndRename( opContext );
+        invalidateAuthenticatorCaches( opContext.getDn() );
+    }
+
+
+    public void move( NextInterceptor next, MoveOperationContext opContext ) throws NamingException
+    {
+        if ( IS_DEBUG )
+        {
+            LOG.debug( "Moving name = '" + opContext.getDn().getUpName() + " to name = '" +
+                    opContext.getParent().getUpName() + "'" );
+        }
+
+        checkAuthenticated( MessageTypeEnum.MOD_DN_REQUEST );
+        next.move( opContext );
+        invalidateAuthenticatorCaches( opContext.getDn() );
+    }
+
+
+    public NamingEnumeration<ServerSearchResult> search( NextInterceptor next, SearchOperationContext opContext ) 
+        throws NamingException
+    {
+        if ( IS_DEBUG )
+        {
+            LOG.debug( "Search for base = '" + opContext.getDn().getUpName() + "'" );
+        }
+
+        checkAuthenticated( MessageTypeEnum.SEARCH_REQUEST );
+        return next.search( opContext );
+    }
+
+
+    private void checkAuthenticated( MessageTypeEnum operation ) throws NamingException
+    {
+        try
+        {
+            checkAuthenticated( operation.toString() );
+        }
+        catch ( IllegalStateException ise )
+        {
+            throw new IllegalStateException( "Attempted operation by unauthenticated caller." );
+        }
+    }
+
+    private void checkAuthenticated( String operation ) throws NamingException
+    {
+        ServerContext ctx = ( ServerContext ) InvocationStack.getInstance().peek().getCaller();
+
+        if ( ctx.getPrincipal() != null )
+        {
+            if ( ctx.getEnvironment().containsKey( Context.SECURITY_CREDENTIALS ) )
+            {
+                ctx.removeFromEnvironment( Context.SECURITY_CREDENTIALS );
+            }
+
+            return;
+        }
+
+        String principal = (String)ctx.getEnvironment().get( Context.SECURITY_PRINCIPAL ); 
+        String message = "Attempted operation '" + operation + "' by unauthenticated caller '" + principal + "'.";
+        LOG.error( message );
+        throw new IllegalStateException( message );
+    }
+
+
+    public void bind( NextInterceptor next, BindOperationContext opContext )
+            throws NamingException
+    {
+        // The DN is always normalized here
+        LdapDN normBindDn = opContext.getDn();
+        String bindUpDn = normBindDn.getUpName();
+
+        if ( IS_DEBUG )
+        {
+            LOG.debug( "Bind operation. bindDn: " + bindUpDn );
+        }
+
+        // check if we are already authenticated and if so we return making
+        // sure first that the credentials are not exposed within context
+        ServerContext ctx = ( ServerContext ) InvocationStack.getInstance().peek().getCaller();
+
+        if ( IS_DEBUG )
+        {
+            LOG.debug( "bind: principal: " + ctx.getPrincipal() );
+        }
+
+        if ( ctx.getPrincipal() != null )
+        {
+            if ( ctx.getEnvironment().containsKey( Context.SECURITY_CREDENTIALS ) )
+            {
+                ctx.removeFromEnvironment( Context.SECURITY_CREDENTIALS );
+            }
+
+            return;
+        }
+
+        // pick the first matching authenticator type
+        Collection<Authenticator> authenticators = null;
+
+        for ( String mechanism : opContext.getMechanisms() )
+        {
+            authenticators = getAuthenticators( mechanism );
+
+            if ( authenticators != null )
+            {
+                break;
+            }
+        }
+
+        if ( authenticators == null )
+        {
+            LOG.debug( "No authenticators found, delegating bind to the nexus." );
+
+            // as a last resort try binding via the nexus
+            next.bind( opContext );
+
+            LOG.debug( "Nexus succeeded on bind operation." );
+
+            // bind succeeded if we got this far 
+            ctx.setPrincipal( new TrustedPrincipalWrapper( new LdapPrincipal( normBindDn, LdapJndiProperties
+                    .getAuthenticationLevel( ctx.getEnvironment() ) ) ) );
+
+            // remove creds so there is no security risk
+            ctx.removeFromEnvironment( Context.SECURITY_CREDENTIALS );
+            return;
+        }
+
+        // TODO : we should refactor that.
+        // try each authenticator
+        for ( Authenticator authenticator : authenticators )
+        {
+            try
+            {
+                // perform the authentication
+                LdapPrincipal authorizationId = authenticator.authenticate( normBindDn, ctx );
+
+                // authentication was successful
+                ctx.setPrincipal( new TrustedPrincipalWrapper( authorizationId ) );
+
+                // remove creds so there is no security risk
+                ctx.removeFromEnvironment( Context.SECURITY_CREDENTIALS );
+
+                return;
+            }
+            catch ( LdapAuthenticationException e )
+            {
+                // authentication failed, try the next authenticator
+                if ( LOG.isInfoEnabled() )
+                {
+                    LOG.info( "Authenticator " + authenticator.getClass() + " failed to authenticate " + bindUpDn );
+                }
+            }
+            catch ( Exception e )
+            {
+                // Log other exceptions than LdapAuthenticationException
+                if ( LOG.isWarnEnabled() )
+                {
+                    LOG.warn( "Unexpected exception from " + authenticator.getClass() + " for principal " + bindUpDn, e );
+                }
+            }
+        }
+
+        if ( LOG.isInfoEnabled() )
+        {
+            LOG.info( "Cannot bind to the server " );
+        }
+
+        throw new LdapAuthenticationException();
+    }
+
+    /**
+     * FIXME This doesn't secure anything actually.
+     * <p/>
+     * Created this wrapper to pass to ctx.setPrincipal() which is public for added
+     * security.  This adds more security because an instance of this class is not
+     * easily accessible whereas LdapPrincipals can be accessed easily from a context
+     * althought they cannot be instantiated outside of the authn package.  Malicious
+     * code may not be able to set the principal to what they would like but they
+     * could switch existing principals using the now public ServerContext.setPrincipal()
+     * method.  To avoid this we make sure that this metho takes a TrustedPrincipalWrapper
+     * as opposed to the LdapPrincipal.  Only this service can create and call setPrincipal
+     * with a TrustedPrincipalWrapper.
+     */
+    public final class TrustedPrincipalWrapper
+    {
+        /**
+         * the wrapped ldap principal
+         */
+        private final LdapPrincipal principal;
+
+
+        /**
+         * Creates a TrustedPrincipalWrapper around an LdapPrincipal.
+         *
+         * @param principal the LdapPrincipal to wrap
+         */
+        private TrustedPrincipalWrapper( LdapPrincipal principal )
+        {
+            this.principal = principal;
+        }
+
+
+        /**
+         * Gets the LdapPrincipal this TrustedPrincipalWrapper wraps.
+         *
+         * @return the wrapped LdapPrincipal
+         */
+        public LdapPrincipal getPrincipal()
+        {
+            return principal;
+        }
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/authn/Authenticator.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/authn/Authenticator.java
new file mode 100644
index 0000000..1fc0804
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/authn/Authenticator.java
@@ -0,0 +1,85 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.authn;
+
+
+import javax.naming.Context;
+import javax.naming.NamingException;
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.jndi.ServerContext;
+import org.apache.directory.server.core.partition.PartitionNexus;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+
+
+/**
+ * Authenticates users who access {@link PartitionNexus}.
+ * <p>
+ * {@link Authenticator}s are registered to and configured by
+ * {@link AuthenticationInterceptor} interceptor.
+ * <p>
+ * {@link AuthenticationInterceptor} authenticates users by calling
+ * {@link #authenticate(LdapDN,ServerContext)}, and then {@link Authenticator}
+ * checks JNDI {@link Context} environment properties
+ * ({@link Context#SECURITY_PRINCIPAL} and {@link Context#SECURITY_CREDENTIALS})
+ * of current {@link Context}.
+ *
+ * @see AbstractAuthenticator
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public interface Authenticator
+{
+    /**
+     * Returns the type of this authenticator (e.g. <tt>'simple'</tt>,
+     * <tt>'none'</tt>,...).
+     */
+    String getAuthenticatorType();
+
+
+    /**
+     * Called by {@link AuthenticationInterceptor} to indicate that this
+     * authenticator is being placed into service.
+     */
+    void init( DirectoryService directoryService ) throws NamingException;
+
+
+    /**
+     * Called by {@link AuthenticationInterceptor} to indicate that this
+     * authenticator is being removed from service.
+     */
+    void destroy();
+
+    /**
+     * Callback used to respond to password changes by invalidating a password
+     * cache if implemented.  This is an additional feature of an authenticator
+     * which need not be implemented: empty implementation is sufficient.  This
+     * is called on every del, modify, and modifyRdn operation.
+     * 
+     * @param bindDn the already normalized distinguished name of the bind principal
+     */
+    void invalidateCache( LdapDN bindDn );
+
+    /**
+     * Performs authentication and returns the principal if succeeded.
+     */
+    LdapPrincipal authenticate( LdapDN bindDn, ServerContext ctx ) throws NamingException;
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/authn/LdapPrincipal.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/authn/LdapPrincipal.java
new file mode 100644
index 0000000..526fcc3
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/authn/LdapPrincipal.java
@@ -0,0 +1,159 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.authn;
+
+
+import java.io.Serializable;
+import java.security.Principal;
+
+import org.apache.directory.shared.ldap.constants.AuthenticationLevel;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.util.StringTools;
+
+
+/**
+ * An alternative X500 user implementation that has access to the distinguished
+ * name of the principal as well as the String representation.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public final class LdapPrincipal implements Principal, Serializable
+{
+    private static final long serialVersionUID = 3906650782395676720L;
+
+    /** the normalized distinguished name of the principal */
+    private final LdapDN name;
+
+    /** the no name anonymous user whose DN is the empty String */
+    public static final LdapPrincipal ANONYMOUS = new LdapPrincipal();
+
+    /** the authentication level for this principal */
+    private final AuthenticationLevel authenticationLevel;
+    
+    /** The userPassword
+     * @todo security risk remove this immediately
+     */
+    private byte[] userPassword;
+
+
+    /**
+     * Creates a new LDAP/X500 principal without any group associations.  Keep
+     * this package friendly so only code in the package can create a
+     * trusted principal.
+     *
+     * @param name the normalized distinguished name of the principal
+     * @param authenticationLevel the authentication level for this principal
+     */
+    public LdapPrincipal( LdapDN name, AuthenticationLevel authenticationLevel )
+    {
+        this.name = name;
+        if ( ! name.isNormalized() )
+        {
+            throw new IllegalStateException( "Names used for principals must be normalized!" );
+        }
+        this.authenticationLevel = authenticationLevel;
+        this.userPassword = null;
+    }
+
+    /**
+     * Creates a new LDAP/X500 principal without any group associations.  Keep
+     * this package friendly so only code in the package can create a
+     * trusted principal.
+     *
+     * @param name the normalized distinguished name of the principal
+     * @param authenticationLevel the authentication level for this principal
+     * @param userPassword The user password
+     */
+    public LdapPrincipal( LdapDN name, AuthenticationLevel authenticationLevel, byte[] userPassword )
+    {
+        this.name = name;
+        this.authenticationLevel = authenticationLevel;
+        this.userPassword = new byte[ userPassword.length ];
+        System.arraycopy( userPassword, 0, this.userPassword, 0, userPassword.length );
+    }
+
+
+    /**
+     * Creates a principal for the no name anonymous user whose DN is the empty
+     * String.
+     */
+    public LdapPrincipal()
+    {
+        name = new LdapDN();
+        authenticationLevel = AuthenticationLevel.NONE;
+        userPassword = null;
+    }
+
+
+    /**
+     * Gets a cloned copy of the normalized distinguished name of this
+     * principal as a JNDI {@link LdapDN}.
+     *
+     * @return the normalized distinguished name of the principal as a JNDI {@link LdapDN}
+     */
+    public LdapDN getJndiName()
+    {
+        return ( LdapDN ) name.clone();
+    }
+
+
+    /**
+     * Returns the normalized distinguished name of the principal as a String.
+     */
+    public String getName()
+    {
+        return name.getNormName();
+    }
+
+
+    /**
+     * Gets the authentication level associated with this LDAP principle.
+     *
+     * @return the authentication level
+     */
+    public AuthenticationLevel getAuthenticationLevel()
+    {
+        return authenticationLevel;
+    }
+
+
+    /**
+     * Returns string representation of the normalized distinguished name
+     * of this principal.
+     */
+    public String toString()
+    {
+        return "['" + name.getUpName() + "', '" + StringTools.utf8ToString( userPassword ) +"']'";
+    }
+
+
+    public byte[] getUserPassword()
+    {
+        return userPassword;
+    }
+
+
+    public void setUserPassword( byte[] userPassword )
+    {
+        this.userPassword = new byte[ userPassword.length ];
+        System.arraycopy( userPassword, 0, this.userPassword, 0, userPassword.length );
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/authn/SimpleAuthenticator.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/authn/SimpleAuthenticator.java
new file mode 100644
index 0000000..479efe4
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/authn/SimpleAuthenticator.java
@@ -0,0 +1,738 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.authn;
+
+
+import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.naming.Context;
+import javax.naming.NamingException;
+
+import org.apache.commons.collections.map.LRUMap;
+import org.apache.directory.server.core.authz.AciAuthorizationInterceptor;
+import org.apache.directory.server.core.authz.DefaultAuthorizationInterceptor;
+import org.apache.directory.server.core.collective.CollectiveAttributeInterceptor;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerStringValue;
+import org.apache.directory.server.core.event.EventInterceptor;
+import org.apache.directory.server.core.exception.ExceptionInterceptor;
+import org.apache.directory.server.core.interceptor.context.LookupOperationContext;
+import org.apache.directory.server.core.invocation.Invocation;
+import org.apache.directory.server.core.invocation.InvocationStack;
+import org.apache.directory.server.core.jndi.ServerContext;
+import org.apache.directory.server.core.normalization.NormalizationInterceptor;
+import org.apache.directory.server.core.operational.OperationalAttributeInterceptor;
+import org.apache.directory.server.core.partition.PartitionNexusProxy;
+import org.apache.directory.server.core.referral.ReferralInterceptor;
+import org.apache.directory.server.core.schema.SchemaInterceptor;
+import org.apache.directory.server.core.subtree.SubentryInterceptor;
+import org.apache.directory.server.core.trigger.TriggerInterceptor;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.constants.AuthenticationLevel;
+import org.apache.directory.shared.ldap.constants.LdapSecurityConstants;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.entry.Value;
+import org.apache.directory.shared.ldap.exception.LdapAuthenticationException;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.util.ArrayUtils;
+import org.apache.directory.shared.ldap.util.Base64;
+import org.apache.directory.shared.ldap.util.StringTools;
+import org.apache.directory.shared.ldap.util.UnixCrypt;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * A simple {@link Authenticator} that authenticates clear text passwords
+ * contained within the <code>userPassword</code> attribute in DIT. If the
+ * password is stored with a one-way encryption applied (e.g. SHA), the password
+ * is hashed the same way before comparison.
+ * 
+ * We use a cache to speedup authentication, where the DN/password are stored.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class SimpleAuthenticator extends AbstractAuthenticator
+{
+    private static final Logger LOG = LoggerFactory.getLogger( SimpleAuthenticator.class );
+    
+    /** A speedup for logger in debug mode */
+    private static final boolean IS_DEBUG = LOG.isDebugEnabled();
+
+    /**
+     * A cache to store passwords. It's a speedup, we will be able to avoid backend lookups.
+     * 
+     * Note that the backend also use a cache mechanism, but for performance gain, it's good 
+     * to manage a cache here. The main problem is that when a user modify his password, we will
+     * have to update it at three different places :
+     * - in the backend,
+     * - in the partition cache,
+     * - in this cache.
+     * 
+     * The update of the backend and partition cache is already correctly handled, so we will
+     * just have to offer an access to refresh the local cache.
+     * 
+     * We need to be sure that frequently used passwords be always in cache, and not discarded.
+     * We will use a LRU cache for this purpose. 
+     */ 
+    private final LRUMap credentialCache;
+    
+    /** Declare a default for this cache. 100 entries seems to be enough */
+    private static final int DEFAULT_CACHE_SIZE = 100;
+
+    /**
+     * Define the interceptors we should *not* go through when we will have to request the backend
+     * about a userPassword.
+     */
+    private static final Collection<String> USERLOOKUP_BYPASS;
+    static
+    {
+        Set<String> c = new HashSet<String>();
+        c.add( NormalizationInterceptor.class.getName() );
+        c.add( AuthenticationInterceptor.class.getName() );
+        c.add( ReferralInterceptor.class.getName() );
+        c.add( AciAuthorizationInterceptor.class.getName() );
+        c.add( DefaultAuthorizationInterceptor.class.getName() );
+        c.add( ExceptionInterceptor.class.getName() );
+        c.add( OperationalAttributeInterceptor.class.getName() );
+        c.add( SchemaInterceptor.class.getName() );
+        c.add( SubentryInterceptor.class.getName() );
+        c.add( CollectiveAttributeInterceptor.class.getName() );
+        c.add( EventInterceptor.class.getName() );
+        c.add( TriggerInterceptor.class.getName() );
+        USERLOOKUP_BYPASS = Collections.unmodifiableCollection( c );
+    }
+
+
+    /**
+     * Creates a new instance.
+     * @see AbstractAuthenticator
+     */
+    public SimpleAuthenticator()
+    {
+        super( AuthenticationLevel.SIMPLE.toString() );
+        credentialCache = new LRUMap( DEFAULT_CACHE_SIZE );
+    }
+
+    /**
+     * Creates a new instance, with an initial cache size
+     * @param cacheSize the size of the credential cache
+     */
+    public SimpleAuthenticator( int cacheSize )
+    {
+        super( AuthenticationLevel.SIMPLE.toString() );
+
+        credentialCache = new LRUMap( cacheSize > 0 ? cacheSize : DEFAULT_CACHE_SIZE );
+    }
+
+    /**
+     * A private class to store all informations about the existing
+     * password found in the cache or get from the backend.
+     * 
+     * This is necessary as we have to compute :
+     * - the used algorithm
+     * - the salt if any
+     * - the password itself.
+     * 
+     * If we have a on-way encrypted password, it is stored using this 
+     * format :
+     * {<algorithm>}<encrypted password>
+     * where the encrypted password format can be :
+     * - MD5/SHA : base64([<salt (8 bytes)>]<password>)
+     * - crypt : <salt (2 btytes)><password> 
+     * 
+     * Algorithm are currently MD5, SMD5, SHA, SSHA, CRYPT and empty
+     */
+    private class EncryptionMethod
+    {
+        private byte[] salt;
+        private LdapSecurityConstants algorithm;
+        
+        private EncryptionMethod( LdapSecurityConstants algorithm, byte[] salt )
+        {
+            this.algorithm = algorithm;
+            this.salt = salt;
+        }
+    }
+    
+    /**
+     * Get the password either from cache or from backend.
+     * @param principalDN The DN from which we want the password
+     * @return A byte array which can be empty if the password was not found
+     * @throws NamingException If we have a problem during the lookup operation
+     */
+    private LdapPrincipal getStoredPassword( Registries registries, LdapDN principalDN ) throws NamingException
+    {
+        LdapPrincipal principal;
+        String principalNorm = principalDN.getNormName();
+        
+        synchronized( credentialCache )
+        {
+            principal = (LdapPrincipal)credentialCache.get( principalNorm );
+        }
+        
+        byte[] storedPassword;
+        
+        if ( principal == null )
+        {
+            // Not found in the cache
+            // Get the user password from the backend
+            storedPassword = lookupUserPassword( registries, principalDN );
+            
+            
+            // Deal with the special case where the user didn't enter a password
+            // We will compare the empty array with the credentials. Sometime,
+            // a user does not set a password. This is bad, but there is nothing
+            // we can do against that, except education ...
+            if ( storedPassword == null )
+            {
+                storedPassword = ArrayUtils.EMPTY_BYTE_ARRAY;
+            }
+
+            // Create the new principal before storing it in the cache
+            principal = new LdapPrincipal( principalDN, AuthenticationLevel.SIMPLE, storedPassword );
+            
+            // Now, update the local cache.
+            synchronized( credentialCache )
+            {
+                credentialCache.put( principalDN.getNormName(), principal );
+            }
+        }
+        
+        return principal;
+    }
+
+    /**
+     * Get the user credentials from the environment. It is stored into the
+     * ServcerContext.
+     *
+     * @param ctx the naming context to get the credentials from
+     * @return the credentials
+     * @throws LdapAuthenticationException if the there are probelms with security
+     * credentials provided
+     */
+    private byte[] getCredentials( ServerContext ctx ) throws LdapAuthenticationException
+    {
+        Object creds = ctx.getEnvironment().get( Context.SECURITY_CREDENTIALS );
+        byte[] credentials;
+
+        if ( creds == null )
+        {
+            credentials = ArrayUtils.EMPTY_BYTE_ARRAY;
+        }
+        else if ( creds instanceof String )
+        {
+            credentials = StringTools.getBytesUtf8( ( String ) creds );
+        }
+        else if ( creds instanceof byte[] )
+        {
+            // This is the general case. When dealing with a BindRequest operation,
+            // received by the server, the credentials are always stored into a byte array
+            credentials = (byte[])creds;
+        }
+        else
+        {
+            LOG.info( "Incorrect credentials stored in {}", Context.SECURITY_CREDENTIALS );
+            throw new LdapAuthenticationException();
+        }
+        
+        return credentials;
+    }
+
+
+    /**
+     * Looks up <tt>userPassword</tt> attribute of the entry whose name is the
+     * value of {@link Context#SECURITY_PRINCIPAL} environment variable, and
+     * authenticates a user with the plain-text password.
+     * 
+     * We have at least 6 algorithms to encrypt the password :
+     * - SHA
+     * - SSHA (salted SHA)
+     * - MD5
+     * - SMD5 (slated MD5)
+     * - crypt (unix crypt)
+     * - plain text, ie no encryption.
+     * 
+     *  If we get an encrypted password, it is prefixed by the used algorithm, between
+     *  brackets : {SSHA}password ...
+     *  
+     *  If the password is using SSHA, SMD5 or crypt, some 'salt' is added to the password :
+     *  - length(password) - 20, starting at 21th position for SSHA
+     *  - length(password) - 16, starting at 16th position for SMD5
+     *  - length(password) - 2, starting at 3rd position for crypt
+     *  
+     *  For (S)SHA and (S)MD5, we have to transform the password from Base64 encoded text
+     *  to a byte[] before comparing the password with the stored one.
+     *  For crypt, we only have to remove the salt.
+     *  
+     *  At the end, we use the digest() method for (S)SHA and (S)MD5, the crypt() method for
+     *  the CRYPT algorithm and a straight comparison for PLAIN TEXT passwords.
+     *  
+     *  The stored password is always using the unsalted form, and is stored as a bytes array.
+     */
+    public LdapPrincipal authenticate( LdapDN principalDn, ServerContext ctx ) throws NamingException
+    {
+        if ( IS_DEBUG )
+        {
+            LOG.debug( "Authenticating {}", principalDn );
+        }
+        
+        // ---- extract password from JNDI environment
+        byte[] credentials = getCredentials( ctx );
+        
+        LdapPrincipal principal = getStoredPassword( getDirectoryService().getRegistries(), principalDn );
+        
+        // Get the stored password, either from cache or from backend
+        byte[] storedPassword = principal.getUserPassword();
+        
+        // Short circuit for PLAIN TEXT passwords : we compare the byte array directly
+        // Are the passwords equal ?
+        if ( Arrays.equals( credentials, storedPassword ) )
+        {
+            if ( IS_DEBUG )
+            {
+                LOG.debug( "{} Authenticated", principalDn );
+            }
+            
+            return principal;
+        }
+        
+        // Let's see if the stored password was encrypted
+        LdapSecurityConstants algorithm = findAlgorithm( storedPassword );
+        
+        if ( algorithm != null )
+        {
+            EncryptionMethod encryptionMethod = new EncryptionMethod( algorithm, null );
+            
+            // Let's get the encrypted part of the stored password
+            // We should just keep the password, excluding the algorithm
+            // and the salt, if any.
+            // But we should also get the algorithm and salt to
+            // be able to encrypt the submitted user password in the next step
+            byte[] encryptedStored = splitCredentials( storedPassword, encryptionMethod );
+            
+            // Reuse the slatedPassword informations to construct the encrypted
+            // password given by the user.
+            byte[] userPassword = encryptPassword( credentials, encryptionMethod );
+            
+            // Now, compare the two passwords.
+            if ( Arrays.equals( userPassword, encryptedStored ) )
+            {
+                if ( IS_DEBUG )
+                {
+                    LOG.debug( "{} Authenticated", principalDn );
+                }
+
+                return principal;
+            }
+            else
+            {
+                // Bad password ...
+                String message = "Password not correct for user '" + principalDn.getUpName() + "'";
+                LOG.info( message );
+                throw new LdapAuthenticationException(message);
+            }
+        }
+        else
+        {
+            // Bad password ...
+            String message = "Password not correct for user '" + principalDn.getUpName() + "'";
+            LOG.info( message );
+            throw new LdapAuthenticationException(message);
+        }
+    }
+    
+    private static void split( byte[] all, int offset, byte[] left, byte[] right )
+    {
+        System.arraycopy( all, offset, left, 0, left.length );
+        System.arraycopy( all, offset + left.length, right, 0, right.length );
+    }
+
+    /**
+     * Decopose the stored password in an algorithm, an eventual salt
+     * and the password itself.
+     * 
+     * If the algorithm is SHA, SSHA, MD5 or SMD5, the part following the algorithm
+     * is base64 encoded
+     * 
+     * @param encryptionMethod The structure to feed
+     * @return The password
+     * @param credentials the credentials to split
+     */
+    private byte[] splitCredentials( byte[] credentials, EncryptionMethod encryptionMethod )
+    {
+        int pos = encryptionMethod.algorithm.getName().length() + 2;
+        
+        switch ( encryptionMethod.algorithm )
+        {
+            case HASH_METHOD_MD5 :
+            case HASH_METHOD_SHA :
+                try
+                {
+                    // We just have the password just after the algorithm, base64 encoded.
+                    // Just decode the password and return it.
+                    return Base64.decode( new String( credentials, pos, credentials.length - pos, "UTF-8" ).toCharArray() );
+                }
+                catch ( UnsupportedEncodingException uee )
+                {
+                    // do nothing 
+                    return credentials;
+                }
+                
+            case HASH_METHOD_SMD5 :
+            case HASH_METHOD_SSHA :
+                try
+                {
+                    // The password is associated with a salt. Decompose it 
+                    // in two parts, after having decoded the password.
+                    // The salt will be stored into the EncryptionMethod structure
+                    // The salt is at the end of the credentials, and is 8 bytes long
+                    byte[] passwordAndSalt = Base64.decode( new String( credentials, pos, credentials.length - pos, "UTF-8" ).
+                        toCharArray() );
+                    
+                    encryptionMethod.salt = new byte[8];
+                    byte[] password = new byte[passwordAndSalt.length - encryptionMethod.salt.length];
+                    split( passwordAndSalt, 0, password, encryptionMethod.salt );
+                    
+                    return password;
+                }
+                catch ( UnsupportedEncodingException uee )
+                {
+                    // do nothing 
+                    return credentials;
+                }
+                
+            case HASH_METHOD_CRYPT :
+                // The password is associated with a salt. Decompose it 
+                // in two parts, storing the salt into the EncryptionMethod structure.
+                // The salt comes first, not like for SSHA and SMD5, and is 2 bytes long
+                encryptionMethod.salt = new byte[2];
+                byte[] password = new byte[credentials.length - encryptionMethod.salt.length - pos];
+                split( credentials, pos, encryptionMethod.salt, password );
+                
+                return password;
+                
+            default :
+                // unknown method
+                return credentials;
+                
+        }
+    }
+    
+    /**
+     * Get the algorithm from the stored password. 
+     * It can be found on the beginning of the stored password, between 
+     * curly brackets.
+     * @param credentials the credentials of the user
+     * @return the name of the algorithm to use
+     * TODO use an enum for the algorithm
+     */
+    private LdapSecurityConstants findAlgorithm( byte[] credentials )
+    {
+        if ( ( credentials == null ) || ( credentials.length == 0 ) )
+        {
+            return null;
+        }
+        
+        if ( credentials[0] == '{' )
+        {
+            // get the algorithm
+            int pos = 1;
+            
+            while ( pos < credentials.length )
+            {
+                if ( credentials[pos] == '}' )
+                {
+                    break;
+                }
+                
+                pos++;
+            }
+            
+            if ( pos < credentials.length )
+            {
+                if ( pos == 1 )
+                {
+                    // We don't have an algorithm : return the credentials as is
+                    return null;
+                }
+                
+                String algorithm = new String( credentials, 1, pos - 1 ).toLowerCase();
+                
+                return LdapSecurityConstants.getAlgorithm( algorithm );
+            }
+            else
+            {
+                // We don't have an algorithm
+                return null;
+            }
+        }
+        else
+        {
+            // No '{algo}' part
+            return null;
+        }
+    }
+
+    /**
+     * Compute the hashed password given an algorithm, the credentials and 
+     * an optional salt.
+     *
+     * @param algorithm the algorithm to use
+     * @param password the credentials
+     * @param salt the optional salt
+     * @return the digested credentials
+     */
+    private static byte[] digest( LdapSecurityConstants algorithm, byte[] password, byte[] salt )
+    {
+        MessageDigest digest;
+
+        try
+        {
+            digest = MessageDigest.getInstance( algorithm.getName() );
+        }
+        catch ( NoSuchAlgorithmException e1 )
+        {
+            return null;
+        }
+
+        if ( salt != null )
+        {
+            digest.update( password );
+            digest.update( salt );
+            return digest.digest();
+        }
+        else
+        {
+            return digest.digest( password );
+        }
+    }
+
+    private byte[] encryptPassword( byte[] credentials, EncryptionMethod encryptionMethod )
+    {
+        byte[] salt = encryptionMethod.salt;
+        
+        switch ( encryptionMethod.algorithm )
+        {
+            case HASH_METHOD_SHA :
+            case HASH_METHOD_SSHA :
+                return digest( LdapSecurityConstants.HASH_METHOD_SHA, credentials, salt );
+
+            case HASH_METHOD_MD5 :
+            case HASH_METHOD_SMD5 :
+                return digest( LdapSecurityConstants.HASH_METHOD_MD5, credentials, salt );
+
+            case HASH_METHOD_CRYPT :
+                if ( salt == null )
+                {
+                    salt = new byte[2];
+                    SecureRandom sr = new SecureRandom();
+                    int i1 = sr.nextInt( 64 );
+                    int i2 = sr.nextInt( 64 );
+                
+                    salt[0] = ( byte ) ( i1 < 12 ? ( i1 + '.' ) : i1 < 38 ? ( i1 + 'A' - 12 ) : ( i1 + 'a' - 38 ) );
+                    salt[1] = ( byte ) ( i2 < 12 ? ( i2 + '.' ) : i2 < 38 ? ( i2 + 'A' - 12 ) : ( i2 + 'a' - 38 ) );
+                }
+
+                String saltWithCrypted = UnixCrypt.crypt( StringTools.utf8ToString( credentials ), 
+                    StringTools.utf8ToString( salt ) );
+                String crypted = saltWithCrypted.substring( 2 );
+                
+                return StringTools.getBytesUtf8( crypted );
+                
+            default :
+                return credentials;
+        }
+    }
+
+    /**
+     * Local function which request the password from the backend
+     * @param principalDn the principal to lookup
+     * @return the credentials from the backend
+     * @throws NamingException if there are problems accessing backend
+     */
+    private byte[] lookupUserPassword( Registries registries, LdapDN principalDn ) throws NamingException
+    {
+        // ---- lookup the principal entry's userPassword attribute
+        Invocation invocation = InvocationStack.getInstance().peek();
+        PartitionNexusProxy proxy = invocation.getProxy();
+        ServerEntry userEntry;
+
+        try
+        {
+            LookupOperationContext lookupContex  = new LookupOperationContext( registries, 
+                new String[] { SchemaConstants.USER_PASSWORD_AT } );
+            lookupContex.setDn( principalDn );
+            
+            userEntry = proxy.lookup( lookupContex, USERLOOKUP_BYPASS ); 
+
+            if ( userEntry == null )
+            {
+                throw new LdapAuthenticationException( "Failed to lookup user for authentication: " + principalDn );
+            }
+        }
+        catch ( Exception cause )
+        {
+            LOG.error( "Authentication error : " + cause.getMessage() );
+            LdapAuthenticationException e = new LdapAuthenticationException();
+            e.setRootCause( e );
+            throw e;
+        }
+
+        Value<?> userPassword;
+
+        EntryAttribute userPasswordAttr = userEntry.get( SchemaConstants.USER_PASSWORD_AT );
+
+        // ---- assert that credentials match
+        if ( userPasswordAttr == null )
+        {
+            return StringTools.EMPTY_BYTES;
+        }
+        else
+        {
+            userPassword = userPasswordAttr.get();
+
+            if ( userPassword instanceof ServerStringValue )
+            {
+                return StringTools.getBytesUtf8( (String)userPassword.get() );
+            }
+            else
+            {
+                return (byte[])userPassword.get();
+            }
+        }
+    }
+
+    /**
+     * Get the algorithm of a password, which is stored in the form "{XYZ}...".
+     * The method returns null, if the argument is not in this form. It returns
+     * XYZ, if XYZ is an algorithm known to the MessageDigest class of
+     * java.security.
+     * 
+     * @param password a byte[]
+     * @return included message digest alorithm, if any
+     * @throws IllegalArgumentException if the algorithm cannot be identified
+     */
+    protected String getAlgorithmForHashedPassword( byte[] password ) throws IllegalArgumentException
+    {
+        String result = null;
+
+        // Check if password arg is string or byte[]
+        String sPassword = StringTools.utf8ToString( password );
+        int rightParen = sPassword.indexOf( '}' );
+
+        if ( ( sPassword.length() > 2 ) &&
+             ( sPassword.charAt( 0 ) == '{' ) &&
+             ( rightParen > -1 ) )
+        {
+            String algorithm = sPassword.substring( 1, rightParen );
+
+            if ( LdapSecurityConstants.HASH_METHOD_CRYPT.getName().equalsIgnoreCase( algorithm ) )
+            {
+                return algorithm;
+            }
+            
+            try
+            {
+                MessageDigest.getInstance( algorithm );
+                result = algorithm;
+            }
+            catch ( NoSuchAlgorithmException e )
+            {
+                LOG.warn( "Unknown message digest algorithm in password: " + algorithm, e );
+            }
+        }
+
+        return result;
+    }
+
+
+    /**
+     * Creates a digested password. For a given hash algorithm and a password
+     * value, the algorithm is applied to the password, and the result is Base64
+     * encoded. The method returns a String which looks like "{XYZ}bbbbbbb",
+     * whereas XYZ is the name of the algorithm, and bbbbbbb is the Base64
+     * encoded value of XYZ applied to the password.
+     * 
+     * @param algorithm
+     *            an algorithm which is supported by
+     *            java.security.MessageDigest, e.g. SHA
+     * @param password
+     *            password value, a byte[]
+     * 
+     * @return a digested password, which looks like
+     *         {SHA}LhkDrSoM6qr0fW6hzlfOJQW61tc=
+     * 
+     * @throws IllegalArgumentException
+     *             if password is neither a String nor a byte[], or algorithm is
+     *             not known to java.security.MessageDigest class
+     */
+    protected String createDigestedPassword( String algorithm, byte[] password ) throws IllegalArgumentException
+    {
+        // create message digest object
+        try
+        {
+            if ( LdapSecurityConstants.HASH_METHOD_CRYPT.getName().equalsIgnoreCase( algorithm ) )
+            {
+                String saltWithCrypted = UnixCrypt.crypt( StringTools.utf8ToString( password ), "" );
+                String crypted = saltWithCrypted.substring( 2 );
+                return '{' + algorithm + '}' + Arrays.toString( StringTools.getBytesUtf8( crypted ) );
+            }
+            else
+            {
+                MessageDigest digest = MessageDigest.getInstance( algorithm );
+                
+                // calculate hashed value of password
+                byte[] fingerPrint = digest.digest( password );
+                char[] encoded = Base64.encode( fingerPrint );
+
+                // create return result of form "{alg}bbbbbbb"
+                return '{' + algorithm + '}' + new String( encoded );
+            }
+        }
+        catch ( NoSuchAlgorithmException nsae )
+        {
+            LOG.error( "Cannot create a digested password for algorithm '{}'", algorithm );
+            throw new IllegalArgumentException( nsae.getMessage() );
+        }
+    }
+
+    /**
+     * Remove the principal form the cache. This is used when the user changes
+     * his password.
+     */
+    public void invalidateCache( LdapDN bindDn )
+    {
+        synchronized( credentialCache )
+        {
+            credentialCache.remove( bindDn.getNormName() );
+        }
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/authn/StrongAuthenticator.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/authn/StrongAuthenticator.java
new file mode 100644
index 0000000..bed8d72
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/authn/StrongAuthenticator.java
@@ -0,0 +1,59 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.authn;
+
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.core.jndi.ServerContext;
+import org.apache.directory.shared.ldap.constants.AuthenticationLevel;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+
+/**
+ * An {@link Authenticator} that handles SASL connections (X.501 authentication
+ * level <tt>'strong'</tt>).  The principal has been authenticated during SASL
+ * negotiation; therefore, no additional authentication is necessary in this
+ * {@link Authenticator}.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class StrongAuthenticator extends AbstractAuthenticator
+{
+    /**
+     * Creates a new instance of SaslAuthenticator.
+     */
+    public StrongAuthenticator()
+    {
+        super( AuthenticationLevel.STRONG.toString() );
+    }
+
+
+    /**
+     * User has already been authenticated during SASL negotiation.  Set the authentication level
+     * to strong and return an {@link LdapPrincipal}.
+     */
+    public LdapPrincipal authenticate( LdapDN principalDn, ServerContext ctx ) throws NamingException
+    {
+        // Possibly check if user account is disabled, other account checks.
+        return new LdapPrincipal( principalDn, AuthenticationLevel.STRONG );
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/authz/AciAuthorizationInterceptor.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/authz/AciAuthorizationInterceptor.java
new file mode 100644
index 0000000..787f083
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/authz/AciAuthorizationInterceptor.java
@@ -0,0 +1,1256 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.authz;
+
+
+import javax.naming.directory.SearchControls;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.directory.server.constants.ServerDNConstants;
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.authn.LdapPrincipal;
+import org.apache.directory.server.core.authz.support.ACDFEngine;
+import org.apache.directory.server.core.entry.ServerAttribute;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerEntryUtils;
+import org.apache.directory.server.core.entry.ServerSearchResult;
+import org.apache.directory.server.core.enumeration.SearchResultFilter;
+import org.apache.directory.server.core.enumeration.SearchResultFilteringEnumeration;
+import org.apache.directory.server.core.interceptor.BaseInterceptor;
+import org.apache.directory.server.core.interceptor.InterceptorChain;
+import org.apache.directory.server.core.interceptor.NextInterceptor;
+import org.apache.directory.server.core.interceptor.context.AddOperationContext;
+import org.apache.directory.server.core.interceptor.context.CompareOperationContext;
+import org.apache.directory.server.core.interceptor.context.DeleteOperationContext;
+import org.apache.directory.server.core.interceptor.context.EntryOperationContext;
+import org.apache.directory.server.core.interceptor.context.GetMatchedNameOperationContext;
+import org.apache.directory.server.core.interceptor.context.ListOperationContext;
+import org.apache.directory.server.core.interceptor.context.LookupOperationContext;
+import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
+import org.apache.directory.server.core.interceptor.context.MoveAndRenameOperationContext;
+import org.apache.directory.server.core.interceptor.context.MoveOperationContext;
+import org.apache.directory.server.core.interceptor.context.RenameOperationContext;
+import org.apache.directory.server.core.interceptor.context.SearchOperationContext;
+import org.apache.directory.server.core.invocation.Invocation;
+import org.apache.directory.server.core.invocation.InvocationStack;
+import org.apache.directory.server.core.jndi.ServerContext;
+import org.apache.directory.server.core.jndi.ServerLdapContext;
+import org.apache.directory.server.core.partition.PartitionNexusProxy;
+import org.apache.directory.server.core.subtree.SubentryInterceptor;
+import org.apache.directory.server.schema.ConcreteNameComponentNormalizer;
+import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
+import org.apache.directory.server.schema.registries.OidRegistry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.aci.ACIItem;
+import org.apache.directory.shared.ldap.aci.ACIItemParser;
+import org.apache.directory.shared.ldap.aci.ACITuple;
+import org.apache.directory.shared.ldap.aci.MicroOperation;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.entry.Modification;
+import org.apache.directory.shared.ldap.entry.Value;
+import org.apache.directory.shared.ldap.exception.LdapNamingException;
+import org.apache.directory.shared.ldap.exception.LdapNoPermissionException;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * An ACI based authorization service.
+ *
+ * @org.apache.xbean.XBean
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class AciAuthorizationInterceptor extends BaseInterceptor
+{
+    /** the logger for this class */
+    private static final Logger LOG = LoggerFactory.getLogger( AciAuthorizationInterceptor.class );
+
+    /**
+     * the multivalued op attr used to track the perscriptive access control
+     * subentries that apply to an entry.
+     */
+    private static final String AC_SUBENTRY_ATTR = "accessControlSubentries";
+
+    private static final Collection<MicroOperation> ADD_PERMS;
+    private static final Collection<MicroOperation> READ_PERMS;
+    private static final Collection<MicroOperation> COMPARE_PERMS;
+    private static final Collection<MicroOperation> SEARCH_ENTRY_PERMS;
+    private static final Collection<MicroOperation> SEARCH_ATTRVAL_PERMS;
+    private static final Collection<MicroOperation> REMOVE_PERMS;
+    private static final Collection<MicroOperation> MATCHEDNAME_PERMS;
+    private static final Collection<MicroOperation> BROWSE_PERMS;
+    private static final Collection<MicroOperation> LOOKUP_PERMS;
+    private static final Collection<MicroOperation> REPLACE_PERMS;
+    private static final Collection<MicroOperation> RENAME_PERMS;
+    private static final Collection<MicroOperation> EXPORT_PERMS;
+    private static final Collection<MicroOperation> IMPORT_PERMS;
+    private static final Collection<MicroOperation> MOVERENAME_PERMS;
+
+    static
+    {
+        Set<MicroOperation> set = new HashSet<MicroOperation>( 2 );
+        set.add( MicroOperation.BROWSE );
+        set.add( MicroOperation.RETURN_DN );
+        SEARCH_ENTRY_PERMS = Collections.unmodifiableCollection( set );
+
+        set = new HashSet<MicroOperation>( 2 );
+        set.add( MicroOperation.READ );
+        set.add( MicroOperation.BROWSE );
+        LOOKUP_PERMS = Collections.unmodifiableCollection( set );
+
+        set = new HashSet<MicroOperation>( 2 );
+        set.add( MicroOperation.ADD );
+        set.add( MicroOperation.REMOVE );
+        REPLACE_PERMS = Collections.unmodifiableCollection( set );
+
+        set = new HashSet<MicroOperation>( 2 );
+        set.add( MicroOperation.EXPORT );
+        set.add( MicroOperation.RENAME );
+        MOVERENAME_PERMS = Collections.unmodifiableCollection( set );
+
+        SEARCH_ATTRVAL_PERMS = Collections.singleton( MicroOperation.READ );
+        ADD_PERMS = Collections.singleton( MicroOperation.ADD );
+        READ_PERMS = Collections.singleton( MicroOperation.READ );
+        COMPARE_PERMS = Collections.singleton( MicroOperation.COMPARE );
+        REMOVE_PERMS = Collections.singleton( MicroOperation.REMOVE );
+        MATCHEDNAME_PERMS = Collections.singleton( MicroOperation.DISCLOSE_ON_ERROR );
+        BROWSE_PERMS = Collections.singleton( MicroOperation.BROWSE );
+        RENAME_PERMS = Collections.singleton( MicroOperation.RENAME );
+        EXPORT_PERMS = Collections.singleton( MicroOperation.EXPORT );
+        IMPORT_PERMS = Collections.singleton( MicroOperation.IMPORT );
+    }
+
+    /** a tupleCache that responds to add, delete, and modify attempts */
+    private TupleCache tupleCache;
+    
+    /** a groupCache that responds to add, delete, and modify attempts */
+    private GroupCache groupCache;
+    
+    /** a normalizing ACIItem parser */
+    private ACIItemParser aciParser;
+    
+    /** use and instance of the ACDF engine */
+    private ACDFEngine engine;
+    
+    /** interceptor chain */
+    private InterceptorChain chain;
+    
+    /** Global registries */
+    private Registries registries;
+    
+    /** attribute type registry */
+    private AttributeTypeRegistry atRegistry;
+    
+    /** whether or not this interceptor is activated */
+    private boolean enabled;
+    
+    /** the system wide subschemaSubentryDn */
+    private String subschemaSubentryDn;
+
+    private AttributeType objectClassType;
+    private AttributeType acSubentryType;
+
+    private String subentryOid;
+
+    /** A storage for the entryACI attributeType */
+    private AttributeType entryAciType;
+
+    /** the subentry ACI attribute type */
+    private AttributeType subentryAciType;
+    
+    public static final SearchControls DEFAULT_SEARCH_CONTROLS = new SearchControls();
+
+    /**
+     * Initializes this interceptor based service by getting a handle on the nexus, setting up
+     * the tupe and group membership caches and the ACIItem parser and the ACDF engine.
+     *
+     * @param directoryService the directory service core
+     * @throws NamingException if there are problems during initialization
+     */
+    public void init( DirectoryService directoryService ) throws NamingException
+    {
+        super.init( directoryService );
+
+        tupleCache = new TupleCache( directoryService );
+        groupCache = new GroupCache( directoryService );
+        registries = directoryService.getRegistries();
+        atRegistry = registries.getAttributeTypeRegistry();
+        OidRegistry oidRegistry = registries.getOidRegistry();
+        
+        // look up some constant information
+        String objectClassOid = oidRegistry.getOid( SchemaConstants.OBJECT_CLASS_AT );
+        subentryOid = oidRegistry.getOid( SchemaConstants.SUBENTRY_OC );
+        String acSubentryOid = oidRegistry.getOid( AC_SUBENTRY_ATTR );
+        objectClassType = atRegistry.lookup( objectClassOid );
+        acSubentryType = atRegistry.lookup( acSubentryOid );
+        entryAciType = atRegistry.lookup( SchemaConstants.ENTRY_ACI_AT_OID ); 
+        subentryAciType = atRegistry.lookup( SchemaConstants.SUBENTRY_ACI_AT_OID );
+        
+        aciParser = new ACIItemParser( new ConcreteNameComponentNormalizer( atRegistry, oidRegistry ), atRegistry.getNormalizerMapping() );
+        engine = new ACDFEngine( registries.getOidRegistry(), atRegistry );
+        chain = directoryService.getInterceptorChain();
+        enabled = directoryService.isAccessControlEnabled();
+
+        // stuff for dealing with subentries (garbage for now)
+        Value<?> subschemaSubentry = 
+            directoryService.getPartitionNexus().getRootDSE( null ).
+                get( SchemaConstants.SUBSCHEMA_SUBENTRY_AT ).get();
+        LdapDN subschemaSubentryDnName = new LdapDN( (String)(subschemaSubentry.get()) );
+        subschemaSubentryDnName.normalize( atRegistry.getNormalizerMapping() );
+        subschemaSubentryDn = subschemaSubentryDnName.toNormName();
+    }
+
+
+    private void protectCriticalEntries( LdapDN dn ) throws NamingException
+    {
+        LdapDN principalDn = getPrincipal().getJndiName();
+
+        if ( dn.isEmpty() )
+        {
+            String msg = "The rootDSE cannot be deleted, moved or renamed!";
+            LOG.error( msg );
+            throw new LdapNoPermissionException( msg );
+        }
+
+        if ( isTheAdministrator( dn ) )
+        {
+            String msg = "User '" + principalDn.getUpName();
+            msg += "' does not have permission to move or rename the admin";
+            msg += " account.  No one not even the admin can del, move or";
+            msg += " rename " + dn.getUpName() + "!";
+            LOG.error( msg );
+            throw new LdapNoPermissionException( msg );
+        }
+    }
+
+
+    /**
+     * Adds perscriptiveACI tuples to a collection of tuples by accessing the
+     * tupleCache.  The tuple cache is accessed for each A/C subentry
+     * associated with the protected entry.  Note that subentries are handled
+     * differently: their parent, the administrative entry is accessed to
+     * determine the perscriptiveACIs effecting the AP and hence the subentry
+     * which is considered to be in the same context.
+     *
+     * @param tuples the collection of tuples to add to
+     * @param dn the normalized distinguished name of the protected entry
+     * @param entry the target entry that access to is being controled
+     * @throws NamingException if there are problems accessing attribute values
+     * @param proxy the partition nexus proxy object
+     */
+    private void addPerscriptiveAciTuples( PartitionNexusProxy proxy, Collection<ACITuple> tuples, LdapDN dn,
+        ServerEntry entry ) throws NamingException
+    {
+        EntryAttribute oc = entry.get( objectClassType );
+        
+        /*
+         * If the protected entry is a subentry, then the entry being evaluated
+         * for perscriptiveACIs is in fact the administrative entry.  By
+         * substituting the administrative entry for the actual subentry the
+         * code below this "if" statement correctly evaluates the effects of
+         * perscriptiveACI on the subentry.  Basically subentries are considered
+         * to be in the same naming context as their access point so the subentries
+         * effecting their parent entry applies to them as well.
+         */
+        if ( oc.contains( SchemaConstants.SUBENTRY_OC ) || oc.contains( subentryOid ) )
+        {
+            LdapDN parentDn = ( LdapDN ) dn.clone();
+            parentDn.remove( dn.size() - 1 );
+            entry = proxy.lookup( new LookupOperationContext( registries, parentDn), PartitionNexusProxy.LOOKUP_BYPASS );
+        }
+
+        EntryAttribute subentries = entry.get( acSubentryType );
+        
+        if ( subentries == null )
+        {
+            return;
+        }
+        
+        for ( Value<?> value:subentries )
+        {
+            String subentryDn = ( String ) value.get();
+            tuples.addAll( tupleCache.getACITuples( subentryDn ) );
+        }
+    }
+
+
+    /**
+     * Adds the set of entryACI tuples to a collection of tuples.  The entryACI
+     * is parsed and tuples are generated on they fly then added to the collection.
+     *
+     * @param tuples the collection of tuples to add to
+     * @param entry the target entry that access to is being regulated
+     * @throws NamingException if there are problems accessing attribute values
+     */
+    private void addEntryAciTuples( Collection<ACITuple> tuples, ServerEntry entry ) throws NamingException
+    {
+        EntryAttribute entryAci = entry.get( entryAciType );
+        
+        if ( entryAci == null )
+        {
+            return;
+        }
+
+        for ( Value<?> value:entryAci )
+        {
+            String aciString = ( String ) value.get();
+            ACIItem item;
+
+            try
+            {
+                item = aciParser.parse( aciString );
+            }
+            catch ( ParseException e )
+            {
+                String msg = "failed to parse entryACI: " + aciString;
+                LOG.error( msg, e );
+                throw new LdapNamingException( msg, ResultCodeEnum.OPERATIONS_ERROR );
+            }
+
+            tuples.addAll( item.toTuples() );
+        }
+    }
+
+
+    /**
+     * Adds the set of subentryACI tuples to a collection of tuples.  The subentryACI
+     * is parsed and tuples are generated on the fly then added to the collection.
+     *
+     * @param tuples the collection of tuples to add to
+     * @param dn the normalized distinguished name of the protected entry
+     * @param entry the target entry that access to is being regulated
+     * @throws NamingException if there are problems accessing attribute values
+     * @param proxy the partition nexus proxy object
+     */
+    private void addSubentryAciTuples( PartitionNexusProxy proxy, Collection<ACITuple> tuples, LdapDN dn, ServerEntry entry )
+        throws NamingException
+    {
+        // only perform this for subentries
+        if ( !entry.contains( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.SUBENTRY_OC ) )
+        {
+            return;
+        }
+
+        // get the parent or administrative entry for this subentry since it
+        // will contain the subentryACI attributes that effect subentries
+        LdapDN parentDn = ( LdapDN ) dn.clone();
+        parentDn.remove( dn.size() - 1 );
+        ServerEntry administrativeEntry =  
+            proxy.lookup( 
+                new LookupOperationContext( 
+                    registries, 
+                    parentDn, 
+                    new String[]
+                               { SchemaConstants.SUBENTRY_ACI_AT }) , 
+                PartitionNexusProxy.LOOKUP_BYPASS );
+        
+        EntryAttribute subentryAci = administrativeEntry.get( subentryAciType );
+
+        if ( subentryAci == null )
+        {
+            return;
+        }
+
+        for ( Value<?> value:subentryAci )
+        {
+            String aciString = ( String ) value.get();
+            ACIItem item;
+
+            try
+            {
+                item = aciParser.parse( aciString );
+            }
+            catch ( ParseException e )
+            {
+                String msg = "failed to parse subentryACI: " + aciString;
+                LOG.error( msg, e );
+                throw new LdapNamingException( msg, ResultCodeEnum.OPERATIONS_ERROR );
+            }
+
+            tuples.addAll( item.toTuples() );
+        }
+    }
+
+
+    /* -------------------------------------------------------------------------------
+     * Within every access controled interceptor method we must retrieve the ACITuple
+     * set for all the perscriptiveACIs that apply to the candidate, the target entry
+     * operated upon.  This ACITuple set is gotten from the TupleCache by looking up
+     * the subentries referenced by the accessControlSubentries operational attribute
+     * within the target entry.
+     *
+     * Then the entry is inspected for an entryACI.  This is not done for the add op
+     * since it could introduce a security breech.  So for non-add ops if present a
+     * set of ACITuples are generated for all the entryACIs within the entry.  This
+     * set is combined with the ACITuples cached for the perscriptiveACI affecting
+     * the target entry.  If the entry is a subentry the ACIs are also processed for
+     * the subentry to generate more ACITuples.  This subentry TupleACI set is joined
+     * with the entry and perscriptive ACI.
+     *
+     * The union of ACITuples are fed into the engine along with other parameters
+     * to decide whether a permission is granted or rejected for the specific
+     * operation.
+     * -------------------------------------------------------------------------------
+     */
+
+    public void add( NextInterceptor next, AddOperationContext addContext ) throws NamingException
+    {
+        // Access the principal requesting the operation, and bypass checks if it is the admin
+        Invocation invocation = InvocationStack.getInstance().peek();
+        LdapPrincipal principal = ( ( ServerContext ) invocation.getCaller() ).getPrincipal();
+        LdapDN principalDn = principal.getJndiName();
+        
+        ServerEntry serverEntry = addContext.getEntry(); 
+        //Attributes entry = ServerEntryUtils.toAttributesImpl( serverEntry );
+
+        LdapDN name = addContext.getDn();
+
+        // bypass authz code if we are disabled
+        if ( !enabled )
+        {
+            next.add( addContext );
+            return;
+        }
+
+        // bypass authz code but manage caches if operation is performed by the admin
+        if ( isPrincipalAnAdministrator( principalDn ) )
+        {
+            next.add( addContext );
+            tupleCache.subentryAdded( name, serverEntry );
+            groupCache.groupAdded( name, serverEntry );
+            return;
+        }
+
+        // perform checks below here for all non-admin users
+        SubentryInterceptor subentryInterceptor = ( SubentryInterceptor ) chain.get( SubentryInterceptor.class.getName() );
+        ServerEntry subentryAttrs = subentryInterceptor.getSubentryAttributes( name, serverEntry );
+        
+        for ( EntryAttribute attribute:serverEntry )
+        {
+            subentryAttrs.put( attribute );
+        }
+
+        // Assemble all the information required to make an access control decision
+        Set<LdapDN> userGroups = groupCache.getGroups( principalDn.toNormName() );
+        Collection<ACITuple> tuples = new HashSet<ACITuple>();
+
+        // Build the total collection of tuples to be considered for add rights
+        // NOTE: entryACI are NOT considered in adds (it would be a security breech)
+        addPerscriptiveAciTuples( invocation.getProxy(), tuples, name, subentryAttrs );
+        addSubentryAciTuples( invocation.getProxy(), tuples, name, subentryAttrs );
+
+        // check if entry scope permission is granted
+        PartitionNexusProxy proxy = invocation.getProxy();
+        engine.checkPermission( registries, proxy, userGroups, principalDn, principal.getAuthenticationLevel(), name, null, null,
+            ADD_PERMS, tuples, subentryAttrs, null );
+
+        // now we must check if attribute type and value scope permission is granted
+        for ( EntryAttribute attribute:serverEntry )
+        {
+            for ( Value<?> value:attribute )
+            {
+                engine.checkPermission( registries, proxy, userGroups, principalDn, principal.getAuthenticationLevel(), name, attribute
+                    .getUpId(), value, ADD_PERMS, tuples, serverEntry, null );
+            }
+        }
+
+        // if we've gotten this far then access has been granted
+        next.add( addContext );
+
+        // if the entry added is a subentry or a groupOf[Unique]Names we must
+        // update the ACITuple cache and the groups cache to keep them in sync
+        tupleCache.subentryAdded( name, serverEntry );
+        groupCache.groupAdded( name, serverEntry );
+    }
+
+
+    private boolean isTheAdministrator( LdapDN normalizedDn )
+    {
+        return normalizedDn.getNormName().equals( ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED );
+    }
+
+
+    public void delete( NextInterceptor next, DeleteOperationContext deleteContext ) throws NamingException
+    {
+        LdapDN name = deleteContext.getDn();
+        
+        // Access the principal requesting the operation, and bypass checks if it is the admin
+        Invocation invocation = InvocationStack.getInstance().peek();
+        PartitionNexusProxy proxy = invocation.getProxy();
+
+        LdapPrincipal principal = ( ( ServerContext ) invocation.getCaller() ).getPrincipal();
+        LdapDN principalDn = principal.getJndiName();
+
+        // bypass authz code if we are disabled
+        if ( ! enabled )
+        {
+            next.delete( deleteContext );
+            return;
+        }
+
+        ServerEntry entry = proxy.lookup( new LookupOperationContext( registries, name ) , PartitionNexusProxy.LOOKUP_BYPASS );
+
+        protectCriticalEntries( name );
+
+        // bypass authz code but manage caches if operation is performed by the admin
+        if ( isPrincipalAnAdministrator( principalDn ) )
+        {
+            next.delete( deleteContext );
+            tupleCache.subentryDeleted( name, entry );
+            groupCache.groupDeleted( name, entry );
+            return;
+        }
+
+        Set<LdapDN> userGroups = groupCache.getGroups( principalDn.toString() );
+        Collection<ACITuple> tuples = new HashSet<ACITuple>();
+        addPerscriptiveAciTuples( proxy, tuples, name, entry );
+        addEntryAciTuples( tuples, entry );
+        addSubentryAciTuples( proxy, tuples, name, entry );
+
+        engine.checkPermission( registries, proxy, userGroups, principalDn, principal.getAuthenticationLevel(), name, null, null,
+            REMOVE_PERMS, tuples, entry, null );
+
+        next.delete( deleteContext );
+        tupleCache.subentryDeleted( name, entry );
+        groupCache.groupDeleted( name, entry );
+    }
+
+
+    public void modify( NextInterceptor next, ModifyOperationContext opContext ) throws NamingException
+    {
+        // Access the principal requesting the operation, and bypass checks if it is the admin
+        Invocation invocation = InvocationStack.getInstance().peek();
+        PartitionNexusProxy proxy = invocation.getProxy();
+        LdapDN name = opContext.getDn();
+
+        // Access the principal requesting the operation, and bypass checks if it is the admin
+        ServerEntry entry = proxy.lookup( new LookupOperationContext( registries, name ), PartitionNexusProxy.LOOKUP_BYPASS );
+        
+        LdapPrincipal principal = ( ( ServerContext ) invocation.getCaller() ).getPrincipal();
+        LdapDN principalDn = principal.getJndiName();
+
+        // bypass authz code if we are disabled
+        if ( !enabled )
+        {
+            next.modify( opContext );
+            return;
+        }
+
+        List<Modification> mods = opContext.getModItems();
+
+        // bypass authz code but manage caches if operation is performed by the admin
+        if ( isPrincipalAnAdministrator( principalDn ) )
+        {
+            next.modify( opContext );
+            /**
+             * @TODO: A virtual entry can be created here for not hitting the backend again.
+             */
+            ServerEntry modifiedEntry = proxy.lookup( new LookupOperationContext( registries, name ), PartitionNexusProxy.LOOKUP_BYPASS );
+            tupleCache.subentryModified( name, mods, modifiedEntry );
+            groupCache.groupModified( name, mods, entry, registries );
+            return;
+        }
+
+        Set<LdapDN> userGroups = groupCache.getGroups( principalDn.toString() );
+        Collection<ACITuple> tuples = new HashSet<ACITuple>();
+        addPerscriptiveAciTuples( proxy, tuples, name, entry );
+        addEntryAciTuples( tuples, entry );
+        addSubentryAciTuples( proxy, tuples, name, entry );
+
+        engine.checkPermission( registries, proxy, userGroups, principalDn, principal.getAuthenticationLevel(), name, null, null,
+            Collections.singleton( MicroOperation.MODIFY ), tuples, entry, null );
+
+        Collection<MicroOperation> perms = null;
+        ServerEntry entryView = ( ServerEntry ) entry.clone();
+        
+        for ( Modification mod : mods )
+        {
+            ServerAttribute attr = (ServerAttribute)mod.getAttribute();
+
+            switch ( mod.getOperation() )
+            {
+                case ADD_ATTRIBUTE :
+                    perms = ADD_PERMS;
+                
+                    // If the attribute is being created with an initial value ...
+                    if ( entry.get( attr.getId() ) == null )
+                    {
+                        // ... we also need to check if adding the attribute is permitted
+                        engine.checkPermission( registries, proxy, userGroups, principalDn, principal.getAuthenticationLevel(), name,
+                                attr.getId(), null, perms, tuples, entry, null );
+                    }
+                    
+                    break;
+
+                case REMOVE_ATTRIBUTE :
+                    perms = REMOVE_PERMS;
+                    EntryAttribute entryAttr = entry.get( attr.getId() );
+
+                    if ( entryAttr != null )
+                    {
+                        // If there is only one value remaining in the attribute ...
+                        if ( entryAttr.size() == 1 )
+                        {
+                            // ... we also need to check if removing the attribute at all is permitted
+                            engine.checkPermission( registries, proxy, userGroups, principalDn, principal.getAuthenticationLevel(), name,
+                                    attr.getId(), null, perms, tuples, entry, null );
+                        }
+                    }
+                    
+                    break;
+
+                case REPLACE_ATTRIBUTE :
+                    perms = REPLACE_PERMS;
+                    break;
+            }
+
+            /**
+             * Update the entry view as the current modification is applied to the original entry.
+             * This is especially required for handling the MaxValueCount protected item. Number of
+             * values for an attribute after a modification should be known in advance in order to
+             * check permissions for MaxValueCount protected item. So during addition of the first
+             * value of an attribute it can be rejected if the permission denied due the the
+             * MaxValueCount protected item. This is not the perfect implementation as required by
+             * the specification because the system should reject the addition exactly on the right
+             * value of the attribute. However as we do not have that much granularity in our
+             * implementation (we consider an Attribute Addition itself a Micro Operation,
+             * not the individual Value Additions) we just handle this when the first value of an
+             * attribute is being checked for relevant permissions below. 
+             */
+            entryView = ServerEntryUtils.getTargetEntry( mod, entryView, registries );
+            
+            for ( Value<?> value:attr )
+            {                
+                engine.checkPermission( registries, proxy, userGroups, principalDn, principal.getAuthenticationLevel(), name,
+                        attr.getId(), value, perms, tuples, entry, entryView );
+            }
+        }
+
+        
+
+        next.modify( opContext );
+        /**
+         * @TODO: A virtual entry can be created here for not hitting the backend again.
+         */
+        ServerEntry modifiedEntry = proxy.lookup( new LookupOperationContext( registries, name ), PartitionNexusProxy.LOOKUP_BYPASS );
+        tupleCache.subentryModified( name, mods, modifiedEntry );
+        groupCache.groupModified( name, mods, entry, registries );
+    }
+
+    public boolean hasEntry( NextInterceptor next, EntryOperationContext entryContext ) throws NamingException
+    {
+        LdapDN name = entryContext.getDn();
+        Invocation invocation = InvocationStack.getInstance().peek();
+        PartitionNexusProxy proxy = invocation.getProxy();
+        
+        ServerEntry entry = proxy.lookup( new LookupOperationContext( registries, name ), PartitionNexusProxy.LOOKUP_BYPASS );
+            
+        
+        LdapPrincipal principal = ( ( ServerContext ) invocation.getCaller() ).getPrincipal();
+        LdapDN principalDn = principal.getJndiName();
+
+        if ( isPrincipalAnAdministrator( principalDn ) || !enabled || ( name.size() == 0 ) ) // no checks on the rootdse
+        {
+            // No need to go down to the stack, if the dn is empty : it's the rootDSE, and it exists !
+            return name.size() == 0 || next.hasEntry( entryContext );
+        }
+
+        Set<LdapDN> userGroups = groupCache.getGroups( principalDn.toNormName() );
+        Collection<ACITuple> tuples = new HashSet<ACITuple>();
+        addPerscriptiveAciTuples( proxy, tuples, name, entry );
+        addEntryAciTuples( tuples, entry );
+        addSubentryAciTuples( proxy, tuples, name, entry );
+
+        // check that we have browse access to the entry
+        engine.checkPermission( registries, proxy, userGroups, principalDn, principal.getAuthenticationLevel(), name, null, null,
+            BROWSE_PERMS, tuples, entry, null );
+
+        return next.hasEntry( entryContext );
+    }
+
+
+    /**
+     * Checks if the READ permissions exist to the entry and to each attribute type and
+     * value.
+     *
+     * @todo not sure if we should hide attribute types/values or throw an exception
+     * instead.  I think we're going to have to use a filter to restrict the return
+     * of attribute types and values instead of throwing an exception.  Lack of read
+     * perms to attributes and their values results in their removal when returning
+     * the entry.
+     *
+     * @param principal the user associated with the call
+     * @param dn the name of the entry being looked up
+     * @param entry the raw entry pulled from the nexus
+     * @throws NamingException if undlying access to the DIT fails
+     */
+    private void checkLookupAccess( LdapPrincipal principal, LdapDN dn, ServerEntry entry ) throws NamingException
+    {
+        // no permissions checks on the RootDSE
+        if ( dn.toString().trim().equals( "" ) )
+        {
+            return;
+        }
+
+        PartitionNexusProxy proxy = InvocationStack.getInstance().peek().getProxy();
+        LdapDN userName = principal.getJndiName();
+        Set<LdapDN> userGroups = groupCache.getGroups( userName.toNormName() );
+        Collection<ACITuple> tuples = new HashSet<ACITuple>();
+        addPerscriptiveAciTuples( proxy, tuples, dn, entry );
+        addEntryAciTuples( tuples, entry );
+        addSubentryAciTuples( proxy, tuples, dn, entry );
+
+        // check that we have read access to the entry
+        engine.checkPermission( registries, proxy, userGroups, userName, principal.getAuthenticationLevel(), dn, null, null,
+            LOOKUP_PERMS, tuples, entry, null );
+
+        // check that we have read access to every attribute type and value
+        for ( EntryAttribute attribute:entry )
+        {
+            
+            for ( Value<?> value:attribute )
+            {
+                engine.checkPermission( 
+                    registries, 
+                    proxy, 
+                    userGroups, 
+                    userName, 
+                    principal.getAuthenticationLevel(), 
+                    dn, 
+                    attribute.getUpId(), 
+                    value, 
+                    READ_PERMS, 
+                    tuples, 
+                    entry, 
+                    null );
+            }
+        }
+    }
+
+
+    public ServerEntry lookup( NextInterceptor next, LookupOperationContext lookupContext ) throws NamingException
+    {
+        Invocation invocation = InvocationStack.getInstance().peek();
+        LdapPrincipal principal = ( ( ServerContext ) invocation.getCaller() ).getPrincipal();
+        LdapDN principalDn = principal.getJndiName();
+        
+        if ( !principalDn.isNormalized() )
+        {
+            principalDn.normalize( atRegistry.getNormalizerMapping() );
+        }
+        
+        if ( isPrincipalAnAdministrator( principalDn ) || !enabled )
+        {
+            return next.lookup( lookupContext );
+        }
+
+        PartitionNexusProxy proxy = invocation.getProxy();
+        ServerEntry entry = proxy.lookup( lookupContext, PartitionNexusProxy.LOOKUP_BYPASS );
+
+        checkLookupAccess( principal, lookupContext.getDn(), entry );
+        return next.lookup( lookupContext );
+    }
+
+    public void rename( NextInterceptor next, RenameOperationContext renameContext ) throws NamingException
+    {
+        LdapDN name = renameContext.getDn();
+
+        // Access the principal requesting the operation, and bypass checks if it is the admin
+        Invocation invocation = InvocationStack.getInstance().peek();
+        PartitionNexusProxy proxy = invocation.getProxy();
+        
+        ServerEntry entry = proxy.lookup( new LookupOperationContext( registries, name ), PartitionNexusProxy.LOOKUP_BYPASS );
+        
+        LdapPrincipal principal = ( ( ServerContext ) invocation.getCaller() ).getPrincipal();
+        LdapDN principalDn = principal.getJndiName();
+        LdapDN newName = ( LdapDN ) name.clone();
+        newName.remove( name.size() - 1 );
+
+        newName.add( ( String ) renameContext.getNewRdn().getValue() );
+
+        // bypass authz code if we are disabled
+        if ( !enabled )
+        {
+            next.rename( renameContext );
+            return;
+        }
+
+        protectCriticalEntries( name );
+
+        // bypass authz code but manage caches if operation is performed by the admin
+        if ( isPrincipalAnAdministrator( principalDn ) )
+        {
+            next.rename( renameContext );
+            tupleCache.subentryRenamed( name, newName );
+            
+            // TODO : this method returns a boolean : what should we do with the result ?
+            groupCache.groupRenamed( name, newName );
+
+            return;
+        }
+
+        Set<LdapDN> userGroups = groupCache.getGroups( principalDn.toString() );
+        Collection<ACITuple> tuples = new HashSet<ACITuple>();
+        addPerscriptiveAciTuples( proxy, tuples, name, entry );
+        addEntryAciTuples( tuples, entry );
+        addSubentryAciTuples( proxy, tuples, name, entry );
+
+        engine.checkPermission( registries, proxy, userGroups, principalDn, principal.getAuthenticationLevel(), name, null, null,
+            RENAME_PERMS, tuples, entry, null );
+
+        next.rename( renameContext );
+        tupleCache.subentryRenamed( name, newName );
+        groupCache.groupRenamed( name, newName );
+    }
+
+
+    public void moveAndRename( NextInterceptor next, MoveAndRenameOperationContext moveAndRenameContext )
+        throws NamingException
+    {
+        LdapDN oriChildName = moveAndRenameContext.getDn();
+        LdapDN newParentName = moveAndRenameContext.getParent();
+
+        // Access the principal requesting the operation, and bypass checks if it is the admin
+        Invocation invocation = InvocationStack.getInstance().peek();
+        PartitionNexusProxy proxy = invocation.getProxy();
+        
+        ServerEntry entry = proxy.lookup( new LookupOperationContext( registries, oriChildName ), PartitionNexusProxy.LOOKUP_BYPASS );
+        
+            LdapPrincipal principal = ( ( ServerContext ) invocation.getCaller() ).getPrincipal();
+        LdapDN principalDn = principal.getJndiName();
+        LdapDN newName = ( LdapDN ) newParentName.clone();
+        newName.add( moveAndRenameContext.getNewRdn().getUpName() );
+
+        // bypass authz code if we are disabled
+        if ( !enabled )
+        {
+            next.moveAndRename( moveAndRenameContext );
+            return;
+        }
+
+        protectCriticalEntries( oriChildName );
+
+        // bypass authz code but manage caches if operation is performed by the admin
+        if ( isPrincipalAnAdministrator( principalDn ) )
+        {
+            next.moveAndRename( moveAndRenameContext );
+            tupleCache.subentryRenamed( oriChildName, newName );
+            groupCache.groupRenamed( oriChildName, newName );
+            return;
+        }
+
+        Set<LdapDN> userGroups = groupCache.getGroups( principalDn.toString() );
+        Collection<ACITuple> tuples = new HashSet<ACITuple>();
+        addPerscriptiveAciTuples( proxy, tuples, oriChildName, entry );
+        addEntryAciTuples( tuples, entry );
+        addSubentryAciTuples( proxy, tuples, oriChildName, entry );
+
+        engine.checkPermission( registries, proxy, userGroups, principalDn, principal.getAuthenticationLevel(), oriChildName, null,
+            null, MOVERENAME_PERMS, tuples, entry, null );
+
+        // Get the entry again without operational attributes
+        // because access control subentry operational attributes
+        // will not be valid at the new location.
+        // This will certainly be fixed by the SubentryInterceptor,
+        // but after this service.
+        ServerEntry importedEntry = proxy.lookup( 
+            new LookupOperationContext( registries, oriChildName ), 
+                PartitionNexusProxy.LOOKUP_EXCLUDING_OPR_ATTRS_BYPASS );
+            
+        
+        // As the target entry does not exist yet and so
+        // its subentry operational attributes are not there,
+        // we need to construct an entry to represent it
+        // at least with minimal requirements which are object class
+        // and access control subentry operational attributes.
+        SubentryInterceptor subentryInterceptor = ( SubentryInterceptor ) chain.get( SubentryInterceptor.class.getName() );
+        ServerEntry subentryAttrs = subentryInterceptor.getSubentryAttributes( newName, importedEntry );
+        
+        for ( EntryAttribute attribute:importedEntry )
+        {
+            subentryAttrs.put( attribute );
+        }
+        
+        Collection<ACITuple> destTuples = new HashSet<ACITuple>();
+        // Import permission is only valid for prescriptive ACIs
+        addPerscriptiveAciTuples( proxy, destTuples, newName, subentryAttrs );
+        // Evaluate the target context to see whether it
+        // allows an entry named newName to be imported as a subordinate.
+        engine.checkPermission( registries, proxy, userGroups, principalDn, principal.getAuthenticationLevel(), newName, null,
+            null, IMPORT_PERMS, destTuples, subentryAttrs, null );
+
+
+        next.moveAndRename( moveAndRenameContext );
+        tupleCache.subentryRenamed( oriChildName, newName );
+        groupCache.groupRenamed( oriChildName, newName );
+    }
+
+
+    public void move( NextInterceptor next, MoveOperationContext moveContext ) throws NamingException
+    {
+        LdapDN oriChildName = moveContext.getDn();
+        LdapDN newParentName = moveContext.getParent();
+        
+        // Access the principal requesting the operation, and bypass checks if it is the admin
+        Invocation invocation = InvocationStack.getInstance().peek();
+        PartitionNexusProxy proxy = invocation.getProxy();
+        ServerEntry entry = proxy.lookup( new LookupOperationContext( registries, oriChildName ), PartitionNexusProxy.LOOKUP_BYPASS );
+       
+        LdapDN newName = ( LdapDN ) newParentName.clone();
+        newName.add( oriChildName.get( oriChildName.size() - 1 ) );
+        LdapPrincipal principal = ( ( ServerContext ) invocation.getCaller() ).getPrincipal();
+        LdapDN principalDn = principal.getJndiName();
+
+        // bypass authz code if we are disabled
+        if ( !enabled )
+        {
+            next.move( moveContext );
+            return;
+        }
+
+        protectCriticalEntries( oriChildName);
+
+        // bypass authz code but manage caches if operation is performed by the admin
+        if ( isPrincipalAnAdministrator( principalDn ) )
+        {
+            next.move( moveContext );
+            tupleCache.subentryRenamed( oriChildName, newName );
+            groupCache.groupRenamed( oriChildName, newName );
+            return;
+        }
+
+        Set<LdapDN> userGroups = groupCache.getGroups( principalDn.toString() );
+        Collection<ACITuple> tuples = new HashSet<ACITuple>();
+        addPerscriptiveAciTuples( proxy, tuples, oriChildName, entry );
+        addEntryAciTuples( tuples, entry );
+        addSubentryAciTuples( proxy, tuples, oriChildName, entry );
+
+        engine.checkPermission( registries, proxy, userGroups, principalDn, principal.getAuthenticationLevel(), oriChildName, null,
+            null, EXPORT_PERMS, tuples, entry, null );
+        
+        // Get the entry again without operational attributes
+        // because access control subentry operational attributes
+        // will not be valid at the new location.
+        // This will certainly be fixed by the SubentryInterceptor,
+        // but after this service.
+        ServerEntry importedEntry = proxy.lookup( 
+            new LookupOperationContext( registries, oriChildName ), 
+                PartitionNexusProxy.LOOKUP_EXCLUDING_OPR_ATTRS_BYPASS );
+            
+        // As the target entry does not exist yet and so
+        // its subentry operational attributes are not there,
+        // we need to construct an entry to represent it
+        // at least with minimal requirements which are object class
+        // and access control subentry operational attributes.
+        SubentryInterceptor subentryInterceptor = ( SubentryInterceptor ) chain.get( SubentryInterceptor.class.getName() );
+        ServerEntry subentryAttrs = subentryInterceptor.getSubentryAttributes( newName, importedEntry );
+        
+        for ( EntryAttribute attribute:importedEntry )
+        {
+            subentryAttrs.put( attribute );
+        }
+        
+        Collection<ACITuple> destTuples = new HashSet<ACITuple>();
+        // Import permission is only valid for prescriptive ACIs
+        addPerscriptiveAciTuples( proxy, destTuples, newName, subentryAttrs );
+        // Evaluate the target context to see whether it
+        // allows an entry named newName to be imported as a subordinate.
+        engine.checkPermission( registries, proxy, userGroups, principalDn, principal.getAuthenticationLevel(), newName, null,
+            null, IMPORT_PERMS, destTuples, subentryAttrs, null );
+
+        next.move( moveContext );
+        tupleCache.subentryRenamed( oriChildName, newName );
+        groupCache.groupRenamed( oriChildName, newName );
+    }
+
+    
+    public NamingEnumeration<ServerSearchResult> list( NextInterceptor next, ListOperationContext opContext ) throws NamingException
+    {
+        Invocation invocation = InvocationStack.getInstance().peek();
+        ServerLdapContext ctx = ( ServerLdapContext ) invocation.getCaller();
+        LdapPrincipal user = ctx.getPrincipal();
+        NamingEnumeration<ServerSearchResult> e = next.list( opContext );
+        
+        if ( isPrincipalAnAdministrator( user.getJndiName() ) || !enabled )
+        {
+            return e;
+        }
+        
+        AuthorizationFilter authzFilter = new AuthorizationFilter();
+        return new SearchResultFilteringEnumeration( e, DEFAULT_SEARCH_CONTROLS, invocation, authzFilter, "List authorization Filter" );
+    }
+
+
+    public NamingEnumeration<ServerSearchResult> search( NextInterceptor next, SearchOperationContext opContext ) throws NamingException
+    {
+        Invocation invocation = InvocationStack.getInstance().peek();
+        ServerLdapContext ctx = ( ServerLdapContext ) invocation.getCaller();
+        LdapPrincipal user = ctx.getPrincipal();
+        LdapDN principalDn = user.getJndiName();
+        NamingEnumeration<ServerSearchResult> e = next.search( opContext );
+
+        boolean isSubschemaSubentryLookup = subschemaSubentryDn.equals( opContext.getDn().getNormName() );
+        SearchControls searchCtls = opContext.getSearchControls();
+        boolean isRootDSELookup = opContext.getDn().size() == 0 && searchCtls.getSearchScope() == SearchControls.OBJECT_SCOPE;
+
+        if ( isPrincipalAnAdministrator( principalDn ) || !enabled || isRootDSELookup || isSubschemaSubentryLookup )
+        {
+            return e;
+        }
+        
+        AuthorizationFilter authzFilter = new AuthorizationFilter();
+        return new SearchResultFilteringEnumeration( e, searchCtls, invocation, authzFilter, "Search authorization Filter" );
+    }
+
+    
+    public final boolean isPrincipalAnAdministrator( LdapDN principalDn )
+    {
+        return groupCache.isPrincipalAnAdministrator( principalDn );
+    }
+    
+
+    public boolean compare( NextInterceptor next, CompareOperationContext opContext ) throws NamingException
+    {
+        LdapDN name = opContext.getDn();
+        String oid = opContext.getOid();
+        Value<?> value = (Value<?>)opContext.getValue();
+        
+        // Access the principal requesting the operation, and bypass checks if it is the admin
+        Invocation invocation = InvocationStack.getInstance().peek();
+        PartitionNexusProxy proxy = invocation.getProxy();
+        ServerEntry entry = proxy.lookup( 
+                new LookupOperationContext( registries, name ), 
+                PartitionNexusProxy.LOOKUP_BYPASS );
+
+        LdapPrincipal principal = ( ( ServerContext ) invocation.getCaller() ).getPrincipal();
+        LdapDN principalDn = principal.getJndiName();
+
+        if ( isPrincipalAnAdministrator( principalDn ) || !enabled )
+        {
+            return next.compare( opContext );
+        }
+
+        Set<LdapDN> userGroups = groupCache.getGroups( principalDn.toNormName() );
+        Collection<ACITuple> tuples = new HashSet<ACITuple>();
+        addPerscriptiveAciTuples( proxy, tuples, name, entry );
+        addEntryAciTuples( tuples, entry );
+        addSubentryAciTuples( proxy, tuples, name, entry );
+
+        engine.checkPermission( registries, proxy, userGroups, principalDn, principal.getAuthenticationLevel(), name, null, null,
+            READ_PERMS, tuples, entry, null );
+        engine.checkPermission( registries, proxy, userGroups, principalDn, principal.getAuthenticationLevel(), name, oid, value,
+            COMPARE_PERMS, tuples, entry, null );
+
+        return next.compare( opContext );
+    }
+
+
+    public LdapDN getMatchedName ( NextInterceptor next, GetMatchedNameOperationContext opContext ) throws NamingException
+    {
+        // Access the principal requesting the operation, and bypass checks if it is the admin
+        Invocation invocation = InvocationStack.getInstance().peek();
+        PartitionNexusProxy proxy = invocation.getProxy();
+        LdapPrincipal principal = ( ( ServerContext ) invocation.getCaller() ).getPrincipal();
+        LdapDN principalDn = principal.getJndiName();
+        
+        if ( isPrincipalAnAdministrator( principalDn ) || !enabled )
+        {
+            return next.getMatchedName( opContext );
+        }
+
+        // get the present matched name
+        ServerEntry entry;
+        LdapDN matched = next.getMatchedName( opContext );
+
+        // check if we have disclose on error permission for the entry at the matched dn
+        // if not remove rdn and check that until nothing is left in the name and return
+        // that but if permission is granted then short the process and return the dn
+        while ( matched.size() > 0 )
+        {
+            entry = proxy.lookup( new LookupOperationContext( registries, matched ), PartitionNexusProxy.GETMATCHEDDN_BYPASS );
+            
+            Set<LdapDN> userGroups = groupCache.getGroups( principalDn.toString() );
+            Collection<ACITuple> tuples = new HashSet<ACITuple>();
+            addPerscriptiveAciTuples( proxy, tuples, matched, entry );
+            addEntryAciTuples( tuples, entry );
+            addSubentryAciTuples( proxy, tuples, matched, entry );
+
+            if ( engine.hasPermission( registries, proxy, userGroups, principalDn, principal.getAuthenticationLevel(), matched, null,
+                null, MATCHEDNAME_PERMS, tuples, entry, null ) )
+            {
+                return matched;
+            }
+
+            matched.remove( matched.size() - 1 );
+        }
+
+        return matched;
+    }
+
+
+    public void cacheNewGroup( LdapDN name, ServerEntry entry ) throws NamingException
+    {
+        groupCache.groupAdded( name, entry );
+    }
+
+
+    private boolean filter( Invocation invocation, LdapDN normName, ServerSearchResult result ) throws NamingException
+    {
+        ServerEntry resultEntry = result.getServerEntry();
+
+        /*
+         * First call hasPermission() for entry level "Browse" and "ReturnDN" perm
+         * tests.  If we hasPermission() returns false we immediately short the
+         * process and return false.
+         */
+        ServerEntry entry = invocation.getProxy().lookup( 
+                new LookupOperationContext( registries, normName ), PartitionNexusProxy.LOOKUP_BYPASS );
+        
+        ServerLdapContext ctx = ( ServerLdapContext ) invocation.getCaller();
+        LdapDN userDn = ctx.getPrincipal().getJndiName();
+        Set<LdapDN> userGroups = groupCache.getGroups( userDn.toNormName() );
+        Collection<ACITuple> tuples = new HashSet<ACITuple>();
+        addPerscriptiveAciTuples( invocation.getProxy(), tuples, normName, entry );
+        addEntryAciTuples( tuples, entry );
+        addSubentryAciTuples( invocation.getProxy(), tuples, normName, entry );
+
+        if ( !engine.hasPermission( 
+                        registries, 
+                        invocation.getProxy(), 
+                        userGroups, 
+                        userDn, 
+                        ctx.getPrincipal().getAuthenticationLevel(), 
+                        normName, 
+                        null, 
+                        null, 
+                        SEARCH_ENTRY_PERMS, 
+                        tuples, 
+                        entry, 
+                        null ) )
+        {
+            return false;
+        }
+
+        /*
+         * For each attribute type we check if access is allowed to the type.  If not
+         * the attribute is yanked out of the entry to be returned.  If permission is
+         * allowed we move on to check if the values are allowed.  Values that are
+         * not allowed are removed from the attribute.  If the attribute has no more
+         * values remaining then the entire attribute is removed.
+         */
+        List<AttributeType> attributeToRemove = new ArrayList<AttributeType>();
+        
+        for ( AttributeType attributeType:resultEntry.getAttributeTypes() )
+        {
+            // if attribute type scope access is not allowed then remove the attribute and continue
+            String id = attributeType.getName();
+            EntryAttribute attr = resultEntry.get( attributeType );
+        
+            if ( !engine.hasPermission( 
+                        registries, 
+                        invocation.getProxy(), 
+                        userGroups, 
+                        userDn,
+                        ctx.getPrincipal().getAuthenticationLevel(), 
+                        normName, 
+                        id, 
+                        null, 
+                        SEARCH_ATTRVAL_PERMS, 
+                        tuples, 
+                        entry, 
+                        null ) )
+            {
+                attributeToRemove.add( attributeType );
+                
+                continue;
+            }
+
+            List<Value<?>> valueToRemove = new ArrayList<Value<?>>();
+            
+            // attribute type scope is ok now let's determine value level scope
+            for ( Value<?> value:attr )
+            {
+                if ( !engine.hasPermission( 
+                        registries, 
+                        invocation.getProxy(), 
+                        userGroups, 
+                        userDn, 
+                        ctx.getPrincipal().getAuthenticationLevel(), 
+                        normName, 
+                        attr.getUpId(), 
+                        value, 
+                        SEARCH_ATTRVAL_PERMS, 
+                        tuples,
+                        entry, 
+                        null ) )
+                {
+                    valueToRemove.add( value );
+                }
+            }
+            
+            for ( Value<?> value:valueToRemove )
+            {
+                attr.remove( value );
+            }
+            
+            if ( attr.size() == 0 )
+            {
+                attributeToRemove.add( attributeType );
+            }
+        }
+        
+        for ( AttributeType attributeType:attributeToRemove )
+        {
+            resultEntry.removeAttributes( attributeType );
+        }
+
+        result.setServerEntry( resultEntry );
+        return true;
+    }
+
+
+    /**
+     * WARNING: create one of these filters fresh every time for each new search.
+     */
+    class AuthorizationFilter implements SearchResultFilter
+    {
+        public boolean accept( Invocation invocation, ServerSearchResult result, SearchControls controls )
+            throws NamingException
+        {
+            LdapDN normName = result.getDn().normalize( atRegistry.getNormalizerMapping() );
+            return filter( invocation, normName, result );
+        }
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/authz/DefaultAuthorizationInterceptor.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/authz/DefaultAuthorizationInterceptor.java
new file mode 100644
index 0000000..d80f610
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/authz/DefaultAuthorizationInterceptor.java
@@ -0,0 +1,587 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.authz;
+
+
+import javax.naming.directory.SearchControls;
+import javax.naming.ldap.LdapContext;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.NoPermissionException;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.directory.server.constants.ServerDNConstants;
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerSearchResult;
+import org.apache.directory.server.core.enumeration.SearchResultFilter;
+import org.apache.directory.server.core.enumeration.SearchResultFilteringEnumeration;
+import org.apache.directory.server.core.interceptor.BaseInterceptor;
+import org.apache.directory.server.core.interceptor.Interceptor;
+import org.apache.directory.server.core.interceptor.NextInterceptor;
+import org.apache.directory.server.core.interceptor.context.DeleteOperationContext;
+import org.apache.directory.server.core.interceptor.context.ListOperationContext;
+import org.apache.directory.server.core.interceptor.context.LookupOperationContext;
+import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
+import org.apache.directory.server.core.interceptor.context.MoveAndRenameOperationContext;
+import org.apache.directory.server.core.interceptor.context.MoveOperationContext;
+import org.apache.directory.server.core.interceptor.context.RenameOperationContext;
+import org.apache.directory.server.core.interceptor.context.SearchOperationContext;
+import org.apache.directory.server.core.invocation.Invocation;
+import org.apache.directory.server.core.invocation.InvocationStack;
+import org.apache.directory.server.core.jndi.ServerContext;
+import org.apache.directory.server.core.partition.PartitionNexus;
+import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.entry.Value;
+import org.apache.directory.shared.ldap.exception.LdapNoPermissionException;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.schema.OidNormalizer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * An {@link Interceptor} that controls access to {@link PartitionNexus}.
+ * If a user tries to perform any operations that requires
+ * permission he or she doesn't have, {@link NoPermissionException} will be
+ * thrown and therefore the current invocation chain will terminate.
+ *
+ * @org.apache.xbean.XBean
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class DefaultAuthorizationInterceptor extends BaseInterceptor
+{
+    /** the logger for this class */
+    private static final Logger LOG = LoggerFactory.getLogger( DefaultAuthorizationInterceptor.class );
+
+    /**
+     * the base distinguished {@link Name} for all users
+     */
+    private static LdapDN USER_BASE_DN;
+
+    /**
+     * the base distinguished {@link Name} for all groups
+     */
+    private static LdapDN GROUP_BASE_DN;
+
+    /**
+     * the distinguished {@link Name} for the administrator group
+     */
+    private static LdapDN ADMIN_GROUP_DN;
+
+    /**
+     * the name parser used by this service
+     */
+    private boolean enabled = true;
+    
+    private Set<String> administrators = new HashSet<String>(2);
+    
+    /** The normalizer mapping containing a relation between an OID and a normalizer */
+    private Map<String, OidNormalizer> normalizerMapping;
+    
+    private PartitionNexus nexus;
+
+    /** A starage for the uniqueMember attributeType */
+    private AttributeType uniqueMemberAT;
+
+
+    /**
+     * Creates a new instance.
+     */
+    public DefaultAuthorizationInterceptor()
+    {
+    }
+
+
+    public void init( DirectoryService directoryService ) throws NamingException
+    {
+        nexus = directoryService.getPartitionNexus();
+        normalizerMapping = directoryService.getRegistries().getAttributeTypeRegistry().getNormalizerMapping();
+
+        // disable this static module if basic access control mechanisms are enabled
+        enabled = ! directoryService.isAccessControlEnabled();
+        
+        USER_BASE_DN = PartitionNexus.getUsersBaseName();
+        USER_BASE_DN.normalize( normalizerMapping );
+        
+        GROUP_BASE_DN = PartitionNexus.getGroupsBaseName();
+        GROUP_BASE_DN.normalize( normalizerMapping );
+     
+        ADMIN_GROUP_DN = new LdapDN( ServerDNConstants.ADMINISTRATORS_GROUP_DN );
+        ADMIN_GROUP_DN.normalize( normalizerMapping );
+
+        AttributeTypeRegistry attrRegistry = directoryService.getRegistries().getAttributeTypeRegistry();
+        
+        uniqueMemberAT = attrRegistry.lookup( SchemaConstants.UNIQUE_MEMBER_AT_OID );
+        
+        loadAdministrators( directoryService.getRegistries() );
+    }
+    
+    
+    private void loadAdministrators( Registries registries ) throws NamingException
+    {
+        // read in the administrators and cache their normalized names
+        Set<String> newAdministrators = new HashSet<String>( 2 );
+        ServerEntry adminGroup = nexus.lookup( new LookupOperationContext( registries, ADMIN_GROUP_DN ) );
+        
+        if ( adminGroup == null )
+        {
+            return;
+        }
+        
+        EntryAttribute uniqueMember = adminGroup.get( uniqueMemberAT );
+        
+        for ( Value<?> value:uniqueMember )
+        {
+            LdapDN memberDn = new LdapDN( ( String ) value.get() );
+            memberDn.normalize( normalizerMapping );
+            newAdministrators.add( memberDn.getNormName() );
+        }
+        
+        administrators = newAdministrators;
+    }
+
+    
+    // Note:
+    //    Lookup, search and list operations need to be handled using a filter
+    // and so we need access to the filter service.
+
+    public void delete( NextInterceptor nextInterceptor, DeleteOperationContext opContext ) throws NamingException
+    {
+        LdapDN name = opContext.getDn();
+        
+        if ( !enabled )
+        {
+            nextInterceptor.delete( opContext );
+            return;
+        }
+
+        LdapDN principalDn = getPrincipal().getJndiName();
+
+        if ( name.isEmpty() )
+        {
+            String msg = "The rootDSE cannot be deleted!";
+            LOG.error( msg );
+            throw new LdapNoPermissionException( msg );
+        }
+
+        if ( name.getNormName().equals( ADMIN_GROUP_DN.getNormName() ) )
+        {
+            String msg = "The Administrators group cannot be deleted!";
+            LOG.error( msg );
+            throw new LdapNoPermissionException( msg );
+        }
+
+        if ( isTheAdministrator( name ) )
+        {
+            String msg = "User " + principalDn.getUpName();
+            msg += " does not have permission to delete the admin account.";
+            msg += " No one not even the admin can delete this account!";
+            LOG.error( msg );
+            throw new LdapNoPermissionException( msg );
+        }
+
+        if ( name.size() > 2 )
+        {
+            if ( !isAnAdministrator( principalDn ) )
+            {
+                if ( name.startsWith( USER_BASE_DN ) )
+                {
+                    String msg = "User " + principalDn.getUpName();
+                    msg += " does not have permission to delete the user account: ";
+                    msg += name.getUpName() + ". Only the admin can delete user accounts.";
+                    LOG.error( msg );
+                    throw new LdapNoPermissionException( msg );
+                }
+        
+                if ( name.startsWith( GROUP_BASE_DN ) )
+                {
+                    String msg = "User " + principalDn.getUpName();
+                    msg += " does not have permission to delete the group entry: ";
+                    msg += name.getUpName() + ". Only the admin can delete groups.";
+                    LOG.error( msg );
+                    throw new LdapNoPermissionException( msg );
+                }
+            }
+        }
+
+        nextInterceptor.delete( opContext );
+    }
+
+    
+    private boolean isTheAdministrator( LdapDN normalizedDn )
+    {
+        return normalizedDn.getNormName().equals( ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED );
+    }
+    
+    
+    private boolean isAnAdministrator( LdapDN normalizedDn )
+    {
+        return isTheAdministrator( normalizedDn ) || administrators.contains( normalizedDn.getNormName() );
+
+    }
+    
+
+    // ------------------------------------------------------------------------
+    // Entry Modification Operations
+    // ------------------------------------------------------------------------
+
+    /**
+     * This policy needs to be really tight too because some attributes may take
+     * part in giving the user permissions to protected resources.  We do not want
+     * users to self access these resources.  As far as we're concerned no one but
+     * the admin needs access.
+     */
+    public void modify( NextInterceptor nextInterceptor, ModifyOperationContext opContext )
+        throws NamingException
+    {
+        if ( enabled )
+        {
+            LdapDN dn = opContext.getDn();
+            
+            protectModifyAlterations( dn );
+            nextInterceptor.modify( opContext );
+
+            // update administrators if we change administrators group
+            if ( dn.getNormName().equals( ADMIN_GROUP_DN.getNormName() ) )
+            {
+                loadAdministrators( opContext.getRegistries() );
+            }
+        }
+        else
+        {
+            nextInterceptor.modify( opContext );
+        }
+    }
+
+
+    private void protectModifyAlterations( LdapDN dn ) throws NamingException
+    {
+        LdapDN principalDn = getPrincipal().getJndiName();
+
+        if ( dn.isEmpty() )
+        {
+            String msg = "The rootDSE cannot be modified!";
+            LOG.error( msg );
+            throw new LdapNoPermissionException( msg );
+        }
+
+        if ( ! isAnAdministrator( principalDn ) )
+        {
+            // allow self modifications 
+            if ( dn.getNormName().equals( getPrincipal().getJndiName().getNormName() ) )
+            {
+                return;
+            }
+            
+            if ( dn.getNormName().equals( ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED ) )
+            {
+                String msg = "User " + principalDn.getUpName();
+                msg += " does not have permission to modify the account of the";
+                msg += " admin user.";
+                LOG.error( msg );
+                throw new LdapNoPermissionException( msg );
+            }
+
+            if ( dn.size() > 2 ) 
+                {
+                if ( dn.startsWith( USER_BASE_DN ) )
+                {
+                    String msg = "User " + principalDn.getUpName();
+                    msg += " does not have permission to modify the account of the";
+                    msg += " user " + dn.getUpName() + ".\nEven the owner of an account cannot";
+                    msg += " modify it.\nUser accounts can only be modified by the";
+                    msg += " administrator.";
+                    LOG.error( msg );
+                    throw new LdapNoPermissionException( msg );
+                }
+    
+                if ( dn.startsWith( GROUP_BASE_DN ) )
+                {
+                    String msg = "User " + principalDn.getUpName();
+                    msg += " does not have permission to modify the group entry ";
+                    msg += dn.getUpName() + ".\nGroups can only be modified by the admin.";
+                    LOG.error( msg );
+                    throw new LdapNoPermissionException( msg );
+                }
+            }
+        }
+    }
+    
+    
+    // ------------------------------------------------------------------------
+    // DN altering operations are a no no for any user entry.  Basically here
+    // are the rules of conduct to follow:
+    //
+    //  o No user should have the ability to move or rename their entry
+    //  o Only the administrator can move or rename non-admin user entries
+    //  o The administrator entry cannot be moved or renamed by anyone
+    // ------------------------------------------------------------------------
+
+    public void rename( NextInterceptor nextInterceptor, RenameOperationContext opContext )
+        throws NamingException
+    {
+        if ( enabled )
+        {
+            protectDnAlterations( opContext.getDn() );
+        }
+        
+        nextInterceptor.rename( opContext );
+    }
+
+
+    public void move( NextInterceptor nextInterceptor, MoveOperationContext opContext ) throws NamingException
+    {
+        if ( enabled )
+        {
+            protectDnAlterations( opContext.getDn() );
+        }
+        
+        nextInterceptor.move( opContext );
+    }
+
+
+    public void moveAndRename( NextInterceptor nextInterceptor, MoveAndRenameOperationContext opContext ) throws NamingException
+    {
+        if ( enabled )
+        {
+            protectDnAlterations( opContext.getDn() );
+        }
+        
+        nextInterceptor.moveAndRename( opContext );
+    }
+
+
+    private void protectDnAlterations( LdapDN dn ) throws NamingException
+    {
+        LdapDN principalDn = getPrincipal().getJndiName();
+
+        if ( dn.isEmpty() )
+        {
+            String msg = "The rootDSE cannot be moved or renamed!";
+            LOG.error( msg );
+            throw new LdapNoPermissionException( msg );
+        }
+
+        if ( dn.getNormName().equals( ADMIN_GROUP_DN.getNormName() ) )
+        {
+            String msg = "The Administrators group cannot be moved or renamed!";
+            LOG.error( msg );
+            throw new LdapNoPermissionException( msg );
+        }
+        
+        if ( isTheAdministrator( dn ) )
+        {
+            String msg = "User '" + principalDn.getUpName();
+            msg += "' does not have permission to move or rename the admin";
+            msg += " account.  No one not even the admin can move or";
+            msg += " rename " + dn.getUpName() + "!";
+            LOG.error( msg );
+            throw new LdapNoPermissionException( msg );
+        }
+
+        if ( dn.size() > 2 && dn.startsWith( USER_BASE_DN ) && !isAnAdministrator( principalDn ) )
+        {
+            String msg = "User '" + principalDn.getUpName();
+            msg += "' does not have permission to move or rename the user";
+            msg += " account: " + dn.getUpName() + ". Only the admin can move or";
+            msg += " rename user accounts.";
+            LOG.error( msg );
+            throw new LdapNoPermissionException( msg );
+        }
+
+        if ( dn.size() > 2 && dn.startsWith( GROUP_BASE_DN ) && !isAnAdministrator( principalDn ) )
+        {
+            String msg = "User " + principalDn.getUpName();
+            msg += " does not have permission to move or rename the group entry ";
+            msg += dn.getUpName() + ".\nGroups can only be moved or renamed by the admin.";
+            throw new LdapNoPermissionException( msg );
+        }
+    }
+
+
+    public ServerEntry lookup( NextInterceptor nextInterceptor, LookupOperationContext opContext ) throws NamingException
+    {
+        ServerEntry serverEntry = nextInterceptor.lookup( opContext );
+        
+        if ( !enabled || ( serverEntry == null ) )
+        {
+            return serverEntry;
+        }
+
+        protectLookUp( opContext.getDn() );
+        return serverEntry;
+    }
+
+
+    private void protectLookUp( LdapDN normalizedDn ) throws NamingException
+    {
+        LdapContext ctx = ( LdapContext ) InvocationStack.getInstance().peek().getCaller();
+        LdapDN principalDn = ( ( ServerContext ) ctx ).getPrincipal().getJndiName();
+        
+        if ( !isAnAdministrator( principalDn ) )
+        {
+            if ( normalizedDn.size() > 2 )
+            {
+                if( normalizedDn.startsWith( USER_BASE_DN ) )
+                {
+                    // allow for self reads
+                    if ( normalizedDn.getNormName().equals( principalDn.getNormName() ) )
+                    {
+                        return;
+                    }
+    
+                    String msg = "Access to user account '" + normalizedDn.getUpName() + "' not permitted";
+                    msg += " for user '" + principalDn.getUpName() + "'.  Only the admin can";
+                    msg += " access user account information";
+                    LOG.error( msg );
+                    throw new LdapNoPermissionException( msg );
+                }
+
+                if ( normalizedDn.startsWith( GROUP_BASE_DN ) )
+                {
+                    // allow for self reads
+                    if ( normalizedDn.getNormName().equals( principalDn.getNormName() ) )
+                    {
+                        return;
+                    }
+    
+                    String msg = "Access to group '" + normalizedDn.getUpName() + "' not permitted";
+                    msg += " for user '" + principalDn.getUpName() + "'.  Only the admin can";
+                    msg += " access group information";
+                    LOG.error( msg );
+                    throw new LdapNoPermissionException( msg );
+                }
+            }
+
+            if ( isTheAdministrator( normalizedDn ) )
+            {
+                // allow for self reads
+                if ( normalizedDn.getNormName().equals( principalDn.getNormName() ) )
+                {
+                    return;
+                }
+
+                String msg = "Access to admin account not permitted for user '";
+                msg += principalDn.getUpName() + "'.  Only the admin can";
+                msg += " access admin account information";
+                LOG.error( msg );
+                throw new LdapNoPermissionException( msg );
+            }
+        }
+    }
+
+
+    public NamingEnumeration<ServerSearchResult> search( NextInterceptor nextInterceptor, SearchOperationContext opContext ) throws NamingException
+    {
+        NamingEnumeration<ServerSearchResult> e = nextInterceptor.search( opContext );
+
+        if ( !enabled )
+        {
+            return e;
+        }
+
+        Invocation invocation = InvocationStack.getInstance().peek();
+
+        return new SearchResultFilteringEnumeration( e, opContext.getSearchControls(), invocation, 
+            new SearchResultFilter()
+        {
+            public boolean accept( Invocation invocation, ServerSearchResult result, SearchControls controls )
+                throws NamingException
+            {
+                return DefaultAuthorizationInterceptor.this.isSearchable( invocation, result );
+            }
+        }, "Search Default Authorization filter" );
+    }
+
+
+    public NamingEnumeration<ServerSearchResult> list( NextInterceptor nextInterceptor, ListOperationContext opContext ) throws NamingException
+    {
+        NamingEnumeration<ServerSearchResult> result = nextInterceptor.list( opContext );
+        
+        if ( !enabled )
+        {
+            return result;
+        }
+
+        Invocation invocation = InvocationStack.getInstance().peek();
+        
+        return new SearchResultFilteringEnumeration( result, null, invocation, new SearchResultFilter()
+        {
+            public boolean accept( Invocation invocation, ServerSearchResult result, SearchControls controls )
+                throws NamingException
+            {
+                return DefaultAuthorizationInterceptor.this.isSearchable( invocation, result );
+            }
+        }, "List Default Authorization filter" );
+    }
+
+
+    private boolean isSearchable( Invocation invocation, ServerSearchResult result ) throws NamingException
+    {
+        LdapDN principalDn = ( ( ServerContext ) invocation.getCaller() ).getPrincipal().getJndiName();
+        LdapDN dn = result.getDn();
+        
+        if ( !dn.isNormalized() )
+        {
+            dn.normalize( normalizerMapping );
+        }
+
+        // Admin users gets full access to all entries
+        if ( isAnAdministrator( principalDn ) )
+        {
+            return true;
+        }
+        
+        // Users reading their own entries should be allowed to see all
+        boolean isSelfRead = dn.getNormName().equals( principalDn.getNormName() );
+        
+        if ( isSelfRead )
+        {
+            return true;
+        }
+        
+        // Block off reads to anything under ou=users and ou=groups if not a self read
+        if ( dn.size() > 2 )
+        {
+            // stuff this if in here instead of up in outer if to prevent 
+            // constant needless reexecution for all entries in other depths
+            
+            if ( dn.getNormName().endsWith( USER_BASE_DN.getNormName() ) 
+                || dn.getNormName().endsWith( GROUP_BASE_DN.getNormName() ) )
+            {
+                return false;
+            }
+        }
+        
+        // Non-admin users cannot read the admin entry
+        return ! isTheAdministrator( dn );
+
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/authz/GroupCache.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/authz/GroupCache.java
new file mode 100644
index 0000000..6228c1c
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/authz/GroupCache.java
@@ -0,0 +1,558 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.authz;
+
+
+import javax.naming.directory.SearchControls;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.directory.server.constants.ServerDNConstants;
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.entry.ServerAttribute;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerSearchResult;
+import org.apache.directory.server.core.interceptor.context.SearchOperationContext;
+import org.apache.directory.server.core.partition.PartitionNexus;
+import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.entry.Modification;
+import org.apache.directory.shared.ldap.entry.ModificationOperation;
+import org.apache.directory.shared.ldap.entry.Value;
+import org.apache.directory.shared.ldap.entry.client.ClientStringValue;
+import org.apache.directory.shared.ldap.filter.BranchNode;
+import org.apache.directory.shared.ldap.filter.EqualityNode;
+import org.apache.directory.shared.ldap.filter.OrNode;
+import org.apache.directory.shared.ldap.message.AliasDerefMode;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.schema.OidNormalizer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+
+/**
+ * A cache for tracking static group membership.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class GroupCache
+{
+    /** the logger for this class */
+    private static final Logger LOG = LoggerFactory.getLogger( GroupCache.class );
+
+    /** Speedup for logs */
+    private static final boolean IS_DEBUG = LOG.isDebugEnabled();
+
+    /** String key for the DN of a group to a Set (HashSet) for the Strings of member DNs */
+    private final Map<String, Set<String>> groups = new HashMap<String, Set<String>>();
+
+    /** a handle on the partition nexus */
+    private final PartitionNexus nexus;
+
+    /** A storage for the member attributeType */
+    private AttributeType memberAT;
+
+    /** A storage for the uniqueMember attributeType */
+    private AttributeType uniqueMemberAT;
+
+    /**
+     * The OIDs normalizer map
+     */
+    private Map<String, OidNormalizer> normalizerMap;
+
+    /** the normalized dn of the administrators group */
+    private LdapDN administratorsGroupDn;
+
+    private static final Set<LdapDN> EMPTY_GROUPS = new HashSet<LdapDN>();
+
+
+    /**
+     * Creates a static group cache.
+     *
+     * @param directoryService the directory service core
+     * @throws NamingException if there are failures on initialization 
+     */
+    public GroupCache( DirectoryService directoryService ) throws NamingException
+    {
+        normalizerMap = directoryService.getRegistries().getAttributeTypeRegistry().getNormalizerMapping();
+        nexus = directoryService.getPartitionNexus();
+        AttributeTypeRegistry attributeTypeRegistry = directoryService.getRegistries().getAttributeTypeRegistry();
+
+        memberAT = attributeTypeRegistry.lookup( SchemaConstants.MEMBER_AT_OID );
+        uniqueMemberAT = attributeTypeRegistry.lookup( SchemaConstants.UNIQUE_MEMBER_AT_OID );
+
+        // stuff for dealing with the admin group
+        administratorsGroupDn = parseNormalized( ServerDNConstants.ADMINISTRATORS_GROUP_DN );
+
+        initialize( directoryService.getRegistries() );
+    }
+
+
+    private LdapDN parseNormalized( String name ) throws NamingException
+    {
+        LdapDN dn = new LdapDN( name );
+        dn.normalize( normalizerMap );
+        return dn;
+    }
+
+
+    private void initialize( Registries registries ) throws NamingException
+    {
+        // search all naming contexts for static groups and generate
+        // normalized sets of members to cache within the map
+
+        BranchNode filter = new OrNode();
+        filter.addNode( new EqualityNode( SchemaConstants.OBJECT_CLASS_AT, new ClientStringValue(
+            SchemaConstants.GROUP_OF_NAMES_OC ) ) );
+        filter.addNode( new EqualityNode( SchemaConstants.OBJECT_CLASS_AT, new ClientStringValue(
+            SchemaConstants.GROUP_OF_UNIQUE_NAMES_OC ) ) );
+
+        Iterator<String> suffixes = nexus.listSuffixes( null );
+
+        while ( suffixes.hasNext() )
+        {
+            String suffix = suffixes.next();
+            LdapDN baseDn = new LdapDN( suffix );
+            SearchControls ctls = new SearchControls();
+            ctls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+            NamingEnumeration<ServerSearchResult> results = nexus.search( new SearchOperationContext( registries,
+                baseDn, AliasDerefMode.DEREF_ALWAYS, filter, ctls ) );
+
+            while ( results.hasMore() )
+            {
+                ServerSearchResult result = results.next();
+                LdapDN groupDn = result.getDn().normalize( normalizerMap );
+                EntryAttribute members = getMemberAttribute( result.getServerEntry() );
+
+                if ( members != null )
+                {
+                    Set<String> memberSet = new HashSet<String>( members.size() );
+                    addMembers( memberSet, members );
+                    groups.put( groupDn.getNormName(), memberSet );
+                }
+                else
+                {
+                    LOG.warn( "Found group '{}' without any member or uniqueMember attributes", groupDn.getUpName() );
+                }
+            }
+
+            results.close();
+        }
+
+        if ( IS_DEBUG )
+        {
+            LOG.debug( "group cache contents on startup:\n {}", groups );
+        }
+    }
+
+
+    /**
+     * Gets the member attribute regardless of whether groupOfNames or
+     * groupOfUniqueNames is used.
+     *
+     * @param entry the entry inspected for member attributes
+     * @return the member attribute
+     */
+    private EntryAttribute getMemberAttribute( ServerEntry entry ) throws NamingException
+    {
+        EntryAttribute oc = entry.get( SchemaConstants.OBJECT_CLASS_AT );
+
+        if ( oc == null )
+        {
+            EntryAttribute member = entry.get( memberAT );
+
+            if ( member != null )
+            {
+                return member;
+            }
+
+            EntryAttribute uniqueMember = entry.get( uniqueMemberAT );
+
+            if ( uniqueMember != null )
+            {
+                return uniqueMember;
+            }
+
+            return null;
+        }
+
+        if ( oc.contains( SchemaConstants.GROUP_OF_NAMES_OC ) || oc.contains( SchemaConstants.GROUP_OF_NAMES_OC_OID ) )
+        {
+            return entry.get( memberAT );
+        }
+
+        if ( oc.contains( SchemaConstants.GROUP_OF_UNIQUE_NAMES_OC )
+            || oc.contains( SchemaConstants.GROUP_OF_UNIQUE_NAMES_OC_OID ) )
+        {
+            return entry.get( uniqueMemberAT );
+        }
+
+        return null;
+    }
+
+
+    /**
+     * Adds normalized member DNs to the set of normalized member names.
+     *
+     * @param memberSet the set of member Dns (Strings)
+     * @param members the member attribute values being added
+     * @throws NamingException if there are problems accessing the attr values
+     */
+    private void addMembers( Set<String> memberSet, EntryAttribute members ) throws NamingException
+    {
+        for ( Value<?> value : members )
+        {
+
+            // get and normalize the DN of the member
+            String memberDn = ( String ) value.get();
+
+            try
+            {
+                memberDn = parseNormalized( memberDn ).toString();
+            }
+            catch ( NamingException e )
+            {
+                LOG.warn( "Malformed member DN in groupOf[Unique]Names entry.  Member not added to GroupCache.", e );
+            }
+
+            memberSet.add( memberDn );
+        }
+    }
+
+
+    /**
+     * Removes a set of member names from an existing set.
+     *
+     * @param memberSet the set of normalized member DNs
+     * @param members the set of member values
+     * @throws NamingException if there are problems accessing the attr values
+     */
+    private void removeMembers( Set<String> memberSet, EntryAttribute members ) throws NamingException
+    {
+        for ( Value<?> value : members )
+        {
+            // get and normalize the DN of the member
+            String memberDn = ( String ) value.get();
+
+            try
+            {
+                memberDn = parseNormalized( memberDn ).toString();
+            }
+            catch ( NamingException e )
+            {
+                LOG.warn( "Malformed member DN in groupOf[Unique]Names entry.  Member not removed from GroupCache.", e );
+            }
+
+            memberSet.remove( memberDn );
+        }
+    }
+
+
+    /**
+     * Adds a groups members to the cache.  Called by interceptor to account for new
+     * group additions.
+     *
+     * @param name the user provided name for the group entry
+     * @param entry the group entry's attributes
+     * @throws NamingException if there are problems accessing the attr values
+     */
+    public void groupAdded( LdapDN name, ServerEntry entry ) throws NamingException
+    {
+        EntryAttribute members = getMemberAttribute( entry );
+
+        if ( members == null )
+        {
+            return;
+        }
+
+        Set<String> memberSet = new HashSet<String>( members.size() );
+        addMembers( memberSet, members );
+        groups.put( name.getNormName(), memberSet );
+
+        if ( IS_DEBUG )
+        {
+            LOG.debug( "group cache contents after adding '{}' :\n {}", name.getUpName(), groups );
+        }
+    }
+
+
+    /**
+     * Deletes a group's members from the cache.  Called by interceptor to account for
+     * the deletion of groups.
+     *
+     * @param name the normalized DN of the group entry
+     * @param entry the attributes of entry being deleted
+     */
+    public void groupDeleted( LdapDN name, ServerEntry entry ) throws NamingException
+    {
+        EntryAttribute members = getMemberAttribute( entry );
+
+        if ( members == null )
+        {
+            return;
+        }
+
+        groups.remove( name.getNormName() );
+
+        if ( IS_DEBUG )
+        {
+            LOG.debug( "group cache contents after deleting '{}' :\n {}", name.getUpName(), groups );
+        }
+    }
+
+
+    /**
+     * Utility method to modify a set of member names based on a modify operation
+     * that changes the members of a group.
+     *
+     * @param memberSet the set of members to be altered
+     * @param modOp the type of modify operation being performed
+     * @param members the members being added, removed or replaced
+     * @throws NamingException if there are problems accessing attribute values
+     */
+    private void modify( Set<String> memberSet, ModificationOperation modOp, EntryAttribute members )
+        throws NamingException
+    {
+
+        switch ( modOp )
+        {
+            case ADD_ATTRIBUTE:
+                addMembers( memberSet, members );
+                break;
+
+            case REPLACE_ATTRIBUTE:
+                if ( members.size() > 0 )
+                {
+                    memberSet.clear();
+                    addMembers( memberSet, members );
+                }
+
+                break;
+
+            case REMOVE_ATTRIBUTE:
+                removeMembers( memberSet, members );
+                break;
+
+            default:
+                throw new InternalError( "Undefined modify operation value of " + modOp );
+        }
+    }
+
+
+    /**
+     * Modifies the cache to reflect changes via modify operations to the group entries.
+     * Called by the interceptor to account for modify ops on groups.
+     *
+     * @param name the normalized name of the group entry modified
+     * @param mods the modification operations being performed
+     * @param entry the group entry being modified
+     * @throws NamingException if there are problems accessing attribute  values
+     */
+    public void groupModified( LdapDN name, List<Modification> mods, ServerEntry entry, Registries registries )
+        throws NamingException
+    {
+        EntryAttribute members = null;
+        String memberAttrId = null;
+        EntryAttribute oc = entry.get( SchemaConstants.OBJECT_CLASS_AT );
+
+        if ( oc.contains( SchemaConstants.GROUP_OF_NAMES_OC ) )
+        {
+            members = entry.get( memberAT );
+            memberAttrId = SchemaConstants.MEMBER_AT;
+        }
+
+        if ( oc.contains( SchemaConstants.GROUP_OF_UNIQUE_NAMES_OC ) )
+        {
+            members = entry.get( uniqueMemberAT );
+            memberAttrId = SchemaConstants.UNIQUE_MEMBER_AT;
+        }
+
+        if ( members == null )
+        {
+            return;
+        }
+
+        for ( Modification modification : mods )
+        {
+            if ( memberAttrId.equalsIgnoreCase( modification.getAttribute().getId() ) )
+            {
+                Set<String> memberSet = groups.get( name.getNormName() );
+
+                if ( memberSet != null )
+                {
+                    modify( memberSet, modification.getOperation(), ( ServerAttribute ) modification.getAttribute() );
+                }
+
+                break;
+            }
+        }
+
+        if ( IS_DEBUG )
+        {
+            LOG.debug( "group cache contents after modifying '{}' :\n {}", name.getUpName(), groups );
+        }
+    }
+
+
+    /**
+     * Modifies the cache to reflect changes via modify operations to the group entries.
+     * Called by the interceptor to account for modify ops on groups.
+     *
+     * @param name the normalized name of the group entry modified
+     * @param modOp the modify operation being performed
+     * @param mods the modifications being performed
+     * @throws NamingException if there are problems accessing attribute  values
+     */
+    public void groupModified( LdapDN name, ModificationOperation modOp, ServerEntry mods ) throws NamingException
+    {
+        EntryAttribute members = getMemberAttribute( mods );
+
+        if ( members == null )
+        {
+            return;
+        }
+
+        Set<String> memberSet = groups.get( name.getNormName() );
+
+        if ( memberSet != null )
+        {
+            modify( memberSet, modOp, members );
+        }
+
+        if ( IS_DEBUG )
+        {
+            LOG.debug( "group cache contents after modifying '{}' :\n {}", name.getUpName(), groups );
+        }
+    }
+
+
+    /**
+     * An optimization.  By having this method here we can directly access the group
+     * membership information and lookup to see if the principalDn is contained within.
+     * 
+     * @param principalDn the normalized DN of the user to check if they are an admin
+     * @return true if the principal is an admin or the admin
+     */
+    public final boolean isPrincipalAnAdministrator( LdapDN principalDn )
+    {
+        if ( principalDn.getNormName().equals( ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED ) )
+        {
+            return true;
+        }
+
+        Set<String> members = groups.get( administratorsGroupDn.getNormName() );
+
+        if ( members == null )
+        {
+            LOG.warn( "What do you mean there is no administrators group? This is bad news." );
+            return false;
+        }
+
+        return members.contains( principalDn.toNormName() );
+    }
+
+
+    /**
+     * Gets the set of groups a user is a member of.  The groups are returned
+     * as normalized Name objects within the set.
+     *
+     * @param member the member (user) to get the groups for
+     * @return a Set of Name objects representing the groups
+     * @throws NamingException if there are problems accessing attribute  values
+     */
+    public Set<LdapDN> getGroups( String member ) throws NamingException
+    {
+        LdapDN normMember;
+
+        try
+        {
+            normMember = parseNormalized( member );
+        }
+        catch ( NamingException e )
+        {
+            LOG
+                .warn(
+                    "Malformed member DN.  Could not find groups for member '{}' in GroupCache. Returning empty set for groups!",
+                    member, e );
+            return EMPTY_GROUPS;
+        }
+
+        Set<LdapDN> memberGroups = null;
+
+        for ( String group : groups.keySet() )
+        {
+            Set<String> members = groups.get( group );
+
+            if ( members == null )
+            {
+                continue;
+            }
+
+            if ( members.contains( normMember.getNormName() ) )
+            {
+                if ( memberGroups == null )
+                {
+                    memberGroups = new HashSet<LdapDN>();
+                }
+
+                memberGroups.add( parseNormalized( group ) );
+            }
+        }
+
+        if ( memberGroups == null )
+        {
+            return EMPTY_GROUPS;
+        }
+
+        return memberGroups;
+    }
+
+
+    public boolean groupRenamed( LdapDN oldName, LdapDN newName )
+    {
+        Set<String> members = groups.remove( oldName.getNormName() );
+
+        if ( members != null )
+        {
+            groups.put( newName.getNormName(), members );
+
+            if ( IS_DEBUG )
+            {
+                LOG.debug( "group cache contents after renaming '{}' :\n{}", oldName.getUpName(), groups );
+            }
+
+            return true;
+        }
+
+        return false;
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/authz/TupleCache.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/authz/TupleCache.java
new file mode 100644
index 0000000..cb86dd7
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/authz/TupleCache.java
@@ -0,0 +1,289 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.authz;
+
+
+import javax.naming.directory.SearchControls;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+
+import java.text.ParseException;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.entry.ServerAttribute;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerSearchResult;
+import org.apache.directory.server.core.interceptor.context.SearchOperationContext;
+import org.apache.directory.server.core.partition.PartitionNexus;
+import org.apache.directory.server.schema.ConcreteNameComponentNormalizer;
+import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
+import org.apache.directory.server.schema.registries.OidRegistry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.aci.ACIItem;
+import org.apache.directory.shared.ldap.aci.ACIItemParser;
+import org.apache.directory.shared.ldap.aci.ACITuple;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.entry.Modification;
+import org.apache.directory.shared.ldap.entry.Value;
+import org.apache.directory.shared.ldap.entry.client.ClientStringValue;
+import org.apache.directory.shared.ldap.exception.LdapSchemaViolationException;
+import org.apache.directory.shared.ldap.filter.EqualityNode;
+import org.apache.directory.shared.ldap.filter.ExprNode;
+import org.apache.directory.shared.ldap.message.AliasDerefMode;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.name.NameComponentNormalizer;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.schema.OidNormalizer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * A cache for tuple sets which responds to specific events to perform
+ * cache house keeping as access control subentries are added, deleted
+ * and modified.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class TupleCache
+{
+    /** the logger for this class */
+    private static final Logger LOG = LoggerFactory.getLogger( TupleCache.class );
+
+    /** a map of strings to ACITuple collections */
+    private final Map<String, List<ACITuple>> tuples = new HashMap<String, List<ACITuple>>();
+
+    /** a handle on the partition nexus */
+    private final PartitionNexus nexus;
+
+    /** a normalizing ACIItem parser */
+    private final ACIItemParser aciParser;
+
+    /** A starage for the PrescriptiveACI attributeType */
+    private AttributeType prescriptiveAciAT;
+
+    /**
+     * The OIDs normalizer map
+     */
+    private Map<String, OidNormalizer> normalizerMap;
+
+
+    /**
+     * Creates a ACITuple cache.
+     *
+     * @param directoryService the context factory configuration for the server
+     * @throws NamingException if initialization fails
+     */
+    public TupleCache( DirectoryService directoryService ) throws NamingException
+    {
+        normalizerMap = directoryService.getRegistries().getAttributeTypeRegistry().getNormalizerMapping();
+        this.nexus = directoryService.getPartitionNexus();
+        AttributeTypeRegistry attributeTypeRegistry = directoryService.getRegistries().getAttributeTypeRegistry();
+        OidRegistry oidRegistry = directoryService.getRegistries().getOidRegistry();
+        NameComponentNormalizer ncn = new ConcreteNameComponentNormalizer( attributeTypeRegistry, oidRegistry );
+        aciParser = new ACIItemParser( ncn, normalizerMap );
+        prescriptiveAciAT = attributeTypeRegistry.lookup( SchemaConstants.PRESCRIPTIVE_ACI_AT );
+        initialize( directoryService.getRegistries() );
+    }
+
+
+    private LdapDN parseNormalized( String name ) throws NamingException
+    {
+        LdapDN dn = new LdapDN( name );
+        dn.normalize( normalizerMap );
+        return dn;
+    }
+
+
+    private void initialize( Registries registries ) throws NamingException
+    {
+        // search all naming contexts for access control subentenries
+        // generate ACITuple Arrays for each subentry
+        // add that subentry to the hash
+        Iterator<String> suffixes = nexus.listSuffixes( null );
+
+        while ( suffixes.hasNext() )
+        {
+            String suffix = suffixes.next();
+            LdapDN baseDn = parseNormalized( suffix );
+            ExprNode filter = new EqualityNode( SchemaConstants.OBJECT_CLASS_AT, new ClientStringValue(
+                SchemaConstants.ACCESS_CONTROL_SUBENTRY_OC ) );
+            SearchControls ctls = new SearchControls();
+            ctls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+            NamingEnumeration<ServerSearchResult> results = nexus.search( new SearchOperationContext( registries,
+                baseDn, AliasDerefMode.NEVER_DEREF_ALIASES, filter, ctls ) );
+
+            while ( results.hasMore() )
+            {
+                ServerSearchResult result = results.next();
+                LdapDN subentryDn = result.getDn().normalize( normalizerMap );
+                ServerEntry serverEntry = result.getServerEntry();
+                EntryAttribute aci = serverEntry.get( prescriptiveAciAT );
+
+                if ( aci == null )
+                {
+                    LOG.warn( "Found accessControlSubentry '" + subentryDn + "' without any "
+                        + SchemaConstants.PRESCRIPTIVE_ACI_AT );
+                    continue;
+                }
+
+                subentryAdded( subentryDn, serverEntry );
+            }
+
+            results.close();
+        }
+    }
+
+
+    private boolean hasPrescriptiveACI( ServerEntry entry ) throws NamingException
+    {
+        // only do something if the entry contains prescriptiveACI
+        EntryAttribute aci = entry.get( prescriptiveAciAT );
+
+        if ( aci == null )
+        {
+            if ( entry.contains( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.ACCESS_CONTROL_SUBENTRY_OC )
+                || entry.contains( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.ACCESS_CONTROL_SUBENTRY_OC_OID ) )
+            {
+                // should not be necessary because of schema interceptor but schema checking
+                // can be turned off and in this case we must protect against being able to
+                // add access control information to anything other than an AC subentry
+                throw new LdapSchemaViolationException( "", ResultCodeEnum.OBJECT_CLASS_VIOLATION );
+            }
+            else
+            {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+
+    public void subentryAdded( LdapDN normName, ServerEntry entry ) throws NamingException
+    {
+        // only do something if the entry contains prescriptiveACI
+        EntryAttribute aciAttr = entry.get( prescriptiveAciAT );
+
+        if ( !hasPrescriptiveACI( entry ) )
+        {
+            return;
+        }
+
+        List<ACITuple> entryTuples = new ArrayList<ACITuple>();
+
+        for ( Value<?> value : aciAttr )
+        {
+            String aci = ( String ) value.get();
+            ACIItem item = null;
+
+            try
+            {
+                item = aciParser.parse( aci );
+                entryTuples.addAll( item.toTuples() );
+            }
+            catch ( ParseException e )
+            {
+                String msg = "ACIItem parser failure on \n'" + item + "'\ndue to syntax error. "
+                    + "Cannnot add ACITuples to TupleCache.\n"
+                    + "Check that the syntax of the ACI item is correct. \nUntil this error "
+                    + "is fixed your security settings will not be as expected.";
+                LOG.error( msg, e );
+
+                // do not process this ACI Item because it will be null
+                // continue on to process the next ACI item in the entry
+            }
+        }
+
+        tuples.put( normName.toNormName(), entryTuples );
+    }
+
+
+    public void subentryDeleted( LdapDN normName, ServerEntry entry ) throws NamingException
+    {
+        if ( !hasPrescriptiveACI( entry ) )
+        {
+            return;
+        }
+
+        tuples.remove( normName.toString() );
+    }
+
+
+    public void subentryModified( LdapDN normName, List<Modification> mods, ServerEntry entry ) throws NamingException
+    {
+        if ( !hasPrescriptiveACI( entry ) )
+        {
+            return;
+        }
+
+        for ( Modification mod : mods )
+        {
+            if ( ( ( ServerAttribute ) mod.getAttribute() ).instanceOf( SchemaConstants.PRESCRIPTIVE_ACI_AT ) )
+            {
+                subentryDeleted( normName, entry );
+                subentryAdded( normName, entry );
+            }
+        }
+    }
+
+
+    public void subentryModified( LdapDN normName, ServerEntry mods, ServerEntry entry ) throws NamingException
+    {
+        if ( !hasPrescriptiveACI( entry ) )
+        {
+            return;
+        }
+
+        if ( mods.get( prescriptiveAciAT ) != null )
+        {
+            subentryDeleted( normName, entry );
+            subentryAdded( normName, entry );
+        }
+    }
+
+
+    @SuppressWarnings("unchecked")
+    public List<ACITuple> getACITuples( String subentryDn )
+    {
+        List aciTuples = tuples.get( subentryDn );
+        if ( aciTuples == null )
+        {
+            return Collections.EMPTY_LIST;
+        }
+        return Collections.unmodifiableList( aciTuples );
+    }
+
+
+    public void subentryRenamed( LdapDN oldName, LdapDN newName )
+    {
+        tuples.put( newName.toString(), tuples.remove( oldName.toString() ) );
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/authz/support/ACDFEngine.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/authz/support/ACDFEngine.java
new file mode 100644
index 0000000..8e1b5d7
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/authz/support/ACDFEngine.java
@@ -0,0 +1,267 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.authz.support;
+
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.core.authn.AuthenticationInterceptor;
+import org.apache.directory.server.core.authz.AciAuthorizationInterceptor;
+import org.apache.directory.server.core.authz.DefaultAuthorizationInterceptor;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.event.Evaluator;
+import org.apache.directory.server.core.event.EventInterceptor;
+import org.apache.directory.server.core.event.ExpressionEvaluator;
+import org.apache.directory.server.core.interceptor.context.LookupOperationContext;
+import org.apache.directory.server.core.normalization.NormalizationInterceptor;
+import org.apache.directory.server.core.operational.OperationalAttributeInterceptor;
+import org.apache.directory.server.core.partition.PartitionNexusProxy;
+import org.apache.directory.server.core.schema.SchemaInterceptor;
+import org.apache.directory.server.core.subtree.RefinementEvaluator;
+import org.apache.directory.server.core.subtree.RefinementLeafEvaluator;
+import org.apache.directory.server.core.subtree.SubentryInterceptor;
+import org.apache.directory.server.core.subtree.SubtreeEvaluator;
+import org.apache.directory.server.core.trigger.TriggerInterceptor;
+import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
+import org.apache.directory.server.schema.registries.OidRegistry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.aci.ACITuple;
+import org.apache.directory.shared.ldap.aci.MicroOperation;
+import org.apache.directory.shared.ldap.constants.AuthenticationLevel;
+import org.apache.directory.shared.ldap.entry.Value;
+import org.apache.directory.shared.ldap.exception.LdapNoPermissionException;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+
+/**
+ * An implementation of Access Control Decision Function (18.8, X.501).
+ * <p>
+ * This engine simply filters the collection of tuples using the following
+ * {@link ACITupleFilter}s sequentially:
+ * <ol>
+ * <li>{@link RelatedUserClassFilter}</li>
+ * <li>{@link RelatedProtectedItemFilter}</li>
+ * <li>{@link MaxValueCountFilter}</li>
+ * <li>{@link MaxImmSubFilter}</li>
+ * <li>{@link RestrictedByFilter}</li>
+ * <li>{@link MicroOperationFilter}</li>
+ * <li>{@link HighestPrecedenceFilter}</li>
+ * <li>{@link MostSpecificUserClassFilter}</li>
+ * <li>{@link MostSpecificProtectedItemFilter}</li>
+ * </ol>
+ * <p>
+ * Operation is determined to be permitted if and only if there is at least one
+ * tuple left and all of them grants the access. (18.8.4. X.501)
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ACDFEngine
+{
+    private final ACITupleFilter[] filters;
+
+
+    /**
+     * Creates a new instance.
+     * 
+     * @param oidRegistry an OID registry to be used by internal components
+     * @param attrTypeRegistry an attribute type registry to be used by internal components 
+     * 
+     * @throws NamingException if failed to initialize internal components
+     */
+    public ACDFEngine(OidRegistry oidRegistry, AttributeTypeRegistry attrTypeRegistry) throws NamingException
+    {
+        Evaluator entryEvaluator = new ExpressionEvaluator( oidRegistry, attrTypeRegistry );
+        SubtreeEvaluator subtreeEvaluator = new SubtreeEvaluator( oidRegistry, attrTypeRegistry );
+        RefinementEvaluator refinementEvaluator = new RefinementEvaluator( new RefinementLeafEvaluator( oidRegistry ) );
+
+        filters = new ACITupleFilter[] {
+            new RelatedUserClassFilter( subtreeEvaluator ),
+            new RelatedProtectedItemFilter( refinementEvaluator, entryEvaluator, oidRegistry, attrTypeRegistry ),
+            new MaxValueCountFilter(),
+            new MaxImmSubFilter(),
+            new RestrictedByFilter(),
+            new MicroOperationFilter(),
+            new HighestPrecedenceFilter(),
+            new MostSpecificUserClassFilter(),
+            new MostSpecificProtectedItemFilter() };
+    }
+
+
+    /**
+     * Checks the user with the specified name can access the specified resource
+     * (entry, attribute type, or attribute value) and throws {@link LdapNoPermissionException}
+     * if the user doesn't have any permission to perform the specified grants.
+     * 
+     * @param proxy the proxy to the partition nexus
+     * @param userGroupNames the collection of the group DNs the user who is trying to access the resource belongs
+     * @param username the DN of the user who is trying to access the resource
+     * @param entryName the DN of the entry the user is trying to access
+     * @param attrId the attribute type of the attribute the user is trying to access.
+     *               <tt>null</tt> if the user is not accessing a specific attribute type.
+     * @param attrValue the attribute value of the attribute the user is trying to access.
+     *                  <tt>null</tt> if the user is not accessing a specific attribute value.
+     * @param microOperations the {@link org.apache.directory.shared.ldap.aci.MicroOperation}s to perform
+     * @param aciTuples {@link org.apache.directory.shared.ldap.aci.ACITuple}s translated from {@link org.apache.directory.shared.ldap.aci.ACIItem}s in the subtree entries
+     * @param entryView in case of a Modify operation, view of the entry being modified as if the modification permitted and completed
+     * @throws NamingException if failed to evaluate ACI items
+     */
+    public void checkPermission( 
+        Registries registries, 
+        PartitionNexusProxy proxy, 
+        Collection<LdapDN> userGroupNames, 
+        LdapDN username,
+        AuthenticationLevel authenticationLevel, 
+        LdapDN entryName, 
+        String attrId, 
+        Value<?> attrValue, 
+        Collection<MicroOperation> microOperations, 
+        Collection<ACITuple> aciTuples, 
+        ServerEntry entry, 
+        ServerEntry entryView ) throws NamingException
+    {
+        if ( !hasPermission( registries, proxy, userGroupNames, username, authenticationLevel, entryName, attrId, attrValue,
+            microOperations, aciTuples, entry, entryView ) )
+        {
+            throw new LdapNoPermissionException();
+        }
+    }
+
+    public static final Collection<String> USER_LOOKUP_BYPASS;
+    static
+    {
+        Collection<String> c = new HashSet<String>();
+        c.add( NormalizationInterceptor.class.getName() );
+        c.add( AuthenticationInterceptor.class.getName() );
+//        c.add( ReferralInterceptor.class.getName() );
+        c.add( AciAuthorizationInterceptor.class.getName() );
+        c.add( DefaultAuthorizationInterceptor.class.getName() );
+//        c.add( ExceptionInterceptor.class.getName() );
+        c.add( OperationalAttributeInterceptor.class.getName() );
+        c.add( SchemaInterceptor.class.getName() );
+        c.add( SubentryInterceptor.class.getName() );
+//        c.add( CollectiveAttributeInterceptor.class.getName() );
+        c.add( EventInterceptor.class.getName() );
+        c.add( TriggerInterceptor.class.getName() );
+        USER_LOOKUP_BYPASS = Collections.unmodifiableCollection( c );
+    }
+
+
+    /**
+     * Returns <tt>true</tt> if the user with the specified name can access the specified resource
+     * (entry, attribute type, or attribute value) and throws {@link LdapNoPermissionException}
+     * if the user doesn't have any permission to perform the specified grants.
+     * 
+     * @param proxy the proxy to the partition nexus
+     * @param userGroupNames the collection of the group DNs the user who is trying to access the resource belongs
+     * @param userName the DN of the user who is trying to access the resource
+     * @param entryName the DN of the entry the user is trying to access
+     * @param attrId the attribute type of the attribute the user is trying to access.
+     *               <tt>null</tt> if the user is not accessing a specific attribute type.
+     * @param attrValue the attribute value of the attribute the user is trying to access.
+     *                  <tt>null</tt> if the user is not accessing a specific attribute value.
+     * @param microOperations the {@link org.apache.directory.shared.ldap.aci.MicroOperation}s to perform
+     * @param aciTuples {@link org.apache.directory.shared.ldap.aci.ACITuple}s translated from {@link org.apache.directory.shared.ldap.aci.ACIItem}s in the subtree entries
+     * @param entryView in case of a Modify operation, view of the entry being modified as if the modification permitted and completed
+     */
+    public boolean hasPermission( 
+        Registries registries, 
+        PartitionNexusProxy proxy, 
+        Collection<LdapDN> userGroupNames, 
+        LdapDN userName,
+        AuthenticationLevel authenticationLevel, 
+        LdapDN entryName, 
+        String attrId, 
+        Value<?> attrValue, 
+        Collection<MicroOperation> microOperations, 
+        Collection<ACITuple> aciTuples, 
+        ServerEntry entry, 
+        ServerEntry entryView ) throws NamingException
+    {
+        if ( entryName == null )
+        {
+            throw new NullPointerException( "entryName" );
+        }
+
+        ServerEntry userEntry = proxy.lookup( new LookupOperationContext( registries, userName ), USER_LOOKUP_BYPASS );
+
+        // Determine the scope of the requested operation.
+        OperationScope scope;
+        
+        if ( attrId == null )
+        {
+            scope = OperationScope.ENTRY;
+        }
+        else if ( attrValue == null )
+        {
+            scope = OperationScope.ATTRIBUTE_TYPE;
+        }
+        else
+        {
+            scope = OperationScope.ATTRIBUTE_TYPE_AND_VALUE;
+        }
+
+        // Clone aciTuples in case it is unmodifiable.
+        aciTuples = new ArrayList<ACITuple>( aciTuples );
+
+        // Filter unrelated and invalid tuples
+        for ( ACITupleFilter filter : filters )
+        {
+            aciTuples = filter.filter( 
+                registries, 
+                aciTuples, 
+                scope, 
+                proxy, 
+                userGroupNames, 
+                userName, 
+                userEntry,
+                authenticationLevel, 
+                entryName, 
+                attrId, 
+                attrValue, 
+                entry, 
+                microOperations, 
+                entryView );
+        }
+
+        // Deny access if no tuples left.
+        if ( aciTuples.size() == 0 )
+        {
+            return false;
+        }
+
+        // Grant access if and only if one or more tuples remain and
+        // all grant access. Otherwise deny access.
+        for ( ACITuple tuple : aciTuples )
+        {
+            if ( !tuple.isGrant() )
+            {
+                return false;
+            }
+        }
+
+        return true;
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/authz/support/ACITupleFilter.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/authz/support/ACITupleFilter.java
new file mode 100644
index 0000000..228ee2e
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/authz/support/ACITupleFilter.java
@@ -0,0 +1,83 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.authz.support;
+
+
+import java.util.Collection;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.partition.PartitionNexusProxy;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.aci.ACITuple;
+import org.apache.directory.shared.ldap.aci.MicroOperation;
+import org.apache.directory.shared.ldap.constants.AuthenticationLevel;
+import org.apache.directory.shared.ldap.entry.Value;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+
+/**
+ * An interface that filters the specified collection of tuples using the
+ * specified extra information.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ *
+ */
+public interface ACITupleFilter
+{
+    /**
+     * Returns the collection of the filtered tuples using the specified
+     * extra information.
+     * 
+     * @param tuples the collection of tuples to filter
+     * @param scope the scope of the operation to be performed
+     * @param proxy the proxy interceptor for this filter to access the DIT
+     * @param userGroupNames the collection of group ({@link LdapDN})s which the current user belongs to
+     * @param userName the {@link LdapDN} of the current user
+     * @param userEntry the {@link ServerEntry} of the current user entry in the DIT
+     * @param authenticationLevel the level of authentication of the current user
+     * @param entryName the {@link LdapDN} of the entry the current user accesses
+     * @param attrId the attribute ID the current user accesses
+     * @param attrValue the value of the attribute the current user accesses
+     * @param entry the {@link ServerEntry} of the entry the current user accesses
+     * @param microOperations the set of {@link MicroOperation}s the current user will perform
+     * @param entryView in case of a Modify operation, view of the entry being modified as if the modification permitted and completed
+     * @return the collection of filtered tuples
+     * @throws NamingException if failed to filter the specific tuples
+     */
+    Collection<ACITuple> filter( 
+            Registries registries, 
+            Collection<ACITuple> tuples, 
+            OperationScope scope, 
+            PartitionNexusProxy proxy,
+            Collection<LdapDN> userGroupNames, 
+            LdapDN userName, 
+            ServerEntry userEntry,
+            AuthenticationLevel authenticationLevel, 
+            LdapDN entryName, 
+            String attrId,
+            Value<?> attrValue, 
+            ServerEntry entry, 
+            Collection<MicroOperation> microOperations,
+            ServerEntry entryView )
+        throws NamingException;
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/authz/support/HighestPrecedenceFilter.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/authz/support/HighestPrecedenceFilter.java
new file mode 100644
index 0000000..39ddba4
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/authz/support/HighestPrecedenceFilter.java
@@ -0,0 +1,93 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.authz.support;
+
+
+import java.util.Collection;
+import java.util.Iterator;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.partition.PartitionNexusProxy;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.aci.ACITuple;
+import org.apache.directory.shared.ldap.aci.MicroOperation;
+import org.apache.directory.shared.ldap.constants.AuthenticationLevel;
+import org.apache.directory.shared.ldap.entry.Value;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+
+/**
+ * An {@link ACITupleFilter} that discards all tuples having a precedence less
+ * than the highest remaining precedence. (18.8.4.1, X.501)
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class HighestPrecedenceFilter implements ACITupleFilter
+{
+    public Collection<ACITuple> filter( 
+            Registries registries, 
+            Collection<ACITuple> tuples, 
+            OperationScope scope, 
+            PartitionNexusProxy proxy,
+            Collection<LdapDN> userGroupNames, 
+            LdapDN userName, 
+            ServerEntry userEntry, 
+            AuthenticationLevel authenticationLevel,
+            LdapDN entryName, 
+            String attrId, 
+            Value<?> attrValue, 
+            ServerEntry entry, 
+            Collection<MicroOperation> microOperations,
+            ServerEntry entryView )
+        throws NamingException
+    {
+        if ( tuples.size() <= 1 )
+        {
+            return tuples;
+        }
+
+        int maxPrecedence = -1;
+
+        // Find the maximum precedence for all tuples.
+        for ( ACITuple tuple:tuples )
+        {
+            if ( tuple.getPrecedence() > maxPrecedence )
+            {
+                maxPrecedence = tuple.getPrecedence();
+            }
+        }
+
+        // Remove all tuples whose precedences are not the maximum one.
+        for ( Iterator<ACITuple> i = tuples.iterator(); i.hasNext(); )
+        {
+            ACITuple tuple = i.next();
+            
+            if ( tuple.getPrecedence() != maxPrecedence )
+            {
+                i.remove();
+            }
+        }
+
+        return tuples;
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/authz/support/MaxImmSubFilter.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/authz/support/MaxImmSubFilter.java
new file mode 100644
index 0000000..0b1917c
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/authz/support/MaxImmSubFilter.java
@@ -0,0 +1,191 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.authz.support;
+
+
+import javax.naming.directory.SearchControls;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+
+import org.apache.directory.server.core.authn.AuthenticationInterceptor;
+import org.apache.directory.server.core.authz.AciAuthorizationInterceptor;
+import org.apache.directory.server.core.authz.DefaultAuthorizationInterceptor;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerSearchResult;
+import org.apache.directory.server.core.event.EventInterceptor;
+import org.apache.directory.server.core.interceptor.context.SearchOperationContext;
+import org.apache.directory.server.core.normalization.NormalizationInterceptor;
+import org.apache.directory.server.core.operational.OperationalAttributeInterceptor;
+import org.apache.directory.server.core.partition.PartitionNexusProxy;
+import org.apache.directory.server.core.schema.SchemaInterceptor;
+import org.apache.directory.server.core.subtree.SubentryInterceptor;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.aci.ACITuple;
+import org.apache.directory.shared.ldap.aci.MicroOperation;
+import org.apache.directory.shared.ldap.aci.ProtectedItem;
+import org.apache.directory.shared.ldap.constants.AuthenticationLevel;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.entry.Value;
+import org.apache.directory.shared.ldap.filter.ExprNode;
+import org.apache.directory.shared.ldap.filter.PresenceNode;
+import org.apache.directory.shared.ldap.message.AliasDerefMode;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+
+
+/**
+ * An {@link ACITupleFilter} that discards all tuples that doesn't satisfy
+ * {@link org.apache.directory.shared.ldap.aci.ProtectedItem.MaxImmSub} constraint if available. (18.8.3.3, X.501)
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class MaxImmSubFilter implements ACITupleFilter
+{
+    private final ExprNode childrenFilter;
+    private final SearchControls childrenSearchControls;
+
+
+    public MaxImmSubFilter()
+    {
+        childrenFilter = new PresenceNode( SchemaConstants.OBJECT_CLASS_AT );
+        childrenSearchControls = new SearchControls();
+        childrenSearchControls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+    }
+
+
+    public Collection<ACITuple> filter( 
+            Registries registries, 
+            Collection<ACITuple> tuples, 
+            OperationScope scope, 
+            PartitionNexusProxy proxy,
+            Collection<LdapDN> userGroupNames, 
+            LdapDN userName, 
+            ServerEntry userEntry, 
+            AuthenticationLevel authenticationLevel,
+            LdapDN entryName, 
+            String attrId, 
+            Value<?> attrValue, 
+            ServerEntry entry, 
+            Collection<MicroOperation> microOperations,
+            ServerEntry entryView )
+        throws NamingException
+    {
+        if ( entryName.size() == 0 )
+        {
+            return tuples;
+        }
+
+        if ( tuples.size() == 0 )
+        {
+            return tuples;
+        }
+
+        if ( scope != OperationScope.ENTRY )
+        {
+            return tuples;
+        }
+
+        int immSubCount = -1;
+
+        for ( Iterator<ACITuple> i = tuples.iterator(); i.hasNext(); )
+        {
+            ACITuple tuple = i.next();
+            if ( !tuple.isGrant() )
+            {
+                continue;
+            }
+
+            for ( ProtectedItem item : tuple.getProtectedItems() )
+            {
+                if ( item instanceof ProtectedItem.MaxImmSub )
+                {
+                    if ( immSubCount < 0 )
+                    {
+                        immSubCount = getImmSubCount( registries, proxy, entryName );
+                    }
+
+                    ProtectedItem.MaxImmSub mis = ( ProtectedItem.MaxImmSub ) item;
+                    if ( immSubCount >= mis.getValue() )
+                    {
+                        i.remove();
+                        break;
+                    }
+                }
+            }
+        }
+
+        return tuples;
+    }
+
+    public static final Collection<String> SEARCH_BYPASS;
+    static
+    {
+        Collection<String> c = new HashSet<String>();
+        c.add( NormalizationInterceptor.class.getName() );
+        c.add( AuthenticationInterceptor.class.getName() );
+//        c.add( ReferralInterceptor.class.getName() );
+        c.add( AciAuthorizationInterceptor.class.getName() );
+        c.add( DefaultAuthorizationInterceptor.class.getName() );
+//        c.add( ExceptionInterceptor.class.getName() );
+        c.add( OperationalAttributeInterceptor.class.getName() );
+        c.add( SchemaInterceptor.class.getName() );
+        c.add( SubentryInterceptor.class.getName() );
+//        c.add( CollectiveAttributeInterceptor.class.getName() );
+        c.add( EventInterceptor.class.getName() );
+//        c.add( TriggerInterceptor.class.getName() );
+        SEARCH_BYPASS = Collections.unmodifiableCollection( c );
+    }
+
+
+    private int getImmSubCount( Registries registries, PartitionNexusProxy proxy, LdapDN entryName ) throws NamingException
+    {
+        int cnt = 0;
+        NamingEnumeration<ServerSearchResult> e = null;
+        
+        try
+        {
+            e = proxy.search( new SearchOperationContext( registries, ( LdapDN ) entryName.getPrefix( 1 ),
+                    AliasDerefMode.DEREF_ALWAYS, childrenFilter, childrenSearchControls ), SEARCH_BYPASS );
+
+            while ( e.hasMore() )
+            {
+                e.next();
+                cnt++;
+            }
+
+        }
+        finally
+        {
+            if ( e != null )
+            {
+                e.close();
+            }
+        }
+
+        return cnt;
+    }
+
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/authz/support/MaxValueCountFilter.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/authz/support/MaxValueCountFilter.java
new file mode 100644
index 0000000..f40c38d
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/authz/support/MaxValueCountFilter.java
@@ -0,0 +1,127 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.authz.support;
+
+
+import java.util.Collection;
+import java.util.Iterator;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.partition.PartitionNexusProxy;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.aci.ACITuple;
+import org.apache.directory.shared.ldap.aci.MicroOperation;
+import org.apache.directory.shared.ldap.aci.ProtectedItem;
+import org.apache.directory.shared.ldap.aci.ProtectedItem.MaxValueCountItem;
+import org.apache.directory.shared.ldap.constants.AuthenticationLevel;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.entry.Value;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+
+/**
+ * An {@link ACITupleFilter} that discards all tuples that doesn't satisfy
+ * {@link org.apache.directory.shared.ldap.aci.ProtectedItem.MaxValueCount} constraint if available. (18.8.3.3, X.501)
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class MaxValueCountFilter implements ACITupleFilter
+{
+    public Collection<ACITuple> filter( 
+            Registries registries, 
+            Collection<ACITuple> tuples, 
+            OperationScope scope, 
+            PartitionNexusProxy proxy,
+            Collection<LdapDN> userGroupNames, 
+            LdapDN userName, 
+            ServerEntry userEntry, 
+            AuthenticationLevel authenticationLevel,
+            LdapDN entryName, 
+            String attrId, 
+            Value<?> attrValue, 
+            ServerEntry entry, 
+            Collection<MicroOperation> microOperations,
+            ServerEntry entryView )
+        throws NamingException
+    {
+        if ( scope != OperationScope.ATTRIBUTE_TYPE_AND_VALUE )
+        {
+            return tuples;
+        }
+
+        if ( tuples.size() == 0 )
+        {
+            return tuples;
+        }
+
+        for ( Iterator<ACITuple> i = tuples.iterator(); i.hasNext(); )
+        {
+            ACITuple tuple = i.next();
+            
+            if ( !tuple.isGrant() )
+            {
+                continue;
+            }
+
+            for ( Iterator<ProtectedItem> j = tuple.getProtectedItems().iterator(); j.hasNext(); )
+            {
+                ProtectedItem item = j.next();
+                
+                if ( item instanceof ProtectedItem.MaxValueCount )
+                {
+                    ProtectedItem.MaxValueCount mvc = ( ProtectedItem.MaxValueCount ) item;
+                    
+                    if ( isRemovable( mvc, attrId, entryView ) )
+                    {
+                        i.remove();
+                        break;
+                    }
+                }
+            }
+        }
+
+        return tuples;
+    }
+
+
+    private boolean isRemovable( ProtectedItem.MaxValueCount mvc, String attrId, ServerEntry entryView ) throws NamingException
+    {
+        for ( Iterator<ProtectedItem.MaxValueCountItem> k = mvc.iterator(); k.hasNext(); )
+        {
+            MaxValueCountItem mvcItem = k.next();
+            if ( attrId.equalsIgnoreCase( mvcItem.getAttributeType() ) )
+            {
+                EntryAttribute attr = entryView.get( attrId );
+                int attrCount = attr == null ? 0 : attr.size();
+                
+                if ( attrCount > mvcItem.getMaxCount() )
+                {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/authz/support/MicroOperationFilter.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/authz/support/MicroOperationFilter.java
new file mode 100644
index 0000000..eab5d46
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/authz/support/MicroOperationFilter.java
@@ -0,0 +1,100 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.authz.support;
+
+
+import java.util.Collection;  
+import java.util.Iterator;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.partition.PartitionNexusProxy;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.aci.ACITuple;
+import org.apache.directory.shared.ldap.aci.MicroOperation;
+import org.apache.directory.shared.ldap.constants.AuthenticationLevel;
+import org.apache.directory.shared.ldap.entry.Value;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+
+/**
+ * An {@link ACITupleFilter} that discard tuples which doesn't contain any
+ * related {@link MicroOperation}s. (18.8.3.4, X.501) 
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ *
+ */
+public class MicroOperationFilter implements ACITupleFilter
+{
+    public Collection<ACITuple> filter( 
+            Registries registries, 
+            Collection<ACITuple> tuples, 
+            OperationScope scope, 
+            PartitionNexusProxy proxy,
+            Collection<LdapDN> userGroupNames, 
+            LdapDN userName, 
+            ServerEntry userEntry, 
+            AuthenticationLevel authenticationLevel,
+            LdapDN entryName, 
+            String attrId, 
+            Value<?> attrValue, 
+            ServerEntry entry, 
+            Collection<MicroOperation> microOperations,
+            ServerEntry entryView )
+        throws NamingException
+    {
+        if ( tuples.size() == 0 )
+        {
+            return tuples;
+        }
+
+        for ( Iterator<ACITuple> i = tuples.iterator(); i.hasNext(); )
+        {
+            ACITuple tuple = i.next();
+
+            /*
+             * The ACITuple must contain all the MicroOperations specified within the
+             * microOperations argument.  Just matching a single microOperation is not
+             * enough.  All must be matched to retain the ACITuple.
+             */
+
+            boolean retain = true;
+            
+            for ( MicroOperation microOp:microOperations )
+            {
+                if ( !tuple.getMicroOperations().contains( microOp ) )
+                {
+                    retain = false;
+                    break;
+                }
+            }
+
+            if ( !retain )
+            {
+                i.remove();
+            }
+        }
+
+        return tuples;
+    }
+
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/authz/support/MostSpecificProtectedItemFilter.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/authz/support/MostSpecificProtectedItemFilter.java
new file mode 100644
index 0000000..0eaad2c
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/authz/support/MostSpecificProtectedItemFilter.java
@@ -0,0 +1,122 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.authz.support;
+
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.partition.PartitionNexusProxy;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.aci.ACITuple;
+import org.apache.directory.shared.ldap.aci.MicroOperation;
+import org.apache.directory.shared.ldap.aci.ProtectedItem;
+import org.apache.directory.shared.ldap.constants.AuthenticationLevel;
+import org.apache.directory.shared.ldap.entry.Value;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+
+/**
+ * An {@link ACITupleFilter} that chooses the tuples with the most specific
+ * protected item. (18.8.4.3, X.501)
+ * <p>
+ * If more than one tuple remains, choose the tuples with the most specific
+ * protected item. If the protected item is an attribute and there are tuples 
+ * that specify the attribute type explicitly, discard all other tuples. If
+ * the protected item is an attribute value, and there are tuples that specify
+ * the attribute value explicitly, discard all other tuples. A protected item
+ * which is a rangeOfValues is to be treated as specifying an attribute value
+ * explicitly.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class MostSpecificProtectedItemFilter implements ACITupleFilter
+{
+    public Collection<ACITuple> filter( 
+            Registries registries, 
+            Collection<ACITuple> tuples, 
+            OperationScope scope, 
+            PartitionNexusProxy proxy,
+            Collection<LdapDN> userGroupNames, 
+            LdapDN userName, 
+            ServerEntry userEntry, 
+            AuthenticationLevel authenticationLevel,
+            LdapDN entryName, 
+            String attrId, 
+            Value<?> attrValue, 
+            ServerEntry entry, 
+            Collection<MicroOperation> microOperations,
+            ServerEntry entryView )
+        throws NamingException
+    {
+        if ( tuples.size() <= 1 )
+        {
+            return tuples;
+        }
+
+        Collection<ACITuple> filteredTuples = new ArrayList<ACITuple>();
+
+        // If the protected item is an attribute and there are tuples that
+        // specify the attribute type explicitly, discard all other tuples.
+        for ( ACITuple tuple:tuples )
+        {
+            for ( ProtectedItem item:tuple.getProtectedItems() )
+            {
+                if ( item instanceof ProtectedItem.AttributeType || item instanceof ProtectedItem.AllAttributeValues
+                    || item instanceof ProtectedItem.SelfValue || item instanceof ProtectedItem.AttributeValue )
+                {
+                    filteredTuples.add( tuple );
+                    break;
+                }
+            }
+        }
+
+        if ( filteredTuples.size() > 0 )
+        {
+            return filteredTuples;
+        }
+
+        // If the protected item is an attribute value, and there are tuples
+        // that specify the attribute value explicitly, discard all other tuples.
+        // A protected item which is a rangeOfValues is to be treated as
+        // specifying an attribute value explicitly. 
+        for ( ACITuple tuple:tuples )
+        {
+            for ( ProtectedItem item:tuple.getProtectedItems() )
+            {
+                if ( item instanceof ProtectedItem.RangeOfValues )
+                {
+                    filteredTuples.add( tuple );
+                }
+            }
+        }
+
+        if ( filteredTuples.size() > 0 )
+        {
+            return filteredTuples;
+        }
+
+        return tuples;
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/authz/support/MostSpecificUserClassFilter.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/authz/support/MostSpecificUserClassFilter.java
new file mode 100644
index 0000000..eb98547
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/authz/support/MostSpecificUserClassFilter.java
@@ -0,0 +1,138 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.authz.support;
+
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.partition.PartitionNexusProxy;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.aci.ACITuple;
+import org.apache.directory.shared.ldap.aci.MicroOperation;
+import org.apache.directory.shared.ldap.aci.UserClass;
+import org.apache.directory.shared.ldap.constants.AuthenticationLevel;
+import org.apache.directory.shared.ldap.entry.Value;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+
+/**
+ * An {@link ACITupleFilter} that chooses the tuples with the most specific user
+ * class. (18.8.4.2)
+ * <p>
+ * If more than one tuple remains, choose the tuples with the most specific user
+ * class. If there are any tuples matching the requestor with UserClasses element
+ * name or thisEntry, discard all other tuples. Otherwise if there are any tuples
+ * matching UserGroup, discard all other tuples. Otherwise if there are any tuples
+ * matching subtree, discard all other tuples.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class MostSpecificUserClassFilter implements ACITupleFilter
+{
+    public Collection<ACITuple> filter( 
+            Registries registries, 
+            Collection<ACITuple> tuples, 
+            OperationScope scope, 
+            PartitionNexusProxy proxy,
+            Collection<LdapDN> userGroupNames, 
+            LdapDN userName, 
+            ServerEntry userEntry, 
+            AuthenticationLevel authenticationLevel,
+            LdapDN entryName, 
+            String attrId, 
+            Value<?> attrValue, 
+            ServerEntry entry, 
+            Collection<MicroOperation> microOperations,
+            ServerEntry entryView )
+        throws NamingException
+    {
+        if ( tuples.size() <= 1 )
+        {
+            return tuples;
+        }
+
+        Collection<ACITuple> filteredTuples = new ArrayList<ACITuple>();
+
+        // If there are any tuples matching the requestor with UserClasses
+        // element name or thisEntry, discard all other tuples.
+        for ( ACITuple tuple:tuples )
+        {
+            for ( UserClass userClass:tuple.getUserClasses() )
+            {
+                if ( userClass instanceof UserClass.Name || userClass instanceof UserClass.ThisEntry )
+                {
+                    filteredTuples.add( tuple );
+                    break;
+                }
+            }
+        }
+
+        if ( filteredTuples.size() > 0 )
+        {
+            return filteredTuples;
+        }
+
+        // Otherwise if there are any tuples matching UserGroup,
+        // discard all other tuples.
+        for ( ACITuple tuple:tuples )
+        {
+            for ( UserClass userClass:tuple.getUserClasses() )
+            {
+                if ( userClass instanceof UserClass.UserGroup )
+                {
+                    filteredTuples.add( tuple );
+                    break;
+                }
+            }
+        }
+
+        if ( filteredTuples.size() > 0 )
+        {
+            return filteredTuples;
+        }
+
+        // Otherwise if there are any tuples matching subtree,
+        // discard all other tuples.
+        for ( ACITuple tuple:tuples )
+        {
+            for ( UserClass userClass:tuple.getUserClasses() )
+            {
+                if ( userClass instanceof UserClass.Subtree )
+                {
+                    filteredTuples.add( tuple );
+                    break;
+                }
+            }
+        }
+
+        if ( filteredTuples.size() > 0 )
+        {
+            return filteredTuples;
+        }
+
+        return tuples;
+    }
+
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/authz/support/OperationScope.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/authz/support/OperationScope.java
new file mode 100644
index 0000000..2219bc9
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/authz/support/OperationScope.java
@@ -0,0 +1,71 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.authz.support;
+
+
+/**
+ * An enumeration that represents the scope of user operation.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class OperationScope
+{
+    /**
+     * An operation that affects the whole entry.
+     */
+    public static final OperationScope ENTRY = new OperationScope( "Entry" );
+
+    /**
+     * An operation that affects all values in an attribute type.
+     */
+    public static final OperationScope ATTRIBUTE_TYPE = new OperationScope( "Attribute Type" );
+
+    /**
+     * An operation that affects the specific value in an attribute type.
+     */
+    public static final OperationScope ATTRIBUTE_TYPE_AND_VALUE = new OperationScope( "Attribute Type & Value" );
+
+    private final String name;
+
+
+    private OperationScope(String name)
+    {
+        this.name = name;
+    }
+
+
+    /**
+     * Return the name of this scope.
+     */
+    public String getName()
+    {
+        return name;
+    }
+
+
+    /**
+     * Returns the name of this scope.
+     */
+    public String toString()
+    {
+        return name;
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/authz/support/RelatedProtectedItemFilter.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/authz/support/RelatedProtectedItemFilter.java
new file mode 100644
index 0000000..e997a0b
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/authz/support/RelatedProtectedItemFilter.java
@@ -0,0 +1,287 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.authz.support;
+
+
+import java.util.Collection;
+import java.util.Iterator;
+
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.event.Evaluator;
+import org.apache.directory.server.core.partition.PartitionNexusProxy;
+import org.apache.directory.server.core.subtree.RefinementEvaluator;
+import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
+import org.apache.directory.server.schema.registries.OidRegistry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.aci.ACITuple;
+import org.apache.directory.shared.ldap.aci.MicroOperation;
+import org.apache.directory.shared.ldap.aci.ProtectedItem;
+import org.apache.directory.shared.ldap.aci.ProtectedItem.MaxValueCountItem;
+import org.apache.directory.shared.ldap.aci.ProtectedItem.RestrictedByItem;
+import org.apache.directory.shared.ldap.constants.AuthenticationLevel;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.entry.Value;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.util.AttributeUtils;
+
+
+/**
+ * An {@link ACITupleFilter} that discards all tuples whose {@link ProtectedItem}s
+ * are not related with the operation. (18.8.3.2, X.501)
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class RelatedProtectedItemFilter implements ACITupleFilter
+{
+    private final RefinementEvaluator refinementEvaluator;
+    private final Evaluator entryEvaluator;
+    private final OidRegistry oidRegistry;
+    private final AttributeTypeRegistry attrRegistry;
+
+
+    public RelatedProtectedItemFilter( RefinementEvaluator refinementEvaluator, Evaluator entryEvaluator, 
+        OidRegistry oidRegistry, AttributeTypeRegistry attrRegistry )
+    {
+        this.refinementEvaluator = refinementEvaluator;
+        this.entryEvaluator = entryEvaluator;
+        this.oidRegistry = oidRegistry;
+        this.attrRegistry = attrRegistry;
+    }
+
+
+    public Collection<ACITuple> filter( 
+            Registries registries, 
+            Collection<ACITuple> tuples, 
+            OperationScope scope, 
+            PartitionNexusProxy proxy,
+            Collection<LdapDN> userGroupNames, 
+            LdapDN userName, 
+            ServerEntry userEntry,
+            AuthenticationLevel authenticationLevel, 
+            LdapDN entryName, 
+            String attrId,
+            Value<?> attrValue, 
+            ServerEntry entry, 
+            Collection<MicroOperation> microOperations,
+            ServerEntry entryView )
+        throws NamingException
+    {
+        if ( tuples.size() == 0 )
+        {
+            return tuples;
+        }
+
+        for ( Iterator<ACITuple> i = tuples.iterator(); i.hasNext(); )
+        {
+            ACITuple tuple = i.next();
+            
+            if ( !isRelated( tuple, scope, userName, entryName, attrId, attrValue, entry ) )
+            {
+                i.remove();
+            }
+        }
+
+        return tuples;
+    }
+
+
+    private boolean isRelated( ACITuple tuple, OperationScope scope, LdapDN userName, LdapDN entryName, String attrId,
+                               Value<?> attrValue, ServerEntry entry ) throws NamingException, InternalError
+    {
+        String oid = null;
+        
+        if ( attrId != null )
+        {
+            oid = oidRegistry.getOid( attrId );
+        }
+        
+        for ( ProtectedItem item : tuple.getProtectedItems() )
+        {
+            if ( item == ProtectedItem.ENTRY )
+            {
+                if ( scope == OperationScope.ENTRY )
+                {
+                    return true;
+                }
+            }
+            else if ( item == ProtectedItem.ALL_USER_ATTRIBUTE_TYPES )
+            {
+                if ( scope != OperationScope.ATTRIBUTE_TYPE && scope != OperationScope.ATTRIBUTE_TYPE_AND_VALUE )
+                {
+                    continue;
+                }
+
+                return true;
+            }
+            else if ( item == ProtectedItem.ALL_USER_ATTRIBUTE_TYPES_AND_VALUES )
+            {
+                if ( scope != OperationScope.ATTRIBUTE_TYPE && scope != OperationScope.ATTRIBUTE_TYPE_AND_VALUE )
+                {
+                    continue;
+                }
+
+                return true;
+            }
+            else if ( item instanceof ProtectedItem.AllAttributeValues )
+            {
+                if ( scope != OperationScope.ATTRIBUTE_TYPE_AND_VALUE )
+                {
+                    continue;
+                }
+
+                ProtectedItem.AllAttributeValues aav = ( ProtectedItem.AllAttributeValues ) item;
+                for ( Iterator<String> j = aav.iterator(); j.hasNext(); )
+                {
+                    if ( oid.equals( oidRegistry.getOid( j.next() ) ) )
+                    {
+                        return true;
+                    }
+                }
+            }
+            else if ( item instanceof ProtectedItem.AttributeType )
+            {
+                if ( scope != OperationScope.ATTRIBUTE_TYPE )
+                {
+                    continue;
+                }
+
+                ProtectedItem.AttributeType at = ( ProtectedItem.AttributeType ) item;
+                for ( Iterator<String> j = at.iterator(); j.hasNext(); )
+                {
+                    if ( oid.equals( oidRegistry.getOid( j.next() ) ) )
+                    {
+                        return true;
+                    }
+                }
+            }
+            else if ( item instanceof ProtectedItem.AttributeValue )
+            {
+                if ( scope != OperationScope.ATTRIBUTE_TYPE_AND_VALUE )
+                {
+                    continue;
+                }
+
+                ProtectedItem.AttributeValue av = ( ProtectedItem.AttributeValue ) item;
+                for ( Iterator<Attribute> j = av.iterator(); j.hasNext(); )
+                {
+                    Attribute attr = j.next();
+                    String attrOid = oidRegistry.getOid( attr.getID() );
+                    AttributeType attrType = attrRegistry.lookup( attrOid );
+                    
+                    if ( oid.equals( attrOid ) && AttributeUtils.containsValue( attr, attrValue.get(), attrType ) )
+                    {
+                        return true;
+                    }
+                }
+            }
+            else if ( item instanceof ProtectedItem.Classes )
+            {
+                ProtectedItem.Classes c = ( ProtectedItem.Classes ) item;
+                if ( refinementEvaluator.evaluate( c.getClasses(), entry.get( SchemaConstants.OBJECT_CLASS_AT ) ) )
+                {
+                    return true;
+                }
+            }
+            else if ( item instanceof ProtectedItem.MaxImmSub )
+            {
+                return true;
+            }
+            else if ( item instanceof ProtectedItem.MaxValueCount )
+            {
+                if ( scope != OperationScope.ATTRIBUTE_TYPE_AND_VALUE )
+                {
+                    continue;
+                }
+
+                ProtectedItem.MaxValueCount mvc = ( ProtectedItem.MaxValueCount ) item;
+                for ( Iterator<MaxValueCountItem> j = mvc.iterator(); j.hasNext(); )
+                {
+                    MaxValueCountItem mvcItem = j.next();
+                    if ( oid.equals( oidRegistry.getOid( mvcItem.getAttributeType() ) ) )
+                    {
+                        return true;
+                    }
+                }
+            }
+            else if ( item instanceof ProtectedItem.RangeOfValues )
+            {
+                ProtectedItem.RangeOfValues rov = ( ProtectedItem.RangeOfValues ) item;
+                
+                if ( entryEvaluator.evaluate( rov.getFilter(), entryName.toString(), entry ) )
+                {
+                    return true;
+                }
+            }
+            else if ( item instanceof ProtectedItem.RestrictedBy )
+            {
+                if ( scope != OperationScope.ATTRIBUTE_TYPE_AND_VALUE )
+                {
+                    continue;
+                }
+
+                ProtectedItem.RestrictedBy rb = ( ProtectedItem.RestrictedBy ) item;
+                for ( Iterator<RestrictedByItem> j = rb.iterator(); j.hasNext(); )
+                {
+                    RestrictedByItem rbItem = j.next();
+                    if ( oid.equals( oidRegistry.getOid( rbItem.getAttributeType() ) ) )
+                    {
+                        return true;
+                    }
+                }
+            }
+            else if ( item instanceof ProtectedItem.SelfValue )
+            {
+                if ( scope != OperationScope.ATTRIBUTE_TYPE_AND_VALUE && scope != OperationScope.ATTRIBUTE_TYPE )
+                {
+                    continue;
+                }
+
+                ProtectedItem.SelfValue sv = ( ProtectedItem.SelfValue ) item;
+                for ( Iterator<String> j = sv.iterator(); j.hasNext(); )
+                {
+                    String svItem = j.next();
+                    if ( oid.equals( oidRegistry.getOid( svItem ) ) )
+                    {
+                        EntryAttribute attr = entry.get( oid );
+                        
+                        if ( ( attr != null ) && 
+                             ( ( attr.contains( userName.toNormName() ) || 
+                               ( attr.contains( userName.getUpName() ) ) ) ) )
+                        {
+                            return true;
+                        }
+                    }
+                }
+            }
+            else
+            {
+                throw new InternalError( "Unexpected protectedItem: " + item.getClass().getName() );
+            }
+        }
+
+        return false;
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/authz/support/RelatedUserClassFilter.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/authz/support/RelatedUserClassFilter.java
new file mode 100644
index 0000000..a418222
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/authz/support/RelatedUserClassFilter.java
@@ -0,0 +1,185 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.authz.support;
+
+
+import java.util.Collection;
+import java.util.Iterator;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.partition.PartitionNexusProxy;
+import org.apache.directory.server.core.subtree.SubtreeEvaluator;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.aci.ACITuple;
+import org.apache.directory.shared.ldap.aci.MicroOperation;
+import org.apache.directory.shared.ldap.aci.UserClass;
+import org.apache.directory.shared.ldap.constants.AuthenticationLevel;
+import org.apache.directory.shared.ldap.entry.Value;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.subtree.SubtreeSpecification;
+
+
+/**
+ * An {@link ACITupleFilter} that discards all tuples whose {@link UserClass}es
+ * are not related with the current user. (18.8.3.1, X.501)
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class RelatedUserClassFilter implements ACITupleFilter
+{
+    private static final LdapDN ROOTDSE_NAME = LdapDN.EMPTY_LDAPDN;
+
+    private final SubtreeEvaluator subtreeEvaluator;
+
+
+    public RelatedUserClassFilter(SubtreeEvaluator subtreeEvaluator)
+    {
+        this.subtreeEvaluator = subtreeEvaluator;
+    }
+
+
+    public Collection<ACITuple> filter( 
+            Registries registries, 
+            Collection<ACITuple> tuples, 
+            OperationScope scope, 
+            PartitionNexusProxy proxy,
+            Collection<LdapDN> userGroupNames, 
+            LdapDN userName, 
+            ServerEntry userEntry, 
+            AuthenticationLevel authenticationLevel,
+            LdapDN entryName, 
+            String attrId, 
+            Value<?> attrValue, 
+            ServerEntry entry, 
+            Collection<MicroOperation> microOperations,
+            ServerEntry entryView )
+        throws NamingException
+    {
+        if ( tuples.size() == 0 )
+        {
+            return tuples;
+        }
+
+        for ( Iterator<ACITuple> ii = tuples.iterator(); ii.hasNext(); )
+        {
+            ACITuple tuple = ii.next();
+            
+            if ( tuple.isGrant() )
+            {
+                if ( !isRelated( userGroupNames, 
+                                 userName, 
+                                 userEntry, 
+                                 entryName, 
+                                 tuple.getUserClasses() )
+                    || authenticationLevel.compareTo( tuple.getAuthenticationLevel() ) < 0 )
+                {
+                    ii.remove();
+                }
+            }
+            else
+            // Denials
+            {
+                if ( !isRelated( userGroupNames, 
+                                 userName, 
+                                 userEntry, 
+                                 entryName, 
+                                 tuple.getUserClasses() )
+                    && authenticationLevel.compareTo( tuple.getAuthenticationLevel() ) >= 0 )
+                {
+                    ii.remove();
+                }
+            }
+        }
+
+        return tuples;
+    }
+
+
+    private boolean isRelated( Collection<LdapDN> userGroupNames, LdapDN userName, ServerEntry userEntry, 
+        LdapDN entryName, Collection<UserClass> userClasses ) throws NamingException
+    {
+        for ( UserClass userClass : userClasses )
+        {
+            if ( userClass == UserClass.ALL_USERS )
+            {
+                return true;
+            }
+            else if ( userClass == UserClass.THIS_ENTRY )
+            {
+                if ( userName.equals( entryName ) )
+                {
+                    return true;
+                }
+            }
+            else if ( userClass instanceof UserClass.Name )
+            {
+                UserClass.Name nameUserClass = ( UserClass.Name ) userClass;
+                if ( nameUserClass.getNames().contains( userName ) )
+                {
+                    return true;
+                }
+            }
+            else if ( userClass instanceof UserClass.UserGroup )
+            {
+                UserClass.UserGroup userGroupUserClass = ( UserClass.UserGroup ) userClass;
+                
+                for ( LdapDN userGroupName : userGroupNames )
+                {
+                    if ( userGroupName != null && userGroupUserClass.getNames().contains( userGroupName ) )
+                    {
+                        return true;
+                    }
+                }
+            }
+            else if ( userClass instanceof UserClass.Subtree )
+            {
+                UserClass.Subtree subtree = ( UserClass.Subtree ) userClass;
+                if ( matchUserClassSubtree( userName, userEntry, subtree ) )
+                {
+                    return true;
+                }
+            }
+            else
+            {
+                throw new InternalError( "Unexpected userClass: " + userClass.getClass().getName() );
+            }
+        }
+
+        return false;
+    }
+
+
+    private boolean matchUserClassSubtree( LdapDN userName, ServerEntry userEntry, UserClass.Subtree subtree )
+        throws NamingException
+    {
+        for ( SubtreeSpecification subtreeSpec : subtree.getSubtreeSpecifications() )
+        {
+            if ( subtreeEvaluator.evaluate( subtreeSpec, ROOTDSE_NAME, userName, userEntry ) )
+            {
+                return true;
+            }
+        }
+
+        return false;
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/authz/support/RestrictedByFilter.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/authz/support/RestrictedByFilter.java
new file mode 100644
index 0000000..497fc93
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/authz/support/RestrictedByFilter.java
@@ -0,0 +1,125 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.authz.support;
+
+
+import java.util.Collection;
+import java.util.Iterator;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.partition.PartitionNexusProxy;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.aci.ACITuple;
+import org.apache.directory.shared.ldap.aci.MicroOperation;
+import org.apache.directory.shared.ldap.aci.ProtectedItem;
+import org.apache.directory.shared.ldap.aci.ProtectedItem.RestrictedByItem;
+import org.apache.directory.shared.ldap.constants.AuthenticationLevel;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.entry.Value;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+
+/**
+ * An {@link ACITupleFilter} that discards all tuples that doesn't satisfy
+ * {@link org.apache.directory.shared.ldap.aci.ProtectedItem.RestrictedBy} constraint if available. (18.8.3.3, X.501)
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class RestrictedByFilter implements ACITupleFilter
+{
+    public Collection<ACITuple> filter( 
+            Registries registries, 
+            Collection<ACITuple> tuples, 
+            OperationScope scope, 
+            PartitionNexusProxy proxy,
+            Collection<LdapDN> userGroupNames, 
+            LdapDN userName, 
+            ServerEntry userEntry, 
+            AuthenticationLevel authenticationLevel,
+            LdapDN entryName, 
+            String attrId, 
+            Value<?> attrValue, 
+            ServerEntry entry, 
+            Collection<MicroOperation> microOperations,
+            ServerEntry entryView )
+        throws NamingException
+    {
+        if ( scope != OperationScope.ATTRIBUTE_TYPE_AND_VALUE )
+        {
+            return tuples;
+        }
+
+        if ( tuples.size() == 0 )
+        {
+            return tuples;
+        }
+
+        for ( Iterator<ACITuple> ii = tuples.iterator() ; ii.hasNext(); )
+        {
+            ACITuple tuple = ii.next();
+            
+            if ( !tuple.isGrant() )
+            {
+                continue;
+            }
+
+            if ( isRemovable( tuple, attrId, attrValue, entry ) )
+            {
+                ii.remove();
+            }
+        }
+
+        return tuples;
+    }
+
+
+    public boolean isRemovable( ACITuple tuple, String attrId, Value<?> attrValue, ServerEntry entry ) throws NamingException
+    {
+        for ( ProtectedItem item : tuple.getProtectedItems() )
+        {
+            if ( item instanceof ProtectedItem.RestrictedBy )
+            {
+                ProtectedItem.RestrictedBy rb = ( ProtectedItem.RestrictedBy ) item;
+            
+                for ( Iterator<RestrictedByItem> k = rb.iterator(); k.hasNext(); )
+                {
+                    RestrictedByItem rbItem = k.next();
+                
+                    // TODO Fix DIRSEVER-832 
+                    if ( attrId.equalsIgnoreCase( rbItem.getAttributeType() ) )
+                    {
+                        EntryAttribute attr = entry.get( rbItem.getValuesIn() );
+                        
+                        // TODO Fix DIRSEVER-832
+                        if ( ( attr == null ) || !attr.contains( attrValue ) )
+                        {
+                            return true;
+                        }
+                    }
+                }
+            }
+        }
+
+        return false;
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/authz/support/package-info.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/authz/support/package-info.java
new file mode 100644
index 0000000..efd05ff
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/authz/support/package-info.java
@@ -0,0 +1,32 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.
+ *
+ */
+
+/**
+ * <pre>
+ * <p>
+ * ACDF (Access Control Decision Function) and its support classes.
+ * </p>
+ * </pre>
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+
+package org.apache.directory.server.core.authz.support;
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/changelog/ChangeLog.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/changelog/ChangeLog.java
new file mode 100755
index 0000000..ab6c278
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/changelog/ChangeLog.java
@@ -0,0 +1,182 @@
+/*
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *   or more contributor license agreements.  See the NOTICE file
+ *   distributed with this work for additional information
+ *   regarding copyright ownership.  The ASF licenses this file
+ *   to you 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.
+ *
+ */
+package org.apache.directory.server.core.changelog;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.authn.LdapPrincipal;
+import org.apache.directory.shared.ldap.ldif.LdifEntry;
+
+import javax.naming.NamingException;
+
+
+/**
+ * A facade for the change log subsystem.  It exposes the functionality
+ * needed to interact with the facility to query for changes, take snapshots,
+ * and revert the server to an earlier revision.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public interface ChangeLog
+{
+    /**
+     * Checks whether or not the change log has been enabled to track changes.
+     *
+     * @return true if the change log is tracking changes, false otherwise
+     */
+    boolean isEnabled();
+
+
+    void setEnabled( boolean enabled );
+    
+
+    ChangeLogStore getChangeLogStore();
+
+
+    void setChangeLogStore( ChangeLogStore store );
+
+
+    /**
+     * Gets the current revision for the server.
+     *
+     * @return the current revision
+     * @throws NamingException if there is a problem accessing this information
+     */
+    long getCurrentRevision() throws NamingException;
+
+
+    /**
+     * Records a change as a forward LDIF, a reverse change to revert the change and
+     * the authorized principal triggering the revertable change event.
+     *
+     * @param principal the authorized LDAP principal triggering the change
+     * @param forward LDIF of the change going to the next state
+     * @param reverse LDIF (anti-operation): the change required to revert this change
+     * @return the new revision reached after having applied the forward LDIF
+     * @throws NamingException if there are problems logging the change
+     */
+    long log( LdapPrincipal principal, LdifEntry forward, LdifEntry reverse ) throws NamingException;
+
+
+    /**
+     * Returns whether or not this ChangeLogService supports searching for changes.
+     *
+     * @return true if log searching is supported, false otherwise
+     */
+    boolean isLogSearchSupported();
+
+
+    /**
+     * Returns whether or not this ChangeLogService supports searching for snapshot tags.
+     *
+     * @return true if snapshot tag searching is supported, false otherwise
+     */
+    boolean isTagSearchSupported();
+
+
+    /**
+     * Returns whether or not this ChangeLogService stores snapshot tags.
+     *
+     * @return true if this ChangeLogService supports tag storage, false otherwise
+     */
+    boolean isTagStorageSupported();
+
+
+    /**
+     * Gets the change log query engine which would be used to ask questions
+     * about what changed, when, how and by whom.  It may not be supported by
+     * all implementations.  Some implementations may simply log changes without
+     * direct access to those changes from within the server.
+     *
+     * @return the change log query engine
+     * @throws UnsupportedOperationException if the change log is not searchable
+     */
+    ChangeLogSearchEngine getChangeLogSearchEngine();
+
+
+    /**
+     * Gets the tag search engine used to query the snapshots taken.  If this ChangeLogService
+     * does not support a taggable and searchable store then an UnsupportedOperationException
+     * will result.
+     *
+     * @return the snapshot query engine for this store
+     * @throws UnsupportedOperationException if the tag searching is not supported
+     */
+    TagSearchEngine getTagSearchEngine();
+
+
+    /**
+     * Creates a tag for a snapshot of the server in a specific state at a revision.
+     * If the ChangeLog has a TaggableChangeLogStore then the tag is stored.  If it
+     * does not then it's up to callers to track this tag since it will not be stored
+     * by this service.
+     *
+     * @param revision the revision to tag the snapshot
+     * @return the Tag associated with the revision
+     * @throws NamingException if there is a problem taking a tag
+     * @throws IllegalArgumentException if the revision is out of range (less than 0
+     * and greater than the current revision)
+     */
+    Tag tag( long revision ) throws NamingException;
+
+
+    /**
+     * Creates a tag for a snapshot of the server in a specific state at a revision.
+     * If the ChangeLog has a TaggableChangeLogStore then the tag is stored.  If it
+     * does not then it's up to callers to track this tag since it will not be stored
+     * by this service.
+     *
+     * @param revision the revision to tag the snapshot
+     * @param description some information about what the snapshot tag represents
+     * @return the Tag associated with the revision
+     * @throws NamingException if there is a problem taking a tag
+     * @throws IllegalArgumentException if the revision is out of range (less than 0
+     * and greater than the current revision)
+     */
+    Tag tag( long revision, String description ) throws NamingException;
+
+
+    /**
+     * Creates a snapshot of the server at the current revision.
+     *
+     * @param description some information about what the snapshot tag represents
+     * @return the revision at which the tag is created
+     * @throws NamingException if there is a problem taking a tag
+     */
+    Tag tag( String description ) throws NamingException;
+
+
+    /**
+     * Creates a snapshot of the server at the current revision.
+     *
+     * @return the revision at which the tag is created
+     * @throws NamingException if there is a problem taking a tag
+     */
+    Tag tag() throws NamingException;
+
+    Tag getLatest() throws NamingException;
+
+    void init( DirectoryService service ) throws NamingException;
+
+    void sync() throws NamingException;
+
+    void destroy() throws NamingException;
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/changelog/ChangeLogEvent.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/changelog/ChangeLogEvent.java
new file mode 100755
index 0000000..87082f4
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/changelog/ChangeLogEvent.java
@@ -0,0 +1,109 @@
+/*
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *   or more contributor license agreements.  See the NOTICE file
+ *   distributed with this work for additional information
+ *   regarding copyright ownership.  The ASF licenses this file
+ *   to you 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.
+ *
+ */
+package org.apache.directory.server.core.changelog;
+
+
+import java.io.Serializable;
+
+import org.apache.directory.server.core.authn.LdapPrincipal;
+import org.apache.directory.shared.ldap.ldif.LdifEntry;
+
+
+/**
+ * A loggable directory change event.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ChangeLogEvent implements Serializable
+{
+    private static final long serialVersionUID = 1L;
+    private final String zuluTime;
+    private final long revision;
+    private final LdifEntry forwardLdif;
+    private final LdifEntry reverseLdif;
+    private final LdapPrincipal committer;
+    
+    
+    /**
+     * Creates a new instance of ChangeLogEvent.
+     *
+     * @param revision the revision number for the change
+     * @param zuluTime the timestamp for when the change occurred in generalizedTime format
+     */
+    public ChangeLogEvent( long revision, String zuluTime, LdapPrincipal committer,
+                           LdifEntry forwardLdif, LdifEntry reverseLdif )
+    {
+        this.zuluTime = zuluTime;
+        this.revision = revision;
+        this.forwardLdif = forwardLdif;
+        this.reverseLdif = reverseLdif;
+        this.committer = committer;
+    }
+
+
+    /**
+     * @return the forwardLdif
+     */
+    public LdifEntry getForwardLdif()
+    {
+        return forwardLdif;
+    }
+
+
+    /**
+     * @return the reverseLdif
+     */
+    public LdifEntry getReverseLdif()
+    {
+        return reverseLdif;
+    }
+
+
+    /**
+     * @return the committer
+     */
+    public LdapPrincipal getCommitterPrincipal()
+    {
+        return committer;
+    }
+
+
+    /**
+     * Gets the revision of this event.
+     *
+     * @return the revision
+     */
+    public long getRevision()
+    {
+        return revision;
+    }
+
+
+    /**
+     * Gets the generalizedTime when this event occured.
+     *
+     * @return the zuluTime when this event occured
+     */
+    public String getZuluTime()
+    {
+        return zuluTime;
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/changelog/ChangeLogInterceptor.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/changelog/ChangeLogInterceptor.java
new file mode 100755
index 0000000..d176c8c
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/changelog/ChangeLogInterceptor.java
@@ -0,0 +1,307 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+package org.apache.directory.server.core.changelog;
+
+
+import java.util.Set;
+
+import org.apache.directory.server.constants.ApacheSchemaConstants;
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerEntryUtils;
+import org.apache.directory.server.core.interceptor.BaseInterceptor;
+import org.apache.directory.server.core.interceptor.NextInterceptor;
+import org.apache.directory.server.core.interceptor.context.AddOperationContext;
+import org.apache.directory.server.core.interceptor.context.DeleteOperationContext;
+import org.apache.directory.server.core.interceptor.context.LookupOperationContext;
+import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
+import org.apache.directory.server.core.interceptor.context.MoveAndRenameOperationContext;
+import org.apache.directory.server.core.interceptor.context.MoveOperationContext;
+import org.apache.directory.server.core.interceptor.context.OperationContext;
+import org.apache.directory.server.core.interceptor.context.RenameOperationContext;
+import org.apache.directory.server.core.invocation.Invocation;
+import org.apache.directory.server.core.invocation.InvocationStack;
+import org.apache.directory.server.core.partition.PartitionNexusProxy;
+import org.apache.directory.server.core.schema.SchemaService;
+import org.apache.directory.shared.ldap.entry.Modification;
+import org.apache.directory.shared.ldap.ldif.ChangeType;
+import org.apache.directory.shared.ldap.ldif.LdifEntry;
+import org.apache.directory.shared.ldap.ldif.LdifUtils;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.name.Rdn;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.naming.NamingException;
+
+
+/**
+ * An interceptor which intercepts write operations to the directory and
+ * logs them with the server's ChangeLog service.
+ */
+public class ChangeLogInterceptor extends BaseInterceptor
+{
+    /** for debugging */
+    private static final Logger LOG = LoggerFactory.getLogger( ChangeLogInterceptor.class );
+    
+    /** used to ignore modify operations to tombstone entries */
+    private AttributeType entryDeleted;
+    
+    /** the changelog service to log changes to */
+    private ChangeLog changeLog;
+    
+    /** we need the schema service to deal with special conditions */
+    private SchemaService schemaService;
+
+    // -----------------------------------------------------------------------
+    // Overridden init() and destroy() methods
+    // -----------------------------------------------------------------------
+
+
+    public void init( DirectoryService directoryService ) throws NamingException
+    {
+        super.init( directoryService );
+
+        changeLog = directoryService.getChangeLog();
+        schemaService = directoryService.getSchemaService();
+        entryDeleted = directoryService.getRegistries().getAttributeTypeRegistry()
+                .lookup( ApacheSchemaConstants.ENTRY_DELETED_OID );
+    }
+
+
+    // -----------------------------------------------------------------------
+    // Overridden (only change inducing) intercepted methods
+    // -----------------------------------------------------------------------
+
+    public void add( NextInterceptor next, AddOperationContext opContext ) throws NamingException
+    {
+        next.add( opContext );
+
+        if ( ! changeLog.isEnabled() || opContext.isCollateralOperation() )
+        {
+            return;
+        }
+
+        LdifEntry forward = new LdifEntry();
+        forward.setChangeType( ChangeType.Add );
+        forward.setDn( opContext.getDn().getUpName() );
+        
+        ServerEntry addEntry = opContext.getEntry();
+
+        Set<AttributeType> list = addEntry.getAttributeTypes();
+        
+        for ( AttributeType attributeType:list )
+        {
+            forward.addAttribute( ServerEntryUtils.toAttributeImpl( addEntry.get( attributeType ) ) );
+        }
+        
+        LdifEntry reverse = LdifUtils.reverseAdd( opContext.getDn() );
+        changeLog.log( getPrincipal(), forward, reverse );
+    }
+
+
+    /**
+     * The delete operation has to be stored with a way to restore the deleted element.
+     * There is no way to do that but reading the entry and dump it into the LOG.
+     */
+    public void delete( NextInterceptor next, DeleteOperationContext opContext ) throws NamingException
+    {
+        // @todo make sure we're not putting in operational attributes that cannot be user modified
+        // must save the entry if change log is enabled
+        ServerEntry serverEntry = null;
+
+        if ( changeLog.isEnabled() && ! opContext.isCollateralOperation() )
+        {
+            serverEntry = getAttributes( opContext );
+        }
+
+        next.delete( opContext );
+
+        if ( ! changeLog.isEnabled() || opContext.isCollateralOperation() )
+        {
+            return;
+        }
+
+        LdifEntry forward = new LdifEntry();
+        forward.setChangeType( ChangeType.Delete );
+        forward.setDn( opContext.getDn().getUpName() );
+        LdifEntry reverse = LdifUtils.reverseDel( opContext.getDn(), ServerEntryUtils.toAttributesImpl( serverEntry ) );
+        changeLog.log( getPrincipal(), forward, reverse );
+    }
+
+
+    /**
+     * Gets attributes required for modifications.
+     *
+     * @param dn the dn of the entry to get
+     * @return the entry's attributes (may be immutable if the schema subentry)
+     * @throws NamingException on error accessing the entry's attributes
+     */
+    private ServerEntry getAttributes( OperationContext opContext ) throws NamingException
+    {
+        LdapDN dn = opContext.getDn();
+        ServerEntry serverEntry;
+
+        // @todo make sure we're not putting in operational attributes that cannot be user modified
+        Invocation invocation = InvocationStack.getInstance().peek();
+        PartitionNexusProxy proxy = invocation.getProxy();
+
+        if ( schemaService.isSchemaSubentry( dn.toNormName() ) )
+        {
+            return schemaService.getSubschemaEntryCloned();
+        }
+        else
+        {
+            serverEntry = proxy.lookup( new LookupOperationContext( opContext.getRegistries(), dn ), PartitionNexusProxy.LOOKUP_BYPASS );
+        }
+
+        return serverEntry;
+    }
+
+
+    public void modify( NextInterceptor next, ModifyOperationContext opContext ) throws NamingException
+    {
+        ServerEntry serverEntry = null;
+        Modification modification = ServerEntryUtils.getModificationItem( opContext.getModItems(), entryDeleted );
+        boolean isDelete = ( modification != null );
+
+        if ( ! isDelete && ( changeLog.isEnabled() && ! opContext.isCollateralOperation() ) )
+        {
+            // @todo make sure we're not putting in operational attributes that cannot be user modified
+            serverEntry = getAttributes( opContext );
+        }
+
+        next.modify( opContext );
+
+        // @TODO: needs big consideration!!!
+        // NOTE: perhaps we need to log this as a system operation that cannot and should not be reapplied?
+        if ( isDelete || ! changeLog.isEnabled() || opContext.isCollateralOperation() )
+        {
+            if ( isDelete )
+            {
+                LOG.debug( "Bypassing changelog on modify of entryDeleted attribute." );
+            }
+            return;
+        }
+
+        LdifEntry forward = new LdifEntry();
+        forward.setChangeType( ChangeType.Modify );
+        forward.setDn( opContext.getDn().getUpName() );
+        
+        for ( Modification modItem : opContext.getModItems() )
+        {
+            forward.addModificationItem( ServerEntryUtils.toModificationItemImpl( modItem ) );
+        }
+
+        LdifEntry reverse = LdifUtils.reverseModify( 
+            opContext.getDn(), 
+            ServerEntryUtils.toModificationItemImpl( opContext.getModItems() ), 
+            ServerEntryUtils.toAttributesImpl( serverEntry ) );
+        
+        changeLog.log( getPrincipal(), forward, reverse );
+    }
+
+
+    // -----------------------------------------------------------------------
+    // Though part left as an exercise (Not Any More!)
+    // -----------------------------------------------------------------------
+
+
+    public void rename ( NextInterceptor next, RenameOperationContext renameContext ) throws NamingException
+    {
+        ServerEntry serverEntry = null;
+        
+        if ( changeLog.isEnabled() && ! renameContext.isCollateralOperation() )
+        {
+            // @todo make sure we're not putting in operational attributes that cannot be user modified
+            serverEntry = getAttributes( renameContext );
+        }
+
+        next.rename( renameContext );
+
+        if ( ! changeLog.isEnabled() || renameContext.isCollateralOperation() )
+        {
+            return;
+        }
+
+        LdifEntry forward = new LdifEntry();
+        forward.setChangeType( ChangeType.ModRdn );
+        forward.setDn( renameContext.getDn().getUpName() );
+        forward.setDeleteOldRdn( renameContext.getDelOldDn() );
+
+        LdifEntry reverse = LdifUtils.reverseModifyRdn( ServerEntryUtils.toAttributesImpl( serverEntry ), null, renameContext.getDn(),
+                new Rdn( renameContext.getNewRdn() ) );
+        
+        changeLog.log( getPrincipal(), forward, reverse );
+    }
+
+
+    public void moveAndRename( NextInterceptor next, MoveAndRenameOperationContext opCtx )
+        throws NamingException
+    {
+        ServerEntry serverEntry = null;
+        
+        if ( changeLog.isEnabled() && ! opCtx.isCollateralOperation() )
+        {
+            // @todo make sure we're not putting in operational attributes that cannot be user modified
+            Invocation invocation = InvocationStack.getInstance().peek();
+            PartitionNexusProxy proxy = invocation.getProxy();
+            serverEntry = proxy.lookup( new LookupOperationContext( opCtx.getRegistries(), opCtx.getDn() ),
+                    PartitionNexusProxy.LOOKUP_BYPASS );
+        }
+
+        next.moveAndRename( opCtx );
+
+        if ( ! changeLog.isEnabled() || opCtx.isCollateralOperation() )
+        {
+            return;
+        }
+
+        LdifEntry forward = new LdifEntry();
+        forward.setChangeType( ChangeType.ModDn );
+        forward.setDn( opCtx.getDn().getUpName() );
+        forward.setDeleteOldRdn( opCtx.getDelOldDn() );
+        forward.setNewRdn( opCtx.getNewRdn().getUpName() );
+        forward.setNewSuperior( opCtx.getParent().getUpName() );
+
+        LdifEntry reverse = LdifUtils.reverseModifyRdn( ServerEntryUtils.toAttributesImpl( serverEntry ), opCtx.getParent(), opCtx.getDn(),
+                new Rdn( opCtx.getNewRdn() ) );
+        changeLog.log( getPrincipal(), forward, reverse );
+    }
+
+
+    public void move ( NextInterceptor next, MoveOperationContext opCtx ) throws NamingException
+    {
+        next.move( opCtx );
+
+        if ( ! changeLog.isEnabled() || opCtx.isCollateralOperation() )
+        {
+            return;
+        }
+
+        LdifEntry forward = new LdifEntry();
+        forward.setChangeType( ChangeType.ModDn );
+        forward.setDn( opCtx.getDn().getUpName() );
+        forward.setNewSuperior( opCtx.getParent().getUpName() );
+
+        LdifEntry reverse = LdifUtils.reverseModifyDn( opCtx.getParent(), opCtx.getDn() );
+        changeLog.log( getPrincipal(), forward, reverse );
+    }
+}
\ No newline at end of file
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/changelog/ChangeLogSearchEngine.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/changelog/ChangeLogSearchEngine.java
new file mode 100755
index 0000000..00947a7
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/changelog/ChangeLogSearchEngine.java
@@ -0,0 +1,243 @@
+/*
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *   or more contributor license agreements.  See the NOTICE file
+ *   distributed with this work for additional information
+ *   regarding copyright ownership.  The ASF licenses this file
+ *   to you 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.
+ *
+ */
+package org.apache.directory.server.core.changelog;
+
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+
+import org.apache.directory.server.core.authn.LdapPrincipal;
+import org.apache.directory.shared.ldap.filter.ExprNode;
+import org.apache.directory.shared.ldap.ldif.ChangeType;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.schema.ObjectClass;
+
+
+/**
+ * A custom search engine designed for optimized searching across ChangeLogEvents
+ * within the ChangeLogStore.  The following lookup and search operations are 
+ * provided:
+ * 
+ * <ul>
+ *   <li>lookup change by revision</li>
+ *   <li>lookup change by date</li>
+ *   <li>find all changes</li>
+ *   <li>find all changes before or after a revision</li>
+ *   <li>find all changes in a revision range</li>
+ *   <li>find changes by LDAP namespace scope on DN</li>
+ *   <li>find changes by DN</li>
+ *   <li>find changes by principal</li>
+ *   <li>find changes by change type</li>
+ *   <li>find changes by attribute</li>
+ *   <li>find changes using a restricted LDAP filter on all of the above factors</li>
+ * </ul>
+ * 
+ * Note change lookups by date can be conducted by first looking up a revision 
+ * using a generalizedTime descriptor to find a revision.  Then these revisions 
+ * can be plugged into the revision based find and lookup methods.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public interface ChangeLogSearchEngine
+{
+    /**
+     * Looks up the revision in effect at some time specified by a generalized 
+     * time descriptor.
+     *
+     * @param generalizedTime the generalized time descriptor to find the effective revision for
+     * @return the revision that was in effect at a certain time
+     * @throws NamingException if there are failures accessing the store
+     */
+    long lookup( String generalizedTime ) throws NamingException;
+
+
+    /**
+     * Looks up the ChangeLogEvent for a revision.
+     *
+     * @param revision to get a ChangeLogEvent for
+     * @return the ChangeLogEvent associated with the revision
+     * @throws NamingException if there are failures accessing the store
+     * @throws IllegalArgumentException if the revision is out of range (less than 0
+     * and greater than the current revision)
+     */
+    ChangeLogEvent lookup( long revision ) throws NamingException;
+
+
+    /**
+     * Finds all the ChangeLogEvents within the system since revision 0.
+     *
+     * This method should exhibit isolation characteristics: meaning if the request is
+     * initiated at revision 100 then any subsequent log entries increasing the revision
+     * should not be seen.
+     *
+     * @param order the order in which to return ChangeLogEvents (ordered by revision number)
+     * @return an enumeration of all the ChangeLogEvents
+     * @throws NamingException if there are failures accessing the store
+     */
+    NamingEnumeration<ChangeLogEvent> find( RevisionOrder order ) throws NamingException;
+
+
+    /**
+     * Finds the ChangeLogEvents that occurred before a revision inclusive.
+     *
+     * @param revision the revision number to get the ChangeLogEvents before
+     * @param order the order in which to return ChangeLogEvents (ordered by revision number)
+     * @return an enumeration of all the ChangeLogEvents before and including some revision
+     * @throws NamingException if there are failures accessing the store
+     * @throws IllegalArgumentException if the revision is out of range (less than 0
+     * and greater than the current revision)
+     */
+    NamingEnumeration<ChangeLogEvent> findBefore( long revision, RevisionOrder order ) throws NamingException;
+
+
+    /**
+     * Finds the ChangeLogEvents that occurred after a revision inclusive.
+     *
+     * This method should exhibit isolation characteristics: meaning if the request is
+     * initiated at revision 100 then any subsequent log entries increasing the revision
+     * should not be seen.
+     *
+     * @param revision the revision number to get the ChangeLogEvents after
+     * @param order the order in which to return ChangeLogEvents (ordered by revision number)
+     * @return an enumeration of all the ChangeLogEvents after and including the revision
+     * @throws NamingException if there are failures accessing the store
+     * @throws IllegalArgumentException if the revision is out of range (less than 0
+     * and greater than the current revision)
+     */
+    NamingEnumeration<ChangeLogEvent> findAfter( long revision, RevisionOrder order ) throws NamingException;
+
+
+    /**
+     * Finds the ChangeLogEvents that occurred between a revision range inclusive.
+     *
+     * @param startRevision the revision number to start getting the ChangeLogEvents above
+     * @param endRevision the revision number to start getting the ChangeLogEvents below
+     * @param order the order in which to return ChangeLogEvents (ordered by revision number)
+     * @return an enumeration of all the ChangeLogEvents within some revision range inclusive
+     * @throws NamingException if there are failures accessing the store
+     * @throws IllegalArgumentException if the start and end revisions are out of range
+     * (less than 0 and greater than the current revision), or if startRevision > endRevision
+     */
+    NamingEnumeration<ChangeLogEvent> find( long startRevision, long endRevision, RevisionOrder order )
+        throws NamingException;
+
+    
+    /**
+     * Finds all the ChangeLogEvents on an entry.
+     *
+     * @param dn the normalized DN of the entry to get ChangeLogEvents for
+     * @param order the order in which to return ChangeLogEvents (ordered by revision number)
+     * @return the set of changes that occurred on an entry
+     * @throws NamingException if there are failures accessing the store
+     */
+    NamingEnumeration<ChangeLogEvent> find( LdapDN dn, RevisionOrder order ) throws NamingException;
+    
+    
+    /**
+     * Finds all the ChangeLogEvents on an entry base and/or it's children/descendants.
+     *
+     * @param base the normalized DN of the entry base to get ChangeLogEvents for
+     * @param scope the scope of the search under the base similar to LDAP search scope
+     * @param order the order in which to return ChangeLogEvents (ordered by revision number)
+     * @return the set of changes that occurred on an entry and/or it's descendants depending on the scope
+     * @throws NamingException if there are failures accessing the store
+     */
+    NamingEnumeration<ChangeLogEvent> find( LdapDN base, Scope scope, RevisionOrder order ) throws NamingException;
+    
+
+    /**
+     * Finds all the ChangeLogEvents triggered by a principal in the system.
+     *
+     * @param principal the LDAP principal who triggered the events
+     * @param order the order in which to return ChangeLogEvents (ordered by revision number)
+     * @return the set of changes that were triggered by a specific LDAP user
+     * @throws NamingException if there are failures accessing the store
+     */
+    NamingEnumeration<ChangeLogEvent> find( LdapPrincipal principal, RevisionOrder order ) throws NamingException;
+    
+    
+    /**
+     * Finds all the ChangeLogEvents of a particular change type.
+     * 
+     * @param changeType the change type of the ChangeLogEvents to search for
+     * @param order the order in which to return ChangeLogEvents (ordered by revision number)
+     * @return the set of ChangeLogEvents of a particular ChangeType
+     * @throws NamingException if there are failures accessing the store
+     */
+    NamingEnumeration<ChangeLogEvent> find( ChangeType changeType, RevisionOrder order ) throws NamingException;
+    
+    
+    /**
+     * Finds all the ChangeLogEvents altering a particular attributeType.
+     * 
+     * @param attributeType the attributeType definition for the changed attribute to search changes for
+     * @param order the order in which to return ChangeLogEvents (ordered by revision number)
+     * @return the set of ChangeLogEvents on a particular attributeType
+     * @throws NamingException if there are failures accessing the store
+     */
+    NamingEnumeration<ChangeLogEvent> find( AttributeType attributeType, RevisionOrder order ) throws NamingException;
+    
+
+    /**
+     * Finds all the ChangeLogEvents altering a particular objectClass.
+     * 
+     * @param objectClass the objectClass definition for the entries to search changes for
+     * @param order the order in which to return ChangeLogEvents (ordered by revision number)
+     * @return the set of ChangeLogEvents on a particular attributeType
+     * @throws NamingException if there are failures accessing the store
+     */
+    NamingEnumeration<ChangeLogEvent> find( ObjectClass objectClass, RevisionOrder order ) throws NamingException;
+    
+    
+    /**
+     * Finds all the ChangeLogEvents matched by the filter expression tree parameter.
+     * 
+     * The following attributes can be used in the constrained LDAP filter expression 
+     * tree.  The expression must be normalized and can contain only ATA pairs with the 
+     * following set of attributes:
+     * 
+     * <ul>
+     *   <li>ndn: normalized distinguishedName syntax (defaults to matching a string)</li>
+     *   <li>date: generalizedTime syntax</li>
+     *   <li>revision: integer syntax</li>
+     *   <li>attributeType: numeric OID</li>
+     *   <li>objectClass: numeric OID</li>
+     *   <li>changeType: new changeType syntax</li>
+     *   <li>principal: normalized distinguishedName syntax (defaults to matching a string)</li>
+     * </ul>
+     * 
+     * The following are the only kinds of AVA node types allowed:
+     * 
+     * <ul>
+     *   <li>equality (=) </li>
+     *   <li>greaterThanEq (>=) </li>
+     *   <li>lessThanEq (<=) </li>
+     *   <li>scope (specialized) </li>
+     * </ul>
+     * 
+     * @param filter the filter to use for finding the change
+     * @param order the order in which to return ChangeLogEvents (ordered by revision number)
+     * @return the set of ChangeLogEvents on entries of a particular objectClass
+     * @throws NamingException if there are failures accessing the store
+     */
+    NamingEnumeration<ChangeLogEvent> find( ExprNode filter, RevisionOrder order ) throws NamingException;
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/changelog/ChangeLogStore.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/changelog/ChangeLogStore.java
new file mode 100755
index 0000000..aeae144
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/changelog/ChangeLogStore.java
@@ -0,0 +1,136 @@
+/*
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *   or more contributor license agreements.  See the NOTICE file
+ *   distributed with this work for additional information
+ *   regarding copyright ownership.  The ASF licenses this file
+ *   to you 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.
+ *
+ */
+package org.apache.directory.server.core.changelog;
+
+
+import org.apache.directory.server.core.authn.LdapPrincipal;
+import org.apache.directory.server.core.cursor.Cursor;
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.shared.ldap.ldif.LdifEntry;
+
+import javax.naming.NamingException;
+
+
+/**
+ * A store for change events on the directory which exposes methods for 
+ * managing, querying and in general performing legal operations on the log.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public interface ChangeLogStore 
+{
+    void init( DirectoryService service ) throws NamingException;
+
+
+    void sync() throws NamingException;
+
+
+    void destroy() throws NamingException;
+
+
+    /**
+     * Gets the current revision of the server (a.k.a. the HEAD revision).
+     *
+     * @return the current revision of the server
+     */
+    long getCurrentRevision();
+
+
+    /**
+     * Records a change as a forward LDIF, a reverse change to revert the change and
+     * the authorized principal triggering the revertable change event.
+     *
+     * @param principal the authorized LDAP principal triggering the change
+     * @param forward LDIF of the change going to the next state
+     * @param reverse LDIF (anti-operation): the change required to revert this change
+     * @return the new revision reached after having applied the forward LDIF
+     * @throws NamingException if there are problems logging the change
+     */
+    long log( LdapPrincipal principal, LdifEntry forward, LdifEntry reverse ) throws NamingException;
+
+    
+    /**
+     * Looks up the ChangeLogEvent for a revision.
+     *
+     * @param revision to get a ChangeLogEvent for
+     * @return the ChangeLogEvent associated with the revision
+     * @throws NamingException if there are failures accessing the store
+     * @throws IllegalArgumentException if the revision is out of range (less than 0
+     * and greater than the current revision)
+     */
+    ChangeLogEvent lookup( long revision ) throws NamingException;
+
+
+    /**
+     * Gets a Cursor over all the ChangeLogEvents within the system since
+     * revision 0.
+     *
+     * This method should exhibit isolation characteristics: meaning if the
+     * request is initiated at revision 100, then any subsequent log entries
+     * increasing the revision should not be seen.
+     *
+     * @return a Cursor over all the ChangeLogEvents
+     * @throws NamingException if there are failures accessing the store
+     */
+    Cursor<ChangeLogEvent> find() throws NamingException;
+
+
+    /**
+     * Gets a Cursor over the ChangeLogEvents that occurred before a revision
+     * exclusive.
+     *
+     * @param revision the revision number to get the ChangeLogEvents before
+     * @return a Cursor over the ChangeLogEvents before a revision
+     * @throws NamingException if there are failures accessing the store
+     * @throws IllegalArgumentException if the revision is out of range (less than 0
+     * and greater than the current revision)
+     */
+    Cursor<ChangeLogEvent> findBefore( long revision ) throws NamingException;
+
+
+    /**
+     * Finds the ChangeLogEvents that occurred after a revision exclusive.
+     *
+     * This method should exhibit isolation characteristics: meaning if the request is
+     * initiated at revision 100 then any subsequent log entries increasing the revision
+     * should not be seen.
+     *
+     * @param revision the revision number to get the ChangeLogEvents after
+     * @return a Cursor of all the ChangeLogEvents after and including the revision
+     * @throws NamingException if there are failures accessing the store
+     * @throws IllegalArgumentException if the revision is out of range (less than 0
+     * and greater than the current revision)
+     */
+    Cursor<ChangeLogEvent> findAfter( long revision ) throws NamingException;
+
+
+    /**
+     * Finds the ChangeLogEvents that occurred between a revision range inclusive.
+     *
+     * @param startRevision the revision number to start getting the ChangeLogEvents above
+     * @param endRevision the revision number to start getting the ChangeLogEvents below
+     * @return an enumeration of all the ChangeLogEvents within some revision range inclusive
+     * @throws NamingException if there are failures accessing the store
+     * @throws IllegalArgumentException if the start and end revisions are out of range
+     * (less than 0 and greater than the current revision), or if startRevision > endRevision
+     */
+    Cursor<ChangeLogEvent> find( long startRevision, long endRevision ) throws NamingException;
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/changelog/DefaultChangeLog.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/changelog/DefaultChangeLog.java
new file mode 100644
index 0000000..9b7fe07
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/changelog/DefaultChangeLog.java
@@ -0,0 +1,207 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements.  See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership.  The ASF licenses this file

+ * to you 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.

+ */

+package org.apache.directory.server.core.changelog;

+

+

+import org.apache.directory.server.core.authn.LdapPrincipal;

+import org.apache.directory.server.core.DirectoryService;

+import org.apache.directory.shared.ldap.ldif.LdifEntry;

+

+import javax.naming.NamingException;

+

+

+/**

+ * The default ChangeLog service implementation.

+ *

+ * @org.apache.xbean.XBean

+ *

+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>

+ * @version $Rev$, $Date$

+ */

+public class DefaultChangeLog implements ChangeLog

+{

+    private boolean enabled;

+    private Tag latest;

+    private ChangeLogStore store = new MemoryChangeLogStore();

+

+

+    public ChangeLogStore getChangeLogStore()

+    {

+        return store;

+    }

+

+

+    public void setChangeLogStore( ChangeLogStore store )

+    {

+        this.store = store;

+    }

+

+

+    public long getCurrentRevision() throws NamingException

+    {

+        return store.getCurrentRevision();

+    }

+

+

+    public long log( LdapPrincipal principal, LdifEntry forward, LdifEntry reverse ) throws NamingException

+    {

+        if ( ! enabled )

+        {

+            throw new IllegalStateException( "The ChangeLog has not been enabled." );

+        }

+

+        return store.log( principal, forward, reverse );

+    }

+

+

+    public boolean isLogSearchSupported()

+    {

+        return store instanceof SearchableChangeLogStore;

+    }

+

+

+    public boolean isTagSearchSupported()

+    {

+        return store instanceof TaggableSearchableChangeLogStore;

+    }

+

+

+    public boolean isTagStorageSupported()

+    {

+        return store instanceof TaggableChangeLogStore;

+    }

+

+

+    public ChangeLogSearchEngine getChangeLogSearchEngine()

+    {

+        if ( isLogSearchSupported() )

+        {

+            return ( ( SearchableChangeLogStore ) store ).getChangeLogSearchEngine();

+        }

+

+        throw new UnsupportedOperationException(

+                "The underlying changelog store does not support searching through it's logs" );

+    }

+

+

+    public TagSearchEngine getTagSearchEngine()

+    {

+        if ( isTagSearchSupported() )

+        {

+            return ( ( TaggableSearchableChangeLogStore ) store ).getTagSearchEngine();

+        }

+

+        throw new UnsupportedOperationException(

+                "The underlying changelog store does not support searching through it's tags" );

+    }

+

+

+    public Tag tag( long revision, String description ) throws NamingException

+    {

+        if ( revision < 0 )

+        {

+            throw new IllegalArgumentException( "revision must be greater than or equal to 0" );

+        }

+

+        if ( revision > store.getCurrentRevision() )

+        {

+            throw new IllegalArgumentException( "revision must be less than or equal to the current revision" );

+        }

+

+        if ( store instanceof TaggableChangeLogStore )

+        {

+            return latest = ( ( TaggableChangeLogStore ) store ).tag( revision );

+        }

+

+        return latest = new Tag( revision, description );

+    }

+

+

+    public Tag tag( long revision ) throws NamingException

+    {

+        return tag( revision, null );

+    }

+

+

+    public Tag tag( String description ) throws NamingException

+    {

+        return tag( store.getCurrentRevision(), description );

+    }

+

+

+    public Tag tag() throws NamingException

+    {

+        return tag( store.getCurrentRevision(), null );

+    }

+

+

+    public void setEnabled( boolean enabled )

+    {

+        this.enabled = enabled;

+    }

+

+

+    public boolean isEnabled()

+    {

+        return enabled;

+    }

+

+

+    public Tag getLatest() throws NamingException

+    {

+        if ( latest != null )

+        {

+            return latest;

+        }

+        

+        if ( store instanceof TaggableChangeLogStore )

+        {

+            return latest = ( ( TaggableChangeLogStore ) store ).getLatest();

+        }

+

+        return null;

+    }

+

+

+    public void init( DirectoryService service ) throws NamingException

+    {

+        if ( enabled )

+        {

+            store.init( service );

+        }

+    }

+

+

+    public void sync() throws NamingException

+    {

+        if ( enabled )

+        {

+            store.sync();

+        }

+    }

+

+

+    public void destroy() throws NamingException

+    {

+        if ( enabled )

+        {

+            store.destroy();

+        }

+    }

+}

diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/changelog/MemoryChangeLogStore.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/changelog/MemoryChangeLogStore.java
new file mode 100644
index 0000000..b82434c
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/changelog/MemoryChangeLogStore.java
@@ -0,0 +1,482 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements.  See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership.  The ASF licenses this file

+ * to you 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.

+ */

+package org.apache.directory.server.core.changelog;

+

+import java.io.BufferedReader;

+import java.io.EOFException;

+import java.io.File;

+import java.io.FileInputStream;

+import java.io.FileOutputStream;

+import java.io.FileReader;

+import java.io.FileWriter;

+import java.io.IOException;

+import java.io.ObjectInputStream;

+import java.io.ObjectOutputStream;

+import java.io.PrintWriter;

+import java.util.ArrayList;

+import java.util.Collections;

+import java.util.HashMap;

+import java.util.List;

+import java.util.Map;

+import java.util.Properties;

+

+import org.apache.directory.server.core.DirectoryService;

+import org.apache.directory.server.core.authn.LdapPrincipal;

+import org.apache.directory.server.core.cursor.Cursor;

+import org.apache.directory.server.core.cursor.ListCursor;

+import org.apache.directory.shared.ldap.ldif.LdifEntry;

+import org.apache.directory.shared.ldap.util.DateUtils;

+

+import javax.naming.NamingException;

+

+

+/**

+ * A change log store that keeps it's information in memory.

+ *

+ * @org.apache.xbean.XBean

+ *

+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>

+ * @version $Rev$, $Date$

+ */

+public class MemoryChangeLogStore implements TaggableChangeLogStore

+{

+    private static final String REV_FILE = "revision";

+    private static final String TAG_FILE = "tags";

+    private static final String CHANGELOG_FILE = "changelog.dat";

+

+    private long currentRevision;

+    private Tag latest;

+    private final Map<Long,Tag> tags = new HashMap<Long,Tag>( 100 );

+    private final List<ChangeLogEvent> events = new ArrayList<ChangeLogEvent>();

+    private File workingDirectory;

+

+

+    public Tag tag( long revision ) throws NamingException

+    {

+        if ( tags.containsKey( revision ) )

+        {

+            return tags.get( revision );

+        }

+

+        latest = new Tag( revision, null );

+        tags.put( revision, latest );

+        return latest;

+    }

+

+

+    public Tag tag() throws NamingException

+    {

+        if ( latest != null && latest.getRevision() == currentRevision )

+        {

+            return latest;

+        }

+

+        latest = new Tag( currentRevision, null );

+        tags.put( currentRevision, latest );

+        return latest;

+    }

+

+

+    public Tag tag( String description ) throws NamingException

+    {

+        if ( latest != null && latest.getRevision() == currentRevision )

+        {

+            return latest;

+        }

+

+        latest = new Tag( currentRevision, description );

+        tags.put( currentRevision, latest );

+        return latest;

+    }

+

+

+    public void init( DirectoryService service ) throws NamingException

+    {

+        workingDirectory = service.getWorkingDirectory();

+        loadRevision();

+        loadTags();

+        loadChangeLog();

+    }

+

+

+    private void loadRevision() throws NamingException

+    {

+        File revFile = new File( workingDirectory, REV_FILE );

+        if ( revFile.exists() )

+        {

+            BufferedReader reader = null;

+            try

+            {

+                reader = new BufferedReader( new FileReader( revFile ) );

+                String line = reader.readLine();

+                currentRevision = Long.valueOf( line );

+            }

+            catch ( IOException e )

+            {

+                throw new NamingException( "Failed to open stream to read from revfile: " + revFile.getAbsolutePath() );

+            }

+            finally

+            {

+                if ( reader != null )

+                {

+                    //noinspection EmptyCatchBlock

+                    try

+                    {

+                        reader.close();

+                    }

+                    catch ( IOException e )

+                    {

+                    }

+                }

+            }

+        }

+    }

+

+

+    private void saveRevision() throws NamingException

+    {

+        File revFile = new File( workingDirectory, REV_FILE );

+        if ( revFile.exists() )

+        {

+            revFile.delete();

+        }

+

+        PrintWriter out = null;

+        try

+        {

+            out = new PrintWriter( new FileWriter( revFile ) );

+            out.println( currentRevision );

+            out.flush();

+        }

+        catch ( IOException e )

+        {

+            throw new NamingException( "Failed to write out revision file." );

+        }

+        finally

+        {

+            if ( out != null )

+            {

+                out.close();

+            }

+        }

+    }

+

+

+    private void saveTags() throws NamingException

+    {

+        File tagFile = new File( workingDirectory, TAG_FILE );

+        if ( tagFile.exists() )

+        {

+            tagFile.delete();

+        }

+

+        FileOutputStream out = null;

+        try

+        {

+            out = new FileOutputStream( tagFile );

+

+            Properties props = new Properties();

+            for ( Tag tag : tags.values() )

+            {

+                String key = String.valueOf( tag.getRevision() );

+                if ( tag.getDescription() == null )

+                {

+                    props.setProperty( key, "null" );

+                }

+                else

+                {

+                    props.setProperty( key, tag.getDescription() );

+                }

+            }

+

+            props.store( out, null );

+            out.flush();

+        }

+        catch ( IOException e )

+        {

+            throw new NamingException( "Failed to write out revision file." );

+        }

+        finally

+        {

+            if ( out != null )

+            {

+                //noinspection EmptyCatchBlock

+                try

+                {

+                    out.close();

+                }

+                catch ( IOException e )

+                {

+                }

+            }

+        }

+    }

+

+

+    private void loadTags() throws NamingException

+    {

+        File revFile = new File( workingDirectory, REV_FILE );

+        if ( revFile.exists() )

+        {

+            Properties props = new Properties();

+            FileInputStream in = null;

+            try

+            {

+                in = new FileInputStream( revFile );

+                props.load( in );

+                ArrayList<Long> revList = new ArrayList<Long>();

+                for ( Object key : props.keySet() )

+                {

+                    revList.add( Long.valueOf( ( String ) key ) );

+                }

+

+                Collections.sort( revList );

+                Tag tag = null;

+

+                // @todo need some serious syncrhoization here on tags

+                tags.clear();

+                for ( Long lkey : revList )

+                {

+                    String rev = String.valueOf( lkey );

+                    String desc = props.getProperty( rev );

+

+                    if ( desc != null && desc.equals( "null" ) )

+                    {

+                        tag = new Tag( lkey, null );

+                    }

+                    else

+                    {

+                        tag = new Tag( lkey, desc );

+                    }

+

+                    tags.put( lkey, tag );

+                }

+

+                latest = tag;

+            }

+            catch ( IOException e )

+            {

+                throw new NamingException( "Failed to open stream to read from revfile: " + revFile.getAbsolutePath() );

+            }

+            finally

+            {

+                if ( in != null )

+                {

+                    //noinspection EmptyCatchBlock

+                    try

+                    {

+                        in.close();

+                    }

+                    catch ( IOException e )

+                    {

+                    }

+                }

+            }

+        }

+    }

+

+

+    private void loadChangeLog() throws NamingException

+    {

+        File file = new File( workingDirectory, CHANGELOG_FILE );

+        if ( file.exists() )

+        {

+            ObjectInputStream in = null;

+

+            try

+            {

+                in = new ObjectInputStream( new FileInputStream( file ) );

+                ArrayList<ChangeLogEvent> changeLogEvents = new ArrayList<ChangeLogEvent>();

+

+                while ( true )

+                {

+                    try

+                    {

+                        ChangeLogEvent event = ( ChangeLogEvent ) in.readObject();

+                        changeLogEvents.add( event );

+                    }

+                    catch ( EOFException eofe )

+                    {

+                        break;

+                    }

+                }

+

+                // @todo man o man we need some synchronization later after getting this to work

+                this.events.clear();

+                this.events.addAll( changeLogEvents );

+            }

+            catch ( Exception e )

+            {

+                NamingException ne = new NamingException( "Failed to open stream to read from changelog file: "

+                        + file.getAbsolutePath() );

+                ne.setRootCause( e );

+                throw ne;

+            }

+            finally

+            {

+                if ( in != null )

+                {

+                    //noinspection EmptyCatchBlock

+                    try

+                    {

+                        in.close();

+                    }

+                    catch ( IOException e )

+                    {

+                    }

+                }

+            }

+        }

+    }

+

+

+    private void saveChangeLog() throws NamingException

+    {

+        File file = new File( workingDirectory, CHANGELOG_FILE );

+        if ( file.exists() )

+        {

+            file.delete();

+        }

+

+        try

+        {

+            file.createNewFile();

+        }

+        catch ( IOException e )

+        {

+            NamingException ne = new NamingException( "Failed to create new file for changelog: "

+                    + file.getAbsolutePath() );

+            ne.setRootCause( e );

+            throw ne;

+        }

+

+        ObjectOutputStream out = null;

+

+        try

+        {

+            out = new ObjectOutputStream( new FileOutputStream( file ) );

+

+            for ( ChangeLogEvent event : events )

+            {

+                out.writeObject( event );

+            }

+

+            out.flush();

+        }

+        catch ( Exception e )

+        {

+            NamingException ne = new NamingException( "Failed to open stream to write to changelog file: "

+                    + file.getAbsolutePath() );

+            ne.setRootCause( e );

+            throw ne;

+        }

+        finally

+        {

+            if ( out != null )

+            {

+                //noinspection EmptyCatchBlock

+                try

+                {

+                    out.close();

+                }

+                catch ( IOException e )

+                {

+                }

+            }

+        }

+    }

+

+

+    public void sync() throws NamingException

+    {

+        saveRevision();

+        saveTags();

+        saveChangeLog();

+    }

+

+

+    public void destroy() throws NamingException

+    {

+        saveRevision();

+        saveTags();

+        saveChangeLog();

+    }

+

+

+    public long getCurrentRevision()

+    {

+        return currentRevision;

+    }

+

+

+    public long log( LdapPrincipal principal, LdifEntry forward, LdifEntry reverse ) throws NamingException

+    {

+        currentRevision++;

+        ChangeLogEvent event = new ChangeLogEvent( currentRevision, DateUtils.getGeneralizedTime(), 

+                principal, forward, reverse );

+        events.add( event );

+        return currentRevision;

+    }

+

+

+    public ChangeLogEvent lookup( long revision ) throws NamingException

+    {

+        if ( revision < 0 )

+        {

+            throw new IllegalArgumentException( "revision must be greater than or equal to 0" );

+        }

+

+        if ( revision > getCurrentRevision() )

+        {

+            throw new IllegalArgumentException( "The revision must not be greater than the current revision" );

+        }

+

+        return events.get( ( int ) revision );

+    }

+

+

+    public Cursor<ChangeLogEvent> find() throws NamingException

+    {

+        return new ListCursor<ChangeLogEvent>( events );

+    }

+

+

+    public Cursor<ChangeLogEvent> findBefore( long revision ) throws NamingException

+    {

+        return new ListCursor<ChangeLogEvent>( events, ( int ) revision );

+    }

+

+

+    public Cursor<ChangeLogEvent> findAfter( long revision ) throws NamingException

+    {

+        return new ListCursor<ChangeLogEvent>( ( int ) revision, events );

+    }

+

+

+    public Cursor<ChangeLogEvent> find( long startRevision, long endRevision ) throws NamingException

+    {

+        return new ListCursor<ChangeLogEvent>( ( int ) startRevision, events, ( int ) ( endRevision + 1 ) );

+    }

+

+

+    public Tag getLatest()

+    {

+        return latest;

+    }

+}

diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/changelog/OriginalChangeLogInterceptor.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/changelog/OriginalChangeLogInterceptor.java
new file mode 100755
index 0000000..0cc468d
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/changelog/OriginalChangeLogInterceptor.java
@@ -0,0 +1,536 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+package org.apache.directory.server.core.changelog;
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.entry.ServerAttribute;
+import org.apache.directory.server.core.entry.ServerEntryUtils;
+import org.apache.directory.server.core.interceptor.BaseInterceptor;
+import org.apache.directory.server.core.interceptor.NextInterceptor;
+import org.apache.directory.server.core.interceptor.context.AddOperationContext;
+import org.apache.directory.server.core.interceptor.context.DeleteOperationContext;
+import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
+import org.apache.directory.server.core.interceptor.context.MoveAndRenameOperationContext;
+import org.apache.directory.server.core.interceptor.context.MoveOperationContext;
+import org.apache.directory.server.core.interceptor.context.RenameOperationContext;
+import org.apache.directory.server.core.invocation.InvocationStack;
+import org.apache.directory.server.core.jndi.ServerContext;
+import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
+import org.apache.directory.shared.ldap.entry.Modification;
+import org.apache.directory.shared.ldap.entry.Value;
+import org.apache.directory.shared.ldap.ldif.ChangeType;
+import org.apache.directory.shared.ldap.ldif.LdifEntry;
+import org.apache.directory.shared.ldap.ldif.LdifUtils;
+import org.apache.directory.shared.ldap.util.Base64;
+import org.apache.directory.shared.ldap.util.DateUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.naming.NamingException;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Queue;
+
+
+/**
+ * An interceptor which maintains a change LOG as it intercepts changes to the
+ * directory.  It mainains a changes.LOG file using the LDIF format for changes.
+ * It appends changes to this file so the entire LDIF file can be loaded to 
+ * replicate the state of the server.
+ * 
+ */
+public class OriginalChangeLogInterceptor extends BaseInterceptor implements Runnable
+{
+    /** logger used by this class */
+    private static final Logger LOG = LoggerFactory.getLogger( OriginalChangeLogInterceptor.class );
+
+    /** time to wait before automatically waking up the writer thread */
+    private static final long WAIT_TIMEOUT_MILLIS = 1000;
+    
+    /** the changes.LOG file's stream which we append change LOG messages to */
+    private PrintWriter out;
+    
+    /** queue of string buffers awaiting serialization to the LOG file */
+    private final Queue<StringBuilder> queue = new LinkedList<StringBuilder>();
+    
+    /** a handle on the attributeType registry to determine the binary nature of attributes */
+    private AttributeTypeRegistry atRegistry;
+    
+    /** determines if this service has been activated */
+    private boolean isActive;
+    
+    /** thread used to asynchronously write change logs to disk */
+    private Thread writer;
+    
+    
+    // -----------------------------------------------------------------------
+    // Overridden init() and destroy() methods
+    // -----------------------------------------------------------------------
+
+    
+    public void init( DirectoryService directoryService ) throws NamingException
+    {
+        super.init( directoryService );
+
+        // Get a handle on the attribute registry to check if attributes are binary
+        atRegistry = directoryService.getRegistries().getAttributeTypeRegistry();
+
+        // Open a print stream to use for flushing LDIFs into
+        File changes = new File( directoryService.getWorkingDirectory(), "changes.LOG" );
+        
+        try
+        {
+            if ( changes.exists() )
+            {
+                out = new PrintWriter( new FileWriter( changes, true ) );
+            }
+            else
+            {
+                out = new PrintWriter( new FileWriter( changes ) );
+            }
+        }
+        catch( Exception e )
+        {
+            LOG.error( "Failed to open the change LOG file: " + changes, e );
+        }
+        
+        out.println( "# -----------------------------------------------------------------------------" );
+        out.println( "# Initializing changelog service: " + DateUtils.getGeneralizedTime() );
+        out.println( "# -----------------------------------------------------------------------------" );
+        out.flush();
+        
+        writer = new Thread( this );
+        isActive = true;
+        writer.start();
+    }
+    
+    
+    public void destroy()
+    {
+        // Gracefully stop writer thread and push remaining enqueued buffers ourselves
+        isActive = false;
+        
+        do
+        {
+            // Let's notify the writer thread to make it die faster
+            synchronized( queue )
+            {
+                queue.notifyAll();
+            }
+            
+            // Sleep tiny bit waiting for the writer to die
+            try
+            {
+                Thread.sleep( 50 );
+            }
+            catch ( InterruptedException e )
+            {
+                LOG.error( "Failed to sleep while waiting for writer to die", e );
+            }
+        } while ( writer.isAlive() );
+        
+        // Ok lock down queue and start draining it
+        synchronized( queue )
+        {
+            while ( ! queue.isEmpty() )
+            {
+                StringBuilder buf = queue.poll();
+                
+                if ( buf != null )
+                {
+                    out.println( buf );
+                }
+            }
+        }
+
+        // Print message that we're stopping LOG service, flush and close
+        out.println( "# -----------------------------------------------------------------------------" );
+        out.println( "# Deactivating changelog service: " + DateUtils.getGeneralizedTime() );
+        out.println( "# -----------------------------------------------------------------------------" );
+        out.flush();
+        out.close();
+        
+        super.destroy();
+    }
+    
+    
+    // -----------------------------------------------------------------------
+    // Implementation for Runnable.run() for writer Thread
+    // -----------------------------------------------------------------------
+
+    
+    public void run()
+    {
+        while ( isActive )
+        {
+            StringBuilder buf;
+
+            // Grab semphore to queue and dequeue from it
+            synchronized( queue )
+            {
+                try 
+                { 
+                    queue.wait( WAIT_TIMEOUT_MILLIS ); 
+                } 
+                catch ( InterruptedException e ) 
+                { 
+                    LOG.error( "Failed to to wait() on queue", e );
+                }
+                
+                buf = queue.poll();
+                queue.notifyAll();
+            }
+            
+            // Do writing outside of synch block to allow other threads to enqueue
+            if ( buf != null )
+            {
+                out.println( buf );
+                out.flush();
+            }
+        }
+    }
+    
+    
+    // -----------------------------------------------------------------------
+    // Overridden (only change inducing) intercepted methods
+    // -----------------------------------------------------------------------
+
+    public void add( NextInterceptor next, AddOperationContext opContext ) throws NamingException
+    {
+        StringBuilder buf;
+        next.add( opContext );
+        
+        if ( ! isActive )
+        {
+            return;
+        }
+        
+        // Append comments that can be used to track the user and time this operation occurred
+        buf = new StringBuilder();
+        buf.append( "\n#! creatorsName: " );
+        buf.append( getPrincipalName() );
+        buf.append( "\n#! createTimestamp: " );
+        buf.append( DateUtils.getGeneralizedTime() );
+        
+        // Append the LDIF entry now
+        buf.append( LdifUtils.convertToLdif( ServerEntryUtils.toAttributesImpl( opContext.getEntry() ) ) );
+
+        // Enqueue the buffer onto a queue that is emptied by another thread asynchronously. 
+        synchronized ( queue )
+        {
+            queue.offer( buf );
+            queue.notifyAll();
+        }
+    }
+
+    /**
+     * The delete operation has to be stored with a way to restore the deleted element.
+     * There is no way to do that but reading the entry and dump it into the LOG.
+     */
+    public void delete( NextInterceptor next, DeleteOperationContext opContext ) throws NamingException
+    {
+        next.delete( opContext );
+
+        if ( ! isActive )
+        {
+            return;
+        }
+        
+        // Append comments that can be used to track the user and time this operation occurred
+        StringBuilder buf = new StringBuilder();
+        buf.append( "\n#! deletorsName: " );
+        buf.append( getPrincipalName() );
+        buf.append( "\n#! deleteTimestamp: " );
+        buf.append( DateUtils.getGeneralizedTime() );
+        
+        LdifEntry entry = new LdifEntry();
+        entry.setDn( opContext.getDn().getUpName() );
+        entry.setChangeType( ChangeType.Delete );
+        buf.append( LdifUtils.convertToLdif( entry ) );
+        
+
+        // Enqueue the buffer onto a queue that is emptied by another thread asynchronously. 
+        synchronized ( queue )
+        {
+            queue.offer( buf );
+            queue.notifyAll();
+        }
+    }
+
+    
+    public void modify( NextInterceptor next, ModifyOperationContext opContext ) throws NamingException
+    {
+        StringBuilder buf;
+        next.modify( opContext );
+
+        if ( ! isActive )
+        {
+            return;
+        }
+        
+        // Append comments that can be used to track the user and time this operation occurred
+        buf = new StringBuilder();
+        buf.append( "\n#! modifiersName: " );
+        buf.append( getPrincipalName() );
+        buf.append( "\n#! modifyTimestamp: " );
+        buf.append( DateUtils.getGeneralizedTime() );
+        
+        // Append the LDIF record now
+        buf.append( "\ndn: " );
+        buf.append( opContext.getDn() );
+        buf.append( "\nchangetype: modify" );
+
+        List<Modification> mods = opContext.getModItems();
+        
+        for ( Modification mod :mods )
+        {
+            append( buf, (ServerAttribute)mod.getAttribute(), mod.getOperation().toString() + ": ");
+        }
+        
+        buf.append( "\n" );
+
+        // Enqueue the buffer onto a queue that is emptied by another thread asynchronously. 
+        synchronized ( queue )
+        {
+            queue.offer( buf );
+            queue.notifyAll();
+        }
+    }
+
+
+    // -----------------------------------------------------------------------
+    // Though part left as an exercise (Not Any More!)
+    // -----------------------------------------------------------------------
+
+    
+    public void rename ( NextInterceptor next, RenameOperationContext renameContext ) throws NamingException
+    {
+        next.rename( renameContext );
+        
+        if ( ! isActive )
+        {
+            return;
+        }
+        
+        StringBuilder buf;
+        
+        // Append comments that can be used to track the user and time this operation occurred
+        buf = new StringBuilder();
+        buf.append( "\n#! principleName: " );
+        buf.append( getPrincipalName() );
+        buf.append( "\n#! operationTimestamp: " );
+        buf.append( DateUtils.getGeneralizedTime() );
+        
+        // Append the LDIF record now
+        buf.append( "\ndn: " );
+        buf.append( renameContext.getDn() );
+        buf.append( "\nchangetype: modrdn" );
+        buf.append( "\nnewrdn: " ).append( renameContext.getNewRdn() );
+        buf.append( "\ndeleteoldrdn: " ).append( renameContext.getDelOldDn() ? "1" : "0" );
+        
+        buf.append( "\n" );
+
+        // Enqueue the buffer onto a queue that is emptied by another thread asynchronously. 
+        synchronized ( queue )
+        {
+            queue.offer( buf );
+            queue.notifyAll();
+        }
+    }
+
+    
+    public void moveAndRename( NextInterceptor next, MoveAndRenameOperationContext moveAndRenameOperationContext )
+        throws NamingException
+    {
+        next.moveAndRename( moveAndRenameOperationContext );
+        
+        if ( ! isActive )
+        {
+            return;
+        }
+        
+        StringBuilder buf;
+        
+        // Append comments that can be used to track the user and time this operation occurred
+        buf = new StringBuilder();
+        buf.append( "\n#! principleName: " );
+        buf.append( getPrincipalName() );
+        buf.append( "\n#! operationTimestamp: " );
+        buf.append( DateUtils.getGeneralizedTime() );
+        
+        // Append the LDIF record now
+        buf.append( "\ndn: " );
+        buf.append( moveAndRenameOperationContext.getDn() );
+        buf.append( "\nchangetype: modrdn" ); // FIXME: modrdn --> moddn ?
+        buf.append( "\nnewrdn: " ).append( moveAndRenameOperationContext.getNewRdn() );
+        buf.append( "\ndeleteoldrdn: " ).append( moveAndRenameOperationContext.getDelOldDn() ? "1" : "0" );
+        buf.append( "\nnewsperior: " ).append( moveAndRenameOperationContext.getParent() );
+        
+        buf.append( "\n" );
+
+        // Enqueue the buffer onto a queue that is emptied by another thread asynchronously. 
+        synchronized ( queue )
+        {
+            queue.offer( buf );
+            queue.notifyAll();
+        }
+    }
+
+    
+    public void move ( NextInterceptor next, MoveOperationContext moveOperationContext ) throws NamingException
+    {
+        next.move( moveOperationContext );
+        
+        if ( ! isActive )
+        {
+            return;
+        }
+        
+        StringBuilder buf;
+        
+        // Append comments that can be used to track the user and time this operation occurred
+        buf = new StringBuilder();
+        buf.append( "\n#! principleName: " );
+        buf.append( getPrincipalName() );
+        buf.append( "\n#! operationTimestamp: " );
+        buf.append( DateUtils.getGeneralizedTime() );
+        
+        // Append the LDIF record now
+        buf.append( "\ndn: " );
+        buf.append( moveOperationContext.getDn() );
+        buf.append( "\nchangetype: moddn" );
+        buf.append( "\nnewsperior: " ).append( moveOperationContext.getParent() );
+        
+        buf.append( "\n" );
+
+        // Enqueue the buffer onto a queue that is emptied by another thread asynchronously. 
+        synchronized ( queue )
+        {
+            queue.offer( buf );
+            queue.notifyAll();
+        }
+    }
+
+    
+    // -----------------------------------------------------------------------
+    // Private utility methods used by interceptor methods
+    // -----------------------------------------------------------------------
+
+    
+    /**
+     * Appends an Attribute and its values to a buffer containing an LDIF entry taking
+     * into account whether or not the attribute's syntax is binary or not.
+     * 
+     * @param buf the buffer written to and returned (for chaining)
+     * @param attr the attribute written to the buffer
+     * @return the buffer argument to allow for call chaining.
+     * @throws NamingException if the attribute is not identified by the registry
+     */
+    private StringBuilder append( StringBuilder buf, ServerAttribute attr ) throws NamingException
+    {
+        String id = attr.getId();
+        boolean isBinary = ! atRegistry.lookup( id ).getSyntax().isHumanReadable();
+        
+        if ( isBinary )
+        {
+            for ( Value<?> value:attr )
+            {
+                buf.append( "\n" );
+                buf.append( id );
+                buf.append( ":: " );
+                String encoded;
+                
+                if ( value.get() instanceof String )
+                {
+                    encoded = ( String ) value.get();
+                    
+                    try
+                    {
+                        encoded = new String( Base64.encode( encoded.getBytes( "UTF-8" ) ) );
+                    }
+                    catch ( UnsupportedEncodingException e )
+                    {
+                        LOG.error( "can't convert to UTF-8: " + encoded, e );
+                    }
+                }
+                else
+                {
+                    encoded = new String( Base64.encode( ( byte[] ) value.get() ) );
+                }
+                buf.append( encoded );
+            }
+        }
+        else
+        {
+            for ( Value<?> value:attr )
+            {
+                buf.append( "\n" );
+                buf.append( id );
+                buf.append( ": " );
+                buf.append( value.get() );
+            }
+        }
+        
+        return buf;
+    }
+    
+
+    /**
+     * Gets the DN of the user currently bound to the server executing this operation.  If 
+     * the user is anonymous "" is returned.
+     * 
+     * @return the DN of the user executing the current intercepted operation
+     * @throws NamingException if we cannot access the interceptor stack
+     */
+    private String getPrincipalName() throws NamingException
+    {
+        ServerContext ctx = ( ServerContext ) InvocationStack.getInstance().peek().getCaller();
+        return ctx.getPrincipal().getName();
+    }
+
+
+    /**
+     * Appends a modification delta instruction to an LDIF: i.e. 
+     * <pre>
+     * add: telephoneNumber
+     * telephoneNumber: +1 408 555 1234
+     * telephoneNumber: +1 408 444 9999
+     * -
+     * </pre>
+     * 
+     * @param buf the buffer to append the attribute delta to
+     * @param mod the modified values if any for that attribute
+     * @param modOp the modification operation as a string followd by ": "
+     * @return the buffer argument provided for chaining
+     * @throws NamingException if the modification attribute id is undefined
+     */
+    private StringBuilder append( StringBuilder buf, ServerAttribute mod, String modOp ) throws NamingException
+    {
+        buf.append( "\n" );
+        buf.append( modOp );
+        buf.append( mod.getId() );
+        append( buf, mod );
+        buf.append( "\n-" );
+        return buf;
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/changelog/RevisionOrder.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/changelog/RevisionOrder.java
new file mode 100755
index 0000000..11074d1
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/changelog/RevisionOrder.java
@@ -0,0 +1,52 @@
+/*
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *   or more contributor license agreements.  See the NOTICE file
+ *   distributed with this work for additional information
+ *   regarding copyright ownership.  The ASF licenses this file
+ *   to you 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.
+ *
+ */
+package org.apache.directory.server.core.changelog;
+
+
+/**
+ * The order, based on revision numbers, in which to return log changes or 
+ * tags.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public enum RevisionOrder
+{
+    AscendingOrder( true ),
+    DescendingOrder( false );
+    
+
+    private final boolean ascending;
+    
+
+    private RevisionOrder( boolean ascending )
+    {
+        this.ascending = ascending;
+    }
+
+
+    /**
+     * @return the ascending
+     */
+    public boolean isAscending()
+    {
+        return ascending;
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/changelog/Scope.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/changelog/Scope.java
new file mode 100755
index 0000000..28517a8
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/changelog/Scope.java
@@ -0,0 +1,55 @@
+/*
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *   or more contributor license agreements.  See the NOTICE file
+ *   distributed with this work for additional information
+ *   regarding copyright ownership.  The ASF licenses this file
+ *   to you 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.
+ *
+ */
+package org.apache.directory.server.core.changelog;
+
+
+import javax.naming.directory.SearchControls;
+
+
+/**
+ * TODO Scope.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public enum Scope
+{
+    Subtree( SearchControls.SUBTREE_SCOPE ),
+    OneLevel( SearchControls.ONELEVEL_SCOPE ),
+    Object( SearchControls.OBJECT_SCOPE );
+    
+    
+    private final int scope;
+    
+    
+    private Scope( int scope )
+    {
+        this.scope = scope;
+    }
+
+
+    /**
+     * @return the scope
+     */
+    public int getScope()
+    {
+        return scope;
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/changelog/SearchableChangeLogStore.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/changelog/SearchableChangeLogStore.java
new file mode 100755
index 0000000..f009805
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/changelog/SearchableChangeLogStore.java
@@ -0,0 +1,37 @@
+/*
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *   or more contributor license agreements.  See the NOTICE file
+ *   distributed with this work for additional information
+ *   regarding copyright ownership.  The ASF licenses this file
+ *   to you 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.
+ *
+ */
+package org.apache.directory.server.core.changelog;
+
+
+/**
+ * TODO SearchableChangeLogStore.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public interface SearchableChangeLogStore extends ChangeLogStore
+{
+    /**
+     * Get's the search engine for this SearchableChangeLogStore.
+     * 
+     * @return the search engine for this SearchableChangeLogStore
+     */
+    ChangeLogSearchEngine getChangeLogSearchEngine();
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/changelog/Tag.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/changelog/Tag.java
new file mode 100755
index 0000000..2c93d26
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/changelog/Tag.java
@@ -0,0 +1,83 @@
+/*
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *   or more contributor license agreements.  See the NOTICE file
+ *   distributed with this work for additional information
+ *   regarding copyright ownership.  The ASF licenses this file
+ *   to you 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.
+ *
+ */
+package org.apache.directory.server.core.changelog;
+
+
+/**
+ * A tag on a revision representing a snapshot of the directory server's 
+ * state.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class Tag
+{
+    /*
+     * TODO should we have date in which tag was taken
+     * TODO should we have the date of the revision that was tagged
+     */
+    private final long revision;
+    private final String description;
+    
+    
+    public Tag( long revision, String description )
+    {
+        this.revision = revision;
+        this.description = description;
+    }
+
+
+    /**
+     * @return the revision
+     */
+    public long getRevision()
+    {
+        return revision;
+    }
+
+
+    /**
+     * @return the description
+     */
+    public String getDescription()
+    {
+        return description;
+    }
+
+
+    public boolean equals( Object other )
+    {
+        if ( other instanceof Tag )
+        {
+            Tag ot = ( Tag ) other;
+
+            if ( description != null && ot.getDescription() != null )
+            {
+                return revision == ot.getRevision() && description.equals( ot.getDescription() );
+            }
+            else if ( description == null && ot.getDescription() == null )
+            {
+                return revision == ot.getRevision();
+            }
+        }
+
+        return false;
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/changelog/TagSearchEngine.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/changelog/TagSearchEngine.java
new file mode 100755
index 0000000..462bae8
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/changelog/TagSearchEngine.java
@@ -0,0 +1,131 @@
+/*
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *   or more contributor license agreements.  See the NOTICE file
+ *   distributed with this work for additional information
+ *   regarding copyright ownership.  The ASF licenses this file
+ *   to you 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.
+ *
+ */
+package org.apache.directory.server.core.changelog;
+
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+
+
+/**
+ * An optional search interface supported by TaggableChangeLogStores.  This 
+ * interface enables the:
+ * 
+ * <ul>
+ *   <li>lookup of tags by revision</li>
+ *   <li>finding all tags</li>
+ *   <li>finding tags before or after a revision</li>
+ *   <li>finding tags in some revision range</li>
+ * </ul>
+ * 
+ * While investigating these interface methods keep in mind that only one 
+ * tag can exist on a revision.  Unlike subversion which allows multiple 
+ * tags on a revision we only allow at most one: more than one is pointless.
+ * 
+ * Date wise searches for tags are not present within this interface since 
+ * they should be used in conjunction with the ChangeLogSearchEngine which
+ * is by default supported by TaggableSearchableChangeLogStores.  The 
+ * ChangeLogSearchEngine can find revisions based on time descriptors and
+ * returned revisions can be checked for the presence of tags using this
+ * interface.  The whole point to enabling both search engines in a single
+ * interfaces is because if you can search for tags you should be able to 
+ * search for revisions: however the converse may not be the case.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public interface TagSearchEngine
+{
+    /**
+     * Gets the tag for a specific snapshot if that snapshot exists. 
+     *
+     * @param revision the revision number to use to check for a snapshot
+     * @return the snapshot at the revision if one exists, otherwise null
+     * @throws NamingException if there is a problem accessing the store
+     */
+    Tag lookup( long revision ) throws NamingException;
+    
+    /**
+     * Checks to see if a snapshot exists for a specific revision. 
+     *
+     * @param revision the revision number to use to check for a snapshot
+     * @return true if a snapshot exists at the revision, false otherwise
+     * @throws NamingException if there is a problem accessing the store
+     */
+    boolean has( long revision ) throws NamingException;
+
+
+    // -----------------------------------------------------------------------
+    // Tag Search Operations
+    // -----------------------------------------------------------------------
+
+    
+    /**
+     * Finds all the snapshot tags taken since revision 0 until the current 
+     * revision.
+     *
+     * @param order the revision order in which to return snapshot tags 
+     * @return an enumeration over the tags of all snapshots taken since revision 0
+     * @throws NamingException if there is a problem accessing the store
+     */
+    NamingEnumeration<Tag> find( RevisionOrder order ) throws NamingException;
+    
+    /**
+     * Finds all the snapshot tags taken before a specific revision.  If a tag 
+     * exists at the revision parameter it will be returned as well.
+     *
+     * @param revision the revision number to get snapshots before 
+     * @param order the revision order in which to return snapshot tags 
+     * @return an enumeration over the tags of all snapshots taken before a revision inclusive
+     * @throws NamingException if there is a problem accessing the store
+     * @throws IllegalArgumentException if the revision is greater than the current revision
+     * or less than 0.
+     */
+    NamingEnumeration<Tag> findBefore( long revision, RevisionOrder order ) throws NamingException;
+    
+    /**
+     * Finds all the snapshot tags taken after a specific revision.  If a tag 
+     * exists at the revision parameter it will be returned as well.
+     *
+     * @param revision the revision number to get snapshots after
+     * @param order the revision order in which to return snapshot tags 
+     * @return an enumeration over the tags of all snapshots taken after a revision inclusive
+     * @throws NamingException if there is a problem accessing the store
+     * @throws IllegalArgumentException if the revision is greater than the current revision
+     * or less than 0.
+     */
+    NamingEnumeration<Tag> findAfter( long revision, RevisionOrder order ) throws NamingException;
+    
+    /**
+     * Enumerates over the tags of all snapshots taken between a specific revision 
+     * range inclusive.  The first revision parameter should be less than or equal 
+     * to the second revision parameter.
+     *
+     * @param startRevision the revision to start on inclusive
+     * @param endRevision the revision to end on inclusive
+     * @param order the revision order in which to return snapshot tags
+     * @return enumeration over all the snapshots taken in a revision range inclusive
+     * @throws NamingException if there is a problem accessing the store
+     * @throws IllegalArgumentException if the revision range is not constructed properly
+     * or if either revision number is greater than the current revision or less than 0.
+     */
+    NamingEnumeration<Tag> find( long startRevision, long endRevision, RevisionOrder order ) 
+        throws NamingException;
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/changelog/TaggableChangeLogStore.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/changelog/TaggableChangeLogStore.java
new file mode 100755
index 0000000..b5126f8
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/changelog/TaggableChangeLogStore.java
@@ -0,0 +1,72 @@
+/*
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *   or more contributor license agreements.  See the NOTICE file
+ *   distributed with this work for additional information
+ *   regarding copyright ownership.  The ASF licenses this file
+ *   to you 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.
+ *
+ */
+package org.apache.directory.server.core.changelog;
+
+import javax.naming.NamingException;
+
+
+/**
+ * A ChangeLogStore which allows tagging for tracking server state snapshots.
+ * At most one tag per revision can be created.  There is no point to creating
+ * more than one tag on a revision in our case for snapshotting server state.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public interface TaggableChangeLogStore extends ChangeLogStore
+{
+    /**
+     * Creates a tag for a snapshot of the server in a specific state at a revision.
+     *
+     * @param revision the revision to tag the snapshot
+     * @throws NamingException if there is a problem taking a tag, or if
+     * the revision does not exist
+     * @return the Tag associated with the revision
+     */
+    Tag tag( long revision ) throws NamingException;
+
+    /**
+     * Creates a snapshot of the server at the current revision.
+     *
+     * @return the revision at which the tag is created
+     * @throws NamingException if there is a problem taking a tag
+     */
+    Tag tag() throws NamingException;
+
+    /**
+     * Creates a snapshot of the server at the current revision with a description
+     * of the snapshot tag.
+     *
+     * @param description a description of the state associate with the tag
+     * @return the revision at which the tag is created
+     * @throws NamingException if there is a problem taking a tag
+     */
+    Tag tag( String description ) throws NamingException;
+
+
+    /**
+     * Gets the latest tag if one was at all taken.
+     *
+     * @return the last tag to have been created (youngest), or null if no
+     * tags have been created
+     * @throws NamingException on failures to access the tag store
+     */
+    Tag getLatest() throws NamingException;
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/changelog/TaggableSearchableChangeLogStore.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/changelog/TaggableSearchableChangeLogStore.java
new file mode 100755
index 0000000..c7b85e2
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/changelog/TaggableSearchableChangeLogStore.java
@@ -0,0 +1,37 @@
+/*
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *   or more contributor license agreements.  See the NOTICE file
+ *   distributed with this work for additional information
+ *   regarding copyright ownership.  The ASF licenses this file
+ *   to you 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.
+ *
+ */
+package org.apache.directory.server.core.changelog;
+
+
+/**
+ * TODO TaggableSearchableChangeLogStore.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public interface TaggableSearchableChangeLogStore extends TaggableChangeLogStore, SearchableChangeLogStore
+{
+    /**
+     * Get's the tag search engine for this TaggableSearchableChangeLogStore.
+     *
+     * @return the snapshot query engine for this store.
+     */
+    TagSearchEngine getTagSearchEngine();
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/changelog/package-info.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/changelog/package-info.java
new file mode 100644
index 0000000..863bc51
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/changelog/package-info.java
@@ -0,0 +1,56 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.
+ *
+ */
+
+/**
+ * <pre>
+ * <h1>Ideas</h1>
+ * <p>
+ * Use AdminModel to control what goes into the changelog ?  Or use admin model to identify 
+ * scopes/concerns which are put into different channels in the changelog?  Cool idea perhaps,
+ * perhaps not.  To some degree the DN does the scope thingy for us.  There really is no point
+ * to having an additional scope parameter.
+ * </p><p>
+ * Perhaps we can also inject a new revisions (multi-valued) operational attribute into 
+ * entries to track the revisions of changes in the changeLog to that entry.  This can
+ * be used to ask the server for a log of changes that have been performed on a specific 
+ * entry.  Whoa that's really hot for auditing!
+ * </p><p>
+ * We could try to do the same thing (meaning having a tags operational attribute) with revisions.
+ * However this is pointless since the tag revision would already be in the revisions attribute.  Also
+ * a tag would select entries dynamically: all entries with revisions below the tag revision would be
+ * selected in the tag.  This leads to a neat idea: you can easily regenerate not only the revision 
+ * history of an entry, you can do it for an entire subtree, and furthermore you might even be able
+ * to conduct search operations based on a tag and the state of the server in the past.  This would be
+ * pretty wild.
+ * </p><p>
+ * Another neat thing that could be done is to request an attribute by revision using the protocol 
+ * based tagging mechanism in LDAP.  For example we have language based tags like cn;lang-en so why
+ * not have version based tags like cn;revision-23.  When requested in this mannar the server can 
+ * reconstruct the state of the attribute at a specific revision and return it to the user.  This is
+ * an incredible capability when storing the configurations of systems in LDAP.  Being able to rollback
+ * to a previous configuration or just inquire about a previous state is a powerful feature to have.
+ * </p>
+ * </pre>
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+
+package org.apache.directory.server.core.changelog;
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/collective/CollectiveAttributeInterceptor.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/collective/CollectiveAttributeInterceptor.java
new file mode 100644
index 0000000..d887b69
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/collective/CollectiveAttributeInterceptor.java
@@ -0,0 +1,368 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.collective;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.entry.DefaultServerAttribute;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerSearchResult;
+import org.apache.directory.server.core.enumeration.SearchResultFilter;
+import org.apache.directory.server.core.enumeration.SearchResultFilteringEnumeration;
+import org.apache.directory.server.core.interceptor.BaseInterceptor;
+import org.apache.directory.server.core.interceptor.NextInterceptor;
+import org.apache.directory.server.core.interceptor.context.AddOperationContext;
+import org.apache.directory.server.core.interceptor.context.ListOperationContext;
+import org.apache.directory.server.core.interceptor.context.LookupOperationContext;
+import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
+import org.apache.directory.server.core.interceptor.context.SearchOperationContext;
+import org.apache.directory.server.core.invocation.Invocation;
+import org.apache.directory.server.core.invocation.InvocationStack;
+import org.apache.directory.server.core.partition.PartitionNexus;
+import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.entry.Value;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.SearchControls;
+import java.util.HashSet;
+import java.util.Set;
+
+
+/**
+ * An interceptor based service dealing with collective attribute
+ * management.  This service intercepts read operations on entries to
+ * inject collective attribute value pairs into the response based on
+ * the entires inclusion within collectiveAttributeSpecificAreas and
+ * collectiveAttributeInnerAreas.
+ *
+ * @org.apache.xbean.XBean
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class CollectiveAttributeInterceptor extends BaseInterceptor
+{
+    /** The global registries */
+    private Registries registries;
+    
+    /** The attributeType registry */
+    private AttributeTypeRegistry atRegistry;
+    
+    private PartitionNexus nexus;
+    
+    private CollectiveAttributesSchemaChecker collectiveAttributesSchemaChecker;
+
+
+    /**
+     * the search result filter to use for collective attribute injection
+     */
+    private final SearchResultFilter SEARCH_FILTER = new SearchResultFilter()
+    {
+        public boolean accept( Invocation invocation, ServerSearchResult result, SearchControls controls )
+            throws NamingException
+        {
+            LdapDN name = ((ServerSearchResult)result).getDn();
+            
+            if ( name.isNormalized() == false )
+            {
+                name = LdapDN.normalize( name, atRegistry.getNormalizerMapping() );
+            }
+            
+            ServerEntry entry = result.getServerEntry();
+            String[] retAttrs = controls.getReturningAttributes();
+            addCollectiveAttributes( name, entry, retAttrs );
+            result.setServerEntry( entry );
+            return true;
+        }
+    };
+
+    public void init( DirectoryService directoryService ) throws NamingException
+    {
+        super.init( directoryService );
+        nexus = directoryService.getPartitionNexus();
+        atRegistry = directoryService.getRegistries().getAttributeTypeRegistry();
+        collectiveAttributesSchemaChecker = new CollectiveAttributesSchemaChecker( nexus, atRegistry );
+        registries = directoryService.getRegistries();
+    }
+
+
+    /**
+     * Adds the set of collective attributes requested in the returning attribute list
+     * and contained in subentries referenced by the entry. Excludes collective
+     * attributes that are specified to be excluded via the 'collectiveExclusions'
+     * attribute in the entry.
+     *
+     * @param normName name of the entry being processed
+     * @param entry the entry to have the collective attributes injected
+     * @param retAttrs array or attribute type to be specifically included in the result entry(s)
+     * @throws NamingException if there are problems accessing subentries
+     */
+    private void addCollectiveAttributes( LdapDN normName, ServerEntry entry, String[] retAttrs ) throws NamingException
+    {
+        EntryAttribute caSubentries;
+
+        //noinspection StringEquality
+        if ( ( retAttrs == null ) || ( retAttrs.length != 1 ) || ( retAttrs[0] != SchemaConstants.ALL_USER_ATTRIBUTES ) )
+        {
+            ServerEntry entryWithCAS = nexus.lookup( new LookupOperationContext( registries, normName, new String[] { 
+                SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT_OID } ) );
+            caSubentries = entryWithCAS.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT );
+        }
+        else
+        {
+            caSubentries = entry.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT );
+        }
+        
+        /*
+         * If there are no collective attribute subentries referenced
+         * then we have no collective attributes to inject to this entry.
+         */
+        if ( caSubentries == null )
+        {
+            return;
+        }
+    
+        /*
+         * Before we proceed we need to lookup the exclusions within the
+         * entry and build a set of exclusions for rapid lookup.  We use
+         * OID values in the exclusions set instead of regular names that
+         * may have case variance.
+         */
+        EntryAttribute collectiveExclusions = entry.get( SchemaConstants.COLLECTIVE_EXCLUSIONS_AT );
+        Set<String> exclusions = new HashSet<String>();
+        
+        if ( collectiveExclusions != null )
+        {
+            if ( collectiveExclusions.contains( SchemaConstants.EXCLUDE_ALL_COLLECTIVE_ATTRIBUTES_AT_OID )
+                || collectiveExclusions.contains( SchemaConstants.EXCLUDE_ALL_COLLECTIVE_ATTRIBUTES_AT  ) )
+            {
+                /*
+                 * This entry does not allow any collective attributes
+                 * to be injected into itself.
+                 */
+                return;
+            }
+
+            exclusions = new HashSet<String>();
+            
+            for ( Value<?> value:collectiveExclusions )
+            {
+                AttributeType attrType = atRegistry.lookup( ( String ) value.get() );
+                exclusions.add( attrType.getOid() );
+            }
+        }
+        
+        /*
+         * If no attributes are requested specifically
+         * then it means all user attributes are requested.
+         * So populate the array with all user attributes indicator: "*".
+         */
+        if ( retAttrs == null )
+        {
+            retAttrs = SchemaConstants.ALL_USER_ATTRIBUTES_ARRAY;
+        }
+        
+        /*
+         * Construct a set of requested attributes for easier tracking.
+         */ 
+        Set<String> retIdsSet = new HashSet<String>( retAttrs.length );
+        
+        for ( String retAttr:retAttrs )
+        {
+            if ( retAttr.equals( SchemaConstants.ALL_USER_ATTRIBUTES ) ||
+                retAttr.equals( SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES ) )
+            {
+                retIdsSet.add( retAttr );
+            }
+            else
+            {
+                retIdsSet.add( atRegistry.lookup( retAttr ).getOid() );
+            }
+        }
+
+        /*
+         * For each collective subentry referenced by the entry we lookup the
+         * attributes of the subentry and copy collective attributes from the
+         * subentry into the entry.
+         */
+        for ( Value<?> value:caSubentries )
+        {
+            String subentryDnStr = ( String ) value.get();
+            LdapDN subentryDn = new LdapDN( subentryDnStr );
+            ServerEntry subentry = nexus.lookup( new LookupOperationContext( registries, subentryDn ) );
+            
+            for ( AttributeType attributeType:subentry.getAttributeTypes() )
+            {
+                String attrId = attributeType.getName();
+                
+                if ( !attributeType.isCollective() )
+                {
+                    continue;
+                }
+                
+                /*
+                 * Skip the addition of this collective attribute if it is excluded
+                 * in the 'collectiveAttributes' attribute.
+                 */
+                if ( exclusions.contains( attributeType.getOid() ) )
+                {
+                    continue;
+                }
+                
+                Set<AttributeType> allSuperTypes = getAllSuperTypes( attributeType );
+
+                for ( String retId : retIdsSet )
+                {
+                    if ( retId.equals( SchemaConstants.ALL_USER_ATTRIBUTES ) || retId.equals( SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES ) )
+                    {
+                        continue;
+                    }
+
+                    AttributeType retType = atRegistry.lookup( retId );
+
+                    if ( allSuperTypes.contains( retType ) )
+                    {
+                        retIdsSet.add( atRegistry.lookup( attrId ).getOid() );
+                        break;
+                    }
+                }
+
+                /*
+                 * If not all attributes or this collective attribute requested specifically
+                 * then bypass the inclusion process.
+                 */
+                if ( !( retIdsSet.contains( SchemaConstants.ALL_USER_ATTRIBUTES ) || 
+                    retIdsSet.contains( atRegistry.lookup( attrId ).getOid() ) ) )
+                {
+                    continue;
+                }
+                
+                EntryAttribute subentryColAttr = subentry.get( attrId );
+                EntryAttribute entryColAttr = entry.get( attrId );
+
+                /*
+                 * If entry does not have attribute for collective attribute then create it.
+                 */
+                if ( entryColAttr == null )
+                {
+                    entryColAttr = new DefaultServerAttribute( attrId, atRegistry.lookup( attrId ) );
+                    entry.put( entryColAttr );
+                }
+
+                /*
+                 *  Add all the collective attribute values in the subentry
+                 *  to the currently processed collective attribute in the entry.
+                 */
+                for ( Value<?> subentryColVal:subentryColAttr )
+                {
+                    entryColAttr.add( (String)subentryColVal.get() );
+                }
+            }
+        }
+    }
+    
+    
+    private Set<AttributeType> getAllSuperTypes( AttributeType id ) throws NamingException
+    {
+        Set<AttributeType> allSuperTypes = new HashSet<AttributeType>();
+        AttributeType superType = id;
+        
+        while ( superType != null )
+        {
+            superType = superType.getSuperior();
+            
+            if ( superType != null )
+            {
+                allSuperTypes.add( superType );
+            }
+        }
+        
+        return allSuperTypes;
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Interceptor Method Overrides
+    // ------------------------------------------------------------------------
+    public ServerEntry lookup( NextInterceptor nextInterceptor, LookupOperationContext opContext ) throws NamingException
+    {
+        ServerEntry result = nextInterceptor.lookup( opContext );
+        
+        if ( result == null )
+        {
+            return null;
+        }
+        
+        if ( ( opContext.getAttrsId() == null ) || ( opContext.getAttrsId().size() == 0 ) ) 
+        {
+            addCollectiveAttributes( opContext.getDn(), result, SchemaConstants.ALL_USER_ATTRIBUTES_ARRAY );
+        }
+        else
+        {
+            addCollectiveAttributes( opContext.getDn(), result, opContext.getAttrsIdArray() );
+        }
+
+        return result;
+    }
+
+
+    public NamingEnumeration<ServerSearchResult> list( NextInterceptor nextInterceptor, ListOperationContext opContext ) throws NamingException
+    {
+        NamingEnumeration<ServerSearchResult> result = nextInterceptor.list( opContext );
+        Invocation invocation = InvocationStack.getInstance().peek();
+        
+        return new SearchResultFilteringEnumeration( result, new SearchControls(), invocation, SEARCH_FILTER, "List collective Filter" );
+    }
+
+
+    public NamingEnumeration<ServerSearchResult> search( NextInterceptor nextInterceptor, SearchOperationContext opContext ) throws NamingException
+    {
+        NamingEnumeration<ServerSearchResult> result = nextInterceptor.search( opContext );
+        Invocation invocation = InvocationStack.getInstance().peek();
+        
+        return new SearchResultFilteringEnumeration( 
+            result, opContext.getSearchControls(), invocation, SEARCH_FILTER, "Search collective Filter" );
+    }
+    
+    // ------------------------------------------------------------------------
+    // Partial Schema Checking
+    // ------------------------------------------------------------------------
+    
+    public void add( NextInterceptor next, AddOperationContext opContext ) throws NamingException
+    {
+        collectiveAttributesSchemaChecker.checkAdd( opContext.getDn(), opContext.getEntry() );
+        
+        next.add( opContext );
+    }
+
+
+    public void modify( NextInterceptor next, ModifyOperationContext opContext ) throws NamingException
+    {
+        collectiveAttributesSchemaChecker.checkModify( opContext.getRegistries(),opContext.getDn(), opContext.getModItems() );
+
+        next.modify( opContext );
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/collective/CollectiveAttributesSchemaChecker.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/collective/CollectiveAttributesSchemaChecker.java
new file mode 100644
index 0000000..766f90d
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/collective/CollectiveAttributesSchemaChecker.java
@@ -0,0 +1,142 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.core.collective;
+
+import java.util.List;
+import java.util.Set;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.core.entry.ServerAttribute;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerEntryUtils;
+import org.apache.directory.server.core.interceptor.context.LookupOperationContext;
+import org.apache.directory.server.core.partition.PartitionNexus;
+import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.entry.Modification;
+import org.apache.directory.shared.ldap.entry.ModificationOperation;
+import org.apache.directory.shared.ldap.exception.LdapSchemaViolationException;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.schema.SchemaUtils;
+
+
+/**
+ * Schema checking utilities specifically for operations on collective attributes.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev:$
+ */
+public class CollectiveAttributesSchemaChecker
+{
+    private PartitionNexus nexus = null;
+    private AttributeTypeRegistry attrTypeRegistry = null;
+    
+    public CollectiveAttributesSchemaChecker( PartitionNexus nexus, AttributeTypeRegistry attrTypeRegistry )
+    {
+        this.nexus = nexus;
+        this.attrTypeRegistry = attrTypeRegistry;
+    }
+    
+    /* package scope*/ void checkAdd( LdapDN normName, ServerEntry entry ) throws LdapSchemaViolationException, NamingException
+    {
+        if ( entry.hasObjectClass( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRY_OC ) )
+        {
+            return;
+        }
+        
+        if ( containsAnyCollectiveAttributes( entry ) )
+        {
+            /*
+             * TODO: Replace the Exception and the ResultCodeEnum with the correct ones.
+             */
+            throw new LdapSchemaViolationException(
+                "Collective attributes cannot be stored in non-collectiveAttributeSubentries",
+                ResultCodeEnum.OTHER);
+        }
+    }
+    
+    public void checkModify( Registries registries, LdapDN normName, List<Modification> mods ) throws NamingException
+    {
+        ServerEntry originalEntry = nexus.lookup( new LookupOperationContext( registries, normName ) );
+        ServerEntry targetEntry = ServerEntryUtils.toServerEntry( 
+            SchemaUtils.getTargetEntry( ServerEntryUtils.toModificationItemImpl( mods ), ServerEntryUtils.toAttributesImpl( originalEntry ) ),
+            normName,
+            registries);
+        
+        EntryAttribute targetObjectClasses = targetEntry.get( SchemaConstants.OBJECT_CLASS_AT );
+        
+        if ( targetObjectClasses.contains( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRY_OC ) )
+        {
+            return;
+        }
+        
+        if ( addsAnyCollectiveAttributes( mods ) )
+        {
+            /*
+             * TODO: Replace the Exception and the ResultCodeEnum with the correct ones.
+             */
+            throw new LdapSchemaViolationException(
+                "Cannot operate on collective attributes in non-collectiveAttributeSubentries",
+                ResultCodeEnum.OTHER);
+        }
+    }
+    
+    
+    private boolean addsAnyCollectiveAttributes( List<Modification> mods ) throws NamingException
+    {
+        for ( Modification mod:mods )
+        {
+            ServerAttribute attr = (ServerAttribute)mod.getAttribute();
+            String attrID = attr.getId();
+            AttributeType attrType = attrTypeRegistry.lookup( attrID );
+            ModificationOperation modOp = mod.getOperation();
+            
+            if ( ( ( modOp == ModificationOperation.ADD_ATTRIBUTE ) || ( modOp == ModificationOperation.REPLACE_ATTRIBUTE ) ) &&
+                attrType.isCollective() )
+            {
+                return true;
+            }
+        }
+        
+        return false;
+    }
+    
+    
+    private boolean containsAnyCollectiveAttributes( ServerEntry entry ) throws NamingException
+    {
+        Set<AttributeType> attributeTypes = entry.getAttributeTypes();
+        
+        for ( AttributeType attributeType:attributeTypes )
+        {
+            if ( attributeType.isCollective() )
+            {
+                return true;
+            }
+        }
+        
+        return false;
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/configuration/AttributesPropertyEditor.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/configuration/AttributesPropertyEditor.java
new file mode 100644
index 0000000..b2c9286
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/configuration/AttributesPropertyEditor.java
@@ -0,0 +1,249 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.configuration;
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.shared.ldap.ldif.LdifComposer;
+import org.apache.directory.shared.ldap.ldif.LdifComposerImpl;
+import org.apache.directory.shared.ldap.ldif.LdifReader;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.util.MultiMap;
+import org.apache.directory.shared.ldap.util.StringTools;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import java.beans.PropertyEditor;
+import java.beans.PropertyEditorSupport;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+
+
+/**
+ * A JavaBeans {@link PropertyEditor} that can convert {@link Attributes} to
+ * LDIF string and vice versa. This class is useful when you're going to
+ * configure a {@link DirectoryService} with 3rd party containers such as <a
+ * href="http://www.springframework.org/">Spring Framework</a>.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class AttributesPropertyEditor extends PropertyEditorSupport
+{
+
+    /**
+     * Creates a new instance.
+     */
+    public AttributesPropertyEditor()
+    {
+        super();
+    }
+
+    /**
+     * Creates a new instance with source object.
+     */
+    public AttributesPropertyEditor( Object source )
+    {
+        super( source );
+    }
+
+    /**
+     * Returns LDIF string of {@link Attributes} object.
+     */
+    @SuppressWarnings("deprecation")
+    public String getAsText()
+    {
+        LdifComposer composer = new LdifComposerImpl();
+        MultiMap map = new MultiMap()
+        {
+            // FIXME Stop forking commons-collections.
+            private final org.apache.commons.collections.MultiHashMap map = 
+                new org.apache.commons.collections.MultiHashMap();
+
+            public Object remove( Object arg0, Object arg1 )
+            {
+                return map.remove( arg0, arg1 );
+            }
+
+            public int size()
+            {
+                return map.size();
+            }
+
+            public Object get( Object arg0 )
+            {
+                return map.get( arg0 );
+            }
+
+            public boolean containsValue( Object arg0 )
+            {
+                return map.containsValue( arg0 );
+            }
+
+            public Object put( Object arg0, Object arg1 )
+            {
+                return map.put( arg0, arg1 );
+            }
+
+            public Object remove( Object arg0 )
+            {
+                return map.remove( arg0 );
+            }
+
+            @SuppressWarnings("unchecked")
+            public Collection<Object> values()
+            {
+                return map.values();
+            }
+
+            public boolean isEmpty()
+            {
+                return map.isEmpty();
+            }
+
+            public boolean containsKey( Object key )
+            {
+                return map.containsKey( key );
+            }
+
+            @SuppressWarnings("unchecked")
+            public void putAll( Map arg0 )
+            {
+                map.putAll( arg0 );
+            }
+
+            public void clear()
+            {
+                map.clear();
+            }
+
+            public Set<?> keySet()
+            {
+                return map.keySet();
+            }
+
+            public Set<?> entrySet()
+            {
+                return map.entrySet();
+            }
+        };
+
+        Attributes attrs = (Attributes) getValue();
+        try
+        {
+            NamingEnumeration<? extends Attribute> e = attrs.getAll();
+            while ( e.hasMore() )
+            {
+                Attribute attr = e.next();
+                NamingEnumeration<? extends Object> e2 = attr.getAll();
+                while ( e2.hasMoreElements() )
+                {
+                    Object value = e2.next();
+                    map.put( attr.getID(), value );
+                }
+            }
+
+            return composer.compose( map );
+        }
+        catch ( Exception e )
+        {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    /**
+     * Read an entry (without DN)
+     * 
+     * @param text
+     *            The ldif format file
+     * @return An Attributes.
+     */
+    private Attributes readEntry( String text )
+    {
+        StringReader strIn = new StringReader( text );
+        BufferedReader in = new BufferedReader( strIn );
+
+        String line = null;
+        Attributes attributes = new AttributesImpl( true );
+
+        try
+        {
+            while ( ( line = in.readLine() ) != null )
+            {
+                if ( line.length() == 0 )
+                {
+                    continue;
+                }
+
+                String addedLine = line.trim();
+
+                if ( StringTools.isEmpty( addedLine ) )
+                {
+                    continue;
+                }
+
+                Attribute attribute = LdifReader.parseAttributeValue( addedLine );
+                Attribute oldAttribute = attributes.get( attribute.getID() );
+
+                if ( oldAttribute != null )
+                {
+                    try
+                    {
+                        oldAttribute.add( attribute.get() );
+                        attributes.put( oldAttribute );
+                    }
+                    catch (NamingException ne)
+                    {
+                        // Do nothing
+                    }
+                }
+                else
+                {
+                    attributes.put( attribute );
+                }
+            }
+        }
+        catch (IOException ioe)
+        {
+            // Do nothing : we can't reach this point !
+        }
+
+        return attributes;
+    }
+
+    /**
+     * Converts the specified LDIF string into {@link Attributes}.
+     */
+    public void setAsText( String text ) throws IllegalArgumentException
+    {
+        if ( text == null )
+        {
+            text = "";
+        }
+
+        setValue( readEntry( text ) );
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/enumeration/ReferralHandlingEnumeration.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/enumeration/ReferralHandlingEnumeration.java
new file mode 100644
index 0000000..5b78518
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/enumeration/ReferralHandlingEnumeration.java
@@ -0,0 +1,267 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.enumeration;
+
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.SearchControls;
+
+import org.apache.directory.server.core.entry.ServerSearchResult;
+import org.apache.directory.server.core.interceptor.context.LookupOperationContext;
+import org.apache.directory.server.core.partition.PartitionNexus;
+import org.apache.directory.server.core.referral.ReferralLut;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.codec.util.LdapURL;
+import org.apache.directory.shared.ldap.codec.util.LdapURLEncodingException;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.entry.Value;
+import org.apache.directory.shared.ldap.exception.LdapReferralException;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.schema.OidNormalizer;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * A wrapper enumeration which saves referral entries to be returned last.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class ReferralHandlingEnumeration implements NamingEnumeration<ServerSearchResult>
+{
+    private final Logger log = LoggerFactory.getLogger( ReferralHandlingEnumeration.class );
+    private final List<ServerSearchResult> referrals = new ArrayList<ServerSearchResult>();
+    private final NamingEnumeration<ServerSearchResult> underlying;
+    private final ReferralLut lut;
+    private final PartitionNexus nexus;
+    private final boolean doThrow;
+    private final int scope;
+    private ServerSearchResult prefetched;
+    private int refIndex = -1;
+
+    /**
+     * The OIDs normalizer map
+     */
+    private Map<String, OidNormalizer> normalizerMap;
+    
+    /** The global registries */
+    private Registries registries;
+
+    public ReferralHandlingEnumeration( 
+            NamingEnumeration<ServerSearchResult> underlying, 
+            ReferralLut lut, 
+            Registries registries, 
+            PartitionNexus nexus, 
+            int scope, 
+            boolean doThrow ) throws NamingException
+    {
+        normalizerMap = registries.getAttributeTypeRegistry().getNormalizerMapping();
+        this.underlying = underlying;
+        this.doThrow = doThrow;
+        this.lut = lut;
+        this.scope = scope;
+        this.nexus = nexus;
+        this.registries = registries;
+        prefetch();
+    }
+
+
+    public void prefetch() throws NamingException
+    {
+        while ( underlying.hasMore() )
+        {
+            ServerSearchResult result = underlying.next();
+            LdapDN dn = new LdapDN( result.getDn() );
+            dn.normalize( normalizerMap );
+            
+            if ( lut.isReferral( dn ) )
+            {
+                referrals.add( result );
+                continue;
+            }
+            
+            prefetched = result;
+            return;
+        }
+
+        refIndex++;
+        prefetched = referrals.get( refIndex );
+        if ( doThrow )
+        {
+            doReferralExceptionOnSearchBase( registries );
+        }
+    }
+
+
+    public ServerSearchResult next() throws NamingException
+    {
+        ServerSearchResult retval = prefetched;
+        prefetch();
+        return retval;
+    }
+
+
+    public boolean hasMore() throws NamingException
+    {
+        return underlying.hasMore() || refIndex < referrals.size();
+    }
+
+
+    public void close() throws NamingException
+    {
+        underlying.close();
+        referrals.clear();
+        prefetched = null;
+        refIndex = Integer.MAX_VALUE;
+    }
+
+
+    public boolean hasMoreElements()
+    {
+        try
+        {
+            return hasMore();
+        }
+        catch ( NamingException e )
+        {
+            log.error( "Naming enumeration failure.  Closing enumeration early!", e );
+            try
+            {
+                close();
+            }
+            catch ( NamingException e1 )
+            {
+                log.error( "Naming enumeration failure.  Failed to properly close enumeration!", e1 );
+            }
+        }
+
+        return false;
+    }
+
+
+    public ServerSearchResult nextElement()
+    {
+        try
+        {
+            return next();
+        }
+        catch ( NamingException e )
+        {
+            log.error( "NamingEnumeration closed prematurely without returning elements.", e );
+        }
+
+        throw new NoSuchElementException( "NamingEnumeration closed prematurely without returning elements." );
+    }
+
+
+    public void doReferralExceptionOnSearchBase( Registries registries ) throws NamingException
+    {
+        // the refs attribute may be filtered out so we might need to lookup the entry
+        EntryAttribute refs = prefetched.getServerEntry().get( SchemaConstants.REF_AT );
+        
+        if ( refs == null )
+        {
+            LdapDN prefetchedDn = new LdapDN( prefetched.getDn() );
+            prefetchedDn.normalize( normalizerMap );
+            refs = nexus.lookup( new LookupOperationContext( registries, prefetchedDn ) ).get( SchemaConstants.REF_AT );
+        }
+
+        if ( refs == null )
+        {
+            throw new IllegalStateException( prefetched.getDn()
+                + " does not seem like a referral but we're trying to handle it as one." );
+        }
+
+        List<String> list = new ArrayList<String>( refs.size() );
+        
+        for ( Value<?> value:refs )
+        {
+            String val = (String)value.get();
+
+            // need to add non-ldap URLs as-is
+            if ( !val.startsWith( "ldap" ) )
+            {
+                list.add( val );
+                continue;
+            }
+
+            // parse the ref value and normalize the DN according to schema 
+            LdapURL ldapUrl = new LdapURL();
+            try
+            {
+                ldapUrl.parse( val.toCharArray() );
+            }
+            catch ( LdapURLEncodingException e )
+            {
+                log
+                    .error( "Bad URL (" + val + ") for ref in " + prefetched.getDn()
+                        + ".  Reference will be ignored." );
+            }
+
+            StringBuilder buf = new StringBuilder();
+            buf.append( ldapUrl.getScheme() );
+            buf.append( ldapUrl.getHost() );
+            
+            if ( ldapUrl.getPort() > 0 )
+            {
+                buf.append( ":" );
+                buf.append( ldapUrl.getPort() );
+            }
+            
+            buf.append( "/" );
+            buf.append( ldapUrl.getDn() );
+            buf.append( "??" );
+
+            switch ( scope )
+            {
+                case ( SearchControls.SUBTREE_SCOPE  ):
+                    buf.append( "sub" );
+                    break;
+
+                // if we search for one level and encounter a referral then search
+                // must be continued at that node using base level search scope
+                case ( SearchControls.ONELEVEL_SCOPE  ):
+                    buf.append( "base" );
+                    break;
+                    
+                case ( SearchControls.OBJECT_SCOPE  ):
+                    buf.append( "base" );
+                    break;
+                    
+                default:
+                    throw new IllegalStateException( "Unknown recognized search scope: " + scope );
+            }
+
+            list.add( buf.toString() );
+        }
+        
+        LdapReferralException lre = new LdapReferralException( list );
+        throw lre;
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/enumeration/SearchResultEnumeration.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/enumeration/SearchResultEnumeration.java
new file mode 100644
index 0000000..aafc7db
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/enumeration/SearchResultEnumeration.java
@@ -0,0 +1,36 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.enumeration;
+
+
+import javax.naming.NamingEnumeration;
+
+import org.apache.directory.server.core.entry.ServerSearchResult;
+
+
+/**
+ * An enumeration that represents search result
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public interface SearchResultEnumeration extends NamingEnumeration<ServerSearchResult>
+{
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/enumeration/SearchResultFilter.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/enumeration/SearchResultFilter.java
new file mode 100644
index 0000000..df4e72f
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/enumeration/SearchResultFilter.java
@@ -0,0 +1,52 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.enumeration;
+
+
+import org.apache.directory.server.core.entry.ServerSearchResult;
+import org.apache.directory.server.core.invocation.Invocation;
+
+import javax.naming.NamingException;
+import javax.naming.directory.SearchControls;
+
+
+/**
+ * A filter is used to modify search results while they are being returned from
+ * naming enumerations containing DbSearchResults.  These filters are used in
+ * conjunction with a {@link SearchResultFilteringEnumeration}.
+ * Multiple filters can be applied one after the other and hence they are stackable.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public interface SearchResultFilter
+{
+    /**
+     * Filters the contents of search results on the way out the door to client
+     * callers.  These filters can and do produce side-effects on the results if
+     * if need be the attributes or names within the result should be cloned.
+     *
+     * @param result the database search result to return
+     * @param controls search controls associated with the invocation
+     * @return true if the result is to be returned, false if it is to be
+     * discarded from the result set
+     */
+    boolean accept( Invocation invocation, ServerSearchResult result, SearchControls controls ) throws NamingException;
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/enumeration/SearchResultFilteringEnumeration.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/enumeration/SearchResultFilteringEnumeration.java
new file mode 100644
index 0000000..7c5b20a
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/enumeration/SearchResultFilteringEnumeration.java
@@ -0,0 +1,366 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.enumeration;
+
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Hashtable;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.Context;
+import javax.naming.Name;
+import javax.naming.spi.DirectoryManager;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.DirContext;
+
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerEntryUtils;
+import org.apache.directory.server.core.entry.ServerSearchResult;
+import org.apache.directory.server.core.invocation.Invocation;
+import org.apache.directory.shared.ldap.exception.OperationAbandonedException;
+import org.apache.directory.shared.ldap.message.AbandonListener;
+import org.apache.directory.shared.ldap.message.AbandonableRequest;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * A enumeration decorator which filters database search results as they are
+ * being enumerated back to the client caller.
+ *
+ * @see SearchResultFilter
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class SearchResultFilteringEnumeration implements NamingEnumeration<ServerSearchResult>, AbandonListener
+{
+    /** the logger used by this class */
+    private static final Logger log = LoggerFactory.getLogger( SearchResultFilteringEnumeration.class );
+
+    /** the list of filters to be applied */
+    private final List<SearchResultFilter> filters;
+    
+    /** the underlying decorated enumeration */
+    private final NamingEnumeration<ServerSearchResult> decorated;
+
+    /** the first accepted search result that is prefetched */
+    private ServerSearchResult prefetched;
+    
+    /** flag storing closed state of this naming enumeration */
+    private boolean isClosed = false;
+    
+    /** the controls associated with the search operation */
+    private final SearchControls searchControls;
+    
+    /** the Invocation that representing the search creating this enumeration */
+    private final Invocation invocation;
+    
+    /** whether or not the caller context has object factories which need to be applied to the results */
+    private final boolean applyObjectFactories;
+    
+    /** whether or not this search has been abandoned */
+    private boolean abandoned = false;
+
+    /** A name used to distinguish enumeration while debugging */
+    private String name;
+
+    // ------------------------------------------------------------------------
+    // C O N S T R U C T O R S
+    // ------------------------------------------------------------------------
+
+    /**
+     * Creates a new database result filtering enumeration to decorate an
+     * underlying enumeration.
+     *
+     * @param decorated the underlying decorated enumeration
+     * @param searchControls the search controls associated with the search
+     * creating this enumeration
+     * @param invocation the invocation representing the seach that created this enumeration
+     */
+    public SearchResultFilteringEnumeration( NamingEnumeration<ServerSearchResult> decorated, SearchControls searchControls,
+        Invocation invocation, SearchResultFilter filter, String name ) throws NamingException
+    {
+        this.searchControls = searchControls;
+        this.invocation = invocation;
+        this.filters = new ArrayList<SearchResultFilter>();
+        this.filters.add( filter );
+        this.decorated = decorated;
+        this.applyObjectFactories = invocation.getCaller().getEnvironment().containsKey( Context.OBJECT_FACTORIES );
+        this.name = name;
+
+        if ( !decorated.hasMore() )
+        {
+            close();
+            return;
+        }
+
+        prefetch();
+    }
+
+
+    /**
+     * Creates a new database result filtering enumeration to decorate an
+     * underlying enumeration.
+     *
+     * @param decorated the underlying decorated enumeration
+     * @param searchControls the search controls associated with the search
+     * creating this enumeration
+     * @param invocation the invocation representing the seach that created this enumeration
+     */
+    public SearchResultFilteringEnumeration( NamingEnumeration<ServerSearchResult> decorated, SearchControls searchControls,
+        Invocation invocation, List<SearchResultFilter> filters, String name ) throws NamingException
+    {
+        this.searchControls = searchControls;
+        this.invocation = invocation;
+        this.filters = new ArrayList<SearchResultFilter>();
+        this.filters.addAll( filters );
+        this.decorated = decorated;
+        this.applyObjectFactories = invocation.getCaller().getEnvironment().containsKey( Context.OBJECT_FACTORIES );
+        this.name = name;
+        
+
+        if ( !decorated.hasMore() )
+        {
+            close();
+            return;
+        }
+
+        prefetch();
+    }
+
+
+    // ------------------------------------------------------------------------
+    // New SearchResultFilter management methods
+    // ------------------------------------------------------------------------
+
+    /**
+     * Adds a database search result filter to this filtering enumeration at
+     * the very end of the filter list.  Filters are applied in the order of
+     * addition.
+     *
+     * @param filter a filter to apply to the results
+     * @return the result of {@link List#add(Object)}
+     */
+    public boolean addResultFilter( SearchResultFilter filter )
+    {
+        return filters.add( filter );
+    }
+
+
+    /**
+     * Removes a database search result filter from the filter list of this
+     * filtering enumeration.
+     *
+     * @param filter a filter to remove from the filter list
+     * @return the result of {@link List#remove(Object)}
+     */
+    public boolean removeResultFilter( SearchResultFilter filter )
+    {
+        return filters.remove( filter );
+    }
+
+
+    /**
+     * Gets an unmodifiable list of filters.
+     *
+     * @return the result of {@link Collections#unmodifiableList(List)}
+     */
+    public List<SearchResultFilter> getFilters()
+    {
+        return Collections.unmodifiableList( filters );
+    }
+
+
+    // ------------------------------------------------------------------------
+    // NamingEnumeration Methods
+    // ------------------------------------------------------------------------
+
+    public void close() throws NamingException
+    {
+        isClosed = true;
+        decorated.close();
+    }
+
+
+    public boolean hasMore()
+    {
+        return !isClosed;
+    }
+
+
+    public ServerSearchResult next() throws NamingException
+    {
+        ServerSearchResult retVal = this.prefetched;
+        prefetch();
+        return retVal;
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Enumeration Methods
+    // ------------------------------------------------------------------------
+
+    public boolean hasMoreElements()
+    {
+        return !isClosed;
+    }
+
+
+    public ServerSearchResult nextElement()
+    {
+        ServerSearchResult retVal = this.prefetched;
+
+        try
+        {
+            prefetch();
+        }
+        catch ( NamingException e )
+        {
+            throw new RuntimeException( "Failed to prefetch.", e );
+        }
+
+        return retVal;
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Private utility methods
+    // ------------------------------------------------------------------------
+
+    
+    private void applyObjectFactories( ServerSearchResult result ) throws NamingException
+    {
+        // if already populated or no factories are available just return
+        if ( ( result.getObject() != null ) || !applyObjectFactories )
+        {
+            return;
+        }
+
+        DirContext ctx = ( DirContext ) invocation.getCaller();
+        Hashtable<?,?> env = ctx.getEnvironment();
+        ServerEntry serverEntry = result.getServerEntry();
+        Name name = new LdapDN( result.getDn() );
+        
+        try
+        {
+            Object obj = DirectoryManager.getObjectInstance( null, name, ctx, env, ServerEntryUtils.toAttributesImpl( serverEntry ) );
+            result.setObject( obj );
+        }
+        catch ( Exception e )
+        {
+            StringBuffer buf = new StringBuffer();
+            buf.append( "ObjectFactories threw exception while attempting to generate an object for " );
+            buf.append( result.getDn() );
+            buf.append( ". Call on SearchResult.getObject() will return null." );
+            log.warn( buf.toString(), e );
+        }
+    }
+
+
+    /**
+     * Keeps getting results from the underlying decorated filter and applying
+     * the filters until a result is accepted by all and set as the prefetced
+     * result to return on the next() result request.  If no prefetched value
+     * can be found before exhausting the decorated enumeration, then this and
+     * the underlying enumeration is closed.
+     *
+     * @throws NamingException if there are problems getting results from the
+     * underlying enumeration
+     */
+    private void prefetch() throws NamingException
+    {
+        ServerSearchResult tmp;
+        
+        if ( abandoned )
+        {
+            this.close();
+            throw new OperationAbandonedException();
+        }
+
+        outer: while ( decorated.hasMore() )
+        {
+            boolean accepted = true;
+            tmp = decorated.next();
+
+            // don't waste using a for loop if we got 0 or 1 element
+            if ( filters.isEmpty() )
+            {
+                this.prefetched = tmp;
+                applyObjectFactories( this.prefetched );
+                return;
+            }
+            else if ( filters.size() == 1 )
+            {
+                accepted = filters.get( 0 ).accept( invocation, tmp, searchControls );
+                if ( accepted )
+                {
+                    this.prefetched = tmp;
+                    applyObjectFactories( this.prefetched );
+                    return;
+                }
+
+                continue;
+            }
+
+            // apply all filters shorting their application on result denials
+            for ( int ii = 0; ii < filters.size(); ii++ )
+            {
+                SearchResultFilter filter = filters.get( ii );
+                accepted &= filter.accept( invocation, tmp, searchControls );
+
+                if ( !accepted )
+                {
+                    continue outer;
+                }
+            }
+
+            /*
+             * If we get here then a result has been accepted by all the
+             * filters so we set the result as the prefetched value to return
+             * on the following call to the next() or nextElement() methods
+             */
+            this.prefetched = tmp;
+            applyObjectFactories( this.prefetched );
+            return;
+        }
+
+        /*
+         * If we get here then no result was found to be accepted by all
+         * filters before we exhausted the decorated enumeration so we close
+         */
+        close();
+    }
+
+
+    public void requestAbandoned( AbandonableRequest req )
+    {
+        this.abandoned = true;
+    }
+    
+    public String toString()
+    {
+        return name;
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/event/Evaluator.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/event/Evaluator.java
new file mode 100644
index 0000000..74b2ee6
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/event/Evaluator.java
@@ -0,0 +1,49 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.event;
+
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.shared.ldap.filter.ExprNode;
+
+
+/**
+ * Tests if an entry is eligable for return by evaluating a filter expression on
+ * the candidate.  The evaluation can proceed by applying the filter on the 
+ * attributes of the entry itself or indices can be used for rapid evaluation.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public interface Evaluator
+{
+    /**
+     * Evaluates a candidate to determine if a filter expression selects it.
+     * 
+     * @param node the filter expression to evaluate on the candidate
+     * @param dn the normalized distinguished name of the entry being tested
+     * @param entry the entry to evaluate
+     * @return true if the filter selects the candidate false otherwise
+     * @throws javax.naming.NamingException if there is a database fault during evaluation
+     */
+    boolean evaluate( ExprNode node, String dn, ServerEntry entry ) throws NamingException;
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/event/EventInterceptor.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/event/EventInterceptor.java
new file mode 100644
index 0000000..f711bcb
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/event/EventInterceptor.java
@@ -0,0 +1,510 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.event;
+
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.interceptor.BaseInterceptor;
+import org.apache.directory.server.core.interceptor.NextInterceptor;
+import org.apache.directory.server.core.interceptor.context.AddOperationContext;
+import org.apache.directory.server.core.interceptor.context.DeleteOperationContext;
+import org.apache.directory.server.core.interceptor.context.LookupOperationContext;
+import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
+import org.apache.directory.server.core.interceptor.context.MoveAndRenameOperationContext;
+import org.apache.directory.server.core.interceptor.context.MoveOperationContext;
+import org.apache.directory.server.core.interceptor.context.RenameOperationContext;
+import org.apache.directory.server.core.invocation.Invocation;
+import org.apache.directory.server.core.invocation.InvocationStack;
+import org.apache.directory.server.core.normalization.NormalizingVisitor;
+import org.apache.directory.server.core.partition.PartitionNexus;
+import org.apache.directory.server.core.partition.PartitionNexusProxy;
+import org.apache.directory.server.schema.ConcreteNameComponentNormalizer;
+import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
+import org.apache.directory.server.schema.registries.OidRegistry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.entry.Modification;
+import org.apache.directory.shared.ldap.filter.AndNode;
+import org.apache.directory.shared.ldap.filter.BranchNode;
+import org.apache.directory.shared.ldap.filter.ExprNode;
+import org.apache.directory.shared.ldap.filter.LeafNode;
+import org.apache.directory.shared.ldap.filter.NotNode;
+import org.apache.directory.shared.ldap.filter.ScopeNode;
+import org.apache.directory.shared.ldap.message.AliasDerefMode;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.name.NameComponentNormalizer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.naming.Binding;
+import javax.naming.NamingException;
+import javax.naming.directory.SearchControls;
+import javax.naming.event.EventContext;
+import javax.naming.event.NamespaceChangeListener;
+import javax.naming.event.NamingEvent;
+import javax.naming.event.NamingListener;
+import javax.naming.event.ObjectChangeListener;
+
+
+/**
+ * An interceptor based serivice for notifying NamingListeners of EventContext
+ * and EventDirContext changes.
+ *
+ * @org.apache.xbean.XBean
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class EventInterceptor extends BaseInterceptor
+{
+    private static Logger log = LoggerFactory.getLogger( EventInterceptor.class );
+
+    private PartitionNexus nexus;
+    private Map<NamingListener, Object> sources = new HashMap<NamingListener, Object>();
+    private Evaluator evaluator;
+    private AttributeTypeRegistry attributeRegistry;
+    private NormalizingVisitor visitor;
+
+
+    public void init( DirectoryService directoryService ) throws NamingException
+    {
+        super.init( directoryService );
+
+        OidRegistry oidRegistry = directoryService.getRegistries().getOidRegistry();
+        attributeRegistry = directoryService.getRegistries().getAttributeTypeRegistry();
+        evaluator = new ExpressionEvaluator( oidRegistry, attributeRegistry );
+        nexus = directoryService.getPartitionNexus();
+        NameComponentNormalizer ncn = new ConcreteNameComponentNormalizer( attributeRegistry, oidRegistry );
+        visitor = new NormalizingVisitor( ncn, directoryService.getRegistries() );
+    }
+
+
+    /**
+     * Registers a NamingListener with this service for notification of change.
+     *
+     * @param ctx the context used to register on (the source)
+     * @param name the name of the base/target
+     * @param filter the filter to use for evaluating event triggering
+     * @param searchControls the search controls to use when evaluating triggering
+     * @param namingListener the naming listener to register
+     * @throws NamingException if there are failures adding the naming listener
+     */
+    public void addNamingListener( EventContext ctx, LdapDN name, ExprNode filter, SearchControls searchControls,
+        NamingListener namingListener ) throws NamingException
+    {
+        LdapDN normalizedBaseDn = new LdapDN( name );
+        normalizedBaseDn.normalize( attributeRegistry.getNormalizerMapping() );
+
+        // -------------------------------------------------------------------
+        // must normalize the filter here: need to handle special cases
+        // -------------------------------------------------------------------
+
+        if ( filter.isLeaf() )
+        {
+            LeafNode ln = ( LeafNode ) filter;
+
+            if ( !attributeRegistry.hasAttributeType( ln.getAttribute() ) )
+            {
+                StringBuffer buf = new StringBuffer();
+                buf.append( "undefined filter based on undefined attributeType '" );
+                buf.append( ln.getAttribute() );
+                buf.append( "' not evaluted at all.  Only using scope node." );
+                log.warn( buf.toString() );
+                filter = null;
+            }
+            else
+            {
+                filter.accept( visitor );
+            }
+        }
+        else
+        {
+            filter.accept( visitor );
+
+            // Check that after pruning/normalization we have a valid branch node at the top
+            BranchNode child = ( BranchNode ) filter;
+
+            // If the remaining filter branch node has no children set filter to null
+            if ( child.getChildren().size() == 0 )
+            {
+                log.warn( "Undefined branchnode filter without child nodes not evaluted at all. "
+                    + "Only using scope node." );
+                filter = null;
+            }
+
+            // Now for AND & OR nodes with a single child left replace them with their child
+            if ( child.getChildren().size() == 1 && !( child instanceof NotNode ) )
+            {
+                filter = child.getFirstChild();
+            }
+        }
+
+        ScopeNode scope = new ScopeNode( AliasDerefMode.NEVER_DEREF_ALIASES, normalizedBaseDn.toNormName(),
+            searchControls.getSearchScope() );
+
+        if ( filter != null )
+        {
+            BranchNode and = new AndNode();
+            and.addNode( scope );
+            and.addNode( filter );
+            filter = and;
+        }
+        else
+        {
+            filter = scope;
+        }
+
+        EventSourceRecord rec = new EventSourceRecord( name, filter, ctx, searchControls, namingListener );
+        Object obj = sources.get( namingListener );
+
+        if ( obj == null )
+        {
+            sources.put( namingListener, rec );
+        }
+        else if ( obj instanceof EventSourceRecord )
+        {
+            List<Object> list = new ArrayList<Object>();
+            list.add( obj );
+            list.add( rec );
+            sources.put( namingListener, list );
+        }
+        else if ( obj instanceof List )
+        {
+            //noinspection unchecked
+            List<Object> list = ( List<Object> ) obj;
+            list.add( rec );
+        }
+    }
+
+
+    public void removeNamingListener( EventContext ctx, NamingListener namingListener )
+    {
+        Object obj = sources.get( namingListener );
+
+        if ( obj == null )
+        {
+            return;
+        }
+
+        if ( obj instanceof EventSourceRecord )
+        {
+            sources.remove( namingListener );
+        }
+        else if ( obj instanceof List )
+        {
+            List<EventSourceRecord> list = ( List<EventSourceRecord> ) obj;
+
+            for ( int ii = 0; ii < list.size(); ii++ )
+            {
+                EventSourceRecord rec = list.get( ii );
+                if ( rec.getEventContext() == ctx )
+                {
+                    list.remove( ii );
+                }
+            }
+
+            if ( list.isEmpty() )
+            {
+                sources.remove( namingListener );
+            }
+        }
+
+    }
+
+
+    public void add( NextInterceptor next, AddOperationContext opContext ) throws NamingException
+    {
+        next.add( opContext );
+        //super.add( next, opContext );
+
+        LdapDN name = opContext.getDn();
+        ServerEntry entry = opContext.getEntry();
+
+        Set<EventSourceRecord> selecting = getSelectingSources( name, entry );
+
+        if ( selecting.isEmpty() )
+        {
+            return;
+        }
+
+        Iterator<EventSourceRecord> list = selecting.iterator();
+
+        while ( list.hasNext() )
+        {
+            EventSourceRecord rec = list.next();
+            NamingListener listener = rec.getNamingListener();
+
+            if ( listener instanceof NamespaceChangeListener )
+            {
+                NamespaceChangeListener nclistener = ( NamespaceChangeListener ) listener;
+                Binding binding = new Binding( name.getUpName(), entry, false );
+                nclistener.objectAdded( new NamingEvent( rec.getEventContext(), NamingEvent.OBJECT_ADDED, binding,
+                    null, entry ) );
+            }
+        }
+    }
+
+
+    public void delete( NextInterceptor next, DeleteOperationContext opContext ) throws NamingException
+    {
+        LdapDN name = opContext.getDn();
+        ServerEntry entry = nexus.lookup( new LookupOperationContext( opContext.getRegistries(), name ) );
+
+        next.delete( opContext );
+        //super.delete( next, opContext );
+
+        Set<EventSourceRecord> selecting = getSelectingSources( name, entry );
+
+        if ( selecting.isEmpty() )
+        {
+            return;
+        }
+
+        Iterator<EventSourceRecord> list = selecting.iterator();
+
+        while ( list.hasNext() )
+        {
+            EventSourceRecord rec = ( EventSourceRecord ) list.next();
+            NamingListener listener = rec.getNamingListener();
+
+            if ( listener instanceof NamespaceChangeListener )
+            {
+                NamespaceChangeListener nclistener = ( NamespaceChangeListener ) listener;
+                Binding binding = new Binding( name.getUpName(), entry, false );
+                nclistener.objectRemoved( new NamingEvent( rec.getEventContext(), NamingEvent.OBJECT_REMOVED, null,
+                    binding, entry ) );
+            }
+        }
+    }
+
+
+    private void notifyOnModify( Registries registries, LdapDN name, List<Modification> mods, ServerEntry oriEntry )
+        throws NamingException
+    {
+        ServerEntry entry = nexus.lookup( new LookupOperationContext( registries, name ) );
+        Set<EventSourceRecord> selecting = getSelectingSources( name, entry );
+
+        if ( selecting.isEmpty() )
+        {
+            return;
+        }
+
+        Iterator<EventSourceRecord> list = selecting.iterator();
+
+        while ( list.hasNext() )
+        {
+            EventSourceRecord rec = list.next();
+            NamingListener listener = rec.getNamingListener();
+
+            if ( listener instanceof ObjectChangeListener )
+            {
+                ObjectChangeListener oclistener = ( ObjectChangeListener ) listener;
+                Binding before = new Binding( name.getUpName(), oriEntry, false );
+                Binding after = new Binding( name.getUpName(), entry, false );
+                oclistener.objectChanged( new NamingEvent( rec.getEventContext(), NamingEvent.OBJECT_CHANGED, after,
+                    before, mods ) );
+            }
+        }
+    }
+
+
+    public void modify( NextInterceptor next, ModifyOperationContext opContext ) throws NamingException
+    {
+        Invocation invocation = InvocationStack.getInstance().peek();
+        PartitionNexusProxy proxy = invocation.getProxy();
+        ServerEntry oriEntry = proxy.lookup(
+            new LookupOperationContext( opContext.getRegistries(), opContext.getDn() ),
+            PartitionNexusProxy.LOOKUP_BYPASS );
+
+        next.modify( opContext );
+
+        notifyOnModify( opContext.getRegistries(), opContext.getDn(), opContext.getModItems(), oriEntry );
+    }
+
+
+    private void notifyOnNameChange( Registries registries, LdapDN oldName, LdapDN newName ) throws NamingException
+    {
+        ServerEntry entry = nexus.lookup( new LookupOperationContext( registries, newName ) );
+        Set<EventSourceRecord> selecting = getSelectingSources( oldName, entry );
+
+        if ( selecting.isEmpty() )
+        {
+            return;
+        }
+
+        Iterator<EventSourceRecord> list = selecting.iterator();
+
+        while ( list.hasNext() )
+        {
+            EventSourceRecord rec = list.next();
+            NamingListener listener = rec.getNamingListener();
+
+            if ( listener instanceof NamespaceChangeListener )
+            {
+                NamespaceChangeListener nclistener = ( NamespaceChangeListener ) listener;
+                Binding oldBinding = new Binding( oldName.getUpName(), entry, false );
+                Binding newBinding = new Binding( newName.getUpName(), entry, false );
+                nclistener.objectRenamed( new NamingEvent( rec.getEventContext(), NamingEvent.OBJECT_RENAMED,
+                    newBinding, oldBinding, entry ) );
+            }
+        }
+    }
+
+
+    public void rename( NextInterceptor next, RenameOperationContext opContext ) throws NamingException
+    {
+        next.rename( opContext );
+        //super.rename( next, opContext );
+
+        LdapDN newName = ( LdapDN ) opContext.getDn().clone();
+        newName.remove( newName.size() - 1 );
+        newName.add( opContext.getNewRdn() );
+        newName.normalize( attributeRegistry.getNormalizerMapping() );
+        notifyOnNameChange( opContext.getRegistries(), opContext.getDn(), newName );
+    }
+
+
+    public void moveAndRename( NextInterceptor next, MoveAndRenameOperationContext opContext ) throws NamingException
+    {
+        next.moveAndRename( opContext );
+        //super.moveAndRename( next, opContext );
+
+        LdapDN newName = ( LdapDN ) opContext.getParent().clone();
+        newName.add( opContext.getNewRdn() );
+        notifyOnNameChange( opContext.getRegistries(), opContext.getDn(), newName );
+    }
+
+
+    public void move( NextInterceptor next, MoveOperationContext opContext ) throws NamingException
+    {
+        next.move( opContext );
+        //super.move( next, opContext );
+
+        LdapDN oriChildName = opContext.getDn();
+
+        LdapDN newName = ( LdapDN ) opContext.getParent().clone();
+        newName.add( oriChildName.get( oriChildName.size() - 1 ) );
+        notifyOnNameChange( opContext.getRegistries(), oriChildName, newName );
+    }
+
+
+    Set<EventSourceRecord> getSelectingSources( LdapDN name, ServerEntry entry ) throws NamingException
+    {
+        if ( sources.isEmpty() )
+        {
+            return Collections.EMPTY_SET;
+        }
+
+        Set<EventSourceRecord> selecting = new HashSet<EventSourceRecord>();
+        Iterator<Object> list = sources.values().iterator();
+
+        while ( list.hasNext() )
+        {
+            Object obj = list.next();
+
+            if ( obj instanceof EventSourceRecord )
+            {
+                EventSourceRecord rec = ( EventSourceRecord ) obj;
+
+                if ( evaluator.evaluate( rec.getFilter(), name.toNormName(), entry ) )
+                {
+                    selecting.add( rec );
+                }
+            }
+            else if ( obj instanceof List )
+            {
+                List<EventSourceRecord> records = ( List<EventSourceRecord> ) obj;
+
+                for ( EventSourceRecord rec : records )
+                {
+                    if ( evaluator.evaluate( rec.getFilter(), name.toNormName(), entry ) )
+                    {
+                        selecting.add( rec );
+                    }
+                }
+            }
+            else
+            {
+                throw new IllegalStateException( "Unexpected class type of " + obj.getClass() );
+            }
+        }
+
+        return selecting;
+    }
+
+    class EventSourceRecord
+    {
+        private LdapDN base;
+        private SearchControls controls;
+        private ExprNode filter;
+        private EventContext context;
+        private NamingListener listener;
+
+
+        public EventSourceRecord( LdapDN base, ExprNode filter, EventContext context, SearchControls controls,
+            NamingListener listener )
+        {
+            this.filter = filter;
+            this.context = context;
+            this.base = base;
+            this.controls = controls;
+            this.listener = listener;
+        }
+
+
+        public NamingListener getNamingListener()
+        {
+            return listener;
+        }
+
+
+        public ExprNode getFilter()
+        {
+            return filter;
+        }
+
+
+        public EventContext getEventContext()
+        {
+            return context;
+        }
+
+
+        public LdapDN getBase()
+        {
+            return base;
+        }
+
+
+        public SearchControls getSearchControls()
+        {
+            return controls;
+        }
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/event/ExpressionEvaluator.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/event/ExpressionEvaluator.java
new file mode 100644
index 0000000..b633752
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/event/ExpressionEvaluator.java
@@ -0,0 +1,144 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.event;
+
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
+import org.apache.directory.server.schema.registries.OidRegistry;
+import org.apache.directory.shared.ldap.filter.AndNode;
+import org.apache.directory.shared.ldap.filter.BranchNode;
+import org.apache.directory.shared.ldap.filter.ExprNode;
+import org.apache.directory.shared.ldap.filter.NotNode;
+import org.apache.directory.shared.ldap.filter.OrNode;
+
+
+
+/**
+ * Top level filter expression evaluator implemenation.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class ExpressionEvaluator implements Evaluator
+{
+    /** Leaf Evaluator flyweight use for leaf filter assertions */
+    private LeafEvaluator leafEvaluator;
+
+
+    // ------------------------------------------------------------------------
+    // C O N S T R U C T O R S
+    // ------------------------------------------------------------------------
+
+    /**
+     * Creates a top level Evaluator where leaves are delegated to a leaf node
+     * evaluator which is already provided.
+     *
+     * @param leafEvaluator handles leaf node evaluation.
+     */
+    public ExpressionEvaluator(LeafEvaluator leafEvaluator)
+    {
+        this.leafEvaluator = leafEvaluator;
+    }
+
+
+    /**
+     * Creates a top level Evaluator where leaves are delegated to a leaf node
+     * evaluator which will be created.
+     *
+     * @param oidRegistry the oid reg used for attrID to oid resolution
+     * @param attributeTypeRegistry the attribtype reg used for value comparison
+     */
+    public ExpressionEvaluator(OidRegistry oidRegistry, AttributeTypeRegistry attributeTypeRegistry)
+    {
+        SubstringEvaluator substringEvaluator = null;
+        substringEvaluator = new SubstringEvaluator( oidRegistry, attributeTypeRegistry );
+        leafEvaluator = new LeafEvaluator( oidRegistry, attributeTypeRegistry, substringEvaluator );
+    }
+
+
+    /**
+     * Gets the leaf evaluator used by this top level expression evaluator.
+     *
+     * @return the leaf evaluator used by this top level expression evaluator
+     */
+    public LeafEvaluator getLeafEvaluator()
+    {
+        return leafEvaluator;
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Evaluator.evaluate() implementation
+    // ------------------------------------------------------------------------
+
+    /**
+     * @see Evaluator#evaluate(ExprNode, String, ServerEntry)
+     */
+    public boolean evaluate( ExprNode node, String dn, ServerEntry entry ) throws NamingException
+    {
+        if ( node.isLeaf() )
+        {
+            return leafEvaluator.evaluate( node, dn, entry );
+        }
+
+        BranchNode bnode = ( BranchNode ) node;
+
+        if ( bnode instanceof OrNode )
+        {
+            for ( ExprNode child: bnode.getChildren() )
+            {
+                if ( evaluate( child, dn, entry ) )
+                {
+                    return true;
+                }
+            }
+
+            return false;
+        }
+        else if ( bnode instanceof AndNode )
+        {
+            for ( ExprNode child: bnode.getChildren() )
+            {
+                if ( !evaluate( child, dn, entry ) )
+                {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+        else if ( bnode instanceof NotNode )
+        {
+            if ( null != bnode.getFirstChild() )
+            {
+                return !evaluate( bnode.getFirstChild(), dn, entry );
+            }
+
+            throw new NamingException( "Negation has no child: " + node );
+        }
+        else
+        {
+                throw new NamingException( "Unrecognized branch node operator: " + bnode );
+        }
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/event/LeafEvaluator.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/event/LeafEvaluator.java
new file mode 100644
index 0000000..52d9c78
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/event/LeafEvaluator.java
@@ -0,0 +1,366 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.event;
+
+
+import java.util.Comparator;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
+import org.apache.directory.server.schema.registries.OidRegistry;
+import org.apache.directory.shared.ldap.NotImplementedException;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.entry.Value;
+import org.apache.directory.shared.ldap.filter.ApproximateNode;
+import org.apache.directory.shared.ldap.filter.EqualityNode;
+import org.apache.directory.shared.ldap.filter.ExprNode;
+import org.apache.directory.shared.ldap.filter.ExtensibleNode;
+import org.apache.directory.shared.ldap.filter.GreaterEqNode;
+import org.apache.directory.shared.ldap.filter.LessEqNode;
+import org.apache.directory.shared.ldap.filter.PresenceNode;
+import org.apache.directory.shared.ldap.filter.ScopeNode;
+import org.apache.directory.shared.ldap.filter.SimpleNode;
+import org.apache.directory.shared.ldap.filter.SubstringNode;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.schema.MatchingRule;
+import org.apache.directory.shared.ldap.schema.Normalizer;
+
+
+/**
+ * Evaluates LeafNode assertions on candidates using a database.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class LeafEvaluator implements Evaluator
+{
+    /** equality matching type constant */
+    private static final int EQUALITY_MATCH = 0;
+    /** ordering matching type constant */
+    private static final int ORDERING_MATCH = 1;
+    /** substring matching type constant */
+    private static final int SUBSTRING_MATCH = 3;
+
+    /** Oid Registry used to translate attributeIds to OIDs */
+    private OidRegistry oidRegistry;
+    /** AttributeType registry needed for normalizing and comparing values */
+    private AttributeTypeRegistry attributeTypeRegistry;
+    /** Substring node evaluator we depend on */
+    private SubstringEvaluator substringEvaluator;
+    /** ScopeNode evaluator we depend on */
+    private ScopeEvaluator scopeEvaluator;
+
+    /** Constants used for comparisons */
+    private static final boolean COMPARE_GREATER = true;
+    private static final boolean COMPARE_LESSER = false;
+
+
+    /**
+     * Creates a leaf expression node evaluator.
+     *
+     * @param substringEvaluator
+     */
+    public LeafEvaluator( OidRegistry oidRegistry, AttributeTypeRegistry attributeTypeRegistry,
+        SubstringEvaluator substringEvaluator )
+    {
+        this.oidRegistry = oidRegistry;
+        this.attributeTypeRegistry = attributeTypeRegistry;
+        this.scopeEvaluator = new ScopeEvaluator();
+        this.substringEvaluator = substringEvaluator;
+    }
+
+
+    public ScopeEvaluator getScopeEvaluator()
+    {
+        return scopeEvaluator;
+    }
+
+
+    public SubstringEvaluator getSubstringEvaluator()
+    {
+        return substringEvaluator;
+    }
+
+
+    /**
+     * @see Evaluator#evaluate(ExprNode, String, ServerEntry)
+     */
+    public boolean evaluate( ExprNode node, String dn, ServerEntry entry ) throws NamingException
+    {
+        if ( node instanceof ScopeNode )
+        {
+            return scopeEvaluator.evaluate( node, dn, entry );
+        }
+
+        if ( node instanceof PresenceNode )
+        {
+            String attrId = ( ( PresenceNode ) node ).getAttribute();
+            return evalPresence( attrId, entry );
+        }
+        else if ( ( node instanceof EqualityNode ) || ( node instanceof ApproximateNode ) )
+        {
+            return evalEquality( ( EqualityNode ) node, entry );
+        }
+        else if ( node instanceof GreaterEqNode )
+        {
+            return evalGreaterOrLesser( ( GreaterEqNode ) node, entry, COMPARE_GREATER );
+        }
+        else if ( node instanceof LessEqNode )
+        {
+            return evalGreaterOrLesser( ( LessEqNode ) node, entry, COMPARE_LESSER );
+        }
+        else if ( node instanceof SubstringNode )
+        {
+            return substringEvaluator.evaluate( node, dn, entry );
+        }
+        else if ( node instanceof ExtensibleNode )
+        {
+            throw new NotImplementedException();
+        }
+        else
+        {
+            throw new NamingException( "Unrecognized leaf node type: " + node );
+        }
+    }
+
+
+    /**
+     * Evaluates a simple greater than or less than attribute value assertion on
+     * a perspective candidate.
+     * 
+     * @param node the greater than or less than node to evaluate
+     * @param entry the perspective candidate
+     * @param isGreater true if it is a greater than or equal to comparison,
+     *      false if it is a less than or equal to comparison.
+     * @return the ava evaluation on the perspective candidate
+     * @throws javax.naming.NamingException if there is a database access failure
+     */
+    private boolean evalGreaterOrLesser( SimpleNode node, ServerEntry entry, boolean isGreaterOrLesser )
+        throws NamingException
+    {
+        String attrId = node.getAttribute();
+
+        // get the attribute associated with the node
+        AttributeType type = attributeTypeRegistry.lookup( oidRegistry.getOid( attrId ) );
+        EntryAttribute attr = entry.get( type );
+
+        // If we do not have the attribute just return false
+        if ( null == attr )
+        {
+            return false;
+        }
+
+        /*
+         * We need to iterate through all values and for each value we normalize
+         * and use the comparator to determine if a match exists.
+         */
+        Normalizer normalizer = getNormalizer( attrId );
+        Comparator comparator = getComparator( attrId );
+        Object filterValue = normalizer.normalize( node.getValue() );
+
+        /*
+         * Cheaper to not check isGreater in one loop - better to separate
+         * out into two loops which you choose to execute based on isGreater
+         */
+        if ( isGreaterOrLesser == COMPARE_GREATER )
+        {
+            for ( Value<?> value : attr )
+            {
+                Object normValue = normalizer.normalize( value );
+
+                // Found a value that is greater than or equal to the ava value
+                if ( 0 >= comparator.compare( normValue, filterValue ) )
+                {
+                    return true;
+                }
+            }
+        }
+        else
+        {
+            for ( Value<?> value : attr )
+            {
+                Object normValue = normalizer.normalize( value );
+
+                // Found a value that is less than or equal to the ava value
+                if ( 0 <= comparator.compare( normValue, filterValue ) )
+                {
+                    return true;
+                }
+            }
+        }
+
+        // no match so return false
+        return false;
+    }
+
+
+    /**
+     * Evaluates a simple presence attribute value assertion on a perspective
+     * candidate.
+     * 
+     * @param attrId the name of the attribute tested for presence 
+     * @param entry the perspective candidate
+     * @return the ava evaluation on the perspective candidate
+     */
+    private boolean evalPresence( String attrId, ServerEntry entry ) throws NamingException
+    {
+        if ( entry == null )
+        {
+            return false;
+        }
+
+        return null != entry.get( attrId );
+    }
+
+
+    /**
+     * Evaluates a simple equality attribute value assertion on a perspective
+     * candidate.
+     *
+     * @param node the equality node to evaluate
+     * @param entry the perspective candidate
+     * @return the ava evaluation on the perspective candidate
+     * @throws javax.naming.NamingException if there is a database access failure
+     */
+    private boolean evalEquality( EqualityNode node, ServerEntry entry ) throws NamingException
+    {
+        Normalizer normalizer = getNormalizer( node.getAttribute() );
+        Comparator comparator = getComparator( node.getAttribute() );
+
+        // get the attribute associated with the node
+        EntryAttribute attr = entry.get( node.getAttribute() );
+
+        // If we do not have the attribute just return false
+        if ( null == attr )
+        {
+            return false;
+        }
+
+        // check if AVA value exists in attribute
+        if ( attr.contains( node.getValue() ) )
+        {
+            return true;
+        }
+
+        // get the normalized AVA filter value
+        Object filterValue = normalizer.normalize( node.getValue().get() );
+
+        // check if the normalized value is present
+        if ( filterValue instanceof String )
+        {
+            if ( attr.contains( ( String ) filterValue ) )
+            {
+                return true;
+            }
+        }
+        else if ( attr.contains( ( byte[] ) filterValue ) )
+        {
+            return true;
+        }
+
+        /*
+         * We need to now iterate through all values because we could not get
+         * a lookup to work.  For each value we normalize and use the comparator
+         * to determine if a match exists.
+         */
+        for ( Value<?> value : attr )
+        {
+            Object normValue = normalizer.normalize( value.get() );
+
+            if ( 0 == comparator.compare( normValue, filterValue ) )
+            {
+                return true;
+            }
+        }
+
+        // no match so return false
+        return false;
+    }
+
+
+    /**
+     * Gets the comparator for equality matching.
+     *
+     * @param attrId the attribute identifier
+     * @return the comparator for equality matching
+     * @throws javax.naming.NamingException if there is a failure
+     */
+    private Comparator getComparator( String attrId ) throws NamingException
+    {
+        MatchingRule mrule = getMatchingRule( attrId, EQUALITY_MATCH );
+        return mrule.getComparator();
+    }
+
+
+    /**
+     * Gets the normalizer for equality matching.
+     *
+     * @param attrId the attribute identifier
+     * @return the normalizer for equality matching
+     * @throws javax.naming.NamingException if there is a failure
+     */
+    private Normalizer getNormalizer( String attrId ) throws NamingException
+    {
+        MatchingRule mrule = getMatchingRule( attrId, EQUALITY_MATCH );
+        return mrule.getNormalizer();
+    }
+
+
+    /**
+     * Gets the matching rule for an attributeType.
+     *
+     * @param attrId the attribute identifier
+     * @return the matching rule
+     * @throws javax.naming.NamingException if there is a failure
+     */
+    private MatchingRule getMatchingRule( String attrId, int matchType ) throws NamingException
+    {
+        MatchingRule mrule = null;
+        String oid = oidRegistry.getOid( attrId );
+        AttributeType type = attributeTypeRegistry.lookup( oid );
+
+        switch ( matchType )
+        {
+            case ( EQUALITY_MATCH ):
+                mrule = type.getEquality();
+                break;
+
+            case ( SUBSTRING_MATCH ):
+                mrule = type.getSubstr();
+                break;
+
+            case ( ORDERING_MATCH ):
+                mrule = type.getOrdering();
+                break;
+
+            default:
+                throw new NamingException( "Unknown match type: " + matchType );
+        }
+
+        if ( ( mrule == null ) && ( matchType != EQUALITY_MATCH ) )
+        {
+            mrule = type.getEquality();
+        }
+
+        return mrule;
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/event/ScopeEvaluator.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/event/ScopeEvaluator.java
new file mode 100644
index 0000000..6cf5881
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/event/ScopeEvaluator.java
@@ -0,0 +1,72 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.event;
+
+
+import javax.naming.NamingException;
+import javax.naming.directory.SearchControls;
+
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.shared.ldap.filter.ExprNode;
+import org.apache.directory.shared.ldap.filter.ScopeNode;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+
+/**
+ * Evaluates ScopeNode assertions on candidates using a database.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class ScopeEvaluator implements Evaluator
+{
+    public ScopeEvaluator()
+    {
+    }
+
+
+    /**
+     * @see Evaluator#evaluate(ExprNode , String, ServerEntry)
+     */
+    public boolean evaluate( ExprNode node, String dn, ServerEntry record ) throws NamingException
+    {
+        ScopeNode snode = ( ScopeNode ) node;
+
+        switch ( snode.getScope() )
+        {
+            case ( SearchControls.OBJECT_SCOPE  ):
+                return dn.equals( snode.getBaseDn() );
+            
+            case ( SearchControls.ONELEVEL_SCOPE  ):
+                if ( dn.endsWith( snode.getBaseDn() ) )
+                {
+                    LdapDN candidateDn = new LdapDN( dn );
+                    LdapDN scopeDn = new LdapDN( snode.getBaseDn() );
+                    return ( scopeDn.size() + 1 ) == candidateDn.size();
+                }
+            
+            case ( SearchControls.SUBTREE_SCOPE  ):
+                return dn.endsWith( snode.getBaseDn() );
+            
+            default:
+                throw new NamingException( "Unrecognized search scope!" );
+        }
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/event/SubstringEvaluator.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/event/SubstringEvaluator.java
new file mode 100644
index 0000000..9136d85
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/event/SubstringEvaluator.java
@@ -0,0 +1,129 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.event;
+
+
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
+import org.apache.directory.server.schema.registries.OidRegistry;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.entry.Value;
+import org.apache.directory.shared.ldap.filter.ExprNode;
+import org.apache.directory.shared.ldap.filter.SubstringNode;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.schema.MatchingRule;
+import org.apache.directory.shared.ldap.schema.Normalizer;
+
+
+/**
+ * Evaluates substring filter assertions on an entry.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class SubstringEvaluator implements Evaluator
+{
+    /** Oid Registry used to translate attributeIds to OIDs */
+    private OidRegistry oidRegistry;
+    /** AttributeType registry needed for normalizing and comparing values */
+    private AttributeTypeRegistry attributeTypeRegistry;
+
+
+    /**
+     * Creates a new SubstringEvaluator for substring expressions.
+     *
+     * @param oidRegistry the OID registry for name to OID mapping
+     * @param attributeTypeRegistry the attributeType registry
+     */
+    public SubstringEvaluator(OidRegistry oidRegistry, AttributeTypeRegistry attributeTypeRegistry)
+    {
+        this.oidRegistry = oidRegistry;
+        this.attributeTypeRegistry = attributeTypeRegistry;
+    }
+
+
+    /**
+     * @see Evaluator#evaluate( ExprNode, String, ServerEntry )
+     */
+    public boolean evaluate( ExprNode node, String dn, ServerEntry entry ) throws NamingException
+    {
+        Pattern regex = null;
+        SubstringNode snode = (SubstringNode)node;
+        String oid = oidRegistry.getOid( snode.getAttribute() );
+        AttributeType type = attributeTypeRegistry.lookup( oid );
+        MatchingRule matchingRule = type.getSubstr();
+        
+        if ( matchingRule == null )
+        {
+            matchingRule = type.getEquality();
+        }
+        
+        Normalizer normalizer = matchingRule.getNormalizer();
+        
+
+        // get the attribute
+        EntryAttribute attr = entry.get( snode.getAttribute() );
+
+        // if the attribute does not exist just return false
+        if ( null == attr )
+        {
+            return false;
+        }
+
+        // compile the regular expression to search for a matching attribute
+        try
+        {
+            regex = snode.getRegex( normalizer );
+        }
+        catch ( PatternSyntaxException pse )
+        {
+            NamingException ne = new NamingException( "SubstringNode '" + node + "' had " + "incorrect syntax" );
+            ne.setRootCause( pse );
+            throw ne;
+        }
+
+        /*
+         * Cycle through the attribute values testing normalized version 
+         * obtained from using the substring matching rule's normalizer.
+         * The test uses the comparator obtained from the appropriate 
+         * substring matching rule.
+         */
+
+        for ( Value<?> value: attr )
+        {
+            String normValue = ( String ) normalizer.normalize( value );
+
+            // Once match is found cleanup and return true
+
+            if ( regex.matcher( normValue ).matches() )
+            {
+                return true;
+            }
+        }
+
+        // we fell through so a match was not found - assertion was false.
+        return false;
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/exception/ExceptionInterceptor.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/exception/ExceptionInterceptor.java
new file mode 100644
index 0000000..ebdfab9
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/exception/ExceptionInterceptor.java
@@ -0,0 +1,588 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.exception;
+
+import org.apache.commons.collections.map.LRUMap;
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.entry.ServerAttribute;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerSearchResult;
+import org.apache.directory.server.core.interceptor.BaseInterceptor;
+import org.apache.directory.server.core.interceptor.NextInterceptor;
+import org.apache.directory.server.core.interceptor.context.AddOperationContext;
+import org.apache.directory.server.core.interceptor.context.DeleteOperationContext;
+import org.apache.directory.server.core.interceptor.context.EntryOperationContext;
+import org.apache.directory.server.core.interceptor.context.GetMatchedNameOperationContext;
+import org.apache.directory.server.core.interceptor.context.ListOperationContext;
+import org.apache.directory.server.core.interceptor.context.LookupOperationContext;
+import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
+import org.apache.directory.server.core.interceptor.context.MoveAndRenameOperationContext;
+import org.apache.directory.server.core.interceptor.context.MoveOperationContext;
+import org.apache.directory.server.core.interceptor.context.RenameOperationContext;
+import org.apache.directory.server.core.interceptor.context.SearchOperationContext;
+import org.apache.directory.server.core.invocation.Invocation;
+import org.apache.directory.server.core.invocation.InvocationStack;
+import org.apache.directory.server.core.partition.Partition;
+import org.apache.directory.server.core.partition.PartitionNexus;
+import org.apache.directory.server.core.partition.PartitionNexusProxy;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.entry.Modification;
+import org.apache.directory.shared.ldap.entry.ModificationOperation;
+import org.apache.directory.shared.ldap.entry.Value;
+import org.apache.directory.shared.ldap.exception.LdapAttributeInUseException;
+import org.apache.directory.shared.ldap.exception.LdapContextNotEmptyException;
+import org.apache.directory.shared.ldap.exception.LdapNameAlreadyBoundException;
+import org.apache.directory.shared.ldap.exception.LdapNameNotFoundException;
+import org.apache.directory.shared.ldap.exception.LdapNamingException;
+import org.apache.directory.shared.ldap.exception.LdapOperationNotSupportedException;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.schema.OidNormalizer;
+import org.apache.directory.shared.ldap.util.EmptyEnumeration;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * An {@link org.apache.directory.server.core.interceptor.Interceptor} that detects any operations that breaks integrity
+ * of {@link Partition} and terminates the current invocation chain by
+ * throwing a {@link NamingException}. Those operations include when an entry
+ * already exists at a DN and is added once again to the same DN.
+ *
+ * @org.apache.xbean.XBean
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class ExceptionInterceptor extends BaseInterceptor
+{
+    /** The global registries */
+    private Registries registries;
+    
+    private PartitionNexus nexus;
+    private LdapDN subschemSubentryDn;
+    
+    /**
+     * The OIDs normalizer map
+     */
+    private Map<String, OidNormalizer> normalizerMap;
+    
+    /**
+     * A cache to store entries which are not aliases. 
+     * It's a speedup, we will be able to avoid backend lookups.
+     * 
+     * Note that the backend also use a cache mechanism, but for performance gain, it's good 
+     * to manage a cache here. The main problem is that when a user modify the parent, we will
+     * have to update it at three different places :
+     * - in the backend,
+     * - in the partition cache,
+     * - in this cache.
+     * 
+     * The update of the backend and partition cache is already correctly handled, so we will
+     * just have to offer an access to refresh the local cache. This should be done in 
+     * delete, modify and move operations.
+     * 
+     * We need to be sure that frequently used DNs are always in cache, and not discarded.
+     * We will use a LRU cache for this purpose. 
+     */ 
+    private final LRUMap notAliasCache = new LRUMap( DEFAULT_CACHE_SIZE );
+
+    /** Declare a default for this cache. 100 entries seems to be enough */
+    private static final int DEFAULT_CACHE_SIZE = 100;
+
+    
+    /**
+     * Creates an interceptor that is also the exception handling service.
+     */
+    public ExceptionInterceptor()
+    {
+    }
+
+
+    public void init( DirectoryService directoryService ) throws NamingException
+    {
+        nexus = directoryService.getPartitionNexus();
+        normalizerMap = directoryService.getRegistries().getAttributeTypeRegistry().getNormalizerMapping();
+        Value<?> attr = nexus.getRootDSE( null ).get( SchemaConstants.SUBSCHEMA_SUBENTRY_AT ).get();
+        subschemSubentryDn = new LdapDN( ( String ) attr.get() );
+        subschemSubentryDn.normalize( normalizerMap );
+        registries = directoryService.getRegistries();
+    }
+
+
+    public void destroy()
+    {
+    }
+
+    /**
+     * In the pre-invocation state this interceptor method checks to see if the entry to be added already exists.  If it
+     * does an exception is raised.
+     */
+    public void add( NextInterceptor nextInterceptor, AddOperationContext opContext )
+        throws NamingException
+    {
+        LdapDN name = opContext.getDn();
+        
+        if ( subschemSubentryDn.getNormName().equals( name.getNormName() ) )
+        {
+            throw new LdapNameAlreadyBoundException( 
+                "The global schema subentry cannot be added since it exists by default." );
+        }
+        
+        // check if the entry already exists
+        if ( nextInterceptor.hasEntry( new EntryOperationContext( registries, name ) ) )
+        {
+            NamingException ne = new LdapNameAlreadyBoundException( name.getUpName() + " already exists!" );
+            ne.setResolvedName( new LdapDN( name.getUpName() ) );
+            throw ne;
+        }
+
+        LdapDN parentDn = ( LdapDN ) name.clone();
+        parentDn.remove( name.size() - 1 );
+
+        // check if we're trying to add to a parent that is an alias
+        boolean notAnAlias;
+        
+        synchronized( notAliasCache )
+        {
+            notAnAlias = notAliasCache.containsKey( parentDn.getNormName() );
+        }
+        
+        if ( ! notAnAlias )
+        {
+            // We don't know if the parent is an alias or not, so we will launch a 
+            // lookup, and update the cache if it's not an alias
+            ServerEntry attrs;
+            
+            try
+            {
+                attrs = nextInterceptor.lookup( new LookupOperationContext( registries, parentDn ) );
+            }
+            catch ( Exception e )
+            {
+                LdapNameNotFoundException e2 = new LdapNameNotFoundException( "Parent " + parentDn.getUpName() 
+                    + " not found" );
+                e2.setResolvedName( new LdapDN( nexus.getMatchedName( new GetMatchedNameOperationContext( registries, parentDn ) ).getUpName() ) );
+                throw e2;
+            }
+            
+            EntryAttribute objectClass = attrs.get( SchemaConstants.OBJECT_CLASS_AT );
+            
+            if ( objectClass.contains( SchemaConstants.ALIAS_OC ) )
+            {
+                String msg = "Attempt to add entry to alias '" + name.getUpName() + "' not allowed.";
+                ResultCodeEnum rc = ResultCodeEnum.ALIAS_PROBLEM;
+                NamingException e = new LdapNamingException( msg, rc );
+                e.setResolvedName( new LdapDN( parentDn.getUpName() ) );
+                throw e;
+            }
+            else
+            {
+                synchronized ( notAliasCache )
+                {
+                    notAliasCache.put( parentDn.getNormName(), parentDn );
+                }
+            }
+        }
+
+        nextInterceptor.add( opContext );
+    }
+
+
+    /**
+     * Checks to make sure the entry being deleted exists, and has no children, otherwise throws the appropriate
+     * LdapException.
+     */
+    public void delete( NextInterceptor nextInterceptor, DeleteOperationContext opContext ) throws NamingException
+    {
+        LdapDN name = opContext.getDn();
+        
+        if ( name.getNormName().equalsIgnoreCase( subschemSubentryDn.getNormName() ) )
+        {
+            throw new LdapOperationNotSupportedException( 
+                "Can not allow the deletion of the subschemaSubentry (" + 
+                subschemSubentryDn + ") for the global schema.",
+                ResultCodeEnum.UNWILLING_TO_PERFORM );
+        }
+        
+        // check if entry to delete exists
+        String msg = "Attempt to delete non-existant entry: ";
+        assertHasEntry( nextInterceptor, msg, name );
+
+        // check if entry to delete has children (only leaves can be deleted)
+        boolean hasChildren = false;
+        NamingEnumeration<ServerSearchResult> list = nextInterceptor.list( new ListOperationContext( registries, name ) );
+        
+        if ( list.hasMore() )
+        {
+            hasChildren = true;
+        }
+
+        list.close();
+        
+        if ( hasChildren )
+        {
+            LdapContextNotEmptyException e = new LdapContextNotEmptyException();
+            e.setResolvedName( new LdapDN( name.getUpName() ) );
+            throw e;
+        }
+
+        synchronized( notAliasCache )
+        {
+            if ( notAliasCache.containsKey( name.getNormName() ) )
+            {
+                notAliasCache.remove( name.getNormName() );
+            }
+        }
+        
+        nextInterceptor.delete( opContext );
+    }
+
+
+    /**
+     * Checks to see the base being searched exists, otherwise throws the appropriate LdapException.
+     */
+    public NamingEnumeration<ServerSearchResult> list( NextInterceptor nextInterceptor, ListOperationContext opContext ) throws NamingException
+    {
+        if ( opContext.getDn().getNormName().equals( subschemSubentryDn.getNormName() ) )
+        {
+            // there is nothing under the schema subentry
+            return new EmptyEnumeration<ServerSearchResult>();
+        }
+        
+        // check if entry to search exists
+        String msg = "Attempt to search under non-existant entry: ";
+        assertHasEntry( nextInterceptor, msg, opContext.getDn() );
+
+        return nextInterceptor.list( opContext );
+    }
+
+
+    /**
+     * Checks to see the base being searched exists, otherwise throws the appropriate LdapException.
+     */
+    public ServerEntry lookup( NextInterceptor nextInterceptor, LookupOperationContext opContext ) throws NamingException
+    {
+        if ( opContext.getDn().getNormName().equals( subschemSubentryDn.getNormName() ) )
+        {
+            return nexus.getRootDSE( null );
+        }
+        
+        // check if entry to lookup exists
+        String msg = "Attempt to lookup non-existant entry: ";
+        assertHasEntry( nextInterceptor, msg, opContext.getDn() );
+
+        return nextInterceptor.lookup( opContext );
+    }
+
+
+    /**
+     * Checks to see the entry being modified exists, otherwise throws the appropriate LdapException.
+     */
+    public void modify( NextInterceptor nextInterceptor, ModifyOperationContext opContext )
+        throws NamingException
+    {
+        // check if entry to modify exists
+        String msg = "Attempt to modify non-existant entry: ";
+
+        // handle operations against the schema subentry in the schema service
+        // and never try to look it up in the nexus below
+        if ( opContext.getDn().getNormName().equalsIgnoreCase( subschemSubentryDn.getNormName() ) )
+        {
+            nextInterceptor.modify( opContext );
+            return;
+        }
+        
+        assertHasEntry( nextInterceptor, msg, opContext.getDn() );
+
+        ServerEntry entry = nexus.lookup( new LookupOperationContext( registries, opContext.getDn() ) );
+        List<Modification> items = opContext.getModItems();
+
+        for ( Modification item : items )
+        {
+            if ( item.getOperation() == ModificationOperation.ADD_ATTRIBUTE )
+            {
+                EntryAttribute modAttr = (ServerAttribute)item.getAttribute();
+                EntryAttribute entryAttr = entry.get( modAttr.getId() );
+
+                if ( entryAttr != null )
+                {
+                    for ( Value<?> value:modAttr )
+                    {
+                        if ( entryAttr.contains( value ) )
+                        {
+                            throw new LdapAttributeInUseException( "Trying to add existing value '" + value
+                                    + "' to attribute " + modAttr.getId() );
+                        }
+                    }
+                }
+            }
+        }
+
+        // Let's assume that the new modified entry may be an alias,
+        // but we don't want to check that now...
+        // We will simply remove the DN from the NotAlias cache.
+        // It would be smarter to check the modified attributes, but
+        // it would also be more complex.
+        synchronized( notAliasCache )
+        {
+            if ( notAliasCache.containsKey( opContext.getDn().getNormName() ) )
+            {
+                notAliasCache.remove( opContext.getDn().getNormName() );
+            }
+        }
+
+        nextInterceptor.modify( opContext );
+    }
+
+    /**
+     * Checks to see the entry being renamed exists, otherwise throws the appropriate LdapException.
+     */
+    public void rename( NextInterceptor nextInterceptor, RenameOperationContext opContext )
+        throws NamingException
+    {
+        LdapDN dn = opContext.getDn();
+        
+        if ( dn.getNormName().equalsIgnoreCase( subschemSubentryDn.getNormName() ) )
+        {
+            throw new LdapOperationNotSupportedException( 
+                "Can not allow the renaming of the subschemaSubentry (" + 
+                subschemSubentryDn + ") for the global schema: it is fixed at " + subschemSubentryDn,
+                ResultCodeEnum.UNWILLING_TO_PERFORM );
+        }
+        
+        // check if entry to rename exists
+        String msg = "Attempt to rename non-existant entry: ";
+        assertHasEntry( nextInterceptor, msg, dn );
+
+        // check to see if target entry exists
+        LdapDN newDn = ( LdapDN ) dn.clone();
+        newDn.remove( dn.size() - 1 );
+        newDn.add( opContext.getNewRdn() );
+        newDn.normalize( normalizerMap );
+        
+        if ( nextInterceptor.hasEntry( new EntryOperationContext( registries, newDn ) ) )
+        {
+            LdapNameAlreadyBoundException e;
+            e = new LdapNameAlreadyBoundException( "target entry " + newDn.getUpName() + " already exists!" );
+            e.setResolvedName( new LdapDN( newDn.getUpName() ) );
+            throw e;
+        }
+
+        // Remove the previous entry from the notAnAlias cache
+        synchronized( notAliasCache )
+        {
+            if ( notAliasCache.containsKey( dn.getNormName() ) )
+            {
+                notAliasCache.remove( dn.getNormName() );
+            }
+        }
+
+        nextInterceptor.rename( opContext );
+    }
+
+
+    /**
+     * Checks to see the entry being moved exists, and so does its parent, otherwise throws the appropriate
+     * LdapException.
+     */
+    public void move( NextInterceptor nextInterceptor, MoveOperationContext opContext ) throws NamingException
+    {
+        LdapDN oriChildName = opContext.getDn();
+        LdapDN newParentName = opContext.getParent();
+        
+        if ( oriChildName.getNormName().equalsIgnoreCase( subschemSubentryDn.getNormName() ) )
+        {
+            throw new LdapOperationNotSupportedException( 
+                "Can not allow the move of the subschemaSubentry (" + 
+                subschemSubentryDn + ") for the global schema: it is fixed at " + subschemSubentryDn,
+                ResultCodeEnum.UNWILLING_TO_PERFORM );
+        }
+        
+        // check if child to move exists
+        String msg = "Attempt to move to non-existant parent: ";
+        assertHasEntry( nextInterceptor, msg, oriChildName );
+
+        // check if parent to move to exists
+        msg = "Attempt to move to non-existant parent: ";
+        assertHasEntry( nextInterceptor, msg, newParentName );
+
+        // check to see if target entry exists
+        String rdn = oriChildName.get( oriChildName.size() - 1 );
+        LdapDN target = ( LdapDN ) newParentName.clone();
+        target.add( rdn );
+        
+        if ( nextInterceptor.hasEntry( new EntryOperationContext( registries, target ) ) )
+        {
+            // we must calculate the resolved name using the user provided Rdn value
+            String upRdn = new LdapDN( oriChildName.getUpName() ).get( oriChildName.size() - 1 );
+            LdapDN upTarget = ( LdapDN ) newParentName.clone();
+            upTarget.add( upRdn );
+
+            LdapNameAlreadyBoundException e;
+            e = new LdapNameAlreadyBoundException( "target entry " + upTarget.getUpName() + " already exists!" );
+            e.setResolvedName( new LdapDN( upTarget.getUpName() ) );
+            throw e;
+        }
+
+        // Remove the original entry from the NotAlias cache, if needed
+        synchronized( notAliasCache )
+        {
+            if ( notAliasCache.containsKey( oriChildName.getNormName() ) )
+            {
+                notAliasCache.remove( oriChildName.getNormName() );
+            }
+        }
+                
+        nextInterceptor.move( opContext );
+    }
+
+
+    /**
+     * Checks to see the entry being moved exists, and so does its parent, otherwise throws the appropriate
+     * LdapException.
+     */
+    public void moveAndRename( NextInterceptor nextInterceptor, MoveAndRenameOperationContext opContext ) throws NamingException
+    {
+        LdapDN oriChildName = opContext.getDn();
+        LdapDN parent = opContext.getParent();
+
+        if ( oriChildName.getNormName().equalsIgnoreCase( subschemSubentryDn.getNormName() ) )
+        {
+            throw new LdapOperationNotSupportedException( 
+                "Can not allow the move of the subschemaSubentry (" + 
+                subschemSubentryDn + ") for the global schema: it is fixed at " + subschemSubentryDn,
+                ResultCodeEnum.UNWILLING_TO_PERFORM );
+        }
+        
+        // check if child to move exists
+        String msg = "Attempt to move to non-existant parent: ";
+        assertHasEntry( nextInterceptor, msg, oriChildName );
+
+        // check if parent to move to exists
+        msg = "Attempt to move to non-existant parent: ";
+        assertHasEntry( nextInterceptor, msg, parent );
+
+        // check to see if target entry exists
+        LdapDN target = ( LdapDN ) parent.clone();
+        target.add( opContext.getNewRdn() );
+
+        if ( nextInterceptor.hasEntry( new EntryOperationContext( registries, target ) ) )
+        {
+            // we must calculate the resolved name using the user provided Rdn value
+            LdapDN upTarget = ( LdapDN ) parent.clone();
+            upTarget.add( opContext.getNewRdn() );
+
+            LdapNameAlreadyBoundException e;
+            e = new LdapNameAlreadyBoundException( "target entry " + upTarget.getUpName() + " already exists!" );
+            e.setResolvedName( new LdapDN( upTarget.getUpName() ) );
+            throw e;
+        }
+
+        // Remove the original entry from the NotAlias cache, if needed
+        synchronized( notAliasCache )
+        {
+            if ( notAliasCache.containsKey( oriChildName.getNormName() ) )
+            {
+                notAliasCache.remove( oriChildName.getNormName() );
+            }
+        }
+        
+        nextInterceptor.moveAndRename( opContext );
+    }
+
+
+    /**
+     * Checks to see the entry being searched exists, otherwise throws the appropriate LdapException.
+     */
+    public NamingEnumeration<ServerSearchResult> search( NextInterceptor nextInterceptor, SearchOperationContext opContext ) throws NamingException
+    {
+        LdapDN base = opContext.getDn();
+
+        try
+        {
+            NamingEnumeration<ServerSearchResult> result =  nextInterceptor.search( opContext );
+            
+            if ( ! result.hasMoreElements() )
+            {
+                if ( !base.isEmpty() && !( subschemSubentryDn.toNormName() ).equalsIgnoreCase( base.toNormName() ) )
+                {
+                    // We just check that the entry exists only if we didn't found any entry
+                    assertHasEntry( nextInterceptor, "Attempt to search under non-existant entry:" , base );
+                }
+            }
+
+            return result;
+        }
+        catch ( NamingException ne )
+        {
+            String msg = "Attempt to search under non-existant entry: ";
+            assertHasEntry( nextInterceptor, msg, base );
+            throw ne;
+        }
+    }
+
+
+    /**
+     * Asserts that an entry is present and as a side effect if it is not, creates a LdapNameNotFoundException, which is
+     * used to set the before exception on the invocation - eventually the exception is thrown.
+     *
+     * @param msg        the message to prefix to the distinguished name for explanation
+     * @param dn         the distinguished name of the entry that is asserted
+     * @throws NamingException if the entry does not exist
+     * @param nextInterceptor the next interceptor in the chain
+     */
+    private void assertHasEntry( NextInterceptor nextInterceptor, String msg, LdapDN dn ) throws NamingException
+    {
+        if ( subschemSubentryDn.getNormName().equals( dn.getNormName() ) )
+        {
+            return;
+        }
+        
+        Invocation invocation = InvocationStack.getInstance().peek();
+        PartitionNexusProxy proxy = invocation.getProxy();
+        
+        if ( !nextInterceptor.hasEntry( new EntryOperationContext( registries, dn ) ) )
+        {
+            LdapNameNotFoundException e;
+
+            if ( msg != null )
+            {
+                e = new LdapNameNotFoundException( msg + dn.getUpName() );
+            }
+            else
+            {
+                e = new LdapNameNotFoundException( dn.getUpName() );
+            }
+
+            e.setResolvedName( 
+                new LdapDN( 
+                    proxy.getMatchedName( 
+                        new GetMatchedNameOperationContext( registries, dn ) ).getUpName() ) );
+            throw e;
+        }
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/BaseInterceptor.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/BaseInterceptor.java
new file mode 100644
index 0000000..fff9a1b
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/BaseInterceptor.java
@@ -0,0 +1,243 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.interceptor;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.authn.LdapPrincipal;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerSearchResult;
+import org.apache.directory.server.core.interceptor.context.AddContextPartitionOperationContext;
+import org.apache.directory.server.core.interceptor.context.AddOperationContext;
+import org.apache.directory.server.core.interceptor.context.BindOperationContext;
+import org.apache.directory.server.core.interceptor.context.CompareOperationContext;
+import org.apache.directory.server.core.interceptor.context.DeleteOperationContext;
+import org.apache.directory.server.core.interceptor.context.EntryOperationContext;
+import org.apache.directory.server.core.interceptor.context.GetMatchedNameOperationContext;
+import org.apache.directory.server.core.interceptor.context.GetRootDSEOperationContext;
+import org.apache.directory.server.core.interceptor.context.GetSuffixOperationContext;
+import org.apache.directory.server.core.interceptor.context.ListOperationContext;
+import org.apache.directory.server.core.interceptor.context.ListSuffixOperationContext;
+import org.apache.directory.server.core.interceptor.context.LookupOperationContext;
+import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
+import org.apache.directory.server.core.interceptor.context.MoveAndRenameOperationContext;
+import org.apache.directory.server.core.interceptor.context.MoveOperationContext;
+import org.apache.directory.server.core.interceptor.context.RemoveContextPartitionOperationContext;
+import org.apache.directory.server.core.interceptor.context.RenameOperationContext;
+import org.apache.directory.server.core.interceptor.context.SearchOperationContext;
+import org.apache.directory.server.core.interceptor.context.UnbindOperationContext;
+import org.apache.directory.server.core.invocation.InvocationStack;
+import org.apache.directory.server.core.jndi.ServerContext;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+import javax.naming.Context;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.ldap.LdapContext;
+import java.util.Iterator;
+
+
+/**
+ * A easy-to-use implementation of {@link Interceptor}.  All methods are
+ * implemented to pass the flow of control to next interceptor by defaults.
+ * Please override the methods you have concern in.
+ *
+ * @org.apache.xbean.XBean
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public abstract class BaseInterceptor implements Interceptor
+{
+    /**
+     * default interceptor name is its class, preventing accidental duplication of interceptors by naming
+     * instances differently
+     * @return (default, class name) interceptor name
+     */
+    public String getName()
+    {
+        return getClass().getName();
+    }
+    
+    /**
+     * Returns {@link LdapPrincipal} of current context.
+     * @return the authenticated principal
+     */
+    public static LdapPrincipal getPrincipal()
+    {
+        ServerContext ctx = ( ServerContext ) getContext();
+        return ctx.getPrincipal();
+    }
+
+
+    /**
+     * Returns the current JNDI {@link Context}.
+     * @return the context on the invocation stack
+     */
+    public static LdapContext getContext()
+    {
+        return ( LdapContext ) InvocationStack.getInstance().peek().getCaller();
+    }
+
+
+    /**
+     * Creates a new instance.
+     */
+    protected BaseInterceptor()
+    {
+    }
+
+
+    /**
+     * This method does nothing by default.
+     */
+    public void init( DirectoryService directoryService ) throws NamingException
+    {
+    }
+
+
+    /**
+     * This method does nothing by default.
+     */
+    public void destroy()
+    {
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Interceptor's Invoke Method
+    // ------------------------------------------------------------------------
+
+    public void add( NextInterceptor next, AddOperationContext opContext ) throws NamingException
+    {
+        next.add( opContext );
+    }
+
+
+    public void delete( NextInterceptor next, DeleteOperationContext opContext ) throws NamingException
+    {
+        next.delete( opContext );
+    }
+
+
+    public LdapDN getMatchedName ( NextInterceptor next, GetMatchedNameOperationContext opContext ) throws NamingException
+    {
+        return next.getMatchedName( opContext );
+    }
+
+
+    public ServerEntry getRootDSE( NextInterceptor next, GetRootDSEOperationContext opContext ) throws NamingException
+    {
+        return next.getRootDSE( opContext );
+    }
+
+
+    public LdapDN getSuffix( NextInterceptor next, GetSuffixOperationContext opContext ) throws NamingException
+    {
+        return next.getSuffix( opContext );
+    }
+
+
+    public boolean hasEntry( NextInterceptor next, EntryOperationContext opContext ) throws NamingException
+    {
+        return next.hasEntry( opContext );
+    }
+
+
+    public NamingEnumeration<ServerSearchResult> list( NextInterceptor next, ListOperationContext opContext ) 
+        throws NamingException
+    {
+        return next.list( opContext );
+    }
+
+
+    public Iterator<String> listSuffixes ( NextInterceptor next, ListSuffixOperationContext opContext ) 
+        throws NamingException
+    {
+        return next.listSuffixes( opContext );
+    }
+
+
+    public ServerEntry lookup( NextInterceptor next, LookupOperationContext opContext ) throws NamingException
+    {
+        return next.lookup( opContext );
+    }
+
+    
+    public void modify( NextInterceptor next, ModifyOperationContext opContext ) throws NamingException
+    {
+        next.modify( opContext );
+    }
+
+
+    public void rename( NextInterceptor next, RenameOperationContext opContext ) throws NamingException
+    {
+        next.rename( opContext );
+    }
+
+
+    public void moveAndRename( NextInterceptor next, MoveAndRenameOperationContext opContext )
+        throws NamingException
+    {
+        next.moveAndRename( opContext );
+    }
+
+
+    public void move( NextInterceptor next, MoveOperationContext opContext ) throws NamingException
+    {
+        next.move( opContext );
+    }
+
+
+    public NamingEnumeration<ServerSearchResult> search( NextInterceptor next, SearchOperationContext opContext ) throws NamingException
+    {
+        return next.search( opContext );
+    }
+
+
+    public void addContextPartition( NextInterceptor next, AddContextPartitionOperationContext opContext ) throws NamingException
+    {
+        next.addContextPartition( opContext );
+    }
+
+
+    public void removeContextPartition( NextInterceptor next, RemoveContextPartitionOperationContext opContext ) throws NamingException
+    {
+        next.removeContextPartition( opContext );
+    }
+
+
+    public boolean compare( NextInterceptor next, CompareOperationContext opContext ) throws NamingException
+    {
+        return next.compare( opContext );
+    }
+
+
+    public void bind( NextInterceptor next, BindOperationContext opContext ) throws NamingException
+    {
+        next.bind( opContext );
+    }
+
+
+    public void unbind( NextInterceptor next, UnbindOperationContext opContext ) throws NamingException
+    {
+        next.unbind( opContext );
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/Interceptor.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/Interceptor.java
new file mode 100644
index 0000000..4e5b072
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/Interceptor.java
@@ -0,0 +1,251 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.interceptor;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerSearchResult;
+import org.apache.directory.server.core.interceptor.context.AddContextPartitionOperationContext;
+import org.apache.directory.server.core.interceptor.context.AddOperationContext;
+import org.apache.directory.server.core.interceptor.context.BindOperationContext;
+import org.apache.directory.server.core.interceptor.context.CompareOperationContext;
+import org.apache.directory.server.core.interceptor.context.DeleteOperationContext;
+import org.apache.directory.server.core.interceptor.context.EntryOperationContext;
+import org.apache.directory.server.core.interceptor.context.GetMatchedNameOperationContext;
+import org.apache.directory.server.core.interceptor.context.GetRootDSEOperationContext;
+import org.apache.directory.server.core.interceptor.context.GetSuffixOperationContext;
+import org.apache.directory.server.core.interceptor.context.ListOperationContext;
+import org.apache.directory.server.core.interceptor.context.ListSuffixOperationContext;
+import org.apache.directory.server.core.interceptor.context.LookupOperationContext;
+import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
+import org.apache.directory.server.core.interceptor.context.MoveAndRenameOperationContext;
+import org.apache.directory.server.core.interceptor.context.MoveOperationContext;
+import org.apache.directory.server.core.interceptor.context.RemoveContextPartitionOperationContext;
+import org.apache.directory.server.core.interceptor.context.RenameOperationContext;
+import org.apache.directory.server.core.interceptor.context.SearchOperationContext;
+import org.apache.directory.server.core.interceptor.context.UnbindOperationContext;
+import org.apache.directory.server.core.partition.Partition;
+import org.apache.directory.server.core.partition.PartitionNexus;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import java.util.Iterator;
+
+
+/**
+ * Filters invocations on {@link PartitionNexus}.  {@link Interceptor}
+ * filters most method calls performed on {@link PartitionNexus} just
+ * like Servlet filters do.
+ * <p/>
+ * <h2>Interceptor Chaining</h2>
+ * 
+ * Interceptors should usually pass the control
+ * of current invocation to the next interceptor by calling an appropriate method
+ * on {@link NextInterceptor}.  The flow control is returned when the next 
+ * interceptor's filter method returns. You can therefore implement pre-, post-,
+ * around- invocation handler by how you place the statement.  Otherwise, you
+ * can transform the invocation into other(s).
+ * <p/>
+ * <h3>Pre-invocation Filtering</h3>
+ * <pre>
+ * public void delete( NextInterceptor nextInterceptor, Name name )
+ * {
+ *     System.out.println( "Starting invocation." );
+ *     nextInterceptor.delete( name );
+ * }
+ * </pre>
+ * <p/>
+ * <h3>Post-invocation Filtering</h3>
+ * <pre>
+ * public void delete( NextInterceptor nextInterceptor, Name name )
+ * {
+ *     nextInterceptor.delete( name );
+ *     System.out.println( "Invocation ended." );
+ * }
+ * </pre>
+ * <p/>
+ * <h3>Around-invocation Filtering</h3>
+ * <pre>
+ * public void delete( NextInterceptor nextInterceptor, Name name )
+ * {
+ *     long startTime = System.currentTimeMillis();
+ *     try
+ *     {
+ *         nextInterceptor.delete( name );
+ *     }
+ *     finally
+ *     {
+ *         long endTime = System.currentTimeMillis();
+ *         System.out.println( ( endTime - startTime ) + "ms elapsed." );
+ *     }
+ * }
+ * </pre>
+ * <p/>
+ * <h3>Transforming invocations</h3>
+ * <pre>
+ * public void delete( NextInterceptor nextInterceptor, Name name )
+ * {
+ *     // transform deletion into modification.
+ *     Attribute mark = new AttributeImpl( "entryDeleted", "true" );
+ *     nextInterceptor.modify( name, DirContext.REPLACE_ATTRIBUTE, mark );
+ * }
+ * </pre>
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ * @see NextInterceptor
+ */
+public interface Interceptor
+{
+    /**
+     * Name that must be unique in an interceptor chain
+     * @return name of this interceptor, must be unique in an interceptor chain.
+     */
+    String getName();
+
+    /**
+     * Intializes this interceptor.  This is invoked by {@link InterceptorChain}
+     * when this intercepter is loaded into interceptor chain.
+     */
+    void init( DirectoryService directoryService ) throws NamingException;
+
+
+    /**
+     * Deinitializes this interceptor.  This is invoked by {@link InterceptorChain}
+     * when this intercepter is unloaded from interceptor chain.
+     */
+    void destroy();
+
+
+    /**
+     * Filters {@link PartitionNexus#getRootDSE( GetRootDSEOperationContext )} call.
+     */
+    ServerEntry getRootDSE( NextInterceptor next, GetRootDSEOperationContext  opContext ) throws NamingException;
+
+
+    /**
+     * Filters {@link PartitionNexus#getMatchedName( GetMatchedNameOperationContext )} call.
+     */
+    LdapDN getMatchedName( NextInterceptor next, GetMatchedNameOperationContext opContext ) throws NamingException;
+
+
+    /**
+     * Filters {@link PartitionNexus#getSuffix( GetSuffixOperationContext )} call.
+     */
+    LdapDN getSuffix ( NextInterceptor next, GetSuffixOperationContext opContext ) throws NamingException;
+
+
+    /**
+     * Filters {@link PartitionNexus#listSuffixes( ListSuffixOperationContext )} call.
+     */
+    Iterator<String> listSuffixes( NextInterceptor next, ListSuffixOperationContext opContext ) throws NamingException;
+
+
+    /**
+     * Filters {@link PartitionNexus#addContextPartition( AddContextPartitionOperationContext )} call.
+     */
+    void addContextPartition( NextInterceptor next, AddContextPartitionOperationContext opContext ) throws NamingException;
+
+
+    /**
+     * Filters {@link PartitionNexus#removeContextPartition( RemoveContextPartitionOperationContext )} call.
+     */
+    void removeContextPartition( NextInterceptor next, RemoveContextPartitionOperationContext opContext ) throws NamingException;
+
+
+    /**
+     * Filters {@link PartitionNexus#compare( CompareOperationContext )} call.
+     */
+    boolean compare( NextInterceptor next, CompareOperationContext opContext) throws NamingException;
+
+
+    /**
+     * Filters {@link Partition#delete( DeleteOperationContext )} call.
+     */
+    void delete( NextInterceptor next, DeleteOperationContext opContext ) throws NamingException;
+
+
+    /**
+     * Filters {@link Partition#add( AddOperationContext )} call.
+     */
+    void add( NextInterceptor next, AddOperationContext opContext ) throws NamingException;
+
+
+    /**
+     * Filters {@link Partition#modify( ModifyOperationContext )} call.
+     */
+    void modify( NextInterceptor next, ModifyOperationContext opContext ) throws NamingException;
+
+
+    /**
+     * Filters {@link Partition#list( ListOperationContext )} call.
+     */
+    NamingEnumeration<ServerSearchResult> list( NextInterceptor next, ListOperationContext opContext ) throws NamingException;
+
+
+    /**
+     * Filters {@link Partition#search( SearchOperationContext )} call.
+     */
+    NamingEnumeration<ServerSearchResult> search( NextInterceptor next, SearchOperationContext opContext ) throws NamingException;
+
+
+    /**
+     * Filters {@link Partition#lookup( LookupOperationContext )} call.
+     */
+    ServerEntry lookup( NextInterceptor next, LookupOperationContext opContext ) throws NamingException;
+
+
+    /**
+     * Filters {@link Partition#hasEntry( EntryOperationContext )} call.
+     */
+    boolean hasEntry( NextInterceptor next, EntryOperationContext opContext ) throws NamingException;
+
+
+    /**
+     * Filters {@link Partition#rename( RenameOperationContext )} call.
+     */
+    void rename( NextInterceptor next, RenameOperationContext opContext ) throws NamingException;
+
+
+    /**
+     * Filters {@link Partition#move( MoveOperationContext )} call.
+     */
+    void move( NextInterceptor next, MoveOperationContext opContext ) throws NamingException;
+
+
+    /**
+     * Filters {@link Partition#moveAndRename( MoveAndRenameOperationContext) } call.
+     */
+    void moveAndRename( NextInterceptor next, MoveAndRenameOperationContext opContext )
+        throws NamingException;
+
+    /**
+     * Filters {@link Partition#bind( BindOperationContext )} call.
+     */
+    void bind( NextInterceptor next, BindOperationContext opContext )
+        throws NamingException;
+
+    /**
+     * Filters {@link Partition#unbind( UnbindOperationContext )} call.
+     */
+    void unbind( NextInterceptor next, UnbindOperationContext opContext ) throws NamingException;
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/InterceptorChain.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/InterceptorChain.java
new file mode 100644
index 0000000..c387cee
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/InterceptorChain.java
@@ -0,0 +1,1423 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.interceptor;
+
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerSearchResult;
+import org.apache.directory.server.core.interceptor.context.AddContextPartitionOperationContext;
+import org.apache.directory.server.core.interceptor.context.AddOperationContext;
+import org.apache.directory.server.core.interceptor.context.BindOperationContext;
+import org.apache.directory.server.core.interceptor.context.CompareOperationContext;
+import org.apache.directory.server.core.interceptor.context.DeleteOperationContext;
+import org.apache.directory.server.core.interceptor.context.EntryOperationContext;
+import org.apache.directory.server.core.interceptor.context.GetMatchedNameOperationContext;
+import org.apache.directory.server.core.interceptor.context.GetRootDSEOperationContext;
+import org.apache.directory.server.core.interceptor.context.GetSuffixOperationContext;
+import org.apache.directory.server.core.interceptor.context.ListOperationContext;
+import org.apache.directory.server.core.interceptor.context.ListSuffixOperationContext;
+import org.apache.directory.server.core.interceptor.context.LookupOperationContext;
+import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
+import org.apache.directory.server.core.interceptor.context.MoveAndRenameOperationContext;
+import org.apache.directory.server.core.interceptor.context.MoveOperationContext;
+import org.apache.directory.server.core.interceptor.context.RemoveContextPartitionOperationContext;
+import org.apache.directory.server.core.interceptor.context.RenameOperationContext;
+import org.apache.directory.server.core.interceptor.context.SearchOperationContext;
+import org.apache.directory.server.core.interceptor.context.UnbindOperationContext;
+import org.apache.directory.server.core.invocation.Invocation;
+import org.apache.directory.server.core.invocation.InvocationStack;
+import org.apache.directory.server.core.partition.PartitionNexus;
+import org.apache.directory.server.core.partition.PartitionNexusProxy;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.naming.ConfigurationException;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+
+
+/**
+ * Manages the chain of {@link Interceptor}s.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class InterceptorChain
+{
+    private static final Logger LOG = LoggerFactory.getLogger( InterceptorChain.class );
+
+    /** Speedup for logs */
+    private static final boolean IS_DEBUG = LOG.isDebugEnabled();
+
+    private final Interceptor FINAL_INTERCEPTOR = new Interceptor()
+    {
+        private PartitionNexus nexus;
+
+
+        public String getName()
+        {
+            return "FINAL";
+        }
+
+        public void init( DirectoryService directoryService )
+        {
+            this.nexus = directoryService.getPartitionNexus();
+        }
+
+
+        public void destroy()
+        {
+            // unused
+        }
+
+
+        public boolean compare( NextInterceptor next, CompareOperationContext opContext ) throws NamingException
+        {
+            return nexus.compare( opContext );
+        }
+
+
+        public ServerEntry getRootDSE( NextInterceptor next, GetRootDSEOperationContext opContext ) throws NamingException
+        {
+            return nexus.getRootDSE( opContext );
+        }
+
+
+        public LdapDN getMatchedName( NextInterceptor next, GetMatchedNameOperationContext opContext ) throws NamingException
+        {
+            return ( LdapDN ) nexus.getMatchedName( opContext ).clone();
+        }
+
+
+        public LdapDN getSuffix( NextInterceptor next, GetSuffixOperationContext opContext ) throws NamingException
+        {
+            return ( LdapDN ) nexus.getSuffix( opContext ).clone();
+        }
+
+
+        public Iterator<String> listSuffixes( NextInterceptor next, ListSuffixOperationContext opContext ) throws NamingException
+        {
+            return nexus.listSuffixes( opContext );
+        }
+
+
+        public void delete( NextInterceptor next, DeleteOperationContext opContext ) throws NamingException
+        {
+            nexus.delete( opContext );
+        }
+
+
+        public void add( NextInterceptor next, AddOperationContext opContext ) throws NamingException
+        {
+            nexus.add( opContext );
+        }
+
+
+        public void modify( NextInterceptor next, ModifyOperationContext opContext ) throws NamingException
+        {
+            nexus.modify( opContext );
+        }
+
+
+        public NamingEnumeration<ServerSearchResult> list( NextInterceptor next, ListOperationContext opContext ) throws NamingException
+        {
+            return nexus.list( opContext );
+        }
+
+
+        public NamingEnumeration<ServerSearchResult> search( NextInterceptor next, SearchOperationContext opContext ) throws NamingException
+        {
+            return nexus.search( opContext );
+        }
+
+
+        public ServerEntry lookup( NextInterceptor next, LookupOperationContext opContext ) throws NamingException
+        {
+            return ( ServerEntry ) nexus.lookup( opContext ).clone();
+        }
+
+
+        public boolean hasEntry( NextInterceptor next, EntryOperationContext opContext ) throws NamingException
+        {
+            return nexus.hasEntry( opContext );
+        }
+
+
+        public void rename( NextInterceptor next, RenameOperationContext opContext )
+            throws NamingException
+        {
+            nexus.rename( opContext );
+        }
+
+
+        public void move( NextInterceptor next, MoveOperationContext opContext ) throws NamingException
+        {
+            nexus.move( opContext );
+        }
+
+
+        public void moveAndRename( NextInterceptor next, MoveAndRenameOperationContext opContext )
+            throws NamingException
+        {
+            nexus.moveAndRename( opContext );
+        }
+
+
+        public void addContextPartition( NextInterceptor next, AddContextPartitionOperationContext opContext )
+            throws NamingException
+        {
+            nexus.addContextPartition( opContext );
+        }
+
+
+        public void removeContextPartition( NextInterceptor next, RemoveContextPartitionOperationContext opContext ) throws NamingException
+        {
+            nexus.removeContextPartition( opContext );
+        }
+
+
+        public void bind( NextInterceptor next, BindOperationContext opContext )  throws NamingException
+        {
+            nexus.bind( opContext );
+        }
+
+
+        public void unbind( NextInterceptor next, UnbindOperationContext opContext ) throws NamingException
+        {
+            nexus.unbind( opContext );
+        }
+    };
+
+    private final Map<String, Entry> name2entry = new HashMap<String, Entry>();
+
+    private final Entry tail;
+
+    private Entry head;
+
+    private DirectoryService directoryService;
+
+
+    /**
+     * Create a new interceptor chain.
+     */
+    public InterceptorChain()
+    {
+        tail = new Entry( "tail", null, null, FINAL_INTERCEPTOR );
+        head = tail;
+    }
+
+
+    /**
+     * Initializes and registers all interceptors according to the specified
+     * {@link DirectoryService}.
+     * @throws javax.naming.NamingException if an interceptor cannot be initialized.
+     * @param directoryService the directory core
+     */
+    public synchronized void init( DirectoryService directoryService ) throws NamingException
+    {
+        // Initialize tail first.
+        this.directoryService = directoryService;
+        FINAL_INTERCEPTOR.init( directoryService );
+
+        // And register and initialize all interceptors
+        try
+        {
+            for ( Interceptor interceptor: directoryService.getInterceptors() )
+            {
+                if ( IS_DEBUG )
+                {
+                    LOG.debug( "Adding interceptor " + interceptor.getName() );
+                }
+
+                register( interceptor );
+            }
+        }
+        catch ( Throwable t )
+        {
+            // destroy if failed to initialize all interceptors.
+            destroy();
+
+            if ( t instanceof NamingException )
+            {
+                throw ( NamingException ) t;
+            }
+            else
+            {
+                throw new InterceptorException( null, "Failed to initialize interceptor chain.", t );
+            }
+        }
+    }
+
+
+    /**
+     * Deinitializes and deregisters all interceptors this chain contains.
+     */
+    public synchronized void destroy()
+    {
+        List<Entry> entries = new ArrayList<Entry>();
+        Entry e = tail;
+
+        do
+        {
+            entries.add( e );
+            e = e.prevEntry;
+        }
+        while ( e != null );
+
+        for ( Entry entry:entries )
+        {
+            if ( entry != tail )
+            {
+                try
+                {
+                    deregister( entry.getName() );
+                }
+                catch ( Throwable t )
+                {
+                    LOG.warn( "Failed to deregister an interceptor: " + entry.getName(), t );
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Returns the registered interceptor with the specified name.
+     * @param interceptorName name of the interceptor to look for
+     * @return <tt>null</tt> if the specified name doesn't exist.
+     */
+    public Interceptor get( String interceptorName )
+    {
+        Entry e = name2entry.get( interceptorName );
+        if ( e == null )
+        {
+            return null;
+        }
+
+        return e.interceptor;
+    }
+
+
+    /**
+     * Returns the list of all registered interceptors.
+     * @return a list of all the registered interceptors.
+     */
+    public synchronized List<Interceptor> getAll()
+    {
+        List<Interceptor> result = new ArrayList<Interceptor>();
+        Entry e = head;
+
+        do
+        {
+            result.add( e.interceptor );
+            e = e.nextEntry;
+        }
+        while ( e != tail );
+
+        return result;
+    }
+
+
+    public synchronized void addFirst( Interceptor interceptor ) throws NamingException
+    {
+        register0( interceptor, head );
+    }
+
+
+    public synchronized void addLast( Interceptor interceptor ) throws NamingException
+    {
+        register0( interceptor, tail );
+    }
+
+
+    public synchronized void addBefore( String nextInterceptorName, Interceptor interceptor )
+        throws NamingException
+    {
+        Entry e = name2entry.get( nextInterceptorName );
+        if ( e == null )
+        {
+            throw new ConfigurationException( "Interceptor not found: " + nextInterceptorName );
+        }
+        register0( interceptor, e );
+    }
+
+
+    public synchronized String remove( String interceptorName ) throws NamingException
+    {
+        return deregister( interceptorName );
+    }
+
+
+    public synchronized void addAfter( String prevInterceptorName, Interceptor interceptor )
+        throws NamingException
+    {
+        Entry e = name2entry.get( prevInterceptorName );
+        if ( e == null )
+        {
+            throw new ConfigurationException( "Interceptor not found: " + prevInterceptorName );
+        }
+        register0( interceptor, e.nextEntry );
+    }
+
+
+    /**
+     * Adds and initializes an interceptor with the specified configuration.
+     * @param interceptor interceptor to add to end of chain
+     * @throws javax.naming.NamingException if there is already an interceptor of this name or the interceptor
+     * cannot be initialized.
+     */
+    private void register( Interceptor interceptor ) throws NamingException
+    {
+        checkAddable( interceptor );
+        register0( interceptor, tail );
+    }
+
+
+    /**
+     * Removes and deinitializes the interceptor with the specified name.
+     * @param name name of interceptor to remove
+     * @return name of interceptor removed, if any
+     * @throws javax.naming.ConfigurationException if no interceptor registered under that name
+     */
+    private String deregister( String name ) throws ConfigurationException
+    {
+        Entry entry = checkOldName( name );
+        Entry prevEntry = entry.prevEntry;
+        Entry nextEntry = entry.nextEntry;
+
+        if ( nextEntry == null )
+        {
+            // Don't deregister tail
+            return null;
+        }
+
+        if ( prevEntry == null )
+        {
+            nextEntry.prevEntry = null;
+            head = nextEntry;
+        }
+        else
+        {
+            prevEntry.nextEntry = nextEntry;
+            nextEntry.prevEntry = prevEntry;
+        }
+
+        name2entry.remove( name );
+        entry.interceptor.destroy();
+
+        return entry.getName();
+    }
+
+    private void register0( Interceptor interceptor, Entry nextEntry ) throws NamingException
+    {
+        String name = interceptor.getName();
+
+        interceptor.init( directoryService );
+        Entry newEntry;
+        if ( nextEntry == head )
+        {
+            newEntry = new Entry( interceptor.getName(), null, head, interceptor );
+            head.prevEntry = newEntry;
+            head = newEntry;
+        }
+        else if ( head == tail )
+        {
+            newEntry = new Entry( interceptor.getName(), null, tail, interceptor );
+            tail.prevEntry = newEntry;
+            head = newEntry;
+        }
+        else
+        {
+            newEntry = new Entry( interceptor.getName(), nextEntry.prevEntry, nextEntry, interceptor );
+            nextEntry.prevEntry.nextEntry = newEntry;
+            nextEntry.prevEntry = newEntry;
+        }
+
+        name2entry.put( name, newEntry );
+    }
+
+
+    /**
+     * Throws an exception when the specified interceptor name is not registered in this chain.
+     *
+     * @param name name of interceptor to look for
+     * @return An interceptor entry with the specified name.
+     * @throws javax.naming.ConfigurationException if no interceptor has that name
+     */
+    private Entry checkOldName( String name ) throws ConfigurationException
+    {
+        Entry e = name2entry.get( name );
+
+        if ( e == null )
+        {
+            throw new ConfigurationException( "Unknown interceptor name:" + name );
+        }
+
+        return e;
+    }
+
+
+    /**
+     * Checks the specified interceptor name is already taken and throws an exception if already taken.
+     * @param interceptor interceptor to check
+     * @throws javax.naming.ConfigurationException if interceptor name is already registered
+     */
+    private void checkAddable( Interceptor interceptor ) throws ConfigurationException
+    {
+        if ( name2entry.containsKey( interceptor.getName() ) )
+        {
+            throw new ConfigurationException( "Other interceptor is using name '" + interceptor.getName() + "'" );
+        }
+    }
+
+
+    /**
+     * Gets the InterceptorEntry to use first with bypass information considered.
+     *
+     * @return the first entry to use.
+     */
+    private Entry getStartingEntry()
+    {
+        if ( InvocationStack.getInstance().isEmpty() )
+        {
+            return head;
+        }
+
+        Invocation invocation = InvocationStack.getInstance().peek();
+        if ( !invocation.hasBypass() )
+        {
+            return head;
+        }
+
+        if ( invocation.isBypassed( PartitionNexusProxy.BYPASS_ALL ) )
+        {
+            return tail;
+        }
+
+        Entry next = head;
+        while ( next != tail )
+        {
+            if ( invocation.isBypassed( next.getName() ) )
+            {
+                next = next.nextEntry;
+            }
+            else
+            {
+                return next;
+            }
+        }
+
+        return tail;
+    }
+
+
+    public ServerEntry getRootDSE( GetRootDSEOperationContext opContext ) throws NamingException
+    {
+        Entry entry = getStartingEntry();
+        Interceptor head = entry.interceptor;
+        NextInterceptor next = entry.nextInterceptor;
+
+        try
+        {
+            return head.getRootDSE( next, opContext );
+        }
+        catch ( NamingException ne )
+        {
+            throw ne;
+        }
+        catch ( Throwable e )
+        {
+            throwInterceptorException( head, e );
+            throw new InternalError(); // Should be unreachable
+        }
+    }
+
+
+    public LdapDN getMatchedName( GetMatchedNameOperationContext opContext ) throws NamingException
+    {
+        Entry entry = getStartingEntry();
+        Interceptor head = entry.interceptor;
+        NextInterceptor next = entry.nextInterceptor;
+
+        try
+        {
+            return head.getMatchedName( next, opContext );
+        }
+        catch ( NamingException ne )
+        {
+            throw ne;
+        }
+        catch ( Throwable e )
+        {
+            throwInterceptorException( head, e );
+            throw new InternalError(); // Should be unreachable
+        }
+    }
+
+
+    public LdapDN getSuffix( GetSuffixOperationContext opContext ) throws NamingException
+    {
+        Entry entry = getStartingEntry();
+        Interceptor head = entry.interceptor;
+        NextInterceptor next = entry.nextInterceptor;
+
+        try
+        {
+            return head.getSuffix( next, opContext );
+        }
+        catch ( NamingException ne )
+        {
+            throw ne;
+        }
+        catch ( Throwable e )
+        {
+            throwInterceptorException( head, e );
+            throw new InternalError(); // Should be unreachable
+        }
+    }
+
+
+    public boolean compare( CompareOperationContext opContext ) throws NamingException
+    {
+        Entry entry = getStartingEntry();
+        Interceptor head = entry.interceptor;
+        NextInterceptor next = entry.nextInterceptor;
+
+        try
+        {
+            return head.compare( next, opContext );
+        }
+        catch ( NamingException ne )
+        {
+            throw ne;
+        }
+        catch ( Throwable e )
+        {
+            throwInterceptorException( head, e );
+            throw new InternalError(); // Should be unreachable
+        }
+    }
+
+
+    public Iterator<String> listSuffixes( ListSuffixOperationContext opContext ) throws NamingException
+    {
+        Entry entry = getStartingEntry();
+        Interceptor head = entry.interceptor;
+        NextInterceptor next = entry.nextInterceptor;
+
+        try
+        {
+            return head.listSuffixes( next, opContext );
+        }
+        catch ( NamingException ne )
+        {
+            throw ne;
+        }
+        catch ( Throwable e )
+        {
+            throwInterceptorException( head, e );
+            throw new InternalError(); // Should be unreachable
+        }
+    }
+
+
+    public void addContextPartition( AddContextPartitionOperationContext opContext ) throws NamingException
+    {
+        Entry entry = getStartingEntry();
+        Interceptor head = entry.interceptor;
+        NextInterceptor next = entry.nextInterceptor;
+
+        try
+        {
+            head.addContextPartition( next, opContext );
+        }
+        catch ( NamingException ne )
+        {
+            throw ne;
+        }
+        catch ( Throwable e )
+        {
+            throwInterceptorException( head, e );
+            throw new InternalError(); // Should be unreachable
+        }
+    }
+
+
+    public void removeContextPartition( RemoveContextPartitionOperationContext opContext ) throws NamingException
+    {
+        Entry entry = getStartingEntry();
+        Interceptor head = entry.interceptor;
+        NextInterceptor next = entry.nextInterceptor;
+
+        try
+        {
+            head.removeContextPartition( next, opContext );
+        }
+        catch ( NamingException ne )
+        {
+            throw ne;
+        }
+        catch ( Throwable e )
+        {
+            throwInterceptorException( head, e );
+            throw new InternalError(); // Should be unreachable
+        }
+    }
+
+
+    public void delete( DeleteOperationContext opContext ) throws NamingException
+    {
+        Entry entry = getStartingEntry();
+        Interceptor head = entry.interceptor;
+        NextInterceptor next = entry.nextInterceptor;
+
+        try
+        {
+            head.delete( next, opContext );
+        }
+        catch ( NamingException ne )
+        {
+            throw ne;
+        }
+        catch ( Throwable e )
+        {
+            throwInterceptorException( head, e );
+        }
+    }
+
+
+    public void add( AddOperationContext opContext ) throws NamingException
+    {
+        Entry node = getStartingEntry();
+        Interceptor head = node.interceptor;
+        NextInterceptor next = node.nextInterceptor;
+
+        try
+        {
+            head.add( next, opContext );
+        }
+        catch ( NamingException ne )
+        {
+            throw ne;
+        }
+        catch ( Throwable e )
+        {
+            throwInterceptorException( head, e );
+        }
+    }
+
+
+    public void bind( BindOperationContext opContext ) throws NamingException
+    {
+        Entry node = getStartingEntry();
+        Interceptor head = node.interceptor;
+        NextInterceptor next = node.nextInterceptor;
+
+        try
+        {
+            head.bind( next, opContext );
+        }
+        catch ( NamingException ne )
+        {
+            throw ne;
+        }
+        catch ( Throwable e )
+        {
+            throwInterceptorException( head, e );
+        }
+    }
+
+
+    public void unbind( UnbindOperationContext opContext ) throws NamingException
+    {
+        Entry node = getStartingEntry();
+        Interceptor head = node.interceptor;
+        NextInterceptor next = node.nextInterceptor;
+
+        try
+        {
+            head.unbind( next, opContext );
+        }
+        catch ( NamingException ne )
+        {
+            throw ne;
+        }
+        catch ( Throwable e )
+        {
+            throwInterceptorException( head, e );
+        }
+    }
+
+
+    public void modify( ModifyOperationContext opContext ) throws NamingException
+    {
+        Entry entry = getStartingEntry();
+        Interceptor head = entry.interceptor;
+        NextInterceptor next = entry.nextInterceptor;
+
+        try
+        {
+            head.modify( next, opContext );
+        }
+        catch ( NamingException ne )
+        {
+            throw ne;
+        }
+        catch ( Throwable e )
+        {
+            throwInterceptorException( head, e );
+        }
+    }
+
+
+    public NamingEnumeration<ServerSearchResult> list( ListOperationContext opContext ) throws NamingException
+    {
+        Entry entry = getStartingEntry();
+        Interceptor head = entry.interceptor;
+        NextInterceptor next = entry.nextInterceptor;
+
+        try
+        {
+            return head.list( next, opContext );
+        }
+        catch ( NamingException ne )
+        {
+            throw ne;
+        }
+        catch ( Throwable e )
+        {
+            throwInterceptorException( head, e );
+            throw new InternalError(); // Should be unreachable
+        }
+    }
+
+
+    public NamingEnumeration<ServerSearchResult> search( SearchOperationContext opContext )
+        throws NamingException
+    {
+        Entry entry = getStartingEntry();
+        Interceptor head = entry.interceptor;
+        NextInterceptor next = entry.nextInterceptor;
+
+        try
+        {
+            return head.search( next, opContext );
+        }
+        catch ( NamingException ne )
+        {
+            throw ne;
+        }
+        catch ( Throwable e )
+        {
+            throwInterceptorException( head, e );
+            throw new InternalError(); // Should be unreachable
+        }
+    }
+
+
+    public ServerEntry lookup( LookupOperationContext opContext ) throws NamingException
+    {
+        Entry entry = getStartingEntry();
+        Interceptor head = entry.interceptor;
+        NextInterceptor next = entry.nextInterceptor;
+
+        try
+        {
+            return head.lookup( next, opContext );
+        }
+        catch ( NamingException ne )
+        {
+            throw ne;
+        }
+        catch ( Throwable e )
+        {
+            throwInterceptorException( head, e );
+            throw new InternalError(); // Should be unreachable
+        }
+    }
+
+
+    public boolean hasEntry( EntryOperationContext opContext ) throws NamingException
+    {
+        Entry entry = getStartingEntry();
+        Interceptor head = entry.interceptor;
+        NextInterceptor next = entry.nextInterceptor;
+
+        try
+        {
+            return head.hasEntry( next, opContext );
+        }
+        catch ( NamingException ne )
+        {
+            throw ne;
+        }
+        catch ( Throwable e )
+        {
+            throwInterceptorException( head, e );
+            throw new InternalError(); // Should be unreachable
+        }
+    }
+
+
+    public void rename( RenameOperationContext opContext ) throws NamingException
+    {
+        Entry entry = getStartingEntry();
+        Interceptor head = entry.interceptor;
+        NextInterceptor next = entry.nextInterceptor;
+
+        try
+        {
+            head.rename( next, opContext );
+        }
+        catch ( NamingException ne )
+        {
+            throw ne;
+        }
+        catch ( Throwable e )
+        {
+            throwInterceptorException( head, e );
+        }
+    }
+
+
+    public void move( MoveOperationContext opContext ) throws NamingException
+    {
+        Entry entry = getStartingEntry();
+        Interceptor head = entry.interceptor;
+        NextInterceptor next = entry.nextInterceptor;
+
+        try
+        {
+            head.move( next, opContext );
+        }
+        catch ( NamingException ne )
+        {
+            throw ne;
+        }
+        catch ( Throwable e )
+        {
+            throwInterceptorException( head, e );
+        }
+    }
+
+
+    public void moveAndRename( MoveAndRenameOperationContext opContext ) throws NamingException
+    {
+        Entry entry = getStartingEntry();
+        Interceptor head = entry.interceptor;
+        NextInterceptor next = entry.nextInterceptor;
+
+        try
+        {
+            head.moveAndRename( next, opContext );
+        }
+        catch ( NamingException ne )
+        {
+            throw ne;
+        }
+        catch ( Throwable e )
+        {
+            throwInterceptorException( head, e );
+        }
+    }
+
+    /**
+     * Represents an internal entry of this chain.
+     */
+    private class Entry
+    {
+        private volatile Entry prevEntry;
+
+        private volatile Entry nextEntry;
+
+        private final String name;
+
+        private final Interceptor interceptor;
+
+        private final NextInterceptor nextInterceptor;
+
+
+        private String getName()
+        {
+            return name;
+        }
+
+
+        private Entry( String name, Entry prevEntry, Entry nextEntry, Interceptor interceptor )
+        {
+            this.name = name;
+
+            if ( interceptor == null )
+            {
+                throw new NullPointerException( "interceptor" );
+            }
+
+            this.prevEntry = prevEntry;
+            this.nextEntry = nextEntry;
+            this.interceptor = interceptor;
+            this.nextInterceptor = new NextInterceptor()
+            {
+                private Entry getNextEntry()
+                {
+                    if ( InvocationStack.getInstance().isEmpty() )
+                    {
+                        return Entry.this.nextEntry;
+                    }
+
+                    Invocation invocation = InvocationStack.getInstance().peek();
+                    if ( !invocation.hasBypass() )
+                    {
+                        return Entry.this.nextEntry;
+                    }
+
+                    //  I don't think we really need this since this check is performed by the chain when
+                    //  getting the interceptor head to use.
+                    //
+                    //                    if ( invocation.isBypassed( DirectoryPartitionNexusProxy.BYPASS_ALL ) )
+                    //                    {
+                    //                        return tail;
+                    //                    }
+
+                    Entry next = Entry.this.nextEntry;
+                    while ( next != tail )
+                    {
+                        if ( invocation.isBypassed( next.getName() ) )
+                        {
+                            next = next.nextEntry;
+                        }
+                        else
+                        {
+                            return next;
+                        }
+                    }
+
+                    return next;
+                }
+
+
+                public boolean compare( CompareOperationContext opContext ) throws NamingException
+                {
+                    Entry next = getNextEntry();
+                    Interceptor interceptor = next.interceptor;
+
+                    try
+                    {
+                        return interceptor.compare( next.nextInterceptor, opContext );
+                    }
+                    catch ( NamingException ne )
+                    {
+                        throw ne;
+                    }
+                    catch ( Throwable e )
+                    {
+                        throwInterceptorException( interceptor, e );
+                        throw new InternalError(); // Should be unreachable
+                    }
+                }
+
+
+                public ServerEntry getRootDSE( GetRootDSEOperationContext opContext ) throws NamingException
+                {
+                    Entry next = getNextEntry();
+                    Interceptor interceptor = next.interceptor;
+
+                    try
+                    {
+                        return interceptor.getRootDSE( next.nextInterceptor, opContext );
+                    }
+                    catch ( NamingException ne )
+                    {
+                        throw ne;
+                    }
+                    catch ( Throwable e )
+                    {
+                        throwInterceptorException( interceptor, e );
+                        throw new InternalError(); // Should be unreachable
+                    }
+                }
+
+
+                public LdapDN getMatchedName( GetMatchedNameOperationContext opContext ) throws NamingException
+                {
+                    Entry next = getNextEntry();
+                    Interceptor interceptor = next.interceptor;
+
+                    try
+                    {
+                        return interceptor.getMatchedName( next.nextInterceptor, opContext );
+                    }
+                    catch ( NamingException ne )
+                    {
+                        throw ne;
+                    }
+                    catch ( Throwable e )
+                    {
+                        throwInterceptorException( interceptor, e );
+                        throw new InternalError(); // Should be unreachable
+                    }
+                }
+
+
+                public LdapDN getSuffix( GetSuffixOperationContext opContext ) throws NamingException
+                {
+                    Entry next = getNextEntry();
+                    Interceptor interceptor = next.interceptor;
+
+                    try
+                    {
+                        return interceptor.getSuffix( next.nextInterceptor, opContext );
+                    }
+                    catch ( NamingException ne )
+                    {
+                        throw ne;
+                    }
+                    catch ( Throwable e )
+                    {
+                        throwInterceptorException( interceptor, e );
+                        throw new InternalError(); // Should be unreachable
+                    }
+                }
+
+
+                public Iterator<String> listSuffixes( ListSuffixOperationContext opContext ) throws NamingException
+                {
+                    Entry next = getNextEntry();
+                    Interceptor interceptor = next.interceptor;
+
+                    try
+                    {
+                        return interceptor.listSuffixes( next.nextInterceptor, opContext );
+                    }
+                    catch ( NamingException ne )
+                    {
+                        throw ne;
+                    }
+                    catch ( Throwable e )
+                    {
+                        throwInterceptorException( interceptor, e );
+                        throw new InternalError(); // Should be unreachable
+                    }
+                }
+
+
+                public void delete( DeleteOperationContext opContext ) throws NamingException
+                {
+                    Entry next = getNextEntry();
+                    Interceptor interceptor = next.interceptor;
+
+                    try
+                    {
+                        interceptor.delete( next.nextInterceptor, opContext );
+                    }
+                    catch ( NamingException ne )
+                    {
+                        throw ne;
+                    }
+                    catch ( Throwable e )
+                    {
+                        throwInterceptorException( interceptor, e );
+                    }
+                }
+
+
+                public void add( AddOperationContext opContext ) throws NamingException
+                {
+                    Entry next = getNextEntry();
+                    Interceptor interceptor = next.interceptor;
+
+                    try
+                    {
+                        interceptor.add( next.nextInterceptor, opContext );
+                    }
+                    catch ( NamingException ne )
+                    {
+                        throw ne;
+                    }
+                    catch ( Throwable e )
+                    {
+                        throwInterceptorException( interceptor, e );
+                    }
+                }
+
+
+                public void modify( ModifyOperationContext opContext ) throws NamingException
+                {
+                    Entry next = getNextEntry();
+                    Interceptor interceptor = next.interceptor;
+
+                    try
+                    {
+                        interceptor.modify( next.nextInterceptor, opContext );
+                    }
+                    catch ( NamingException ne )
+                    {
+                        throw ne;
+                    }
+                    catch ( Throwable e )
+                    {
+                        throwInterceptorException( interceptor, e );
+                    }
+                }
+
+                
+                public NamingEnumeration<ServerSearchResult> list( ListOperationContext opContext ) throws NamingException
+                {
+                    Entry next = getNextEntry();
+                    Interceptor interceptor = next.interceptor;
+
+                    try
+                    {
+                        return interceptor.list( next.nextInterceptor, opContext );
+                    }
+                    catch ( NamingException ne )
+                    {
+                        throw ne;
+                    }
+                    catch ( Throwable e )
+                    {
+                        throwInterceptorException( interceptor, e );
+                        throw new InternalError(); // Should be unreachable
+                    }
+                }
+
+
+                public NamingEnumeration<ServerSearchResult> search( SearchOperationContext opContext )
+                    throws NamingException
+                {
+                    Entry next = getNextEntry();
+                    Interceptor interceptor = next.interceptor;
+
+                    try
+                    {
+                        return interceptor.search( next.nextInterceptor, opContext );
+                    }
+                    catch ( NamingException ne )
+                    {
+                        throw ne;
+                    }
+                    catch ( Throwable e )
+                    {
+                        throwInterceptorException( interceptor, e );
+                        throw new InternalError(); // Should be unreachable
+                    }
+                }
+
+
+                public ServerEntry lookup( LookupOperationContext opContext ) throws NamingException
+                {
+                    Entry next = getNextEntry();
+                    Interceptor interceptor = next.interceptor;
+
+                    try
+                    {
+                        return interceptor.lookup( next.nextInterceptor, opContext );
+                    }
+                    catch ( NamingException ne )
+                    {
+                        throw ne;
+                    }
+                    catch ( Throwable e )
+                    {
+                        throwInterceptorException( interceptor, e );
+                        throw new InternalError(); // Should be unreachable
+                    }
+                }
+
+
+                public boolean hasEntry( EntryOperationContext opContext ) throws NamingException
+                {
+                    Entry next = getNextEntry();
+                    Interceptor interceptor = next.interceptor;
+
+                    try
+                    {
+                        return interceptor.hasEntry( next.nextInterceptor, opContext );
+                    }
+                    catch ( NamingException ne )
+                    {
+                        throw ne;
+                    }
+                    catch ( Throwable e )
+                    {
+                        throwInterceptorException( interceptor, e );
+                        throw new InternalError(); // Should be unreachable
+                    }
+                }
+
+
+                public void rename( RenameOperationContext opContext ) throws NamingException
+                {
+                    Entry next = getNextEntry();
+                    Interceptor interceptor = next.interceptor;
+
+                    try
+                    {
+                        interceptor.rename( next.nextInterceptor, opContext );
+                    }
+                    catch ( NamingException ne )
+                    {
+                        throw ne;
+                    }
+                    catch ( Throwable e )
+                    {
+                        throwInterceptorException( interceptor, e );
+                    }
+                }
+
+
+                public void move( MoveOperationContext opContext ) throws NamingException
+                {
+                    Entry next = getNextEntry();
+                    Interceptor interceptor = next.interceptor;
+
+                    try
+                    {
+                        interceptor.move( next.nextInterceptor, opContext );
+                    }
+                    catch ( NamingException ne )
+                    {
+                        throw ne;
+                    }
+                    catch ( Throwable e )
+                    {
+                        throwInterceptorException( interceptor, e );
+                    }
+                }
+
+
+                public void moveAndRename( MoveAndRenameOperationContext opContext )
+                    throws NamingException
+                {
+                    Entry next = getNextEntry();
+                    Interceptor interceptor = next.interceptor;
+
+                    try
+                    {
+                        interceptor.moveAndRename( next.nextInterceptor, opContext );
+                    }
+                    catch ( NamingException ne )
+                    {
+                        throw ne;
+                    }
+                    catch ( Throwable e )
+                    {
+                        throwInterceptorException( interceptor, e );
+                    }
+                }
+
+
+                public void bind( BindOperationContext opContext ) throws NamingException
+                {
+                    Entry next = getNextEntry();
+                    Interceptor interceptor = next.interceptor;
+    
+                    try
+                    {
+                        interceptor.bind( next.nextInterceptor, opContext );
+                    }
+                    catch ( NamingException ne )
+                    {
+                        throw ne;
+                    }
+                    catch ( Throwable e )
+                    {
+                        throwInterceptorException( interceptor, e );
+                    }
+                }
+
+
+                public void unbind( UnbindOperationContext opContext ) throws NamingException
+                {
+                    Entry next = getNextEntry();
+                    Interceptor interceptor = next.interceptor;
+
+                    try
+                    {
+                        interceptor.unbind( next.nextInterceptor, opContext );
+                    }
+                    catch ( NamingException ne )
+                    {
+                        throw ne;
+                    }
+                    catch ( Throwable e )
+                    {
+                        throwInterceptorException( interceptor, e );
+                    }
+                }
+
+
+                public void addContextPartition( AddContextPartitionOperationContext opContext ) throws NamingException
+                {
+                    Entry next = getNextEntry();
+                    Interceptor interceptor = next.interceptor;
+
+                    try
+                    {
+                        interceptor.addContextPartition( next.nextInterceptor, opContext );
+                    }
+                    catch ( NamingException ne )
+                    {
+                        throw ne;
+                    }
+                    catch ( Throwable e )
+                    {
+                        throwInterceptorException( interceptor, e );
+                        throw new InternalError(); // Should be unreachable
+                    }
+                }
+
+
+                public void removeContextPartition( RemoveContextPartitionOperationContext opContext ) throws NamingException
+                {
+                    Entry next = getNextEntry();
+                    Interceptor interceptor = next.interceptor;
+
+                    try
+                    {
+                        interceptor.removeContextPartition( next.nextInterceptor, opContext );
+                    }
+                    catch ( NamingException ne )
+                    {
+                        throw ne;
+                    }
+                    catch ( Throwable e )
+                    {
+                        throwInterceptorException( interceptor, e );
+                        throw new InternalError(); // Should be unreachable
+                    }
+                }
+            };
+        }
+    }
+
+
+    private static void throwInterceptorException( Interceptor interceptor, Throwable e ) throws InterceptorException
+    {
+        throw new InterceptorException( interceptor, "Unexpected exception.", e );
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/InterceptorException.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/InterceptorException.java
new file mode 100644
index 0000000..6dd2910
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/InterceptorException.java
@@ -0,0 +1,103 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.interceptor;
+
+
+import javax.naming.NamingException;
+
+
+/**
+ * A {@link NamingException} that wraps uncaught runtime exceptions thrown
+ * from {@link Interceptor}s.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class InterceptorException extends NamingException
+{
+    private static final long serialVersionUID = 3258690996517746233L;
+
+    /**
+     * The Interceptor causing the failure
+     */
+    private final Interceptor interceptor;
+
+
+    /**
+     * Creates an InterceptorException without a message.
+     *
+     * @param interceptor the Interceptor causing the failure
+     */
+    public InterceptorException(Interceptor interceptor)
+    {
+        this.interceptor = interceptor;
+    }
+
+
+    /**
+     * Creates an InterceptorException with a custom message.
+     *
+     * @param interceptor the Interceptor causing the failure
+     * @param explanation String explanation of why the Interceptor failed
+     */
+    public InterceptorException(Interceptor interceptor, String explanation)
+    {
+        super( explanation );
+        this.interceptor = interceptor;
+    }
+
+
+    /**
+     * Creates an InterceptorException without a message.
+     *
+     * @param interceptor the Interceptor causing the failure
+     * @param rootCause   the root cause of this exception
+     */
+    public InterceptorException(Interceptor interceptor, Throwable rootCause)
+    {
+        this( interceptor );
+        super.setRootCause( rootCause );
+    }
+
+
+    /**
+     * Creates an InterceptorException without a message.
+     *
+     * @param interceptor the Interceptor causing the failure
+     * @param explanation String explanation of why the Interceptor failed
+     * @param rootCause   the root cause of this exception
+     */
+    public InterceptorException(Interceptor interceptor, String explanation, Throwable rootCause)
+    {
+        this( interceptor, explanation );
+        super.setRootCause( rootCause );
+    }
+
+
+    /**
+     * Gets the interceptor this exception is associated with.
+     *
+     * @return the interceptor this exception is associated with
+     */
+    public Interceptor getInterceptor()
+    {
+        return interceptor;
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/NextInterceptor.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/NextInterceptor.java
new file mode 100644
index 0000000..dce491d
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/NextInterceptor.java
@@ -0,0 +1,174 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.interceptor;
+
+
+import java.util.Iterator;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerSearchResult;
+import org.apache.directory.server.core.interceptor.context.AddContextPartitionOperationContext;
+import org.apache.directory.server.core.interceptor.context.AddOperationContext;
+import org.apache.directory.server.core.interceptor.context.BindOperationContext;
+import org.apache.directory.server.core.interceptor.context.CompareOperationContext;
+import org.apache.directory.server.core.interceptor.context.DeleteOperationContext;
+import org.apache.directory.server.core.interceptor.context.EntryOperationContext;
+import org.apache.directory.server.core.interceptor.context.GetMatchedNameOperationContext;
+import org.apache.directory.server.core.interceptor.context.GetRootDSEOperationContext;
+import org.apache.directory.server.core.interceptor.context.GetSuffixOperationContext;
+import org.apache.directory.server.core.interceptor.context.ListOperationContext;
+import org.apache.directory.server.core.interceptor.context.ListSuffixOperationContext;
+import org.apache.directory.server.core.interceptor.context.LookupOperationContext;
+import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
+import org.apache.directory.server.core.interceptor.context.MoveAndRenameOperationContext;
+import org.apache.directory.server.core.interceptor.context.MoveOperationContext;
+import org.apache.directory.server.core.interceptor.context.RemoveContextPartitionOperationContext;
+import org.apache.directory.server.core.interceptor.context.RenameOperationContext;
+import org.apache.directory.server.core.interceptor.context.SearchOperationContext;
+import org.apache.directory.server.core.interceptor.context.UnbindOperationContext;
+import org.apache.directory.server.core.partition.PartitionNexus;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+
+/**
+ * Represents the next {@link Interceptor} in the interceptor chain.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ * @see Interceptor
+ * @see InterceptorChain
+ */
+public interface NextInterceptor
+{
+    /**
+     * Calls the next interceptor's {@link Interceptor#compare( NextInterceptor, CompareOperationContext )}.
+     */
+    boolean compare( CompareOperationContext opContext ) throws NamingException;
+
+
+    /**
+     * Calls the next interceptor's {@link Interceptor#getRootDSE( NextInterceptor, GetRootDSEOperationContext )}.
+     */
+    ServerEntry getRootDSE( GetRootDSEOperationContext opContext ) throws NamingException;
+
+
+    /**
+     * Calls the next interceptor's {@link Interceptor#getMatchedName( NextInterceptor, GetMatchedNameOperationContext )}.
+     */
+    LdapDN getMatchedName( GetMatchedNameOperationContext opContext ) throws NamingException;
+
+
+    /**
+     * Calls the next interceptor's {@link Interceptor#getSuffix( NextInterceptor, GetSuffixOperationContext )}.
+     */
+    LdapDN getSuffix( GetSuffixOperationContext opContext ) throws NamingException;
+
+
+    /**
+     * Calls the next interceptor's {@link Interceptor#listSuffixes( NextInterceptor, ListSuffixOperationContext )}.
+     */
+    Iterator<String> listSuffixes( ListSuffixOperationContext opContext ) throws NamingException;
+
+
+    /**
+     * Calls the next interceptor's {@link PartitionNexus#addContextPartition( AddContextPartitionOperationContext )}.
+     */
+    void addContextPartition( AddContextPartitionOperationContext opContext ) throws NamingException;
+
+
+    /**
+     * Calls the next interceptor's {@link PartitionNexus#removeContextPartition( RemoveContextPartitionOperationContext )}.
+     */
+    void removeContextPartition( RemoveContextPartitionOperationContext opContext ) throws NamingException;
+
+
+    /**
+     * Calls the next interceptor's {@link Interceptor#delete(NextInterceptor, DeleteOperationContext )}.
+     */
+    void delete( DeleteOperationContext opContext ) throws NamingException;
+
+
+    /**
+     * Calls the next interceptor's {@link Interceptor#add( NextInterceptor, AddOperationContext )}.
+     */
+    void add( AddOperationContext opContext ) throws NamingException;
+
+
+    /**
+     * Calls the next interceptor's {@link Interceptor#modify( NextInterceptor, ModifyOperationContext )}.
+     */
+    void modify( ModifyOperationContext opContext ) throws NamingException;
+
+    /**
+     * Calls the next interceptor's {@link Interceptor#list( NextInterceptor, ListOperationContext )}.
+     */
+    NamingEnumeration<ServerSearchResult> list( ListOperationContext opContext ) throws NamingException;
+
+
+    /**
+     * Calls the next interceptor's {@link Interceptor#search( NextInterceptor, SearchOperationContext opContext )}.
+     */
+    NamingEnumeration<ServerSearchResult> search( SearchOperationContext opContext )
+        throws NamingException;
+
+
+    /**
+     * Calls the next interceptor's {@link Interceptor#lookup( NextInterceptor, LookupOperationContext )}.
+     */
+    ServerEntry lookup( LookupOperationContext opContext ) throws NamingException;
+
+
+    /**
+     * Calls the next interceptor's {@link Interceptor#hasEntry( NextInterceptor, EntryOperationContext )}.
+     */
+    boolean hasEntry( EntryOperationContext opContext ) throws NamingException;
+
+
+    /**
+     * Calls the next interceptor's {@link Interceptor#rename( NextInterceptor, RenameOperationContext )}.
+     */
+    void rename( RenameOperationContext opContext ) throws NamingException;
+
+
+    /**
+     * Calls the next interceptor's {@link Interceptor#move( NextInterceptor, MoveOperationContext )}.
+     */
+    void move( MoveOperationContext opContext ) throws NamingException;
+
+
+    /**
+     * Calls the next interceptor's {@link Interceptor#moveAndRename( NextInterceptor, MoveAndRenameOperationContext )}.
+     */
+    void moveAndRename( MoveAndRenameOperationContext opContext ) throws NamingException;
+
+
+    /**
+     * Calls the next interceptor's {@link Interceptor#bind( NextInterceptor, BindOperationContext )}
+     */
+    void bind( BindOperationContext opContext ) throws NamingException;
+
+    /**
+     * Calls the next interceptor's {@link Interceptor#unbind( NextInterceptor, UnbindOperationContext )}
+     */
+    void unbind( UnbindOperationContext opContext ) throws NamingException;
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/AbstractOperationContext.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/AbstractOperationContext.java
new file mode 100644
index 0000000..f4d87d5
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/AbstractOperationContext.java
@@ -0,0 +1,275 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.interceptor.context;
+
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.naming.ldap.Control;
+
+import org.apache.directory.server.core.invocation.Invocation;
+import org.apache.directory.server.core.invocation.InvocationStack;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+
+/**
+ * This abstract class stores common context elements, like the DN, which is used
+ * in all the contexts.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public abstract class AbstractOperationContext implements OperationContext
+{
+    private static final Control[] EMPTY_CONTROLS = new Control[0];
+
+    /** The DN associated with the context */
+    private LdapDN dn;
+    
+    /** The associated request's controls */
+    private Map<String, Control> requestControls = new HashMap<String, Control>(4);
+
+    /** The associated response's controls */
+    private Map<String, Control> responseControls = new HashMap<String, Control>(4);
+
+    /** Area for storing various user specified parameters */
+    private Map<String, Object> parameters = new HashMap<String, Object>(2);
+
+    /** A flag to tell that this is a collateral operation */
+    private boolean collateralOperation;
+    
+    /** The global registries reference */
+    private Registries registries;
+
+    
+    /**
+     * Creates a new instance of AbstractOperationContext.
+     *
+     * @param registries The global registries
+     */
+    public AbstractOperationContext( Registries registries )
+    {
+        this.registries = registries;
+    }
+    
+    
+    /**
+     * Creates a new instance of AbstractOperationContext.
+     *
+     * @param registries The global registries
+     * @param dn The associated DN
+     */
+    public AbstractOperationContext( Registries registries, LdapDN dn )
+    {
+        this.dn = dn;
+        this.registries = registries;
+    }
+
+
+    /**
+     * Creates a new instance of AbstractOperationContext.
+     *
+     * @param registries The global registries
+     * @param dn the associated DN
+     * @param collateralOperation true if op is collateral, false otherwise
+     */
+    public AbstractOperationContext( Registries registries, LdapDN dn, boolean collateralOperation )
+    {
+        this.dn = dn;
+        this.collateralOperation = collateralOperation;
+        this.registries = registries; 
+    }
+
+
+    /**
+     * Creates an operation context where the operation is considered a side
+     * effect of a direct operation.
+     *
+     * @param registries The global registries
+     * @param collateralOperation true if this is a side effect operation
+     */
+    public AbstractOperationContext( Registries registries, boolean collateralOperation )
+    {
+        this.collateralOperation = collateralOperation;
+        this.registries = registries;
+    }
+
+    
+    /**
+     * Gets a user specified parameter associated with this operation.
+     * 
+     * @param key the key for the parameter
+     * @return the value if one exists for this parameter
+     */
+    public Object get( String key )
+    {
+    	return parameters.get( key );
+    }
+    
+
+    /**
+     * Sets a user specified parameter associated with this operation.
+     * 
+     * @param key the key for the parameter
+     * @param value the value of the parameter
+     * @return the existing value which is replaced for this parameter
+     */
+    public Object put( String key, Object value )
+    {
+    	return parameters.put( key, value );
+    }
+    
+
+    /**
+     * Tells if the current operation is considered a side effect of the
+     * current context
+     */
+    public boolean isCollateralOperation()
+    {
+        return collateralOperation;
+    }
+
+
+    public void setCollateralOperation( boolean collateralOperation )
+    {
+        this.collateralOperation = collateralOperation;
+    }
+
+
+    /**
+     * @return The associated DN
+     */
+    public LdapDN getDn()
+    {
+        return dn;
+    }
+
+    
+    /**
+     * Set the context DN
+     *
+     * @param dn The DN to set
+     */
+    public void setDn( LdapDN dn )
+    {
+        this.dn = dn;
+    }
+
+    
+    public void addRequestControl( Control requestControl )
+    {
+        requestControls.put( requestControl.getID(), requestControl );
+    }
+
+    
+    public Control getRequestControl( String numericOid )
+    {
+        return requestControls.get( numericOid );
+    }
+
+    
+    public boolean hasRequestControl( String numericOid )
+    {
+        return requestControls.containsKey( numericOid );
+    }
+
+
+    public void addResponseControl( Control responseControl )
+    {
+        responseControls.put( responseControl.getID(), responseControl );
+    }
+
+
+    public Control getResponseControl( String numericOid )
+    {
+        return responseControls.get( numericOid );
+    }
+
+
+    public boolean hasResponseControl( String numericOid )
+    {
+        return responseControls.containsKey( numericOid );
+    }
+
+
+    public Control[] getResponseControls()
+    {
+        if ( responseControls.isEmpty() )
+        {
+            return EMPTY_CONTROLS;
+        }
+        
+        return responseControls.values().toArray( EMPTY_CONTROLS );
+    }
+
+
+    public boolean hasResponseControls()
+    {
+        return ! responseControls.isEmpty();
+    }
+
+
+    public int getResponseControlCount()
+    {
+        return responseControls.size();
+    }
+
+
+    public void addRequestControls( Control[] requestControls )
+    {
+        for ( Control c : requestControls )
+        {
+            this.requestControls.put( c.getID(), c );
+        }
+    }
+
+    
+    /**
+     * @return The AttributeTypeRegistry
+     */
+    public Registries getRegistries()
+    {
+        return registries;
+    }
+    
+    
+    /**
+     * Add the invocation into the invocation stack
+     * 
+     * @param invocation The new invocation.
+     */
+    public void push( Invocation invocation )
+    {
+        InvocationStack stack = InvocationStack.getInstance();
+        stack.push( invocation );
+    }
+    
+    
+    /**
+     * Remove the invocation fro the invocation stack
+     */
+    public void pop()
+    {
+        InvocationStack stack = InvocationStack.getInstance();
+        stack.pop();
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/AddContextPartitionOperationContext.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/AddContextPartitionOperationContext.java
new file mode 100644
index 0000000..6d6a663
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/AddContextPartitionOperationContext.java
@@ -0,0 +1,81 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.interceptor.context;
+
+
+import org.apache.directory.server.core.partition.Partition;
+import org.apache.directory.server.schema.registries.Registries;
+
+
+/**
+ * A AddContextPartition context used for Interceptors. It contains all the informations
+ * needed for the addContextPartition operation, and used by all the interceptors.  If 
+ * it does not have a partition set for it, then it will load and instantiate it 
+ * automatically using the information in the partition configuration.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class AddContextPartitionOperationContext extends EmptyOperationContext
+{
+    /** the instantiated partition class */
+    private Partition partition;
+       
+    
+    /**
+     * Creates a new instance of AddContextPartitionOperationContext.
+     *
+     * @param partition The partition to add
+     */
+    public AddContextPartitionOperationContext( Registries registries, Partition partition )
+    {
+        super( registries );
+        this.partition = partition;
+    }
+    
+    
+    /**
+     * @see Object#toString()
+     */
+    public String toString()
+    {
+        return "AddContextPartitionOperationContext for partition context '" + partition.getId() + "'";
+    }
+
+    
+    /**
+     * @return The partition
+     */
+    public Partition getPartition()
+    {
+        return partition;
+    }
+
+    
+    /**
+     * Set the partition.
+     * 
+     * @param partition the partition
+     */
+    public void setPartitionConfiguration( Partition partition )
+    {
+        this.partition = partition;
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/AddOperationContext.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/AddOperationContext.java
new file mode 100644
index 0000000..afc77ba
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/AddOperationContext.java
@@ -0,0 +1,132 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.interceptor.context;
+
+
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+
+/**
+ * A Add context used for Interceptors. It contains all the informations
+ * needed for the add operation, and used by all the interceptors
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class AddOperationContext extends AbstractOperationContext
+{
+    /** The added entry  */
+    private ServerEntry entry;
+
+
+    /**
+     * Creates a new instance of AddOperationContext.
+     */
+    public AddOperationContext( Registries registries )
+    {
+        super( registries );
+    }
+
+
+    /**
+     * Creates a new instance of AddOperationContext.
+     */
+    public AddOperationContext( Registries registries, LdapDN dn )
+    {
+        super( registries, dn );
+    }
+
+
+    /**
+     * Creates a new instance of ModifyOperationContext.
+     */
+    public AddOperationContext( Registries registries, ServerEntry entry )
+    {
+        super( registries, entry.getDn() );
+        this.entry = entry;
+    }
+
+
+    /**
+     * Creates a new instance of AddOperationContext.
+     *
+     * @param collateralOperation whether or not this is a side-effect
+     */
+    public AddOperationContext( Registries registries, boolean collateralOperation )
+    {
+        super( registries, collateralOperation );
+    }
+
+
+    /**
+     * Creates a new instance of AddOperationContext.
+     *
+     * @param dn the name of the entry being added
+     * @param collateralOperation whether or not this is a side-effect
+     */
+    public AddOperationContext( Registries registries, LdapDN dn, boolean collateralOperation )
+    {
+        super( registries, dn, collateralOperation );
+    }
+
+
+    /**
+     * Creates a new instance of ModifyOperationContext.
+     *
+     * @param dn the name of the entry being added
+     * @param entry the entry being added
+     * @param collateralOperation whether or not this is a side-effect
+     */
+    public AddOperationContext( Registries registries, LdapDN dn, ServerEntry entry, boolean collateralOperation )
+    {
+        super( registries, dn, collateralOperation );
+        this.entry = entry;
+    }
+
+
+    /**
+     * @return The added attributes
+     */
+    public ServerEntry getEntry()
+    {
+        return entry;
+    }
+
+
+    /**
+     * Set the added attributes
+     * @param entry The added attributes
+     */
+    public void setEntry( ServerEntry entry )
+    {
+        this.entry = entry;
+    }
+
+
+    /**
+     * @see Object#toString()
+     */
+    public String toString()
+    {
+        return "AddContext for DN '" + getDn().getUpName() + "'" + ", added entry: " + entry;
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/BindOperationContext.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/BindOperationContext.java
new file mode 100644
index 0000000..f948b27
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/BindOperationContext.java
@@ -0,0 +1,105 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.interceptor.context;
+
+import java.util.List;
+
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.util.StringTools;
+
+/**
+ * A Bind context used for Interceptors. It contains all the informations
+ * needed for the bind operation, and used by all the interceptors
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class BindOperationContext extends AbstractOperationContext
+{
+    /** The list of supported mechanisms */
+    private List<String> mechanisms;
+    
+    /** The password */
+    private byte[] credentials;
+    
+    /** The SASL identifier */
+    private String saslAuthId;
+    
+    /**
+     * Creates a new instance of BindOperationContext.
+     *
+     * @param registries The global registries
+     */
+    public BindOperationContext( Registries registries )
+    {
+        super( registries );
+    }
+
+    
+    /**
+     * @return The list of supported mechanisms
+     */
+    public List<String> getMechanisms()
+    {
+        return mechanisms;
+    }
+
+    public void setMechanisms( List<String> mechanisms )
+    {
+        this.mechanisms = mechanisms;
+    }
+
+    /**
+     * @return The principal password
+     */
+    public byte[] getCredentials()
+    {
+        return credentials;
+    }
+
+    public void setCredentials( byte[] credentials )
+    {
+        this.credentials = credentials;
+    }
+
+    /**
+     * @return The SASL authentication ID
+     */
+    public String getSaslAuthId()
+    {
+        return saslAuthId;
+    }
+
+    public void setSaslAuthId( String saslAuthId )
+    {
+        this.saslAuthId = saslAuthId;
+    }
+    
+    /**
+     * @see Object#toString()
+     */
+    public String toString()
+    {
+        return "BindContext for DN '" + getDn().getUpName() + "', credentials <" +
+            ( credentials != null ? StringTools.dumpBytes( credentials ) : "" ) + ">" +
+            ( ( mechanisms != null ) ? ", mechanisms : <" + StringTools.listToString( mechanisms ) + ">" : "" ) +
+            ( saslAuthId != null ? ", saslAuthId <" + saslAuthId + ">" : "" );
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/CompareOperationContext.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/CompareOperationContext.java
new file mode 100644
index 0000000..01f7d27
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/CompareOperationContext.java
@@ -0,0 +1,146 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.interceptor.context;
+
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.util.StringTools;
+
+/**
+ * A Compare context used for Interceptors. It contains all the informations
+ * needed for the compare operation, and used by all the interceptors
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class CompareOperationContext extends AbstractOperationContext
+{
+    /** The entry OID */
+    private String oid;
+
+    /** The value to be compared */
+    private Object value;
+    
+    
+    /**
+     * 
+     * Creates a new instance of CompareOperationContext.
+     *
+     */
+    public CompareOperationContext( Registries registries )
+    {
+        super( registries );
+    }
+
+    /**
+     * 
+     * Creates a new instance of CompareOperationContext.
+     *
+     */
+    public CompareOperationContext( Registries registries, LdapDN dn )
+    {
+        super( registries, dn );
+    }
+
+    /**
+     * 
+     * Creates a new instance of LookupOperationContext.
+     *
+     */
+    public CompareOperationContext( Registries registries, String oid )
+    {
+        super( registries );
+        this.oid = oid;
+    }
+
+    /**
+     * 
+     * Creates a new instance of LookupOperationContext.
+     *
+     */
+    public CompareOperationContext( Registries registries, LdapDN dn, String oid )
+    {
+        super( registries, dn );
+        this.oid = oid;
+    }
+
+    /**
+     * 
+     * Creates a new instance of LookupOperationContext.
+     *
+     */
+    public CompareOperationContext( Registries registries, LdapDN dn, String oid, Object value )
+    {
+        super( registries, dn );
+        this.oid = oid;
+        this.value = value;
+    }
+
+    /**
+     * @return The compared OID
+     */
+    public String getOid() 
+    {
+        return oid;
+    }
+
+    /**
+     * Set the compared OID
+     * @param oid The compared OID
+     */
+    public void setOid( String  oid ) 
+    {
+        this.oid = oid;
+    }
+
+    /**
+     * @return The value to compare
+     */
+    public Object getValue() 
+    {
+        return value;
+    }
+
+    /**
+     * Set the value to compare
+     * @param value The value to compare
+     */
+    public void setValue( Object value ) 
+    {
+        this.value = value;
+    }
+
+    /**
+     * @see Object#toString()
+     */
+    public String toString()
+    {
+        return "CompareContext for DN '" + getDn().getUpName() + "'" + 
+            ( ( oid != null ) ? ", oid : <" + oid + ">" : "" ) +
+            ( ( value != null ) ? ", value :'" +
+                    ( ( value instanceof String ) ?
+                            value :
+                            ( ( value instanceof byte[] ) ?
+                                    StringTools.dumpBytes( (byte[])value ) : 
+                                        "unknown value type" ) )
+                        + "'"
+                    : "" );
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/DeleteOperationContext.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/DeleteOperationContext.java
new file mode 100644
index 0000000..02e211c
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/DeleteOperationContext.java
@@ -0,0 +1,84 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.interceptor.context;
+
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+/**
+ * A Delete context used for Interceptors. It contains all the informations
+ * needed for the delete operation, and used by all the interceptors
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class DeleteOperationContext extends AbstractOperationContext
+{
+    /**
+     * Creates a new instance of DeleteOperationContext.
+     */
+    public DeleteOperationContext( Registries registries )
+    {
+        super( registries );
+    }
+    
+
+    /**
+     * Creates a new instance of DeleteOperationContext.
+     *
+     * @param collateralOperation true if this is a side effect operation
+     */
+    public DeleteOperationContext( Registries registries, boolean collateralOperation )
+    {
+        super( registries, collateralOperation );
+    }
+
+
+    /**
+     * Creates a new instance of DeleteOperationContext.
+     *
+     * @param deleteDn The entry DN to delete
+     */
+    public DeleteOperationContext( Registries registries, LdapDN deleteDn )
+    {
+        super( registries, deleteDn );
+    }
+
+
+    /**
+     * Creates a new instance of DeleteOperationContext.
+     *
+     * @param deleteDn The entry DN to delete
+     * @param collateralOperation true if this is a side effect operation
+     */
+    public DeleteOperationContext( Registries registries, LdapDN deleteDn, boolean collateralOperation )
+    {
+        super( registries, deleteDn, collateralOperation );
+    }
+
+
+    /**
+     * @see Object#toString()
+     */
+    public String toString()
+    {
+        return "DeleteContext for DN '" + getDn().getUpName() + "'";
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/EmptyOperationContext.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/EmptyOperationContext.java
new file mode 100644
index 0000000..5a958a8
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/EmptyOperationContext.java
@@ -0,0 +1,67 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.interceptor.context;
+
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+/**
+ * An EmptySuffix context used for Interceptors. It contains no data, and mask
+ * the DN in AbstractOperationContext
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class EmptyOperationContext extends AbstractOperationContext
+{
+    /**
+     * Creates a new instance of EmptyOperationContext.
+     */
+    public EmptyOperationContext( Registries registries )
+    {
+        super( registries, LdapDN.EMPTY_LDAPDN );
+    }
+    
+
+    /**
+     * Set the context DN
+     *
+     * @param dn The DN to set
+     */
+    public void setDn( LdapDN dn )
+    {
+        if ( dn.equals( LdapDN.EMPTY_LDAPDN ) )
+        {
+            return;
+        }
+        
+        throw new UnsupportedOperationException( 
+            "Cannot set the empty operation context to anything other than the EmptyDN" );
+    }
+
+    
+    /**
+     * @see Object#toString()
+     */
+    public String toString()
+    {
+        return "EmptyOperationContext";
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/EntryOperationContext.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/EntryOperationContext.java
new file mode 100644
index 0000000..f346382
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/EntryOperationContext.java
@@ -0,0 +1,59 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.interceptor.context;
+
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+/**
+ * A Entry context used for Interceptors. It contains all the informations
+ * needed for the hasEntry operation, and used by all the interceptors
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class EntryOperationContext extends AbstractOperationContext
+{
+    /**
+     * Creates a new instance of EntryOperationContext.
+     */
+    public EntryOperationContext( Registries registries )
+    {
+        super( registries );
+    }
+    
+    /**
+     * Creates a new instance of EntryOperationContext.
+     *
+     * @param entryDn The Entry DN to unbind
+     */
+    public EntryOperationContext( Registries registries, LdapDN entryDn )
+    {
+        super( registries, entryDn );
+    }
+    
+    /**
+     * @see Object#toString()
+     */
+    public String toString()
+    {
+        return "EntryContext for DN '" + getDn().getUpName() + "'";
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/GetMatchedNameOperationContext.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/GetMatchedNameOperationContext.java
new file mode 100644
index 0000000..bb44153
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/GetMatchedNameOperationContext.java
@@ -0,0 +1,59 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.interceptor.context;
+
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+/**
+ * A GetMatchedName context used for Interceptors. It contains all the informations
+ * needed for the getMatchedName operation, and used by all the interceptors
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class GetMatchedNameOperationContext extends AbstractOperationContext
+{
+    /**
+     * Creates a new instance of GetMatchedNameOperationContext.
+     */
+    public GetMatchedNameOperationContext( Registries registries )
+    {
+        super( registries );
+    }
+    
+    /**
+     * Creates a new instance of GetMatchedNameOperationContext.
+     *
+     * @param dn The DN to match
+     */
+    public GetMatchedNameOperationContext( Registries registries, LdapDN dn )
+    {
+        super( registries, dn );
+    }
+    
+    /**
+     * @see Object#toString()
+     */
+    public String toString()
+    {
+        return "GetMatchedNameContext with DN '" + getDn().getUpName() + "'";
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/GetRootDSEOperationContext.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/GetRootDSEOperationContext.java
new file mode 100644
index 0000000..85ff4ed
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/GetRootDSEOperationContext.java
@@ -0,0 +1,59 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.interceptor.context;
+
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+/**
+ * A GetRootDSE context used for Interceptors. It contains all the informations
+ * needed for the getRootDSE operation, and used by all the interceptors
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class GetRootDSEOperationContext extends AbstractOperationContext
+{
+    /**
+     * Creates a new instance of GetRootDSEOperationContext.
+     */
+    public GetRootDSEOperationContext( Registries registries )
+    {
+        super( registries );
+    }
+    
+    /**
+     * Creates a new instance of GetRootDSEOperationContext.
+     *
+     * @param dn The entry DN used to get the rootDSE
+     */
+    public GetRootDSEOperationContext( Registries registries, LdapDN dn )
+    {
+        super( registries, dn );
+    }
+    
+    /**
+     * @see Object#toString()
+     */
+    public String toString()
+    {
+        return "GetRootDSEContext with DN '" + getDn().getUpName() + "'";
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/GetSuffixOperationContext.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/GetSuffixOperationContext.java
new file mode 100644
index 0000000..d452b20
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/GetSuffixOperationContext.java
@@ -0,0 +1,59 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.interceptor.context;
+
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+/**
+ * A GetSuffix context used for Interceptors. It contains all the informations
+ * needed for the GetSuffix operation, and used by all the interceptors
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class GetSuffixOperationContext extends AbstractOperationContext
+{
+    /**
+     * Creates a new instance of GetSuffixOperationContext.
+     */
+    public GetSuffixOperationContext( Registries registries )
+    {
+        super( registries );
+    }
+    
+    /**
+     * Creates a new instance of GetSuffixOperationContext.
+     *
+     * @param dn The DN to get the suffix from
+     */
+    public GetSuffixOperationContext( Registries registries, LdapDN dn )
+    {
+        super( registries, dn );
+    }
+    
+    /**
+     * @see Object#toString()
+     */
+    public String toString()
+    {
+        return "GetSuffixOperationContext with DN '" + getDn().getUpName() + "'";
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/ListOperationContext.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/ListOperationContext.java
new file mode 100644
index 0000000..ac86bbc
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/ListOperationContext.java
@@ -0,0 +1,84 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.interceptor.context;
+
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.message.AliasDerefMode;
+
+/**
+ * A ListContext context used for Interceptors. It contains all the informations
+ * needed for the List operation, and used by all the interceptors
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ListOperationContext extends AbstractOperationContext
+{
+    private AliasDerefMode aliasDerefMode = AliasDerefMode.DEREF_ALWAYS;
+
+
+    /**
+     * Creates a new instance of ListOperationContext.
+     */
+    public ListOperationContext( Registries registries )
+    {
+        super( registries );
+    }
+
+
+    /**
+     * Creates a new instance of ListOperationContext.
+     *
+     * @param dn The DN to get the suffix from
+     */
+    public ListOperationContext( Registries registries, LdapDN dn )
+    {
+        super( registries, dn );
+    }
+
+
+    /**
+     * Creates a new instance of ListOperationContext.
+     *
+     * @param dn The DN to get the suffix from
+     * @param aliasDerefMode the alias dereferencing mode to use
+     */
+    public ListOperationContext( Registries registries, LdapDN dn, AliasDerefMode aliasDerefMode )
+    {
+        super( registries, dn );
+        this.aliasDerefMode = aliasDerefMode;
+    }
+
+    
+    /**
+     * @see Object#toString()
+     */
+    public String toString()
+    {
+        return "ListOperationContext with DN '" + getDn().getUpName() + "'";
+    }
+
+
+    public AliasDerefMode getAliasDerefMode()
+    {
+        return aliasDerefMode;
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/ListSuffixOperationContext.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/ListSuffixOperationContext.java
new file mode 100644
index 0000000..ddc7eef
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/ListSuffixOperationContext.java
@@ -0,0 +1,59 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.interceptor.context;
+
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+/**
+ * A ListSuffix context used for Interceptors. It contains all the informations
+ * needed for the ListSuffix operation, and used by all the interceptors
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ListSuffixOperationContext extends AbstractOperationContext
+{
+    /**
+     * Creates a new instance of ListSuffixOperationContext.
+     */
+    public ListSuffixOperationContext( Registries registries )
+    {
+        super( registries );
+    }
+    
+    /**
+     * Creates a new instance of ListSuffixOperationContext.
+     *
+     * @param dn The DN to get the suffix from
+     */
+    public ListSuffixOperationContext( Registries registries, LdapDN dn )
+    {
+        super( registries, dn );
+    }
+    
+    /**
+     * @see Object#toString()
+     */
+    public String toString()
+    {
+        return "ListSuffixOperationContext with DN '" + getDn().getUpName() + "'";
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/LookupOperationContext.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/LookupOperationContext.java
new file mode 100644
index 0000000..7108ffb
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/LookupOperationContext.java
@@ -0,0 +1,225 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.interceptor.context;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.util.StringTools;
+
+/**
+ * A Lookup context used for Interceptors. It contains all the informations
+ * needed for the lookup operation, and used by all the interceptors
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class LookupOperationContext extends AbstractOperationContext
+{
+    /** The list of attributes id to return */
+    private List<String> attrsId;
+    
+    /** The list of attributes OIDs for attributes to be returned */
+    private List<String> attrsOid;
+    
+    /**
+     * 
+     * Creates a new instance of LookupOperationContext.
+     *
+     */
+    public LookupOperationContext( Registries registries )
+    {
+        super( registries );
+    }
+
+    /**
+     * 
+     * Creates a new instance of LookupOperationContext.
+     *
+     */
+    public LookupOperationContext( Registries registries, LdapDN dn )
+    {
+        super( registries, dn );
+    }
+
+    /**
+     * 
+     * Creates a new instance of LookupOperationContext.
+     *
+     */
+    public LookupOperationContext( Registries registries, String attrsId[] )
+    {
+        super( registries );
+        this.attrsId = new ArrayList<String>();
+        attrsOid = new ArrayList<String>();
+        setAttrsId( attrsId );
+    }
+
+    /**
+     * 
+     * Creates a new instance of LookupOperationContext.
+     *
+     */
+    public LookupOperationContext( Registries registries, LdapDN dn, String attrsId[] )
+    {
+        super( registries, dn );
+        this.attrsId = new ArrayList<String>();
+        attrsOid = new ArrayList<String>();
+        setAttrsId( attrsId );
+    }
+
+    /**
+     * @return Get the attribute ids as a String array
+     */
+    public String[] getAttrsIdArray()
+    {
+        if ( attrsId == null )
+        {
+            return new String[]{};
+        }
+        else
+        {
+            String[] attrs = new String[ attrsId.size()];
+            return attrsId.toArray( attrs );
+        }
+    }
+
+    /**
+     * Set the attribute Ids
+     *
+     * @param attrsId The String array containing all the attribute IDs
+     */
+    public void setAttrsId( String[] attrsId )
+    {
+        if ( attrsId == null )
+        {
+            this.attrsId = new ArrayList<String>();
+        }
+        else
+        {
+            this.attrsId = new ArrayList<String>( Arrays.asList( attrsId ) );
+        }
+    }
+
+    /**
+     * @return Get the attribute oids as a String array
+     */
+    public String[] getAttrsOidArray()
+    {
+        String[] attrs = new String[ attrsId.size()];
+        return attrsOid.toArray( attrs );
+    }
+
+    /**
+     * Set the attribute oIds
+     *
+     * @param attrsOid The String array containing all the attribute OIDs
+     */
+    public void setAttrsOid( String[] attrsOid )
+    {
+        if ( attrsOid == null )
+        {
+            this.attrsOid = new ArrayList<String>();
+        }
+        else
+        {
+            this.attrsOid = new ArrayList<String>( Arrays.asList( attrsOid ) );
+        }
+    }
+    
+    /**
+     * Add an attribute OID to the current list, creating the list if necessary
+     *
+     * @param attrOid The oid to add
+     */
+    public void addAttrsOid( String attrOid )
+    {
+        if ( attrsOid == null )
+        {
+            attrsOid = new ArrayList<String>(); 
+        }
+        
+        attrsOid.add( attrOid );
+    }
+
+    /**
+     * Add an attribute ID to the current list, creating the list if necessary
+     *
+     * @param attrId the Id to add
+     */
+    public void addAttrsId( String attrId )
+    {
+        if ( attrsId == null )
+        {
+            attrsId = new ArrayList<String>(); 
+        }
+        
+        attrsId.add( attrId );
+    }
+
+    /**
+     * Add an attribute ID and OID to the current lists, creating the lists if necessary
+     *
+     * @param attrId the Id to add
+     * @param attrOid The oid to add
+     */
+    public void addAttrs( String attrId, String attrOid )
+    {
+        if ( attrsId == null )
+        {
+            attrsId = new ArrayList<String>(); 
+        }
+        
+        if ( attrsOid == null )
+        {
+            attrsOid = new ArrayList<String>(); 
+        }
+        
+        attrsId.add( attrId );
+        attrsOid.add( attrOid );
+    }
+
+    /**
+     * @return The attribute IDs list
+     */
+    public List<String> getAttrsId()
+    {
+        return attrsId;
+    }
+
+    /**
+     * @return The attribute OIDs list
+     */
+    public List<String> getAttrsOid()
+    {
+        return attrsOid;
+    }
+    
+    /**
+     * @see Object#toString()
+     */
+    public String toString()
+    {
+        return "LookupContext for DN '" + getDn().getUpName() + "'" + ( ( attrsId != null ) ? ", attributes : <" + StringTools.listToString( attrsId ) + ">" : "" );
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/ModifyOperationContext.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/ModifyOperationContext.java
new file mode 100644
index 0000000..01016d7
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/ModifyOperationContext.java
@@ -0,0 +1,136 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.interceptor.context;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerModification;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.entry.Modification;
+import org.apache.directory.shared.ldap.entry.ModificationOperation;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+/**
+ * A Modify context used for Interceptors. It contains all the informations
+ * needed for the modify operation, and used by all the interceptors
+ * 
+ * This context can use either Attributes or ModificationItem, but not both.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ModifyOperationContext extends AbstractOperationContext
+{
+    /** The modification items */
+    private List<Modification> modItems;
+
+
+    /**
+     * 
+     * Creates a new instance of ModifyOperationContext.
+     *
+     */
+    public ModifyOperationContext( Registries registries )
+    {
+        super( registries );
+    }
+
+
+    /**
+     * Creates a new instance of ModifyOperationContext.
+     *
+     * @param dn the dn of the entry to be modified
+     * @param modItems the modifications to be performed on the entry
+     */
+    public ModifyOperationContext( Registries registries, LdapDN dn, List<Modification> modItems )
+    {
+        super( registries, dn );
+        this.modItems = modItems;
+    }
+
+
+    /**
+     * Creates a new instance of ModifyOperationContext.
+     *
+     * @param dn the dn of the entry to be modified
+     * @param modItems the modifications to be performed on the entry
+     * @param collateralOperation true if op is collateral, false otherwise
+     */
+    public ModifyOperationContext( Registries registries, LdapDN dn, List<Modification> modItems, boolean collateralOperation )
+    {
+        super( registries, dn, collateralOperation );
+        this.modItems = modItems;
+    }
+
+
+    /**
+     * Set the modified attributes
+     * @param modItems The modified attributes
+     */
+    public void setModItems( List<Modification> modItems )
+    {
+        this.modItems = modItems;
+    }
+
+
+    /**
+     * @return The modifications
+     */
+    public List<Modification> getModItems() 
+    {
+        return modItems;
+    }
+
+
+    public static List<Modification> createModItems( ServerEntry serverEntry, ModificationOperation modOp ) throws NamingException
+    {
+        List<Modification> items = new ArrayList<Modification>( serverEntry.size() );
+        
+        for ( EntryAttribute attribute:serverEntry )
+        {
+            items.add( new ServerModification( modOp, attribute ) );
+        }
+
+        return items;
+    }
+
+
+    /**
+     * @see Object#toString()
+     */
+    public String toString()
+    {
+        StringBuilder sb = new StringBuilder();
+        
+        sb.append("ModifyContext for DN '").append( getDn().getUpName() ).append( "', modifications :\n" );
+        
+        for ( Modification mod:modItems )
+        {
+            sb.append( mod ).append( '\n' );
+        }
+        
+        return sb.toString();
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/MoveAndRenameOperationContext.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/MoveAndRenameOperationContext.java
new file mode 100644
index 0000000..bf978ba
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/MoveAndRenameOperationContext.java
@@ -0,0 +1,93 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.interceptor.context;
+
+
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.name.Rdn;
+
+
+/**
+ * A Move And Rename context used for Interceptors. It contains all the informations
+ * needed for the modify DN operation, and used by all the interceptors
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class MoveAndRenameOperationContext extends RenameOperationContext
+{
+    /** The parent DN */
+    private LdapDN parent;
+
+
+    /**
+     * Creates a new instance of MoveAndRenameOperationContext.
+     */
+    public MoveAndRenameOperationContext( Registries registries )
+    {
+        super( registries );
+    }
+
+
+    /**
+     * Creates a new instance of MoveAndRenameOperationContext.
+     *
+     * @param oldDn the original source entry DN to be moved and renamed
+     * @param parent the new entry superior of the target after the move
+     * @param newRdn the new rdn to use for the target once renamed
+     * @param delOldRdn true if the old rdn value is deleted, false otherwise
+     */
+    public MoveAndRenameOperationContext( Registries registries, LdapDN oldDn, LdapDN parent, Rdn newRdn, boolean delOldRdn )
+    {
+        super( registries, oldDn, newRdn, delOldRdn );
+        this.parent = parent;
+    }
+
+
+    /**
+     *  @return The parent DN
+     */
+    public LdapDN getParent()
+    {
+        return parent;
+    }
+
+
+    /**
+     * Set the parent DN
+     *
+     * @param parent The parent
+     */
+    public void setParent( LdapDN parent )
+    {
+        this.parent = parent;
+    }
+
+
+    /**
+     * @see Object#toString()
+     */
+    public String toString()
+    {
+        return "ReplaceContext for old DN '" + getDn().getUpName() + "'" +
+        ", parent '" + parent + "'";
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/MoveOperationContext.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/MoveOperationContext.java
new file mode 100644
index 0000000..a783735
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/MoveOperationContext.java
@@ -0,0 +1,85 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.interceptor.context;
+
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+/**
+ * A Move context used for Interceptors. It contains all the informations
+ * needed for the modify DN operation, and used by all the interceptors
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class MoveOperationContext extends AbstractOperationContext
+{
+    /** The parent DN */
+    private LdapDN parent;
+    
+    /**
+     * 
+     * Creates a new instance of MoveOperationContext.
+     *
+     */
+    public MoveOperationContext( Registries registries )
+    {
+        super( registries );
+    }
+
+    /**
+     * 
+     * Creates a new instance of MoveOperationContext.
+     *
+     */
+    public MoveOperationContext( Registries registries, LdapDN oldDn, LdapDN parent )
+    {
+        super( registries, oldDn );
+        this.parent = parent;
+    }
+
+    /**
+     *  @return The parent DN
+     */
+    public LdapDN getParent()
+    {
+        return parent;
+    }
+
+    /**
+     * Set the parent DN
+     *
+     * @param parent The parent
+     */
+    public void setParent( LdapDN parent )
+    {
+        this.parent = parent;
+    }
+
+    /**
+     * @see Object#toString()
+     */
+    public String toString()
+    {
+        return "ReplaceContext for old DN '" + getDn().getUpName() + "'" +
+        ", parent '" + parent + "'";
+    }
+
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/OperationContext.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/OperationContext.java
new file mode 100644
index 0000000..a00c47f
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/OperationContext.java
@@ -0,0 +1,157 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.interceptor.context;
+
+
+import javax.naming.ldap.Control;
+
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+
+/**
+ * This interface represent the context passed as an argument to each interceptor.
+ * It will contain data used by all the operations.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public interface OperationContext
+{
+    /**
+     * Checks to see if this operation is an indirect system issued operation.
+     * Collateral operations often result from direct operations.
+     *
+     * @return true if the operation represents a collateral request
+     */
+    boolean isCollateralOperation();
+
+
+    /**
+     * Sets this operation context to represent an operation that results as a
+     * byproduct of another directly issued request.
+     *
+     * @param collateralOperation true if this is collateral, false otherwise
+     */
+    void setCollateralOperation( boolean collateralOperation );
+
+
+    /**
+     * @return The associated DN
+     */
+    LdapDN getDn();
+    
+    
+    /**
+     *  @return The global registries 
+     */
+    Registries getRegistries();
+    
+    /**
+     * Set the context DN
+     *
+     * @param dn The DN to set
+     */
+    void setDn( LdapDN dn );
+
+    
+    /**
+     * Adds a response control to this operation.
+     *
+     * @param responseControl the response control to add to this operation.
+     */
+    void addResponseControl( Control responseControl );
+    
+    
+    /** 
+     * Checks to see if a response control is present on this operation.
+     *
+     * @param numericOid the numeric OID of the control also known as it's type OID
+     * @return true if the control is associated with this operation, false otherwise
+     */
+    boolean hasResponseControl( String numericOid );
+    
+    
+    /**
+     * Gets a response control if present for this request.
+     * 
+     * @param numericOid the numeric OID of the control also known as it's type OID
+     * @return the control if present
+     */
+    Control getResponseControl( String numericOid );
+    
+    
+    /**
+     * Gets all the response controls producted during this operation.
+     *
+     * @return an array over all the response controls 
+     */
+    Control[] getResponseControls();
+    
+    
+    /**
+     * Checks if any response controls have been generated for this operation.
+     *
+     * @return true if any response controls have been generated, false otherwise
+     */
+    boolean hasResponseControls();
+    
+    
+    /**
+     * Checks the number of response controls have been generated for this operation.
+     *
+     * @return the number of response controls that have been generated
+     */
+    int getResponseControlCount();
+    
+    
+    /**
+     * Adds a request control to this operation.
+     *
+     * @param requestControl the request control to add to this operation.
+     */
+    void addRequestControl( Control requestControl );
+    
+    
+    /** 
+     * Checks to see if a request control is present on this request.
+     *
+     * @param numericOid the numeric OID of the control also known as it's type OID
+     * @return true if the control is associated with this operation, false otherwise
+     */
+    boolean hasRequestControl( String numericOid );
+    
+    
+    /**
+     * Gets a request control if present for this request.
+     * 
+     * @param numericOid the numeric OID of the control also known as it's type OID
+     * @return the control if present
+     */
+    Control getRequestControl( String numericOid );
+
+
+    /**
+     * Adds many request controls to this operation.
+     *
+     * @param requestControls the request controls to add to this operation.
+     */
+    void addRequestControls( Control[] requestControls );
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/RemoveContextPartitionOperationContext.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/RemoveContextPartitionOperationContext.java
new file mode 100644
index 0000000..b83470f
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/RemoveContextPartitionOperationContext.java
@@ -0,0 +1,60 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.interceptor.context;
+
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+/**
+ * A RemoveContextPartition context used for Interceptors. It contains all the informations
+ * needed for the removeContextPartition operation, and used by all the interceptors
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class RemoveContextPartitionOperationContext extends AbstractOperationContext
+{
+    /**
+     * Creates a new instance of RemoveContextPartitionOperationContext.
+     */
+    public RemoveContextPartitionOperationContext( Registries registries )
+    {
+        super( registries );
+    }
+    
+    /**
+     * Creates a new instance of RemoveContextPartitionOperationContext.
+     *
+     * @param registries
+     * @param dn The Entry DN from which the partition should be removed
+     */
+    public RemoveContextPartitionOperationContext( Registries registries, LdapDN dn )
+    {
+        super( registries, dn );
+    }
+    
+    /**
+     * @see Object#toString()
+     */
+    public String toString()
+    {
+        return "RemoveContextPartitionOperationContext for DN '" + getDn().getUpName() + "'";
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/RenameOperationContext.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/RenameOperationContext.java
new file mode 100644
index 0000000..84af78a
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/RenameOperationContext.java
@@ -0,0 +1,117 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.interceptor.context;
+
+
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.name.Rdn;
+
+
+/**
+ * A RenameService context used for Interceptors. It contains all the informations
+ * needed for the modify DN operation, and used by all the interceptors
+ * 
+ * This is used whne the modifyDN is about changing the RDN, not the base DN.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class RenameOperationContext extends AbstractOperationContext
+{
+    /** The new DN */
+    private Rdn newRdn;
+
+    /** The flag to remove the old DN Attribute  */
+    private boolean delOldDn;
+
+
+    /**
+     * Creates a new instance of RenameOperationContext.
+     */
+    public RenameOperationContext( Registries registries )
+    {
+        super( registries );
+    }
+
+
+    /**
+     * Creates a new instance of RenameOperationContext.
+     *
+     * @param oldDn the dn of the entry before the rename
+     * @param newRdn the new RDN to use for the target
+     * @param delOldDn true if we delete the old RDN value
+     */
+    public RenameOperationContext( Registries registries, LdapDN oldDn, Rdn newRdn, boolean delOldDn )
+    {
+        super( registries, oldDn );
+        this.newRdn = newRdn;
+        this.delOldDn = delOldDn;
+    }
+
+
+    /**
+     * @return The delete old DN flag
+     */
+    public boolean getDelOldDn() 
+    {
+        return delOldDn;
+    }
+
+
+    /**
+     * Set the flag to delete the old DN
+     * @param delOldDn the flag to set
+     */
+    public void setDelOldDn( boolean delOldDn ) 
+    {
+        this.delOldDn = delOldDn;
+    }
+
+
+    /**
+     * @return The new RDN
+     */
+    public Rdn getNewRdn()
+    {
+        return newRdn;
+    }
+
+
+    /**
+     * Set the new RDN
+     * @param newRdn The new RDN
+     */
+    public void setNewRdn( Rdn newRdn )
+    {
+        this.newRdn = newRdn;
+    }
+
+
+    /**
+     * @see Object#toString()
+     */
+    public String toString()
+    {
+        return "RenameContext for old DN '" + getDn().getUpName() + "'" +
+        ", new RDN '" + newRdn + "'" +
+        ( delOldDn ? ", delete old Dn" : "" ) ; 
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/ReplaceOperationContext.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/ReplaceOperationContext.java
new file mode 100644
index 0000000..a9e0ae7
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/ReplaceOperationContext.java
@@ -0,0 +1,85 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.interceptor.context;
+
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+/**
+ * A Replace context used for Interceptors. It contains all the informations
+ * needed for the modify DN operation, and used by all the interceptors
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ReplaceOperationContext extends AbstractOperationContext
+{
+    /** The parent DN */
+    private LdapDN parent;
+    
+    /**
+     * 
+     * Creates a new instance of ReplaceOperationContext.
+     *
+     */
+    public ReplaceOperationContext( Registries registries )
+    {
+        super( registries );
+    }
+
+    /**
+     * 
+     * Creates a new instance of ReplaceOperationContext.
+     *
+     */
+    public ReplaceOperationContext( Registries registries, LdapDN oldDn, LdapDN parent )
+    {
+        super( registries, oldDn );
+        this.parent = parent;
+    }
+
+    /**
+     *  @return The parent DN
+     */
+    public LdapDN getParent()
+    {
+        return parent;
+    }
+
+    /**
+     * Set the parent DN
+     *
+     * @param parent The parent
+     */
+    public void setParent( LdapDN parent )
+    {
+        this.parent = parent;
+    }
+
+    /**
+     * @see Object#toString()
+     */
+    public String toString()
+    {
+        return "ReplaceContext for old DN '" + getDn().getUpName() + "'" +
+        ", parent '" + parent + "'";
+    }
+
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/SearchOperationContext.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/SearchOperationContext.java
new file mode 100644
index 0000000..ae302f8
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/SearchOperationContext.java
@@ -0,0 +1,143 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.interceptor.context;
+
+
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.filter.ExprNode;
+import org.apache.directory.shared.ldap.message.AliasDerefMode;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+import javax.naming.directory.SearchControls;
+
+
+/**
+ * A Search context used for Interceptors. It contains all the informations
+ * needed for the search operation, and used by all the interceptors
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class SearchOperationContext extends AbstractOperationContext
+{
+    /** The filter */
+    private ExprNode filter;
+    
+    /** The controls */
+    private SearchControls searchControls;
+
+    /** The mode of alias handling */
+    private AliasDerefMode aliasDerefMode;
+
+
+    /**
+     * Creates a new instance of SearchOperationContext.
+     */
+    public SearchOperationContext( Registries registries )
+    {
+        super( registries );
+    }
+
+
+    /**
+     * Creates a new instance of SearchOperationContext.
+     * @param aliasDerefMode the alias dereferencing mode
+     * @param dn the dn of the search base
+     * @param filter the filter AST to use for the search
+     * @param searchControls the search controls
+     */
+    public SearchOperationContext( Registries registries, LdapDN dn, AliasDerefMode aliasDerefMode, ExprNode filter,
+                                   SearchControls searchControls )
+    {
+        super( registries, dn );
+        this.filter = filter;
+        this.aliasDerefMode = aliasDerefMode;
+        this.searchControls = searchControls;
+    }
+
+
+    /**
+     * @return The filter
+     */
+    public ExprNode getFilter()
+    {
+        return filter;
+    }
+
+
+    /**
+     * Set the filter into the context.
+     *
+     * @param filter The filter to set
+     */
+    public void setFilter( ExprNode filter )
+    {
+        this.filter = filter;
+    }
+
+
+    /**
+     *  @return The search controls
+     */
+    public SearchControls getSearchControls()
+    {
+        return searchControls;
+    }
+
+
+    /**
+     * Set the search controls
+     *
+     * @param searchControls The search controls
+     */
+    public void setSearchControls( SearchControls searchControls )
+    {
+        this.searchControls = searchControls;
+    }
+
+
+    /**
+     * @see Object#toString()
+     */
+    public String toString()
+    {
+        return "SearchContext for DN '" + getDn().getUpName() + "', filter :'"
+        + filter + "'"; 
+    }
+
+
+    /**
+     *  @return The alias handling mode
+     */
+    public AliasDerefMode getAliasDerefMode()
+    {
+        return aliasDerefMode;
+    }
+
+
+    /**
+     * Set the alias handling mode
+     *  @param aliasDerefMode The alias handling mode
+     */
+    public void setAliasDerefMode( AliasDerefMode aliasDerefMode )
+    {
+        this.aliasDerefMode = aliasDerefMode;
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/UnbindOperationContext.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/UnbindOperationContext.java
new file mode 100644
index 0000000..a2abe78
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/interceptor/context/UnbindOperationContext.java
@@ -0,0 +1,59 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.interceptor.context;
+
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+/**
+ * A Unbind context used for Interceptors. It contains all the informations
+ * needed for the unbind operation, and used by all the interceptors
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class UnbindOperationContext extends AbstractOperationContext
+{
+    /**
+     * Creates a new instance of UnbindOperationContext.
+     */
+    public UnbindOperationContext( Registries registries )
+    {
+        super( registries );
+    }
+    
+    /**
+     * Creates a new instance of UnbindOperationContext.
+     *
+     * @param principalDn The principal DN to unbind
+     */
+    public UnbindOperationContext( Registries registries, LdapDN principalDn )
+    {
+        super( registries, principalDn );
+    }
+    
+    /**
+     * @see Object#toString()
+     */
+    public String toString()
+    {
+        return "UnbindContext for DN '" + getDn().getUpName() + "'";
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/invocation/Invocation.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/invocation/Invocation.java
new file mode 100644
index 0000000..ba13103
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/invocation/Invocation.java
@@ -0,0 +1,150 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.invocation;
+
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Collection;
+import java.util.Set;
+
+import javax.naming.Context;
+
+import org.apache.directory.server.core.partition.PartitionNexus;
+import org.apache.directory.server.core.partition.PartitionNexusProxy;
+
+
+/**
+ * Represents a call from JNDI {@link Context} to {@link PartitionNexus}.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class Invocation
+{
+    private final Context caller;
+    private final String name;
+    private final Collection<String> bypassed;
+    private final PartitionNexusProxy proxy;
+    
+    private static final Set<String> EMPTY_STRING_SET = new HashSet<String>();
+
+
+    /**
+     * Creates a new instance that represents an invocation without parameters.
+     * 
+     * @param caller the JNDI {@link Context} that made this invocation
+     * @param name the name of the called method
+     */
+    public Invocation( PartitionNexusProxy proxy, Context caller, String name )
+    {
+        this( proxy, caller, name, EMPTY_STRING_SET );
+    }
+
+
+    /**
+     * Creates a new instance.
+     * 
+     * @param caller the JNDI {@link Context} that made this invocation
+     * @param name the name of the called method
+     * @param parameters the array of parameters passed to the called method
+     * @param bypassed the set of bypassed Interceptor names
+     */
+    public Invocation( PartitionNexusProxy proxy, Context caller, String name, Collection<String> bypassed )
+    {
+        if ( proxy == null )
+        {
+            throw new NullPointerException( "proxy" );
+        }
+        
+        if ( caller == null )
+        {
+            throw new NullPointerException( "caller" );
+        }
+        
+        if ( name == null )
+        {
+            throw new NullPointerException( "name" );
+        }
+
+        if ( bypassed == null )
+        {
+            this.bypassed = EMPTY_STRING_SET;
+        }
+        else
+        {
+            this.bypassed = Collections.unmodifiableCollection( bypassed );
+        }
+
+        this.proxy = proxy;
+        this.caller = caller;
+        this.name = name;
+    }
+
+
+    /**
+     * Returns the proxy object to the {@link PartitionNexus}.
+     */
+    public PartitionNexusProxy getProxy()
+    {
+        return proxy;
+    }
+
+
+    /**
+     * Returns the JNDI {@link Context} which made this invocation.
+     */
+    public Context getCaller()
+    {
+        return caller;
+    }
+
+
+    /**
+     * Returns the name of the called method.
+     */
+    public String getName()
+    {
+        return name;
+    }
+
+
+    /**
+     * Checks to see if an interceptor is bypassed.
+     *
+     * @param interceptorName the interceptorName of the interceptor to check for bypass
+     * @return true if the interceptor should be bypassed, false otherwise
+     */
+    public boolean isBypassed( String interceptorName )
+    {
+        return bypassed.contains( interceptorName );
+    }
+
+
+    /**
+     * Checks to see if any interceptors are bypassed by this Invocation.
+     *
+     * @return true if at least one bypass exists
+     */
+    public boolean hasBypass()
+    {
+        return !bypassed.isEmpty();
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/invocation/InvocationStack.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/invocation/InvocationStack.java
new file mode 100644
index 0000000..cab752f
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/invocation/InvocationStack.java
@@ -0,0 +1,133 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.invocation;
+
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.naming.Context;
+
+
+/**
+ * Keeps track of recursive {@link Invocation}s.  This stack assumes an invocation
+ * occurs in the same thread since it is called first, so we manages stacks
+ * for each invocation in {@link ThreadLocal}-like manner.  You can just use
+ * {@link #getInstance()} to get current invocation stack.
+ * <p>
+ * Using {@link InvocationStack}, you can find out current effective JNDI
+ * {@link Context} or detect infinite recursions.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public final class InvocationStack
+{
+    // I didn't use ThreadLocal to release contexts explicitly.
+    // It seems like JDK 1.5 supports explicit release by introducing
+    // <tt>ThreadLocal.remove()</tt>, but we're still targetting 1.4.
+    private static final Map<Thread, InvocationStack> stacks = 
+        Collections.synchronizedMap( new IdentityHashMap<Thread, InvocationStack>() );
+
+    private final Thread thread;
+    private final List<Invocation> stack = new ArrayList<Invocation>();
+
+    /**
+     * Returns the invocation stack of current thread.
+     */
+    public static InvocationStack getInstance()
+    {
+        Thread currentThread = Thread.currentThread();
+        InvocationStack ctx;
+        ctx = stacks.get( currentThread );
+
+        if ( ctx == null )
+        {
+            ctx = new InvocationStack( currentThread );
+        }
+
+        return ctx;
+    }
+
+    private InvocationStack( Thread currentThread )
+    {
+        thread = currentThread;
+        stacks.put( currentThread, this );
+    }
+
+
+    /**
+     * Returns an array of {@link Invocation}s.  0th element is the
+     * latest invocation.
+     */
+    public Invocation[] toArray()
+    {
+        Invocation[] result = new Invocation[stack.size()];
+        result = stack.toArray( result );
+        return result;
+    }
+
+
+    /**
+     * Returns the latest invocation.
+     */
+    public Invocation peek()
+    {
+        return stack.get( 0 );
+    }
+
+
+    /**
+     * Returns true if the stack is empty false otherwise.
+     */
+    public boolean isEmpty()
+    {
+        return stack.isEmpty();
+    }
+
+
+    /**
+     * Pushes the specified invocation to this stack.
+     */
+    public void push( Invocation invocation )
+    {
+        stack.add( 0, invocation );
+    }
+
+
+    /**
+     * Pops the latest invocation from this stack.  This stack is released
+     * automatically if you pop all items from this stack.
+     */
+    public Invocation pop()
+    {
+        Invocation invocation = stack.remove( 0 );
+        
+        if ( stack.size() == 0 )
+        {
+            stacks.remove( thread );
+        }
+
+        return invocation;
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/jndi/AbstractContextFactory.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/jndi/AbstractContextFactory.java
new file mode 100644
index 0000000..8ed3a6f
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/jndi/AbstractContextFactory.java
@@ -0,0 +1,54 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.jndi;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import javax.naming.InitialContext;
+import javax.naming.spi.InitialContextFactory;
+
+
+/**
+ * A server-side JNDI provider implementation of {@link InitialContextFactory}.
+ * This class can be utilized via JNDI API in the standard fashion:
+ * <p>
+ * <code>
+ * Hashtable env = new Hashtable();
+ * env.put( Context.PROVIDER_URL, "ou=system" );
+ * env.put(
+ * Context.INITIAL_CONTEXT_FACTORY, "org.apache.directory.server.core.jndi.CoreContextFactory" );
+ * InitialContext initialContext = new InitialContext( env );
+ * </code>
+ * <p>
+ * Unfortunately, {@link InitialContext} creates a new instance of
+ * {@link InitialContextFactory} implementation everytime it is instantiated,
+ * so this factory maintains only a static, singleton instance of
+ * {@link DirectoryService}, which provides actual implementation.
+ * Please note that you'll also have to maintain any stateful information
+ * as using singleton pattern if you're going to extend this factory.
+ * <p>
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ * 
+ * @see javax.naming.spi.InitialContextFactory
+ */
+public abstract class AbstractContextFactory implements InitialContextFactory
+{
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/jndi/CoreContextFactory.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/jndi/CoreContextFactory.java
new file mode 100644
index 0000000..301d3b9
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/jndi/CoreContextFactory.java
@@ -0,0 +1,165 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.jndi;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.shared.ldap.constants.AuthenticationLevel;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.util.StringTools;
+
+import javax.naming.ConfigurationException;
+import javax.naming.Context;
+import javax.naming.NamingException;
+import javax.naming.spi.InitialContextFactory;
+import java.util.Hashtable;
+
+
+/**
+ * A simplistic implementation of {@link AbstractContextFactory}.
+ * This class simply extends {@link AbstractContextFactory} and leaves all
+ * abstract event listener methods as empty.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class CoreContextFactory implements InitialContextFactory
+{
+    public synchronized Context getInitialContext( Hashtable env ) throws NamingException
+    {
+        env = ( Hashtable<String, Object> ) env.clone();
+        LdapDN principalDn = null;
+        if ( env.containsKey( Context.SECURITY_PRINCIPAL ) )
+        {
+            if ( env.get( Context.SECURITY_PRINCIPAL ) instanceof LdapDN )
+            {
+                principalDn = ( LdapDN ) env.get( Context.SECURITY_PRINCIPAL );
+            }
+        }
+
+        String principal = getPrincipal( env );
+        byte[] credential = getCredential( env );
+        String authentication = getAuthentication( env );
+        String providerUrl = getProviderUrl( env );
+
+        DirectoryService service = ( DirectoryService ) env.get( DirectoryService.JNDI_KEY );
+
+        if ( service == null )
+        {
+            throw new ConfigurationException( "Cannot find directory service in environment: " + env );
+        }
+
+        if ( ! service.isStarted() )
+        {
+            return new DeadContext();
+        }
+
+        ServerLdapContext ctx = ( ServerLdapContext ) service.getJndiContext( principalDn, principal, credential,
+                authentication, providerUrl );
+
+        // check to make sure we have access to the specified dn in provider URL
+        ctx.lookup( "" );
+        return ctx;        
+    }
+
+
+    public static String getProviderUrl( Hashtable<String, Object> env )
+    {
+        String providerUrl;
+        Object value;
+        value = env.get( Context.PROVIDER_URL );
+        if ( value == null )
+        {
+            value = "";
+        }
+        providerUrl = value.toString();
+
+        env.put( Context.PROVIDER_URL, providerUrl );
+
+        return providerUrl;
+    }
+
+
+    public static String getAuthentication( Hashtable<String, Object> env )
+    {
+        String authentication;
+        Object value = env.get( Context.SECURITY_AUTHENTICATION );
+        if ( value == null )
+        {
+            authentication = AuthenticationLevel.NONE.toString();
+        }
+        else
+        {
+            authentication = value.toString();
+        }
+
+        env.put( Context.SECURITY_AUTHENTICATION, authentication );
+
+        return authentication;
+    }
+
+
+    public static byte[] getCredential( Hashtable<String, Object> env ) throws javax.naming.ConfigurationException
+    {
+        byte[] credential;
+        Object value = env.get( Context.SECURITY_CREDENTIALS );
+        if ( value == null )
+        {
+            credential = null;
+        }
+        else if ( value instanceof String )
+        {
+            credential = StringTools.getBytesUtf8( (String)value );
+        }
+        else if ( value instanceof byte[] )
+        {
+            credential = ( byte[] ) value;
+        }
+        else
+        {
+            throw new javax.naming.ConfigurationException( "Can't convert '" + Context.SECURITY_CREDENTIALS + "' to byte[]." );
+        }
+
+        if ( credential != null )
+        {
+            env.put( Context.SECURITY_CREDENTIALS, credential );
+        }
+
+        return credential;
+    }
+
+
+    public static String getPrincipal( Hashtable<String,Object> env )
+    {
+        String principal;
+        Object value = env.get( Context.SECURITY_PRINCIPAL );
+        if ( value == null )
+        {
+            principal = null;
+        }
+        else
+        {
+            principal = value.toString();
+            env.put( Context.SECURITY_PRINCIPAL, principal );
+        }
+
+        return principal;
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/jndi/DeadContext.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/jndi/DeadContext.java
new file mode 100644
index 0000000..336c33e
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/jndi/DeadContext.java
@@ -0,0 +1,503 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.jndi;
+
+
+import java.util.Hashtable;
+
+import javax.naming.Context;
+import javax.naming.Name;
+import javax.naming.NameClassPair;
+import javax.naming.NameParser;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.event.EventDirContext;
+import javax.naming.event.NamingListener;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.ModificationItem;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.Control;
+import javax.naming.ldap.ExtendedRequest;
+import javax.naming.ldap.ExtendedResponse;
+import javax.naming.ldap.LdapContext;
+
+import org.apache.directory.shared.ldap.exception.LdapServiceUnavailableException;
+import org.apache.directory.shared.ldap.message.ModificationItemImpl;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+
+import javax.naming.Binding;
+
+
+/**
+ * A do nothing placeholder context whose methods throw ServiceUnavailableExceptions.
+ * JNDI provider returns this context when you perform JNDI operations against the
+ * core directory service that has been shutdown or not started.  By returning a
+ * non-null Context we prevent an unnecessary exception being thrown by
+ * {@link javax.naming.InitialContext} and any one of its subclasses.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class DeadContext implements LdapContext, EventDirContext
+{
+    private final String EXCEPTION_MSG = "Context operation unavailable when "
+        + "invoked after directory service core provider has been shutdown";
+
+
+    public Control[] getConnectControls() throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public Control[] getRequestControls() throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public Control[] getResponseControls() throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public void reconnect( Control[] connCtls ) throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public void setRequestControls( Control[] requestControls ) throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public ExtendedResponse extendedOperation( ExtendedRequest request ) throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public LdapContext newInstance( Control[] requestControls ) throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public Attributes getAttributes( String name ) throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public void modifyAttributes( String name, int mod_op, Attributes attrs ) throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public void modifyAttributes( String name, ModificationItem[] mods ) throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public Attributes getAttributes( Name name ) throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public void modifyAttributes( Name name, int mod_op, Attributes attrs ) throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public void modifyAttributes( Name name, ModificationItem[] mods ) throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+    public DirContext getSchema( String name ) throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public DirContext getSchemaClassDefinition( String name ) throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public DirContext getSchema( Name name ) throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public DirContext getSchemaClassDefinition( Name name ) throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public void modifyAttributes( String name, ModificationItemImpl[] mods ) throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public void modifyAttributes( Name name, ModificationItemImpl[] mods ) throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public NamingEnumeration<SearchResult> search( String name, Attributes matchingAttributes ) throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public NamingEnumeration<SearchResult> search( Name name, Attributes matchingAttributes ) throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public void bind( String name, Object obj, Attributes attrs ) throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public void rebind( String name, Object obj, Attributes attrs ) throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public void bind( Name name, Object obj, Attributes attrs ) throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public void rebind( Name name, Object obj, Attributes attrs ) throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public Attributes getAttributes( String name, String[] attrIds ) throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public Attributes getAttributes( Name name, String[] attrIds ) throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public DirContext createSubcontext( String name, Attributes attrs ) throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public DirContext createSubcontext( Name name, Attributes attrs ) throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public NamingEnumeration<SearchResult> search( String name, Attributes matchingAttributes, String[] attributesToReturn )
+        throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public NamingEnumeration<SearchResult> search( Name name, Attributes matchingAttributes, String[] attributesToReturn )
+        throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public NamingEnumeration<SearchResult> search( String name, String filter, SearchControls cons ) throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public NamingEnumeration<SearchResult> search( Name name, String filter, SearchControls cons ) throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public NamingEnumeration<SearchResult> search( String name, String filterExpr, Object[] filterArgs, SearchControls cons )
+        throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public NamingEnumeration<SearchResult> search( Name name, String filterExpr, Object[] filterArgs, SearchControls cons )
+        throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public void close() throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public String getNameInNamespace() throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public void destroySubcontext( String name ) throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public void unbind( String name ) throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public Hashtable<String,Object>  getEnvironment() throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public void destroySubcontext( Name name ) throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public void unbind( Name name ) throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public Object lookup( String name ) throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public Object lookupLink( String name ) throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public Object removeFromEnvironment( String propName ) throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public void bind( String name, Object obj ) throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public void rebind( String name, Object obj ) throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public Object lookup( Name name ) throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public Object lookupLink( Name name ) throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public void bind( Name name, Object obj ) throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public void rebind( Name name, Object obj ) throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public void rename( String oldName, String newName ) throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public Context createSubcontext( String name ) throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public Context createSubcontext( Name name ) throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public void rename( Name oldName, Name newName ) throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public NameParser getNameParser( String name ) throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public NameParser getNameParser( Name name ) throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public NamingEnumeration<NameClassPair> list( String name ) throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public NamingEnumeration<Binding> listBindings( String name ) throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public NamingEnumeration<NameClassPair> list( Name name ) throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public NamingEnumeration<Binding> listBindings( Name name ) throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public Object addToEnvironment( String propName, Object propVal ) throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public String composeName( String name, String prefix ) throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public Name composeName( Name name, Name prefix ) throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public void addNamingListener( Name name, String s, SearchControls searchControls, NamingListener namingListener )
+        throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public void addNamingListener( String s, String s1, SearchControls searchControls, NamingListener namingListener )
+        throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public void addNamingListener( Name name, String s, Object[] objects, SearchControls searchControls,
+        NamingListener namingListener ) throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public void addNamingListener( String s, String s1, Object[] objects, SearchControls searchControls,
+        NamingListener namingListener ) throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public void addNamingListener( Name name, int i, NamingListener namingListener ) throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public void addNamingListener( String s, int i, NamingListener namingListener ) throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public void removeNamingListener( NamingListener namingListener ) throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+
+
+    public boolean targetMustExist() throws NamingException
+    {
+        throw new LdapServiceUnavailableException( EXCEPTION_MSG, ResultCodeEnum.UNAVAILABLE );
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/jndi/JavaLdapSupport.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/jndi/JavaLdapSupport.java
new file mode 100644
index 0000000..441dfd6
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/jndi/JavaLdapSupport.java
@@ -0,0 +1,197 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.jndi;
+
+
+import org.apache.directory.server.core.entry.DefaultServerAttribute;
+import org.apache.directory.server.core.entry.ServerAttribute;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+import javax.naming.NamingException;
+
+
+/**
+ * Contains constants and serialization methods used to implement functionality
+ * associated with RFC 2713 which enables the storage and representation of Java
+ * objects within an LDAP directory.
+ *
+ * @see <a href="http://www.faqs.org/rfcs/rfc2713.html">RFC 2713</a>
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+class JavaLdapSupport
+{
+    // ------------------------------------------------------------------------
+    // Attribute Id Constants Used By The Java LDAP BootstrapSchema
+    // ------------------------------------------------------------------------
+
+    /** the javaObject attribute */
+    public static final String JOBJECT_ATTR = "javaObject";
+    /** the javaContainer attribute */
+    public static final String JCONTAINER_ATTR = "javaContainer";
+    /** the javaSerializedObject attribute */
+    public static final String JSERIALIZEDOBJ_ATTR = "javaSerializedObject";
+
+    /** the javaClassName attribute */
+    public static final String JCLASSNAME_ATTR = "javaClassName";
+    /** the javaClassNames attribute */
+    public static final String JCLASSNAMES_ATTR = "javaClassNames";
+    /** the javaSerializedData attribute */
+    public static final String JSERIALDATA_ATTR = "javaSerializedData";
+
+
+    // ------------------------------------------------------------------------
+    // Package Friendly & Private Utility Methods 
+    // ------------------------------------------------------------------------
+
+    /**
+     * Resusitates an object from a serialized attribute in an entry that 
+     * conforms to the specifications for representing Java Objects in an LDAP 
+     * Directory (RFC 2713).
+     *
+     * @param attributes the entry representing a serialized object
+     * @return the deserialized object
+     * @throws NamingException if the object cannot be serialized
+     */
+    static Object deserialize( ServerEntry serverEntry ) throws NamingException
+    {
+        ObjectInputStream in = null;
+        String className = ( String ) serverEntry.get( JCLASSNAME_ATTR ).getString();
+
+        try
+        {
+            byte[] data = ( byte[] ) serverEntry.get( JSERIALDATA_ATTR ).getBytes();
+            in = new ObjectInputStream( new ByteArrayInputStream( data ) );
+            return in.readObject();
+        }
+        catch ( Exception e )
+        {
+            NamingException ne = new NamingException( "De-serialization of '" + className + "' instance failed:\n"
+                + e.getMessage() );
+            ne.setRootCause( e );
+            throw ne;
+        }
+        finally
+        {
+            try
+            {
+                if ( in != null )
+                {
+                    in.close();
+                }
+            }
+            catch ( IOException e )
+            {
+                throw new NamingException( "object deserialization stream close() failure" );
+            }
+        }
+    }
+
+
+    /**
+     * Serializes an object into a byte array.
+     *
+     * @param obj the object to serialize
+     * @return the object's serialized byte array form
+     * @throws NamingException of the object cannot be serialized
+     */
+    static byte[] serialize( Object obj ) throws NamingException
+    {
+        ByteArrayOutputStream bytesOut = null;
+        ObjectOutputStream out = null;
+
+        try
+        {
+            bytesOut = new ByteArrayOutputStream();
+            out = new ObjectOutputStream( bytesOut );
+            out.writeObject( obj );
+            return bytesOut.toByteArray();
+        }
+        catch ( Exception e )
+        {
+            NamingException ne = new NamingException( "Serialization of '" + obj + "' failed:\n" + e.getMessage() );
+            ne.setRootCause( e );
+            throw ne;
+        }
+        finally
+        {
+            try
+            {
+                if ( out != null )
+                {
+                    out.close();
+                }
+            }
+            catch ( IOException e )
+            {
+                throw new NamingException( "object serialization stream close() failure" );
+            }
+        }
+    }
+
+
+    /**
+     * Serializes an object into an entry using the attributes specified in
+     * RFC 2713 to represent the serialized object.
+     *
+     * @param entry the set of attributes representing entry
+     * @param obj the object to serialize
+     * @throws NamingException if the object cannot be serialized
+     */
+    static void serialize( ServerEntry entry, Object obj, Registries registries ) throws NamingException
+    {
+        /* Let's add the object classes first:
+         * objectClass: top
+         * objectClass: javaObject
+         * objectClass: javaContainer
+         * objectClass: javaSerializedObject
+         */
+        entry.put( SchemaConstants.OBJECT_CLASS_AT,
+                SchemaConstants.TOP_OC,
+                JOBJECT_ATTR,
+                JCONTAINER_ATTR,
+                JSERIALIZEDOBJ_ATTR );
+
+        // Add the javaClassName and javaSerializedData attributes
+        entry.put( JCLASSNAME_ATTR, obj.getClass().getName() );
+        entry.put( JSERIALDATA_ATTR, serialize( obj ) );
+
+        // Add all the class names this object can be cast to:
+        Class<?>[] classes = obj.getClass().getClasses();
+        AttributeType attributeType = registries.getAttributeTypeRegistry().lookup( JCLASSNAMES_ATTR );
+        ServerAttribute javaClassNames = new DefaultServerAttribute( attributeType, JCLASSNAMES_ATTR );
+
+        for ( int ii = 0; ii < classes.length; ii++ )
+        {
+            javaClassNames.add( classes[ii].getName() );
+        }
+
+        entry.put( javaClassNames );
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/jndi/LdapJndiProperties.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/jndi/LdapJndiProperties.java
new file mode 100644
index 0000000..d0a5b11
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/jndi/LdapJndiProperties.java
@@ -0,0 +1,297 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.jndi;
+
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Hashtable;
+import java.util.List;
+
+import javax.naming.Context;
+import javax.naming.NamingException;
+
+import org.apache.directory.shared.ldap.constants.AuthenticationLevel;
+import org.apache.directory.shared.ldap.exception.LdapConfigurationException;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.util.StringTools;
+
+
+/**
+ * A wrapper around a JNDI environment which checks for correct LDAP specific 
+ * environment settings.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class LdapJndiProperties
+{
+    private static final String SASL_AUTHID = "java.naming.security.sasl.authorizationId";
+
+    private LdapDN providerDn;
+    private LdapDN bindDn;
+    private String saslAuthId;
+    private AuthenticationLevel level;
+    private List<String> mechanisms = new ArrayList<String>();
+    private byte[] credentials;
+
+
+    public static AuthenticationLevel getAuthenticationLevel( Hashtable env ) throws NamingException
+    {
+        AuthenticationLevel level;
+        Object credobj = env.get( Context.SECURITY_CREDENTIALS );
+        Object authentication = env.get( Context.SECURITY_AUTHENTICATION );
+
+        // -------------------------------------------------------------------
+        // Figure out and set the authentication level and mechanisms
+        // -------------------------------------------------------------------
+
+        if ( authentication == null )
+        {
+            // if the property is not set but Context.SECURITY_CREDENTIALS is then SIMPLE
+            if ( credobj == null )
+            {
+                level = AuthenticationLevel.NONE;
+            }
+            else
+            {
+                level = AuthenticationLevel.SIMPLE;
+            }
+        }
+        else if ( !( authentication instanceof String ) )
+        {
+            throw new LdapConfigurationException( "Don't know how to interpret " + authentication.getClass()
+                + " objects for environment property " + Context.SECURITY_AUTHENTICATION );
+        }
+        else
+        {
+            if ( AuthenticationLevel.NONE.toString().equals( authentication ) )
+            {
+                level = AuthenticationLevel.NONE;
+            }
+            else if ( AuthenticationLevel.SIMPLE.toString().equals( authentication ) )
+            {
+                level = AuthenticationLevel.SIMPLE;
+            }
+            else
+            {
+                level = AuthenticationLevel.STRONG;
+            }
+        }
+
+        return level;
+    }
+
+
+    public static LdapJndiProperties getLdapJndiProperties( Hashtable env ) throws NamingException
+    {
+        if ( env == null )
+        {
+            throw new LdapConfigurationException( "environment cannot be null" );
+        }
+
+        LdapJndiProperties props = new LdapJndiProperties();
+        Object principal = env.get( Context.SECURITY_PRINCIPAL );
+        Object credobj = env.get( Context.SECURITY_CREDENTIALS );
+        Object authentication = env.get( Context.SECURITY_AUTHENTICATION );
+
+        // -------------------------------------------------------------------
+        // check for the provider URL property 
+        // -------------------------------------------------------------------
+
+        if ( !env.containsKey( Context.PROVIDER_URL ) )
+        {
+            String msg = "Expected property " + Context.PROVIDER_URL;
+            msg += " but could not find it in env!";
+            throw new LdapConfigurationException( msg );
+        }
+
+        String url = ( String ) env.get( Context.PROVIDER_URL );
+        if ( url == null )
+        {
+            String msg = "Expected value for property " + Context.PROVIDER_URL;
+            msg += " but it was set to null in env!";
+            throw new LdapConfigurationException( msg );
+        }
+
+        if ( url.trim().equals( "" ) )
+        {
+            props.providerDn = LdapDN.EMPTY_LDAPDN;
+        }
+        else
+        {
+            props.providerDn = new LdapDN( url );
+        }
+
+        // -------------------------------------------------------------------
+        // Figure out and set the authentication level and mechanisms
+        // -------------------------------------------------------------------
+
+        if ( authentication == null )
+        {
+            // if the property is not set but Context.SECURITY_CREDENTIALS is then SIMPLE
+            if ( credobj == null )
+            {
+                props.level = AuthenticationLevel.NONE;
+                props.mechanisms.add( AuthenticationLevel.NONE.toString() );
+            }
+            else
+            {
+                props.level = AuthenticationLevel.SIMPLE;
+                props.mechanisms.add( AuthenticationLevel.SIMPLE.toString() );
+            }
+        }
+        else if ( !( authentication instanceof String ) )
+        {
+            throw new LdapConfigurationException( "Don't know how to interpret " + authentication.getClass()
+                + " objects for environment property " + Context.SECURITY_AUTHENTICATION );
+        }
+        else
+        {
+            if ( AuthenticationLevel.NONE.toString().equals( authentication ) )
+            {
+                props.level = AuthenticationLevel.NONE;
+                props.mechanisms.add( AuthenticationLevel.NONE.toString() );
+            }
+            else if ( AuthenticationLevel.SIMPLE.toString().equals( authentication ) )
+            {
+                props.level = AuthenticationLevel.SIMPLE;
+                props.mechanisms.add( AuthenticationLevel.SIMPLE.toString() );
+            }
+            else
+            {
+                props.level = AuthenticationLevel.STRONG;
+                String[] mechList = ( ( String ) authentication ).trim().split( " " );
+                for ( String mech : mechList )
+                {
+                    if ( !mech.trim().equals( "" ) )
+                    {
+                        props.mechanisms.add( mech );
+                    }
+                }
+            }
+        }
+
+        // -------------------------------------------------------------------
+        // Figure out and set the security principal bindDn and saslAuthId
+        // -------------------------------------------------------------------
+
+        if ( principal == null && props.level == AuthenticationLevel.SIMPLE )
+        {
+            throw new LdapConfigurationException( Context.SECURITY_PRINCIPAL + " cannot be null." );
+        }
+        else if ( principal == null && props.level == AuthenticationLevel.NONE )
+        {
+            props.bindDn = LdapDN.EMPTY_LDAPDN;
+        }
+        else if ( !( principal instanceof String ) )
+        {
+            throw new LdapConfigurationException( "Don't know how to interpret " + principal.getClass()
+                + " objects for environment property " + Context.SECURITY_PRINCIPAL );
+        }
+        else if ( ( ( String ) principal ).trim().equals( "" ) )
+        {
+            props.bindDn = LdapDN.EMPTY_LDAPDN;
+        }
+        else
+        {
+            props.bindDn = new LdapDN( ( String ) principal );
+        }
+        
+
+        if ( env.get( SASL_AUTHID ) != null && props.level == AuthenticationLevel.STRONG )
+        {
+            Object obj = env.get( SASL_AUTHID );
+            if ( obj instanceof String )
+            {
+                props.saslAuthId = ( String ) obj;
+            }
+            else
+            {
+                throw new LdapConfigurationException( "Don't know how to interpret " + obj.getClass()
+                    + " objects for environment property " + SASL_AUTHID );
+            }
+            props.saslAuthId = ( String ) principal;
+        }
+
+        // -------------------------------------------------------------------
+        // Figure out the credentials
+        // -------------------------------------------------------------------
+
+        if ( props.level == AuthenticationLevel.SIMPLE && credobj == null )
+        {
+            throw new LdapConfigurationException( "cannot specify simple authentication with supplying credentials" );
+        }
+        else if ( credobj != null )
+        {
+            if ( credobj instanceof String )
+            {
+                props.credentials = StringTools.getBytesUtf8( ( String ) credobj );
+            }
+            else if ( credobj instanceof byte[] )
+            {
+                props.credentials = ( byte[] ) credobj;
+            }
+            else
+            {
+                throw new LdapConfigurationException( "Don't know how to interpret " + credobj.getClass()
+                    + " objects for environment property " + Context.SECURITY_CREDENTIALS );
+            }
+        }
+
+        return props;
+    }
+
+
+    public LdapDN getBindDn()
+    {
+        return bindDn;
+    }
+
+
+    public LdapDN getProviderDn()
+    {
+        return providerDn;
+    }
+
+
+    public String getSaslAuthId()
+    {
+        return saslAuthId;
+    }
+
+
+    public AuthenticationLevel getAuthenticationLevel()
+    {
+        return level;
+    }
+
+
+    public List<String> getAuthenticationMechanisms()
+    {
+        return Collections.unmodifiableList( mechanisms );
+    }
+
+
+    public byte[] getCredentials()
+    {
+        return credentials;
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/jndi/ServerContext.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/jndi/ServerContext.java
new file mode 100644
index 0000000..e7e301c
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/jndi/ServerContext.java
@@ -0,0 +1,1217 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.jndi;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.authn.AuthenticationInterceptor;
+import org.apache.directory.server.core.authn.LdapPrincipal;
+import org.apache.directory.server.core.entry.DefaultServerEntry;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerEntryUtils;
+import org.apache.directory.server.core.entry.ServerSearchResult;
+import org.apache.directory.server.core.interceptor.context.AddOperationContext;
+import org.apache.directory.server.core.interceptor.context.BindOperationContext;
+import org.apache.directory.server.core.interceptor.context.DeleteOperationContext;
+import org.apache.directory.server.core.interceptor.context.EntryOperationContext;
+import org.apache.directory.server.core.interceptor.context.GetRootDSEOperationContext;
+import org.apache.directory.server.core.interceptor.context.ListOperationContext;
+import org.apache.directory.server.core.interceptor.context.LookupOperationContext;
+import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
+import org.apache.directory.server.core.interceptor.context.MoveAndRenameOperationContext;
+import org.apache.directory.server.core.interceptor.context.MoveOperationContext;
+import org.apache.directory.server.core.interceptor.context.RenameOperationContext;
+import org.apache.directory.server.core.interceptor.context.SearchOperationContext;
+import org.apache.directory.server.core.partition.PartitionNexus;
+import org.apache.directory.server.core.partition.PartitionNexusProxy;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.constants.JndiPropertyConstants;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.entry.Modification;
+import org.apache.directory.shared.ldap.exception.LdapNoPermissionException;
+import org.apache.directory.shared.ldap.exception.LdapSchemaViolationException;
+import org.apache.directory.shared.ldap.filter.ExprNode;
+import org.apache.directory.shared.ldap.filter.PresenceNode;
+import org.apache.directory.shared.ldap.message.AliasDerefMode;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.name.AttributeTypeAndValue;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.name.Rdn;
+import org.apache.directory.shared.ldap.util.AttributeUtils;
+import org.apache.directory.shared.ldap.util.StringTools;
+
+import javax.naming.Context;
+import javax.naming.InvalidNameException;
+import javax.naming.Name;
+import javax.naming.NameNotFoundException;
+import javax.naming.NameParser;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.Reference;
+import javax.naming.Referenceable;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.SearchControls;
+import javax.naming.event.EventContext;
+import javax.naming.event.NamingListener;
+import javax.naming.ldap.Control;
+import javax.naming.spi.DirStateFactory;
+import javax.naming.spi.DirectoryManager;
+import java.io.Serializable;
+import java.net.InetSocketAddress;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Set;
+
+
+/**
+ * A non-federated abstract Context implementation.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public abstract class ServerContext implements EventContext
+{
+    /** property key used for deleting the old RDN on a rename */
+    public static final String DELETE_OLD_RDN_PROP = JndiPropertyConstants.JNDI_LDAP_DELETE_RDN;
+
+    /** Empty array of controls for use in dealing with them */
+    protected static final Control[] EMPTY_CONTROLS = new Control[0];
+
+    /** The directory service which owns this context **/
+    private final DirectoryService service;
+
+    /** The global registries **/
+    protected final Registries registries;
+
+    /** The interceptor proxy to the backend nexus */
+    private final PartitionNexus nexusProxy;
+
+    /** The cloned environment used by this Context */
+    private final Hashtable<String, Object> env;
+
+    /** The distinguished name of this Context */
+    private final LdapDN dn;
+
+    /** The set of registered NamingListeners */
+    private final Set<NamingListener> listeners = new HashSet<NamingListener>();
+
+    /** The Principal associated with this context */
+    private LdapPrincipal principal;
+
+    /** The request controls to set on operations before performing them */
+    protected Control[] requestControls = EMPTY_CONTROLS;
+
+    /** The response controls to set after performing operations */
+    protected Control[] responseControls = EMPTY_CONTROLS;
+
+    /** Connection level controls associated with the session */
+    protected Control[] connectControls = EMPTY_CONTROLS;
+
+
+    // ------------------------------------------------------------------------
+    // Constructors
+    // ------------------------------------------------------------------------
+
+    /**
+     * Must be called by all subclasses to initialize the nexus proxy and the
+     * environment settings to be used by this Context implementation.  This
+     * specific contstructor relies on the presence of the {@link
+     * Context#PROVIDER_URL} key and value to determine the distinguished name
+     * of the newly created context.  It also checks to make sure the
+     * referenced name actually exists within the system.  This constructor
+     * is used for all InitialContext requests.
+     * 
+     * @param service the parent service that manages this context
+     * @param env the environment properties used by this context.
+     * @throws NamingException if the environment parameters are not set 
+     * correctly.
+     */
+    @SuppressWarnings(value =
+        { "unchecked" })
+    protected ServerContext( DirectoryService service, Hashtable<String, Object> env ) throws NamingException
+    {
+        this.service = service;
+
+        // set references to cloned env and the proxy
+        this.nexusProxy = new PartitionNexusProxy( this, service );
+
+        this.env = env;
+        LdapJndiProperties props = LdapJndiProperties.getLdapJndiProperties( this.env );
+        dn = props.getProviderDn();
+
+        // need to issue a bind operation here
+        doBindOperation( props.getBindDn(), props.getCredentials(), props.getAuthenticationMechanisms(), props
+            .getSaslAuthId() );
+
+        registries = service.getRegistries();
+
+        if ( !nexusProxy.hasEntry( new EntryOperationContext( registries, dn ) ) )
+        {
+            throw new NameNotFoundException( dn + " does not exist" );
+        }
+    }
+
+
+    /**
+     * Must be called by all subclasses to initialize the nexus proxy and the
+     * environment settings to be used by this Context implementation.  This
+     * constructor is used to propagate new contexts from existing contexts.
+     *
+     * @param principal the directory user principal that is propagated
+     * @param dn the distinguished name of this context
+     * @param service the directory service core
+     * @throws NamingException if there is a problem creating the new context
+     */
+    public ServerContext( DirectoryService service, LdapPrincipal principal, Name dn ) throws NamingException
+    {
+        this.service = service;
+        this.dn = ( LdapDN ) dn.clone();
+
+        this.env = new Hashtable<String, Object>();
+        this.env.put( PROVIDER_URL, dn.toString() );
+        this.env.put( DirectoryService.JNDI_KEY, service );
+        this.nexusProxy = new PartitionNexusProxy( this, service );
+        this.principal = principal;
+        registries = service.getRegistries();
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Protected Methods for Control [De]Marshalling 
+    // ------------------------------------------------------------------------
+    // Use these methods instead of manually calling the nexusProxy so we can
+    // add request controls to operation contexts before the call and extract 
+    // response controls from the contexts after the call.  NOTE that the 
+    // requestControls must be cleared after each operation.  This makes a 
+    // context not thread safe.
+    // ------------------------------------------------------------------------
+
+    /**
+     * Used to encapsulate [de]marshalling of controls before and after add operations.
+     * @param entry
+     * @param target
+     */
+    protected void doAddOperation( LdapDN target, ServerEntry entry ) throws NamingException
+    {
+        // setup the op context and populate with request controls
+        AddOperationContext opCtx = new AddOperationContext( service.getRegistries(), entry );
+
+        opCtx.addRequestControls( requestControls );
+
+        // execute add operation
+        nexusProxy.add( opCtx );
+
+        // clear the request controls and set the response controls 
+        requestControls = EMPTY_CONTROLS;
+        responseControls = opCtx.getResponseControls();
+    }
+
+
+    /**
+     * Used to encapsulate [de]marshalling of controls before and after delete operations.
+     * @param target
+     */
+    protected void doDeleteOperation( LdapDN target ) throws NamingException
+    {
+        // setup the op context and populate with request controls
+        DeleteOperationContext opCtx = new DeleteOperationContext( registries, target );
+        opCtx.addRequestControls( requestControls );
+
+        // execute delete operation
+        nexusProxy.delete( opCtx );
+
+        // clear the request controls and set the response controls 
+        requestControls = EMPTY_CONTROLS;
+        responseControls = opCtx.getResponseControls();
+    }
+
+
+    /**
+     * Used to encapsulate [de]marshalling of controls before and after list operations.
+     * @param dn
+     * @param aliasDerefMode
+     * @param filter
+     * @param searchControls
+     * @return NamingEnumeration
+     */
+    protected NamingEnumeration<ServerSearchResult> doSearchOperation( LdapDN dn, AliasDerefMode aliasDerefMode,
+        ExprNode filter, SearchControls searchControls ) throws NamingException
+    {
+        // setup the op context and populate with request controls
+        SearchOperationContext opCtx = new SearchOperationContext( registries, dn, aliasDerefMode, filter,
+            searchControls );
+        opCtx.addRequestControls( requestControls );
+
+        // execute search operation
+        NamingEnumeration<ServerSearchResult> results = nexusProxy.search( opCtx );
+
+        // clear the request controls and set the response controls 
+        requestControls = EMPTY_CONTROLS;
+        responseControls = opCtx.getResponseControls();
+
+        return results;
+    }
+
+
+    /**
+     * Used to encapsulate [de]marshalling of controls before and after list operations.
+     * @param dn
+     * @param aliasDerefMode
+     * @param filter
+     * @param searchControls
+     * @return NamingEnumeration
+     */
+    protected NamingEnumeration<ServerSearchResult> doSearchOperation( LdapDN dn, AliasDerefMode aliasDerefMode,
+        ExprNode filter, SearchControls searchControls, InetSocketAddress clientAddress ) throws NamingException
+    {
+        // setup the op context and populate with request controls
+        SearchOperationContext opCtx = new SearchOperationContext( registries, dn, aliasDerefMode, filter,
+            searchControls );
+
+        opCtx.put( "client_hostname", clientAddress.getHostName() );
+        opCtx.put( "client_port", String.valueOf( clientAddress.getPort() ) );
+        opCtx.addRequestControls( requestControls );
+
+        // execute search operation
+        NamingEnumeration<ServerSearchResult> results = nexusProxy.search( opCtx );
+
+        // clear the request controls and set the response controls 
+        requestControls = EMPTY_CONTROLS;
+        responseControls = opCtx.getResponseControls();
+
+        return results;
+    }
+
+
+    /**
+     * Used to encapsulate [de]marshalling of controls before and after list operations.
+     */
+    protected NamingEnumeration<ServerSearchResult> doListOperation( LdapDN target ) throws NamingException
+    {
+        // setup the op context and populate with request controls
+        ListOperationContext opCtx = new ListOperationContext( registries, target );
+        opCtx.addRequestControls( requestControls );
+
+        // execute list operation
+        NamingEnumeration<ServerSearchResult> results = nexusProxy.list( opCtx );
+
+        // clear the request controls and set the response controls 
+        requestControls = EMPTY_CONTROLS;
+        responseControls = opCtx.getResponseControls();
+
+        return results;
+    }
+
+
+    protected ServerEntry doGetRootDSEOperation( LdapDN target ) throws NamingException
+    {
+        GetRootDSEOperationContext opCtx = new GetRootDSEOperationContext( registries, target );
+        opCtx.addRequestControls( requestControls );
+
+        // do not reset request controls since this is not an external 
+        // operation and not do bother setting the response controls either
+        return nexusProxy.getRootDSE( opCtx );
+    }
+
+
+    /**
+     * Used to encapsulate [de]marshalling of controls before and after lookup operations.
+     */
+    protected ServerEntry doLookupOperation( LdapDN target ) throws NamingException
+    {
+        // setup the op context and populate with request controls
+        LookupOperationContext opCtx;
+
+        // execute lookup/getRootDSE operation
+        opCtx = new LookupOperationContext( registries, target );
+        opCtx.addRequestControls( requestControls );
+        ServerEntry serverEntry = nexusProxy.lookup( opCtx );
+
+        // clear the request controls and set the response controls 
+        requestControls = EMPTY_CONTROLS;
+        responseControls = opCtx.getResponseControls();
+        return serverEntry;
+    }
+
+
+    /**
+     * Used to encapsulate [de]marshalling of controls before and after lookup operations.
+     */
+    protected ServerEntry doLookupOperation( LdapDN target, String[] attrIds ) throws NamingException
+    {
+        // setup the op context and populate with request controls
+        LookupOperationContext opCtx;
+
+        // execute lookup/getRootDSE operation
+        opCtx = new LookupOperationContext( registries, target, attrIds );
+        opCtx.addRequestControls( requestControls );
+        ServerEntry serverEntry = nexusProxy.lookup( opCtx );
+
+        // clear the request controls and set the response controls 
+        requestControls = EMPTY_CONTROLS;
+        responseControls = opCtx.getResponseControls();
+
+        // Now remove the ObjectClass attribute if it has not been requested
+        if ( ( opCtx.getAttrsId() != null ) && ( opCtx.getAttrsId().size() != 0 ) )
+        {
+            if ( ( serverEntry.get( SchemaConstants.OBJECT_CLASS_AT ) != null )
+                && ( serverEntry.get( SchemaConstants.OBJECT_CLASS_AT ).size() == 0 ) )
+            {
+                serverEntry.removeAttributes( SchemaConstants.OBJECT_CLASS_AT );
+            }
+        }
+
+        return serverEntry;
+    }
+
+
+    /**
+     * Used to encapsulate [de]marshalling of controls before and after bind operations.
+     */
+    protected void doBindOperation( LdapDN bindDn, byte[] credentials, List<String> mechanisms, String saslAuthId )
+        throws NamingException
+    {
+        // setup the op context and populate with request controls
+        BindOperationContext opCtx = new BindOperationContext( registries );
+        opCtx.setDn( bindDn );
+        opCtx.setCredentials( credentials );
+        opCtx.setMechanisms( mechanisms );
+        opCtx.setSaslAuthId( saslAuthId );
+        opCtx.addRequestControls( requestControls );
+
+        // execute bind operation
+        this.nexusProxy.bind( opCtx );
+
+        // clear the request controls and set the response controls 
+        requestControls = EMPTY_CONTROLS;
+        responseControls = opCtx.getResponseControls();
+    }
+
+
+    /**
+     * Used to encapsulate [de]marshalling of controls before and after moveAndRename operations.
+     */
+    protected void doMoveAndRenameOperation( LdapDN oldDn, LdapDN parent, String newRdn, boolean delOldDn )
+        throws NamingException
+    {
+        // setup the op context and populate with request controls
+        MoveAndRenameOperationContext opCtx = new MoveAndRenameOperationContext( registries, oldDn, parent, new Rdn(
+            newRdn ), delOldDn );
+        opCtx.addRequestControls( requestControls );
+
+        // execute moveAndRename operation
+        nexusProxy.moveAndRename( opCtx );
+
+        // clear the request controls and set the response controls 
+        requestControls = EMPTY_CONTROLS;
+        responseControls = opCtx.getResponseControls();
+    }
+
+
+    /**
+     * Used to encapsulate [de]marshalling of controls before and after modify operations.
+     */
+    protected void doModifyOperation( LdapDN dn, List<Modification> modifications ) throws NamingException
+    {
+        // setup the op context and populate with request controls
+        ModifyOperationContext opCtx = new ModifyOperationContext( registries, dn, modifications );
+        opCtx.addRequestControls( requestControls );
+
+        // execute modify operation
+        nexusProxy.modify( opCtx );
+
+        // clear the request controls and set the response controls 
+        requestControls = EMPTY_CONTROLS;
+        responseControls = opCtx.getResponseControls();
+    }
+
+
+    /**
+     * Used to encapsulate [de]marshalling of controls before and after moveAndRename operations.
+     */
+    protected void doMove( LdapDN oldDn, LdapDN target ) throws NamingException
+    {
+        // setup the op context and populate with request controls
+        MoveOperationContext opCtx = new MoveOperationContext( registries, oldDn, target );
+        opCtx.addRequestControls( requestControls );
+
+        // execute move operation
+        nexusProxy.move( opCtx );
+
+        // clear the request controls and set the response controls 
+        requestControls = EMPTY_CONTROLS;
+        responseControls = opCtx.getResponseControls();
+    }
+
+
+    /**
+     * Used to encapsulate [de]marshalling of controls before and after rename operations.
+     */
+    protected void doRename( LdapDN oldDn, String newRdn, boolean delOldRdn ) throws NamingException
+    {
+        // setup the op context and populate with request controls
+        RenameOperationContext opCtx = new RenameOperationContext( registries, oldDn, new Rdn( newRdn ), delOldRdn );
+        opCtx.addRequestControls( requestControls );
+
+        // execute rename operation
+        nexusProxy.rename( opCtx );
+
+        // clear the request controls and set the response controls 
+        requestControls = EMPTY_CONTROLS;
+        responseControls = opCtx.getResponseControls();
+    }
+
+
+    // ------------------------------------------------------------------------
+    // New Impl Specific Public Methods
+    // ------------------------------------------------------------------------
+
+    /**
+     * Gets a handle on the root context of the DIT.  The RootDSE as the present user.
+     *
+     * @return the rootDSE context
+     * @throws NamingException if this fails
+     */
+    public abstract ServerContext getRootContext() throws NamingException;
+
+
+    /**
+     * Gets the {@link DirectoryService} associated with this context.
+     *
+     * @return the directory service associated with this context
+     */
+    public DirectoryService getService()
+    {
+        return service;
+    }
+
+
+    /**
+     * Gets the principal of the authenticated user which also happens to own
+     *
+     * @return the principal associated with this context
+     */
+    public LdapPrincipal getPrincipal()
+    {
+        return principal;
+    }
+
+
+    /**
+     * Sets the principal of the authenticated user which also happens to own.
+     * This method can be invoked only once to keep this property safe.  This
+     * method has been changed to be public but it can only be set by the
+     * AuthenticationInterceptor to prevent malicious code from changing the
+     * effective principal.
+     *
+     * @param wrapper the wrapper - has to go
+     * @todo get ride of using this wrapper and protect this call with a security manager
+     */
+    public void setPrincipal( AuthenticationInterceptor.TrustedPrincipalWrapper wrapper )
+    {
+        this.principal = wrapper.getPrincipal();
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Protected Accessor Methods
+    // ------------------------------------------------------------------------
+
+    /**
+     * Gets the RootNexus proxy.
+     *
+     * @return the proxy to the backend nexus.
+     */
+    protected PartitionNexus getNexusProxy()
+    {
+        return nexusProxy;
+    }
+
+
+    /**
+     * Gets the distinguished name of the entry associated with this Context.
+     * 
+     * @return the distinguished name of this Context's entry.
+     */
+    protected Name getDn()
+    {
+        return dn;
+    }
+
+
+    // ------------------------------------------------------------------------
+    // JNDI Context Interface Methods
+    // ------------------------------------------------------------------------
+
+    /**
+     * @see javax.naming.Context#close()
+     */
+    public void close() throws NamingException
+    {
+        for ( NamingListener listener : listeners )
+        {
+            ( ( PartitionNexusProxy ) this.nexusProxy ).removeNamingListener( this, listener );
+        }
+    }
+
+
+    /**
+     * @see javax.naming.Context#getNameInNamespace()
+     */
+    public String getNameInNamespace() throws NamingException
+    {
+        return dn.getUpName();
+    }
+
+
+    /**
+     * @see javax.naming.Context#getEnvironment()
+     */
+    public Hashtable<String, Object> getEnvironment()
+    {
+        return env;
+    }
+
+
+    /**
+     * @see javax.naming.Context#addToEnvironment(java.lang.String, 
+     * java.lang.Object)
+     */
+    public Object addToEnvironment( String propName, Object propVal ) throws NamingException
+    {
+        return env.put( propName, propVal );
+    }
+
+
+    /**
+     * @see javax.naming.Context#removeFromEnvironment(java.lang.String)
+     */
+    public Object removeFromEnvironment( String propName ) throws NamingException
+    {
+        return env.remove( propName );
+    }
+
+
+    /**
+     * @see javax.naming.Context#createSubcontext(java.lang.String)
+     */
+    public Context createSubcontext( String name ) throws NamingException
+    {
+        return createSubcontext( new LdapDN( name ) );
+    }
+
+
+    /**
+     * @see javax.naming.Context#createSubcontext(javax.naming.Name)
+     */
+    public Context createSubcontext( Name name ) throws NamingException
+    {
+        LdapDN target = buildTarget( name );
+        ServerEntry serverEntry = new DefaultServerEntry( registries, target );
+        serverEntry.add( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC, JavaLdapSupport.JCONTAINER_ATTR );
+
+        // Now add the CN attribute, which is mandatory
+        Rdn rdn = target.getRdn();
+
+        if ( rdn != null )
+        {
+            if ( SchemaConstants.CN_AT.equals( rdn.getNormType() ) )
+            {
+                serverEntry.put( rdn.getUpType(), ( String ) rdn.getUpValue() );
+            }
+            else
+            {
+                // No CN in the rdn, this is an error
+                throw new LdapSchemaViolationException( name
+                    + " does not contains the mandatory 'cn' attribute for JavaContainer ObjectClass!",
+                    ResultCodeEnum.OBJECT_CLASS_VIOLATION );
+            }
+        }
+        else
+        {
+            // No CN in the rdn, this is an error
+            throw new LdapSchemaViolationException( name
+                + " does not contains the mandatory 'cn' attribute for JavaContainer ObjectClass!",
+                ResultCodeEnum.OBJECT_CLASS_VIOLATION );
+        }
+
+        /*
+         * Add the new context to the server which as a side effect adds 
+         * operational attributes to the serverEntry refering instance which
+         * can them be used to initialize a new ServerLdapContext.  Remember
+         * we need to copy over the controls as well to propagate the complete 
+         * environment besides what's in the hashtable for env.
+         */
+        doAddOperation( target, serverEntry );
+        return new ServerLdapContext( service, principal, target );
+    }
+
+
+    /**
+     * @see javax.naming.Context#destroySubcontext(java.lang.String)
+     */
+    public void destroySubcontext( String name ) throws NamingException
+    {
+        destroySubcontext( new LdapDN( name ) );
+    }
+
+
+    /**
+     * @see javax.naming.Context#destroySubcontext(javax.naming.Name)
+     */
+    public void destroySubcontext( Name name ) throws NamingException
+    {
+        LdapDN target = buildTarget( name );
+
+        if ( target.size() == 0 )
+        {
+            throw new LdapNoPermissionException( "can't delete the rootDSE" );
+        }
+
+        doDeleteOperation( target );
+    }
+
+
+    /**
+     * @see javax.naming.Context#bind(java.lang.String, java.lang.Object)
+     */
+    public void bind( String name, Object obj ) throws NamingException
+    {
+        bind( new LdapDN( name ), obj );
+    }
+
+
+    private void injectRdnAttributeValues( LdapDN target, ServerEntry serverEntry ) throws NamingException
+    {
+        // Add all the RDN attributes and their values to this entry
+        Rdn rdn = target.getRdn( target.size() - 1 );
+
+        if ( rdn.size() == 1 )
+        {
+            serverEntry.put( rdn.getUpType(), ( String ) rdn.getValue() );
+        }
+        else
+        {
+            for ( AttributeTypeAndValue atav : rdn )
+            {
+                serverEntry.put( atav.getUpType(), ( String ) atav.getNormValue() );
+            }
+        }
+    }
+
+
+    /**
+     * @see javax.naming.Context#bind(javax.naming.Name, java.lang.Object)
+     */
+    public void bind( Name name, Object obj ) throws NamingException
+    {
+        // First, use state factories to do a transformation
+        DirStateFactory.Result res = DirectoryManager.getStateToBind( obj, name, this, env, null );
+
+        LdapDN target = buildTarget( name );
+
+        // let's be sure that the Attributes is case insensitive
+        ServerEntry outServerEntry = ServerEntryUtils.toServerEntry( AttributeUtils.toCaseInsensitive( res
+            .getAttributes() ), target, registries );
+
+        if ( outServerEntry != null )
+        {
+            doAddOperation( target, outServerEntry );
+            return;
+        }
+
+        if ( obj instanceof ServerEntry )
+        {
+            doAddOperation( target, ( ServerEntry ) obj );
+        }
+        // Check for Referenceable
+        else if ( obj instanceof Referenceable )
+        {
+            throw new NamingException( "Do not know how to store Referenceables yet!" );
+        }
+        // Store different formats
+        else if ( obj instanceof Reference )
+        {
+            // Store as ref and add outAttrs
+            throw new NamingException( "Do not know how to store References yet!" );
+        }
+        else if ( obj instanceof Serializable )
+        {
+            // Serialize and add outAttrs
+            ServerEntry serverEntry = new DefaultServerEntry( registries, target );
+
+            if ( ( outServerEntry != null ) && ( outServerEntry.size() > 0 ) )
+            {
+                for ( EntryAttribute serverAttribute : outServerEntry )
+                {
+                    serverEntry.put( serverAttribute );
+                }
+            }
+
+            // Get target and inject all rdn attributes into entry
+            injectRdnAttributeValues( target, serverEntry );
+
+            // Serialize object into entry attributes and add it.
+            JavaLdapSupport.serialize( serverEntry, obj, registries );
+            doAddOperation( target, serverEntry );
+        }
+        else if ( obj instanceof DirContext )
+        {
+            // Grab attributes and merge with outAttrs
+            ServerEntry serverEntry = ServerEntryUtils.toServerEntry( ( ( DirContext ) obj ).getAttributes( "" ),
+                target, registries );
+
+            if ( ( outServerEntry != null ) && ( outServerEntry.size() > 0 ) )
+            {
+                for ( EntryAttribute serverAttribute : outServerEntry )
+                {
+                    serverEntry.put( serverAttribute );
+                }
+            }
+
+            injectRdnAttributeValues( target, serverEntry );
+            doAddOperation( target, serverEntry );
+        }
+        else
+        {
+            throw new NamingException( "Can't find a way to bind: " + obj );
+        }
+    }
+
+
+    /**
+     * @see javax.naming.Context#rename(java.lang.String, java.lang.String)
+     */
+    public void rename( String oldName, String newName ) throws NamingException
+    {
+        rename( new LdapDN( oldName ), new LdapDN( newName ) );
+    }
+
+
+    /**
+     * @see javax.naming.Context#rename(javax.naming.Name, javax.naming.Name)
+     */
+    public void rename( Name oldName, Name newName ) throws NamingException
+    {
+        LdapDN oldDn = buildTarget( oldName );
+        LdapDN newDn = buildTarget( newName );
+
+        if ( oldDn.size() == 0 )
+        {
+            throw new LdapNoPermissionException( "can't rename the rootDSE" );
+        }
+
+        // calculate parents
+        LdapDN oldBase = ( LdapDN ) oldName.clone();
+        oldBase.remove( oldName.size() - 1 );
+        LdapDN newBase = ( LdapDN ) newName.clone();
+        newBase.remove( newName.size() - 1 );
+
+        String newRdn = newName.get( newName.size() - 1 );
+        String oldRdn = oldName.get( oldName.size() - 1 );
+        boolean delOldRdn = true;
+
+        /*
+         * Attempt to use the java.naming.ldap.deleteRDN environment property
+         * to get an override for the deleteOldRdn option to modifyRdn.  
+         */
+        if ( null != env.get( DELETE_OLD_RDN_PROP ) )
+        {
+            String delOldRdnStr = ( String ) env.get( DELETE_OLD_RDN_PROP );
+            delOldRdn = !delOldRdnStr.equalsIgnoreCase( "false" ) && !delOldRdnStr.equalsIgnoreCase( "no" )
+                && !delOldRdnStr.equals( "0" );
+        }
+
+        /*
+         * We need to determine if this rename operation corresponds to a simple
+         * RDN name change or a move operation.  If the two names are the same
+         * except for the RDN then it is a simple modifyRdn operation.  If the
+         * names differ in size or have a different baseDN then the operation is
+         * a move operation.  Furthermore if the RDN in the move operation 
+         * changes it is both an RDN change and a move operation.
+         */
+        if ( ( oldName.size() == newName.size() ) && oldBase.equals( newBase ) )
+        {
+            doRename( oldDn, newRdn, delOldRdn );
+        }
+        else
+        {
+            LdapDN target = ( LdapDN ) newDn.clone();
+            target.remove( newDn.size() - 1 );
+
+            if ( newRdn.equalsIgnoreCase( oldRdn ) )
+            {
+                doMove( oldDn, target );
+            }
+            else
+            {
+                doMoveAndRenameOperation( oldDn, target, newRdn, delOldRdn );
+            }
+        }
+    }
+
+
+    /**
+     * @see javax.naming.Context#rebind(java.lang.String, java.lang.Object)
+     */
+    public void rebind( String name, Object obj ) throws NamingException
+    {
+        rebind( new LdapDN( name ), obj );
+    }
+
+
+    /**
+     * @see javax.naming.Context#rebind(javax.naming.Name, java.lang.Object)
+     */
+    public void rebind( Name name, Object obj ) throws NamingException
+    {
+        LdapDN target = buildTarget( name );
+
+        if ( nexusProxy.hasEntry( new EntryOperationContext( registries, target ) ) )
+        {
+            doDeleteOperation( target );
+        }
+
+        bind( name, obj );
+    }
+
+
+    /**
+     * @see javax.naming.Context#unbind(java.lang.String)
+     */
+    public void unbind( String name ) throws NamingException
+    {
+        unbind( new LdapDN( name ) );
+    }
+
+
+    /**
+     * @see javax.naming.Context#unbind(javax.naming.Name)
+     */
+    public void unbind( Name name ) throws NamingException
+    {
+        doDeleteOperation( buildTarget( name ) );
+    }
+
+
+    /**
+     * @see javax.naming.Context#lookup(java.lang.String)
+     */
+    public Object lookup( String name ) throws NamingException
+    {
+        if ( StringTools.isEmpty( name ) )
+        {
+            return lookup( LdapDN.EMPTY_LDAPDN );
+        }
+        else
+        {
+            return lookup( new LdapDN( name ) );
+        }
+    }
+
+
+    /**
+     * @see javax.naming.Context#lookup(javax.naming.Name)
+     */
+    public Object lookup( Name name ) throws NamingException
+    {
+        Object obj;
+        LdapDN target = buildTarget( name );
+
+        ServerEntry serverEntry;
+
+        if ( name.size() == 0 )
+        {
+            serverEntry = doGetRootDSEOperation( target );
+        }
+        else
+        {
+            serverEntry = doLookupOperation( target );
+        }
+
+        try
+        {
+            obj = DirectoryManager.getObjectInstance( null, name, this, env, ServerEntryUtils
+                .toAttributesImpl( serverEntry ) );
+        }
+        catch ( Exception e )
+        {
+            String msg = "Failed to create an object for " + target;
+            msg += " using object factories within the context's environment.";
+            NamingException ne = new NamingException( msg );
+            ne.setRootCause( e );
+            throw ne;
+        }
+
+        if ( obj != null )
+        {
+            return obj;
+        }
+
+        // First lets test and see if the entry is a serialized java object
+        if ( serverEntry.get( JavaLdapSupport.JCLASSNAME_ATTR ) != null )
+        {
+            // Give back serialized object and not a context
+            return JavaLdapSupport.deserialize( serverEntry );
+        }
+
+        // Initialize and return a context since the entry is not a java object
+        return new ServerLdapContext( service, principal, target );
+    }
+
+
+    /**
+     * @see javax.naming.Context#lookupLink(java.lang.String)
+     */
+    public Object lookupLink( String name ) throws NamingException
+    {
+        throw new UnsupportedOperationException();
+    }
+
+
+    /**
+     * @see javax.naming.Context#lookupLink(javax.naming.Name)
+     */
+    public Object lookupLink( Name name ) throws NamingException
+    {
+        throw new UnsupportedOperationException();
+    }
+
+
+    /**
+     * Non-federated implementation presuming the name argument is not a 
+     * composite name spanning multiple namespaces but a compound name in 
+     * the same LDAP namespace.  Hence the parser returned is always the
+     * same as calling this method with the empty String. 
+     * 
+     * @see javax.naming.Context#getNameParser(java.lang.String)
+     */
+    public NameParser getNameParser( String name ) throws NamingException
+    {
+        return new NameParser()
+        {
+            public Name parse( String name ) throws NamingException
+            {
+                return new LdapDN( name );
+            }
+        };
+    }
+
+
+    /**
+     * Non-federated implementation presuming the name argument is not a 
+     * composite name spanning multiple namespaces but a compound name in 
+     * the same LDAP namespace.  Hence the parser returned is always the
+     * same as calling this method with the empty String Name.
+     * 
+     * @see javax.naming.Context#getNameParser(javax.naming.Name)
+     */
+    public NameParser getNameParser( Name name ) throws NamingException
+    {
+        return new NameParser()
+        {
+            public Name parse( String name ) throws NamingException
+            {
+                return new LdapDN( name );
+            }
+        };
+    }
+
+
+    /**
+     * @see javax.naming.Context#list(java.lang.String)
+     */
+    @SuppressWarnings(value =
+        { "unchecked" })
+    public NamingEnumeration list( String name ) throws NamingException
+    {
+        return list( new LdapDN( name ) );
+    }
+
+
+    /**
+     * @see javax.naming.Context#list(javax.naming.Name)
+     */
+    @SuppressWarnings(value =
+        { "unchecked" })
+    public NamingEnumeration list( Name name ) throws NamingException
+    {
+        return ServerEntryUtils.toSearchResultEnum( doListOperation( buildTarget( name ) ) );
+    }
+
+
+    /**
+     * @see javax.naming.Context#listBindings(java.lang.String)
+     */
+    @SuppressWarnings(value =
+        { "unchecked" })
+    public NamingEnumeration listBindings( String name ) throws NamingException
+    {
+        return listBindings( new LdapDN( name ) );
+    }
+
+
+    /**
+     * @see javax.naming.Context#listBindings(javax.naming.Name)
+     */
+    @SuppressWarnings(value =
+        { "unchecked" })
+    public NamingEnumeration listBindings( Name name ) throws NamingException
+    {
+        // Conduct a special one level search at base for all objects
+        LdapDN base = buildTarget( name );
+        PresenceNode filter = new PresenceNode( SchemaConstants.OBJECT_CLASS_AT );
+        SearchControls ctls = new SearchControls();
+        ctls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+        AliasDerefMode aliasDerefMode = AliasDerefMode.getEnum( getEnvironment() );
+        return doSearchOperation( base, aliasDerefMode, filter, ctls );
+    }
+
+
+    /**
+     * @see javax.naming.Context#composeName(java.lang.String, java.lang.String)
+     */
+    public String composeName( String name, String prefix ) throws NamingException
+    {
+        return composeName( new LdapDN( name ), new LdapDN( prefix ) ).toString();
+    }
+
+
+    /**
+     * @see javax.naming.Context#composeName(javax.naming.Name,
+     * javax.naming.Name)
+     */
+    public Name composeName( Name name, Name prefix ) throws NamingException
+    {
+        // No prefix reduces to name, or the name relative to this context
+        if ( prefix == null || prefix.size() == 0 )
+        {
+            return name;
+        }
+
+        /*
+         * Example: This context is ou=people and say name is the relative
+         * name of uid=jwalker and the prefix is dc=domain.  Then we must
+         * compose the name relative to prefix which would be:
+         * 
+         * uid=jwalker,ou=people,dc=domain.
+         * 
+         * The following general algorithm generates the right name:
+         *      1). Find the Dn for name and walk it from the head to tail
+         *          trying to match for the head of prefix.
+         *      2). Remove name components from the Dn until a match for the 
+         *          head of the prefix is found.
+         *      3). Return the remainder of the fqn or Dn after chewing off some
+         */
+
+        // 1). Find the Dn for name and walk it from the head to tail
+        Name fqn = buildTarget( name );
+        String head = prefix.get( 0 );
+
+        // 2). Walk the fqn trying to match for the head of the prefix
+        while ( fqn.size() > 0 )
+        {
+            // match found end loop
+            if ( fqn.get( 0 ).equalsIgnoreCase( head ) )
+            {
+                return fqn;
+            }
+            else
+            // 2). Remove name components from the Dn until a match 
+            {
+                fqn.remove( 0 );
+            }
+        }
+
+        String msg = "The prefix '" + prefix + "' is not an ancestor of this ";
+        msg += "entry '" + dn + "'";
+        throw new NamingException( msg );
+    }
+
+
+    // ------------------------------------------------------------------------
+    // EventContext implementations
+    // ------------------------------------------------------------------------
+
+    public void addNamingListener( Name name, int scope, NamingListener namingListener ) throws NamingException
+    {
+        ExprNode filter = new PresenceNode( SchemaConstants.OBJECT_CLASS_AT );
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( scope );
+        ( ( PartitionNexusProxy ) this.nexusProxy ).addNamingListener( this, buildTarget( name ), filter, controls,
+            namingListener );
+        listeners.add( namingListener );
+    }
+
+
+    public void addNamingListener( String name, int scope, NamingListener namingListener ) throws NamingException
+    {
+        addNamingListener( new LdapDN( name ), scope, namingListener );
+    }
+
+
+    public void removeNamingListener( NamingListener namingListener ) throws NamingException
+    {
+        ( ( PartitionNexusProxy ) this.nexusProxy ).removeNamingListener( this, namingListener );
+        listeners.remove( namingListener );
+    }
+
+
+    public boolean targetMustExist() throws NamingException
+    {
+        return false;
+    }
+
+
+    /**
+     * Allows subclasses to register and unregister listeners.
+     *
+     * @return the set of listeners used for tracking registered name listeners.
+     */
+    protected Set<NamingListener> getListeners()
+    {
+        return listeners;
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Utility Methods to Reduce Code
+    // ------------------------------------------------------------------------
+
+    /**
+     * Clones this context's DN and adds the components of the name relative to 
+     * this context to the left hand side of this context's cloned DN. 
+     * 
+     * @param relativeName a name relative to this context.
+     * @return the name of the target
+     * @throws InvalidNameException if relativeName is not a valid name in
+     *      the LDAP namespace.
+     */
+    LdapDN buildTarget( Name relativeName ) throws InvalidNameException
+    {
+        LdapDN target = ( LdapDN ) dn.clone();
+
+        // Add to left hand side of cloned DN the relative name arg
+        target.addAllNormalized( target.size(), relativeName );
+        return target;
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/jndi/ServerDirContext.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/jndi/ServerDirContext.java
new file mode 100644
index 0000000..068f025
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/jndi/ServerDirContext.java
@@ -0,0 +1,864 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.jndi;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.authn.LdapPrincipal;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerEntryUtils;
+import org.apache.directory.server.core.interceptor.context.EntryOperationContext;
+import org.apache.directory.server.core.partition.PartitionNexusProxy;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.entry.Modification;
+import org.apache.directory.shared.ldap.entry.client.ClientBinaryValue;
+import org.apache.directory.shared.ldap.entry.client.ClientStringValue;
+import org.apache.directory.shared.ldap.filter.AndNode;
+import org.apache.directory.shared.ldap.filter.BranchNode;
+import org.apache.directory.shared.ldap.filter.EqualityNode;
+import org.apache.directory.shared.ldap.filter.ExprNode;
+import org.apache.directory.shared.ldap.filter.FilterParser;
+import org.apache.directory.shared.ldap.filter.PresenceNode;
+import org.apache.directory.shared.ldap.filter.SimpleNode;
+import org.apache.directory.shared.ldap.message.AliasDerefMode;
+import org.apache.directory.shared.ldap.message.ModificationItemImpl;
+import org.apache.directory.shared.ldap.name.AttributeTypeAndValue;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.name.Rdn;
+import org.apache.directory.shared.ldap.util.AttributeUtils;
+import org.apache.directory.shared.ldap.util.StringTools;
+
+import javax.naming.Name;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.Reference;
+import javax.naming.Referenceable;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.InvalidSearchFilterException;
+import javax.naming.directory.ModificationItem;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.event.EventDirContext;
+import javax.naming.event.NamingListener;
+import javax.naming.spi.DirStateFactory;
+import javax.naming.spi.DirectoryManager;
+import java.io.Serializable;
+import java.net.InetSocketAddress;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+
+
+/**
+ * The DirContext implementation for the Server Side JNDI LDAP provider.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public abstract class ServerDirContext extends ServerContext implements EventDirContext
+{
+    // ------------------------------------------------------------------------
+    // Constructors
+    // ------------------------------------------------------------------------
+
+    /**
+     * Creates a new ServerDirContext by reading the PROVIDER_URL to resolve the
+     * distinguished name for this context.
+     *
+     * @param service the parent service that manages this context
+     * @param env the environment used for this context
+     * @throws NamingException if something goes wrong
+     */
+    public ServerDirContext( DirectoryService service, Hashtable<String, Object> env ) throws NamingException
+    {
+        super( service, env );
+    }
+
+
+    /**
+     * Creates a new ServerDirContext with a distinguished name which is used to
+     * set the PROVIDER_URL to the distinguished name for this context.
+     *
+     * @param principal the principal which is propagated
+     * @param dn the distinguished name of this context
+     */
+    public ServerDirContext( DirectoryService service, LdapPrincipal principal, Name dn ) throws NamingException
+    {
+        super( service, principal, dn );
+    }
+
+
+    // ------------------------------------------------------------------------
+    // DirContext Implementations
+    // ------------------------------------------------------------------------
+
+    /**
+     * @see javax.naming.directory.DirContext#getAttributes(java.lang.String)
+     */
+    public Attributes getAttributes( String name ) throws NamingException
+    {
+        return getAttributes( new LdapDN( name ) );
+    }
+
+
+    /**
+     * @see javax.naming.directory.DirContext#getAttributes(javax.naming.Name)
+     */
+    public Attributes getAttributes( Name name ) throws NamingException
+    {
+        return ServerEntryUtils.toAttributesImpl( doLookupOperation( buildTarget( name ) ) );
+    }
+
+
+    /**
+     * @see javax.naming.directory.DirContext#getAttributes(java.lang.String,
+     *      java.lang.String[])
+     */
+    public Attributes getAttributes( String name, String[] attrIds ) throws NamingException
+    {
+        return getAttributes( new LdapDN( name ), attrIds );
+    }
+
+
+    /**
+     * @see javax.naming.directory.DirContext#getAttributes(javax.naming.Name,
+     *      java.lang.String[])
+     */
+    public Attributes getAttributes( Name name, String[] attrIds ) throws NamingException
+    {
+        return ServerEntryUtils.toAttributesImpl( doLookupOperation( buildTarget( name ), attrIds ) );
+    }
+
+
+    /**
+     * @see javax.naming.directory.DirContext#modifyAttributes(java.lang.String,
+     *      int, javax.naming.directory.Attributes)
+     */
+    public void modifyAttributes( String name, int modOp, Attributes attrs ) throws NamingException
+    {
+        modifyAttributes( new LdapDN( name ), modOp, AttributeUtils.toCaseInsensitive( attrs ) );
+    }
+
+
+    /**
+     * @see javax.naming.directory.DirContext#modifyAttributes(java.lang.String,
+     *      int, javax.naming.directory.Attributes)
+     */
+    public void modifyAttributes( Name name, int modOp, Attributes attrs ) throws NamingException
+    {
+        List<ModificationItemImpl> modItems = null;
+
+        if ( attrs != null )
+        {
+            modItems = new ArrayList<ModificationItemImpl>( attrs.size() );
+            NamingEnumeration<? extends Attribute> e = ( NamingEnumeration<? extends Attribute> ) attrs.getAll();
+
+            while ( e.hasMore() )
+            {
+                modItems.add( new ModificationItemImpl( modOp, e.next() ) );
+            }
+        }
+
+        List<Modification> newMods = ServerEntryUtils.toServerModification( modItems, registries
+            .getAttributeTypeRegistry() );
+
+        if ( name instanceof LdapDN )
+        {
+            doModifyOperation( buildTarget( name ), newMods );
+        }
+        else
+        {
+            doModifyOperation( buildTarget( new LdapDN( name ) ), newMods );
+        }
+    }
+
+
+    /**
+     * @see javax.naming.directory.DirContext#modifyAttributes(java.lang.String,
+     *      javax.naming.directory.ModificationItem[])
+     */
+    public void modifyAttributes( String name, ModificationItem[] mods ) throws NamingException
+    {
+        ModificationItemImpl[] newMods = new ModificationItemImpl[mods.length];
+
+        for ( int i = 0; i < mods.length; i++ )
+        {
+            newMods[i] = new ModificationItemImpl( mods[i] );
+        }
+
+        modifyAttributes( new LdapDN( name ), newMods );
+    }
+
+
+    /**
+     * @see javax.naming.directory.DirContext#modifyAttributes(java.lang.String,
+     *      javax.naming.directory.ModificationItem[])
+     */
+    public void modifyAttributes( String name, ModificationItemImpl[] mods ) throws NamingException
+    {
+        modifyAttributes( new LdapDN( name ), mods );
+    }
+
+
+    /**
+     * @see javax.naming.directory.DirContext#modifyAttributes(
+     * javax.naming.Name, javax.naming.directory.ModificationItem[])
+     */
+    public void modifyAttributes( Name name, ModificationItem[] mods ) throws NamingException
+    {
+        List<Modification> newMods = ServerEntryUtils
+            .toServerModification( mods, registries.getAttributeTypeRegistry() );
+        doModifyOperation( buildTarget( new LdapDN( name ) ), newMods );
+    }
+
+
+    /**
+     * @see javax.naming.directory.DirContext#modifyAttributes(
+     * javax.naming.Name, javax.naming.directory.ModificationItem[])
+     */
+    public void modifyAttributes( Name name, List<ModificationItemImpl> mods ) throws NamingException
+    {
+        List<Modification> newMods = ServerEntryUtils
+            .toServerModification( mods, registries.getAttributeTypeRegistry() );
+        doModifyOperation( buildTarget( new LdapDN( name ) ), newMods );
+    }
+
+
+    /**
+     * @see javax.naming.directory.DirContext#bind(java.lang.String,
+     *      java.lang.Object, javax.naming.directory.Attributes)
+     */
+    public void bind( String name, Object obj, Attributes attrs ) throws NamingException
+    {
+        bind( new LdapDN( name ), obj, AttributeUtils.toCaseInsensitive( attrs ) );
+    }
+
+
+    /**
+     * @see javax.naming.directory.DirContext#bind(javax.naming.Name,
+     *      java.lang.Object, javax.naming.directory.Attributes)
+     */
+    public void bind( Name name, Object obj, Attributes attrs ) throws NamingException
+    {
+        if ( ( null == obj ) && ( null == attrs ) )
+        {
+            throw new NamingException( "Both obj and attrs args are null. "
+                + "At least one of these parameters must not be null." );
+        }
+
+        // A null attrs defaults this to the Context.bind() operation
+        if ( null == attrs )
+        {
+            super.bind( name, obj );
+            return;
+        }
+
+        LdapDN target = buildTarget( name );
+
+        ServerEntry serverEntry = ServerEntryUtils.toServerEntry( AttributeUtils.toCaseInsensitive( attrs ), target,
+            registries );
+
+        // No object binding so we just add the attributes
+        if ( null == obj )
+        {
+            ServerEntry clone = ( ServerEntry ) serverEntry.clone();
+            doAddOperation( target, clone );
+            return;
+        }
+
+        // First, use state factories to do a transformation
+        DirStateFactory.Result res = DirectoryManager.getStateToBind( obj, name, this, getEnvironment(), attrs );
+        ServerEntry outServerEntry = ServerEntryUtils.toServerEntry( res.getAttributes(), target, registries );
+
+        if ( outServerEntry != serverEntry )
+        {
+            ServerEntry clone = ( ServerEntry ) serverEntry.clone();
+
+            if ( ( outServerEntry != null ) && ( outServerEntry.size() > 0 ) )
+            {
+                for ( EntryAttribute attribute : outServerEntry )
+                {
+                    clone.put( attribute );
+                }
+            }
+
+            doAddOperation( target, clone );
+            return;
+        }
+
+        // Check for Referenceable
+        if ( obj instanceof Referenceable )
+        {
+            throw new NamingException( "Do not know how to store Referenceables yet!" );
+        }
+
+        // Store different formats
+        if ( obj instanceof Reference )
+        {
+            // Store as ref and add outAttrs
+            throw new NamingException( "Do not know how to store References yet!" );
+        }
+        else if ( obj instanceof Serializable )
+        {
+            // Serialize and add outAttrs
+            ServerEntry clone = ( ServerEntry ) serverEntry.clone();
+
+            if ( outServerEntry != null && outServerEntry.size() > 0 )
+            {
+                for ( EntryAttribute attribute : outServerEntry )
+                {
+                    clone.put( attribute );
+                }
+            }
+
+            // Serialize object into entry attributes and add it.
+            JavaLdapSupport.serialize( serverEntry, obj, registries );
+            doAddOperation( target, clone );
+        }
+        else if ( obj instanceof DirContext )
+        {
+            // Grab attributes and merge with outAttrs
+            ServerEntry entry = ServerEntryUtils.toServerEntry( ( ( DirContext ) obj ).getAttributes( "" ), target,
+                registries );
+
+            if ( ( outServerEntry != null ) && ( outServerEntry.size() > 0 ) )
+            {
+                for ( EntryAttribute attribute : outServerEntry )
+                {
+                    entry.put( attribute );
+                }
+            }
+
+            doAddOperation( target, entry );
+        }
+        else
+        {
+            throw new NamingException( "Can't find a way to bind: " + obj );
+        }
+    }
+
+
+    /**
+     * @see javax.naming.directory.DirContext#rebind(java.lang.String,
+     *      java.lang.Object, javax.naming.directory.Attributes)
+     */
+    public void rebind( String name, Object obj, Attributes attrs ) throws NamingException
+    {
+        rebind( new LdapDN( name ), obj, AttributeUtils.toCaseInsensitive( attrs ) );
+    }
+
+
+    /**
+     * @see javax.naming.directory.DirContext#rebind(javax.naming.Name,
+     *      java.lang.Object, javax.naming.directory.Attributes)
+     */
+    public void rebind( Name name, Object obj, Attributes attrs ) throws NamingException
+    {
+        LdapDN target = buildTarget( name );
+
+        if ( getNexusProxy().hasEntry( new EntryOperationContext( registries, target ) ) )
+        {
+            doDeleteOperation( target );
+        }
+
+        bind( name, obj, AttributeUtils.toCaseInsensitive( attrs ) );
+    }
+
+
+    /**
+     * @see javax.naming.directory.DirContext#createSubcontext(java.lang.String,
+     *      javax.naming.directory.Attributes)
+     */
+    public DirContext createSubcontext( String name, Attributes attrs ) throws NamingException
+    {
+        return createSubcontext( new LdapDN( name ), AttributeUtils.toCaseInsensitive( attrs ) );
+    }
+
+
+    /**
+     * @see javax.naming.directory.DirContext#createSubcontext(
+     * javax.naming.Name, javax.naming.directory.Attributes)
+     */
+    public DirContext createSubcontext( Name name, Attributes attrs ) throws NamingException
+    {
+        if ( null == attrs )
+        {
+            return ( DirContext ) super.createSubcontext( name );
+        }
+
+        LdapDN target = buildTarget( name );
+        Rdn rdn = target.getRdn( target.size() - 1 );
+
+        attrs = AttributeUtils.toCaseInsensitive( attrs );
+        Attributes attributes = ( Attributes ) attrs.clone();
+
+        if ( rdn.size() == 1 )
+        {
+            String rdnAttribute = rdn.getUpType();
+            String rdnValue = ( String ) rdn.getValue();
+
+            // Add the Rdn attribute
+            boolean doRdnPut = attributes.get( rdnAttribute ) == null;
+            doRdnPut = doRdnPut || attributes.get( rdnAttribute ).size() == 0;
+
+            // TODO Fix DIRSERVER-832
+            doRdnPut = doRdnPut || !attributes.get( rdnAttribute ).contains( rdnValue );
+
+            if ( doRdnPut )
+            {
+                attributes.put( rdnAttribute, rdnValue );
+            }
+        }
+        else
+        {
+            for ( Iterator<AttributeTypeAndValue> ii = rdn.iterator(); ii.hasNext(); /**/)
+            {
+                AttributeTypeAndValue atav = ii.next();
+
+                // Add the Rdn attribute
+                boolean doRdnPut = attributes.get( atav.getNormType() ) == null;
+                doRdnPut = doRdnPut || attributes.get( atav.getNormType() ).size() == 0;
+
+                // TODO Fix DIRSERVER-832
+                doRdnPut = doRdnPut || !attributes.get( atav.getNormType() ).contains( atav.getNormValue() );
+
+                if ( doRdnPut )
+                {
+                    attributes.put( atav.getNormType(), atav.getNormValue() );
+                }
+            }
+        }
+
+        // Add the new context to the server which as a side effect adds
+        doAddOperation( target, ServerEntryUtils.toServerEntry( attributes, target, registries ) );
+
+        // Initialize the new context
+        return new ServerLdapContext( getService(), getPrincipal(), target );
+    }
+
+
+    /**
+     * Presently unsupported operation!
+     */
+    public DirContext getSchema( Name name ) throws NamingException
+    {
+        throw new UnsupportedOperationException();
+    }
+
+
+    /**
+     * Presently unsupported operation!
+     */
+    public DirContext getSchema( String name ) throws NamingException
+    {
+        throw new UnsupportedOperationException();
+    }
+
+
+    /**
+     * Presently unsupported operation!
+     */
+    public DirContext getSchemaClassDefinition( Name name ) throws NamingException
+    {
+        throw new UnsupportedOperationException();
+    }
+
+
+    /**
+     * Presently unsupported operation!
+     */
+    public DirContext getSchemaClassDefinition( String name ) throws NamingException
+    {
+        throw new UnsupportedOperationException();
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Search Operation Implementations
+    // ------------------------------------------------------------------------
+
+    /**
+     * @see javax.naming.directory.DirContext#search(java.lang.String,
+     *      javax.naming.directory.Attributes)
+     */
+    public NamingEnumeration<SearchResult> search( String name, Attributes matchingAttributes ) throws NamingException
+    {
+        return search( new LdapDN( name ), matchingAttributes, null );
+    }
+
+
+    /**
+     * @see javax.naming.directory.DirContext#search(javax.naming.Name,
+     *      javax.naming.directory.Attributes)
+     */
+    public NamingEnumeration<SearchResult> search( Name name, Attributes matchingAttributes ) throws NamingException
+    {
+        return search( name, AttributeUtils.toCaseInsensitive( matchingAttributes ), null );
+    }
+
+
+    /**
+     * @see javax.naming.directory.DirContext#search(java.lang.String,
+     *      javax.naming.directory.Attributes, java.lang.String[])
+     */
+    public NamingEnumeration<SearchResult> search( String name, Attributes matchingAttributes,
+        String[] attributesToReturn ) throws NamingException
+    {
+        return search( new LdapDN( name ), AttributeUtils.toCaseInsensitive( matchingAttributes ), attributesToReturn );
+    }
+
+
+    /**
+     * @see javax.naming.directory.DirContext#search(javax.naming.Name,
+     *      javax.naming.directory.Attributes, java.lang.String[])
+     */
+    public NamingEnumeration<SearchResult> search( Name name, Attributes matchingAttributes, String[] attributesToReturn )
+        throws NamingException
+    {
+        SearchControls ctls = new SearchControls();
+        LdapDN target = buildTarget( name );
+
+        // If we need to return specific attributes add em to the SearchControls
+        if ( null != attributesToReturn )
+        {
+            ctls.setReturningAttributes( attributesToReturn );
+        }
+
+        // If matchingAttributes is null/empty use a match for everything filter
+        matchingAttributes = AttributeUtils.toCaseInsensitive( matchingAttributes );
+
+        if ( ( null == matchingAttributes ) || ( matchingAttributes.size() <= 0 ) )
+        {
+            PresenceNode filter = new PresenceNode( SchemaConstants.OBJECT_CLASS_AT );
+            AliasDerefMode aliasDerefMode = AliasDerefMode.getEnum( getEnvironment() );
+            return ServerEntryUtils.toSearchResultEnum( doSearchOperation( target, aliasDerefMode, filter, ctls ) );
+        }
+
+        // Handle simple filter expressions without multiple terms
+        if ( matchingAttributes.size() == 1 )
+        {
+            NamingEnumeration<? extends Attribute> list = matchingAttributes.getAll();
+            Attribute attr = list.next();
+            list.close();
+
+            if ( attr.size() == 1 )
+            {
+                Object value = attr.get();
+                SimpleNode node;
+
+                if ( value instanceof byte[] )
+                {
+                    node = new EqualityNode( attr.getID(), new ClientBinaryValue( ( byte[] ) value ) );
+                }
+                else
+                {
+                    node = new EqualityNode( attr.getID(), new ClientStringValue( ( String ) value ) );
+                }
+
+                AliasDerefMode aliasDerefMode = AliasDerefMode.getEnum( getEnvironment() );
+                return ServerEntryUtils.toSearchResultEnum( doSearchOperation( target, aliasDerefMode, node, ctls ) );
+            }
+        }
+
+        /*
+         * Go through the set of attributes using each attribute value pair as 
+         * an attribute value assertion within one big AND filter expression.
+         */
+        Attribute attr;
+        SimpleNode node;
+        BranchNode filter = new AndNode();
+        NamingEnumeration<? extends Attribute> list = matchingAttributes.getAll();
+
+        // Loop through each attribute value pair
+        while ( list.hasMore() )
+        {
+            attr = list.next();
+
+            /*
+             * According to JNDI if an attribute in the matchingAttributes
+             * list does not have any values then we match for just the presence
+             * of the attribute in the entry
+             */
+            if ( attr.size() == 0 )
+            {
+                filter.addNode( new PresenceNode( attr.getID() ) );
+                continue;
+            }
+
+            /*
+             * With 1 or more value we build a set of simple nodes and add them
+             * to the AND node - each attribute value pair is a simple AVA node.
+             */
+            for ( int ii = 0; ii < attr.size(); ii++ )
+            {
+                Object val = attr.get( ii );
+
+                // Add simpel AVA node if its value is a String 
+                if ( val instanceof String )
+                {
+                    node = new EqualityNode( attr.getID(), new ClientStringValue( ( String ) val ) );
+                    filter.addNode( node );
+                }
+            }
+        }
+
+        AliasDerefMode aliasDerefMode = AliasDerefMode.getEnum( getEnvironment() );
+        return ServerEntryUtils.toSearchResultEnum( doSearchOperation( target, aliasDerefMode, filter, ctls ) );
+    }
+
+
+    /**
+     * @see javax.naming.directory.DirContext#search(java.lang.String,
+     *      java.lang.String, javax.naming.directory.SearchControls)
+     */
+    public NamingEnumeration<SearchResult> search( String name, String filter, SearchControls cons )
+        throws NamingException
+    {
+        return search( new LdapDN( name ), filter, cons );
+    }
+
+
+    /**
+     * A search overload that is used for optimizing search handling in the
+     * LDAP protocol provider which deals with an ExprNode instance rather than
+     * a String for the filter.
+     *
+     * @param name the relative name of the object serving as the search base
+     * @param filter the search filter as an expression tree
+     * @param cons the search controls to use
+     * @return an enumeration over the SearchResults
+     * @throws NamingException if there are problems performing the search
+     */
+    public NamingEnumeration<SearchResult> search( Name name, ExprNode filter, SearchControls cons )
+        throws NamingException
+    {
+        LdapDN target = buildTarget( name );
+        AliasDerefMode aliasDerefMode = AliasDerefMode.getEnum( getEnvironment() );
+        return ServerEntryUtils.toSearchResultEnum( doSearchOperation( target, aliasDerefMode, filter, cons ) );
+    }
+
+
+    /**
+     * A search overload that is used for optimizing search handling in the
+     * LDAP protocol provider which deals with an ExprNode instance rather than
+     * a String for the filter.
+     *
+     * @param name the relative name of the object serving as the search base
+     * @param filter the search filter as an expression tree
+     * @param cons the search controls to use
+     * @return an enumeration over the SearchResults
+     * @throws NamingException if there are problems performing the search
+     */
+    public NamingEnumeration<SearchResult> search( Name name, ExprNode filter, SearchControls cons, InetSocketAddress clientAddress )
+        throws NamingException
+    {
+        LdapDN target = buildTarget( name );
+        AliasDerefMode aliasDerefMode = AliasDerefMode.getEnum( getEnvironment() );
+        return ServerEntryUtils.toSearchResultEnum( doSearchOperation( target, aliasDerefMode, filter, cons, clientAddress ) );
+    }
+
+
+    /**
+     * @see javax.naming.directory.DirContext#search(javax.naming.Name,
+     *      java.lang.String, javax.naming.directory.SearchControls)
+     */
+    public NamingEnumeration<SearchResult> search( Name name, String filter, SearchControls cons )
+        throws NamingException
+    {
+        ExprNode filterNode;
+        LdapDN target = buildTarget( name );
+
+        try
+        {
+            filterNode = FilterParser.parse( filter );
+        }
+        catch ( ParseException pe )
+        {
+            InvalidSearchFilterException isfe = new InvalidSearchFilterException(
+                "Encountered parse exception while parsing the filter: '" + filter + "'" );
+            isfe.setRootCause( pe );
+            throw isfe;
+        }
+
+        AliasDerefMode aliasDerefMode = AliasDerefMode.getEnum( getEnvironment() );
+        return ServerEntryUtils.toSearchResultEnum( doSearchOperation( target, aliasDerefMode, filterNode, cons ) );
+    }
+
+
+    /**
+     * @see javax.naming.directory.DirContext#search(java.lang.String,
+     *      java.lang.String, java.lang.Object[],
+     *      javax.naming.directory.SearchControls)
+     */
+    public NamingEnumeration<SearchResult> search( String name, String filterExpr, Object[] filterArgs,
+        SearchControls cons ) throws NamingException
+    {
+        return search( new LdapDN( name ), filterExpr, filterArgs, cons );
+    }
+
+
+    /**
+     * @see javax.naming.directory.DirContext#search(javax.naming.Name,
+     *      java.lang.String, java.lang.Object[],
+     *      javax.naming.directory.SearchControls)
+     */
+    public NamingEnumeration<SearchResult> search( Name name, String filterExpr, Object[] filterArgs,
+        SearchControls cons ) throws NamingException
+    {
+        int start;
+        int index;
+
+        StringBuffer buf = new StringBuffer( filterExpr );
+
+        // Scan until we hit the end of the string buffer 
+        for ( int ii = 0; ii < buf.length(); ii++ )
+        {
+            try
+            {
+                // Advance until we hit the start of a variable
+                while ( ii < buf.length() && '{' != buf.charAt( ii ) )
+                {
+                    ii++;
+                }
+
+                // Record start of variable at '{'
+                start = ii;
+
+                // Advance to the end of a variable at '}'
+                while ( '}' != buf.charAt( ii ) )
+                {
+                    ii++;
+                }
+            }
+            catch ( IndexOutOfBoundsException e )
+            {
+                // End of filter so done.
+                break;
+            }
+
+            // Parse index
+            index = Integer.parseInt( buf.substring( start + 1, ii ) );
+
+            if ( filterArgs[index] instanceof String )
+            {
+                /*
+                 * Replace the '{ i }' with the string representation of the value
+                 * held in the filterArgs array at index index.
+                 */
+                buf.replace( start, ii + 1, ( String ) filterArgs[index] );
+            }
+            else if ( filterArgs[index] instanceof byte[] )
+            {
+                String hexstr = "#" + StringTools.toHexString( ( byte[] ) filterArgs[index] );
+                buf.replace( start, ii + 1, hexstr );
+            }
+            else
+            {
+                /*
+                 * Replace the '{ i }' with the string representation of the value
+                 * held in the filterArgs array at index index.
+                 */
+                buf.replace( start, ii + 1, filterArgs[index].toString() );
+            }
+        }
+
+        return search( name, buf.toString(), cons );
+    }
+
+
+    // ------------------------------------------------------------------------
+    // EventDirContext implementations
+    // ------------------------------------------------------------------------
+
+    public void addNamingListener( Name name, String filterStr, SearchControls searchControls,
+        NamingListener namingListener ) throws NamingException
+    {
+        ExprNode filter;
+
+        try
+        {
+            filter = FilterParser.parse( filterStr );
+        }
+        catch ( Exception e )
+        {
+            NamingException e2 = new NamingException( "could not parse filter: " + filterStr );
+            e2.setRootCause( e );
+            throw e2;
+        }
+
+        ( ( PartitionNexusProxy ) getNexusProxy() ).addNamingListener( this, buildTarget( name ), filter,
+            searchControls, namingListener );
+        getListeners().add( namingListener );
+    }
+
+
+    public void addNamingListener( String name, String filter, SearchControls searchControls,
+        NamingListener namingListener ) throws NamingException
+    {
+        addNamingListener( new LdapDN( name ), filter, searchControls, namingListener );
+    }
+
+
+    public void addNamingListener( Name name, String filterExpr, Object[] filterArgs, SearchControls searchControls,
+        NamingListener namingListener ) throws NamingException
+    {
+        int start;
+        StringBuffer buf = new StringBuffer( filterExpr );
+
+        // Scan until we hit the end of the string buffer
+        for ( int ii = 0; ii < buf.length(); ii++ )
+        {
+            // Advance until we hit the start of a variable
+            while ( '{' != buf.charAt( ii ) )
+            {
+                ii++;
+            }
+
+            // Record start of variable at '{'
+            start = ii;
+
+            // Advance to the end of a variable at '}'
+            while ( '}' != buf.charAt( ii ) )
+            {
+                ii++;
+            }
+
+            /*
+             * Replace the '{ i }' with the string representation of the value
+             * held in the filterArgs array at index index.
+             */
+            buf.replace( start, ii + 1, filterArgs[ii].toString() );
+        }
+
+        addNamingListener( name, buf.toString(), searchControls, namingListener );
+    }
+
+
+    public void addNamingListener( String name, String filter, Object[] objects, SearchControls searchControls,
+        NamingListener namingListener ) throws NamingException
+    {
+        addNamingListener( new LdapDN( name ), filter, objects, searchControls, namingListener );
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/jndi/ServerDirObjectFactory.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/jndi/ServerDirObjectFactory.java
new file mode 100644
index 0000000..91653b2
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/jndi/ServerDirObjectFactory.java
@@ -0,0 +1,73 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.jndi;
+
+
+import javax.naming.spi.DirObjectFactory;
+
+
+/**
+ * A specialized ObjectFactory that is optimized for our server-side JNDI
+ * provider.  This factory reports the Class of objects that it is creates as
+ * well as the objectClass corresponding to that Class.  This makes it easier
+ * for the server side provider to lookup the respective factory rather than
+ * attempt several others within the list of object factories in the order of
+ * greatest specificity.  JNDI SPI methods are inefficient since they are
+ * designed to try all object factories to produce the object.  Our provider
+ * looks up the most specific object factory based on this additional
+ * information.  This makes a huge difference when the number of ObjectFactory
+ * instances is large.
+ * <p/>
+ * Eventually, it is highly feasible for generated schemas, to also include
+ * state and object factories for various objectClasses, or domain objects.
+ * This means the number of factories will increase.  By associating object and
+ * state factories with their respective objectClasses and Classes we can
+ * integrate these DAOs into the schema subsystem making factory lookups
+ * extremely fast and efficient without costing the user too much to create and
+ * store objects within the directory.  At the end of the day the directory
+ * becomes a hierarchical object store where lookup, bind and rebind are the
+ * only operations besides search to access and store objects.  That's pretty
+ * PHAT!
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public interface ServerDirObjectFactory extends DirObjectFactory
+{
+    /**
+     * Gets either the OID for the objectClass or the human readable name for
+     * the objectClass this DirStateFactory is associated with.  Note
+     * that associating this factory with an objectClass automatically
+     * associates this DirObjectFactory with all descendents of the objectClass.
+     *
+     * @return the OID or human readable name of the objectClass associated with this ObjectFactory
+     */
+    String getObjectClassId();
+
+
+    /**
+     * Gets the Class instance associated with this ObjectFactory.  Objects to
+     * be created by this ObjectFactory will be of this type, a subclass of
+     * this type, or implement this type if it is an interface.
+     *
+     * @return the Class associated with this factory.
+     */
+    Class<?> getAssociatedClass();
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/jndi/ServerDirStateFactory.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/jndi/ServerDirStateFactory.java
new file mode 100644
index 0000000..7a9ae24
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/jndi/ServerDirStateFactory.java
@@ -0,0 +1,67 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.jndi;
+
+
+import javax.naming.spi.DirStateFactory;
+
+
+/**
+ * A specialized StateFactory that is optimized for our server-side JNDI
+ * provider.  This factory reports the id of the objectClass that it
+ * is associated with.  This makes it easier for the server side provider to
+ * find the required factory rather than attempt several others within the list
+ * of state factories.  JNDI SPI methods are inefficient since they are designed
+ * to try all state factories to produce an object.  Our provider looks up
+ * the most specific state factories based on additional information.  This
+ * makes a huge difference when the number of StateFactories becomes large.
+ * <br/>
+ * Eventually, it is highly feasible for generated schemas, to also include
+ * state and object factories for various objectClasses.  This means the number
+ * of factories will increase.  By associating object and state factories with
+ * their respective objectClasses we can integrate this into the schema
+ * subsystem making factory lookups extremely fast and efficient without costing
+ * the user too much to create and store objects within the directory.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public interface ServerDirStateFactory extends DirStateFactory
+{
+    /**
+     * Gets either the OID for the objectClass or the human readable name for
+     * the objectClass this DirStateFactory is associated with.  Note
+     * that associating this factory with an objectClass automatically
+     * associates this DirStateFactory with all descendents of the objectClass.
+     *
+     * @return the OID or human readable name of the objectClass associated with this StateFactory
+     */
+    String getObjectClassId();
+
+
+    /**
+     * Gets the Class instance associated with this StateFactory.  Objects to
+     * be persisted by this StateFactory must be of this type, a subclass of
+     * this type, or implement this type if it is an interface.
+     *
+     * @return the class associated with this factory.
+     */
+    Class<?> getAssociatedClass();
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/jndi/ServerLdapContext.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/jndi/ServerLdapContext.java
new file mode 100644
index 0000000..6510b81
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/jndi/ServerLdapContext.java
@@ -0,0 +1,269 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.jndi;
+
+
+import java.util.Hashtable;
+
+import javax.naming.NamingException;
+import javax.naming.ldap.Control;
+import javax.naming.ldap.ExtendedRequest;
+import javax.naming.ldap.ExtendedResponse;
+import javax.naming.ldap.LdapContext;
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.authn.LdapPrincipal;
+import org.apache.directory.server.core.entry.ServerBinaryValue;
+import org.apache.directory.server.core.entry.ServerStringValue;
+import org.apache.directory.server.core.interceptor.context.CompareOperationContext;
+import org.apache.directory.server.core.interceptor.context.UnbindOperationContext;
+import org.apache.directory.server.core.referral.ReferralInterceptor;
+import org.apache.directory.shared.ldap.NotImplementedException;
+import org.apache.directory.shared.ldap.entry.Value;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.util.StringTools;
+
+
+/**
+ * An implementation of a JNDI LdapContext.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class ServerLdapContext extends ServerDirContext implements LdapContext
+{
+    /** A reference to the RTeferralService interceptor */
+    private transient ReferralInterceptor refService;
+    
+
+    /**
+     * Creates an instance of an ServerLdapContext.
+     *
+     * @param service the parent service that manages this context
+     * @param env the JNDI environment parameters
+     * @throws NamingException the context cannot be created
+     */
+    public ServerLdapContext( DirectoryService service, Hashtable<String, Object> env ) throws NamingException
+    {
+        super( service, env );
+        refService = ( ( ReferralInterceptor ) service.getInterceptorChain().get( ReferralInterceptor.class.getName() ) );
+    }
+
+
+    /**
+     * Creates a new ServerDirContext with a distinguished name which is used to
+     * set the PROVIDER_URL to the distinguished name for this context.
+     *
+     * @param principal the directory user principal that is propagated
+     * @param dn the distinguished name of this context
+     * @param service the directory service core
+     * @throws NamingException if there are problems instantiating 
+     */
+    public ServerLdapContext( DirectoryService service, LdapPrincipal principal, LdapDN dn ) throws NamingException
+    {
+        super( service, principal, dn );
+        refService = ( ( ReferralInterceptor ) service.getInterceptorChain().get( ReferralInterceptor.class.getName() ) );
+    }
+
+
+    /**
+     * @see javax.naming.ldap.LdapContext#extendedOperation(
+     * javax.naming.ldap.ExtendedRequest)
+     */
+    public ExtendedResponse extendedOperation( ExtendedRequest request )
+    {
+        throw new NotImplementedException();
+    }
+
+
+    /**
+     * @see javax.naming.ldap.LdapContext#newInstance(
+     * javax.naming.ldap.Control[])
+     */
+    public LdapContext newInstance( Control[] requestControls ) throws NamingException
+    {
+        ServerLdapContext ctx = new ServerLdapContext( getService(), getPrincipal(), ( LdapDN ) getDn() );
+        ctx.setRequestControls( requestControls );
+        return ctx;
+    }
+
+
+    /**
+     * @see javax.naming.ldap.LdapContext#reconnect(javax.naming.ldap.Control[])
+     */
+    public void reconnect( Control[] connCtls ) throws NamingException
+    {
+        this.connectControls = connCtls;
+    }
+
+
+    /**
+     * @see javax.naming.ldap.LdapContext#getConnectControls()
+     */
+    public Control[] getConnectControls() throws NamingException
+    {
+        return this.connectControls;
+    }
+
+
+    /**
+     * @see javax.naming.ldap.LdapContext#setRequestControls(
+     * javax.naming.ldap.Control[])
+     */
+    public void setRequestControls( Control[] requestControls ) throws NamingException
+    {
+        this.requestControls = requestControls;
+    }
+
+
+    /**
+     * @see javax.naming.ldap.LdapContext#getRequestControls()
+     */
+    public Control[] getRequestControls() throws NamingException
+    {
+        return requestControls;
+    }
+
+
+    /**
+     * @see javax.naming.ldap.LdapContext#getResponseControls()
+     */
+    public Control[] getResponseControls() throws NamingException
+    {
+        return responseControls;
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Additional ApacheDS Specific JNDI Functionality
+    // ------------------------------------------------------------------------
+
+    /**
+     * Explicitly exposes an LDAP compare operation which JNDI does not
+     * directly provide.  All normalization and schema checking etcetera
+     * is handled by this call.
+     *
+     * @param name the name of the entri
+     * @param oid the name or object identifier for the attribute to compare
+     * @param value the value to compare the attribute to
+     * @return true if the entry has the value for the attribute, false otherwise
+     * @throws NamingException if the backing store cannot be accessed, or
+     * permission is not allowed for this operation or the oid is not recognized,
+     * or the attribute is not present in the entry ... you get the picture.
+     */
+    public boolean compare( LdapDN name, String oid, Object value ) throws NamingException
+    {
+        Value<?> val = null;
+        
+        AttributeType attributeType = getService().getRegistries().getAttributeTypeRegistry().lookup( oid );
+        
+        // make sure we add the request controls to operation
+        if ( attributeType.getSyntax().isHumanReadable() )
+        {
+            if ( value instanceof String )
+            {
+                val = new ServerStringValue( attributeType, (String)value );
+            }
+            else if ( value instanceof byte[] )
+            {
+                val = new ServerStringValue( attributeType, StringTools.utf8ToString( (byte[])value ) );
+            }
+            else
+            {
+                throw new NamingException( "Bad value for the OID " + oid );
+            }
+        }
+        else
+        {
+            if ( value instanceof String )
+            {
+                val = new ServerBinaryValue( attributeType, StringTools.getBytesUtf8( (String)value ) );
+            }
+            else if ( value instanceof byte[] )
+            {
+                val = new ServerBinaryValue( attributeType, (byte[])value );
+            }
+            else
+            {
+                throw new NamingException( "Bad value for the OID " + oid );
+            }
+        }
+        
+        
+        CompareOperationContext opCtx = new CompareOperationContext( registries, name, oid, val );
+        opCtx.addRequestControls( requestControls );
+
+        // execute operation
+        boolean result = super.getNexusProxy().compare( opCtx );
+        
+        // extract the response controls from the operation and return
+        responseControls = getResponseControls();
+        requestControls = EMPTY_CONTROLS;
+        return result;
+    }
+
+
+    /**
+     * Calling this method tunnels an unbind call down into the partition holding 
+     * the bindDn.  The bind() counter part is not exposed because it is automatically
+     * called when you create a new initial context for a new connection (on wire) or 
+     * (programatic) caller.
+     * 
+     * @throws NamingException if there are failures encountered while unbinding
+     */
+    public void ldapUnbind() throws NamingException
+    {
+        LdapDN principalDn = super.getPrincipal().getJndiName();
+        UnbindOperationContext opCtx = new UnbindOperationContext( registries, principalDn );
+        opCtx.addRequestControls( requestControls );
+        super.getNexusProxy().unbind( opCtx );
+        responseControls = opCtx.getResponseControls();
+        requestControls = EMPTY_CONTROLS;
+    }
+
+
+    /**
+     * Check if a Name is a referral
+     * @param name The Name to check
+     * @return <code>true</code> if the Name is a referral.
+     * @throws NamingException If the Name is incorrect
+     */
+    public boolean isReferral( String name ) throws NamingException
+    {
+        return refService.isReferral( name );
+    }
+
+    /**
+     * Check if a Name is a referral
+     * @param name The Name to check
+     * @return <code>true</code> if the Name is a referral.
+     * @throws NamingException If the Name is incorrect
+     */
+    public boolean isReferral( LdapDN name ) throws NamingException
+    {
+        return refService.isReferral( name );
+    }
+
+    public ServerContext getRootContext() throws NamingException
+    {
+        return new ServerLdapContext( getService(), getPrincipal(), new LdapDN() );
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/normalization/ExpandingVisitor.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/normalization/ExpandingVisitor.java
new file mode 100644
index 0000000..eb563d3
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/normalization/ExpandingVisitor.java
@@ -0,0 +1,185 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.normalization;
+
+
+import java.util.Iterator;
+import java.util.List;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
+import org.apache.directory.shared.ldap.filter.ApproximateNode;
+import org.apache.directory.shared.ldap.filter.BranchNode;
+import org.apache.directory.shared.ldap.filter.EqualityNode;
+import org.apache.directory.shared.ldap.filter.ExprNode;
+import org.apache.directory.shared.ldap.filter.ExtensibleNode;
+import org.apache.directory.shared.ldap.filter.FilterVisitor;
+import org.apache.directory.shared.ldap.filter.GreaterEqNode;
+import org.apache.directory.shared.ldap.filter.LeafNode;
+import org.apache.directory.shared.ldap.filter.LessEqNode;
+import org.apache.directory.shared.ldap.filter.OrNode;
+import org.apache.directory.shared.ldap.filter.PresenceNode;
+import org.apache.directory.shared.ldap.filter.SubstringNode;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+
+
+/**
+ *
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 429176 $
+ */
+public class ExpandingVisitor implements FilterVisitor
+{
+    private final AttributeTypeRegistry attrRegistry;
+
+
+    /**
+     * 
+     * Creates a new instance of ExpandingVisitor.
+     *
+     * @param attrRegistry The AttributeType registry
+     */
+    public ExpandingVisitor( AttributeTypeRegistry attrRegistry )
+    {
+        this.attrRegistry = attrRegistry;
+    }
+
+
+    public boolean canVisit( ExprNode node )
+    {
+        return node instanceof BranchNode;
+    }
+
+
+    public List<ExprNode> getOrder( BranchNode node, List<ExprNode> children )
+    {
+        return children;
+    }
+
+
+    public boolean isPrefix()
+    {
+        return false;
+    }
+
+
+    public Object visit( ExprNode node )
+    {
+        BranchNode bnode = ( BranchNode ) node;
+
+        // --------------------------------------------------------------------
+        // we want to check each child leaf node to see if it must be expanded
+        // children that are branch nodes are recursively visited
+        // --------------------------------------------------------------------
+
+        final List<ExprNode> children = bnode.getChildren();
+        int childNumber = 0;
+
+        for ( ExprNode child : children )
+        {
+            if ( child instanceof LeafNode )
+            {
+                LeafNode leaf = ( LeafNode ) child;
+
+                try
+                {
+                    if ( attrRegistry.hasDescendants( leaf.getAttribute() ) )
+                    {
+                        // create a new OR node to hold all descendent forms
+                        // add to this node the generalized leaf node and 
+                        // replace the old leaf with the new OR branch node
+                        BranchNode orNode = new OrNode();
+                        orNode.getChildren().add( leaf );
+                        children.set( childNumber++, orNode );
+
+                        // iterate through descendants adding them to the orNode
+                        Iterator<AttributeType> descendants = attrRegistry.descendants( leaf.getAttribute() );
+
+                        while ( descendants.hasNext() )
+                        {
+                            LeafNode newLeaf = null;
+                            AttributeType descendant = descendants.next();
+
+                            if ( leaf instanceof PresenceNode )
+                            {
+                                newLeaf = new PresenceNode( descendant.getOid() );
+                            }
+                            else if ( leaf instanceof ApproximateNode )
+                            {
+                                ApproximateNode approximateNode = ( ApproximateNode ) leaf;
+
+                                newLeaf = new ApproximateNode( descendant.getOid(), approximateNode.getValue() );
+                            }
+                            else if ( leaf instanceof EqualityNode )
+                            {
+                                EqualityNode equalityNode = ( EqualityNode ) leaf;
+
+                                newLeaf = new EqualityNode( descendant.getOid(), equalityNode.getValue() );
+                            }
+                            else if ( leaf instanceof GreaterEqNode )
+                            {
+                                GreaterEqNode greaterEqNode = ( GreaterEqNode ) leaf;
+
+                                newLeaf = new GreaterEqNode( descendant.getOid(), greaterEqNode.getValue() );
+                            }
+                            else if ( leaf instanceof LessEqNode )
+                            {
+                                LessEqNode lessEqNode = ( LessEqNode ) leaf;
+
+                                newLeaf = new LessEqNode( descendant.getOid(), lessEqNode.getValue() );
+                            }
+                            else if ( leaf instanceof ExtensibleNode )
+                            {
+                                ExtensibleNode extensibleNode = ( ExtensibleNode ) leaf;
+                                newLeaf = new ExtensibleNode( descendant.getOid(), extensibleNode.getValue(),
+                                    extensibleNode.getMatchingRuleId(), extensibleNode.hasDnAttributes() );
+                            }
+                            else if ( leaf instanceof SubstringNode )
+                            {
+                                SubstringNode substringNode = ( SubstringNode ) leaf;
+                                newLeaf = new SubstringNode( descendant.getOid(), substringNode.getInitial(),
+                                    substringNode.getFinal() );
+                            }
+                            else
+                            {
+                                throw new IllegalStateException( "Unknown assertion type: " + leaf );
+                            }
+
+                            orNode.addNode( newLeaf );
+                        }
+                    }
+                }
+                catch ( NamingException e )
+                {
+                    // log something here and throw a runtime excpetion
+                    throw new RuntimeException( "Failed to expand node" );
+                }
+            }
+            else
+            {
+                visit( child );
+            }
+        } // end for loop
+
+        return null;
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/normalization/NormalizationInterceptor.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/normalization/NormalizationInterceptor.java
new file mode 100644
index 0000000..f8b2733
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/normalization/NormalizationInterceptor.java
@@ -0,0 +1,275 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.normalization;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerSearchResult;
+import org.apache.directory.server.core.interceptor.BaseInterceptor;
+import org.apache.directory.server.core.interceptor.NextInterceptor;
+import org.apache.directory.server.core.interceptor.context.AddContextPartitionOperationContext;
+import org.apache.directory.server.core.interceptor.context.AddOperationContext;
+import org.apache.directory.server.core.interceptor.context.BindOperationContext;
+import org.apache.directory.server.core.interceptor.context.CompareOperationContext;
+import org.apache.directory.server.core.interceptor.context.DeleteOperationContext;
+import org.apache.directory.server.core.interceptor.context.EntryOperationContext;
+import org.apache.directory.server.core.interceptor.context.GetMatchedNameOperationContext;
+import org.apache.directory.server.core.interceptor.context.GetSuffixOperationContext;
+import org.apache.directory.server.core.interceptor.context.ListOperationContext;
+import org.apache.directory.server.core.interceptor.context.LookupOperationContext;
+import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
+import org.apache.directory.server.core.interceptor.context.MoveAndRenameOperationContext;
+import org.apache.directory.server.core.interceptor.context.MoveOperationContext;
+import org.apache.directory.server.core.interceptor.context.RemoveContextPartitionOperationContext;
+import org.apache.directory.server.core.interceptor.context.RenameOperationContext;
+import org.apache.directory.server.core.interceptor.context.SearchOperationContext;
+import org.apache.directory.server.core.partition.PartitionNexus;
+import org.apache.directory.server.schema.ConcreteNameComponentNormalizer;
+import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
+import org.apache.directory.server.schema.registries.OidRegistry;
+import org.apache.directory.shared.ldap.filter.ExprNode;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.name.NameComponentNormalizer;
+import org.apache.directory.shared.ldap.schema.OidNormalizer;
+import org.apache.directory.shared.ldap.util.EmptyEnumeration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import java.util.Map;
+
+
+/**
+ * A name normalization service.  This service makes sure all relative and distinuished
+ * names are normalized before calls are made against the respective interface methods
+ * on {@link PartitionNexus}.
+ *
+ * @org.apache.xbean.XBean
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class NormalizationInterceptor extends BaseInterceptor
+{
+    /** logger used by this class */
+    private static final Logger LOG = LoggerFactory.getLogger( NormalizationInterceptor.class );
+
+    /** a filter node value normalizer and undefined node remover */
+    private NormalizingVisitor normVisitor;
+
+    /** The association between attributeTypes and their normalizers */
+    private Map<String, OidNormalizer> attrNormalizers; 
+    
+    /** The globa attributeType registry */
+    private AttributeTypeRegistry attributeRegistry;
+
+    /**
+     * Initialize the registries, normalizers. 
+     */
+    public void init( DirectoryService directoryService ) throws NamingException
+    {
+        OidRegistry oidRegistry = directoryService.getRegistries().getOidRegistry();
+        attributeRegistry = directoryService.getRegistries().getAttributeTypeRegistry();
+        NameComponentNormalizer ncn = new ConcreteNameComponentNormalizer( attributeRegistry, oidRegistry );
+        normVisitor = new NormalizingVisitor( ncn, directoryService.getRegistries() );
+        //expVisitor = new ExpandingVisitor( attributeRegistry );
+        attrNormalizers = attributeRegistry.getNormalizerMapping();
+    }
+
+    /**
+     * The destroy method does nothing
+     */
+    public void destroy()
+    {
+    }
+
+    // ------------------------------------------------------------------------
+    // Normalize all Name based arguments for ContextPartition interface operations
+    // ------------------------------------------------------------------------
+
+    public void add( NextInterceptor nextInterceptor, AddOperationContext opContext ) throws NamingException
+    {
+        opContext.getDn().normalize( attrNormalizers );
+        nextInterceptor.add( opContext );
+    }
+
+
+    public void delete( NextInterceptor nextInterceptor, DeleteOperationContext opContext ) throws NamingException
+    {
+        opContext.getDn().normalize( attrNormalizers );
+        nextInterceptor.delete( opContext );
+    }
+
+
+    public void modify( NextInterceptor nextInterceptor, ModifyOperationContext opContext ) throws NamingException
+    {
+        opContext.getDn().normalize( attrNormalizers );
+        nextInterceptor.modify( opContext );
+    }
+
+
+    public void rename( NextInterceptor nextInterceptor, RenameOperationContext opContext ) throws NamingException
+    {
+        LdapDN rdn = new LdapDN();
+        rdn.add( opContext.getNewRdn() );
+        rdn.normalize( attrNormalizers );
+        opContext.setNewRdn( rdn.getRdn() );
+
+        opContext.getDn().normalize( attrNormalizers );
+        nextInterceptor.rename( opContext );
+    }
+
+
+    public void move( NextInterceptor nextInterceptor, MoveOperationContext opContext ) throws NamingException
+    {
+        opContext.getDn().normalize( attrNormalizers );
+        opContext.getParent().normalize( attrNormalizers);
+        nextInterceptor.move( opContext );
+    }
+
+
+    public void moveAndRename( NextInterceptor nextInterceptor, MoveAndRenameOperationContext opContext )
+        throws NamingException
+    {
+        LdapDN rdn = new LdapDN();
+        rdn.add( opContext.getNewRdn() );
+        rdn.normalize( attrNormalizers );
+        opContext.setNewRdn( rdn.getRdn() );
+
+        opContext.getDn().normalize( attrNormalizers );
+        opContext.getParent().normalize( attrNormalizers );
+        nextInterceptor.moveAndRename( opContext );
+    }
+
+
+    public NamingEnumeration<ServerSearchResult> search( NextInterceptor nextInterceptor, SearchOperationContext opContext ) throws NamingException
+    {
+        ExprNode filter = opContext.getFilter();
+        opContext.getDn().normalize( attrNormalizers );
+        ExprNode result = ( ExprNode ) filter.accept( normVisitor );
+
+        if ( result == null )
+        {
+            LOG.warn( "undefined filter based on undefined attributeType not evaluted at all.  Returning empty enumeration." );
+            return new EmptyEnumeration<ServerSearchResult>();
+        }
+        else
+        {
+            opContext.setFilter( result );
+            
+            // TODO Normalize the returned Attributes, storing the UP attributes to format the returned values.
+            return nextInterceptor.search( opContext );
+        }
+    }
+
+
+    public boolean hasEntry( NextInterceptor nextInterceptor, EntryOperationContext opContext ) throws NamingException
+    {
+        opContext.getDn().normalize( attrNormalizers );
+        return nextInterceptor.hasEntry( opContext );
+    }
+
+
+    public NamingEnumeration<ServerSearchResult> list( NextInterceptor nextInterceptor, ListOperationContext opContext ) throws NamingException
+    {
+        opContext.getDn().normalize( attrNormalizers );
+        return nextInterceptor.list( opContext );
+    }
+
+    
+    private String[] normalizeAttrsId( String[] attrIds ) throws NamingException
+    {
+        if ( attrIds == null )
+        {
+            return attrIds;
+        }
+        
+        String[] normalizedAttrIds = new String[attrIds.length];
+        int pos = 0;
+        
+        for ( String id:attrIds )
+        {
+            String oid = attributeRegistry.lookup( id ).getOid();
+            normalizedAttrIds[pos++] = oid;
+        }
+        
+        return normalizedAttrIds;
+    }
+
+    public ServerEntry lookup( NextInterceptor nextInterceptor, LookupOperationContext opContext ) throws NamingException
+    {
+        opContext.getDn().normalize( attrNormalizers );
+        
+        if ( opContext.getAttrsId() != null )
+        {
+            // We have to normalize the requested IDs
+            opContext.setAttrsId( normalizeAttrsId( opContext.getAttrsIdArray() ) );
+        }
+        
+        return nextInterceptor.lookup( opContext );
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Normalize all Name based arguments for other interface operations
+    // ------------------------------------------------------------------------
+
+    public LdapDN getMatchedName ( NextInterceptor nextInterceptor, GetMatchedNameOperationContext opContext ) throws NamingException
+    {
+        opContext.getDn().normalize( attrNormalizers );
+        return nextInterceptor.getMatchedName( opContext );
+    }
+
+
+    public LdapDN getSuffix ( NextInterceptor nextInterceptor, GetSuffixOperationContext opContext ) throws NamingException
+    {
+        opContext.getDn().normalize( attrNormalizers );
+        return nextInterceptor.getSuffix( opContext );
+    }
+
+
+    public boolean compare( NextInterceptor next, CompareOperationContext opContext ) throws NamingException
+    {
+        opContext.getDn().normalize( attrNormalizers );
+        return next.compare( opContext );
+    }
+    
+    
+    public void bind( NextInterceptor next, BindOperationContext opContext )  throws NamingException
+    {
+        opContext.getDn().normalize( attrNormalizers );
+        next.bind( opContext );
+    }
+
+
+    public void addContextPartition( NextInterceptor next, AddContextPartitionOperationContext opContext ) throws NamingException
+    {
+        next.addContextPartition( opContext );
+    }
+
+
+    public void removeContextPartition( NextInterceptor next, RemoveContextPartitionOperationContext opContext ) throws NamingException
+    {
+        opContext.getDn().normalize( attrNormalizers );
+        next.removeContextPartition( opContext );
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/normalization/NormalizingVisitor.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/normalization/NormalizingVisitor.java
new file mode 100644
index 0000000..4b50db6
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/normalization/NormalizingVisitor.java
@@ -0,0 +1,643 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.normalization;
+
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.naming.InvalidNameException;
+import javax.naming.NamingException;
+
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.entry.Value;
+import org.apache.directory.shared.ldap.entry.client.ClientBinaryValue;
+import org.apache.directory.shared.ldap.entry.client.ClientStringValue;
+import org.apache.directory.shared.ldap.filter.AndNode;
+import org.apache.directory.shared.ldap.filter.BranchNode;
+import org.apache.directory.shared.ldap.filter.ExprNode;
+import org.apache.directory.shared.ldap.filter.ExtensibleNode;
+import org.apache.directory.shared.ldap.filter.FilterVisitor;
+import org.apache.directory.shared.ldap.filter.LeafNode;
+import org.apache.directory.shared.ldap.filter.NotNode;
+import org.apache.directory.shared.ldap.filter.PresenceNode;
+import org.apache.directory.shared.ldap.filter.SimpleNode;
+import org.apache.directory.shared.ldap.filter.SubstringNode;
+import org.apache.directory.shared.ldap.name.NameComponentNormalizer;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.util.ByteBuffer;
+import org.apache.directory.shared.ldap.util.StringTools;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * A filter visitor which normalizes leaf node values as it visits them.  It also removes
+ * leaf nodes from branches whose attributeType is undefined.  It obviously cannot remove
+ * a leaf node from a filter which is only a leaf node.  Checks to see if a filter is a
+ * leaf node with undefined attributeTypes should be done outside this visitor.
+ *
+ * Since this visitor may remove filter nodes it may produce negative results on filters,
+ * like NOT branch nodes without a child or AND and OR nodes with one or less children.  This
+ * might make some partition implementations choke.  To avoid this problem we clean up branch
+ * nodes that don't make sense.  For example all BranchNodes without children are just
+ * removed.  An AND and OR BranchNode with a single child is replaced with it's child for
+ * all but the topmost branchnode which we cannot replace.  So again the top most branch
+ * node must be inspected by code outside of this visitor.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class NormalizingVisitor implements FilterVisitor
+{
+    /** logger used by this class */
+    private static final Logger log = LoggerFactory.getLogger( NormalizingVisitor.class );
+
+    /** the name component normalizer used by this visitor */
+    private final NameComponentNormalizer ncn;
+
+    /** the global registries used to resolve OIDs for attributeType ids */
+    private final Registries registries;
+
+
+    /**
+     * Chars which need to be escaped in a filter
+     * '\0' | '(' | ')' | '*' | '\'
+     */
+    private static final boolean[] FILTER_CHAR =
+        { 
+            true,  false, false, false, false, false, false, false, // 00 -> 07 NULL
+            false, false, false, false, false, false, false, false, // 08 -> 0F
+            false, false, false, false, false, false, false, false, // 10 -> 17
+            false, false, false, false, false, false, false, false, // 18 -> 1F
+            false, false, false, false, false, false, false, false, // 20 -> 27
+            true,  true,  true,  false, false, false, false, false, // 28 -> 2F '(', ')', '*'
+            false, false, false, false, false, false, false, false, // 30 -> 37
+            false, false, false, false, false, false, false, false, // 38 -> 3F 
+            false, false, false, false, false, false, false, false, // 40 -> 47
+            false, false, false, false, false, false, false, false, // 48 -> 4F
+            false, false, false, false, false, false, false, false, // 50 -> 57
+            false, false, false, false, true,  false, false, false, // 58 -> 5F '\'
+            false, false, false, false, false, false, false, false, // 60 -> 67
+            false, false, false, false, false, false, false, false, // 68 -> 6F
+            false, false, false, false, false, false, false, false, // 70 -> 77
+            false, false, false, false, false, false, false, false  // 78 -> 7F
+        };
+
+    /**
+     * 
+     * Creates a new instance of NormalizingVisitor.
+     *
+     * @param ncn The name component normalizer to use
+     * @param registries The global registries
+     */
+    public NormalizingVisitor( NameComponentNormalizer ncn, Registries registries )
+    {
+        this.ncn = ncn;
+        this.registries = registries;
+    }
+
+
+    /**
+     * Check if the given char is a filter escaped char
+     * &lt;filterEscapedChars&gt; ::= '\0' | '(' | ')' | '*' | '\'
+     *
+     * @param c the char we want to test
+     * @return true if the char is a pair char only
+     */
+    public static boolean isFilterChar( char c )
+    {
+        return ( ( ( c | 0x7F ) == 0x7F ) && FILTER_CHAR[c & 0x7f] );
+    }
+
+    /**
+     * Decodes sequences of escaped hex within an attribute's value into 
+     * a UTF-8 String.  The hex is decoded inline and the complete decoded
+     * String is returned.
+     * 
+     * @param str the string containing hex escapes
+     * @return the decoded string
+     */
+    private static final String decodeEscapedHex( String str ) throws InvalidNameException
+    {
+        // create buffer and add everything before start of scan
+        StringBuffer buf = new StringBuffer();
+        ByteBuffer bb = new ByteBuffer();
+        boolean escaped = false;
+        
+        // start scanning until we find an escaped series of bytes
+        for ( int ii = 0; ii < str.length(); ii++ )
+        {
+            char c = str.charAt( ii );
+            
+            if ( c == '\\' )
+            {
+                // we have the start of a hex escape sequence
+                if ( StringTools.isHex( str, ii+1 ) && StringTools.isHex ( str, ii+2 ) )
+                {
+                    bb.clear();
+                    int advancedBy = StringTools.collectEscapedHexBytes( bb, str, ii );
+                    ii+=advancedBy-1;
+                    buf.append( StringTools.utf8ToString( bb.buffer(), bb.position() ) );
+                    escaped = false;
+                    continue;
+                }
+                else if ( !escaped )
+                {
+                    // It may be an escaped char ( '\0', '(', ')', '*', '\' )
+                    escaped = true;
+                    continue;
+                }
+            }
+
+            
+            if ( escaped )
+            {
+                if ( isFilterChar( c ) )
+                {
+                    // It is an escaped char ( '\0', '(', ')', '*', '\' )
+                    // Stores it into the buffer without the '\'
+                    escaped = false;
+                    buf.append( c );
+                    continue;
+                }
+                else
+                {
+                    throw new InvalidNameException( "The value must contain valid escaped characters." );
+                }
+            }
+            else
+            {
+                buf.append( str.charAt( ii ) );
+            }
+        }
+
+        if ( escaped )
+        {
+            // We should not have a '\' at the end of the string
+            throw new InvalidNameException( "The value must not ends with a '\\'." );
+        }
+
+        return buf.toString();
+    }
+
+
+    /**
+     * Un escape the escaped chars in the value
+     */
+    private void unescapeValue( Value<?> value )
+    {
+        if ( !value.isBinary() )
+        {
+            String valStr = (String)value.getNormalizedValue();
+            
+            if ( StringTools.isEmpty( valStr ) )
+            {
+                return;
+            }
+            
+            try
+            {
+                String newStr= decodeEscapedHex( valStr );
+                ((ClientStringValue)value).set( newStr );
+                return;
+            }
+            catch ( InvalidNameException ine )
+            {
+                value.set( null );
+                return;
+            }
+        }
+    }
+
+    /**
+     * A private method used to normalize a value
+     * 
+     * @param attribute The attribute's ID
+     * @param value The value to normalize
+     * @return the normalized value
+     */
+    private Value<?> normalizeValue( String attribute, Value<?> value )
+    {
+        try
+        {
+            Value<?> normalized = null;
+
+            AttributeType attributeType = registries.getAttributeTypeRegistry().lookup( attribute );
+
+            if ( attributeType.getSyntax().isHumanReadable() )
+            {
+                if ( value.isBinary() )
+                {
+                    normalized = new ClientStringValue( ( String ) ncn.normalizeByName( attribute, StringTools
+                        .utf8ToString( ( byte[] ) value.get() ) ) );
+                    
+                    unescapeValue( normalized );
+                }
+                else
+                {
+                    normalized = new ClientStringValue( ( String ) ncn.normalizeByName( attribute, ( String ) value
+                        .get() ) );
+                    
+                    unescapeValue( normalized );
+                }
+            }
+            else
+            {
+                if ( value.isBinary() )
+                {
+                    normalized = new ClientBinaryValue( ( byte[] ) ncn.normalizeByName( attribute, ( byte[] ) value
+                        .get() ) );
+                }
+                else
+                {
+                    normalized = new ClientBinaryValue( ( byte[] ) ncn.normalizeByName( attribute, ( String ) value
+                        .get() ) );
+
+                }
+            }
+
+            return normalized;
+        }
+        catch ( NamingException ne )
+        {
+            log.warn( "Failed to normalize filter value: {}", ne.getMessage(), ne );
+            return null;
+        }
+
+    }
+
+
+    /**
+     * Visit a PresenceNode. If the attribute exists, the node is returned, otherwise
+     * null is returned.
+     * 
+     * @param node the node to visit
+     * @return The visited node
+     */
+    private ExprNode visitPresenceNode( PresenceNode node )
+    {
+        try
+        {
+            node.setAttribute( registries.getOidRegistry().getOid( node.getAttribute() ) );
+            return node;
+        }
+        catch ( NamingException ne )
+        {
+            log.warn( "Failed to normalize filter node attribute: {}, error: {}", node.getAttribute(), ne.getMessage() );
+            return null;
+        }
+    }
+
+
+    /**
+     * Visit a SimpleNode. If the attribute exists, the node is returned, otherwise
+     * null is returned. SimpleNodes are :
+     *  - ApproximateNode
+     *  - EqualityNode
+     *  - GreaterEqNode
+     *  - LesserEqNode
+     *  
+     * @param node the node to visit
+     * @return the visited node
+     */
+    private ExprNode visitSimpleNode( SimpleNode node )
+    {
+        // still need this check here in case the top level is a leaf node
+        // with an undefined attributeType for its attribute
+        if ( !ncn.isDefined( node.getAttribute() ) )
+        {
+            return null;
+        }
+
+        Value<?> normalized = normalizeValue( node.getAttribute(), node.getValue() );
+
+        if ( normalized == null )
+        {
+            return null;
+        }
+
+        try
+        {
+            node.setAttribute( registries.getOidRegistry().getOid( node.getAttribute() ) );
+            node.setValue( normalized );
+            return node;
+        }
+        catch ( NamingException ne )
+        {
+            log.warn( "Failed to normalize filter node attribute: {}, error: {}", node.getAttribute(), ne.getMessage() );
+            return null;
+        }
+    }
+
+
+    /**
+     * Visit a SubstringNode. If the attribute exists, the node is returned, otherwise
+     * null is returned. 
+     * 
+     * Normalizing substring value is pretty complex. It's not currently implemented...
+     * 
+     * @param node the node to visit
+     * @return the visited node
+     */
+    private ExprNode visitSubstringNode( SubstringNode node )
+    {
+        // still need this check here in case the top level is a leaf node
+        // with an undefined attributeType for its attribute
+        if ( !ncn.isDefined( node.getAttribute() ) )
+        {
+            return null;
+        }
+
+        Value<?> normInitial = null;
+
+        if ( node.getInitial() != null )
+        {
+            normInitial = normalizeValue( node.getAttribute(), new ClientStringValue( node.getInitial() ) );
+
+            if ( normInitial == null )
+            {
+                return null;
+            }
+        }
+
+        List<String> normAnys = null;
+
+        if ( ( node.getAny() != null ) && ( node.getAny().size() != 0 ) )
+        {
+            normAnys = new ArrayList<String>( node.getAny().size() );
+
+            for ( String any : node.getAny() )
+            {
+                Value<?> normAny = normalizeValue( node.getAttribute(), new ClientStringValue( any ) );
+
+                if ( normAny != null )
+                {
+                    normAnys.add( ( String ) normAny.get() );
+                }
+            }
+
+            if ( normAnys.size() == 0 )
+            {
+                return null;
+            }
+        }
+
+        Value<?> normFinal = null;
+
+        if ( node.getFinal() != null )
+        {
+            normFinal = normalizeValue( node.getAttribute(), new ClientStringValue( node.getFinal() ) );
+
+            if ( normFinal == null )
+            {
+                return null;
+            }
+        }
+
+        try
+        {
+            node.setAttribute( registries.getOidRegistry().getOid( node.getAttribute() ) );
+
+            if ( normInitial != null )
+            {
+                node.setInitial( ( String ) normInitial.get() );
+            }
+            else
+            {
+                node.setInitial( null );
+            }
+
+            node.setAny( normAnys );
+
+            if ( normFinal != null )
+            {
+                node.setFinal( ( String ) normFinal.get() );
+            }
+            else
+            {
+                node.setFinal( null );
+            }
+
+            return node;
+        }
+        catch ( NamingException ne )
+        {
+            log.warn( "Failed to normalize filter node attribute: {}, error: {}", node.getAttribute(), ne.getMessage() );
+            return null;
+        }
+    }
+
+
+    /**
+     * Visit a ExtensibleNode. If the attribute exists, the node is returned, otherwise
+     * null is returned. 
+     * 
+     * TODO implement the logic for ExtensibleNode
+     * 
+     * @param node the node to visit
+     * @return the visited node
+     */
+    private ExprNode visitExtensibleNode( ExtensibleNode node )
+    {
+        try
+        {
+            node.setAttribute( registries.getOidRegistry().getOid( node.getAttribute() ) );
+            return node;
+        }
+        catch ( NamingException ne )
+        {
+            log.warn( "Failed to normalize filter node attribute: {}, error: {}", node.getAttribute(), ne.getMessage() );
+            return null;
+        }
+    }
+
+
+    /**
+     * Visit a BranchNode. BranchNodes are :
+     *  - AndNode
+     *  - NotNode
+     *  - OrNode
+     *  
+     * @param node the node to visit
+     * @return the visited node
+     */
+    private ExprNode visitBranchNode( BranchNode node )
+    {
+        // Two differente cases :
+        // - AND or OR
+        // - NOT
+
+        if ( node instanceof NotNode )
+        {
+            // Manage the NOT
+            ExprNode child = node.getFirstChild();
+
+            ExprNode result = ( ExprNode ) visit( child );
+
+            if ( result == null )
+            {
+                return result;
+            }
+            else if ( result instanceof BranchNode )
+            {
+                node.setChildren( ( ( BranchNode ) result ).getChildren() );
+                return node;
+            }
+            else if ( result instanceof LeafNode )
+            {
+                List<ExprNode> newChildren = new ArrayList<ExprNode>( 1 );
+                newChildren.add( result );
+                node.setChildren( newChildren );
+                return node;
+            }
+        }
+        else
+        {
+            // Manage AND and OR nodes.
+            BranchNode branchNode = node;
+            List<ExprNode> children = node.getChildren();
+
+            // For AND and OR, we may have more than one children.
+            // We may have to remove some of them, so let's create
+            // a new handler to store the correct nodes.
+            List<ExprNode> newChildren = new ArrayList<ExprNode>( children.size() );
+
+            // Now, iterate through all the children
+            for ( int i = 0; i < children.size(); i++ )
+            {
+                ExprNode child = children.get( i );
+
+                ExprNode result = ( ExprNode ) visit( child );
+
+                if ( result != null )
+                {
+                    // As the node is correct, add it to the children 
+                    // list.
+                    newChildren.add( result );
+                }
+            }
+
+            if ( ( branchNode instanceof AndNode ) && ( newChildren.size() != children.size() ) )
+            {
+                return null;
+            }
+
+            if ( newChildren.size() == 0 )
+            {
+                // No more children, return null
+                return null;
+            }
+            else if ( newChildren.size() == 1 )
+            {
+                // As we only have one child, return it
+                // to the caller.
+                return newChildren.get( 0 );
+            }
+            else
+            {
+                branchNode.setChildren( newChildren );
+            }
+        }
+
+        return node;
+    }
+
+
+    /**
+     * Visit the tree, normalizing the leaves and recusrsively visit the branches.
+     * 
+     * Here are the leaves we are visiting :
+     * - PresenceNode ( attr =* )
+     * - ExtensibleNode ( ? )
+     * - SubStringNode ( attr = *X*Y* )
+     * - ApproximateNode ( attr ~= value )
+     * - EqualityNode ( attr = value )
+     * - GreaterEqNode ( attr >= value )
+     * - LessEqNode ( attr <= value )
+     * 
+     * The PresencNode is managed differently from other nodes, as it just check
+     * for the attribute, not the value.
+     * 
+     * @param node the node to visit
+     * @return the visited node
+     */
+    public Object visit( ExprNode node )
+    {
+        // -------------------------------------------------------------------
+        // Handle PresenceNodes
+        // -------------------------------------------------------------------
+
+        if ( node instanceof PresenceNode )
+        {
+            return visitPresenceNode( ( PresenceNode ) node );
+        }
+
+        // -------------------------------------------------------------------
+        // Handle BranchNodes (AndNode, NotNode and OrNode)
+        // -------------------------------------------------------------------
+
+        else if ( node instanceof BranchNode )
+        {
+            return visitBranchNode( ( BranchNode ) node );
+        }
+
+        // -------------------------------------------------------------------
+        // Handle SimpleNodes (ApproximateNode, EqualityNode, GreaterEqNode,
+        // and LesserEqNode) 
+        // -------------------------------------------------------------------
+
+        else if ( node instanceof SimpleNode )
+        {
+            return visitSimpleNode( ( SimpleNode ) node );
+        }
+        else if ( node instanceof ExtensibleNode )
+        {
+            return visitExtensibleNode( ( ExtensibleNode ) node );
+        }
+        else if ( node instanceof SubstringNode )
+        {
+            return visitSubstringNode( ( SubstringNode ) node );
+        }
+        else
+        {
+            return null;
+        }
+    }
+
+
+    public boolean canVisit( ExprNode node )
+    {
+        return true;
+    }
+
+
+    public boolean isPrefix()
+    {
+        return false;
+    }
+
+
+    public List<ExprNode> getOrder( BranchNode node, List<ExprNode> children )
+    {
+        return children;
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/normalization/UndefinedFilterAttributeException.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/normalization/UndefinedFilterAttributeException.java
new file mode 100644
index 0000000..03041a3
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/normalization/UndefinedFilterAttributeException.java
@@ -0,0 +1,70 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.normalization;
+
+import org.apache.directory.shared.ldap.filter.LeafNode;
+
+
+/**
+ * A runtime exception thrown by visitors to denote the failure
+ * to recognize attributes in a filter expression tree.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class UndefinedFilterAttributeException extends RuntimeException
+{
+    private static final long serialVersionUID = -8073762118319523479L;
+    private final LeafNode node;
+
+    
+    public UndefinedFilterAttributeException( LeafNode node )
+    {
+        super();
+        this.node = node;
+    }
+
+
+    public UndefinedFilterAttributeException( LeafNode node, String message )
+    {
+        super( message );
+        this.node = node;
+    }
+
+
+    public UndefinedFilterAttributeException( LeafNode node, String message, Throwable cause )
+    {
+        super( message, cause );
+        this.node = node;
+    }
+
+
+    public UndefinedFilterAttributeException( LeafNode node, Throwable cause )
+    {
+        super( cause );
+        this.node = node;
+    }
+    
+
+    public LeafNode getUndefinedFilterNode()
+    {
+        return node;
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/operational/OperationalAttributeInterceptor.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/operational/OperationalAttributeInterceptor.java
new file mode 100644
index 0000000..a584101
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/operational/OperationalAttributeInterceptor.java
@@ -0,0 +1,485 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.operational;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.directory.server.constants.ApacheSchemaConstants;
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.entry.DefaultServerAttribute;
+import org.apache.directory.server.core.entry.DefaultServerEntry;
+import org.apache.directory.server.core.entry.ServerAttribute;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerModification;
+import org.apache.directory.server.core.entry.ServerSearchResult;
+import org.apache.directory.server.core.enumeration.SearchResultFilter;
+import org.apache.directory.server.core.enumeration.SearchResultFilteringEnumeration;
+import org.apache.directory.server.core.interceptor.BaseInterceptor;
+import org.apache.directory.server.core.interceptor.Interceptor;
+import org.apache.directory.server.core.interceptor.NextInterceptor;
+import org.apache.directory.server.core.interceptor.context.AddOperationContext;
+import org.apache.directory.server.core.interceptor.context.ListOperationContext;
+import org.apache.directory.server.core.interceptor.context.LookupOperationContext;
+import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
+import org.apache.directory.server.core.interceptor.context.MoveAndRenameOperationContext;
+import org.apache.directory.server.core.interceptor.context.MoveOperationContext;
+import org.apache.directory.server.core.interceptor.context.RenameOperationContext;
+import org.apache.directory.server.core.interceptor.context.SearchOperationContext;
+import org.apache.directory.server.core.invocation.Invocation;
+import org.apache.directory.server.core.invocation.InvocationStack;
+import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.entry.Modification;
+import org.apache.directory.shared.ldap.entry.ModificationOperation;
+import org.apache.directory.shared.ldap.entry.Value;
+import org.apache.directory.shared.ldap.name.AttributeTypeAndValue;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.name.Rdn;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.schema.UsageEnum;
+import org.apache.directory.shared.ldap.util.DateUtils;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.SearchControls;
+
+
+/**
+ * An {@link Interceptor} that adds or modifies the default attributes
+ * of entries. There are four default attributes for now;
+ * <tt>'creatorsName'</tt>, <tt>'createTimestamp'</tt>, <tt>'modifiersName'</tt>,
+ * and <tt>'modifyTimestamp'</tt>.
+ *
+ * @org.apache.xbean.XBean
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class OperationalAttributeInterceptor extends BaseInterceptor
+{
+    private final SearchResultFilter DENORMALIZING_SEARCH_FILTER = new SearchResultFilter()
+    {
+        public boolean accept( Invocation invocation, ServerSearchResult result, SearchControls controls ) 
+            throws NamingException
+        {
+            ServerEntry serverEntry = result.getServerEntry(); 
+            
+            if ( controls.getReturningAttributes() == null )
+            {
+                return true;
+            }
+            
+            boolean denormalized = filterDenormalized( serverEntry );
+            
+            result.setServerEntry( serverEntry );
+            
+            return denormalized;
+        }
+    };
+
+    /**
+     * the database search result filter to register with filter service
+     */
+    private final SearchResultFilter SEARCH_FILTER = new SearchResultFilter()
+    {
+        public boolean accept( Invocation invocation, ServerSearchResult result, SearchControls controls )
+            throws NamingException
+        {
+            ServerEntry serverEntry = result.getServerEntry(); 
+            
+            return controls.getReturningAttributes() != null || filterOperationalAttributes( serverEntry );
+        }
+    };
+
+
+    private AttributeTypeRegistry atRegistry;
+
+    private DirectoryService service;
+
+    private LdapDN subschemaSubentryDn;
+    
+    private Registries registries;
+
+
+    /**
+     * Creates the operational attribute management service interceptor.
+     */
+    public OperationalAttributeInterceptor()
+    {
+    }
+
+
+    public void init( DirectoryService directoryService ) throws NamingException
+    {
+        service = directoryService;
+        registries = directoryService.getRegistries();
+        atRegistry = registries.getAttributeTypeRegistry();
+
+        // stuff for dealing with subentries (garbage for now)
+        Value<?> subschemaSubentry = service.getPartitionNexus()
+                .getRootDSE( null ).get( SchemaConstants.SUBSCHEMA_SUBENTRY_AT ).get();
+        subschemaSubentryDn = new LdapDN( (String)subschemaSubentry.get() );
+        subschemaSubentryDn.normalize( directoryService.getRegistries().getAttributeTypeRegistry().getNormalizerMapping() );
+    }
+
+
+    public void destroy()
+    {
+    }
+
+
+    /**
+     * Adds extra operational attributes to the entry before it is added.
+     */
+    public void add( NextInterceptor nextInterceptor, AddOperationContext opContext )
+        throws NamingException
+    {
+        String principal = getPrincipal().getName();
+        
+        ServerEntry entry = opContext.getEntry();
+
+        entry.put( SchemaConstants.CREATORS_NAME_AT, principal );
+        entry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() );
+        
+        nextInterceptor.add( opContext );
+    }
+
+
+    public void modify( NextInterceptor nextInterceptor, ModifyOperationContext opContext )
+        throws NamingException
+    {
+        nextInterceptor.modify( opContext );
+        
+        if ( opContext.getDn().getNormName().equals( subschemaSubentryDn.getNormName() ) ) 
+        {
+            return;
+        }
+
+        // -------------------------------------------------------------------
+        // Add the operational attributes for the modifier first
+        // -------------------------------------------------------------------
+        
+        List<Modification> modItemList = new ArrayList<Modification>(2);
+        
+        AttributeType modifiersNameAt = atRegistry.lookup( SchemaConstants.MODIFIERS_NAME_AT );
+        ServerAttribute attribute = new DefaultServerAttribute( 
+            SchemaConstants.MODIFIERS_NAME_AT,
+            modifiersNameAt, 
+            getPrincipal().getName());
+
+        Modification modifiers = new ServerModification( ModificationOperation.REPLACE_ATTRIBUTE, attribute );
+        //modifiers.setServerModified();
+        modItemList.add( modifiers );
+        
+        AttributeType modifyTimeStampAt = atRegistry.lookup( SchemaConstants.MODIFY_TIMESTAMP_AT );
+        attribute = new DefaultServerAttribute( 
+            SchemaConstants.MODIFY_TIMESTAMP_AT,
+            modifyTimeStampAt,
+            DateUtils.getGeneralizedTime() );
+        
+        Modification timestamp = new ServerModification( ModificationOperation.REPLACE_ATTRIBUTE, attribute );
+        //timestamp.setServerModified();
+        modItemList.add( timestamp );
+
+        // -------------------------------------------------------------------
+        // Make the modify() call happen
+        // -------------------------------------------------------------------
+
+        ModifyOperationContext newModify = new ModifyOperationContext( registries, opContext.getDn(), modItemList );
+        service.getPartitionNexus().modify( newModify );
+    }
+
+
+    public void rename( NextInterceptor nextInterceptor, RenameOperationContext opContext )
+        throws NamingException
+    {
+        nextInterceptor.rename( opContext );
+
+        // add operational attributes after call in case the operation fails
+        ServerEntry serverEntry = new DefaultServerEntry( registries, opContext.getDn() );
+        serverEntry.put( SchemaConstants.MODIFIERS_NAME_AT, getPrincipal().getName() );
+        serverEntry.put( SchemaConstants.MODIFY_TIMESTAMP_AT, DateUtils.getGeneralizedTime() );
+
+        LdapDN newDn = ( LdapDN ) opContext.getDn().clone();
+        newDn.remove( opContext.getDn().size() - 1 );
+        newDn.add( opContext.getNewRdn() );
+        newDn.normalize( atRegistry.getNormalizerMapping() );
+        
+        List<Modification> items = ModifyOperationContext.createModItems( serverEntry, ModificationOperation.REPLACE_ATTRIBUTE );
+
+        ModifyOperationContext newModify = new ModifyOperationContext( registries, newDn, items );
+        
+        service.getPartitionNexus().modify( newModify );
+    }
+
+
+    public void move( NextInterceptor nextInterceptor, MoveOperationContext opContext ) throws NamingException
+    {
+        nextInterceptor.move( opContext );
+
+        // add operational attributes after call in case the operation fails
+        ServerEntry serverEntry = new DefaultServerEntry( registries, opContext.getDn() );
+        serverEntry.put( SchemaConstants.MODIFIERS_NAME_AT, getPrincipal().getName() );
+        serverEntry.put( SchemaConstants.MODIFY_TIMESTAMP_AT, DateUtils.getGeneralizedTime() );
+
+        List<Modification> items = ModifyOperationContext.createModItems( serverEntry, ModificationOperation.REPLACE_ATTRIBUTE );
+
+
+        ModifyOperationContext newModify = 
+            new ModifyOperationContext( registries, opContext.getParent(), items );
+        
+        service.getPartitionNexus().modify( newModify );
+    }
+
+
+    public void moveAndRename( NextInterceptor nextInterceptor, MoveAndRenameOperationContext opContext )
+        throws NamingException
+    {
+        nextInterceptor.moveAndRename( opContext );
+
+        // add operational attributes after call in case the operation fails
+        ServerEntry serverEntry = new DefaultServerEntry( registries, opContext.getDn() );
+        serverEntry.put( SchemaConstants.MODIFIERS_NAME_AT, getPrincipal().getName() );
+        serverEntry.put( SchemaConstants.MODIFY_TIMESTAMP_AT, DateUtils.getGeneralizedTime() );
+
+        List<Modification> items = ModifyOperationContext.createModItems( serverEntry, ModificationOperation.REPLACE_ATTRIBUTE );
+
+        ModifyOperationContext newModify = 
+            new ModifyOperationContext( registries, 
+                opContext.getParent(), items );
+        
+        service.getPartitionNexus().modify( newModify );
+    }
+
+
+    public ServerEntry lookup( NextInterceptor nextInterceptor, LookupOperationContext opContext ) throws NamingException
+    {
+        ServerEntry result = nextInterceptor.lookup( opContext );
+        
+        if ( result == null )
+        {
+            return null;
+        }
+
+        if ( opContext.getAttrsId() == null )
+        {
+            filterOperationalAttributes( result );
+        }
+        else
+        {
+            filter( opContext, result );
+        }
+        
+        return result;
+    }
+
+
+    public NamingEnumeration<ServerSearchResult> list( NextInterceptor nextInterceptor, ListOperationContext opContext ) throws NamingException
+    {
+        NamingEnumeration<ServerSearchResult> result = nextInterceptor.list( opContext );
+        Invocation invocation = InvocationStack.getInstance().peek();
+        
+        return new SearchResultFilteringEnumeration( result, new SearchControls(), invocation, SEARCH_FILTER, "List Operational Filter" );
+    }
+
+
+    public NamingEnumeration<ServerSearchResult> search( NextInterceptor nextInterceptor, SearchOperationContext opContext ) throws NamingException
+    {
+        Invocation invocation = InvocationStack.getInstance().peek();
+        NamingEnumeration<ServerSearchResult> result = nextInterceptor.search( opContext );
+        SearchControls searchCtls = opContext.getSearchControls();
+        
+        if ( searchCtls.getReturningAttributes() != null )
+        {
+            if ( service.isDenormalizeOpAttrsEnabled() )
+            {
+                return new SearchResultFilteringEnumeration( result, searchCtls, invocation, DENORMALIZING_SEARCH_FILTER, "Search Operational Filter denormalized" );
+            }
+                
+            return result;
+        }
+
+        return new SearchResultFilteringEnumeration( result, searchCtls, invocation, SEARCH_FILTER , "Search Operational Filter");
+    }
+
+
+    /**
+     * Filters out the operational attributes within a search results attributes.  The attributes are directly
+     * modified.
+     *
+     * @param attributes the resultant attributes to filter
+     * @return true always
+     * @throws NamingException if there are failures in evaluation
+     */
+    private boolean filterOperationalAttributes( ServerEntry attributes ) throws NamingException
+    {
+        Set<AttributeType> removedAttributes = new HashSet<AttributeType>();
+
+        // Build a list of attributeType to remove
+        for ( AttributeType attributeType:attributes.getAttributeTypes() )
+        {
+            if ( attributeType.getUsage() != UsageEnum.USER_APPLICATIONS )
+            {
+                removedAttributes.add( attributeType );
+            }
+        }
+        
+        // Now remove the attributes which are not USERs
+        for ( AttributeType attributeType:removedAttributes )
+        {
+            attributes.removeAttributes( attributeType );
+        }
+        
+        return true;
+    }
+
+
+    private void filter( LookupOperationContext lookupContext, ServerEntry entry ) throws NamingException
+    {
+        LdapDN dn = lookupContext.getDn();
+        List<String> ids = lookupContext.getAttrsId();
+        
+        // still need to protect against returning op attrs when ids is null
+        if ( ids == null )
+        {
+            filterOperationalAttributes( entry );
+            return;
+        }
+
+        Set<AttributeType> attributeTypes = entry.getAttributeTypes();
+
+        if ( dn.size() == 0 )
+        {
+            for ( AttributeType attributeType:attributeTypes )
+            {
+                if ( !ids.contains( attributeType.getOid() ) )
+                {
+                    entry.removeAttributes( attributeType );
+                }
+            }
+        }
+
+        denormalizeEntryOpAttrs( entry );
+        
+        // do nothing past here since this explicity specifies which
+        // attributes to include - backends will automatically populate
+        // with right set of attributes using ids array
+    }
+
+    
+    public void denormalizeEntryOpAttrs( ServerEntry entry ) throws NamingException
+    {
+        if ( service.isDenormalizeOpAttrsEnabled() )
+        {
+            EntryAttribute attr = entry.get( SchemaConstants.CREATORS_NAME_AT );
+
+            if ( attr != null )
+            {
+                LdapDN creatorsName = new LdapDN( attr.getString() );
+                
+                attr.clear();
+                attr.add( denormalizeTypes( creatorsName ).getUpName() );
+            }
+            
+            attr = entry.get( SchemaConstants.MODIFIERS_NAME_AT );
+            
+            if ( attr != null )
+            {
+                LdapDN modifiersName = new LdapDN( attr.getString() );
+
+                attr.clear();
+                attr.add( denormalizeTypes( modifiersName ).getUpName() );
+            }
+
+            attr = entry.get( ApacheSchemaConstants.SCHEMA_MODIFIERS_NAME_AT );
+            
+            if ( attr != null )
+            {
+                LdapDN modifiersName = new LdapDN( attr.getString() );
+
+                attr.clear();
+                attr.add( denormalizeTypes( modifiersName ).getUpName() );
+            }
+        }
+    }
+
+    
+    /**
+     * Does not create a new DN but alters existing DN by using the first
+     * short name for an attributeType definition.
+     * 
+     * @param dn the normalized distinguished name
+     * @return the distinuished name denormalized
+     * @throws NamingException if there are problems denormalizing
+     */
+    public LdapDN denormalizeTypes( LdapDN dn ) throws NamingException
+    {
+        LdapDN newDn = new LdapDN();
+        
+        for ( int ii = 0; ii < dn.size(); ii++ )
+        {
+            Rdn rdn = dn.getRdn( ii );
+            if ( rdn.size() == 0 )
+            {
+                newDn.add( new Rdn() );
+                continue;
+            }
+            else if ( rdn.size() == 1 )
+            {
+                String name = atRegistry.lookup( rdn.getNormType() ).getName();
+                String value = (String)rdn.getAtav().getNormValue(); 
+                newDn.add( new Rdn( name, name, value, value ) );
+                continue;
+            }
+
+            // below we only process multi-valued rdns
+            StringBuffer buf = new StringBuffer();
+        
+            for ( Iterator<AttributeTypeAndValue> atavs = rdn.iterator(); atavs.hasNext(); /**/ )
+            {
+                AttributeTypeAndValue atav = atavs.next();
+                String type = atRegistry.lookup( rdn.getNormType() ).getName();
+                buf.append( type ).append( '=' ).append( atav.getNormValue() );
+                
+                if ( atavs.hasNext() )
+                {
+                    buf.append( '+' );
+                }
+            }
+            
+            newDn.add( new Rdn(buf.toString()) );
+        }
+        
+        return newDn;
+    }
+
+
+    private boolean filterDenormalized( ServerEntry entry ) throws NamingException
+    {
+        denormalizeEntryOpAttrs( entry );
+        return true;
+    }
+}
\ No newline at end of file
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/AbstractPartition.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/AbstractPartition.java
new file mode 100644
index 0000000..838ed62
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/AbstractPartition.java
@@ -0,0 +1,173 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.interceptor.context.EntryOperationContext;
+import org.apache.directory.server.core.interceptor.context.LookupOperationContext;
+
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+
+
+/**
+ * A {@link Partition} that helps users to implement their own partition.
+ * Most methods are implemented by default.  Please look at the description of
+ * each methods for the detail of implementations.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public abstract class AbstractPartition implements Partition
+{
+    /** {@link DirectoryService} specified at {@link #init(DirectoryService)}. */
+    protected DirectoryService directoryService;
+    /** <tt>true</tt> if and only if this partition is initialized. */
+    protected boolean initialized;
+
+
+    protected AbstractPartition()
+    {
+    }
+
+
+    /**
+     * Sets up (<tt>directoryService</tt> and calls {@link #doInit()} where you have to put your
+     * initialization code in.  {@link #isInitialized()} will return <tt>true</tt> if
+     * {@link #doInit()} returns without any errors.  {@link #destroy()} is called automatically
+     * as a clean-up process if {@link #doInit()} throws an exception.
+     */
+    public final void init( DirectoryService directoryService ) throws NamingException
+    {
+        if ( initialized )
+        {
+            // Already initialized.
+            return;
+        }
+
+        this.directoryService = directoryService;
+        try
+        {
+            doInit();
+            initialized = true;
+        }
+        finally
+        {
+            if ( !initialized )
+            {
+                destroy();
+            }
+        }
+    }
+    
+
+    /**
+     * Override this method to put your initialization code.
+     */
+    protected void doInit()
+    {
+    }
+
+
+    /**
+     * Calls {@link #doDestroy()} where you have to put your destroy code in,
+     * and clears default properties.  Once this method is invoked, {@link #isInitialized()}
+     * will return <tt>false</tt>.
+     */
+    public final void destroy()
+    {
+        try
+        {
+            doDestroy();
+        }
+        finally
+        {
+            initialized = false;
+            directoryService = null;
+        }
+    }
+
+
+    /**
+     * Override this method to put your initialization code.
+     */
+    protected void doDestroy()
+    {
+    }
+
+
+    /**
+     * Returns <tt>true</tt> if this context partition is initialized successfully.
+     */
+    public final boolean isInitialized()
+    {
+        return initialized;
+    }
+
+
+    /**
+     * Returns {@link DirectoryService} that is provided from
+     * {@link #init(DirectoryService)}.
+     * @return return the directory service core
+     */
+    public final DirectoryService getDirectoryService()
+    {
+        return directoryService;
+    }
+
+
+    /**
+     * This method does nothing by default.
+     */
+    public void sync() throws NamingException
+    {
+    }
+
+
+    /**
+     * This method calls {@link Partition#lookup(LookupOperationContext)} and return <tt>true</tt>
+     * if it returns an entry by default.  Please override this method if
+     * there is more effective way for your implementation.
+     */
+    public boolean hasEntry( EntryOperationContext entryContext ) throws NamingException
+    {
+        try
+        {
+            return lookup( new LookupOperationContext( entryContext.getRegistries(), entryContext.getDn() ) ) != null;
+        }
+        catch ( NameNotFoundException e )
+        {
+            return false;
+        }
+    }
+
+
+    /**
+     * This method calls {@link Partition#lookup(LookupOperationContext)}
+     * with null <tt>attributeIds</tt> by default.  Please override
+     * this method if there is more effective way for your implementation.
+     */
+    public ServerEntry lookup( LookupOperationContext lookupContext ) throws NamingException
+    {
+        return null;
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/DefaultPartitionNexus.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/DefaultPartitionNexus.java
new file mode 100644
index 0000000..3664c07
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/DefaultPartitionNexus.java
@@ -0,0 +1,1174 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition;
+
+
+import org.apache.directory.server.constants.ServerDNConstants;
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.entry.DefaultServerAttribute;
+import org.apache.directory.server.core.entry.DefaultServerEntry;
+import org.apache.directory.server.core.entry.ServerAttribute;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerSearchResult;
+import org.apache.directory.server.core.interceptor.context.AddContextPartitionOperationContext;
+import org.apache.directory.server.core.interceptor.context.AddOperationContext;
+import org.apache.directory.server.core.interceptor.context.BindOperationContext;
+import org.apache.directory.server.core.interceptor.context.CompareOperationContext;
+import org.apache.directory.server.core.interceptor.context.DeleteOperationContext;
+import org.apache.directory.server.core.interceptor.context.EntryOperationContext;
+import org.apache.directory.server.core.interceptor.context.GetMatchedNameOperationContext;
+import org.apache.directory.server.core.interceptor.context.GetRootDSEOperationContext;
+import org.apache.directory.server.core.interceptor.context.GetSuffixOperationContext;
+import org.apache.directory.server.core.interceptor.context.ListOperationContext;
+import org.apache.directory.server.core.interceptor.context.ListSuffixOperationContext;
+import org.apache.directory.server.core.interceptor.context.LookupOperationContext;
+import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
+import org.apache.directory.server.core.interceptor.context.MoveAndRenameOperationContext;
+import org.apache.directory.server.core.interceptor.context.MoveOperationContext;
+import org.apache.directory.server.core.interceptor.context.RemoveContextPartitionOperationContext;
+import org.apache.directory.server.core.interceptor.context.RenameOperationContext;
+import org.apache.directory.server.core.interceptor.context.SearchOperationContext;
+import org.apache.directory.server.core.interceptor.context.UnbindOperationContext;
+import org.apache.directory.server.core.partition.impl.btree.Index;
+import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmIndex;
+import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmPartition;
+import org.apache.directory.server.core.partition.tree.BranchNode;
+import org.apache.directory.server.core.partition.tree.LeafNode;
+import org.apache.directory.server.core.partition.tree.Node;
+import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
+import org.apache.directory.server.schema.registries.OidRegistry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.MultiException;
+import org.apache.directory.shared.ldap.NotImplementedException;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.entry.Value;
+import org.apache.directory.shared.ldap.exception.LdapInvalidAttributeIdentifierException;
+import org.apache.directory.shared.ldap.exception.LdapNameNotFoundException;
+import org.apache.directory.shared.ldap.exception.LdapNoSuchAttributeException;
+import org.apache.directory.shared.ldap.filter.ExprNode;
+import org.apache.directory.shared.ldap.filter.PresenceNode;
+import org.apache.directory.shared.ldap.message.CascadeControl;
+import org.apache.directory.shared.ldap.message.EntryChangeControl;
+import org.apache.directory.shared.ldap.message.ManageDsaITControl;
+import org.apache.directory.shared.ldap.message.PersistentSearchControl;
+import org.apache.directory.shared.ldap.message.SubentriesControl;
+import org.apache.directory.shared.ldap.message.extended.NoticeOfDisconnect;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.schema.Normalizer;
+import org.apache.directory.shared.ldap.schema.UsageEnum;
+import org.apache.directory.shared.ldap.util.DateUtils;
+import org.apache.directory.shared.ldap.util.NamespaceTools;
+import org.apache.directory.shared.ldap.util.SingletonEnumeration;
+import org.apache.directory.shared.ldap.util.StringTools;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.naming.ConfigurationException;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.SearchControls;
+import javax.naming.ldap.LdapContext;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+
+/**
+ * A nexus for partitions dedicated for storing entries specific to a naming
+ * context.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class DefaultPartitionNexus extends PartitionNexus
+{
+    private static final Logger LOG = LoggerFactory.getLogger( DefaultPartitionNexus.class );
+
+    /** Speedup for logs */
+    private static final boolean IS_DEBUG = LOG.isDebugEnabled();
+
+    /** the vendorName string proudly set to: Apache Software Foundation*/
+    private static final String ASF = "Apache Software Foundation";
+
+    /** the closed state of this partition */
+    private boolean initialized;
+
+    private DirectoryService directoryService;
+
+    /** the system partition */
+    private Partition system;
+
+    /** the partitions keyed by normalized suffix strings */
+    private Map<String, Partition> partitions = new HashMap<String, Partition>();
+    
+    /** A structure to hold all the partitions */
+    private BranchNode partitionLookupTree = new BranchNode();
+    
+    /** the read only rootDSE attributes */
+    private final ServerEntry rootDSE;
+
+    /** The global registries */
+    private Registries registries;
+    
+    /** The attributeType registry */
+    private AttributeTypeRegistry atRegistry;
+    
+    /** The OID registry */
+    private OidRegistry oidRegistry;
+
+
+    /**
+     * Creates the root nexus singleton of the entire system.  The root DSE has
+     * several attributes that are injected into it besides those that may
+     * already exist.  As partitions are added to the system more namingContexts
+     * attributes are added to the rootDSE.
+     *
+     * @see <a href="http://www.faqs.org/rfcs/rfc3045.html">Vendor Information</a>
+     * @param rootDSE the root entry for the DSA
+     * @throws javax.naming.NamingException on failure to initialize
+     */
+    public DefaultPartitionNexus( ServerEntry rootDSE ) throws NamingException
+    {
+        // setup that root DSE
+        this.rootDSE = rootDSE;
+        
+        // Add the basic informations
+        rootDSE.put( SchemaConstants.SUBSCHEMA_SUBENTRY_AT, ServerDNConstants.CN_SCHEMA_DN );
+        rootDSE.put( SchemaConstants.SUPPORTED_LDAP_VERSION_AT, "3" );
+        rootDSE.put( SchemaConstants.SUPPORTED_FEATURES_AT, SchemaConstants.FEATURE_ALL_OPERATIONAL_ATTRIBUTES );
+        rootDSE.put( SchemaConstants.SUPPORTED_EXTENSION_AT, NoticeOfDisconnect.EXTENSION_OID );
+
+        // Add the supported controls
+        rootDSE.put( SchemaConstants.SUPPORTED_CONTROL_AT, 
+            PersistentSearchControl.CONTROL_OID,
+            EntryChangeControl.CONTROL_OID,
+            SubentriesControl.CONTROL_OID,
+            ManageDsaITControl.CONTROL_OID,
+            CascadeControl.CONTROL_OID );
+
+        // Add the objectClasses
+        rootDSE.put( SchemaConstants.OBJECT_CLASS_AT,
+            SchemaConstants.TOP_OC,
+            SchemaConstants.EXTENSIBLE_OBJECT_OC );
+
+        // Add the 'vendor' name and version infos
+        rootDSE.put( SchemaConstants.VENDOR_NAME_AT, ASF );
+
+        Properties props = new Properties();
+        
+        try
+        {
+            props.load( getClass().getResourceAsStream( "version.properties" ) );
+        }
+        catch ( IOException e )
+        {
+            LOG.error( "failed to LOG version properties" );
+        }
+
+        rootDSE.put( SchemaConstants.VENDOR_VERSION_AT, props.getProperty( "apacheds.version", "UNKNOWN" ) );
+    }
+
+    
+    /**
+     * Always returns the string "NEXUS".
+     *
+     * @return the string "NEXUS"
+     */
+    public String getId()
+    {
+        return "NEXUS";
+    }
+
+
+    // -----------------------------------------------------------------------
+    // C O N F I G U R A T I O N   M E T H O D S
+    // -----------------------------------------------------------------------
+
+
+    /**
+     * Not supported!
+     *
+     * @throws UnsupportedOperationException everytime
+     */
+    public void setId( String id )
+    {
+        throw new UnsupportedOperationException( "The id cannot be set for the partition nexus." );
+    }
+
+
+    /**
+     * Returns root the rootDSE.
+     *
+     * @return the root entry for the DSA
+     */
+    public ServerEntry getContextEntry()
+    {
+        return rootDSE;
+    }
+
+
+    /**
+     * Sets root entry for this BTreePartition.
+     *
+     * @throws UnsupportedOperationException everytime
+     */
+    public void setContextEntry( ServerEntry rootEntry )
+    {
+        throw new UnsupportedOperationException( "Setting the RootDSE is not allowed." );
+    }
+
+
+    /**
+     * Always returns the empty String "".
+     * @return the empty String ""
+     */
+    public String getSuffix()
+    {
+        return "";
+    }
+
+
+    /**
+     * Unsupported operation on the Nexus.
+     * @throws UnsupportedOperationException everytime
+     */
+    public void setSuffix( String suffix )
+    {
+        throw new UnsupportedOperationException();
+    }
+
+
+    /**
+     * Not support!
+     */
+    public void setCacheSize( int cacheSize )
+    {
+        throw new UnsupportedOperationException( "You cannot set the cache size of the nexus" );
+    }
+
+
+    /**
+     * Not supported!
+     *
+     * @throws UnsupportedOperationException always
+     */
+    public int getCacheSize()
+    {
+        throw new UnsupportedOperationException( "There is no cache size associated with the nexus" );
+    }
+
+
+
+    public void init( DirectoryService directoryService )
+        throws NamingException
+    {
+        // NOTE: We ignore ContextPartitionConfiguration parameter here.
+        if ( initialized )
+        {
+            return;
+        }
+
+        this.directoryService = directoryService;
+        registries = directoryService.getRegistries();
+        atRegistry = registries.getAttributeTypeRegistry();
+        oidRegistry = registries.getOidRegistry();
+        
+        initializeSystemPartition();
+        List<Partition> initializedPartitions = new ArrayList<Partition>();
+        initializedPartitions.add( 0, this.system );
+
+        //noinspection unchecked
+        Iterator<? extends Partition> partitions = ( Iterator<? extends Partition> ) directoryService.getPartitions().iterator();
+        try
+        {
+            while ( partitions.hasNext() )
+            {
+                Partition partition = partitions.next();
+                AddContextPartitionOperationContext opCtx = 
+                    new AddContextPartitionOperationContext( registries, partition );
+                addContextPartition( opCtx );
+                initializedPartitions.add( opCtx.getPartition() );
+            }
+            initialized = true;
+        }
+        finally
+        {
+            if ( !initialized )
+            {
+                Iterator<Partition> i = initializedPartitions.iterator();
+                while ( i.hasNext() )
+                {
+                    Partition partition = i.next();
+                    i.remove();
+                    try
+                    {
+                        partition.destroy();
+                    }
+                    catch ( Exception e )
+                    {
+                        LOG.warn( "Failed to destroy a partition: " + partition.getSuffixDn(), e );
+                    }
+                    finally
+                    {
+                        unregister( partition );
+                    }
+                }
+            }
+        }
+    }
+
+
+    private Partition initializeSystemPartition() throws NamingException
+    {
+        // initialize system partition first
+        Partition override = directoryService.getSystemPartition();
+        
+        if ( override != null )
+        {
+            ServerEntry systemEntry = override.getContextEntry();
+            EntryAttribute objectClassAttr = systemEntry.get( SchemaConstants.OBJECT_CLASS_AT );
+            
+            if ( objectClassAttr == null )
+            {
+                systemEntry.put( SchemaConstants.OBJECT_CLASS_AT, 
+                    SchemaConstants.TOP_OC,
+                    SchemaConstants.ORGANIZATIONAL_UNIT_OC,
+                    SchemaConstants.EXTENSIBLE_OBJECT_OC );
+            }
+            else
+            {
+                // Feed the contextEntry with the mandatory ObjectClass values, if they are missing.
+                if ( !objectClassAttr.contains( SchemaConstants.TOP_OC ) )
+                {
+                    objectClassAttr.add( SchemaConstants.TOP_OC );
+                }
+                
+                if ( !objectClassAttr.contains( SchemaConstants.ORGANIZATIONAL_UNIT_OC ) )
+                {
+                    objectClassAttr.add( SchemaConstants.ORGANIZATIONAL_UNIT_OC );
+                }
+
+                if ( !objectClassAttr.contains( SchemaConstants.EXTENSIBLE_OBJECT_OC ) )
+                {
+                    objectClassAttr.add( SchemaConstants.EXTENSIBLE_OBJECT_OC );
+                }
+            }
+            
+            systemEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN );
+            systemEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() );
+            systemEntry.put( NamespaceTools.getRdnAttribute( ServerDNConstants.SYSTEM_DN ),
+                NamespaceTools.getRdnValue( ServerDNConstants.SYSTEM_DN ) );
+            
+            override.setContextEntry( systemEntry );
+            
+            // ---------------------------------------------------------------
+            // check a few things to make sure users configured it properly
+            // ---------------------------------------------------------------
+
+            if ( ! override.getId().equals( "system" ) )
+            {
+                throw new ConfigurationException( "System partition has wrong name: should be 'system' not '"
+                        + override.getId() + "'." );
+            }
+            
+            // add all attribute oids of index configs to a hashset
+            if ( override instanceof JdbmPartition )
+            {
+                Set<Index> indices = ( ( JdbmPartition ) override ).getIndexedAttributes();
+                Set<String> indexOids = new HashSet<String>();
+                OidRegistry registry = registries.getOidRegistry();
+
+                for ( Index index : indices )
+                {
+                    indexOids.add( registry.getOid( index.getAttributeId() ) );
+                }
+
+                if ( ! indexOids.contains( registry.getOid( SchemaConstants.OBJECT_CLASS_AT ) ) )
+                {
+                    LOG.warn( "CAUTION: You have not included objectClass as an indexed attribute" +
+                            "in the system partition configuration.  This will lead to poor " +
+                            "performance.  The server is automatically adding this index for you." );
+                    JdbmIndex index = new JdbmIndex();
+                    index.setAttributeId( SchemaConstants.OBJECT_CLASS_AT );
+                    indices.add( index );
+                }
+
+                ( ( JdbmPartition ) override ).setIndexedAttributes( indices );
+
+                system = override;
+            }
+        }
+        else
+        {
+            system = new JdbmPartition();
+            system.setId( "system" );
+            system.setCacheSize( 500 );
+            system.setSuffix( ServerDNConstants.SYSTEM_DN );
+    
+            // Add objectClass attribute for the system partition
+            Set<Index> indexedAttrs = new HashSet<Index>();
+            indexedAttrs.add( new JdbmIndex( SchemaConstants.OBJECT_CLASS_AT ) );
+            ( ( JdbmPartition ) system ).setIndexedAttributes( indexedAttrs );
+    
+            // Add context entry for system partition
+            ServerEntry systemEntry = new DefaultServerEntry( registries, new LdapDN( ServerDNConstants.SYSTEM_DN ) );
+
+            // Add the ObjectClasses
+            systemEntry.put( SchemaConstants.OBJECT_CLASS_AT,
+                SchemaConstants.TOP_OC,
+                SchemaConstants.ORGANIZATIONAL_UNIT_OC,
+                SchemaConstants.EXTENSIBLE_OBJECT_OC
+                );
+            
+            // Add some operational attributes
+            systemEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN );
+            systemEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() );
+            systemEntry.put( NamespaceTools.getRdnAttribute( ServerDNConstants.SYSTEM_DN ),
+                NamespaceTools.getRdnValue( ServerDNConstants.SYSTEM_DN ) );
+
+            system.setContextEntry( systemEntry );
+        }
+
+        system.init( directoryService );
+        String key = system.getSuffixDn().toString();
+        
+        if ( partitions.containsKey( key ) )
+        {
+            throw new ConfigurationException( "Duplicate partition suffix: " + key );
+        }
+        
+        synchronized ( partitionLookupTree )
+        {
+            partitions.put( key, system );
+            partitionLookupTree.recursivelyAddPartition( partitionLookupTree, system.getSuffixDn(), 0, system );
+            EntryAttribute namingContexts = rootDSE.get( SchemaConstants.NAMING_CONTEXTS_AT );
+            
+            if ( namingContexts == null )
+            {
+                namingContexts = new DefaultServerAttribute( 
+                    registries.getAttributeTypeRegistry().lookup( SchemaConstants.NAMING_CONTEXTS_AT ), 
+                    system.getUpSuffixDn().getUpName() );
+                rootDSE.put( namingContexts );
+            }
+            else
+            {
+                namingContexts.add( system.getUpSuffixDn().getUpName() );
+            }
+        }
+
+        return system;
+    }
+
+
+    public boolean isInitialized()
+    {
+        return initialized;
+    }
+
+
+    public synchronized void destroy()
+    {
+        if ( !initialized )
+        {
+            return;
+        }
+
+        // make sure this loop is not fail fast so all backing stores can
+        // have an attempt at closing down and synching their cached entries
+        for ( String suffix : new HashSet<String>( this.partitions.keySet() ) )
+        {
+            try
+            {
+                removeContextPartition( new RemoveContextPartitionOperationContext( registries, new LdapDN( suffix ) ) );
+            }
+            catch ( NamingException e )
+            {
+                LOG.warn( "Failed to destroy a partition: " + suffix, e );
+            }
+        }
+
+        initialized = false;
+    }
+
+
+    /**
+     * @see Partition#sync()
+     */
+    public void sync() throws NamingException
+    {
+        MultiException error = null;
+
+        for ( Partition partition : this.partitions.values() )
+        {
+            try
+            {
+                partition.sync();
+            }
+            catch ( NamingException e )
+            {
+                LOG.warn( "Failed to flush partition data out.", e );
+                if ( error == null )
+                {
+                    //noinspection ThrowableInstanceNeverThrown
+                    error = new MultiException( "Grouping many exceptions on root nexus sync()" );
+                }
+
+                // @todo really need to send this info to a monitor
+                error.addThrowable( e );
+            }
+        }
+
+        if ( error != null )
+        {
+            String msg = "Encountered failures while performing a sync() operation on backing stores";
+            //noinspection ThrowableInstanceNeverThrown
+            NamingException total = new NamingException( msg );
+            total.setRootCause( error );
+        }
+    }
+
+
+    // ------------------------------------------------------------------------
+    // ContextPartitionNexus Method Implementations
+    // ------------------------------------------------------------------------
+
+    public boolean compare( CompareOperationContext compareContext ) throws NamingException
+    {
+        Partition partition = getPartition( compareContext.getDn() );
+        AttributeTypeRegistry registry = registries.getAttributeTypeRegistry();
+        
+        // complain if we do not recognize the attribute being compared
+        if ( !registry.hasAttributeType( compareContext.getOid() ) )
+        {
+            throw new LdapInvalidAttributeIdentifierException( compareContext.getOid() + " not found within the attributeType registry" );
+        }
+
+        AttributeType attrType = registry.lookup( compareContext.getOid() );
+        
+        EntryAttribute attr = partition.lookup( new LookupOperationContext( registries, compareContext.getDn() ) ).get( attrType.getName() );
+
+        // complain if the attribute being compared does not exist in the entry
+        if ( attr == null )
+        {
+            throw new LdapNoSuchAttributeException();
+        }
+
+        // see first if simple match without normalization succeeds
+        if ( attr.contains( (Value<?>)compareContext.getValue()  ) )
+        {
+            return true;
+        }
+
+        // now must apply normalization to all values (attr and in request) to compare
+
+        /*
+         * Get ahold of the normalizer for the attribute and normalize the request
+         * assertion value for comparisons with normalized attribute values.  Loop
+         * through all values looking for a match.
+         */
+        Normalizer normalizer = attrType.getEquality().getNormalizer();
+        Object reqVal = normalizer.normalize( ((Value<?>)compareContext.getValue()).get() );
+
+        for ( Value<?> value:attr )
+        {
+            Object attrValObj = normalizer.normalize( value.get() );
+            
+            if ( attrValObj instanceof String )
+            {
+                String attrVal = ( String ) attrValObj;
+                if ( ( reqVal instanceof String ) && attrVal.equals( reqVal ) )
+                {
+                    return true;
+                }
+            }
+            else
+            {
+                byte[] attrVal = ( byte[] ) attrValObj;
+                if ( reqVal instanceof byte[] )
+                {
+                    return Arrays.equals( attrVal, ( byte[] ) reqVal );
+                }
+                else if ( reqVal instanceof String )
+                {
+                    return Arrays.equals( attrVal, StringTools.getBytesUtf8( ( String ) reqVal ) );
+                }
+            }
+        }
+
+        return false;
+    }
+
+
+    public synchronized void addContextPartition( AddContextPartitionOperationContext opContext ) throws NamingException
+    {
+        Partition partition = opContext.getPartition();
+
+        // Turn on default indices
+        String key = partition.getSuffix();
+        
+        if ( partitions.containsKey( key ) )
+        {
+            throw new ConfigurationException( "Duplicate partition suffix: " + key );
+        }
+
+        if ( ! partition.isInitialized() )
+        {
+            partition.setContextEntry( partition.getContextEntry() );
+            
+            partition.init( directoryService );
+        }
+        
+        synchronized ( partitionLookupTree )
+        {
+            LdapDN partitionSuffix = partition.getSuffixDn();
+            
+            if ( partitionSuffix == null )
+            {
+                throw new ConfigurationException( "The current partition does not have any suffix: " + partition.getId() );
+            }
+            
+            partitions.put( partitionSuffix.toString(), partition );
+            partitionLookupTree.recursivelyAddPartition( partitionLookupTree, partition.getSuffixDn(), 0, partition );
+
+            EntryAttribute namingContexts = rootDSE.get( SchemaConstants.NAMING_CONTEXTS_AT );
+
+            LdapDN partitionUpSuffix = partition.getUpSuffixDn();
+            
+            if ( partitionUpSuffix == null )
+            {
+                throw new ConfigurationException( "The current partition does not have any user provided suffix: " + partition.getId() );
+            }
+            
+            if ( namingContexts == null )
+            {
+                namingContexts = new DefaultServerAttribute( 
+                    registries.getAttributeTypeRegistry().lookup( SchemaConstants.NAMING_CONTEXTS_AT ), partitionUpSuffix.getUpName() );
+                rootDSE.put( namingContexts );
+            }
+            else
+            {
+                namingContexts.add( partitionUpSuffix.getUpName() );
+            }
+        }
+    }
+
+
+    public synchronized void removeContextPartition( RemoveContextPartitionOperationContext removeContextPartition ) throws NamingException
+    {
+        String key = removeContextPartition.getDn().getNormName();
+        Partition partition = partitions.get( key );
+        
+        if ( partition == null )
+        {
+            throw new NameNotFoundException( "No partition with suffix: " + key );
+        }
+
+        EntryAttribute namingContexts = rootDSE.get( SchemaConstants.NAMING_CONTEXTS_AT );
+        
+        if ( namingContexts != null )
+        {
+            namingContexts.remove( partition.getUpSuffixDn().getUpName() );
+        }
+
+        // Create a new partition list. 
+        // This is easier to create a new structure from scratch than to reorganize
+        // the current structure. As this structure is not modified often
+        // this is an acceptable solution.
+        synchronized ( partitionLookupTree )
+        {
+            partitions.remove( key );
+            partitionLookupTree = new BranchNode();
+            
+            for ( Partition part : partitions.values() )
+            {
+                partitionLookupTree.recursivelyAddPartition( partitionLookupTree, part.getSuffixDn(), 0, partition );
+            }
+    
+            partition.sync();
+            partition.destroy();
+        }
+    }
+
+
+    public Partition getSystemPartition()
+    {
+        return system;
+    }
+
+
+    /**
+     * @see PartitionNexus#getLdapContext()
+     */
+    public LdapContext getLdapContext()
+    {
+        throw new NotImplementedException();
+    }
+
+
+    /**
+     * @see PartitionNexus#getMatchedName( GetMatchedNameOperationContext )
+     */
+    public LdapDN getMatchedName ( GetMatchedNameOperationContext getMatchedNameContext ) throws NamingException
+    {
+        LdapDN dn = ( LdapDN ) getMatchedNameContext.getDn().clone();
+        
+        while ( dn.size() > 0 )
+        {
+            if ( hasEntry( new EntryOperationContext( registries, dn ) ) )
+            {
+                return dn;
+            }
+
+            dn.remove( dn.size() - 1 );
+        }
+
+        return dn;
+    }
+
+
+    public LdapDN getSuffixDn()
+    {
+        return LdapDN.EMPTY_LDAPDN;
+    }
+
+    public LdapDN getUpSuffixDn()
+    {
+        return LdapDN.EMPTY_LDAPDN;
+    }
+
+
+    /**
+     * @see PartitionNexus#getSuffix( GetSuffixOperationContext )
+     */
+    public LdapDN getSuffix ( GetSuffixOperationContext getSuffixContext ) throws NamingException
+    {
+        Partition backend = getPartition( getSuffixContext.getDn() );
+        return backend.getSuffixDn();
+    }
+
+
+    /**
+     * @see PartitionNexus#listSuffixes( ListSuffixOperationContext )
+     */
+    public Iterator<String> listSuffixes ( ListSuffixOperationContext emptyContext ) throws NamingException
+    {
+        return Collections.unmodifiableSet( partitions.keySet() ).iterator();
+    }
+
+
+    public ServerEntry getRootDSE( GetRootDSEOperationContext getRootDSEContext )
+    {
+        return rootDSE;
+    }
+
+
+    /**
+     * Unregisters an ContextPartition with this BackendManager.  Called for each
+     * registered Backend right befor it is to be stopped.  This prevents
+     * protocol server requests from reaching the Backend and effectively puts
+     * the ContextPartition's naming context offline.
+     *
+     * Operations against the naming context should result in an LDAP BUSY
+     * result code in the returnValue if the naming context is not online.
+     *
+     * @param partition ContextPartition component to unregister with this
+     * BackendNexus.
+     * @throws NamingException if there are problems unregistering the partition
+     */
+    private void unregister( Partition partition ) throws NamingException
+    {
+        EntryAttribute namingContexts = rootDSE.get( SchemaConstants.NAMING_CONTEXTS_AT );
+        
+        if ( namingContexts != null )
+        {
+            namingContexts.remove( partition.getSuffixDn().getUpName() );
+        }
+        
+        partitions.remove( partition.getSuffixDn().toString() );
+    }
+
+
+    // ------------------------------------------------------------------------
+    // DirectoryPartition Interface Method Implementations
+    // ------------------------------------------------------------------------
+    public void bind( BindOperationContext bindContext ) throws NamingException
+    {
+        Partition partition = getPartition( bindContext.getDn() );
+        partition.bind( bindContext );
+    }
+
+    public void unbind( UnbindOperationContext unbindContext ) throws NamingException
+    {
+        Partition partition = getPartition( unbindContext.getDn() );
+        partition.unbind( unbindContext );
+    }
+
+
+    /**
+     * @see Partition#delete(DeleteOperationContext)
+     */
+    public void delete( DeleteOperationContext deleteContext ) throws NamingException
+    {
+        Partition backend = getPartition( deleteContext.getDn() );
+        backend.delete( deleteContext );
+    }
+
+
+    /**
+     * Looks up the backend corresponding to the entry first, then checks to
+     * see if the entry already exists.  If so an exception is thrown.  If not
+     * the add operation against the backend proceeds.  This check is performed
+     * here so backend implementors do not have to worry about performing these
+     * kinds of checks.
+     *
+     * @see Partition#add( AddOperationContext )
+     */
+    public void add( AddOperationContext addContext ) throws NamingException
+    {
+        Partition backend = getPartition( addContext.getDn() );
+        backend.add( addContext );
+    }
+
+
+    public void modify( ModifyOperationContext modifyContext ) throws NamingException
+    {
+        Partition backend = getPartition( modifyContext.getDn() );
+        backend.modify( modifyContext );
+    }
+
+
+    /**
+     * @see Partition#list(ListOperationContext)
+     */
+    public NamingEnumeration<ServerSearchResult> list( ListOperationContext opContext ) throws NamingException
+    {
+        Partition backend = getPartition( opContext.getDn() );
+        return backend.list( opContext );
+    }
+
+
+    public NamingEnumeration<ServerSearchResult> search( SearchOperationContext opContext )
+        throws NamingException
+    {
+        LdapDN base = opContext.getDn();
+        SearchControls searchCtls = opContext.getSearchControls();
+        ExprNode filter = opContext.getFilter();
+        
+        if ( base.size() == 0 )
+        {
+            boolean isObjectScope = searchCtls.getSearchScope() == SearchControls.OBJECT_SCOPE;
+            
+            // test for (objectClass=*)
+            boolean isSearchAll = ( ( PresenceNode ) filter ).getAttribute().equals( SchemaConstants.OBJECT_CLASS_AT_OID );
+
+            /*
+             * if basedn is "", filter is "(objectclass=*)" and scope is object
+             * then we have a request for the rootDSE
+             */
+            if ( filter instanceof PresenceNode && isObjectScope && isSearchAll )
+            {
+                String[] ids = searchCtls.getReturningAttributes();
+
+                // -----------------------------------------------------------
+                // If nothing is asked for then we just return the entry asis.
+                // We let other mechanisms filter out operational attributes.
+                // -----------------------------------------------------------
+                if ( ( ids == null ) || ( ids.length == 0 ) )
+                {
+                    ServerEntry rootDSE = (ServerEntry)getRootDSE( null ).clone();
+                    ServerSearchResult result = new ServerSearchResult( LdapDN.EMPTY_LDAPDN, null, rootDSE, false );
+                    return new SingletonEnumeration<ServerSearchResult>( result );
+                }
+                
+                // -----------------------------------------------------------
+                // Collect all the real attributes besides 1.1, +, and * and
+                // note if we've seen these special attributes as well.
+                // -----------------------------------------------------------
+
+                Set<String> realIds = new HashSet<String>();
+                boolean containsAsterisk = false;
+                boolean containsPlus = false;
+                boolean containsOneDotOne = false;
+                
+                for ( String id:ids )
+                {
+                    String idTrimmed = id.trim();
+                    
+                    if ( idTrimmed.equals( SchemaConstants.ALL_USER_ATTRIBUTES ) )
+                    {
+                        containsAsterisk = true;
+                    }
+                    else if ( idTrimmed.equals( SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES ) )
+                    {
+                        containsPlus = true;
+                    }
+                    else if ( idTrimmed.equals( SchemaConstants.NO_ATTRIBUTE ) )
+                    {
+                        containsOneDotOne = true;
+                    }
+                    else
+                    {
+                        try
+                        {
+                            realIds.add( oidRegistry.getOid( idTrimmed ) );
+                        }
+                        catch ( NamingException e )
+                        {
+                            realIds.add( idTrimmed );
+                        }
+                    }
+                }
+
+                // return nothing
+                if ( containsOneDotOne )
+                {
+                    ServerEntry serverEntry = new DefaultServerEntry( registries, base );
+                    ServerSearchResult result = new ServerSearchResult( LdapDN.EMPTY_LDAPDN, null, serverEntry, false );
+                    return new SingletonEnumeration<ServerSearchResult>( result );
+                }
+                
+                // return everything
+                if ( containsAsterisk && containsPlus )
+                {
+                    ServerEntry rootDSE = (ServerEntry)getRootDSE( null ).clone();
+                    ServerSearchResult result = new ServerSearchResult( LdapDN.EMPTY_LDAPDN, null, rootDSE, false );
+                    return new SingletonEnumeration<ServerSearchResult>( result );
+                }
+                
+                ServerEntry serverEntry = new DefaultServerEntry( registries, opContext.getDn() );
+                
+                ServerEntry rootDSE = getRootDSE( new GetRootDSEOperationContext( registries ) );
+                
+                for ( EntryAttribute attribute:rootDSE )
+                {
+                    AttributeType type = atRegistry.lookup( attribute.getUpId() );
+                    
+                    if ( realIds.contains( type.getOid() ) )
+                    {
+                        serverEntry.put( attribute );
+                    }
+                    else if ( containsAsterisk && ( type.getUsage() == UsageEnum.USER_APPLICATIONS ) )
+                    {
+                        serverEntry.put( attribute );
+                    }
+                    else if ( containsPlus && ( type.getUsage() != UsageEnum.USER_APPLICATIONS ) )
+                    {
+                        serverEntry.put( attribute );
+                    }
+                }
+
+                ServerSearchResult result = new ServerSearchResult( LdapDN.EMPTY_LDAPDN, null, serverEntry, false );
+                return new SingletonEnumeration<ServerSearchResult>( result );
+            }
+
+            throw new LdapNameNotFoundException();
+        }
+
+        Partition backend = getPartition( base );
+        return backend.search( opContext );
+    }
+
+
+    public ServerEntry lookup( LookupOperationContext opContext ) throws NamingException
+    {
+        LdapDN dn = opContext.getDn();
+        
+        if ( dn.size() == 0 )
+        {
+            ServerEntry retval = new DefaultServerEntry( registries, opContext.getDn() );
+            Set<AttributeType> attributeTypes = rootDSE.getAttributeTypes();
+     
+            if ( opContext.getAttrsId() != null )
+            {
+                for ( AttributeType attributeType:attributeTypes )
+                {
+                    String oid = attributeType.getOid();
+                    
+                    if ( opContext.getAttrsId().contains( oid ) )
+                    {
+                        EntryAttribute attr = rootDSE.get( oid );
+                        retval.put( (ServerAttribute)attr.clone() );
+                    }
+                    
+                }
+            }
+            else
+            {
+                for ( AttributeType attributeType:attributeTypes )
+                {
+                    String id = attributeType.getName();
+                    
+                    EntryAttribute attr = rootDSE.get( id );
+                    retval.put( (ServerAttribute)attr.clone() );
+                }
+            }
+            
+            return retval;
+        }
+
+        Partition backend = getPartition( dn );
+        return backend.lookup( opContext );
+    }
+
+
+    /**
+     * @see Partition#hasEntry(EntryOperationContext)
+     */
+    public boolean hasEntry( EntryOperationContext opContext ) throws NamingException
+    {
+        LdapDN dn = opContext.getDn();
+        
+        if ( IS_DEBUG )
+        {
+            LOG.debug( "Check if DN '" + dn + "' exists." );
+        }
+
+        if ( dn.size() == 0 )
+        {
+            return true;
+        }
+
+        Partition backend = getPartition( dn );
+        return backend.hasEntry( opContext );
+    }
+
+
+    /**
+     * @see Partition#rename(RenameOperationContext)
+     */
+    public void rename( RenameOperationContext opContext ) throws NamingException
+    {
+        Partition backend = getPartition( opContext.getDn() );
+        backend.rename( opContext );
+    }
+
+
+    /**
+     * @see Partition#move(MoveOperationContext)
+     */
+    public void move( MoveOperationContext opContext ) throws NamingException
+    {
+        Partition backend = getPartition( opContext.getDn() );
+        backend.move( opContext );
+    }
+
+
+    public void moveAndRename( MoveAndRenameOperationContext opContext ) throws NamingException
+    {
+        Partition backend = getPartition( opContext.getDn() );
+        backend.moveAndRename( opContext );
+    }
+
+
+    /**
+     * Gets the partition associated with a normalized dn.
+     *
+     * @param dn the normalized distinguished name to resolve to a partition
+     * @return the backend partition associated with the normalized dn
+     * @throws NamingException if the name cannot be resolved to a partition
+     */
+    public Partition getPartition( LdapDN dn ) throws NamingException
+    {
+        Enumeration<String> rdns = dn.getAll();
+        
+        // This is synchronized so that we can't read the
+        // partitionList when it is modified.
+        synchronized ( partitionLookupTree )
+        {
+            Node currentNode = partitionLookupTree;
+
+            // Iterate through all the RDN until we find the associated partition
+            while ( rdns.hasMoreElements() )
+            {
+                String rdn = rdns.nextElement();
+
+                if ( currentNode == null )
+                {
+                    break;
+                }
+
+                if ( currentNode instanceof LeafNode )
+                {
+                    return ( ( LeafNode ) currentNode ).getPartition();
+                }
+
+                BranchNode currentBranch = ( BranchNode ) currentNode;
+                
+                if ( currentBranch.contains( rdn ) )
+                {
+                    currentNode = currentBranch.getChild( rdn );
+                    
+                    if ( currentNode instanceof LeafNode )
+                    {
+                        return ( ( LeafNode ) currentNode ).getPartition();
+                    }
+                }
+            }
+        }
+        
+        throw new LdapNameNotFoundException( dn.getUpName() );
+    }
+
+
+    public void registerSupportedExtensions( Set<String> extensionOids ) throws NamingException
+    {
+        EntryAttribute supportedExtension = rootDSE.get( SchemaConstants.SUPPORTED_EXTENSION_AT );
+
+        if ( supportedExtension == null )
+        {
+            rootDSE.set( SchemaConstants.SUPPORTED_EXTENSION_AT );
+            supportedExtension = rootDSE.get( SchemaConstants.SUPPORTED_EXTENSION_AT );
+        }
+
+        for ( String extensionOid : extensionOids )
+        {
+            supportedExtension.add( extensionOid );
+        }
+    }
+
+
+    public void registerSupportedSaslMechanisms( Set<String> supportedSaslMechanisms ) throws NamingException
+    {
+        EntryAttribute supportedSaslMechanismsAttribute = rootDSE.get( SchemaConstants.SUPPORTED_SASL_MECHANISMS_AT );
+
+        if ( supportedSaslMechanismsAttribute == null )
+        {
+            rootDSE.set( SchemaConstants.SUPPORTED_SASL_MECHANISMS_AT );
+            supportedSaslMechanismsAttribute = rootDSE.get( SchemaConstants.SUPPORTED_SASL_MECHANISMS_AT );
+        }
+
+        for ( String saslMechanism : supportedSaslMechanisms )
+        {
+            supportedSaslMechanismsAttribute.add( saslMechanism );
+        }
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/Partition.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/Partition.java
new file mode 100644
index 0000000..9fbecef
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/Partition.java
@@ -0,0 +1,342 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerSearchResult;
+import org.apache.directory.server.core.interceptor.context.AddOperationContext;
+import org.apache.directory.server.core.interceptor.context.BindOperationContext;
+import org.apache.directory.server.core.interceptor.context.DeleteOperationContext;
+import org.apache.directory.server.core.interceptor.context.EntryOperationContext;
+import org.apache.directory.server.core.interceptor.context.ListOperationContext;
+import org.apache.directory.server.core.interceptor.context.LookupOperationContext;
+import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
+import org.apache.directory.server.core.interceptor.context.MoveAndRenameOperationContext;
+import org.apache.directory.server.core.interceptor.context.MoveOperationContext;
+import org.apache.directory.server.core.interceptor.context.RenameOperationContext;
+import org.apache.directory.server.core.interceptor.context.SearchOperationContext;
+import org.apache.directory.server.core.interceptor.context.UnbindOperationContext;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+import javax.naming.Context;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+
+
+/**
+ * An interfaces that bridges between underlying JNDI entries and JNDI
+ * {@link Context} API.  DIT (Directory Information Tree) consists one or
+ * above {@link Partition}s whose parent is {@link PartitionNexus},
+ * and all of them are mapped to different
+ * base suffix.  Each partition contains entries whose name ends with that
+ * base suffix.
+ *
+ * @org.apache.xbean.XBean
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public interface Partition
+{
+    /** The name of reserved system partition */
+    String SYSTEM_PARTITION_NAME = "system";
+    
+    /** default partition implementation class */
+    String DEFAULT_PARTITION_IMPLEMENTATION = "org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmPartition";
+    
+    /** the default entry cache size to use for a partition */
+    int DEFAULT_CACHE_SIZE = 10000;
+    
+
+    // -----------------------------------------------------------------------
+    // C O N F I G U R A T I O N   M E T H O D S
+    // -----------------------------------------------------------------------
+
+    
+    /**
+     * Gets the unique identifier for this partition.
+     *
+     * @return the unique identifier for this partition
+     */
+    String getId();
+
+
+    /**
+     * Sets the unique identifier for this partition.
+     *
+     * @param id the unique identifier for this partition
+     */
+    void setId( String id );
+
+
+    /**
+     * Gets the root entry of the partition, the entry for the suffix.
+     *
+     * @return the entry for the suffix of this Partition.
+     */
+    ServerEntry getContextEntry();
+
+
+    /**
+     * Sets the root entry of the partition, the entry for the suffix.
+     *
+     * @param contextEntry the entry for the suffix of this Partition.
+     */
+    void setContextEntry( ServerEntry contextEntry );
+
+    
+    /**
+     * Gets the non-normalized suffix for this Partition as a string.
+     *
+     * @return the suffix string for this Partition.
+     */
+    String getSuffix();
+
+
+    /**
+     * Sets the non-normalized suffix for this Partition as a string.
+     *
+     * @param suffix the suffix string for this Partition.
+     */
+    void setSuffix( String suffix );
+
+
+
+    /**
+     * Used to specify the entry cache size for a Partition.  Various Partition
+     * implementations may interpret this value in different ways: i.e. total cache
+     * size limit verses the number of entries to cache.
+     *
+     * @param cacheSize the size of the cache
+     */
+    void setCacheSize( int cacheSize );
+
+
+    /**
+     * Gets the entry cache size for this partition.
+     *
+     * @return the size of the cache
+     */
+    int getCacheSize();
+
+
+    // -----------------------------------------------------------------------
+    // E N D   C O N F I G U R A T I O N   M E T H O D S
+    // -----------------------------------------------------------------------
+
+
+    /**
+     * Initializes this partition.
+     *
+     * @param core the directory core for the server.
+     * @throws NamingException if initialization fails in any way
+     */
+    void init( DirectoryService core ) throws NamingException;
+
+
+    /**
+     * Deinitialized this partition.
+     */
+    void destroy();
+
+
+    /**
+     * Checks to see if this partition is initialized or not.
+     * @return true if the partition is initialized, false otherwise
+     */
+    boolean isInitialized();
+
+
+    /**
+     * Flushes any changes made to this partition now.
+     * @throws NamingException if buffers cannot be flushed to disk
+     */
+    void sync() throws NamingException;
+
+
+    /**
+     * Gets the distinguished/absolute name of the suffix for all entries
+     * stored within this ContextPartition.
+     *
+     * @return Name representing the distinguished/absolute name of this
+     * ContextPartitions root context.
+     * @throws NamingException if access or suffix parsing fails
+     */
+    LdapDN getSuffixDn() throws NamingException;
+
+    /**
+     * Gets the distinguished/absolute name of the suffix for all entries
+     * stored within this ContextPartition.
+     *
+     * @return Name representing the distinguished/absolute name of this
+     * ContextPartitions root context.
+     * @throws NamingException if access or suffix parsing fails
+     */
+    LdapDN getUpSuffixDn() throws NamingException;
+
+    /**
+     * Deletes a leaf entry from this ContextPartition: non-leaf entries cannot be 
+     * deleted until this operation has been applied to their children.
+     *
+     * @param opContext the context of the entry to
+     * delete from this ContextPartition.
+     * @throws NamingException if there are any problems
+     */
+    void delete( DeleteOperationContext opContext ) throws NamingException;
+
+
+    /**
+     * Adds an entry to this ContextPartition.
+     *
+     * @param opContext the context used  to add and entry to this ContextPartition
+     * @throws NamingException if there are any problems
+     */
+    void add( AddOperationContext opContext ) throws NamingException;
+
+
+    /**
+     * Modifies an entry by adding, removing or replacing a set of attributes.
+     *
+     * @param opContext The contetx containin the modification operation 
+     * to perform on the entry which is one of constants specified by the 
+     * DirContext interface:
+     * <code>ADD_ATTRIBUTE, REMOVE_ATTRIBUTE, REPLACE_ATTRIBUTE</code>.
+     * 
+     * @throws NamingException if there are any problems
+     * @see javax.naming.directory.DirContext
+     * @see javax.naming.directory.DirContext#ADD_ATTRIBUTE
+     * @see javax.naming.directory.DirContext#REMOVE_ATTRIBUTE
+     * @see javax.naming.directory.DirContext#REPLACE_ATTRIBUTE
+     */
+    void modify( ModifyOperationContext opContext ) throws NamingException;
+
+
+    /**
+     * A specialized form of one level search used to return a minimal set of 
+     * information regarding child entries under a base.  Convenience method
+     * used to optimize operations rather than conducting a full search with 
+     * retrieval.
+     *
+     * @param opContext the context containing the distinguished/absolute name for the search/listing
+     * @return a NamingEnumeration containing objects of type {@link SearchResult}
+     * @throws NamingException if there are any problems
+     */
+    NamingEnumeration<ServerSearchResult> list( ListOperationContext opContext ) throws NamingException;
+
+
+    /**
+     * Conducts a search against this ContextPartition.  Namespace specific
+     * parameters for search are contained within the environment using
+     * namespace specific keys into the hash.  For example in the LDAP namespace
+     * a ContextPartition implementation may look for search Controls using a
+     * namespace specific or implementation specific key for the set of LDAP
+     * Controls.
+     *
+     * @param opContext The context containing the information used by the operation
+     * @throws NamingException if there are any problems
+     * @return a NamingEnumeration containing objects of type 
+     * <a href="http://java.sun.com/j2se/1.4.2/docs/api/
+     * javax/naming/directory/SearchResult.html">SearchResult</a>.
+     */
+    NamingEnumeration<ServerSearchResult> search( SearchOperationContext opContext )
+        throws NamingException;
+
+
+    /**
+     * Looks up an entry by distinguished/absolute name.  This is a simplified
+     * version of the search operation used to point read an entry used for
+     * convenience.
+     * 
+     * Depending on the context parameters, we my look for a simple entry,
+     * or for a restricted set of attributes for this entry
+     *
+     * @param lookupContext The context containing the parameters
+     * @return an Attributes object representing the entry
+     * @throws NamingException if there are any problems
+     */
+    ServerEntry lookup( LookupOperationContext lookupContext ) throws NamingException;
+
+    /**
+     * Fast operation to check and see if a particular entry exists.
+     *
+     * @param opContext The context used to pass informations
+     * @return true if the entry exists, false if it does not
+     * @throws NamingException if there are any problems
+     */
+    boolean hasEntry( EntryOperationContext opContext ) throws NamingException;
+
+    /**
+     * Modifies an entry by changing its relative name. Optionally attributes
+     * associated with the old relative name can be removed from the entry.
+     * This makes sense only in certain namespaces like LDAP and will be ignored
+     * if it is irrelavent.
+     *
+     * @param opContext the modify DN context
+     * @throws NamingException if there are any problems
+     */
+    void rename( RenameOperationContext opContext ) throws NamingException;
+
+
+    /**
+     * Transplants a child entry, to a position in the namespace under a new
+     * parent entry.
+     *
+     * @param opContext The context containing the DNs to move
+     * @throws NamingException if there are any problems
+     */
+    void move( MoveOperationContext opContext ) throws NamingException;
+
+
+    /**
+     * Transplants a child entry, to a position in the namespace under a new
+     * parent entry and changes the RN of the child entry which can optionally
+     * have its old RN attributes removed.  The removal of old RN attributes
+     * may not make sense in all namespaces.  If the concept is undefined in a
+     * namespace this parameters is ignored.  An example of a namespace where
+     * this parameter is significant is the LDAP namespace.
+     *
+     * @param opContext The context contain all the information about
+     * the modifyDN operation
+     * @throws NamingException if there are any problems
+     */
+    void moveAndRename( MoveAndRenameOperationContext opContext ) throws NamingException;
+
+
+    /**
+     * Represents a bind operation issued to authenticate a client.  Partitions
+     * need not support this operation.  This operation is here to enable those
+     * interested in implementing virtual directories with ApacheDS.
+     * 
+     * @param opContext the bind context, containing all the needed informations to bind
+     * @throws NamingException if something goes wrong
+     */
+    void bind( BindOperationContext opContext ) throws NamingException;
+
+    /**
+     * Represents an unbind operation issued by an authenticated client.  Partitions
+     * need not support this operation.  This operation is here to enable those
+     * interested in implementing virtual directories with ApacheDS.
+     * 
+     * @param opContext the context used to unbind
+     * @throws NamingException if something goes wrong
+     */
+    void unbind( UnbindOperationContext opContext ) throws NamingException;
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/PartitionNexus.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/PartitionNexus.java
new file mode 100755
index 0000000..1039e75
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/PartitionNexus.java
@@ -0,0 +1,253 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition;
+
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import javax.naming.InvalidNameException;
+import javax.naming.NamingException;
+import javax.naming.ldap.LdapContext;
+
+import org.apache.directory.server.constants.ServerDNConstants;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.interceptor.context.AddContextPartitionOperationContext;
+import org.apache.directory.server.core.interceptor.context.CompareOperationContext;
+import org.apache.directory.server.core.interceptor.context.GetMatchedNameOperationContext;
+import org.apache.directory.server.core.interceptor.context.GetRootDSEOperationContext;
+import org.apache.directory.server.core.interceptor.context.GetSuffixOperationContext;
+import org.apache.directory.server.core.interceptor.context.ListSuffixOperationContext;
+import org.apache.directory.server.core.interceptor.context.RemoveContextPartitionOperationContext;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.schema.NoOpNormalizer;
+import org.apache.directory.shared.ldap.schema.OidNormalizer;
+import org.apache.directory.shared.ldap.util.StringTools;
+
+
+/**
+ * A root {@link Partition} that contains all other partitions, and
+ * routes all operations to the child partition that matches to its base suffixes.
+ * It also provides some extended operations such as accessing rootDSE and
+ * listing base suffixes.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public abstract class PartitionNexus implements Partition
+{
+    /** the admin super user uid */
+    public static final String ADMIN_UID = "admin";
+    
+    /** the initial admin passwd set on startup */
+    public static final String ADMIN_PASSWORD_STRING = "secret";
+    public static final byte[] ADMIN_PASSWORD_BYTES = StringTools.getBytesUtf8( ADMIN_PASSWORD_STRING );
+    
+   
+    /**
+     * Gets the DN for the admin user.
+     * 
+     * @return the admin user DN
+     */
+    public static final LdapDN getAdminName()
+    {
+        LdapDN adminDn = null;
+
+        try
+        {
+            adminDn = new LdapDN( ServerDNConstants.ADMIN_SYSTEM_DN );
+        }
+        catch ( NamingException e )
+        {
+            throw new InternalError();
+        }
+        
+        try
+        {
+            Map<String, OidNormalizer> oidsMap = new HashMap<String, OidNormalizer>();
+            
+            oidsMap.put( SchemaConstants.UID_AT, new OidNormalizer( SchemaConstants.UID_AT_OID, new NoOpNormalizer() ) );
+            oidsMap.put( SchemaConstants.USER_ID_AT, new OidNormalizer( SchemaConstants.UID_AT_OID, new NoOpNormalizer() ) );
+            oidsMap.put( SchemaConstants.UID_AT_OID, new OidNormalizer( SchemaConstants.UID_AT_OID, new NoOpNormalizer() ) );
+            
+            oidsMap.put( SchemaConstants.OU_AT, new OidNormalizer( SchemaConstants.OU_AT_OID, new NoOpNormalizer()  ) );
+            oidsMap.put( SchemaConstants.ORGANIZATIONAL_UNIT_NAME_AT, new OidNormalizer( SchemaConstants.OU_AT_OID, new NoOpNormalizer()  ) );
+            oidsMap.put( SchemaConstants.OU_AT_OID, new OidNormalizer( SchemaConstants.OU_AT_OID, new NoOpNormalizer()  ) );
+
+            adminDn.normalize( oidsMap );
+        }
+        catch ( InvalidNameException ine )
+        {
+            // Nothing we can do ...
+        }
+        catch ( NamingException ne )
+        {
+            // Nothing we can do ...
+        }
+
+        return adminDn;
+    }
+
+
+    /**
+     * Gets the DN for the base entry under which all groups reside.
+     * A new Name instance is created and returned every time.
+     * @return the groups base DN
+     */
+    public static final LdapDN getGroupsBaseName()
+    {
+        LdapDN groupsBaseDn = null;
+
+        try
+        {
+            groupsBaseDn = new LdapDN( ServerDNConstants.GROUPS_SYSTEM_DN );
+        }
+        catch ( NamingException e )
+        {
+            throw new InternalError();
+        }
+
+        return groupsBaseDn;
+    }
+
+
+    /**
+     * Gets the DN for the base entry under which all non-admin users reside.
+     * A new Name instance is created and returned every time.
+     * @return the users base DN
+     */
+    public static final LdapDN getUsersBaseName()
+    {
+        LdapDN usersBaseDn = null;
+
+        try
+        {
+            usersBaseDn = new LdapDN( ServerDNConstants.USERS_SYSTEM_DN );
+        }
+        catch ( NamingException e )
+        {
+            throw new InternalError();
+        }
+
+        return usersBaseDn;
+    }
+
+
+    /**
+     * Gets the LdapContext associated with the calling thread.
+     * 
+     * @return The LdapContext associated with the thread of execution or null
+     * if no context is associated with the calling thread.
+     */
+    public abstract LdapContext getLdapContext();
+
+
+    /**
+     * Get's the RootDSE entry for the DSA.
+     *
+     * @return the attributes of the RootDSE
+     */
+    public abstract ServerEntry getRootDSE( GetRootDSEOperationContext opContext ) throws NamingException;
+
+
+    /**
+     * Performs a comparison check to see if an attribute of an entry has
+     * a specified value.
+     *
+     * @param compareContext the context used to compare
+     * @return true if the entry contains an attribute with the value, false otherwise
+     * @throws NamingException if there is a problem accessing the entry and its values
+     */
+    public abstract boolean compare( CompareOperationContext compareContext ) throws NamingException;
+
+
+    public abstract void addContextPartition( AddContextPartitionOperationContext opContext ) throws NamingException;
+
+
+    public abstract void removeContextPartition( RemoveContextPartitionOperationContext opContext ) throws NamingException;
+
+
+    public abstract Partition getSystemPartition();
+
+
+    /**
+     * Get's the partition corresponding to a distinguished name.  This 
+     * name need not be the name of the partition suffix.  When used in 
+     * conjunction with get suffix this can properly find the partition 
+     * associated with the DN.  Make sure to use the normalized DN.
+     * 
+     * @param dn the normalized distinguished name to get a partition for
+     * @return the partition containing the entry represented by the dn
+     * @throws NamingException if there is no partition for the dn
+     */
+    public abstract Partition getPartition( LdapDN dn ) throws NamingException;
+
+
+    /**
+     * Gets the most significant Dn that exists within the server for any Dn.
+     *
+     * @param getMatchedNameContext the context containing the  distinguished name 
+     * to use for matching.
+     * @return a distinguished name representing the matching portion of dn,
+     * as originally provided by the user on creation of the matched entry or 
+     * the empty string distinguished name if no match was found.
+     * @throws NamingException if there are any problems
+     */
+    public abstract LdapDN getMatchedName ( GetMatchedNameOperationContext getMatchedNameContext ) throws NamingException;
+
+
+    /**
+     * Gets the distinguished name of the suffix that would hold an entry with
+     * the supplied distinguished name parameter.  If the DN argument does not
+     * fall under a partition suffix then the empty string Dn is returned.
+     *
+     * @param suffixContext the Context containing normalized distinguished
+     * name to use for finding a suffix.
+     * @return the suffix portion of dn, or the valid empty string Dn if no
+     * naming context was found for dn.
+     * @throws NamingException if there are any problems
+     */
+    public abstract LdapDN getSuffix ( GetSuffixOperationContext suffixContext ) throws NamingException;
+
+
+    /**
+     * Gets an iteration over the Name suffixes of the partitions managed by this
+     * {@link PartitionNexus}.
+     *
+     * @return Iteration over ContextPartition suffix names as Names.
+     * @throws NamingException if there are any problems
+     */
+    public abstract Iterator<String> listSuffixes( ListSuffixOperationContext opContext ) throws NamingException;
+
+
+    /**
+     * Adds a set of supportedExtension (OID Strings) to the RootDSE.
+     * 
+     * @param extensionOids a set of OID strings to add to the supportedExtension 
+     * attribute in the RootDSE
+     */
+    public abstract void registerSupportedExtensions( Set<String> extensionOids ) throws NamingException;
+
+
+    public abstract void registerSupportedSaslMechanisms( Set<String> strings ) throws NamingException;
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/PartitionNexusProxy.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/PartitionNexusProxy.java
new file mode 100644
index 0000000..e63316b
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/PartitionNexusProxy.java
@@ -0,0 +1,903 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition;
+
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import javax.naming.Context;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.ServiceUnavailableException;
+import javax.naming.directory.SearchControls;
+import javax.naming.event.EventContext;
+import javax.naming.event.NamingListener;
+import javax.naming.ldap.LdapContext;
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.authn.AuthenticationInterceptor;
+import org.apache.directory.server.core.authz.AciAuthorizationInterceptor;
+import org.apache.directory.server.core.authz.DefaultAuthorizationInterceptor;
+import org.apache.directory.server.core.changelog.ChangeLogInterceptor;
+import org.apache.directory.server.core.collective.CollectiveAttributeInterceptor;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerSearchResult;
+import org.apache.directory.server.core.enumeration.SearchResultFilter;
+import org.apache.directory.server.core.enumeration.SearchResultFilteringEnumeration;
+import org.apache.directory.server.core.event.EventInterceptor;
+import org.apache.directory.server.core.exception.ExceptionInterceptor;
+import org.apache.directory.server.core.interceptor.InterceptorChain;
+import org.apache.directory.server.core.interceptor.context.AddContextPartitionOperationContext;
+import org.apache.directory.server.core.interceptor.context.AddOperationContext;
+import org.apache.directory.server.core.interceptor.context.BindOperationContext;
+import org.apache.directory.server.core.interceptor.context.CompareOperationContext;
+import org.apache.directory.server.core.interceptor.context.DeleteOperationContext;
+import org.apache.directory.server.core.interceptor.context.EntryOperationContext;
+import org.apache.directory.server.core.interceptor.context.GetMatchedNameOperationContext;
+import org.apache.directory.server.core.interceptor.context.GetRootDSEOperationContext;
+import org.apache.directory.server.core.interceptor.context.GetSuffixOperationContext;
+import org.apache.directory.server.core.interceptor.context.ListOperationContext;
+import org.apache.directory.server.core.interceptor.context.ListSuffixOperationContext;
+import org.apache.directory.server.core.interceptor.context.LookupOperationContext;
+import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
+import org.apache.directory.server.core.interceptor.context.MoveAndRenameOperationContext;
+import org.apache.directory.server.core.interceptor.context.MoveOperationContext;
+import org.apache.directory.server.core.interceptor.context.RemoveContextPartitionOperationContext;
+import org.apache.directory.server.core.interceptor.context.RenameOperationContext;
+import org.apache.directory.server.core.interceptor.context.SearchOperationContext;
+import org.apache.directory.server.core.interceptor.context.UnbindOperationContext;
+import org.apache.directory.server.core.invocation.Invocation;
+import org.apache.directory.server.core.invocation.InvocationStack;
+import org.apache.directory.server.core.normalization.NormalizationInterceptor;
+import org.apache.directory.server.core.operational.OperationalAttributeInterceptor;
+import org.apache.directory.server.core.referral.ReferralInterceptor;
+import org.apache.directory.server.core.schema.SchemaInterceptor;
+import org.apache.directory.server.core.subtree.SubentryInterceptor;
+import org.apache.directory.server.core.trigger.TriggerInterceptor;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.exception.LdapSizeLimitExceededException;
+import org.apache.directory.shared.ldap.exception.LdapTimeLimitExceededException;
+import org.apache.directory.shared.ldap.filter.ExprNode;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+/**
+ * A decorator that wraps other {@link PartitionNexus} to enable
+ * {@link InterceptorChain} and {@link InvocationStack} support.
+ * All {@link Invocation}s made to this nexus is automatically pushed to
+ * {@link InvocationStack} of the current thread, and popped when
+ * the operation ends.  All invocations are filtered by {@link InterceptorChain}.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class PartitionNexusProxy extends PartitionNexus
+{
+    /**
+     * safe to use set of bypass instructions to lookup raw entries
+     */
+    public static final Collection<String> LOOKUP_BYPASS;
+
+    /**
+     * safe to use set of bypass instructions to getMatchedDn
+     */
+    public static final Collection<String> GETMATCHEDDN_BYPASS;
+
+    /**
+     * safe to use set of bypass instructions to lookup raw entries excluding operational attributes
+     */
+    public static final Collection<String> LOOKUP_EXCLUDING_OPR_ATTRS_BYPASS;
+    
+    public static final Collection<String> GET_ROOT_DSE_BYPASS;
+
+    /**
+     * Bypass String to use when ALL interceptors should be skipped
+     */
+    public static final String BYPASS_ALL = "*";
+
+    /**
+     * Bypass String to use when ALL interceptors should be skipped
+     */
+    public static final Collection<String> BYPASS_ALL_COLLECTION = Collections.singleton( BYPASS_ALL );
+
+    /**
+     * A static object to store the rootDSE entry with all the attributes
+     */
+    private static ServerEntry ROOT_DSE_ALL;
+
+    /**
+     * A static object to store the rootDSE entry without operationnal attributes
+     */
+    private static ServerEntry ROOT_DSE_NO_OPERATIONNAL;
+
+    /**
+     * A mutex to protect the rootDSE construction
+     */
+    private static final Object ROOT_DSE_ALL_MUTEX = new Object();
+
+    /**
+     * A mutex to protect the rootDSE construction
+     */
+    private static final Object ROOT_DSE_NOOP_MUTEX = new Object();
+
+    private final Context caller;
+    private final DirectoryService service;
+
+    static
+    {
+        Collection<String> c = new HashSet<String>();
+        c.add( NormalizationInterceptor.class.getName() );
+        c.add( AuthenticationInterceptor.class.getName() );
+        c.add( ReferralInterceptor.class.getName() );
+        c.add( AciAuthorizationInterceptor.class.getName() );
+        c.add( DefaultAuthorizationInterceptor.class.getName() );
+//        c.add( ExceptionInterceptor.class.getName() );
+        c.add( OperationalAttributeInterceptor.class.getName() );
+        c.add( SchemaInterceptor.class.getName() );
+        c.add( SubentryInterceptor.class.getName() );
+//        c.add( CollectiveAttributeInterceptor.class.getName() );
+        c.add( EventInterceptor.class.getName() );
+//        c.add( TriggerInterceptor.class.getName() );
+        LOOKUP_BYPASS = Collections.unmodifiableCollection( c );
+
+        c = new HashSet<String>();
+//        c.add( NormalizationInterceptor.class.getName() );
+        c.add( AuthenticationInterceptor.class.getName() );
+        c.add( ReferralInterceptor.class.getName() );
+        c.add( AciAuthorizationInterceptor.class.getName() );
+        c.add( DefaultAuthorizationInterceptor.class.getName() );
+//        c.add( ExceptionInterceptor.class.getName() );
+        c.add( SchemaInterceptor.class.getName() );
+        c.add( OperationalAttributeInterceptor.class.getName() );
+        c.add( SubentryInterceptor.class.getName() );
+//        c.add( CollectiveAttributeInterceptor.class.getName() );
+        c.add( EventInterceptor.class.getName() );
+//        c.add( TriggerInterceptor.class.getName() );
+        GETMATCHEDDN_BYPASS = Collections.unmodifiableCollection( c );
+
+        c = new HashSet<String>();
+        c.add( NormalizationInterceptor.class.getName() );
+        c.add( AuthenticationInterceptor.class.getName() );
+        c.add( ReferralInterceptor.class.getName() );
+        c.add( AciAuthorizationInterceptor.class.getName() );
+        c.add( DefaultAuthorizationInterceptor.class.getName() );
+//        c.add( ExceptionInterceptor.class.getName() );
+//        c.add( OperationalAttributeInterceptor.class.getName() );
+        c.add( SchemaInterceptor.class.getName() );
+        c.add( SubentryInterceptor.class.getName() );
+//        c.add( CollectiveAttributeInterceptor.class.getName() );
+        c.add( EventInterceptor.class.getName() );
+        c.add( TriggerInterceptor.class.getName() );
+        LOOKUP_EXCLUDING_OPR_ATTRS_BYPASS = Collections.unmodifiableCollection( c );
+        
+        
+        c = new HashSet<String>();
+        c.add( NormalizationInterceptor.class.getName() );
+        //c.add( AuthenticationInterceptor.class.getName() );
+        c.add( ChangeLogInterceptor.class.getName() );
+        c.add( ReferralInterceptor.class.getName() );
+        c.add( AciAuthorizationInterceptor.class.getName() );
+        c.add( DefaultAuthorizationInterceptor.class.getName() );
+        c.add( ExceptionInterceptor.class.getName() );
+        c.add( OperationalAttributeInterceptor.class.getName() );
+        c.add( SchemaInterceptor.class.getName() );
+        c.add( SubentryInterceptor.class.getName() );
+        c.add( CollectiveAttributeInterceptor.class.getName() );
+        c.add( EventInterceptor.class.getName() );
+        c.add( TriggerInterceptor.class.getName() );
+        GET_ROOT_DSE_BYPASS = Collections.unmodifiableCollection( c );
+
+    }
+
+
+    /**
+     * Creates a new instance.
+     *
+     * @param caller  a JNDI {@link Context} object that will call this proxy
+     * @param service a JNDI service
+     */
+    public PartitionNexusProxy( Context caller, DirectoryService service ) throws NamingException
+    {
+        this.caller = caller;
+        this.service = service;
+    }
+
+
+    public LdapContext getLdapContext()
+    {
+        return service.getPartitionNexus().getLdapContext();
+    }
+
+
+    public String getId()
+    {
+        throw new UnsupportedOperationException( "Nexus partition proxy objects do not have an Id." );
+    }
+
+
+    public void setId( String id )
+    {
+        throw new UnsupportedOperationException( "Not supported by PartitionNexusProxy" );
+    }
+
+
+    public ServerEntry getContextEntry()
+    {
+        throw new UnsupportedOperationException( "Not supported by PartitionNexusProxy" );
+    }
+
+
+    public void setContextEntry( ServerEntry contextEntry )
+    {
+        throw new UnsupportedOperationException( "Not supported by PartitionNexusProxy" );
+    }
+
+
+    public String getSuffix()
+    {
+        throw new UnsupportedOperationException( "Not supported by PartitionNexusProxy" );
+    }
+
+
+    public void setSuffix( String suffix )
+    {
+        throw new UnsupportedOperationException( "Not supported by PartitionNexusProxy" );
+    }
+
+
+    public void setCacheSize( int cacheSize )
+    {
+        throw new UnsupportedOperationException( "Not supported by PartitionNexusProxy" );
+    }
+
+
+    public int getCacheSize()
+    {
+        throw new UnsupportedOperationException( "Not supported by PartitionNexusProxy" );
+    }
+
+
+    public void init( DirectoryService core ) throws NamingException
+    {
+    }
+
+
+    public void destroy()
+    {
+    }
+
+
+    public Partition getSystemPartition()
+    {
+        return service.getPartitionNexus().getSystemPartition();
+    }
+
+
+    public Partition getPartition( LdapDN dn ) throws NamingException
+    {
+        return service.getPartitionNexus().getPartition( dn );
+    }
+
+
+    public LdapDN getSuffixDn() throws NamingException
+    {
+        return service.getPartitionNexus().getSuffixDn();
+    }
+
+    public LdapDN getUpSuffixDn() throws NamingException
+    {
+        return service.getPartitionNexus().getUpSuffixDn();
+    }
+
+
+    public void sync() throws NamingException
+    {
+        this.service.sync();
+    }
+
+
+    public void close() throws NamingException
+    {
+        this.service.shutdown();
+    }
+
+
+    public boolean isInitialized()
+    {
+        return this.service.isStarted();
+    }
+
+
+    public LdapDN getMatchedName( GetMatchedNameOperationContext opContext ) throws NamingException
+    {
+        return getMatchedName( opContext, null );
+    }
+
+
+    public LdapDN getMatchedName( GetMatchedNameOperationContext opContext, Collection<String> bypass ) throws NamingException
+    {
+        ensureStarted();
+        opContext.push( new Invocation( this, caller, "getMatchedName", bypass ) );
+        
+        try
+        {
+            return service.getInterceptorChain().getMatchedName( opContext );
+        }
+        finally
+        {
+            opContext.pop();
+        }
+    }
+
+
+    public LdapDN getSuffix( GetSuffixOperationContext opContext ) throws NamingException
+    {
+        return getSuffix( opContext, null );
+    }
+
+
+    public LdapDN getSuffix( GetSuffixOperationContext opContext, Collection<String> bypass ) throws NamingException
+    {
+        ensureStarted();
+        opContext.push( new Invocation( this, caller, "getSuffixDn", bypass ) );
+        
+        try
+        {
+            return service.getInterceptorChain().getSuffix( opContext );
+        }
+        finally
+        {
+            opContext.pop();
+        }
+    }
+
+
+    public Iterator<String> listSuffixes( ListSuffixOperationContext opContext ) throws NamingException
+    {
+        return listSuffixes( opContext, null );
+    }
+
+
+    public Iterator<String> listSuffixes( ListSuffixOperationContext opContext, Collection<String> bypass ) throws NamingException
+    {
+        ensureStarted();
+        opContext.push( new Invocation( this, caller, "listSuffices", bypass ) );
+        
+        try
+        {
+            return service.getInterceptorChain().listSuffixes( opContext );
+        }
+        finally
+        {
+            opContext.pop();
+        }
+    }
+
+
+    public boolean compare( CompareOperationContext opContext ) throws NamingException
+    {
+        return compare( opContext, null );
+    }
+
+
+    public boolean compare( CompareOperationContext opContext, Collection<String> bypass ) throws NamingException
+    {
+        ensureStarted();
+        opContext.push( new Invocation( this, caller, "compare", bypass ) );
+        
+        try
+        {
+            return service.getInterceptorChain().compare( opContext );
+        }
+        finally
+        {
+            opContext.pop();
+        }
+    }
+
+
+    public void delete( DeleteOperationContext opContext ) throws NamingException
+    {
+        delete( opContext, null );
+    }
+
+
+    public void delete( DeleteOperationContext opContext, Collection<String> bypass ) throws NamingException
+    {
+        ensureStarted();
+        opContext.push( new Invocation( this, caller, "delete", bypass ) );
+        
+        try
+        {
+            service.getInterceptorChain().delete( opContext );
+        }
+        finally
+        {
+            opContext.pop();
+        }
+    }
+
+
+    public void add( AddOperationContext opContext ) throws NamingException
+    {
+        add( opContext, null );
+    }
+
+
+    public void add( AddOperationContext opContext, Collection<String> bypass ) throws NamingException
+    {
+        ensureStarted();
+        opContext.push( new Invocation( this, caller, "add", bypass ) );
+        
+        try
+        {
+            service.getInterceptorChain().add( opContext );
+        }
+        finally
+        {
+            opContext.pop();
+        }
+    }
+
+
+    public void modify( ModifyOperationContext opContext ) throws NamingException
+    {
+        modify( opContext, null );
+    }
+
+
+    public void modify( ModifyOperationContext opContext, Collection<String> bypass ) throws NamingException
+    {
+        ensureStarted();
+        opContext.push( new Invocation( this, caller, "modify", bypass ) );
+        
+        try
+        {
+            service.getInterceptorChain().modify( opContext );
+        }
+        finally
+        {
+            opContext.pop();
+        }
+    }
+
+
+    public NamingEnumeration<ServerSearchResult> list( ListOperationContext opContext ) throws NamingException
+    {
+        return list( opContext, null );
+    }
+
+
+    public NamingEnumeration<ServerSearchResult> list( ListOperationContext opContext, Collection<String> bypass )
+            throws NamingException
+    {
+        ensureStarted();
+        opContext.push( new Invocation( this, caller, "list", bypass ) );
+        
+        try
+        {
+            return service.getInterceptorChain().list( opContext );
+        }
+        finally
+        {
+            opContext.pop();
+        }
+    }
+
+
+    public NamingEnumeration<ServerSearchResult> search( SearchOperationContext opContext )
+            throws NamingException
+    {
+        NamingEnumeration<ServerSearchResult> ne = search( opContext, null );
+
+        if ( ne instanceof SearchResultFilteringEnumeration )
+        {
+            SearchResultFilteringEnumeration results = ( SearchResultFilteringEnumeration ) ne;
+            SearchControls searchCtls = opContext.getSearchControls();
+
+            if ( searchCtls.getTimeLimit() + searchCtls.getCountLimit() > 0 )
+            {
+                // this will be the last filter added so other filters before it must
+                // have passed/approved of the entry to be returned back to the client
+                // so the candidate we have is going to be returned for sure
+                results.addResultFilter( new SearchResultFilter()
+                {
+                    final long startTime = System.currentTimeMillis();
+                    int count = 1; // with prefetch we've missed one which is ok since 1 is the minimum
+
+
+                    public boolean accept( Invocation invocation, ServerSearchResult result, SearchControls controls )
+                            throws NamingException
+                    {
+                        if ( controls.getTimeLimit() > 0 )
+                        {
+                            long runtime = System.currentTimeMillis() - startTime;
+                            if ( runtime > controls.getTimeLimit() )
+                            {
+                                throw new LdapTimeLimitExceededException();
+                            }
+                        }
+
+                        if ( controls.getCountLimit() > 0 )
+                        {
+                            if ( count > controls.getCountLimit() )
+                            {
+                                throw new LdapSizeLimitExceededException();
+                            }
+                        }
+
+                        count++;
+                        return true;
+                    }
+                } );
+            }
+        }
+
+        return ne;
+    }
+
+
+    public NamingEnumeration<ServerSearchResult> search( SearchOperationContext opContext, Collection<String> bypass )
+            throws NamingException
+    {
+        ensureStarted();
+        opContext.push( new Invocation( this, caller, "search", bypass ) );
+        
+        try
+        {
+            return service.getInterceptorChain().search( opContext );
+        }
+        finally
+        {
+            opContext.pop();
+        }
+    }
+
+
+    public ServerEntry lookup( LookupOperationContext opContext ) throws NamingException
+    {
+        if ( opContext.getDn().size() == 0 )
+        {
+            List<String> attrs = opContext.getAttrsId();
+
+            if ( ( attrs == null ) || ( attrs.size() == 0 ) )
+            {
+                synchronized ( ROOT_DSE_NOOP_MUTEX )
+                {
+                    if ( ROOT_DSE_NO_OPERATIONNAL == null )
+                    {
+                        ROOT_DSE_NO_OPERATIONNAL = lookup( opContext, null );
+                    }
+                }
+
+                return ROOT_DSE_NO_OPERATIONNAL;
+            } 
+            else if ( ( attrs.size() == 1 ) && ( attrs.contains( SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES ) ) )
+            {
+                synchronized ( ROOT_DSE_ALL_MUTEX )
+                {
+                    if ( ROOT_DSE_ALL == null )
+                    {
+                        ROOT_DSE_ALL = lookup( opContext, null );
+                    }
+                }
+
+                return ROOT_DSE_ALL;
+            }
+
+        }
+
+        return lookup( opContext, null );
+    }
+
+
+    public ServerEntry lookup( LookupOperationContext opContext, Collection<String> bypass ) throws NamingException
+    {
+        ensureStarted();
+        opContext.push( new Invocation( this, caller, "lookup", bypass ) );
+        
+        try
+        {
+            return service.getInterceptorChain().lookup( opContext );
+        }
+        finally
+        {
+            opContext.pop();
+        }
+    }
+
+    public boolean hasEntry( EntryOperationContext opContext ) throws NamingException
+    {
+        return hasEntry( opContext, null );
+    }
+
+
+    public boolean hasEntry( EntryOperationContext opContext, Collection<String> bypass ) throws NamingException
+    {
+        ensureStarted();
+        opContext.push( new Invocation( this, caller, "hasEntry", bypass ) );
+        
+        try
+        {
+            return service.getInterceptorChain().hasEntry( opContext );
+        }
+        finally
+        {
+            opContext.pop();
+        }
+    }
+
+
+    public void rename( RenameOperationContext opContext ) throws NamingException
+    {
+        rename( opContext, null );
+    }
+
+
+    public void rename( RenameOperationContext opContext, Collection<String> bypass ) throws NamingException
+    {
+        ensureStarted();
+        opContext.push( new Invocation( this, caller, "rename", bypass ) );
+        
+        try
+        {
+            service.getInterceptorChain().rename( opContext );
+        }
+        finally
+        {
+            opContext.pop();
+        }
+    }
+
+
+    public void move( MoveOperationContext opContext ) throws NamingException
+    {
+        move( opContext, null );
+    }
+
+
+    public void move( MoveOperationContext opContext, Collection<String> bypass ) throws NamingException
+    {
+        ensureStarted();
+        opContext.push( new Invocation( this, caller, "move", bypass ) );
+        
+        try
+        {
+            service.getInterceptorChain().move( opContext );
+        }
+        finally
+        {
+            opContext.pop();
+        }
+    }
+
+
+    public void moveAndRename( MoveAndRenameOperationContext opContext ) throws NamingException
+    {
+        moveAndRename( opContext, null );
+    }
+
+
+    public void moveAndRename( MoveAndRenameOperationContext opContext, Collection<String> bypass )
+            throws NamingException
+    {
+        ensureStarted();
+        opContext.push( new Invocation( this, caller, "moveAndRename", bypass ) );
+        
+        try
+        {
+            service.getInterceptorChain().moveAndRename( opContext );
+        }
+        finally
+        {
+            opContext.pop();
+        }
+    }
+
+    /**
+     * TODO : check if we can find another way to procect ourselves from recursion.
+     *
+     * @param opContext The operation context
+     * @param bypass bypass instructions to skip interceptors
+     * @throws NamingException if bind fails
+     */
+    public void bind( BindOperationContext opContext, Collection<String> bypass )
+            throws NamingException
+    {
+        ensureStarted();
+        opContext.push( new Invocation( this, caller, "bind", bypass ) );
+        
+        try
+        {
+            service.getInterceptorChain().bind( opContext );
+        }
+        finally
+        {
+            opContext.pop();
+        }
+    }
+
+
+    public void unbind( UnbindOperationContext opContext, Collection<String> bypass ) throws NamingException
+    {
+        ensureStarted();
+        opContext.push( new Invocation( this, caller, "unbind", bypass ) );
+        
+        try
+        {
+            service.getInterceptorChain().unbind( opContext );
+        }
+        finally
+        {
+            opContext.pop();
+        }
+    }
+
+
+    public void bind( BindOperationContext opContext ) throws NamingException
+    {
+        bind( opContext, null );
+    }
+
+
+    public void unbind( UnbindOperationContext opContext ) throws NamingException
+    {
+        unbind( opContext, null );
+    }
+
+
+    public ServerEntry getRootDSE( GetRootDSEOperationContext opContext ) throws NamingException
+    {
+        if ( opContext.getDn().size() == 0 )
+        {
+            synchronized ( ROOT_DSE_ALL_MUTEX )
+            {
+                if ( ROOT_DSE_ALL == null )
+                {
+                    ROOT_DSE_ALL = getRootDSE( opContext, null );
+                }
+            }
+
+            return ROOT_DSE_ALL;
+        }
+
+        return getRootDSE( opContext, null );
+    }
+
+
+    public ServerEntry getRootDSE( GetRootDSEOperationContext opContext, Collection<String> bypass )
+            throws NamingException
+    {
+        ensureStarted();
+        opContext.push( new Invocation( this, caller, "getRootDSE", GET_ROOT_DSE_BYPASS ) );
+        
+        try
+        {
+            return service.getInterceptorChain().getRootDSE( opContext );
+        }
+        finally
+        {
+            opContext.pop();
+        }
+    }
+
+
+    public void addContextPartition( AddContextPartitionOperationContext opContext ) throws NamingException
+    {
+        addContextPartition( opContext, null );
+    }
+
+
+    public void addContextPartition( AddContextPartitionOperationContext opContext, Collection<String> bypass )
+            throws NamingException
+    {
+        ensureStarted();
+        opContext.push( new Invocation( this, caller, "addContextPartition", bypass ) );
+        
+        try
+        {
+            service.getInterceptorChain().addContextPartition( opContext );
+        }
+        finally
+        {
+            opContext.pop();
+        }
+    }
+
+
+    public void removeContextPartition( RemoveContextPartitionOperationContext opContext ) throws NamingException
+    {
+        removeContextPartition( opContext, null );
+    }
+
+
+    public void removeContextPartition( RemoveContextPartitionOperationContext opContext, Collection<String> bypass )
+            throws NamingException
+    {
+        ensureStarted();
+        opContext.push( new Invocation( this, caller, "removeContextPartition", bypass ) );
+        
+        try
+        {
+            service.getInterceptorChain().removeContextPartition( opContext );
+        }
+        finally
+        {
+            opContext.pop();
+        }
+    }
+
+
+    private void ensureStarted() throws ServiceUnavailableException
+    {
+        if ( !service.isStarted() )
+        {
+            throw new ServiceUnavailableException( "Directory service is not started." );
+        }
+    }
+
+
+    public void registerSupportedExtensions( Set<String> extensionOids ) throws NamingException
+    {
+        service.getPartitionNexus().registerSupportedExtensions( extensionOids );
+    }
+
+
+    public void registerSupportedSaslMechanisms( Set<String> supportedSaslMechanisms ) throws NamingException
+    {
+        service.getPartitionNexus().registerSupportedSaslMechanisms( supportedSaslMechanisms );
+    }
+
+
+    // -----------------------------------------------------------------------
+    // EventContext and EventDirContext notification methods
+    // -----------------------------------------------------------------------
+
+    /*
+     * All listener registration/deregistration methods can be reduced down to
+     * the following methods.  Rather then make these actual intercepted methods
+     * we use them as out of band methods to interface with the notification
+     * interceptor.
+     */
+
+    public void addNamingListener( EventContext ctx, LdapDN name, ExprNode filter, SearchControls searchControls,
+            NamingListener namingListener ) throws NamingException
+    {
+        InterceptorChain chain = service.getInterceptorChain();
+        EventInterceptor interceptor = ( EventInterceptor ) chain.get( EventInterceptor.class.getName() );
+        interceptor.addNamingListener( ctx, name, filter, searchControls, namingListener );
+    }
+
+
+    public void removeNamingListener( EventContext ctx, NamingListener namingListener ) throws NamingException
+    {
+        InterceptorChain chain = service.getInterceptorChain();
+        if ( chain == null )
+        {
+            return;
+        }
+        EventInterceptor interceptor = ( EventInterceptor ) chain.get( EventInterceptor.class.getName() );
+        interceptor.removeNamingListener( ctx, namingListener );
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/BTreePartition.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/BTreePartition.java
new file mode 100644
index 0000000..465ae59
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/BTreePartition.java
@@ -0,0 +1,571 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree;
+
+
+import org.apache.directory.server.core.entry.DefaultServerEntry;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerSearchResult;
+import org.apache.directory.server.core.enumeration.SearchResultEnumeration;
+import org.apache.directory.server.core.interceptor.context.AddOperationContext;
+import org.apache.directory.server.core.interceptor.context.DeleteOperationContext;
+import org.apache.directory.server.core.interceptor.context.EntryOperationContext;
+import org.apache.directory.server.core.interceptor.context.ListOperationContext;
+import org.apache.directory.server.core.interceptor.context.LookupOperationContext;
+import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
+import org.apache.directory.server.core.interceptor.context.MoveAndRenameOperationContext;
+import org.apache.directory.server.core.interceptor.context.MoveOperationContext;
+import org.apache.directory.server.core.interceptor.context.RenameOperationContext;
+import org.apache.directory.server.core.interceptor.context.SearchOperationContext;
+import org.apache.directory.server.core.partition.Oid;
+import org.apache.directory.server.core.partition.Partition;
+import org.apache.directory.server.core.partition.impl.btree.gui.PartitionViewer;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.exception.LdapContextNotEmptyException;
+import org.apache.directory.shared.ldap.exception.LdapNameNotFoundException;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.SearchControls;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+
+/**
+ * An abstract {@link Partition} that uses general BTree operations.
+ *
+ * @org.apache.xbean.XBean
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public abstract class BTreePartition implements Partition
+{
+    protected static final Set<String> SYS_INDEX_OIDS;
+
+    static
+    {
+        Set<String> set = new HashSet<String>();
+        set.add( Oid.ALIAS );
+        set.add( Oid.EXISTANCE );
+        set.add( Oid.HIERARCHY );
+        set.add( Oid.NDN );
+        set.add( Oid.ONEALIAS );
+        set.add( Oid.SUBALIAS );
+        set.add( Oid.UPDN );
+        SYS_INDEX_OIDS = Collections.unmodifiableSet( set );
+    }
+
+    /** the search engine used to search the database */
+    protected SearchEngine searchEngine;
+    protected Optimizer optimizer;
+
+    protected Registries registries;
+
+    protected String id;
+    protected int cacheSize = -1;
+    protected LdapDN suffixDn;
+    protected String suffix;
+    
+    /** The rootDSE context */
+    protected ServerEntry contextEntry;
+
+
+    // ------------------------------------------------------------------------
+    // C O N S T R U C T O R S
+    // ------------------------------------------------------------------------
+
+
+    /**
+     * Creates a B-tree based context partition.
+     */
+    protected BTreePartition()
+    {
+    }
+
+    
+    // ------------------------------------------------------------------------
+    // C O N F I G U R A T I O N   M E T H O D S
+    // ------------------------------------------------------------------------
+
+
+    /**
+     * Used to specify the entry cache size for a Partition.  Various Partition
+     * implementations may interpret this value in different ways: i.e. total cache
+     * size limit verses the number of entries to cache.
+     *
+     * @param cacheSize the maximum size of the cache in the number of entries
+     */
+    public void setCacheSize( int cacheSize )
+    {
+        this.cacheSize = cacheSize;
+    }
+
+
+    /**
+     * Gets the entry cache size for this BTreePartition.
+     *
+     * @return the maximum size of the cache as the number of entries maximum before paging out
+     */
+    public int getCacheSize()
+    {
+        return cacheSize;
+    }
+
+
+    /**
+     * Returns root entry for this BTreePartition.
+     *
+     * @return the root suffix entry for this BTreePartition
+     */
+    public ServerEntry getContextEntry()
+    {
+        if ( contextEntry != null )
+        {
+            return ( ServerEntry ) contextEntry.clone();
+        }
+        else
+        {
+            return null;
+        }
+    }
+
+
+    /**
+     * Sets root entry for this BTreePartition.
+     *
+     * @param rootEntry the root suffix entry of this BTreePartition
+     */
+    public void setContextEntry( ServerEntry rootEntry )
+    {
+        this.contextEntry = ( ServerEntry ) rootEntry.clone();
+    }
+
+    
+    /**
+     * Sets root entry for this BTreePartition.
+     *
+     * @param rootEntry the root suffix entry of this BTreePartition
+     */
+    public void setContextEntry( String rootEntry )
+    {
+        System.out.println( rootEntry );
+    }
+
+    
+    /**
+     * Gets the unique identifier for this partition.
+     *
+     * @return the unique identifier for this partition
+     */
+    public String getId()
+    {
+        return id;
+    }
+
+
+    /**
+     * Sets the unique identifier for this partition.
+     *
+     * @param id the unique identifier for this partition
+     */
+    public void setId( String id )
+    {
+        this.id = id;
+    }
+    
+    
+    // -----------------------------------------------------------------------
+    // E N D   C O N F I G U R A T I O N   M E T H O D S
+    // -----------------------------------------------------------------------
+
+
+    /**
+     * Allows for schema entity registries to be swapped out during runtime.  This is 
+     * primarily here to facilitate the swap out of a temporary bootstrap registry.  
+     * Registry changes require swapping out the search engine used by a partition 
+     * since the registries are used by elements in the search engine.
+     * 
+     * @param registries the schema entity registries
+     */
+    public abstract void setRegistries( Registries registries );
+
+    
+    // ------------------------------------------------------------------------
+    // Public Accessors - not declared in any interfaces just for this class
+    // ------------------------------------------------------------------------
+
+    /**
+     * Gets the DefaultSearchEngine used by this ContextPartition to search the
+     * Database. 
+     *
+     * @return the search engine
+     */
+    public SearchEngine getSearchEngine()
+    {
+        return searchEngine;
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Partition Interface Method Implementations
+    // ------------------------------------------------------------------------
+
+
+    public void delete( DeleteOperationContext opContext ) throws NamingException
+    {
+        LdapDN dn = opContext.getDn();
+        
+        Long id = getEntryId( dn.getNormName() );
+
+        // don't continue if id is null
+        if ( id == null )
+        {
+            throw new LdapNameNotFoundException( "Could not find entry at '" + dn + "' to delete it!" );
+        }
+
+        if ( getChildCount( id ) > 0 )
+        {
+            LdapContextNotEmptyException cnee = new LdapContextNotEmptyException( "[66] Cannot delete entry " + dn
+                + " it has children!" );
+            cnee.setRemainingName( dn );
+            throw cnee;
+        }
+
+        delete( id );
+    }
+
+
+    public abstract void add( AddOperationContext opContext ) throws NamingException;
+
+
+    public abstract void modify( ModifyOperationContext opContext ) throws NamingException;
+
+
+    private static final String[] ENTRY_DELETED_ATTRS = new String[] { "entrydeleted" };
+
+
+    public NamingEnumeration<ServerSearchResult> list( ListOperationContext opContext ) throws NamingException
+    {
+        SearchResultEnumeration list;
+        list = new BTreeSearchResultEnumeration( ENTRY_DELETED_ATTRS, list( getEntryId( opContext.getDn().getNormName() ) ),
+            this, registries );
+        return list;
+    }
+
+
+    public NamingEnumeration<ServerSearchResult> search( SearchOperationContext opContext )
+        throws NamingException
+    {
+        SearchControls searchCtls = opContext.getSearchControls();
+        String[] attrIds = searchCtls.getReturningAttributes();
+        NamingEnumeration<IndexRecord> underlying;
+
+        underlying = searchEngine.search( 
+            opContext.getDn(),
+            opContext.getAliasDerefMode(),
+            opContext.getFilter(), 
+            searchCtls );
+
+        return new BTreeSearchResultEnumeration( attrIds, underlying, this, registries );
+    }
+
+
+    public ServerEntry lookup( LookupOperationContext opContext ) throws NamingException
+    {
+        ServerEntry entry = lookup( getEntryId( opContext.getDn().getNormName() ) );
+
+        if ( ( opContext.getAttrsId() == null ) || ( opContext.getAttrsId().size() == 0 ) )
+        {
+            return entry;
+        }
+
+        ServerEntry retval = new DefaultServerEntry( opContext.getRegistries(), opContext.getDn() );
+
+        for ( String attrId:opContext.getAttrsId() )
+        {
+            EntryAttribute attr = entry.get( attrId );
+
+            if ( attr != null )
+            {
+                retval.put( attr );
+            }
+        }
+
+        return retval;
+    }
+
+
+    public boolean hasEntry( EntryOperationContext opContext ) throws NamingException
+    {
+        return null != getEntryId( opContext.getDn().getNormName() );
+    }
+
+
+    public abstract void rename( RenameOperationContext opContext ) throws NamingException;
+
+
+    public abstract void move( MoveOperationContext opContext ) throws NamingException;
+
+
+    public abstract void moveAndRename( MoveAndRenameOperationContext opContext )
+        throws NamingException;
+
+
+    public abstract void sync() throws NamingException;
+
+
+    public abstract void destroy();
+
+
+    public abstract boolean isInitialized();
+
+
+    public void inspect() throws Exception
+    {
+        PartitionViewer viewer = new PartitionViewer( this, registries );
+        viewer.execute();
+    }
+
+
+    ////////////////////
+    // public abstract methods
+
+    // ------------------------------------------------------------------------
+    // Index Operations 
+    // ------------------------------------------------------------------------
+
+    public abstract void addIndexOn( Index index ) throws NamingException;
+
+
+    public abstract boolean hasUserIndexOn( String attribute ) throws NamingException;
+
+
+    public abstract boolean hasSystemIndexOn( String attribute ) throws NamingException;
+
+
+    public abstract Index getExistanceIndex();
+
+
+    /**
+     * Gets the Index mapping the BigInteger primary keys of parents to the 
+     * BigInteger primary keys of their children.
+     *
+     * @return the hierarchy Index
+     */
+    public abstract Index getHierarchyIndex();
+
+
+    /**
+     * Gets the Index mapping user provided distinguished names of entries as 
+     * Strings to the BigInteger primary keys of entries.
+     *
+     * @return the user provided distinguished name Index
+     */
+    public abstract Index getUpdnIndex();
+
+
+    /**
+     * Gets the Index mapping the normalized distinguished names of entries as
+     * Strings to the BigInteger primary keys of entries.  
+     *
+     * @return the normalized distinguished name Index
+     */
+    public abstract Index getNdnIndex();
+
+
+    /**
+     * Gets the alias index mapping parent entries with scope expanding aliases 
+     * children one level below them; this system index is used to dereference
+     * aliases on one/single level scoped searches.
+     * 
+     * @return the one alias index
+     */
+    public abstract Index getOneAliasIndex();
+
+
+    /**
+     * Gets the alias index mapping relative entries with scope expanding 
+     * alias descendents; this system index is used to dereference aliases on 
+     * subtree scoped searches.
+     * 
+     * @return the sub alias index
+     */
+    public abstract Index getSubAliasIndex();
+
+
+    /**
+     * Gets the system index defined on the ALIAS_ATTRIBUTE which for LDAP would
+     * be the aliasedObjectName and for X.500 would be aliasedEntryName.
+     * 
+     * @return the index on the ALIAS_ATTRIBUTE
+     */
+    public abstract Index getAliasIndex();
+
+
+    /**
+     * Sets the system index defined on the ALIAS_ATTRIBUTE which for LDAP would
+     * be the aliasedObjectName and for X.500 would be aliasedEntryName.
+     * 
+     * @param index the index on the ALIAS_ATTRIBUTE
+     * @throws NamingException if there is a problem setting up the index
+     */
+    public abstract void setAliasIndexOn( Index index ) throws NamingException;
+
+
+    /**
+     * Sets the attribute existance Index.
+     *
+     * @param index the attribute existance Index
+     * @throws NamingException if there is a problem setting up the index
+     */
+    public abstract void setExistanceIndexOn( Index index ) throws NamingException;
+
+
+    /**
+     * Sets the hierarchy Index.
+     *
+     * @param index the hierarchy Index
+     * @throws NamingException if there is a problem setting up the index
+     */
+    public abstract void setHierarchyIndexOn( Index index ) throws NamingException;
+
+
+    /**
+     * Sets the user provided distinguished name Index.
+     *
+     * @param index the updn Index
+     * @throws NamingException if there is a problem setting up the index
+     */
+    public abstract void setUpdnIndexOn( Index index ) throws NamingException;
+
+
+    /**
+     * Sets the normalized distinguished name Index.
+     *
+     * @param index the ndn Index
+     * @throws NamingException if there is a problem setting up the index
+     */
+    public abstract void setNdnIndexOn( Index index ) throws NamingException;
+
+
+    /**
+     * Sets the alias index mapping parent entries with scope expanding aliases 
+     * children one level below them; this system index is used to dereference
+     * aliases on one/single level scoped searches.
+     * 
+     * @param index a one level alias index
+     * @throws NamingException if there is a problem setting up the index
+     */
+    public abstract void setOneAliasIndexOn( Index index ) throws NamingException;
+
+
+    /**
+     * Sets the alias index mapping relative entries with scope expanding 
+     * alias descendents; this system index is used to dereference aliases on 
+     * subtree scoped searches.
+     * 
+     * @param index a subtree alias index
+     * @throws NamingException if there is a problem setting up the index
+     */
+    public abstract void setSubAliasIndexOn( Index index ) throws NamingException;
+
+
+    public abstract Index getUserIndex( String attribute ) throws IndexNotFoundException;
+
+
+    public abstract Index getSystemIndex( String attribute ) throws IndexNotFoundException;
+
+
+    public abstract Long getEntryId( String dn ) throws NamingException;
+
+
+    public abstract String getEntryDn( Long id ) throws NamingException;
+
+
+    public abstract Long getParentId( String dn ) throws NamingException;
+
+
+    public abstract Long getParentId( Long childId ) throws NamingException;
+
+
+    /**
+     * Gets the user provided distinguished name.
+     *
+     * @param id the entry id
+     * @return the user provided distinguished name
+     * @throws NamingException if the updn index cannot be accessed
+     */
+    public abstract String getEntryUpdn( Long id ) throws NamingException;
+
+
+    /**
+     * Gets the user provided distinguished name.
+     *
+     * @param dn the normalized distinguished name
+     * @return the user provided distinguished name
+     * @throws NamingException if the updn and ndn indices cannot be accessed
+     */
+    public abstract String getEntryUpdn( String dn ) throws NamingException;
+
+
+    public abstract ServerEntry lookup( Long id ) throws NamingException;
+
+
+    public abstract void delete( Long id ) throws NamingException;
+
+
+    public abstract NamingEnumeration<IndexRecord> list( Long id ) throws NamingException;
+
+
+    public abstract int getChildCount( Long id ) throws NamingException;
+
+
+    public abstract ServerEntry getSuffixEntry() throws NamingException;
+
+
+    public abstract void setProperty( String key, String value ) throws NamingException;
+
+
+    public abstract String getProperty( String key ) throws NamingException;
+
+
+    public abstract Iterator<String> getUserIndices();
+
+
+    public abstract Iterator<String> getSystemIndices();
+
+
+    public abstract ServerEntry getIndices( Long id ) throws NamingException;
+
+
+    /**
+     * Gets the count of the total number of entries in the database.
+     *
+     * TODO shouldn't this be a BigInteger instead of an int? 
+     * 
+     * @return the number of entries in the database 
+     * @throws NamingException if there is a failure to read the count
+     */
+    public abstract int count() throws NamingException;
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/BTreeSearchResult.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/BTreeSearchResult.java
new file mode 100644
index 0000000..28b00cd
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/BTreeSearchResult.java
@@ -0,0 +1,124 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree;
+
+
+import javax.naming.InvalidNameException;
+
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerSearchResult;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+
+/**
+ * A special search result that includes the unique database primary key or 
+ * 'row id' of the entry in the master table for quick lookup.  This speeds 
+ * up various operations.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class BTreeSearchResult extends ServerSearchResult
+{
+    private static final long serialVersionUID = 3976739172700860977L;
+
+    /** the primary key used for the resultant entry */
+    private final Long id;
+
+
+    // ------------------------------------------------------------------------
+    // C O N S T R U C T O R S
+    // ------------------------------------------------------------------------
+
+    /**
+     * Creates a database search result.
+     * 
+     * @param id the database id of the entry
+     * @param dn the user provided relative or distinguished name
+     * @param obj the object if any
+     * @param attrs the entry
+     */
+    public BTreeSearchResult( Long id, LdapDN dn, Object obj, ServerEntry attrs ) throws InvalidNameException
+    {
+        super( dn, obj, attrs );
+        this.id = id;
+    }
+
+
+    /**
+     * Creates a database search result.
+     * 
+     * @param id the database id of the entry
+     * @param dn the user provided relative or distinguished name
+     * @param obj the object if any
+     * @param attrs the entry
+     * @param isRelative whether or not the name is relative to the base
+     */
+    public BTreeSearchResult( Long id, LdapDN dn, Object obj, ServerEntry attrs, boolean isRelative ) throws InvalidNameException
+    {
+        super( dn, obj, attrs, isRelative );
+        this.id = id;
+    }
+
+
+    /**
+     * Creates a database search result.
+     * 
+     * @param id the database id of the entry
+     * @param dn the user provided relative or distinguished name
+     * @param className the classname of the entry if any
+     * @param obj the object if any
+     * @param attrs the entry
+     */
+    public BTreeSearchResult( Long id, LdapDN dn, String className, Object obj, ServerEntry attrs ) throws InvalidNameException
+    {
+        super( dn, className, obj, attrs );
+        this.id = id;
+    }
+
+
+    /**
+     * Creates a database search result.
+     * 
+     * @param id the database id of the entry
+     * @param dn the user provided relative or distinguished name
+     * @param className the classname of the entry if any
+     * @param obj the object if any
+     * @param attrs the entry
+     * @param isRelative whether or not the name is relative to the base
+     */
+    public BTreeSearchResult( Long id, LdapDN dn, String className, Object obj, ServerEntry attrs,
+        boolean isRelative ) throws InvalidNameException
+    {
+        super( dn, className, obj, attrs, isRelative );
+        this.id = id;
+    }
+
+
+    /**
+     * Gets the unique row id of the entry into the master table.
+     * 
+     * @return Returns the id.
+     */
+    public Long getId()
+    {
+        return id;
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/BTreeSearchResultEnumeration.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/BTreeSearchResultEnumeration.java
new file mode 100644
index 0000000..6b667bc
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/BTreeSearchResultEnumeration.java
@@ -0,0 +1,323 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree;
+
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+
+import org.apache.directory.server.core.entry.DefaultServerEntry;
+import org.apache.directory.server.core.entry.ServerAttribute;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerSearchResult;
+import org.apache.directory.server.core.enumeration.SearchResultEnumeration;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.schema.UsageEnum;
+
+
+/**
+ * An enumeration that transforms another underlying enumeration over a set of 
+ * IndexRecords into an enumeration over a set of SearchResults.  Note that the
+ * SearchResult created may not be complete and other parts of the system may
+ * modify it before return.  This enumeration simply creates a new copy of the 
+ * entry to return stuffing it with the attributes that were specified.  This is
+ * all that it does now but this may change later.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class BTreeSearchResultEnumeration implements SearchResultEnumeration
+{
+    /** Database used to lookup entries from */
+    private BTreePartition partition = null;
+    /** the attributes to return */
+    private final String[] attrIds;
+    /** underlying enumeration over IndexRecords */
+    private final NamingEnumeration<IndexRecord> underlying;
+
+    private boolean attrIdsHasStar = false;
+    private boolean attrIdsHasPlus = false;
+    private Registries registries = null;
+
+
+    /**
+     * using the search parameters supplied to a search call.
+     * Creates an enumeration that returns entries packaged within SearchResults
+     * 
+     * @param attrIds the returned attributes
+     * @param underlying the enumeration over IndexRecords
+     */
+    public BTreeSearchResultEnumeration( String[] attrIds, NamingEnumeration<IndexRecord> underlying, BTreePartition db,
+        Registries registries)
+    {
+        this.partition = db;
+        this.attrIds = attrIds;
+        this.underlying = underlying;
+        this.attrIdsHasStar = containsStar( attrIds );
+        this.attrIdsHasPlus = containsPlus( attrIds );
+        this.registries = registries;
+    }
+
+
+    /**
+     * @see javax.naming.NamingEnumeration#close()
+     */
+    public void close() throws NamingException
+    {
+        underlying.close();
+    }
+
+
+    /**
+     * @see javax.naming.NamingEnumeration#hasMore()
+     */
+    public boolean hasMore() throws NamingException
+    {
+        return underlying.hasMore();
+    }
+
+
+    /**
+     * @see javax.naming.NamingEnumeration#next()
+     */
+    public ServerSearchResult next() throws NamingException
+    {
+        IndexRecord rec = underlying.next();
+        ServerEntry entry = rec.getEntry();
+
+        if ( null == entry )
+        {
+            entry = partition.lookup( (Long)rec.getEntryId() );
+            rec.setEntry( entry );
+            entry = (ServerEntry)entry.clone();
+        }
+        
+        LdapDN dn = entry.getDn();
+
+        if ( ( attrIds == null ) || ( attrIdsHasPlus && attrIdsHasStar ) )
+        {
+        }
+        else if ( attrIdsHasPlus )
+        {
+            entry = new DefaultServerEntry( registries, dn );
+
+            // add all listed attributes
+            for ( String attrId:attrIds )
+            {
+                if ( attrId.equals( SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES ) )
+                {
+                    continue;
+                }
+                // there is no attribute by that name in the entry so we continue
+                if ( null == rec.getEntry().get( attrId ) )
+                {
+                    continue;
+                }
+
+                ServerAttribute attr =  ( ServerAttribute ) rec.getEntry().get( attrId ).clone(); 
+
+                entry.put( attr );
+            }
+
+            // add all operational attributes
+            for ( EntryAttribute attribute:rec.getEntry() )
+            {
+                AttributeType attrType = ((ServerAttribute)attribute).getAttributeType();
+                
+                if ( attrType.getUsage() == UsageEnum.USER_APPLICATIONS )
+                {
+                    continue;
+                }
+
+                ServerAttribute attr = (ServerAttribute)attribute.clone(); 
+                entry.put( attr );
+            }
+        }
+        else if ( attrIdsHasStar )
+        {
+            entry = new DefaultServerEntry( registries, dn );
+
+            // add all listed operational attributes
+            for ( String attrId:attrIds )
+            {
+                if ( attrId.equals( SchemaConstants.ALL_USER_ATTRIBUTES ) )
+                {
+                    continue;
+                }
+                // there is no attribute by that name in the entry so we continue
+                if ( null == rec.getEntry().get( attrId ) )
+                {
+                    continue;
+                }
+
+                // clone attribute to stuff into the new resultant entry
+                ServerAttribute attr = ( ServerAttribute ) rec.getEntry().get( attrId ).clone(); 
+                entry.put( attr );
+            }
+
+            // add all user attributes
+            for ( EntryAttribute attribute:rec.getEntry() )
+            {
+                AttributeType attrType = ((ServerAttribute)attribute).getAttributeType();
+                
+                if ( attrType.getUsage() == UsageEnum.USER_APPLICATIONS )
+                {
+                    ServerAttribute attr = ( ServerAttribute ) rec.getEntry().get( attrType ).clone(); 
+                    entry.put( attr );
+                }
+            }
+        }
+        else
+        {
+            Set<EntryAttribute> entryAttrs = new HashSet<EntryAttribute>(); 
+            
+            for ( EntryAttribute entryAttribute:entry )
+            {
+                entryAttrs.add( entryAttribute );
+            }
+            //entry = new DefaultServerEntry( registries, dn );
+
+            //ServerEntry attrs = rec.getEntry();
+            
+            for ( String attrId:attrIds )
+            {
+                if ( SchemaConstants.NO_ATTRIBUTE.equals( attrId ) )
+                {
+                    break;
+                }
+                
+                EntryAttribute attr = entry.get( registries.getAttributeTypeRegistry().lookup( attrId ) );
+                
+                // there is no attribute by that name in the entry so we continue
+                if ( null == attr )
+                {
+                    // May be it's because the attributeType is a inherited one?
+                    Iterator<AttributeType> descendants = registries.getAttributeTypeRegistry().descendants( attrId );
+                    
+                    while ( descendants.hasNext() )
+                    {
+                        AttributeType atype = descendants.next();
+                        
+                        attr = entry.get( atype );
+                        
+                        if ( attr != null )
+                        {
+                            // we may have more than one descendant, like sn and cn
+                            // for name, so add all of them
+                            //entry.put( (ServerAttribute)attr.clone() );
+                            entryAttrs.remove( attr );
+                        }
+                    }
+                }
+                else
+                {
+                    // clone attribute to stuff into the new resultant entry
+                    //entry.put( (ServerAttribute)attr.clone() );
+                    entryAttrs.remove( attr );
+                }
+            }
+            
+            for ( EntryAttribute entryAttribute:entryAttrs )
+            {
+                entry.remove( entryAttribute );
+            }
+        }
+
+        
+        BTreeSearchResult result = new BTreeSearchResult( (Long)rec.getEntryId(), dn, null, entry );
+        result.setRelative( false );
+        return result;
+    }
+
+
+    private boolean containsStar( String[] ids )
+    {
+        if ( ids == null )
+        {
+            return false;
+        }
+
+        for ( int ii = ids.length - 1; ii >= 0; ii-- )
+        {
+            if ( ids[ii].trim().equals( SchemaConstants.ALL_USER_ATTRIBUTES ) )
+            {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+
+    private boolean containsPlus( String[] ids )
+    {
+        if ( ids == null )
+        {
+            return false;
+        }
+
+        for ( int ii = ids.length - 1; ii >= 0; ii-- )
+        {
+            if ( ids[ii].trim().equals( SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES ) )
+            {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+
+    /**
+     * @see java.util.Enumeration#hasMoreElements()
+     */
+    public boolean hasMoreElements()
+    {
+        return underlying.hasMoreElements();
+    }
+
+
+    /**
+     * @see java.util.Enumeration#nextElement()
+     */
+    public ServerSearchResult nextElement()
+    {
+        try
+        {
+            return next();
+        }
+        catch ( NamingException e )
+        {
+            NoSuchElementException nsee = 
+                new NoSuchElementException( "Encountered NamingException on underlying enumeration." );
+            nsee.initCause( e );
+            throw nsee;
+        }
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/DefaultOptimizer.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/DefaultOptimizer.java
new file mode 100644
index 0000000..fc6cdee
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/DefaultOptimizer.java
@@ -0,0 +1,358 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree;
+
+
+import java.util.List;
+
+import javax.naming.NamingException;
+import javax.naming.directory.SearchControls;
+
+import org.apache.directory.shared.ldap.filter.AndNode;
+import org.apache.directory.shared.ldap.filter.ApproximateNode;
+import org.apache.directory.shared.ldap.filter.AssertionNode;
+import org.apache.directory.shared.ldap.filter.BranchNode;
+import org.apache.directory.shared.ldap.filter.EqualityNode;
+import org.apache.directory.shared.ldap.filter.ExprNode;
+import org.apache.directory.shared.ldap.filter.ExtensibleNode;
+import org.apache.directory.shared.ldap.filter.GreaterEqNode;
+import org.apache.directory.shared.ldap.filter.LeafNode;
+import org.apache.directory.shared.ldap.filter.LessEqNode;
+import org.apache.directory.shared.ldap.filter.NotNode;
+import org.apache.directory.shared.ldap.filter.OrNode;
+import org.apache.directory.shared.ldap.filter.PresenceNode;
+import org.apache.directory.shared.ldap.filter.ScopeNode;
+import org.apache.directory.shared.ldap.filter.SimpleNode;
+import org.apache.directory.shared.ldap.filter.SubstringNode;
+
+
+/**
+ * Optimizer that annotates the filter using scan counts.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class DefaultOptimizer implements Optimizer
+{
+    /** the database this optimizer operates on */
+    private BTreePartition db;
+
+    /**
+     * Creates an optimizer on a database.
+     *
+     * @param db the database this optimizer works for.
+     */
+    public DefaultOptimizer(BTreePartition db)
+    {
+        this.db = db;
+    }
+
+
+    /**
+     * Annotates the expression tree to determine optimal evaluation order based
+     * on the scan count for indices that exist for each expression node.  If an
+     * index on the attribute does not exist an IndexNotFoundException will be
+     * thrown.
+     *
+     * @see Optimizer#annotate(ExprNode)
+     */
+    public void annotate( ExprNode node ) throws NamingException
+    {
+        // Start off with the worst case unless scan count says otherwise.
+        Long count = Long.MAX_VALUE;
+
+        /* --------------------------------------------------------------------
+         *                 H A N D L E   L E A F   N O D E S          
+         * --------------------------------------------------------------------
+         * 
+         * Each leaf node is based on an attribute and it represents a condition
+         * that needs to be statisfied.  We ask the index (if one exists) for 
+         * the attribute to give us a scan count of all the candidates that 
+         * would satisfy the attribute assertion represented by the leaf node.
+         * 
+         * This is conducted differently based on the type of the leaf node.
+         * Comments on each node type explain how each scan count is arrived at.
+         */
+
+        if ( node instanceof ScopeNode )
+        {
+            count = getScopeScan( ( ScopeNode ) node );
+        }
+        else if ( node instanceof AssertionNode )
+        {
+            /* 
+             * Leave it up to the assertion node to determine just how much it
+             * will cost us.  Anyway it defaults to a maximum scan count if a
+             * scan count is not specified by the implementation.
+             */
+        }
+        else if ( node.isLeaf() )
+        {
+            LeafNode leaf = ( LeafNode ) node;
+
+            if ( node instanceof PresenceNode )
+            {
+                count = getPresenceScan( ( PresenceNode ) leaf );
+            }
+            else if ( node instanceof EqualityNode )
+            {
+                count = getEqualityScan( ( EqualityNode ) leaf );
+            }
+            else if ( node instanceof GreaterEqNode )
+            {
+                count = getGreaterLessScan( ( GreaterEqNode ) leaf, SimpleNode.EVAL_GREATER );
+            }
+            else if ( node instanceof LessEqNode )
+            {
+                count = getGreaterLessScan( ( SimpleNode ) leaf, SimpleNode.EVAL_LESSER );
+            }
+            else if ( node instanceof SubstringNode )
+            {
+                /** Cannot really say so we presume the total index count */
+                count = getFullScan( leaf );
+            }
+            else if ( node instanceof ExtensibleNode )
+            {
+                /** Cannot really say so we presume the total index count */
+                count = getFullScan( leaf );
+            }
+            else if ( node instanceof ApproximateNode )
+            {
+                /** Feature not implemented so we just use equality matching */
+                count = getEqualityScan( ( ApproximateNode ) leaf );
+            }
+            else
+            {
+                throw new IllegalArgumentException( "Unrecognized leaf node" );
+            }
+        }
+        // --------------------------------------------------------------------
+        //                 H A N D L E   B R A N C H   N O D E S       
+        // --------------------------------------------------------------------
+        else
+        {
+            if ( node instanceof AndNode )
+            {
+                count = getConjunctionScan( (AndNode)node );
+            }
+            else if ( node instanceof OrNode )
+            {
+                count = getDisjunctionScan( (OrNode)node );
+            }
+            else if ( node instanceof NotNode )
+            {
+                count = getNegationScan( (NotNode)node );
+            }
+            else
+            {
+                throw new IllegalArgumentException( "Unrecognized branch node type" );
+            }
+        }
+
+        // Protect against overflow when counting.
+        if ( count < 0L )
+        {
+            count = Long.MAX_VALUE;
+        }
+
+        node.set( "count", count );
+    }
+
+
+    /**
+     * ANDs or Conjunctions take the count of the smallest child as their count.
+     * This is the best that a conjunction can do and should be used rather than
+     * the worst case. Notice that we annotate the child node with a recursive 
+     * call before accessing its count parameter making the chain recursion 
+     * depth first.
+     *
+     * @param node a AND (Conjunction) BranchNode
+     * @return the calculated scan count
+     * @throws NamingException if there is an error
+     */
+    private long getConjunctionScan( BranchNode node ) throws NamingException
+    {
+        long count = Long.MAX_VALUE;
+        List<ExprNode> children = node.getChildren();
+
+        for ( int ii = 0; ii < children.size(); ii++ )
+        {
+            ExprNode child = children.get( ii );
+            annotate( child );
+            count = Math.min( ( ( Long ) child.get( "count" ) ), count );
+        }
+
+        return count;
+    }
+
+
+    /**
+     * A negation filter is always worst case since we will have to retrieve all
+     * entries from the master table then test each one against the negated
+     * child filter.  There is no way to use the indices.
+     *
+     * @param node the negation node
+     * @return the scan count
+     * @throws NamingException if there is an error
+     */
+    private long getNegationScan( BranchNode node ) throws NamingException
+    {
+        return Long.MAX_VALUE;
+    }
+
+
+    /**
+     * Disjunctions (OR) are the union of candidates across all subexpressions 
+     * so we add all the counts of the child nodes. Notice that we annotate the 
+     * child node with a recursive call.
+     *
+     * @param node the OR branch node
+     * @return the scan count on the OR node
+     * @throws NamingException if there is an error
+     */
+    private long getDisjunctionScan( BranchNode node ) throws NamingException
+    {
+        List<ExprNode> children = node.getChildren();
+        long total = 0L;
+
+        for ( int ii = 0; ii < children.size(); ii++ )
+        {
+            ExprNode child = children.get( ii );
+            annotate( child );
+            total += ( Long ) child.get( "count" );
+        }
+        
+        return total;
+    }
+
+
+    /**
+     * Gets the worst case scan count for all entries that satisfy the equality
+     * assertion in the SimpleNode argument.  
+     *
+     * @param node the node to get a scan count for 
+     * @return the worst case
+     * @throws NamingException if there is an error accessing an index
+     */
+    private long getEqualityScan( SimpleNode node ) throws NamingException
+    {
+        if ( db.hasUserIndexOn( node.getAttribute() ) )
+        {
+            Index idx = db.getUserIndex( node.getAttribute() );
+            return Long.valueOf( idx.count( node.getValue() ) );
+        }
+
+        // count for non-indexed attribute is unknown so we presume da worst
+        return Long.MAX_VALUE;
+    }
+
+
+    /**
+     * Gets a scan count of the nodes that satisfy the greater or less than test
+     * specified by the node.
+     *
+     * @param node the greater or less than node to get a count for 
+     * @param isGreaterThan if true test is for >=, otherwise <=
+     * @return the scan count of all nodes satisfying the AVA
+     * @throws NamingException if there is an error accessing an index
+     */
+    private long getGreaterLessScan( SimpleNode node, boolean isGreaterThan ) throws NamingException
+    {
+        if ( db.hasUserIndexOn( node.getAttribute() ) )
+        {
+            Index idx = db.getUserIndex( node.getAttribute() );
+            int count = idx.count( node.getValue(), isGreaterThan );
+            return Long.valueOf( count );
+        }
+
+        // count for non-indexed attribute is unknown so we presume da worst
+        return Long.MAX_VALUE;
+    }
+
+
+    /**
+     * Gets the total number of entries within the database index if one is 
+     * available otherwise the count of all the entries within the database is
+     * returned.
+     *
+     * @param node the leaf node to get a full scan count for 
+     * @return the worst case full scan count
+     * @throws NamingException if there is an error access database indices
+     */
+    private long getFullScan( LeafNode node ) throws NamingException
+    {
+        if ( db.hasUserIndexOn( node.getAttribute() ) )
+        {
+            Index idx = db.getUserIndex( node.getAttribute() );
+            int count = idx.count();
+            return Long.valueOf( count );
+        }
+
+        return Long.MAX_VALUE;
+    }
+
+
+    /**
+     * Gets the number of entries that would be returned by a presence node
+     * assertion.  Leverages the existance system index for scan counts.
+     *
+     * @param node the presence node
+     * @return the number of entries matched for the presence of an attribute
+     * @throws NamingException if errors result
+     */
+    private long getPresenceScan( PresenceNode node ) throws NamingException
+    {
+        if ( db.hasUserIndexOn( node.getAttribute() ) )
+        {
+            Index idx = db.getExistanceIndex();
+            int count = idx.count( node.getAttribute() );
+            return Long.valueOf( count );
+        }
+
+        return Long.MAX_VALUE;
+    }
+
+
+    /**
+     * Gets the scan count for the scope node attached to this filter.
+     *
+     * @param node the ScopeNode
+     * @return the scan count for scope
+     * @throws NamingException if any errors result
+     */
+    private long getScopeScan( ScopeNode node ) throws NamingException
+    {
+        switch ( node.getScope() )
+        {
+            case ( SearchControls.OBJECT_SCOPE  ):
+                return 1L;
+            
+            case ( SearchControls.ONELEVEL_SCOPE  ):
+                Long id = db.getEntryId( node.getBaseDn() );
+                return Long.valueOf( db.getChildCount( id ) );
+                
+            case ( SearchControls.SUBTREE_SCOPE  ):
+                return Long.valueOf( db.count() );
+            
+            default:
+                throw new IllegalArgumentException( "Unrecognized search scope " + "value for filter scope node" );
+        }
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/DefaultSearchEngine.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/DefaultSearchEngine.java
new file mode 100644
index 0000000..f6e00f1
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/DefaultSearchEngine.java
@@ -0,0 +1,140 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree;
+
+
+import javax.naming.Name;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.SearchControls;
+
+import org.apache.directory.shared.ldap.filter.AndNode;
+import org.apache.directory.shared.ldap.filter.BranchNode;
+import org.apache.directory.shared.ldap.filter.ExprNode;
+import org.apache.directory.shared.ldap.filter.ScopeNode;
+import org.apache.directory.shared.ldap.message.AliasDerefMode;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+
+/**
+ * Given a search filter and a scope the search engine identifies valid
+ * candidate entries returning their ids.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class DefaultSearchEngine implements SearchEngine
+{
+    /** the Optimizer used by this DefaultSearchEngine */
+    private final Optimizer optimizer;
+    /** the Database this DefaultSearchEngine operates on */
+    private BTreePartition db;
+    /** Evaluator flyweight used for filter expression assertions */
+    private ExpressionEvaluator evaluator;
+    /** Enumerator flyweight that creates enumerations on filter expressions */
+    private ExpressionEnumerator enumerator;
+
+
+    // ------------------------------------------------------------------------
+    // C O N S T R U C T O R S
+    // ------------------------------------------------------------------------
+
+    /**
+     * Creates a DefaultSearchEngine for searching a Database without setting
+     * up the database.
+     * @param db the btree based partition
+     * @param enumerator an expression enumerator
+     * @param evaluator an expression evaluator
+     * @param optimizer an optimizer to use during search
+     */
+    public DefaultSearchEngine( BTreePartition db, ExpressionEvaluator evaluator,
+        ExpressionEnumerator enumerator, Optimizer optimizer )
+    {
+        this.db = db;
+        this.evaluator = evaluator;
+        this.enumerator = enumerator;
+        this.optimizer = optimizer;
+    }
+
+
+    /**
+     * Gets the optimizer for this DefaultSearchEngine.
+     *
+     * @return the optimizer
+     */
+    public Optimizer getOptimizer()
+    {
+        return optimizer;
+    }
+
+
+    public NamingEnumeration<IndexRecord> search( Name base, AliasDerefMode aliasDerefMode, ExprNode filter, SearchControls searchCtls )
+        throws NamingException
+    {
+        Name effectiveBase;
+        Long baseId = db.getEntryId( base.toString() );
+        String aliasedBase = ( String ) db.getAliasIndex().reverseLookup( baseId );
+
+        // --------------------------------------------------------------------
+        // Determine the effective base with aliases
+        // --------------------------------------------------------------------
+
+        /*
+         * If the base is not an alias or if alias dereferencing does not
+         * occur on finding the base then we set the effective base to the
+         * given base.
+         */
+        if ( ( null == aliasedBase ) || ! aliasDerefMode.isDerefFindingBase() )
+        {
+            effectiveBase = base;
+        }
+
+        /*
+         * If the base is an alias and alias dereferencing does occur on
+         * finding the base then we set the effective base to the alias target
+         * got from the alias index.
+         */
+        else
+        {
+            effectiveBase = new LdapDN( aliasedBase );
+        }
+        
+        // Add the scope node using the effective base to the filter
+        BranchNode root = new AndNode();
+        ExprNode node = new ScopeNode( aliasDerefMode, effectiveBase.toString(), searchCtls.getSearchScope() );
+        root.getChildren().add( node );
+        root.getChildren().add( filter );
+
+        // Annotate the node with the optimizer and return search enumeration.
+        optimizer.annotate( root );
+        return enumerator.enumerate( root );
+    }
+
+
+    /**
+     * @see SearchEngine#evaluate(ExprNode, Long)
+     */
+    public boolean evaluate( ExprNode ilter, Long id ) throws NamingException
+    {
+        IndexRecord rec = new IndexRecord();
+        rec.setEntryId( id );
+        return evaluator.evaluate( ilter, rec );
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/DisjunctionEnumeration.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/DisjunctionEnumeration.java
new file mode 100644
index 0000000..43ab008
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/DisjunctionEnumeration.java
@@ -0,0 +1,231 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree;
+
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.NoSuchElementException;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+
+
+/**
+ * A Cursor of Cursors performing a union on all underlying Cursors resulting
+ * in the disjunction of expressions represented by the constituant child
+ * Cursors. This cursor prefetches underlying Cursor values so that it can
+ * comply with the defined Cursor semantics.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class DisjunctionEnumeration implements NamingEnumeration<IndexRecord>
+{
+    /** The underlying child enumerations */
+    private final NamingEnumeration<IndexRecord>[] children;
+    
+    /** LUT used to avoid returning duplicates */
+    private final Map<Object, Object> candidates = new HashMap<Object, Object>();
+    /** Index of current cursor used */
+    private int index = 0;
+    /** Candidate to return */
+    private final IndexRecord candidate = new IndexRecord();
+    /** Prefetched record returned */
+    private final IndexRecord prefetched = new IndexRecord();
+    /** Used to determine if this enumeration has been exhausted */
+    private boolean hasMore = true;
+
+
+    // ------------------------------------------------------------------------
+    // C O N S T R U C T O R S
+    // ------------------------------------------------------------------------
+
+    /**
+     * Creates a DisjunctionEnumeration over a set of child NamingEnumerations.
+     * The returned result is the union of all underlying NamingEnumerations 
+     * without duplicates.
+     *
+     * @param children array of child NamingInstances
+     * @throws NamingException if something goes wrong
+     */
+    public DisjunctionEnumeration( NamingEnumeration<IndexRecord>[] children ) throws NamingException
+    {
+        this.children = children;
+
+        // Close this cursor if their are no children.
+        if ( children.length <= 0 )
+        {
+            hasMore = false;
+            return;
+        }
+
+        // Advance to the first cursor that has a candidate for us.
+        while ( !children[index].hasMore() )
+        {
+            index++;
+
+            // Close and return if we exhaust the cursors without finding a
+            // valid candidate to return.
+            if ( index >= children.length )
+            {
+                close();
+                return;
+            }
+        }
+
+        // Grab the next candidate and add it's id to the LUT/hash of candidates
+        IndexRecord rec = children[index].next();
+        prefetched.copy( rec );
+        candidates.put( rec.getEntryId(), rec.getEntryId() );
+    }
+
+
+    // ------------------------------------------------------------------------
+    // java.util.Enumeration Implementation Methods 
+    // ------------------------------------------------------------------------
+
+    /**
+     * @see java.util.Enumeration#nextElement()
+     */
+    public IndexRecord nextElement()
+    {
+        try
+        {
+            return next();
+        }
+        catch ( NamingException e )
+        {
+            throw new NoSuchElementException();
+        }
+    }
+
+
+    /**
+     * @see java.util.Enumeration#hasMoreElements()
+     */
+    public boolean hasMoreElements()
+    {
+        return hasMore();
+    }
+
+
+    // ------------------------------------------------------------------------
+    // NamingEnumeration Method Implementations
+    // ------------------------------------------------------------------------
+
+    /**
+     * Advances this Cursor one position.  Duplicates are not returned so if
+     * underlying cursors keep returning duplicates the child cursors will be
+     * advanced until a unique candidate is found or all child cursors are
+     * exhausted.
+     *
+     * @return a candidate element
+     * @throws NamingException if an error occurs
+     */
+    public IndexRecord next() throws NamingException
+    {
+        // Store the last prefetched candidate to return in candidate
+        candidate.copy( prefetched );
+
+        do
+        {
+            // Advance to a Cursor that has the next valid candidate for us.
+            while ( !children[index].hasMore() )
+            {
+                index++;
+
+                /* Close and return existing prefetched candidate if we
+                 * have exhausted the underlying Cursors without finding a
+                 * valid candidate to return.
+                 */
+                if ( index >= children.length )
+                {
+                    close();
+                    return candidate;
+                }
+            }
+
+            // Grab next candidate!
+            IndexRecord rec = children[index].next();
+            prefetched.copy( rec );
+
+            // Break through do/while if the candidate is seen for the first
+            // time, meaning we have not returned it already.
+        }
+        while ( candidates.containsKey( prefetched.getEntryId() ) );
+
+        // Add candidate to LUT of encountered candidates.
+        candidates.put( candidate.getEntryId(), candidate.getEntryId() );
+
+        // Return the original value saved before overwriting prefetched
+        return candidate;
+    }
+
+
+    /**
+     * Tests if a prefetched value exists and a call to advance will hence
+     * succeed.
+     *
+     * @return true if a call to advance will succeed false otherwise.
+     */
+    public boolean hasMore()
+    {
+        return hasMore;
+    }
+
+
+    /**
+     * Closes all the underlying Cursors and not fail fast.  All enumerations 
+     * will have close attempts made on them.
+     * 
+     * @throws NamingException if we cannot close all enumerations
+     */
+    public void close() throws NamingException
+    {
+        Throwable throwable = null;
+        hasMore = false;
+
+        for ( int ii = 0; ii < children.length; ii++ )
+        {
+            try
+            {
+                // Close all children but don't fail fast meaning don't stop
+                // closing all children if one fails to close for some reason.
+                children[ii].close();
+            }
+            catch ( Throwable t )
+            {
+                throwable = t;
+            }
+        }
+
+        if ( null != throwable && throwable instanceof NamingException )
+        {
+            throw ( NamingException ) throwable;
+        }
+        else if ( null != throwable )
+        {
+            NamingException ne = new NamingException();
+            ne.setRootCause( throwable );
+            throw ne;
+        }
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/Enumerator.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/Enumerator.java
new file mode 100644
index 0000000..b000550
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/Enumerator.java
@@ -0,0 +1,46 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree;
+
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+
+import org.apache.directory.shared.ldap.filter.ExprNode;
+
+
+/**
+ * An enumeration builder or factory for filter expressions.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public interface Enumerator
+{
+    /**
+     * Creates an enumeration to enumerate through the set of candidates 
+     * satisfying a filter expression.
+     * 
+     * @param node a filter expression root
+     * @return an enumeration over the 
+     * @throws NamingException if database access fails
+     */
+    NamingEnumeration<IndexRecord> enumerate( ExprNode node ) throws NamingException;
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/Evaluator.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/Evaluator.java
new file mode 100644
index 0000000..6116b8c
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/Evaluator.java
@@ -0,0 +1,47 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree;
+
+
+import javax.naming.NamingException;
+
+import org.apache.directory.shared.ldap.filter.ExprNode;
+
+
+/**
+ * Tests if an entry is eligable for return by evaluating a filter expression on
+ * the candidate.  The evaluation can proceed by applying the filter on the 
+ * attributes of the entry itself or indices can be used for rapid evaluation.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public interface Evaluator
+{
+    /**
+     * Evaluates a candidate to determine if a filter expression selects it.
+     * 
+     * @param node the filter expression to evaluate on the candidate
+     * @param record the index record of the entry to evaluate
+     * @return true if the filter selects the candidate false otherwise
+     * @throws NamingException if there is a database fault during evaluation
+     */
+    boolean evaluate( ExprNode node, IndexRecord record ) throws NamingException;
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/ExpressionEnumerator.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/ExpressionEnumerator.java
new file mode 100644
index 0000000..e5361d5
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/ExpressionEnumerator.java
@@ -0,0 +1,364 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree;
+
+
+import java.util.List;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+
+import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
+import org.apache.directory.shared.ldap.NotImplementedException;
+import org.apache.directory.shared.ldap.filter.AndNode;
+import org.apache.directory.shared.ldap.filter.ApproximateNode;
+import org.apache.directory.shared.ldap.filter.AssertionNode;
+import org.apache.directory.shared.ldap.filter.BranchNode;
+import org.apache.directory.shared.ldap.filter.EqualityNode;
+import org.apache.directory.shared.ldap.filter.ExprNode;
+import org.apache.directory.shared.ldap.filter.ExtensibleNode;
+import org.apache.directory.shared.ldap.filter.GreaterEqNode;
+import org.apache.directory.shared.ldap.filter.LeafNode;
+import org.apache.directory.shared.ldap.filter.LessEqNode;
+import org.apache.directory.shared.ldap.filter.NotNode;
+import org.apache.directory.shared.ldap.filter.OrNode;
+import org.apache.directory.shared.ldap.filter.PresenceNode;
+import org.apache.directory.shared.ldap.filter.ScopeNode;
+import org.apache.directory.shared.ldap.filter.SimpleNode;
+import org.apache.directory.shared.ldap.filter.SubstringNode;
+
+
+/**
+ * Enumerates over candidates that satisfy a filter expression.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class ExpressionEnumerator implements Enumerator
+{
+    /** The database used by this enumerator */
+    private BTreePartition db = null;
+    /** Enumerator flyweight for evaulating filter scope assertions */
+    private ScopeEnumerator scopeEnumerator;
+    /** Enumerator flyweight for evaulating filter substring assertions */
+    private SubstringEnumerator substringEnumerator;
+    /** Evaluator dependency on a ExpressionEvaluator */
+    private ExpressionEvaluator evaluator;
+
+
+    /**
+     * Creates an expression tree enumerator.
+     *
+     * @param db database used by this enumerator
+     * @param evaluator
+     */
+    public ExpressionEnumerator(BTreePartition db, AttributeTypeRegistry attributeTypeRegistry,
+        ExpressionEvaluator evaluator)
+    {
+        this.db = db;
+        this.evaluator = evaluator;
+
+        LeafEvaluator leafEvaluator = evaluator.getLeafEvaluator();
+        scopeEnumerator = new ScopeEnumerator( db, leafEvaluator.getScopeEvaluator() );
+        substringEnumerator = new SubstringEnumerator( db, attributeTypeRegistry, leafEvaluator.getSubstringEvaluator() );
+    }
+
+
+    /**
+     * Creates an enumeration to enumerate through the set of candidates 
+     * satisfying a filter expression.
+     * 
+     * @param node a filter expression root
+     * @return an enumeration over the 
+     * @throws NamingException if database access fails
+     */
+    public NamingEnumeration<IndexRecord> enumerate( ExprNode node ) throws NamingException
+    {
+        NamingEnumeration<IndexRecord> list = null;
+
+        if ( node instanceof ScopeNode )
+        {
+            list = scopeEnumerator.enumerate( node );
+        }
+        else if ( node instanceof AssertionNode )
+        {
+            throw new IllegalArgumentException( "Cannot produce enumeration " + "on an AssertionNode" );
+        }
+        else if ( node.isLeaf() )
+        {
+            LeafNode leaf = ( LeafNode ) node;
+
+            if ( node instanceof PresenceNode )
+            {
+                list = enumPresence( ( PresenceNode ) node );
+            }
+            else if ( node instanceof EqualityNode )
+            {
+                list = enumEquality( ( EqualityNode ) node );
+            }
+            else if ( node instanceof GreaterEqNode )
+            {
+                list = enumGreaterOrLesser( ( SimpleNode ) node, SimpleNode.EVAL_GREATER );
+            }
+            else if ( node instanceof LessEqNode )
+            {
+                list = enumGreaterOrLesser( ( SimpleNode ) node, SimpleNode.EVAL_LESSER );
+            }
+            else if ( node instanceof SubstringNode )
+            {
+                list = substringEnumerator.enumerate( leaf );
+            }
+            else if ( node instanceof ExtensibleNode )
+            {
+                // N O T   I M P L E M E N T E D   Y E T !
+                throw new NotImplementedException();
+            }
+            else if ( node instanceof ApproximateNode )
+            {
+                list = enumEquality( ( EqualityNode ) node );
+            }
+            else
+            {
+                throw new IllegalArgumentException( "Unknown leaf assertion" );
+            }
+        }
+        else
+        {
+            BranchNode branch = ( BranchNode ) node;
+
+            if ( node instanceof AndNode )
+            {
+                list = enumConj( (AndNode)branch );
+            }
+            else if ( node instanceof OrNode )
+            {
+                list = enumDisj( (OrNode)branch );
+            }
+            else if ( node instanceof NotNode )
+            {
+                list = enumNeg( (NotNode)branch );
+            }
+            else
+            {
+                throw new IllegalArgumentException( "Unknown branch logical operator" );
+            }
+        }
+
+        return list;
+    }
+
+
+    /**
+     * Creates an enumeration over a disjunction expression branch node.
+     *
+     * @param node the disjunction expression branch node
+     */
+    private NamingEnumeration<IndexRecord> enumDisj( OrNode node ) throws NamingException
+    {
+        List<ExprNode> children = node.getChildren();
+        NamingEnumeration<IndexRecord>[] childEnumerations = new NamingEnumeration[children.size()];
+
+        // Recursively create NamingEnumerations for each child expression node
+        for ( int ii = 0; ii < childEnumerations.length; ii++ )
+        {
+            childEnumerations[ii] = enumerate( children.get( ii ) );
+        }
+
+        return new DisjunctionEnumeration( childEnumerations );
+    }
+
+
+    /**
+     * Creates an enumeration over a negation expression branch node.
+     *
+     * @param node a negation expression branch node
+     */
+    private NamingEnumeration<IndexRecord> enumNeg( final BranchNode node ) throws NamingException
+    {
+        NamingEnumeration<IndexRecord> baseEnumeration = null;
+        NamingEnumeration<IndexRecord> enumeration = null;
+        
+        baseEnumeration = db.getNdnIndex().listIndices();
+
+        IndexAssertion assertion = new IndexAssertion()
+        {
+            public boolean assertCandidate( IndexRecord rec ) throws NamingException
+            {
+                // NOTICE THE ! HERE
+                // The candidate is valid if it does not pass assertion. A
+                // candidate that passes assertion is therefore invalid.
+                return !evaluator.evaluate( node.getFirstChild(), rec );
+            }
+        };
+
+        enumeration = new IndexAssertionEnumeration( baseEnumeration, assertion, true );
+        return enumeration;
+    }
+
+
+    /**
+     * Creates an enumeration over a conjunction expression branch node.
+     *
+     * @param node a conjunction expression branch node
+     */
+    private NamingEnumeration<IndexRecord> enumConj( final AndNode node ) throws NamingException
+    {
+        int minIndex = 0;
+        long minValue = Long.MAX_VALUE;
+        long value = Long.MAX_VALUE;
+
+        /*
+         * We scan the child nodes of a branch node searching for the child
+         * expression node with the smallest scan count.  This is the child
+         * we will use for iteration by creating a NamingEnumeration over its
+         * expression.
+         */
+        final List<ExprNode> children = node.getChildren();
+        
+        for ( int ii = 0; ii < children.size(); ii++ )
+        {
+            ExprNode child = children.get( ii );
+            value = ( Long ) child.get( "count" );
+            minValue = Math.min( minValue, value );
+
+            if ( minValue == value )
+            {
+                minIndex = ii;
+            }
+        }
+
+        // Once found we build the child enumeration & the wrapping enum
+        final ExprNode minChild = children.get( minIndex );
+        IndexAssertion assertion = new IndexAssertion()
+        {
+            public boolean assertCandidate( IndexRecord rec ) throws NamingException
+            {
+                for ( int ii = 0; ii < children.size(); ii++ )
+                {
+                    ExprNode child = children.get( ii );
+
+                    // Skip the child (with min scan count) chosen for enum
+                    if ( child == minChild )
+                    {
+                        continue;
+                    }
+                    else if ( !evaluator.evaluate( child, rec ) )
+                    {
+                        return false;
+                    }
+                }
+
+                return true;
+            }
+        };
+
+        // Do recursive call to build child enumeration then wrap and return
+        NamingEnumeration<IndexRecord> underlying = enumerate( minChild );
+        IndexAssertionEnumeration iae;
+        iae = new IndexAssertionEnumeration( underlying, assertion );
+        return iae;
+    }
+
+
+    /**
+     * Returns an enumeration over candidates that satisfy a presence attribute 
+     * value assertion.
+     * 
+     * @param node the presence AVA node
+     * @return an enumeration over the index records matching the AVA
+     * @throws NamingException if there is a failure while accessing the db
+     */
+    private NamingEnumeration<IndexRecord> enumPresence( final PresenceNode node ) throws NamingException
+    {
+        if ( db.hasUserIndexOn( node.getAttribute() ) )
+        {
+            Index idx = db.getExistanceIndex();
+            return idx.listIndices( node.getAttribute() );
+        }
+
+        return nonIndexedScan( node );
+    }
+
+
+    /**
+     * Returns an enumeration over candidates that satisfy a simple greater than
+     * or less than or equal to attribute value assertion.
+     * 
+     * @param node the AVA node
+     * @param isGreater true if >= false if <= is used
+     * @return an enumeration over the index records matching the AVA
+     * @throws NamingException if there is a failure while accessing the db
+     */
+    private NamingEnumeration<IndexRecord> enumGreaterOrLesser( final SimpleNode node, final boolean isGreaterOrLesser ) throws NamingException
+    {
+        if ( db.hasUserIndexOn( node.getAttribute() ) )
+        {
+            Index idx = db.getUserIndex( node.getAttribute() );
+
+            return idx.listIndices( node.getValue(), isGreaterOrLesser );
+        }
+
+        return nonIndexedScan( node );
+    }
+
+
+    /**
+     * Returns an enumeration over candidates that satisfy a simple equality 
+     * attribute value assertion.
+     * 
+     * @param node the equality AVA node
+     * @return an enumeration over the index records matching the AVA
+     * @throws NamingException if there is a failure while accessing the db
+     */
+    private NamingEnumeration<IndexRecord> enumEquality( final EqualityNode node ) throws NamingException
+    {
+        if ( db.hasUserIndexOn( node.getAttribute() ) )
+        {
+            Index idx = db.getUserIndex( node.getAttribute() );
+            return idx.listIndices( node.getValue() );
+        }
+
+        return nonIndexedScan( node );
+    }
+
+
+    /**
+     * Creates a scan over all entries in the database with an assertion to test
+     * for the correct evaluation of a filter expression on a LeafNode.
+     * 
+     * @param node the leaf node to produce a scan over
+     * @return the enumeration over all perspective candidates satisfying expr
+     * @throws NamingException if db access failures result
+     */
+    private NamingEnumeration<IndexRecord> nonIndexedScan( final LeafNode node ) throws NamingException
+    {
+        NamingEnumeration<IndexRecord> underlying = db.getNdnIndex().listIndices();
+        
+        IndexAssertion assertion = new IndexAssertion()
+        {
+            public boolean assertCandidate( IndexRecord record ) throws NamingException
+            {
+                return evaluator.getLeafEvaluator().evaluate( node, record );
+            }
+        };
+
+        return new IndexAssertionEnumeration( underlying, assertion );
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/ExpressionEvaluator.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/ExpressionEvaluator.java
new file mode 100644
index 0000000..76d117d
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/ExpressionEvaluator.java
@@ -0,0 +1,144 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree;
+
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.filter.AndNode;
+import org.apache.directory.shared.ldap.filter.BranchNode;
+import org.apache.directory.shared.ldap.filter.ExprNode;
+import org.apache.directory.shared.ldap.filter.NotNode;
+import org.apache.directory.shared.ldap.filter.OrNode;
+
+
+/**
+ * Top level filter expression evaluator implemenation.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class ExpressionEvaluator implements Evaluator
+{
+    /** Leaf Evaluator flyweight use for leaf filter assertions */
+    private LeafEvaluator leafEvaluator;
+
+
+    // ------------------------------------------------------------------------
+    // C O N S T R U C T O R S
+    // ------------------------------------------------------------------------
+
+    /**
+     * Creates a top level Evaluator where leaves are delegated to a leaf node
+     * evaluator which is already provided.
+     *
+     * @param leafEvaluator handles leaf node evaluation.
+     */
+    public ExpressionEvaluator(LeafEvaluator leafEvaluator)
+    {
+        this.leafEvaluator = leafEvaluator;
+    }
+
+
+    /**
+     * Creates a top level Evaluator where leaves are delegated to a leaf node
+     * evaluator which will be created.
+     *
+     * @param db the database this evaluator operates upon
+     * @param registries the oid reg used for attrID to oid resolution
+     */
+    public ExpressionEvaluator(BTreePartition db, Registries registries )
+    {
+        ScopeEvaluator scopeEvaluator = null;
+        SubstringEvaluator substringEvaluator = null;
+
+        scopeEvaluator = new ScopeEvaluator( db );
+        substringEvaluator = new SubstringEvaluator( db, registries );
+        leafEvaluator = new LeafEvaluator( db, registries, scopeEvaluator, substringEvaluator );
+    }
+
+
+    /**
+     * Gets the leaf evaluator used by this top level expression evaluator.
+     *
+     * @return the leaf evaluator used by this top level expression evaluator
+     */
+    public LeafEvaluator getLeafEvaluator()
+    {
+        return leafEvaluator;
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Evaluator.evaluate() implementation
+    // ------------------------------------------------------------------------
+
+    /**
+     * @see org.apache.directory.server.core.partition.impl.btree.Evaluator#evaluate(ExprNode, IndexRecord)
+     */
+    public boolean evaluate( ExprNode node, IndexRecord record ) throws NamingException
+    {
+        if ( node.isLeaf() )
+        {
+            return leafEvaluator.evaluate( node, record );
+        }
+
+        BranchNode bnode = ( BranchNode ) node;
+
+        if ( bnode instanceof OrNode )
+        {
+            for ( ExprNode child:bnode.getChildren() )
+            {
+                if ( evaluate( child, record ) )
+                {
+                    return true;
+                }
+            }
+
+            return false;
+        }
+        else if ( bnode instanceof AndNode )
+        {
+            for ( ExprNode child:bnode.getChildren() )
+            {
+                if ( !evaluate( child, record ) )
+                {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+        else if ( bnode instanceof NotNode )
+        {
+            if ( null != bnode.getFirstChild() )
+            {
+                return !evaluate( bnode.getFirstChild(), record );
+            }
+
+            throw new NamingException( "Negation has no child: " + node );
+        }
+        else
+        {
+                throw new NamingException( "Unrecognized branch node operator: " + bnode );
+        }
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/LeafEvaluator.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/LeafEvaluator.java
new file mode 100644
index 0000000..5eabfd1
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/LeafEvaluator.java
@@ -0,0 +1,602 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree;
+
+
+import java.util.Comparator;
+import java.util.Iterator;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.NotImplementedException;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.entry.Value;
+import org.apache.directory.shared.ldap.filter.ApproximateNode;
+import org.apache.directory.shared.ldap.filter.EqualityNode;
+import org.apache.directory.shared.ldap.filter.ExprNode;
+import org.apache.directory.shared.ldap.filter.ExtensibleNode;
+import org.apache.directory.shared.ldap.filter.GreaterEqNode;
+import org.apache.directory.shared.ldap.filter.LessEqNode;
+import org.apache.directory.shared.ldap.filter.PresenceNode;
+import org.apache.directory.shared.ldap.filter.ScopeNode;
+import org.apache.directory.shared.ldap.filter.SimpleNode;
+import org.apache.directory.shared.ldap.filter.SubstringNode;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.schema.ByteArrayComparator;
+import org.apache.directory.shared.ldap.schema.MatchingRule;
+import org.apache.directory.shared.ldap.schema.NoOpNormalizer;
+import org.apache.directory.shared.ldap.schema.Normalizer;
+
+
+/**
+ * Evaluates LeafNode assertions on candidates using a database.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class LeafEvaluator implements Evaluator
+{
+    /** equality matching type constant */
+    private static final int EQUALITY_MATCH = 0;
+
+    /** ordering matching type constant */
+    private static final int ORDERING_MATCH = 1;
+
+    /** substring matching type constant */
+    private static final int SUBSTRING_MATCH = 2;
+
+    /** Database used to evaluate leaf with */
+    private BTreePartition db;
+
+    /** Oid Registry used to translate attributeIds to OIDs */
+    private Registries registries;
+
+    /** Substring node evaluator we depend on */
+    private SubstringEvaluator substringEvaluator;
+
+    /** ScopeNode evaluator we depend on */
+    private ScopeEvaluator scopeEvaluator;
+
+
+    /**
+     * Creates a leaf expression node evaluator.
+     *
+     * @param db
+     * @param scopeEvaluator
+     * @param substringEvaluator
+     */
+    public LeafEvaluator( BTreePartition db, Registries registries,
+        ScopeEvaluator scopeEvaluator, SubstringEvaluator substringEvaluator )
+    {
+        this.db = db;
+        this.registries = registries;
+        this.scopeEvaluator = scopeEvaluator;
+        this.substringEvaluator = substringEvaluator;
+    }
+
+
+    public ScopeEvaluator getScopeEvaluator()
+    {
+        return scopeEvaluator;
+    }
+
+
+    public SubstringEvaluator getSubstringEvaluator()
+    {
+        return substringEvaluator;
+    }
+
+
+    /**
+     * Match a filter value against an entry's attribute. An entry's attribute
+     * may have more than one value, and the values may not be normalized. 
+     * @param node
+     * @param attr
+     * @param type
+     * @param normalizer
+     * @param comparator
+     * @return
+     * @throws NamingException
+     */
+    private boolean matchValue( SimpleNode node, EntryAttribute attr, AttributeType type, Normalizer normalizer,
+        Comparator comparator ) throws NamingException
+    {
+        // get the normalized AVA filter value
+        Value<?> filterValue = node.getValue();
+
+        // Check if the attribute normalized value match 
+        // Fast check. If it succeeds, we are done.
+        if ( attr.contains( filterValue ) )
+        {
+            // We are lucky.
+            return true;
+        }
+
+        /*
+         * We need to now iterate through all values because we could not get
+         * a lookup to work.  For each value we normalize and use the comparator
+         * to determine if a match exists.
+         */
+        for ( Value<?> value:attr )
+        {
+            Object normValue = normalizer.normalize( value.get() );
+
+            // TODO Fix DIRSERVER-832
+            if ( 0 == comparator.compare( normValue, filterValue.get() ) )
+            {
+                // The value has been found. get out.
+                return true;
+            }
+        }
+
+        // no match so return false
+        return false;
+    }
+
+
+    /**
+     * Get the entry from the backend, if it's not already into the record
+     */
+    private ServerEntry getEntry( IndexRecord rec ) throws NamingException
+    {
+        // get the attributes associated with the entry 
+        ServerEntry entry = rec.getEntry();
+
+        // resuscitate entry if need be
+        // TODO Is this really needed ? 
+        // How possibly can't we have the entry at this point ?
+        if ( null == entry )
+        {
+            rec.setEntry( db.lookup( ( Long ) rec.getEntryId() ) );
+            entry = rec.getEntry();
+        }
+
+        return entry;
+    }
+
+
+    /**
+     * @see org.apache.directory.server.core.partition.impl.btree.Evaluator#evaluate(ExprNode, IndexRecord)
+     */
+    public boolean evaluate( ExprNode node, IndexRecord record ) throws NamingException
+    {
+        if ( node instanceof ScopeNode )
+        {
+            return scopeEvaluator.evaluate( node, record );
+        }
+
+        if ( node instanceof PresenceNode )
+        {
+            String attrId = ( ( PresenceNode ) node ).getAttribute();
+            return evalPresence( attrId, record );
+        }
+        else if ( node instanceof EqualityNode )
+        {
+            return evalEquality( ( EqualityNode ) node, record );
+        }
+        else if ( node instanceof GreaterEqNode )
+        {
+            return evalGreaterOrLesser( ( SimpleNode ) node, record, SimpleNode.EVAL_GREATER );
+        }
+        else if ( node instanceof LessEqNode )
+        {
+            return evalGreaterOrLesser( ( SimpleNode ) node, record, SimpleNode.EVAL_LESSER );
+        }
+        else if ( node instanceof SubstringNode )
+        {
+            return substringEvaluator.evaluate( node, record );
+        }
+        else if ( node instanceof ExtensibleNode )
+        {
+            throw new NotImplementedException();
+        }
+        else if ( node instanceof ApproximateNode )
+        {
+            return evalEquality( ( ApproximateNode ) node, record );
+        }
+        else
+        {
+            throw new NamingException( "Unrecognized leaf node type: " + node );
+        }
+    }
+
+
+    /**
+     * Evaluates a simple greater than or less than attribute value assertion on
+     * a perspective candidate.
+     * 
+     * @param node the greater than or less than node to evaluate
+     * @param record the IndexRecord of the perspective candidate
+     * @param isGreaterOrLesser true if it is a greater than or equal to comparison,
+     *      false if it is a less than or equal to comparison.
+     * @return the ava evaluation on the perspective candidate
+     * @throws NamingException if there is a database access failure
+     */
+    private boolean evalGreaterOrLesser( SimpleNode node, IndexRecord record, boolean isGreaterOrLesser )
+        throws NamingException
+    {
+        String attrId = node.getAttribute();
+        long id = ( Long ) record.getEntryId();
+
+        if ( db.hasUserIndexOn( attrId ) )
+        {
+            Index idx = db.getUserIndex( attrId );
+
+            if ( isGreaterOrLesser = SimpleNode.EVAL_GREATER )
+            {
+                if ( idx.hasValue( node.getValue(), id, SimpleNode.EVAL_GREATER ) )
+                {
+                    return true;
+                }
+            }
+            else
+            {
+                if ( idx.hasValue( node.getValue(), id, SimpleNode.EVAL_LESSER ) )
+                {
+                    return true;
+                }
+            }
+        }
+
+        // resuscitate entry if need be
+        if ( null == record.getEntry() )
+        {
+            record.setEntry( db.lookup( id ) );
+        }
+
+        // get the attribute associated with the node
+        EntryAttribute attr = record.getEntry().get( 
+                registries.getAttributeTypeRegistry().lookup( node.getAttribute() ) );
+
+        // If we do not have the attribute just return false
+        if ( null == attr )
+        {
+            return false;
+        }
+
+        /*
+         * We need to iterate through all values and for each value we normalize
+         * and use the comparator to determine if a match exists.
+         */
+        Normalizer normalizer = getNormalizer( attrId, ORDERING_MATCH );
+        Comparator comparator = getComparator( attrId, ORDERING_MATCH );
+        Object filterValue = node.getValue();
+
+        /*
+         * Cheaper to not check isGreater in one loop - better to separate
+         * out into two loops which you choose to execute based on isGreater
+         */
+        if ( isGreaterOrLesser == SimpleNode.EVAL_GREATER )
+        {
+            for ( Value<?> value:attr )
+            {
+                Object normValue = normalizer.normalize( value.get() );
+
+                // Found a value that is greater than or equal to the ava value
+                if ( 0 >= comparator.compare( (String)((Value<?>)filterValue).get(), normValue ) )
+                {
+                    return true;
+                }
+            }
+        }
+        else
+        {
+            for ( Value<?> value:attr )
+            {
+                Object normValue = normalizer.normalize( value );
+
+                // Found a value that is less than or equal to the ava value
+                if ( 0 <= comparator.compare( filterValue, normValue ) )
+                {
+                    return true;
+                }
+            }
+        }
+
+        // no match so return false
+        return false;
+    }
+
+
+    /**
+     * Evaluates a simple presence attribute value assertion on a perspective
+     * candidate.
+     * 
+     * @param attrId the name of the attribute tested for presence 
+     * @param rec the IndexRecord of the perspective candidate
+     * @return the ava evaluation on the perspective candidate
+     * @throws NamingException if there is a database access failure
+     */
+    private boolean evalPresence( String attrId, IndexRecord rec ) throws NamingException
+    {
+        // First, check if the attributeType is indexed
+        if ( db.hasUserIndexOn( attrId ) )
+        {
+            Index idx = db.getExistanceIndex();
+
+            // We have a fast find if the entry contains 
+            // this attribute type : as the AT was indexed, we
+            // have a direct access to the entry.
+            if ( idx.hasValue( attrId, rec.getEntryId() ) )
+            {
+                return true;
+            }
+
+            // Fallthrough : we may have some descendant 
+            // attributes in some entries.
+        }
+
+        // get the attributes associated with the entry 
+        ServerEntry entry = getEntry( rec );
+
+        // Of course, if the entry does not contains any attributes
+        // (very unlikely !!!), get out of here
+        // TODO Can this simply happens ???
+        if ( entry == null )
+        {
+            return false;
+        }
+
+        // Now, get the AttributeType associated with the Attribute id
+        AttributeType type = registries.getAttributeTypeRegistry().lookup( 
+                registries.getOidRegistry().getOid( attrId ) );
+
+        // here, we may have some descendants if the attribute is not found
+        if ( entry.get( type ) != null )
+        {
+            // The current entry contains this attribute. We can exit
+            return true;
+        }
+        else
+        {
+            // The attribute was not found in the entry, but it may have
+            // some descendant. Let's chack that
+            if ( registries.getAttributeTypeRegistry().hasDescendants( attrId ) )
+            {
+                // Ok, we have to check for each descendant if pone of 
+                // them is present into the entry
+                Iterator<AttributeType> descendants = registries.getAttributeTypeRegistry().descendants( attrId );
+
+                while ( descendants.hasNext() )
+                {
+                    AttributeType descendant = descendants.next();
+
+                    if ( entry.get( descendant ) != null )
+                    {
+                        // We have found one descendant : exit
+                        return true;
+                    }
+                }
+            }
+
+            // We have checked all the descendant, without success.
+            // Get out, and return a failure status
+            return false;
+        }
+    }
+
+
+    /**
+     * Evaluates a simple equality attribute value assertion on a perspective
+     * candidate.
+     *
+     * @param node the equality node to evaluate
+     * @param rec the IndexRecord of the perspective candidate
+     * @return the ava evaluation on the perspective candidate
+     * @throws NamingException if there is a database access failure
+     */
+    private boolean evalEquality( SimpleNode node, IndexRecord rec ) throws NamingException
+    {
+        String filterAttr = node.getAttribute();
+        Value<?> filterValue = node.getValue();
+
+        // First, check if the attributeType is indexed
+        if ( db.hasUserIndexOn( filterAttr ) )
+        {
+            // Whatever the attribute has some descendants or not,
+            // we will take a chance to get the associated entry
+            // from the index.
+            Index idx = db.getUserIndex( filterAttr );
+
+            if ( idx.hasValue( filterValue, rec.getEntryId() ) )
+            {
+                return true;
+            }
+            else
+            {
+                // FallThrough : we may have some descendant attributes
+                // which values are equal to the filter value.
+            }
+        }
+
+        // Get the normalizer and comparator for this attributeType
+        Normalizer normalizer = getNormalizer( filterAttr, EQUALITY_MATCH );
+        Comparator<?> comparator = getComparator( filterAttr, EQUALITY_MATCH );
+
+        /*
+         * Get the attribute and if it is not set in rec then resusitate it
+         * from the master table and set it in rec for use later if at all.
+         * Before iterating through all values for a match check to see if the
+         * AVA value is contained or the normalized form of the AVA value is 
+         * contained.
+         */
+        // get the attributes associated with the entry 
+        ServerEntry entry = getEntry( rec );
+
+        // Of course, if the entry does not contains any attributes
+        // (very unlikely !!!), get out of here
+        // TODO Can this simply happens ???
+        if ( entry == null )
+        {
+            return false;
+        }
+
+        // get the attribute associated with the node 
+        AttributeType type = registries.getAttributeTypeRegistry().lookup( filterAttr );
+        EntryAttribute attr = entry.get( type );
+
+        if ( attr != null )
+        {
+            // We have found the attribute into the entry.
+            // Check if the normalized value is present
+            if ( attr.contains( filterValue ) )
+            {
+                // We are lucky.
+                return true;
+            }
+            // Check if the unormalized value match
+            else if ( matchValue( node, attr, type, normalizer, comparator ) )
+            {
+                return true;
+            }
+            else
+            {
+                // Fallthrough : we may have a descendant attribute containing the value
+            }
+        }
+        else
+        {
+            // Fallthrough : we may have a descendant attribute containing the value
+        }
+
+        // If we do not have the attribute, loop through the descendant
+        // May be the node Attribute has descendant ?
+        if ( registries.getAttributeTypeRegistry().hasDescendants( filterAttr ) )
+        {
+            Iterator<AttributeType> descendants = registries.getAttributeTypeRegistry().descendants( filterAttr );
+
+            while ( descendants.hasNext() )
+            {
+                AttributeType descendant = descendants.next();
+
+                attr = entry.get( descendant );
+
+                if ( null == attr )
+                {
+                    continue;
+                }
+                else
+                {
+                    // check if the normalized value is present
+                    if ( attr.contains( filterValue ) )
+                    {
+                        return true;
+                    }
+                    // Now check the unormalized value
+                    else if ( matchValue( node, attr, type, normalizer, comparator ) )
+                    {
+                        return true;
+                    }
+                }
+            }
+        }
+
+        return false;
+    }
+
+
+    /**
+     * Gets the comparator for equality matching.
+     *
+     * @param attrId the attribute identifier
+     * @return the comparator for equality matching
+     * @throws NamingException if there is a failure
+     */
+    @SuppressWarnings("unchecked")
+    private Comparator<?> getComparator( String attrId, int matchType ) throws NamingException
+    {
+        MatchingRule mrule = getMatchingRule( attrId, matchType );
+
+        if ( mrule == null )
+        {
+            return ByteArrayComparator.INSTANCE;
+        }
+
+        return mrule.getComparator();
+    }
+
+
+    /**
+     * Gets the normalizer for equality matching.
+     *
+     * @param attrId the attribute identifier
+     * @return the normalizer for equality matching
+     * @throws NamingException if there is a failure
+     */
+    private Normalizer getNormalizer( String attrId, int matchType ) throws NamingException
+    {
+        MatchingRule mrule = getMatchingRule( attrId, matchType );
+
+        if ( mrule == null )
+        {
+            return NoOpNormalizer.INSTANCE;
+        }
+
+        return mrule.getNormalizer();
+    }
+
+
+    /**
+     * Gets the matching rule for an attributeType.
+     *
+     * @param attrId the attribute identifier
+     * @return the matching rule
+     * @throws NamingException if there is a failure
+     */
+    private MatchingRule getMatchingRule( String attrId, int matchType ) throws NamingException
+    {
+        MatchingRule mrule = null;
+        String oid = registries.getOidRegistry().getOid( attrId );
+        AttributeType type = registries.getAttributeTypeRegistry().lookup( oid );
+
+        switch ( matchType )
+        {
+            case ( EQUALITY_MATCH ):
+                mrule = type.getEquality();
+                break;
+
+            case ( SUBSTRING_MATCH ):
+                mrule = type.getSubstr();
+                break;
+
+            case ( ORDERING_MATCH ):
+                mrule = type.getOrdering();
+                break;
+
+            default:
+                throw new NamingException( "Unknown match type: " + matchType );
+        }
+
+        // if there is no ordering or substring matchingRule for the attributeType 
+        // then we fallback to use the equality matching rule to determine the
+        // normalizer and comparator to use.  This possible since 
+        // comparators are redundant and enable ordering to occur.  So if
+        // we can we will use the comparator of the ordering matchingRule
+        // and if not we default to the equality matchingRule's comparator.
+        if ( ( matchType != EQUALITY_MATCH ) && ( mrule == null ) )
+        {
+            return getMatchingRule( attrId, EQUALITY_MATCH );
+        }
+
+        return mrule;
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/NoOpOptimizer.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/NoOpOptimizer.java
new file mode 100644
index 0000000..200e934
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/NoOpOptimizer.java
@@ -0,0 +1,71 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.shared.ldap.filter.BranchNode;
+import org.apache.directory.shared.ldap.filter.ExprNode;
+
+/**
+ * A do nothing optimizer which labels all nodes with <code>
+ * BigInteger.valueOf( Integer.MAX_VALUE ) </code>, instead of actually 
+ * taking scan counts.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class NoOpOptimizer implements Optimizer
+{
+    /** the maximum size for a count Integer.MAX_VALUE as a BigInteger */
+    private static final Long MAX = Long.MAX_VALUE;
+    
+    public void annotate( ExprNode node ) throws NamingException
+    {
+        if ( node.isLeaf() )
+        {
+            node.set( "count", MAX );
+            return;
+        }
+        
+        BranchNode bnode = ( BranchNode ) node;
+        if ( bnode.getChildren().size() == 0 )
+        {
+            bnode.set( "count", MAX );
+            return;
+        }
+        
+        final int limit = bnode.getChildren().size();
+        for ( int ii = 0; ii < limit; ii++ )
+        {
+            ExprNode child = bnode.getChildren().get( ii );
+            if ( child.isLeaf() )
+            {
+                child.set( "count", MAX );
+            }
+            else
+            {
+                annotate( child );
+            }
+        }
+
+        bnode.set( "count", MAX );
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/Optimizer.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/Optimizer.java
new file mode 100644
index 0000000..3509031d
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/Optimizer.java
@@ -0,0 +1,48 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree;
+
+
+import javax.naming.NamingException;
+
+import org.apache.directory.shared.ldap.filter.ExprNode;
+
+
+/**
+ * An optimizer applies heuristics to determine best execution path to a search
+ * filter based on scan counts within database indices.  It annotates the nodes
+ * of an expression subtree by setting a "count" key in the node.  Its goal is
+ * to annotate nodes with counts to indicate which nodes to iterate over thereby
+ * minimizing the number cycles in a search.  The SearchEngine relies on these
+ * count markers to determine the appropriate path.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public interface Optimizer
+{
+    /**
+     * Annotates the expression node tree for optimized traversal metrics.
+     *
+     * @param node the root of the expression node tree
+     * @throws NamingException if there are failures while optimizing
+     */
+    void annotate( ExprNode node ) throws NamingException;
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/ScopeEnumerator.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/ScopeEnumerator.java
new file mode 100644
index 0000000..ee6be4c
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/ScopeEnumerator.java
@@ -0,0 +1,234 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree;
+
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.SearchControls;
+
+import org.apache.directory.shared.ldap.filter.ExprNode;
+import org.apache.directory.shared.ldap.filter.ScopeNode;
+import org.apache.directory.shared.ldap.util.SingletonEnumeration;
+
+
+/**
+ * Enumerates candidates based on scope.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class ScopeEnumerator implements Enumerator
+{
+    /** Database used to enumerate based on scope */
+    private BTreePartition db = null;
+    /** Filter scope expression evaluator */
+    private ScopeEvaluator evaluator = null;
+
+
+    public ScopeEnumerator(BTreePartition db, ScopeEvaluator evaluator)
+    {
+        this.db = db;
+        this.evaluator = evaluator;
+    }
+
+
+    /**
+     * Builds an enumeration over all entries that satisfy the constraints of 
+     * the scope assertion node.
+     *
+     * @param node the scope node 
+     * @return the candidates that are within scope
+     * @throws NamingException if any system indices fail
+     * @see org.apache.directory.server.core.partition.impl.btree.Enumerator#enumerate(ExprNode)
+     */
+    public NamingEnumeration<IndexRecord> enumerate( ExprNode node ) throws NamingException
+    {
+        final ScopeNode snode = ( ScopeNode ) node;
+        final Long id = db.getEntryId( snode.getBaseDn() );
+
+        switch ( snode.getScope() )
+        {
+            case ( SearchControls.OBJECT_SCOPE  ):
+                final IndexRecord record = new IndexRecord();
+                record.setEntryId( id );
+                record.setIndexKey( snode.getBaseDn() );
+                return new SingletonEnumeration<IndexRecord>( record );
+                
+            case ( SearchControls.ONELEVEL_SCOPE  ):
+                return enumerateChildren( snode.getBaseDn(), snode.getDerefAliases().isDerefInSearching() );
+            
+            case ( SearchControls.SUBTREE_SCOPE  ):
+                return enumerateDescendants( snode );
+            
+            default:
+                throw new NamingException( "Unrecognized search scope!" );
+        }
+    }
+
+
+    /**
+     * Constructs an enumeration over all entries within one level scope even
+     * when aliases are enabled while searching.
+     * 
+     * @param dn the base dn
+     * @param deref whether or not we dereference while searching
+     * @return the enumeration of all entries in direct or alias extended one 
+     * level scope to the base
+     * @throws NamingException if any failures occur while accessing system
+     * indices.
+     */
+    private NamingEnumeration<IndexRecord> enumerateChildren( String dn, boolean deref ) throws NamingException
+    {
+        Index idx = db.getHierarchyIndex();
+        final Long id = db.getEntryId( dn );
+        final NamingEnumeration<IndexRecord> children = idx.listIndices( id );
+
+        /*
+         * If alias dereferencing is not enabled while searching then we just
+         * return the enumeration of the base entry's children.
+         */
+        if ( !deref )
+        {
+            return children;
+        }
+
+        /* ====================================================================
+         * From here on Dereferencing while searching is enabled
+         * ====================================================================
+         *
+         * Dereferencing in search is enabled so we need to wrap the child
+         * listing with an assertion enumeration to weed out aliases that will
+         * not be returned.  Next we need to compose an enumeration which 
+         * combines the list of non-alias child entries with those entries that
+         * are brought into one level scope by aliases.
+         */
+
+        // List all entries brought into one level scope at base by aliases
+        idx = db.getOneAliasIndex();
+        NamingEnumeration aliasIntroduced = idx.listIndices( id );
+
+        // Still need to use assertion enum to weed out aliases
+        NamingEnumeration nonAliasChildren = new IndexAssertionEnumeration( children, new AssertNotAlias() );
+
+        // Combine both into one enumeration
+        NamingEnumeration[] all =
+            { nonAliasChildren, aliasIntroduced };
+        return new DisjunctionEnumeration( all );
+    }
+
+
+    /**
+     * Constructs an enumeration over all entries within subtree scope even
+     * when aliases are enabled while searching.
+     * 
+     * @param node the scope node
+     * @return the enumeration of all entries in direct or alias extended 
+     * subtree scope to the base
+     * @throws NamingException if any failures occur while accessing system
+     * indices.
+     */
+    private NamingEnumeration<IndexRecord> enumerateDescendants( final ScopeNode node ) throws NamingException
+    {
+        Index idx = null;
+
+        /*
+         * If we do not dereference while searching then we simply return any
+         * entry that is not a descendant of the base.
+         */
+        if ( !node.getDerefAliases().isDerefInSearching() )
+        {
+            // Gets a NamingEnumeration over all elements
+            idx = db.getNdnIndex();
+            NamingEnumeration<IndexRecord> underlying = idx.listIndices();
+            return new IndexAssertionEnumeration( underlying, new AssertDescendant( node ) );
+        }
+
+        // Create an assertion to assert or evaluate an expression
+        IndexAssertion assertion = new IndexAssertion()
+        {
+            public boolean assertCandidate( IndexRecord rec ) throws NamingException
+            {
+                return evaluator.evaluate( node, rec );
+            }
+        };
+
+        // Gets a NamingEnumeration over all elements
+        idx = db.getNdnIndex();
+        NamingEnumeration<IndexRecord> underlying = idx.listIndices();
+        return new IndexAssertionEnumeration( underlying, assertion );
+    }
+
+    /**
+     * Asserts an entry is a descendant.
+     */
+    class AssertDescendant implements IndexAssertion
+    {
+        /** Scope node with base and alias info */
+        private final ScopeNode scope;
+
+
+        /**
+         * Creates a assertion using a ScopeNode to determine the search base.
+         *
+         * @param node the scope node with search base
+         */
+        AssertDescendant(final ScopeNode node)
+        {
+            scope = node;
+        }
+
+
+        /**
+         * Returns true if the candidate with id is a descendant of the base, 
+         * false otherwise.
+         * 
+         * @see org.apache.directory.server.core.partition.impl.btree.IndexAssertion#assertCandidate(IndexRecord)
+         */
+        public boolean assertCandidate( IndexRecord record ) throws NamingException
+        {
+            String dn = db.getEntryDn( (Long)record.getEntryId() );
+            return dn.endsWith( scope.getBaseDn() );
+        }
+    }
+
+    /**
+     * Asserts an entry is NOT an alias.
+     */
+    class AssertNotAlias implements IndexAssertion
+    {
+        /**
+         * Returns true if the candidate is not an alias, false otherwise.
+         * 
+         * @see IndexAssertion#assertCandidate(IndexRecord)
+         */
+        public boolean assertCandidate( IndexRecord record ) throws NamingException
+        {
+            Index aliasIdx = db.getAliasIndex();
+
+            if ( null == aliasIdx.reverseLookup( record.getEntryId() ) )
+            {
+                return true;
+            }
+
+            return false;
+        }
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/ScopeEvaluator.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/ScopeEvaluator.java
new file mode 100644
index 0000000..40b6078
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/ScopeEvaluator.java
@@ -0,0 +1,208 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree;
+
+
+import javax.naming.NamingException;
+import javax.naming.directory.SearchControls;
+
+import org.apache.directory.shared.ldap.filter.ExprNode;
+import org.apache.directory.shared.ldap.filter.ScopeNode;
+import org.apache.directory.shared.ldap.message.AliasDerefMode;
+
+
+/**
+ * Evaluates ScopeNode assertions on candidates using a database.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class ScopeEvaluator implements Evaluator
+{
+    /** Database used to evaluate scope with */
+    private BTreePartition db;
+
+
+    /**
+     * Creates a scope node evaluator for search expressions.
+     *
+     * @param db the database used to evaluate scope node
+     */
+    public ScopeEvaluator(BTreePartition db)
+    {
+        this.db = db;
+    }
+
+
+    /**
+     * @see org.apache.directory.server.core.partition.impl.btree.Evaluator#evaluate(ExprNode, org.apache.directory.server.core.partition.impl.btree.IndexRecord)
+     */
+    public boolean evaluate( ExprNode node, IndexRecord record ) throws NamingException
+    {
+        ScopeNode snode = ( ScopeNode ) node;
+
+        switch ( snode.getScope() )
+        {
+            case ( SearchControls.OBJECT_SCOPE  ):
+                String dn = db.getEntryDn( (Long)record.getEntryId() );
+                return dn.equals( snode.getBaseDn() );
+                
+            case ( SearchControls.ONELEVEL_SCOPE  ):
+                return assertOneLevelScope( snode, (Long)record.getEntryId() );
+            
+            case ( SearchControls.SUBTREE_SCOPE  ):
+                return assertSubtreeScope( snode, (Long)record.getEntryId() );
+            
+            default:
+                throw new NamingException( "Unrecognized search scope!" );
+        }
+    }
+
+
+    /**
+     * Asserts whether or not a candidate has one level scope while taking
+     * alias dereferencing into account.
+     * 
+     * @param node the scope node containing the base and alias handling mode
+     * @param id the candidate to assert which can be any db entry's id
+     * @return true if the candidate is within one level scope whether or not
+     * alias dereferencing is enabled.
+     * @throws NamingException if the index lookups fail.
+     */
+    public boolean assertSubtreeScope( final ScopeNode node, final Long id ) throws NamingException
+    {
+        String dn = db.getEntryDn( id );
+        AliasDerefMode mode = node.getDerefAliases();
+        Object baseId = db.getEntryId( node.getBaseDn() );
+        boolean isDescendant = dn.endsWith( node.getBaseDn() );
+
+        /*
+         * The candidate id could be any entry in the db.  If search 
+         * dereferencing is not enabled then we return the results of the 
+         * descendant test.
+         */
+        if ( !mode.isDerefInSearching() )
+        {
+            return isDescendant;
+        }
+
+        /*
+         * From here down alias dereferencing is enabled.  We determine if the
+         * candidate id is an alias, if so we reject it since aliases should
+         * not be returned.
+         */
+        Index idx = db.getAliasIndex();
+
+        if ( null != idx.reverseLookup( id ) )
+        {
+            return false;
+        }
+
+        /*
+         * The candidate is NOT an alias at this point.  So if it is a 
+         * descendant we just return it since it is in normal subtree scope.
+         */
+        if ( isDescendant )
+        {
+            return true;
+        }
+
+        /*
+         * At this point the candidate is not a descendant and it is not an 
+         * alias.  We need to check if the candidate is in extended subtree 
+         * scope by performing a lookup on the subtree alias index.  This index 
+         * stores a tuple mapping the baseId to the ids of objects brought 
+         * into subtree scope of the base by an alias: 
+         * 
+         * ( baseId, aliasedObjId )
+         * 
+         * If the candidate id is an object brought into subtree scope then 
+         * the lookup returns true accepting the candidate.  Otherwise the 
+         * candidate is rejected with a false return because it is not in scope.
+         */
+        idx = db.getSubAliasIndex();
+        
+        return idx.hasValue( baseId, id );
+    }
+
+
+    /**
+     * Asserts whether or not a candidate has one level scope while taking
+     * alias dereferencing into account.
+     * 
+     * @param node the scope node containing the base and alias handling mode
+     * @param id the candidate to assert which can be any db entry's id 
+     * @return true if the candidate is within one level scope whether or not
+     * alias dereferencing is enabled.
+     * @throws NamingException if the index lookups fail.
+     */
+    public boolean assertOneLevelScope( final ScopeNode node, final Long id ) throws NamingException
+    {
+        AliasDerefMode mode = node.getDerefAliases();
+        Object baseId = db.getEntryId( node.getBaseDn() );
+        Index idx = db.getHierarchyIndex();
+        boolean isChild = idx.hasValue( baseId, id );
+
+        /*
+         * The candidate id could be any entry in the db.  If search 
+         * dereferencing is not enabled then we return the results of the child 
+         * test. 
+         */
+        if ( !mode.isDerefInSearching() )
+        {
+            return isChild;
+        }
+
+        /*
+         * From here down alias dereferencing is enabled.  We determine if the
+         * candidate id is an alias, if so we reject it since aliases should
+         * not be returned.
+         */
+        idx = db.getAliasIndex();
+        
+        if ( null != idx.reverseLookup( id ) )
+        {
+            return false;
+        }
+
+        /*
+         * The candidate is NOT an alias at this point.  So if it is a child we
+         * just return it since it is in normal one level scope.
+         */
+        if ( isChild )
+        {
+            return true;
+        }
+
+        /*
+         * At this point the candidate is not a child and it is not an alias.
+         * We need to check if the candidate is in extended one level scope by 
+         * performing a lookup on the one level alias index.  This index stores
+         * a tuple mapping the baseId to the id of objects brought into the 
+         * one level scope of the base by an alias: ( baseId, aliasedObjId )
+         * If the candidate id is an object brought into one level scope then 
+         * the lookup returns true accepting the candidate.  Otherwise the 
+         * candidate is rejected with a false return because it is not in scope.
+         */
+        idx = db.getOneAliasIndex();
+        
+        return idx.hasValue( baseId, id );
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/SearchEngine.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/SearchEngine.java
new file mode 100644
index 0000000..8fd6fa3
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/SearchEngine.java
@@ -0,0 +1,100 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree;
+
+
+import org.apache.directory.shared.ldap.constants.JndiPropertyConstants;
+import org.apache.directory.shared.ldap.filter.ExprNode;
+import org.apache.directory.shared.ldap.message.AliasDerefMode;
+
+import javax.naming.Name;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.SearchControls;
+
+
+/**
+ * Given a search filter and a scope the search engine identifies valid
+ * candidate entries returning their ids.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public interface SearchEngine
+{
+    /**
+     * @todo put this in the right place
+     * The alias dereferencing mode key for JNDI providers 
+     */
+    String ALIASMODE_KEY = JndiPropertyConstants.JNDI_LDAP_DAP_DEREF_ALIASES;
+    /** 
+     * @todo put this in the right place
+     * The alias dereferencing mode value for JNDI providers 
+     */
+    String ALWAYS = "always";
+    /** 
+     * @todo put this in the right place
+     * The alias dereferencing mode value for JNDI providers 
+     */
+    String NEVER = "never";
+    /** 
+     * @todo put this in the right place
+     * The alias dereferencing mode value for JNDI providers 
+     */
+    String FINDING = "finding";
+    /** 
+     * @todo put this in the right place
+     * The alias dereferencing mode value for JNDI providers 
+     */
+    String SEARCHING = "searching";
+
+
+    /**
+     * Gets the optimizer for this DefaultSearchEngine.
+     *
+     * @return the optimizer
+     */
+    Optimizer getOptimizer();
+
+
+    /**
+     * Conducts a search on a database.
+     * 
+     * @param base the search base
+     * @param aliasDerefMode the alias dereferencing mode to use
+     * @param filter the search filter AST root
+     * @param searchCtls the JNDI search controls
+     * @return enumeration over SearchResults
+     * @throws NamingException if the search fails
+     */
+    NamingEnumeration<IndexRecord> search( Name base, AliasDerefMode aliasDerefMode, ExprNode filter,
+                              SearchControls searchCtls ) throws NamingException;
+
+
+    /**
+     * Evaluates a filter on an entry with a id.
+     * 
+     * @param filter the filter root AST node
+     * @param id the id of the entry to test
+     * @return true if the filter passes the entry, false otherwise
+     * @throws NamingException if something goes wrong while accessing the db
+     */
+    boolean evaluate( ExprNode filter, Long id ) throws NamingException;
+}
\ No newline at end of file
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/SubstringEnumerator.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/SubstringEnumerator.java
new file mode 100644
index 0000000..4f4a009
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/SubstringEnumerator.java
@@ -0,0 +1,149 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree;
+
+
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+
+import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
+import org.apache.directory.shared.ldap.filter.ExprNode;
+import org.apache.directory.shared.ldap.filter.SubstringNode;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.schema.MatchingRule;
+import org.apache.directory.shared.ldap.schema.Normalizer;
+
+
+/**
+ * Enumerator that creates a NamingEnumeration over the set of candidates that 
+ * satisfy a substring filter expression.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class SubstringEnumerator implements Enumerator
+{
+    /** Database used */
+    private final BTreePartition db;
+    
+    /** Evaluator used is an Avalon dependent object */
+    private final SubstringEvaluator evaluator;
+    
+    /** the attribute type registry */
+    private final AttributeTypeRegistry attributeTypeRegistry;
+
+
+    /**
+     * Creates a SubstringEnumerator for a database.
+     *
+     * @param db the database
+     * @param evaluator a substring evaluator
+     */
+    public SubstringEnumerator(BTreePartition db, AttributeTypeRegistry attributeTypeRegistry,
+        SubstringEvaluator evaluator)
+    {
+        this.db = db;
+        this.evaluator = evaluator;
+        this.attributeTypeRegistry = attributeTypeRegistry;
+    }
+
+
+    // ------------------------------------------------------------------------
+    // SubstringEnumerator Methods
+    // ------------------------------------------------------------------------
+
+    /**
+     * @see Enumerator#enumerate(
+     * org.apache.directory.shared.ldap.filter.ExprNode)
+     */
+    public NamingEnumeration<IndexRecord> enumerate( final ExprNode node ) throws NamingException
+    {
+        Pattern regex = null;
+        Index idx = null;
+        final SubstringNode snode = ( SubstringNode ) node;
+        AttributeType type = attributeTypeRegistry.lookup( snode.getAttribute() );
+
+        MatchingRule matchingRule = type.getSubstr();
+
+        if ( matchingRule == null )
+        {
+            matchingRule = type.getEquality();
+        }
+        
+        Normalizer normalizer = matchingRule.getNormalizer();
+
+        if ( db.hasUserIndexOn( snode.getAttribute() ) )
+        {
+            /*
+             * Build out regex in this block so we do not do it twice in the
+             * evaluator if there is no index on the attribute of the substr ava
+             */
+            try
+            {
+                regex = snode.getRegex( normalizer );
+            }
+            catch ( PatternSyntaxException e )
+            {
+                NamingException ne = new NamingException( "SubstringNode '" + node + "' had incorrect syntax" );
+                ne.setRootCause( e );
+                throw ne;
+            }
+
+            /*
+             * Get the user index and return an index enumeration using the the
+             * compiled regular expression.  Try to constrain even further if
+             * an initial term is available in the substring expression.
+             */
+            idx = db.getUserIndex( snode.getAttribute() );
+            if ( null == snode.getInitial() )
+            {
+                return idx.listIndices( regex );
+            }
+            else
+            {
+                return idx.listIndices( regex, snode.getInitial() );
+            }
+        }
+
+        /*
+         * From this point on we are dealing with an enumeration over entries
+         * based on an attribute that is not indexed.  We have no choice but
+         * to perform a full table scan but need to leverage an index for the
+         * underlying enumeration.  We know that all entries are listed under 
+         * the ndn index and so this will enumerate over all entries as the 
+         * underlying enumeration.  An evaluator in an assertion is used to 
+         * constrain the result set.
+         */
+        NamingEnumeration<IndexRecord> underlying = db.getNdnIndex().listIndices();
+        
+        IndexAssertion assertion = new IndexAssertion()
+        {
+            public boolean assertCandidate( final IndexRecord record ) throws NamingException
+            {
+                return evaluator.evaluate( node, record );
+            }
+        };
+
+        return new IndexAssertionEnumeration( underlying, assertion );
+    }
+}
\ No newline at end of file
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/SubstringEvaluator.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/SubstringEvaluator.java
new file mode 100644
index 0000000..9bd519d
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/SubstringEvaluator.java
@@ -0,0 +1,247 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree;
+
+
+import java.util.Iterator;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.entry.Value;
+import org.apache.directory.shared.ldap.filter.ExprNode;
+import org.apache.directory.shared.ldap.filter.SubstringNode;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.schema.MatchingRule;
+import org.apache.directory.shared.ldap.schema.Normalizer;
+
+
+/**
+ * Evaluates substring filter assertions on an entry.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class SubstringEvaluator implements Evaluator
+{
+    /** Database used while evaluating candidates */
+    private BTreePartition db;
+    
+    /** Oid Registry used to translate attributeIds to OIDs */
+    private Registries registries;
+    
+
+    /**
+     * Creates a new SubstringEvaluator for substring expressions.
+     *
+     * @param db the database this evaluator uses
+     * @param registries the OID registry for name to OID mapping
+     */
+    public SubstringEvaluator( BTreePartition db, Registries registries )
+    {
+        this.db = db;
+        this.registries = registries;
+    }
+
+
+    /**
+     * @see org.apache.directory.server.core.partition.impl.btree.Evaluator#evaluate(ExprNode, IndexRecord)
+     */
+    public boolean evaluate( ExprNode node, IndexRecord record ) throws NamingException
+    {
+        Pattern regex = null;
+        SubstringNode snode = ( SubstringNode ) node;
+        String filterAttribute = snode.getAttribute();
+        
+        String oid = registries.getOidRegistry().getOid( filterAttribute );
+        AttributeType type = registries.getAttributeTypeRegistry().lookup( oid );
+
+        MatchingRule rule = type.getSubstr();
+        
+        if ( rule == null )
+        {
+            rule = type.getEquality();
+        }
+
+        Normalizer normalizer = rule.getNormalizer();
+
+        if ( db.hasUserIndexOn( filterAttribute ) )
+        {
+            Index idx = db.getUserIndex( filterAttribute );
+
+            /*
+             * Note that this is using the reverse half of the index giving a 
+             * considerable performance improvement on this kind of operation.
+             * Otherwise we would have to scan the entire index if there were
+             * no reverse lookups.
+             */
+
+            NamingEnumeration entries = idx.listReverseIndices( record.getEntryId() );
+
+            // compile the regular expression to search for a matching attribute
+            try
+            {
+                regex = snode.getRegex( normalizer );
+            }
+            catch ( PatternSyntaxException pse )
+            {
+                NamingException ne = new NamingException( "SubstringNode '" + node + "' had " + "incorrect syntax" );
+                ne.setRootCause( pse );
+                throw ne;
+            }
+
+            // cycle through the attribute values testing for a match
+            while ( entries.hasMore() )
+            {
+                IndexRecord rec = ( IndexRecord ) entries.next();
+
+                // once match is found cleanup and return true
+                if ( regex.matcher( ( String ) rec.getIndexKey() ).matches() )
+                {
+                    entries.close();
+                    return true;
+                }
+            }
+
+            // we fell through so a match was not found - assertion was false.
+            //return false;
+        }
+
+        // --------------------------------------------------------------------
+        // Index not defined beyond this point
+        // --------------------------------------------------------------------
+
+        ServerEntry entry = record.getEntry();
+        
+        // resuscitate the entry if it has not been and set entry in IndexRecord
+        if ( null == entry )
+        {
+            ServerEntry attrs = db.lookup( (Long)record.getEntryId() );
+            record.setEntry( attrs );
+            entry = record.getEntry();
+        }
+
+        // Of course, if the entry does not contains any attributes
+        // (very unlikely !!!), get out of here
+        // TODO Can this simply happens ???
+        if ( entry == null )
+        {
+            return false;
+        }
+
+        // get the attribute
+        EntryAttribute attr = entry.get( type );
+
+        // if the attribute does not exist just return false
+        if ( attr != null)
+        {
+            // compile the regular expression to search for a matching attribute
+            try
+            {
+                regex = snode.getRegex( normalizer );
+            }
+            catch ( PatternSyntaxException pse )
+            {
+                NamingException ne = new NamingException( "SubstringNode '" + node + "' had " + "incorrect syntax" );
+                ne.setRootCause( pse );
+                throw ne;
+            }
+
+            /*
+             * Cycle through the attribute values testing normalized version 
+             * obtained from using the substring matching rule's normalizer.
+             * The test uses the comparator obtained from the appropriate 
+             * substring matching rule.
+             */
+            for( Value<?> value:attr )
+            {
+                String normValue = ( String ) normalizer.normalize( value.get() );
+    
+                // Once match is found cleanup and return true
+                if ( regex.matcher( normValue ).matches() )
+                {
+                    return true;
+                }
+            }
+            
+            // Fall through as we didn't find any matching value for this attribute.
+            // We will have to check in the potential descendant, if any.
+        }
+        
+        // If we do not have the attribute, loop through the descendant
+        // May be the node Attribute has descendant ?
+        if ( registries.getAttributeTypeRegistry().hasDescendants( filterAttribute ) )
+        {
+            Iterator<AttributeType> descendants = registries.getAttributeTypeRegistry().descendants( filterAttribute );
+
+            while ( descendants.hasNext() )
+            {
+                AttributeType descendant = descendants.next();
+
+                attr = entry.get( descendant );
+
+                if ( null == attr )
+                {
+                    continue;
+                }
+                else
+                {
+                    // compile the regular expression to search for a matching attribute
+                    try
+                    {
+                        regex = snode.getRegex( normalizer );
+                    }
+                    catch ( PatternSyntaxException pse )
+                    {
+                        NamingException ne = new NamingException( "SubstringNode '" + node + "' had " + "incorrect syntax" );
+                        ne.setRootCause( pse );
+                        throw ne;
+                    }
+
+                    /*
+                     * Cycle through the attribute values testing normalized version 
+                     * obtained from using the substring matching rule's normalizer.
+                     * The test uses the comparator obtained from the appropriate 
+                     * substring matching rule.
+                     */
+                    for ( Value<?> value:attr )
+                    {
+                        String normValue = ( String ) normalizer.normalize( value.get() );
+            
+                        // Once match is found cleanup and return true
+                        if ( regex.matcher( normValue ).matches() )
+                        {
+                            return true;
+                        }
+                    }
+                }
+            }
+        }
+        
+        
+        // we fell through so a match was not found - assertion was false.
+        return false;
+    }
+}
\ No newline at end of file
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/gui/ASTNode.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/gui/ASTNode.java
new file mode 100644
index 0000000..c059d86
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/gui/ASTNode.java
@@ -0,0 +1,139 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree.gui;
+
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
+
+import javax.swing.tree.TreeNode;
+
+import org.apache.directory.shared.ldap.filter.BranchNode;
+import org.apache.directory.shared.ldap.filter.ExprNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * A node representing an entry.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class ASTNode implements TreeNode
+{
+    private static final Logger log = LoggerFactory.getLogger( ASTNode.class );
+
+    private final ASTNode parent;
+    private final ExprNode exprNode;
+    private final List<ASTNode> children;
+
+
+    public ASTNode(ASTNode parent, ExprNode exprNode)
+    {
+        children = new ArrayList<ASTNode>( 2 );
+        this.exprNode = exprNode;
+
+        if ( parent == null )
+        {
+            this.parent = this;
+        }
+        else
+        {
+            this.parent = parent;
+        }
+
+        try
+        {
+            if ( exprNode.isLeaf() )
+            {
+                return;
+            }
+
+            BranchNode branch = ( BranchNode ) exprNode;
+            
+            for ( ExprNode child:branch.getChildren() )
+            {
+                children.add( new ASTNode( this, child ) );
+            }
+        }
+        catch ( Exception e )
+        {
+            // FIXME What exception could be thrown here?
+            log.warn( "Unexpected exception: parent=" + parent + ", exprNode=" + exprNode, e );
+        }
+    }
+
+
+    public Enumeration<ASTNode> children()
+    {
+        return Collections.enumeration( children );
+    }
+
+
+    public boolean getAllowsChildren()
+    {
+        return !exprNode.isLeaf();
+    }
+
+
+    public TreeNode getChildAt( int childIndex )
+    {
+        return children.get( childIndex );
+    }
+
+
+    public int getChildCount()
+    {
+        return children.size();
+    }
+
+
+    public int getIndex( TreeNode child )
+    {
+        return children.indexOf( child );
+    }
+
+
+    public TreeNode getParent()
+    {
+        return parent;
+    }
+
+
+    public boolean isLeaf()
+    {
+        return children.size() <= 0;
+    }
+
+
+    public String toString()
+    {
+        return exprNode.toString();
+    }
+
+
+    public ExprNode getExprNode()
+    {
+        return exprNode;
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/gui/AboutDialog.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/gui/AboutDialog.java
new file mode 100644
index 0000000..1e60bca
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/gui/AboutDialog.java
@@ -0,0 +1,180 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree.gui;
+
+
+import java.awt.BorderLayout;
+import java.awt.FlowLayout;
+import java.awt.Frame;
+import java.awt.GridBagLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.WindowEvent;
+
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JTextArea;
+import javax.swing.SwingConstants;
+
+
+/**
+ * An about dialog for the introspector GUI.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class AboutDialog extends JDialog
+{
+    private static final long serialVersionUID = 3257853194544952884L;
+
+    private String title = "About";
+    private String product = "Vendor: Apache Software Foundation";
+    private String version = "Version: 0.1";
+    private String copyright = "Copyright (c) 2003";
+    private String comments = "This is the btree partition introspector.\nParitions "
+        + "can be analyzed by using this tool to inspect\nthe state of system " + "indices and entry attributes.";
+    private JPanel contentPane = new JPanel();
+    private JLabel prodLabel = new JLabel();
+    private JLabel verLabel = new JLabel();
+    private JLabel copLabel = new JLabel();
+    private JTextArea commentField = new JTextArea();
+    private JPanel btnPanel = new JPanel();
+    private JButton okButton = new JButton();
+    private JLabel image = new JLabel();
+    private BorderLayout formLayout = new BorderLayout();
+    private GridBagLayout contentPaneLayout = new GridBagLayout();
+    private FlowLayout btnPaneLayout = new FlowLayout();
+    private JPanel jPanel1 = new JPanel();
+    private JPanel jPanel2 = new JPanel();
+
+
+    /** Creates new About Dialog */
+    public AboutDialog(Frame parent, boolean modal)
+    {
+        super( parent, modal );
+        initGUI();
+        pack();
+    }
+
+
+    public AboutDialog()
+    {
+        super();
+        setModal( true );
+        initGUI();
+        pack();
+    }
+
+
+    /** This method is called from within the constructor to initialize the dialog. */
+    private void initGUI()
+    {
+        addWindowListener( new java.awt.event.WindowAdapter()
+        {
+            public void windowClosing( WindowEvent evt )
+            {
+                closeDialog( evt );
+            }
+        } );
+        getContentPane().setLayout( formLayout );
+        contentPane.setLayout( contentPaneLayout );
+        contentPane.setBorder( javax.swing.BorderFactory.createTitledBorder( javax.swing.BorderFactory
+            .createLineBorder( new java.awt.Color( 153, 153, 153 ), 1 ), "BTree Partition Inspector",
+            javax.swing.border.TitledBorder.LEADING, javax.swing.border.TitledBorder.TOP, new java.awt.Font(
+                "SansSerif", 0, 14 ), new java.awt.Color( 60, 60, 60 ) ) );
+        prodLabel.setText( product );
+        prodLabel.setAlignmentX( 0.5f );
+        contentPane.add( prodLabel, new java.awt.GridBagConstraints( java.awt.GridBagConstraints.RELATIVE,
+            java.awt.GridBagConstraints.RELATIVE, java.awt.GridBagConstraints.REMAINDER, 1, 0.0, 0.0,
+            java.awt.GridBagConstraints.NORTHWEST, java.awt.GridBagConstraints.NONE, new java.awt.Insets( 5, 5, 0, 0 ),
+            5, 0 ) );
+        verLabel.setText( version );
+        contentPane.add( verLabel, new java.awt.GridBagConstraints( java.awt.GridBagConstraints.RELATIVE,
+            java.awt.GridBagConstraints.RELATIVE, java.awt.GridBagConstraints.REMAINDER, 1, 0.0, 0.0,
+            java.awt.GridBagConstraints.NORTHWEST, java.awt.GridBagConstraints.NONE, new java.awt.Insets( 5, 5, 0, 0 ),
+            0, 0 ) );
+        copLabel.setText( copyright );
+        contentPane.add( copLabel, new java.awt.GridBagConstraints( java.awt.GridBagConstraints.RELATIVE,
+            java.awt.GridBagConstraints.RELATIVE, java.awt.GridBagConstraints.REMAINDER, 1, 0.0, 0.0,
+            java.awt.GridBagConstraints.NORTHWEST, java.awt.GridBagConstraints.NONE, new java.awt.Insets( 5, 5, 0, 0 ),
+            0, 0 ) );
+        commentField.setBackground( getBackground() );
+        commentField.setForeground( copLabel.getForeground() );
+        commentField.setFont( copLabel.getFont() );
+        commentField.setText( comments );
+        commentField.setEditable( false );
+        commentField.setBorder( null );
+        contentPane.add( commentField, new java.awt.GridBagConstraints( java.awt.GridBagConstraints.RELATIVE,
+            java.awt.GridBagConstraints.RELATIVE, java.awt.GridBagConstraints.REMAINDER, 3, 0.0, 1.0,
+            java.awt.GridBagConstraints.NORTHWEST, java.awt.GridBagConstraints.BOTH, new java.awt.Insets( 5, 5, 5, 0 ),
+            0, 0 ) );
+
+        image.setText( "ApacheDS" );
+        image.setVerticalTextPosition( SwingConstants.BOTTOM );
+        image.setHorizontalTextPosition( SwingConstants.CENTER );
+        image.setIcon( new ImageIcon( AboutDialog.class.getResource( "server.gif" ) ) );
+        image.setHorizontalAlignment( javax.swing.SwingConstants.CENTER );
+        image.setMinimumSize( new java.awt.Dimension( 120, 44 ) );
+        image.setMaximumSize( new java.awt.Dimension( 120, 44 ) );
+        image.setAlignmentX( 0.5f );
+        image.setBorder( javax.swing.BorderFactory.createEmptyBorder() );
+        image.setPreferredSize( new java.awt.Dimension( 98, 44 ) );
+        image.setSize( new java.awt.Dimension( 120, 200 ) );
+        btnPanel.setLayout( btnPaneLayout );
+        okButton.setText( "OK" );
+        okButton.addActionListener( new ActionListener()
+        {
+            public void actionPerformed( ActionEvent e )
+            {
+                setVisible( false );
+                dispose();
+            }
+        } );
+        btnPanel.add( okButton );
+        getContentPane().add( image, BorderLayout.WEST );
+        getContentPane().add( contentPane, BorderLayout.CENTER );
+        getContentPane().add( btnPanel, BorderLayout.SOUTH );
+        getContentPane().add( jPanel1, java.awt.BorderLayout.NORTH );
+        getContentPane().add( jPanel2, java.awt.BorderLayout.EAST );
+        setTitle( title );
+        setResizable( false );
+        setFont( new java.awt.Font( "Dialog", java.awt.Font.BOLD, 12 ) );
+        formLayout.setHgap( 15 );
+        jPanel1.setMinimumSize( new java.awt.Dimension( 10, 30 ) );
+        jPanel1.setPreferredSize( new java.awt.Dimension( 10, 30 ) );
+        jPanel1.setSize( new java.awt.Dimension( 564, 35 ) );
+        jPanel2.setMinimumSize( new java.awt.Dimension( 72, 165 ) );
+        jPanel2.setPreferredSize( new java.awt.Dimension( 80, 165 ) );
+        jPanel2.setSize( new java.awt.Dimension( 72, 170 ) );
+        jPanel2.setMaximumSize( new java.awt.Dimension( 80, 165 ) );
+    }
+
+
+    /** Closes the dialog */
+    private void closeDialog( WindowEvent evt )
+    {
+        evt.getWindow();
+        setVisible( false );
+        dispose();
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/gui/AddEntryDialog.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/gui/AddEntryDialog.java
new file mode 100644
index 0000000..c57ce1a
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/gui/AddEntryDialog.java
@@ -0,0 +1,297 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree.gui;
+
+
+import java.awt.Frame;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+
+import javax.swing.DefaultCellEditor;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JMenuItem;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JPopupMenu;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.JTextField;
+
+import org.apache.directory.server.core.entry.DefaultServerEntry;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * Allows for operations on entries.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class AddEntryDialog extends JDialog implements ActionListener
+{
+    private static final Logger log = LoggerFactory.getLogger( AddEntryDialog.class );
+
+    private static final long serialVersionUID = 3544671793504663604L;
+
+    private JPanel m_namePnl = new JPanel();
+    private JPanel m_attrPnl = new JPanel();
+    private JPanel m_buttonPnl = new JPanel();
+    private JPanel m_rdnPnl = new JPanel();
+    private JPanel m_dnPnl = new JPanel();
+    private JLabel m_rdnLbl = new JLabel();
+    private JComboBox m_rdnChoice = new JComboBox();
+    private JTextField m_dnText = new JTextField();
+    private JScrollPane m_attrScrollPnl = new JScrollPane();
+    private JTable m_attrTbl = new JTable();
+    private JButton m_doneBut = new JButton();
+    private JButton m_cancelBut = new JButton();
+    private JPopupMenu m_popup;
+
+    private ServerEntry childEntry = null;
+
+
+    /**
+     * Creates new entry addition dialog.
+     *  
+     * @param parent the parent frame
+     * @param modal whether or not to go modal on the dialog
+     */
+    public AddEntryDialog(Frame parent, boolean modal, Registries registries )
+    {
+        super( parent, modal );
+        childEntry = new DefaultServerEntry( registries );
+        childEntry.put( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC );
+        initGUI();
+    }
+
+
+    /** 
+     * This method is called from within the constructor to initialize the form.
+     */
+    private void initGUI()
+    {
+        addWindowListener( new java.awt.event.WindowAdapter()
+        {
+            public void windowClosing( java.awt.event.WindowEvent evt )
+            {
+                closeDialog();
+            }
+        } );
+        pack();
+        setBounds( new java.awt.Rectangle( 0, 0, 447, 364 ) );
+        setTitle( "Add New Entry" );
+        getContentPane().setLayout( new java.awt.GridBagLayout() );
+        getContentPane().add(
+            m_namePnl,
+            new java.awt.GridBagConstraints( 0, 0, 1, 1, 1.0, 0.0, java.awt.GridBagConstraints.NORTH,
+                java.awt.GridBagConstraints.HORIZONTAL, new java.awt.Insets( 5, 5, 5, 5 ), 0, 0 ) );
+        getContentPane().add(
+            m_attrPnl,
+            new java.awt.GridBagConstraints( 0, 1, 1, 1, 1.0, 1.0, java.awt.GridBagConstraints.CENTER,
+                java.awt.GridBagConstraints.BOTH, new java.awt.Insets( 5, 5, 5, 5 ), 0, 0 ) );
+        getContentPane().add(
+            m_buttonPnl,
+            new java.awt.GridBagConstraints( 0, 2, 1, 1, 1.0, 0.05, java.awt.GridBagConstraints.CENTER,
+                java.awt.GridBagConstraints.HORIZONTAL, new java.awt.Insets( 0, 0, 0, 20 ), 0, 0 ) );
+        m_namePnl.setBorder( javax.swing.BorderFactory.createTitledBorder( javax.swing.BorderFactory.createLineBorder(
+            new java.awt.Color( 153, 153, 153 ), 1 ), "Naming", javax.swing.border.TitledBorder.LEADING,
+            javax.swing.border.TitledBorder.TOP, new java.awt.Font( "SansSerif", 0, 14 ), new java.awt.Color( 60, 60,
+                60 ) ) );
+        m_namePnl.setLayout( new javax.swing.BoxLayout( m_namePnl, javax.swing.BoxLayout.Y_AXIS ) );
+        m_namePnl.add( m_rdnPnl );
+        m_namePnl.add( m_dnPnl );
+        m_rdnLbl.setText( "Rdn:" );
+        m_rdnPnl.setLayout( new java.awt.GridBagLayout() );
+        m_rdnPnl.add( m_rdnChoice, new java.awt.GridBagConstraints( 1, 0, 1, 1, 1.0, 0.0,
+            java.awt.GridBagConstraints.WEST, java.awt.GridBagConstraints.NONE, new java.awt.Insets( 0, 10, 0, 0 ), 0,
+            0 ) );
+        m_rdnPnl.add( m_rdnLbl, new java.awt.GridBagConstraints( 0, 0, 1, 1, 0.0, 0.0,
+            java.awt.GridBagConstraints.WEST, java.awt.GridBagConstraints.NONE, new java.awt.Insets( 0, 10, 0, 0 ), 0,
+            0 ) );
+        m_dnPnl.setLayout( new java.awt.GridBagLayout() );
+        m_dnPnl.add( m_dnText, new java.awt.GridBagConstraints( 1, 0, 1, 1, 1.0, 0.0, java.awt.GridBagConstraints.WEST,
+            java.awt.GridBagConstraints.HORIZONTAL, new java.awt.Insets( 0, 5, 0, 0 ), 0, 0 ) );
+        m_dnText.setText( "unknown" );
+        m_dnText.setEditable( false );
+        m_dnText.setBorder( javax.swing.BorderFactory.createTitledBorder( javax.swing.BorderFactory.createLineBorder(
+            new java.awt.Color( 153, 153, 153 ), 1 ), "Dn", javax.swing.border.TitledBorder.LEADING,
+            javax.swing.border.TitledBorder.TOP, new java.awt.Font( "SansSerif", 0, 14 ), new java.awt.Color( 60, 60,
+                60 ) ) );
+        m_rdnChoice.setEditable( true );
+        m_rdnChoice.setMaximumRowCount( 6 );
+
+        m_rdnChoice.setSize( new java.awt.Dimension( 130, 24 ) );
+        m_attrPnl.setLayout( new java.awt.BorderLayout() );
+        m_attrPnl.add( m_attrScrollPnl, java.awt.BorderLayout.CENTER );
+        m_attrScrollPnl.getViewport().add( m_attrTbl );
+        m_attrTbl.setBounds( new java.awt.Rectangle( 78, 60, 32, 32 ) );
+        m_attrTbl.setCellSelectionEnabled( true );
+
+        m_doneBut.setText( "Done" );
+        m_buttonPnl.setLayout( new java.awt.FlowLayout( java.awt.FlowLayout.RIGHT, 10, 5 ) );
+        m_buttonPnl.add( m_doneBut );
+        m_buttonPnl.add( m_cancelBut );
+        m_cancelBut.setText( "Cancel" );
+        m_cancelBut.addActionListener( new ActionListener()
+        {
+            public void actionPerformed( ActionEvent a_evt )
+            {
+                closeDialog();
+            }
+        } );
+        m_attrScrollPnl.setBorder( javax.swing.BorderFactory.createTitledBorder( javax.swing.BorderFactory
+            .createLineBorder( new java.awt.Color( 153, 153, 153 ), 1 ), "Attributes",
+            javax.swing.border.TitledBorder.LEADING, javax.swing.border.TitledBorder.TOP, new java.awt.Font(
+                "SansSerif", 0, 14 ), new java.awt.Color( 60, 60, 60 ) ) );
+
+        m_attrTbl.setModel( new AttributesTableModel( childEntry, null, null, true ) );
+
+        //
+        // Build the table's popup menu
+        //
+
+        m_popup = new JPopupMenu();
+        JMenuItem l_menuItem = new JMenuItem( "Add" );
+        l_menuItem.setActionCommand( "Add" );
+        l_menuItem.addActionListener( this );
+        m_popup.add( l_menuItem );
+        l_menuItem = new JMenuItem( "Delete" );
+        l_menuItem.setActionCommand( "Delete" );
+        l_menuItem.addActionListener( this );
+        m_popup.add( l_menuItem );
+
+        // Add listener to components that can bring up popup menus.
+        m_attrTbl.addMouseListener( new PopupListener() );
+
+        setUpEditor( m_attrTbl );
+    }
+
+
+    private void setUpEditor( JTable l_table )
+    {
+        //Set up the editor for the integer cells.
+        final JTextField l_textField = new JTextField();
+
+        DefaultCellEditor l_textEditor = new DefaultCellEditor( l_textField )
+        {
+            private static final long serialVersionUID = 3256727286014554675L;
+
+
+            //Override DefaultCellEditor's getCellEditorValue method
+            //to return an Integer, not a String:
+            public Object getCellEditorValue()
+            {
+                if ( log.isDebugEnabled() )
+                {
+                    log.debug( "Editor returning '" + l_textField.getText() + "'" );
+                }
+                
+                return l_textField.getText();
+            }
+        };
+
+        l_table.setDefaultEditor( String.class, l_textEditor );
+    }
+
+    class PopupListener extends MouseAdapter
+    {
+        public void mousePressed( MouseEvent e )
+        {
+            maybeShowPopup( e );
+        }
+
+
+        public void mouseReleased( MouseEvent e )
+        {
+            maybeShowPopup( e );
+        }
+
+
+        private void maybeShowPopup( MouseEvent e )
+        {
+            if ( e.isPopupTrigger() )
+            {
+                m_popup.show( e.getComponent(), e.getX(), e.getY() );
+            }
+        }
+    }
+
+
+    public void actionPerformed( ActionEvent a_event )
+    {
+        String l_cmd = a_event.getActionCommand();
+        AttributesTableModel l_model = ( AttributesTableModel ) m_attrTbl.getModel();
+        int l_row = m_attrTbl.getSelectedRow();
+        log.debug( l_cmd );
+
+        if ( l_row >= l_model.getRowCount() || l_row < 0 )
+        {
+            JOptionPane.showMessageDialog( this, "Row needs to be selected to apply operation" );
+        }
+
+        if ( l_cmd.equals( "Add" ) )
+        {
+            l_model.insert( l_row, "xxxx", "xxxx" );
+        }
+        else if ( l_cmd.equals( "Delete" ) )
+        {
+            l_model.delete( l_row );
+        }
+        else
+        {
+            JOptionPane.showMessageDialog( this, "Unrecognized action - abandoning action processing." );
+        }
+    }
+
+
+    /** Closes the dialog */
+    private void closeDialog()
+    {
+        setVisible( false );
+        dispose();
+    }
+
+
+    public void setParentDn( String dn )
+    {
+        m_dnText.setText( dn );
+    }
+
+
+    public ServerEntry getChildEntry()
+    {
+        return childEntry;
+    }
+
+
+    public String getChildDn()
+    {
+        return m_dnText.getText();
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/gui/AnnotatedFilterTreeDialog.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/gui/AnnotatedFilterTreeDialog.java
new file mode 100644
index 0000000..3cabc6c
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/gui/AnnotatedFilterTreeDialog.java
@@ -0,0 +1,144 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree.gui;
+
+
+import java.awt.Frame;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.WindowEvent;
+
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTextArea;
+import javax.swing.JTree;
+import javax.swing.tree.TreeModel;
+
+
+/**
+ * Dialog for showing annotated filter trees.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class AnnotatedFilterTreeDialog extends JDialog
+{
+    private static final long serialVersionUID = 3690476917916513074L;
+    private JPanel jPanel1 = new JPanel();
+    private JTree jTree1 = new JTree();
+    private JPanel jPanel2 = new JPanel();
+    private JPanel jPanel3 = new JPanel();
+    private JTextArea jTextArea1 = new JTextArea();
+    private JScrollPane jScrollPane1 = new JScrollPane();
+    private JButton jButton1 = new JButton();
+
+
+    /** Creates new form JDialog */
+    public AnnotatedFilterTreeDialog(Frame parent, boolean modal)
+    {
+        super( parent, modal );
+        initGUI();
+    }
+
+
+    /** This method is called from within the constructor to initialize the form. */
+    private void initGUI()
+    {
+        addWindowListener( new java.awt.event.WindowAdapter()
+        {
+            public void windowClosing( java.awt.event.WindowEvent evt )
+            {
+                closeDialog( evt );
+            }
+        } );
+        pack();
+        getContentPane().setLayout( new java.awt.GridBagLayout() );
+        getContentPane().add(
+            jPanel1,
+            new java.awt.GridBagConstraints( 0, 0, 1, 1, 1.0, 0.1, java.awt.GridBagConstraints.NORTH,
+                java.awt.GridBagConstraints.BOTH, new java.awt.Insets( 10, 5, 5, 5 ), 0, 0 ) );
+        getContentPane().add(
+            jPanel2,
+            new java.awt.GridBagConstraints( 0, 1, 1, 1, 1.0, 0.8, java.awt.GridBagConstraints.CENTER,
+                java.awt.GridBagConstraints.BOTH, new java.awt.Insets( 5, 5, 5, 5 ), 0, 0 ) );
+        getContentPane().add(
+            jPanel3,
+            new java.awt.GridBagConstraints( 0, 2, 1, 1, 1.0, 0.1, java.awt.GridBagConstraints.SOUTH,
+                java.awt.GridBagConstraints.HORIZONTAL, new java.awt.Insets( 0, 0, 0, 0 ), 0, 0 ) );
+        jPanel1.setLayout( new java.awt.BorderLayout( 10, 10 ) );
+        jPanel1.setBorder( javax.swing.BorderFactory.createTitledBorder( javax.swing.BorderFactory.createLineBorder(
+            new java.awt.Color( 153, 153, 153 ), 1 ), "Search Filter", javax.swing.border.TitledBorder.LEADING,
+            javax.swing.border.TitledBorder.TOP, new java.awt.Font( "SansSerif", 0, 14 ), new java.awt.Color( 60, 60,
+                60 ) ) );
+        jPanel1.add( jTextArea1, java.awt.BorderLayout.CENTER );
+        jScrollPane1.getViewport().add( jTree1 );
+        jTree1.setBounds( new java.awt.Rectangle( 238, 142, 82, 80 ) );
+        jTextArea1.setText( "" );
+        jTextArea1.setEditable( false );
+        setBounds( new java.awt.Rectangle( 0, 0, 485, 414 ) );
+        jPanel2.setLayout( new java.awt.BorderLayout() );
+        jPanel2.setBorder( javax.swing.BorderFactory.createTitledBorder( javax.swing.BorderFactory.createLineBorder(
+            new java.awt.Color( 153, 153, 153 ), 1 ), "Filter Expression Tree",
+            javax.swing.border.TitledBorder.LEADING, javax.swing.border.TitledBorder.TOP, new java.awt.Font(
+                "SansSerif", 0, 14 ), new java.awt.Color( 60, 60, 60 ) ) );
+        jPanel2.add( jScrollPane1, java.awt.BorderLayout.CENTER );
+        jButton1.setText( "Done" );
+        jButton1.setActionCommand( "Done" );
+        jButton1.addActionListener( new ActionListener()
+        {
+            public void actionPerformed( ActionEvent a_event )
+            {
+                AnnotatedFilterTreeDialog.this.setVisible( false );
+                AnnotatedFilterTreeDialog.this.dispose();
+            }
+        } );
+        jButton1.setHorizontalAlignment( javax.swing.SwingConstants.CENTER );
+        jButton1.setAlignmentX( 0.5f );
+        jButton1.setHorizontalTextPosition( javax.swing.SwingConstants.CENTER );
+        jPanel3.setPreferredSize( new java.awt.Dimension( 79, 41 ) );
+        jPanel3.setMinimumSize( new java.awt.Dimension( 79, 41 ) );
+        jPanel3.setSize( new java.awt.Dimension( 471, 35 ) );
+        jPanel3.setToolTipText( "" );
+        jPanel3.add( jButton1 );
+    }
+
+
+    /** Closes the dialog */
+    private void closeDialog( WindowEvent evt )
+    {
+        evt.getWindow();
+        setVisible( false );
+        dispose();
+    }
+
+
+    public void setModel( TreeModel a_model )
+    {
+        this.jTree1.setModel( a_model );
+    }
+
+
+    public void setFilter( String a_filter )
+    {
+        this.jTextArea1.setText( a_filter );
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/gui/AttributesTableModel.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/gui/AttributesTableModel.java
new file mode 100644
index 0000000..1c2dcbd
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/gui/AttributesTableModel.java
@@ -0,0 +1,269 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree.gui;
+
+
+import java.util.ArrayList;
+
+import javax.swing.table.AbstractTableModel;
+
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.entry.Value;
+
+
+/**
+ * A general purpose table model for entry attributes.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class AttributesTableModel extends AbstractTableModel
+{
+    private static final long serialVersionUID = 3256443603340310841L;
+    /** name for the key column */
+    public static final String KEY_COL = "Keys";
+    /** name for the values column */
+    public static final String VAL_COL = "Values";
+
+    /** list of attribute ids */
+    private final transient ArrayList<Object> keyList;
+    /** list of attribute values */
+    private final transient ArrayList<Object> valList;
+
+    /** the attributes for the entry */
+    private final ServerEntry entry;
+    /** the unique id of the entry  */
+    private final Long id;
+    /** the distinguished name of the entry */
+    private final String dn;
+    /** whether or not the model is mutable */
+    private boolean isMutable = true;
+
+
+    /**
+     * Creates a table model for entry attributes.
+     *
+     * @param entry the entry to create a model for
+     * @param id the id for the entry
+     * @param dn the distinguished name of the entry
+     * @param isMutable whether or not the model can be changed
+     */
+    public AttributesTableModel( ServerEntry entry, Long id, String dn, boolean isMutable)
+    {
+        this.dn = dn;
+        this.id = id;
+        this.entry = entry;
+        this.isMutable = isMutable;
+
+        int rowCount = 0;
+
+        for ( EntryAttribute attribute:entry )
+        {
+            String attrId = attribute.getId();
+            rowCount = rowCount + entry.get( attrId ).size();
+        }
+
+        keyList = new ArrayList<Object>( rowCount );
+        valList = new ArrayList<Object>( rowCount );
+
+        for ( EntryAttribute attribute:entry )
+        {
+            String key = attribute.getId();
+
+            for ( Value<?> value:attribute )
+            {
+                keyList.add( attribute.getId() );
+                valList.add( value.get() );
+            }
+        }
+    }
+
+
+    /**
+     * @see AbstractTableModel#getColumnName(int)
+     */
+    public String getColumnName( int col )
+    {
+        if ( col == 0 )
+        {
+            return KEY_COL;
+        }
+        else if ( col == 1 )
+        {
+            return VAL_COL;
+        }
+        else
+        {
+            throw new RuntimeException( "There can only be 2 columns at index " + "0 and at 1" );
+        }
+    }
+
+
+    /**
+     * @see javax.swing.table.AbstractTableModel#getRowCount()
+     */
+    public int getRowCount()
+    {
+        return keyList.size();
+    }
+
+
+    /**
+     * @see javax.swing.table.AbstractTableModel#getColumnCount()
+     */
+    public int getColumnCount()
+    {
+        return 2;
+    }
+
+
+    /**
+     * @see AbstractTableModel#getColumnClass(int)
+     */
+    public Class<String> getColumnClass( int c )
+    {
+        return String.class;
+    }
+
+
+    /**
+     * @see AbstractTableModel#isCellEditable(int, int)
+     */
+    public boolean isCellEditable( int row, int col )
+    {
+        return isMutable;
+    }
+
+
+    /**
+     * @see AbstractTableModel#getValueAt(int, int)
+     */
+    public Object getValueAt( int row, int col )
+    {
+        if ( row >= keyList.size() )
+        {
+            return ( "NULL" );
+        }
+
+        if ( getColumnName( col ).equals( KEY_COL ) )
+        {
+            return keyList.get( row );
+        }
+        else if ( getColumnName( col ).equals( VAL_COL ) )
+        {
+            return valList.get( row );
+        }
+        else
+        {
+            throw new RuntimeException( "You didn't correctly set col names" );
+        }
+    }
+
+
+    /**
+     * @see AbstractTableModel#setValueAt(Object, int, int)
+     */
+    public void setValue( Object val, int row, int col )
+    {
+        ArrayList<Object> list = null;
+
+        if ( col > 1 || col < 0 )
+        {
+            return;
+        }
+        else if ( col == 0 )
+        {
+            list = keyList;
+        }
+        else
+        {
+            list = valList;
+        }
+
+        if ( row >= keyList.size() )
+        {
+            return;
+        }
+
+        list.set( row, val );
+        fireTableCellUpdated( row, col );
+    }
+
+
+    /**
+     * Gets the distinguished name of the entry.
+     *
+     * @return the distinguished name of the entry
+     */
+    public String getEntryDn()
+    {
+        return dn;
+    }
+
+
+    /**
+     * Gets the unique id for the entry.
+     *
+     * @return the unique id for the entry
+     */
+    public Long getEntryId()
+    {
+        return id;
+    }
+
+
+    /**
+     * Deletes a row within the table model.
+     *
+     * @param row the row index to delete
+     */
+    public void delete( int row )
+    {
+        if ( row >= keyList.size() || row < 0 )
+        {
+            return;
+        }
+
+        keyList.remove( row );
+        valList.remove( row );
+        fireTableRowsDeleted( row, row );
+    }
+
+
+    /**
+     * Inserts an attribute key/value into the table model.
+     *
+     * @param row the row index to insert into
+     * @param key the key of the attr to insert
+     * @param val the value of the attr to insert
+     */
+    public void insert( int row, Object key, Object val )
+    {
+        if ( row >= keyList.size() || row < 0 )
+        {
+            return;
+        }
+
+        keyList.add( row, key );
+        valList.add( row, val );
+        fireTableRowsInserted( row, row );
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/gui/EntryDialog.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/gui/EntryDialog.java
new file mode 100644
index 0000000..af93db5
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/gui/EntryDialog.java
@@ -0,0 +1,187 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree.gui;
+
+
+import java.awt.Frame;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.naming.directory.Attributes;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.JTextField;
+
+
+/**
+ * Allows for operations on entries.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class EntryDialog extends JDialog
+{
+    private static final long serialVersionUID = 3761684611092001592L;
+
+    private JPanel m_namePnl = new JPanel();
+    private JPanel m_attrPnl = new JPanel();
+    private JPanel m_buttonPnl = new JPanel();
+    private JPanel m_rdnPnl = new JPanel();
+    private JPanel m_dnPnl = new JPanel();
+    private JLabel m_rdnLbl = new JLabel();
+    private JComboBox m_rdnChoice = new JComboBox();
+    private JTextField m_dnText = new JTextField();
+    private JScrollPane m_attrScrollPnl = new JScrollPane();
+    private JTable m_attrTbl = new JTable();
+    private JButton m_doneBut = new JButton();
+    private JButton m_cancelBut = new JButton();
+
+
+    //    private String m_opMode = "Add" ;
+    //    private String m_dn ;
+    //    private String m_rdn ;
+    //    private Attributes m_entry ;
+
+    /**
+     * Creates new form JDialog
+     *  
+     * @param parent
+     * @param modal
+     */
+    public EntryDialog( Frame parent, boolean modal )
+    {
+        super( parent, modal );
+        initGUI();
+    }
+
+
+    /** 
+     * This method is called from within the constructor to initialize the form.
+     */
+    private void initGUI()
+    {
+        addWindowListener( new java.awt.event.WindowAdapter()
+        {
+            public void windowClosing( java.awt.event.WindowEvent evt )
+            {
+                closeDialog();
+            }
+        } );
+        pack();
+        setBounds( new java.awt.Rectangle( 0, 0, 447, 364 ) );
+        setTitle( "Entry Dialog" );
+        getContentPane().setLayout( new java.awt.GridBagLayout() );
+        getContentPane().add(
+            m_namePnl,
+            new java.awt.GridBagConstraints( 0, 0, 1, 1, 1.0, 0.0, java.awt.GridBagConstraints.NORTH,
+                java.awt.GridBagConstraints.HORIZONTAL, new java.awt.Insets( 5, 5, 5, 5 ), 0, 0 ) );
+        getContentPane().add(
+            m_attrPnl,
+            new java.awt.GridBagConstraints( 0, 1, 1, 1, 1.0, 1.0, java.awt.GridBagConstraints.CENTER,
+                java.awt.GridBagConstraints.BOTH, new java.awt.Insets( 5, 5, 5, 5 ), 0, 0 ) );
+        getContentPane().add(
+            m_buttonPnl,
+            new java.awt.GridBagConstraints( 0, 2, 1, 1, 1.0, 0.05, java.awt.GridBagConstraints.CENTER,
+                java.awt.GridBagConstraints.HORIZONTAL, new java.awt.Insets( 0, 0, 0, 20 ), 0, 0 ) );
+        m_namePnl.setBorder( javax.swing.BorderFactory.createTitledBorder( javax.swing.BorderFactory.createLineBorder(
+            new java.awt.Color( 153, 153, 153 ), 1 ), "Naming", javax.swing.border.TitledBorder.LEADING,
+            javax.swing.border.TitledBorder.TOP, new java.awt.Font( "SansSerif", 0, 14 ), new java.awt.Color( 60, 60,
+                60 ) ) );
+        m_namePnl.setLayout( new javax.swing.BoxLayout( m_namePnl, javax.swing.BoxLayout.Y_AXIS ) );
+        m_namePnl.add( m_rdnPnl );
+        m_namePnl.add( m_dnPnl );
+        m_rdnLbl.setText( "Rdn:" );
+        m_rdnPnl.setLayout( new java.awt.GridBagLayout() );
+        m_rdnPnl.add( m_rdnChoice, new java.awt.GridBagConstraints( 1, 0, 1, 1, 1.0, 0.0,
+            java.awt.GridBagConstraints.WEST, java.awt.GridBagConstraints.NONE, new java.awt.Insets( 0, 10, 0, 0 ), 0,
+            0 ) );
+        m_rdnPnl.add( m_rdnLbl, new java.awt.GridBagConstraints( 0, 0, 1, 1, 0.0, 0.0,
+            java.awt.GridBagConstraints.WEST, java.awt.GridBagConstraints.NONE, new java.awt.Insets( 0, 10, 0, 0 ), 0,
+            0 ) );
+        m_dnPnl.setLayout( new java.awt.GridBagLayout() );
+        m_dnPnl.add( m_dnText, new java.awt.GridBagConstraints( 1, 0, 1, 1, 1.0, 0.0, java.awt.GridBagConstraints.WEST,
+            java.awt.GridBagConstraints.HORIZONTAL, new java.awt.Insets( 0, 5, 0, 0 ), 0, 0 ) );
+        m_dnText.setText( "unknown" );
+        m_dnText.setEditable( false );
+        m_dnText.setBorder( javax.swing.BorderFactory.createTitledBorder( javax.swing.BorderFactory.createLineBorder(
+            new java.awt.Color( 153, 153, 153 ), 1 ), "Dn", javax.swing.border.TitledBorder.LEADING,
+            javax.swing.border.TitledBorder.TOP, new java.awt.Font( "SansSerif", 0, 14 ), new java.awt.Color( 60, 60,
+                60 ) ) );
+        m_rdnChoice.setEditable( true );
+        m_rdnChoice.setMaximumRowCount( 6 );
+
+        m_rdnChoice.setSize( new java.awt.Dimension( 130, 24 ) );
+        m_attrPnl.setLayout( new java.awt.BorderLayout() );
+        m_attrPnl.add( m_attrScrollPnl, java.awt.BorderLayout.CENTER );
+        m_attrScrollPnl.getViewport().add( m_attrTbl );
+        m_attrTbl.setBounds( new java.awt.Rectangle( 78, 60, 32, 32 ) );
+        m_attrTbl.setEditingColumn( 1 );
+        m_attrTbl.setCellSelectionEnabled( true );
+        m_doneBut.setText( "Done" );
+        m_buttonPnl.setLayout( new java.awt.FlowLayout( java.awt.FlowLayout.RIGHT, 10, 5 ) );
+        m_buttonPnl.add( m_doneBut );
+        m_buttonPnl.add( m_cancelBut );
+        m_cancelBut.setText( "Cancel" );
+        m_cancelBut.addActionListener( new ActionListener()
+        {
+            public void actionPerformed( ActionEvent a_evt )
+            {
+                closeDialog();
+            }
+        } );
+        m_attrScrollPnl.setBorder( javax.swing.BorderFactory.createTitledBorder( javax.swing.BorderFactory
+            .createLineBorder( new java.awt.Color( 153, 153, 153 ), 1 ), "Attributes",
+            javax.swing.border.TitledBorder.LEADING, javax.swing.border.TitledBorder.TOP, new java.awt.Font(
+                "SansSerif", 0, 14 ), new java.awt.Color( 60, 60, 60 ) ) );
+    }
+
+
+    /** Closes the dialog */
+    private void closeDialog()
+    {
+        setVisible( false );
+        dispose();
+    }
+
+
+    public void setDn( String a_dn )
+    {
+        //        m_dn = a_dn ;
+        m_dnText.setText( a_dn );
+    }
+
+
+    public void setRdn( String a_rdn )
+    {
+        //        m_rdn = a_rdn ;
+        // m_rdnChoice.setSelectedItem(  ) ;
+    }
+
+
+    public void setEntry( Attributes a_entry )
+    {
+        //        m_entry = a_entry ;
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/gui/EntryNode.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/gui/EntryNode.java
new file mode 100644
index 0000000..ca47c06
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/gui/EntryNode.java
@@ -0,0 +1,223 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree.gui;
+
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.swing.tree.TreeNode;
+
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.partition.impl.btree.BTreePartition;
+import org.apache.directory.server.core.partition.impl.btree.IndexRecord;
+import org.apache.directory.server.core.partition.impl.btree.SearchEngine;
+import org.apache.directory.shared.ldap.filter.ExprNode;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+
+/**
+ * A node representing an entry.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class EntryNode implements TreeNode
+{
+    private final BTreePartition partition;
+    private final EntryNode parent;
+    private final ServerEntry entry;
+    private final ArrayList<TreeNode> children;
+    private final Long id;
+
+
+    public EntryNode(Long id, EntryNode parent, BTreePartition partition, ServerEntry entry, Map<Long, EntryNode> map)
+    {
+        this( id, parent, partition, entry, map, null, null );
+    }
+
+
+    public EntryNode( Long id, EntryNode parent, BTreePartition db, ServerEntry entry, Map<Long, EntryNode> map,
+        ExprNode exprNode, SearchEngine engine )
+    {
+        this.partition = db;
+        this.id = id;
+        this.entry = entry;
+        children = new ArrayList<TreeNode>();
+
+        if ( parent == null )
+        {
+            this.parent = this;
+        }
+        else
+        {
+            this.parent = parent;
+        }
+
+        try
+        {
+            List<IndexRecord> records = new ArrayList<IndexRecord>();
+            NamingEnumeration<IndexRecord> childList = db.list( id );
+            
+            while ( childList.hasMore() )
+            {
+                IndexRecord old = childList.next();
+                IndexRecord newRec = new IndexRecord();
+                newRec.copy( old );
+                records.add( newRec );
+            }
+            
+            childList.close();
+
+            Iterator list = records.iterator();
+
+            while ( list.hasNext() )
+            {
+                IndexRecord rec = ( IndexRecord ) list.next();
+
+                if ( engine != null && exprNode != null )
+                {
+                    if ( db.getChildCount( (Long)rec.getEntryId() ) == 0 )
+                    {
+                        if ( engine.evaluate( exprNode, (Long)rec.getEntryId() ) )
+                        {
+                            ServerEntry newEntry = db.lookup( (Long)rec.getEntryId() );
+                            EntryNode child = new EntryNode( (Long)rec.getEntryId(), this, db, newEntry, map, exprNode,
+                                engine );
+                            children.add( child );
+                        }
+                        else
+                        {
+                            continue;
+                        }
+                    }
+                    else
+                    {
+                        ServerEntry newEntry = db.lookup( (Long)rec.getEntryId() );
+                        EntryNode child = new EntryNode( (Long)rec.getEntryId(), this, db, newEntry, map, exprNode, engine );
+                        children.add( child );
+                    }
+                }
+                else
+                {
+                    ServerEntry newEntry = db.lookup( (Long)rec.getEntryId() );
+                    EntryNode child = new EntryNode( (Long)rec.getEntryId(), this, db, newEntry, map );
+                    children.add( child );
+                }
+            }
+        }
+        catch ( Exception e )
+        {
+            e.printStackTrace();
+        }
+
+        map.put( id, this );
+    }
+
+
+    public Enumeration<TreeNode> children()
+    {
+        return Collections.enumeration( children );
+    }
+
+
+    public boolean getAllowsChildren()
+    {
+        return true;
+    }
+
+
+    public TreeNode getChildAt( int childIndex )
+    {
+        return ( TreeNode ) children.get( childIndex );
+    }
+
+
+    public int getChildCount()
+    {
+        return children.size();
+    }
+
+
+    public int getIndex( TreeNode child )
+    {
+        return children.indexOf( child );
+    }
+
+
+    public TreeNode getParent()
+    {
+        return parent;
+    }
+
+
+    public boolean isLeaf()
+    {
+        return children.size() <= 0;
+    }
+
+
+    public String getEntryDn() throws NamingException
+    {
+        return partition.getEntryDn( id );
+    }
+
+
+    public String toString()
+    {
+        StringBuffer buf = new StringBuffer();
+
+        try
+        {
+            LdapDN dn = new LdapDN( partition.getEntryDn( id ) );
+            buf.append( "(" ).append( id ).append( ") " );
+            buf.append( dn.getRdn() );
+        }
+        catch ( NamingException e )
+        {
+            buf.append( "ERROR: " + e.getMessage() );
+        }
+
+        if ( children.size() > 0 )
+        {
+            buf.append( " [" ).append( children.size() ).append( "]" );
+        }
+
+        return buf.toString();
+    }
+
+
+    public ServerEntry getLdapEntry()
+    {
+        return entry;
+    }
+
+
+    public Long getEntryId()
+    {
+        return id;
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/gui/FilterDialog.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/gui/FilterDialog.java
new file mode 100644
index 0000000..4833ff3
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/gui/FilterDialog.java
@@ -0,0 +1,301 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree.gui;
+
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.Font;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ActionListener;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+
+import javax.swing.BorderFactory;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JDialog;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTextArea;
+import javax.swing.JTextField;
+import javax.swing.border.TitledBorder;
+
+
+/**
+ * A dialog for the filter.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class FilterDialog extends JDialog
+{
+    private static final long serialVersionUID = 3760565295319626294L;
+
+    public static final String RUN_MODE = "Run";
+    public static final String LOAD_MODE = "Load";
+    public static final String DEBUG_MODE = "Debug";
+    public static final String ANNOTATE_MODE = "Annotate";
+
+    public static final String UNLIMITED = "Unlimited";
+
+    public static final String BASE_SCOPE = "Base Object";
+    public static final String SINGLE_SCOPE = "Single Level";
+    public static final String SUBTREE_SCOPE = "Subtree Level";
+
+    public static final String LOAD_CMD = "Load";
+    public static final String SEARCH_CMD = "Search";
+    public static final String CANCEL_CMD = "Cancel";
+
+    private JPanel m_northPnl = new JPanel();
+    private JPanel m_centerPnl = new JPanel();
+    private JTextArea m_filterText = new JTextArea();
+    private JLabel m_scopeLbl = new JLabel();
+    private JComboBox m_scopeChoice = new JComboBox();
+    private JLabel m_limitLbl = new JLabel();
+    private JTextField m_limitField = new JTextField();
+    private JPanel m_southPnl = new JPanel();
+    private JButton m_searchBut = new JButton();
+    private JButton m_cancelBut = new JButton();
+    private JScrollPane m_scrollPane = new JScrollPane();
+    private final String m_mode;
+    private JTextField m_baseText = new JTextField();
+    private JPanel m_basePnl = new JPanel();
+    private JLabel jLabel1 = new JLabel();
+
+
+    /** Creates new form JDialog */
+    public FilterDialog(String a_mode, JFrame parent, boolean modal)
+    {
+        super( parent, modal );
+        m_mode = a_mode;
+        initGUI();
+    }
+
+
+    public void addActionListener( ActionListener l_listener )
+    {
+        m_searchBut.addActionListener( l_listener );
+        m_cancelBut.addActionListener( l_listener );
+    }
+
+
+    /**
+     * This method is called from within the constructor to initialize the form
+     */
+    private void initGUI()
+    {
+        m_baseText.setText( "" );
+        addWindowListener( new WindowAdapter()
+        {
+            public void windowClosing( WindowEvent evt )
+            {
+                closeDialog( evt );
+            }
+        } );
+        pack();
+
+        getContentPane().setLayout( new java.awt.GridBagLayout() );
+        getContentPane().add(
+            m_northPnl,
+            new java.awt.GridBagConstraints( 0, 0, 1, 1, 0.9, 0.0, java.awt.GridBagConstraints.NORTH,
+                java.awt.GridBagConstraints.BOTH, new java.awt.Insets( 5, 5, 6, 0 ), 0, 0 ) );
+        getContentPane().add(
+            m_centerPnl,
+            new GridBagConstraints( 0, 1, 1, 1, 0.9, 0.9, GridBagConstraints.CENTER, GridBagConstraints.BOTH,
+                new Insets( 10, 10, 10, 10 ), 0, 0 ) );
+        getContentPane().add(
+            m_southPnl,
+            new GridBagConstraints( 0, 2, 1, 1, 1.0, 0.0, GridBagConstraints.SOUTH, GridBagConstraints.BOTH,
+                new Insets( 0, 0, 2, 0 ), 0, 0 ) );
+        m_northPnl.setLayout( new GridBagLayout() );
+        m_northPnl.setBorder( null );
+        m_northPnl.add( m_scopeLbl, new java.awt.GridBagConstraints( 0, 0, 1, 1, 0.2, 0.0,
+            java.awt.GridBagConstraints.CENTER, java.awt.GridBagConstraints.NONE, new java.awt.Insets( 5, 0, 5, 0 ), 0,
+            0 ) );
+        m_northPnl.add( m_scopeChoice, new java.awt.GridBagConstraints( 1, 0, 1, 1, 1.0, 0.0,
+            java.awt.GridBagConstraints.CENTER, java.awt.GridBagConstraints.HORIZONTAL,
+            new java.awt.Insets( 9, 0, 7, 5 ), 0, 0 ) );
+        m_northPnl.add( m_limitLbl, new GridBagConstraints( 2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER,
+            GridBagConstraints.NONE, new Insets( 5, 10, 5, 5 ), 0, 0 ) );
+        m_northPnl.add( m_limitField, new java.awt.GridBagConstraints( 3, 0, 1, 1, 1.0, 0.0,
+            java.awt.GridBagConstraints.CENTER, java.awt.GridBagConstraints.HORIZONTAL, new java.awt.Insets( 11, 0, 9,
+                10 ), 0, 0 ) );
+        m_northPnl.add( m_basePnl, new java.awt.GridBagConstraints( 0, 1, 4, 1, 0.0, 0.0,
+            java.awt.GridBagConstraints.CENTER, java.awt.GridBagConstraints.BOTH, new java.awt.Insets( 5, 10, 5, 10 ),
+            0, 0 ) );
+        m_filterText.setText( "" );
+        m_filterText.setBorder( null );
+        m_centerPnl.setLayout( new BorderLayout() );
+        m_centerPnl.setBorder( BorderFactory.createTitledBorder( BorderFactory.createLineBorder( new Color( 153, 153,
+            153 ), 1 ), "Search Filter", TitledBorder.LEADING, TitledBorder.TOP, new Font( "SansSerif", 0, 14 ),
+            new Color( 60, 60, 60 ) ) );
+        m_scrollPane.getViewport().add( m_filterText );
+        m_centerPnl.add( m_scrollPane, BorderLayout.CENTER );
+        m_scopeLbl.setText( "Scope:" );
+        m_scopeLbl.setFont( new java.awt.Font( "Dialog", java.awt.Font.PLAIN, 14 ) );
+        m_scopeChoice.setSize( new java.awt.Dimension( 115, 25 ) );
+        m_scopeChoice.setMaximumSize( new Dimension( 32767, 25 ) );
+        m_scopeChoice.setMinimumSize( new java.awt.Dimension( 115, 25 ) );
+        m_scopeChoice.setPreferredSize( new Dimension( 115, 25 ) );
+        m_scopeChoice.addItem( BASE_SCOPE );
+        m_scopeChoice.addItem( SINGLE_SCOPE );
+        m_scopeChoice.addItem( SUBTREE_SCOPE );
+
+        m_limitLbl.setText( "Limit:" );
+        m_limitField.setText( "Unlimited" );
+        m_limitField.setHorizontalAlignment( JTextField.CENTER );
+        m_southPnl.setLayout( new FlowLayout( FlowLayout.CENTER, 15, 5 ) );
+        m_southPnl.add( m_searchBut );
+
+        if ( m_mode != LOAD_MODE )
+        {
+            m_searchBut.setText( SEARCH_CMD );
+            m_searchBut.setActionCommand( SEARCH_CMD );
+            m_southPnl.add( m_cancelBut );
+        }
+        else
+        {
+            m_searchBut.setText( LOAD_CMD );
+            m_searchBut.setActionCommand( LOAD_CMD );
+        }
+
+        m_cancelBut.setText( CANCEL_CMD );
+        m_cancelBut.setActionCommand( CANCEL_CMD );
+        setBounds( new java.awt.Rectangle( 0, 0, 595, 331 ) );
+        m_basePnl.setLayout( new java.awt.GridBagLayout() );
+        m_basePnl.add( jLabel1,
+            new java.awt.GridBagConstraints( 0, 0, 1, 1, 0.0, 0.0, java.awt.GridBagConstraints.WEST,
+                java.awt.GridBagConstraints.NONE, new java.awt.Insets( 0, 0, 0, 0 ), 0, 0 ) );
+        m_basePnl.add( m_baseText, new java.awt.GridBagConstraints( 1, 0, 1, 1, 1.0, 0.0,
+            java.awt.GridBagConstraints.EAST, java.awt.GridBagConstraints.HORIZONTAL,
+            new java.awt.Insets( 5, 5, 5, 0 ), 0, 0 ) );
+        jLabel1.setText( "Search Base:" );
+        jLabel1.setFont( new java.awt.Font( "SansSerif", java.awt.Font.PLAIN, 14 ) );
+
+        if ( m_mode == RUN_MODE )
+        {
+            setTitle( "Search Filter Dialog: Execute mode" );
+        }
+        else if ( m_mode == LOAD_MODE )
+        {
+            setTitle( "Search Filter Dialog: Load mode" );
+        }
+        else if ( m_mode == DEBUG_MODE )
+        {
+            setTitle( "Search Filter Dialog: Debug mode" );
+        }
+        else if ( m_mode == ANNOTATE_MODE )
+        {
+            setTitle( "Search Filter Dialog: Annotate mode" );
+            this.m_scopeChoice.setEnabled( false );
+            this.m_limitField.setEnabled( false );
+            this.m_baseText.setEnabled( false );
+        }
+        else
+        {
+            throw new RuntimeException( "Unrecognized mode." );
+        }
+    }
+
+
+    /**
+     * Closes the dialog
+     */
+    public void closeDialog( WindowEvent evt )
+    {
+        setVisible( false );
+        dispose();
+    }
+
+
+    public String getScope()
+    {
+        int l_selected = m_scopeChoice.getSelectedIndex();
+        return ( String ) m_scopeChoice.getItemAt( l_selected );
+    }
+
+
+    /*
+     public int getScope()
+     {
+     int l_selected = m_scopeChoice.getSelectedIndex() ;
+     String l_scope = (String) m_scopeChoice.getItemAt(l_selected) ;
+
+     if(l_scope == BASE_SCOPE) {
+     return Backend.BASE_SCOPE ;
+     } else if(l_scope == SINGLE_SCOPE) {
+     return Backend.SINGLE_SCOPE ;
+     } else if(l_scope == SUBTREE_SCOPE) {
+     return Backend.SUBTREE_SCOPE ;
+     }
+
+     throw new RuntimeException("Unexpected scope parameter: " + l_scope) ;
+     }
+     */
+
+    public String getLimit()
+    {
+        return m_limitField.getText();
+    }
+
+
+    /*
+     public String getLimit()
+     {
+     String l_limit = m_limitField.getText() ;
+
+     if(l_limit.equals(UNLIMITED)) {
+     return -1 ;
+     }
+
+     return Integer.parseInt(l_limit) ;
+     }
+     */
+
+    public String getFilter()
+    {
+        return this.m_filterText.getText();
+    }
+
+
+    public void setBase( String a_base )
+    {
+        this.m_baseText.setText( a_base );
+    }
+
+
+    public void setScope( String a_scope )
+    {
+        this.m_scopeChoice.setSelectedItem( a_scope );
+    }
+
+
+    public String getBase()
+    {
+        return this.m_baseText.getText();
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/gui/IndexDialog.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/gui/IndexDialog.java
new file mode 100644
index 0000000..3317a4b
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/gui/IndexDialog.java
@@ -0,0 +1,380 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree.gui;
+
+
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.Frame;
+import java.awt.Panel;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.regex.Pattern;
+
+import javax.naming.NamingEnumeration;
+import javax.swing.BorderFactory;
+import javax.swing.BoxLayout;
+import javax.swing.ButtonGroup;
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JRadioButton;
+import javax.swing.JScrollPane;
+import javax.swing.JTabbedPane;
+import javax.swing.JTable;
+import javax.swing.JTextArea;
+import javax.swing.JTextField;
+import javax.swing.border.TitledBorder;
+import javax.swing.table.DefaultTableModel;
+
+import org.apache.directory.server.core.partition.impl.btree.Index;
+import org.apache.directory.server.core.partition.impl.btree.IndexRecord;
+import org.apache.directory.shared.ldap.util.ExceptionUtils;
+import org.apache.directory.shared.ldap.util.StringTools;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * A dialog showing index values.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class IndexDialog extends JDialog
+{
+    private static final Logger log = LoggerFactory.getLogger( IndexDialog.class );
+
+    private static final long serialVersionUID = 3689917253680445238L;
+
+    public static final String DEFAULT_CURSOR = "Default";
+    public static final String EQUALITY_CURSOR = "Equality";
+    public static final String GREATER_CURSOR = "Greater";
+    public static final String LESS_CURSOR = "Less";
+    public static final String REGEX_CURSOR = "Regex";
+
+    private Panel mainPnl = new Panel();
+    private JTabbedPane tabbedPane = new JTabbedPane();
+    private JPanel listPnl = new JPanel();
+    private JPanel cursorPnl = new JPanel();
+    private JPanel resultsPnl = new JPanel();
+    private JScrollPane jScrollPane2 = new JScrollPane();
+    private JTable resultsTbl = new JTable();
+    private JPanel buttonPnl = new JPanel();
+    private JButton doneBut = new JButton();
+    private JLabel jLabel1 = new JLabel();
+    private JTextField keyText = new JTextField();
+    private JLabel jLabel2 = new JLabel();
+    private JButton scanBut = new JButton();
+
+    private Index index = null;
+
+
+    /** Creates new form JDialog */
+    public IndexDialog( Frame parent, boolean modal, Index index )
+    {
+        super( parent, modal );
+        this.index = index;
+        initGUI();
+    }
+    
+    
+    public IndexDialog( Index index )
+    {
+        super();
+        this.index = index;
+        initGUI();
+    }
+
+
+    /**
+     * This method is called from within the constructor to initialize the
+     * form.
+     */
+    private void initGUI()
+    {
+        addWindowListener( new java.awt.event.WindowAdapter()
+        {
+            public void windowClosing( java.awt.event.WindowEvent evt )
+            {
+                closeDialog();
+            }
+        } );
+
+        pack();
+        setTitle( "Index On Attribute '" + index.getAttribute().getName() + "'" );
+        setBounds( new java.awt.Rectangle( 0, 0, 512, 471 ) );
+        getContentPane().add( mainPnl, java.awt.BorderLayout.CENTER );
+        mainPnl.setLayout( new java.awt.BorderLayout() );
+        mainPnl.add( tabbedPane, java.awt.BorderLayout.CENTER );
+        tabbedPane.add( listPnl, "Listing" );
+        listPnl.setLayout( new java.awt.GridBagLayout() );
+
+        RadioButtonListener radioListener = new RadioButtonListener();
+        JRadioButton radioDefault = new JRadioButton( DEFAULT_CURSOR );
+        radioDefault.setActionCommand( DEFAULT_CURSOR );
+        radioDefault.setSelected( true );
+        radioDefault.addActionListener( radioListener );
+
+        JRadioButton radioEquality = new JRadioButton( EQUALITY_CURSOR );
+        radioEquality.setActionCommand( EQUALITY_CURSOR );
+        radioEquality.addActionListener( radioListener );
+
+        JRadioButton radioGreater = new JRadioButton( GREATER_CURSOR );
+        radioGreater.setActionCommand( GREATER_CURSOR );
+        radioGreater.addActionListener( radioListener );
+
+        JRadioButton radioLess = new JRadioButton( LESS_CURSOR );
+        radioLess.setActionCommand( LESS_CURSOR );
+        radioLess.addActionListener( radioListener );
+
+        JRadioButton radioRegex = new JRadioButton( REGEX_CURSOR );
+        radioRegex.setActionCommand( REGEX_CURSOR );
+        radioRegex.addActionListener( radioListener );
+
+        ButtonGroup group = new ButtonGroup();
+        group.add( radioDefault );
+        group.add( radioEquality );
+        group.add( radioGreater );
+        group.add( radioLess );
+        group.add( radioRegex );
+
+        JPanel radioPanel = new JPanel();
+        radioPanel.setLayout( new BoxLayout( radioPanel, BoxLayout.X_AXIS ) );
+        radioPanel.add( radioDefault );
+        radioPanel.add( radioEquality );
+        radioPanel.add( radioGreater );
+        radioPanel.add( radioLess );
+        radioPanel.add( radioRegex );
+
+        listPnl.add( cursorPnl, new java.awt.GridBagConstraints( 0, 0, 1, 1, 1.0, 0.15,
+            java.awt.GridBagConstraints.NORTH, java.awt.GridBagConstraints.BOTH, new java.awt.Insets( 15, 0, 30, 0 ),
+            0, 0 ) );
+        listPnl.add( resultsPnl, new java.awt.GridBagConstraints( 0, 1, 1, 1, 1.0, 0.8,
+            java.awt.GridBagConstraints.CENTER, java.awt.GridBagConstraints.BOTH, new java.awt.Insets( 0, 0, 0, 0 ), 0,
+            0 ) );
+        listPnl.add( buttonPnl, new java.awt.GridBagConstraints( 0, 2, 1, 1, 1.0, 0.05,
+            java.awt.GridBagConstraints.CENTER, java.awt.GridBagConstraints.BOTH, new java.awt.Insets( 0, 0, 0, 0 ), 0,
+            0 ) );
+        cursorPnl.setLayout( new java.awt.GridBagLayout() );
+        cursorPnl.setBorder( javax.swing.BorderFactory.createTitledBorder( javax.swing.BorderFactory.createLineBorder(
+            new java.awt.Color( 153, 153, 153 ), 1 ), "Display Cursor Constraints",
+            javax.swing.border.TitledBorder.LEADING, javax.swing.border.TitledBorder.TOP, new java.awt.Font(
+                "SansSerif", 0, 14 ), new java.awt.Color( 60, 60, 60 ) ) );
+        cursorPnl.add( jLabel1, new java.awt.GridBagConstraints( 0, 1, 1, 1, 0.0, 0.0,
+            java.awt.GridBagConstraints.WEST, java.awt.GridBagConstraints.NONE, new java.awt.Insets( 0, 15, 0, 10 ), 0,
+            0 ) );
+        cursorPnl.add( keyText, new java.awt.GridBagConstraints( 1, 1, 1, 1, 0.4, 0.0,
+            java.awt.GridBagConstraints.WEST, java.awt.GridBagConstraints.BOTH, new java.awt.Insets( 5, 5, 5, 236 ), 0,
+            0 ) );
+        cursorPnl.add( jLabel2, new java.awt.GridBagConstraints( 0, 0, 1, 1, 0.0, 0.0,
+            java.awt.GridBagConstraints.WEST, java.awt.GridBagConstraints.NONE, new java.awt.Insets( 0, 15, 0, 10 ), 0,
+            0 ) );
+        cursorPnl.add( radioPanel,
+            new java.awt.GridBagConstraints( 1, 0, 1, 1, 0.4, 0.0, java.awt.GridBagConstraints.WEST,
+                java.awt.GridBagConstraints.NONE, new java.awt.Insets( 5, 5, 5, 0 ), 0, 0 ) );
+        resultsPnl.setLayout( new java.awt.BorderLayout() );
+        resultsPnl.setBorder( javax.swing.BorderFactory.createTitledBorder( javax.swing.BorderFactory.createLineBorder(
+            new java.awt.Color( 153, 153, 153 ), 1 ), "Scan Results", javax.swing.border.TitledBorder.LEADING,
+            javax.swing.border.TitledBorder.TOP, new java.awt.Font( "SansSerif", 0, 14 ), new java.awt.Color( 60, 60,
+                60 ) ) );
+        resultsPnl.add( jScrollPane2, java.awt.BorderLayout.CENTER );
+        jScrollPane2.getViewport().add( resultsTbl );
+        buttonPnl.setLayout( new java.awt.FlowLayout( java.awt.FlowLayout.CENTER, 15, 5 ) );
+        buttonPnl.add( doneBut );
+        buttonPnl.add( scanBut );
+        doneBut.setText( "Done" );
+        doneBut.addActionListener( new ActionListener()
+        {
+            public void actionPerformed( ActionEvent e )
+            {
+                closeDialog();
+            }
+        } );
+
+        jLabel1.setText( "Key Constraint:" );
+        keyText.setText( "" );
+        keyText.setMinimumSize( new java.awt.Dimension( 130, 20 ) );
+        keyText.setPreferredSize( new java.awt.Dimension( 130, 20 ) );
+        keyText.setMaximumSize( new java.awt.Dimension( 130, 20 ) );
+        keyText.setFont( new java.awt.Font( "SansSerif", java.awt.Font.PLAIN, 14 ) );
+        keyText.setSize( new java.awt.Dimension( 130, 20 ) );
+        jLabel2.setText( "Cursor Type:" );
+
+        scanBut.setText( "Scan" );
+        scanBut.addActionListener( new ActionListener()
+        {
+            public void actionPerformed( ActionEvent e )
+            {
+                doScan( keyText.getText(), selectedCursorType );
+            }
+        } );
+
+        doScan( null, DEFAULT_CURSOR );
+    }
+
+    private String selectedCursorType = DEFAULT_CURSOR;
+
+    class RadioButtonListener implements ActionListener
+    {
+        public void actionPerformed( ActionEvent e )
+        {
+            if ( e.getActionCommand() == DEFAULT_CURSOR )
+            {
+                selectedCursorType = DEFAULT_CURSOR;
+            }
+            else if ( e.getActionCommand() == EQUALITY_CURSOR )
+            {
+                selectedCursorType = EQUALITY_CURSOR;
+            }
+            else if ( e.getActionCommand() == GREATER_CURSOR )
+            {
+                selectedCursorType = GREATER_CURSOR;
+            }
+            else if ( e.getActionCommand() == LESS_CURSOR )
+            {
+                selectedCursorType = LESS_CURSOR;
+            }
+            else if ( e.getActionCommand() == REGEX_CURSOR )
+            {
+                selectedCursorType = REGEX_CURSOR;
+            }
+        }
+    }
+
+
+    private void closeDialog()
+    {
+        setVisible( false );
+        dispose();
+    }
+
+
+    public boolean doScan( String key, String scanType )
+    {
+        if ( key == null || key.trim().equals( "" ) )
+        {
+            key = null;
+        }
+
+        if ( key == null && scanType != DEFAULT_CURSOR )
+        {
+            JOptionPane.showMessageDialog( null, "Cannot use a " + scanType + " scan type with a null key constraint.",
+                "Missing Key Constraint", JOptionPane.ERROR_MESSAGE );
+            return false;
+        }
+
+        try
+        {
+            NamingEnumeration<IndexRecord> list = null;
+
+            if ( scanType == EQUALITY_CURSOR )
+            {
+                list = index.listIndices( key );
+            }
+            else if ( scanType == GREATER_CURSOR )
+            {
+                list = index.listIndices( key, true );
+            }
+            else if ( scanType == LESS_CURSOR )
+            {
+                list = index.listIndices( key, false );
+            }
+            else if ( scanType == REGEX_CURSOR )
+            {
+                Pattern regex = StringTools.getRegex( key );
+                int starIndex = key.indexOf( '*' );
+
+                if ( starIndex > 0 )
+                {
+                    String prefix = key.substring( 0, starIndex );
+
+                    log.debug( "Regex prefix = {}", prefix );
+
+                    list = index.listIndices( regex, prefix );
+                }
+                else
+                {
+                    list = index.listIndices( regex );
+                }
+            }
+            else
+            {
+                list = index.listIndices();
+            }
+
+            Object[] cols = new Object[2];
+            Object[] row = null;
+            cols[0] = "Keys ( Attribute Value )";
+            cols[1] = "Values ( Entry Id )";
+            DefaultTableModel model = new DefaultTableModel( cols, 0 );
+            int count = 0;
+
+            while ( list.hasMore() )
+            {
+                IndexRecord rec = ( IndexRecord ) list.next();
+                row = new Object[2];
+                row[0] = rec.getIndexKey();
+                row[1] = rec.getEntryId();
+                model.addRow( row );
+                count++;
+            }
+
+            resultsTbl.setModel( model );
+            resultsPnl.setBorder( BorderFactory.createTitledBorder( BorderFactory.createLineBorder( new Color( 153,
+                153, 153 ), 1 ), "Scan Results: " + count, TitledBorder.LEADING, TitledBorder.TOP, new Font(
+                "SansSerif", 0, 14 ), new Color( 60, 60, 60 ) ) );
+
+            if ( isVisible() )
+            {
+                validate();
+            }
+        }
+        catch ( Exception e )
+        {
+            String msg = ExceptionUtils.getStackTrace( e );
+
+            if ( msg.length() > 1024 )
+            {
+                msg = msg.substring( 0, 1024 ) + "\n. . . TRUNCATED . . .";
+            }
+
+            msg = "Error while scanning index " + "on attribute " + index.getAttribute() + " using a " + scanType
+                + " cursor type with a key constraint of '" + key + "':\n" + msg;
+
+            JTextArea area = new JTextArea();
+            area.setText( msg );
+            JOptionPane.showMessageDialog( null, area, "Index Scan Error", JOptionPane.ERROR_MESSAGE );
+            return false;
+        }
+
+        return true;
+    }
+    
+    
+    public static void show( Index index )
+    {
+        IndexDialog dialog = new IndexDialog( index );
+        dialog.setVisible( true );
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/gui/PartitionFrame.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/gui/PartitionFrame.java
new file mode 100644
index 0000000..d33f1c4
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/gui/PartitionFrame.java
@@ -0,0 +1,944 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree.gui;
+
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.Toolkit;
+import java.awt.Window;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Stack;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.SearchControls;
+import javax.swing.JFileChooser;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
+import javax.swing.JMenuItem;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JSeparator;
+import javax.swing.JSplitPane;
+import javax.swing.JTabbedPane;
+import javax.swing.JTable;
+import javax.swing.JTextArea;
+import javax.swing.JTree;
+import javax.swing.event.TreeSelectionEvent;
+import javax.swing.event.TreeSelectionListener;
+import javax.swing.table.DefaultTableModel;
+import javax.swing.tree.DefaultTreeModel;
+import javax.swing.tree.TreeModel;
+import javax.swing.tree.TreeNode;
+import javax.swing.tree.TreePath;
+
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerEntryUtils;
+import org.apache.directory.server.core.interceptor.context.AddOperationContext;
+import org.apache.directory.server.core.partition.impl.btree.BTreePartition;
+import org.apache.directory.server.core.partition.impl.btree.Index;
+import org.apache.directory.server.core.partition.impl.btree.IndexRecord;
+import org.apache.directory.server.schema.registries.Registries;
+
+import org.apache.directory.shared.ldap.filter.ExprNode;
+import org.apache.directory.shared.ldap.filter.FilterParser;
+import org.apache.directory.shared.ldap.ldif.LdifEntry;
+import org.apache.directory.shared.ldap.ldif.LdifReader;
+import org.apache.directory.shared.ldap.message.AliasDerefMode;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.util.StringTools;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * The frame for the database.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class PartitionFrame extends JFrame
+{
+    private static final Logger LOG = LoggerFactory.getLogger( PartitionFrame.class );
+
+    private static final long serialVersionUID = 4049353102291513657L;
+
+    // Swing Stuff
+    private JLabel statusBar = new JLabel( "Ready" );
+    private JPanel mainPnl = new JPanel();
+    private JSplitPane splitPane = new JSplitPane();
+    private JTabbedPane tabbedPane = new JTabbedPane();
+    private JPanel entryPnl = new JPanel();
+    private JPanel idxPnl = new JPanel();
+    private JScrollPane treePane = new JScrollPane();
+    private JTree tree = new JTree();
+    private JScrollPane entryPane = new JScrollPane();
+    private JTable entryTbl = new JTable();
+    private JScrollPane idxPane = new JScrollPane();
+    private JTable idxTbl = new JTable();
+    private JMenu searchMenu = new JMenu();
+    private JMenuItem annotate = new JMenuItem();
+    private JMenuItem run = new JMenuItem();
+    private JMenuItem debug = new JMenuItem();
+    private JMenu indices = new JMenu();
+
+    // Non Swing Stuff
+    private BTreePartition partition;
+    private boolean doCleanUp;
+    private Map<Long, EntryNode> nodes;
+    private EntryNode root;
+
+    
+    /** A handle on the global registries */
+    private Registries registries;
+
+    /**
+     * Creates new form JFrame
+     * 
+     * @param db the partition to view
+     * @throws NamingException if there are problems accessing the partition
+     */
+    public PartitionFrame( BTreePartition db, Registries registries ) throws NamingException
+    {
+        partition = db;
+        this.registries = registries;
+
+        initialize();
+        buildIndicesMenu( partition );
+        pack();
+        load();
+    }
+
+
+    /**
+     * This method is called from within the constructor to initialize the form
+     *
+     * @throws NamingException on partition access errors
+     */
+    private void initialize() throws NamingException
+    {
+        mainPnl.setBorder( null );
+        mainPnl.setLayout( new java.awt.BorderLayout() );
+        mainPnl.add( splitPane, java.awt.BorderLayout.CENTER );
+        splitPane.add( tabbedPane, javax.swing.JSplitPane.RIGHT );
+        splitPane.add( treePane, javax.swing.JSplitPane.LEFT );
+        tabbedPane.add( entryPnl, "Entry Attributes" );
+        tabbedPane.add( idxPnl, "Entry Indices" );
+
+        entryPnl.setLayout( new java.awt.BorderLayout() );
+        entryPnl.add( entryPane, java.awt.BorderLayout.CENTER );
+
+        idxPnl.setLayout( new java.awt.BorderLayout() );
+        idxPnl.add( idxPane, java.awt.BorderLayout.CENTER );
+
+        getContentPane().setLayout( new java.awt.BorderLayout() );
+        JPanel content = new JPanel();
+        content.setPreferredSize( new java.awt.Dimension( 798, 461 ) );
+        content.setLayout( new java.awt.BorderLayout() );
+        content.setBorder( javax.swing.BorderFactory.createEtchedBorder() );
+        content.add( mainPnl, java.awt.BorderLayout.NORTH );
+        getContentPane().add( content, BorderLayout.CENTER );
+        // set title
+        setTitle( "Partition: " + this.partition.getSuffixDn().toString() );
+        // add status bar
+        getContentPane().add( statusBar, BorderLayout.SOUTH );
+        // add menu bar
+        JMenuBar menuBar = new JMenuBar();
+
+        // --------------------------------------------------------------------
+        // 'Backend' Menu
+        // --------------------------------------------------------------------
+
+        JMenu backendMenu = new JMenu( "Backend" );
+        backendMenu.setText( "Partition" );
+        backendMenu.setBackground( new java.awt.Color( 205, 205, 205 ) );
+        backendMenu.setMnemonic( 'B' );
+
+        // create Import menu item
+        JMenuItem add = new JMenuItem( "Add" );
+        backendMenu.add( add );
+        add.setMnemonic( 'A' );
+        add.setBackground( new java.awt.Color( 205, 205, 205 ) );
+        add.addActionListener( new ActionListener()
+        {
+            public void actionPerformed( ActionEvent e )
+            {
+                doAddDialog();
+            }
+        } );
+
+        // create Import menu item
+        JMenuItem importItem = new JMenuItem( "Import" );
+        backendMenu.add( importItem );
+        importItem.setMnemonic( 'I' );
+        importItem.setBackground( new java.awt.Color( 205, 205, 205 ) );
+        importItem.addActionListener( new ActionListener()
+        {
+            public void actionPerformed( ActionEvent e )
+            {
+                doImport();
+            }
+        } );
+
+        // create Exit menu item
+        JMenuItem exit = new JMenuItem( "Exit" );
+        backendMenu.add( exit );
+        exit.setMnemonic( 'E' );
+        exit.setBackground( new java.awt.Color( 205, 205, 205 ) );
+        exit.addActionListener( new ActionListener()
+        {
+            public void actionPerformed( ActionEvent e )
+            {
+                exitForm();
+            }
+        } );
+
+        // create About menu item
+        JMenu helpMenu = new JMenu( "Help" );
+        helpMenu.setMnemonic( 'H' );
+        JMenuItem about = new JMenuItem( "About" );
+        about.setMnemonic( 'A' );
+        about.setBackground( new java.awt.Color( 205, 205, 205 ) );
+        about.addActionListener( new ActionListener()
+        {
+            public void actionPerformed( ActionEvent e )
+            {
+                AboutDialog aboutDialog = new AboutDialog( PartitionFrame.this, true );
+                PartitionFrame.this.centerOnScreen( aboutDialog );
+                aboutDialog.setVisible( true );
+            }
+        } );
+        helpMenu.setBackground( new java.awt.Color( 205, 205, 205 ) );
+        helpMenu.add( about );
+
+        // create Save menu item
+        // create Print menu item
+        menuBar.setBackground( new java.awt.Color( 196, 197, 203 ) );
+        menuBar.add( backendMenu );
+        menuBar.add( searchMenu );
+        menuBar.add( indices );
+        menuBar.add( helpMenu );
+        // sets menu bar
+        setJMenuBar( menuBar );
+        setBounds( new java.awt.Rectangle( 0, 0, 802, 515 ) );
+        setSize( new java.awt.Dimension( 802, 515 ) );
+        setResizable( true );
+
+        addWindowListener( new java.awt.event.WindowAdapter()
+        {
+            public void windowClosing( java.awt.event.WindowEvent evt )
+            {
+                exitForm();
+            }
+        } );
+
+        treePane.getViewport().add( tree );
+        tree.setBounds( new java.awt.Rectangle( 6, 184, 82, 80 ) );
+        tree.setShowsRootHandles( true );
+        tree.setToolTipText( "DB DIT" );
+        tree.setScrollsOnExpand( true );
+        tree.getSelectionModel().addTreeSelectionListener( new TreeSelectionListener()
+        {
+            public void valueChanged( TreeSelectionEvent e )
+            {
+                TreePath path = e.getNewLeadSelectionPath();
+
+                if ( path == null )
+                {
+                    return;
+                }
+
+                Object last = path.getLastPathComponent();
+                try
+                {
+                    if ( last instanceof EntryNode )
+                    {
+                        displayEntry( ( ( EntryNode ) last ).getEntryId(), ( ( EntryNode ) last ).getLdapEntry() );
+                    }
+                }
+                catch ( Exception ex )
+                {
+                    ex.printStackTrace();
+                }
+            }
+        } );
+
+        entryPane.getViewport().add( entryTbl );
+        entryTbl.setBounds( new java.awt.Rectangle( 321, 103, 32, 32 ) );
+
+        idxPane.getViewport().add( idxTbl );
+        idxTbl.setBounds( new java.awt.Rectangle( 429, 134, 32, 32 ) );
+
+        treePane.setSize( new java.awt.Dimension( 285, 435 ) );
+        treePane.setPreferredSize( new java.awt.Dimension( 285, 403 ) );
+        searchMenu.setText( "Search" );
+        searchMenu.setBackground( new java.awt.Color( 205, 205, 205 ) );
+        searchMenu.add( run );
+        searchMenu.add( debug );
+        searchMenu.add( annotate );
+
+        ActionListener searchHandler = new ActionListener()
+        {
+            public void actionPerformed( ActionEvent an_event )
+            {
+                LOG.debug( "action command = {}", an_event.getActionCommand() );
+
+                try
+                {
+                    doFilterDialog( an_event.getActionCommand() );
+                }
+                catch ( NamingException e )
+                {
+                    e.printStackTrace();
+                }
+            }
+        };
+
+        annotate.setText( FilterDialog.ANNOTATE_MODE );
+        annotate.setActionCommand( FilterDialog.ANNOTATE_MODE );
+        annotate.setBackground( new java.awt.Color( 205, 205, 205 ) );
+        annotate.addActionListener( searchHandler );
+
+        run.setText( FilterDialog.RUN_MODE );
+        run.setActionCommand( FilterDialog.RUN_MODE );
+        run.setBackground( new java.awt.Color( 205, 205, 205 ) );
+        run.addActionListener( searchHandler );
+
+        debug.setText( FilterDialog.DEBUG_MODE );
+        debug.setActionCommand( FilterDialog.DEBUG_MODE );
+        debug.setBackground( new java.awt.Color( 205, 205, 205 ) );
+        debug.addActionListener( searchHandler );
+
+        indices.setText( "Indices" );
+        indices.setBackground( new java.awt.Color( 205, 205, 205 ) );
+    }
+
+
+    private void centerOnScreen( Window window )
+    {
+        Dimension frameSize = window.getSize();
+        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
+
+        frameSize.height = ( ( frameSize.height > screenSize.height ) ? screenSize.height : frameSize.height );
+        frameSize.width = ( ( frameSize.width > screenSize.width ) ? screenSize.width : frameSize.width );
+        window.setLocation( ( screenSize.width - frameSize.width ) / 2, ( screenSize.height - frameSize.height ) / 2 );
+    }
+
+
+    /**
+     * Displays a entry addition dialog.
+     */
+    public void doAddDialog()
+    {
+        try
+        {
+            TreePath path = tree.getSelectionModel().getSelectionPath();
+            String parentDn = partition.getSuffixDn().toString();
+
+            if ( null != path )
+            {
+                Object last = path.getLastPathComponent();
+
+                if ( last instanceof EntryNode )
+                {
+                    parentDn = ( ( EntryNode ) last ).getEntryDn();
+                }
+            }
+
+            if ( null == parentDn )
+            {
+                JOptionPane.showMessageDialog( this, "Must select a parent entry to add a child to!" );
+                return;
+            }
+
+            AddEntryDialog dialog = new AddEntryDialog( this, false, registries );
+            dialog.setParentDn( parentDn );
+
+            centerOnScreen( dialog );
+            dialog.setEnabled( true );
+            dialog.setVisible( true );
+        }
+        catch ( Exception e )
+        {
+            e.printStackTrace();
+        }
+    }
+
+
+    /**
+     * Gets the DN of the DIT node selected in the tree view.
+     * 
+     * @return the DN of the selected tree node or the root Dn of the tree if 
+     * nothing has been selected yet.
+     * @throws NamingException on partition access errors
+     */
+    public String getSelectedDn() throws NamingException
+    {
+        TreePath path = tree.getSelectionModel().getSelectionPath();
+
+        if ( null == path )
+        {
+            return partition.getSuffixDn().toString();
+        }
+
+        Object last = path.getLastPathComponent();
+        String base = null;
+
+        if ( last instanceof EntryNode )
+        {
+            try
+            {
+                base = ( ( EntryNode ) last ).getEntryDn();
+            }
+            catch ( NamingException e )
+            {
+                e.printStackTrace();
+            }
+        }
+        else
+        {
+            base = partition.getSuffixDn().toString();
+        }
+
+        return base;
+    }
+
+
+    public void doImport()
+    {
+        FileReader in;
+        JFileChooser chooser = new JFileChooser();
+        int choice = chooser.showOpenDialog( this );
+        File selected = chooser.getSelectedFile();
+
+        if ( JFileChooser.APPROVE_OPTION != choice )
+        {
+            return;
+        }
+
+        try
+        {
+            in = new FileReader( selected );
+
+            for ( LdifEntry entry:new LdifReader( in ) )
+            {
+                String updn = entry.getDn();
+                
+                LdapDN ndn = new LdapDN( StringTools.deepTrimToLower( updn ) );
+
+                ServerEntry attrs = ServerEntryUtils.toServerEntry( entry.getAttributes(), ndn, null );
+
+                if ( null == partition.getEntryId( ndn.toString() ) )
+                {
+                    partition.add( new AddOperationContext( null, attrs ) );
+                    load();
+                }
+            }
+        }
+        catch ( NamingException e )
+        {
+            // @todo display popup with error here!
+            e.printStackTrace();
+        }
+        catch ( FileNotFoundException e )
+        {
+            // @todo display popup with error here!
+            e.printStackTrace();
+        }
+        catch ( Exception e )
+        {
+            // @todo display popup with error here!
+            e.printStackTrace();
+        }
+    }
+
+
+    /**
+     * Exit the Application
+     */
+    private void exitForm()
+    {
+        setEnabled( false );
+        setVisible( false );
+        dispose();
+
+        if ( doCleanUp && partition != null )
+        {
+            try
+            {
+                partition.sync();
+                partition.destroy();
+            }
+            catch ( NamingException e )
+            {
+                e.printStackTrace();
+            }
+
+            System.exit( 0 );
+        }
+    }
+
+
+    public void doRunDebugAnnotate( FilterDialog dialog, String mode )
+    {
+        try
+        {
+            if ( mode.equals( FilterDialog.RUN_MODE ) )
+            {
+                doRun( dialog.getFilter(), dialog.getScope(), dialog.getBase(), dialog.getLimit() );
+            }
+            else if ( mode.equals( FilterDialog.DEBUG_MODE ) )
+            {
+                doDebug( dialog.getFilter(), dialog.getScope(), dialog.getBase(), dialog.getLimit() );
+            }
+            else if ( mode.equals( FilterDialog.ANNOTATE_MODE ) )
+            {
+                if ( doAnnotate( dialog.getFilter() ) )
+                {
+                    // continue
+                }
+                else
+                {
+                    // We failed don't loose users filter buf
+                    // allow user to make edits.
+                    return;
+                }
+
+                LOG.debug( "call to annotate" );
+            }
+            else
+            {
+                throw new RuntimeException( "Unrecognized mode." );
+            }
+        }
+        catch ( Exception e )
+        {
+            // @todo show error popup here!
+            e.printStackTrace();
+        }
+    }
+
+
+    public void doFilterDialog( final String mode ) throws NamingException
+    {
+        final FilterDialog dialog = new FilterDialog( mode, this, true );
+
+        if ( tree.getSelectionModel().getSelectionPath() != null )
+        {
+            dialog.setBase( getSelectedDn() );
+        }
+        else
+        {
+            dialog.setBase( partition.getSuffixDn().toString() );
+        }
+
+        dialog.addActionListener( new ActionListener()
+        {
+            public void actionPerformed( ActionEvent an_event )
+            {
+                String cmd = an_event.getActionCommand();
+
+                if ( cmd.equals( FilterDialog.SEARCH_CMD ) )
+                {
+                    doRunDebugAnnotate( dialog, mode );
+                }
+                else if ( cmd.equals( FilterDialog.CANCEL_CMD ) )
+                {
+                    // Do nothing! Just exit dialog.
+                }
+                else
+                {
+                    throw new RuntimeException( "Unrecognized FilterDialog command: " + cmd );
+                }
+
+                dialog.setVisible( false );
+                dialog.dispose();
+            }
+        } );
+
+        //Center the frame on screen
+        dialog.setSize( 456, 256 );
+        centerOnScreen( dialog );
+        dialog.setEnabled( true );
+        dialog.setVisible( true );
+    }
+
+
+    public boolean doRun( String filter, String scope, String base, String limit ) throws Exception
+    {
+        if ( LOG.isDebugEnabled() )
+        {
+            LOG.debug( "Search attempt using filter '" + filter + "' " + "with scope '" + scope
+                + "' and a return limit of '" + limit + "'" );
+        }
+
+        ExprNode root;
+
+        try
+        {
+            root = FilterParser.parse( filter );
+        }
+        catch ( Exception e )
+        {
+            e.printStackTrace();
+            JTextArea text = new JTextArea();
+            String msg = e.getMessage();
+
+            if ( msg.length() > 1024 )
+            {
+                msg = msg.substring( 0, 1024 ) + "\n. . . truncated . . .";
+            }
+
+            text.setText( msg );
+            text.setEnabled( false );
+            JOptionPane.showMessageDialog( null, text, "Syntax Error", JOptionPane.ERROR_MESSAGE );
+            return false;
+        }
+
+        SearchControls ctls = new SearchControls();
+
+        if ( scope.equals( FilterDialog.BASE_SCOPE ) )
+        {
+            ctls.setSearchScope( SearchControls.OBJECT_SCOPE );
+        }
+        else if ( scope.equals( FilterDialog.SINGLE_SCOPE ) )
+        {
+            ctls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+        }
+        else if ( scope.equals( FilterDialog.SUBTREE_SCOPE ) )
+        {
+            ctls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        }
+        else
+        {
+            throw new RuntimeException( "Unexpected scope parameter: " + scope );
+        }
+
+        int limitMax = Integer.MAX_VALUE;
+        if ( !limit.equals( FilterDialog.UNLIMITED ) )
+        {
+            limitMax = Integer.parseInt( limit );
+        }
+
+        NamingEnumeration cursor = partition
+                .getSearchEngine().search( new LdapDN( base ), AliasDerefMode.DEREF_ALWAYS, root, ctls );
+        String[] cols = new String[2];
+        cols[0] = "id";
+        cols[1] = "dn";
+        DefaultTableModel tableModel = new DefaultTableModel( cols, 0 );
+        Object[] row = new Object[2];
+        int count = 0;
+        while ( cursor.hasMore() && count < limitMax )
+        {
+            IndexRecord rec = ( IndexRecord ) cursor.next();
+            row[0] = rec.getEntryId();
+            row[1] = partition.getEntryDn( ( Long ) row[0] );
+            tableModel.addRow( row );
+            count++;
+        }
+
+        SearchResultDialog results = new SearchResultDialog( this, false );
+        StringBuffer buf = new StringBuffer();
+        buf.append( "base: " );
+        buf.append( base );
+        buf.append( "\n" );
+        buf.append( "scope: " );
+        buf.append( scope );
+        buf.append( "\n" );
+        buf.append( "limit: " );
+        buf.append( limit );
+        buf.append( "\n" );
+        buf.append( "total: " );
+        buf.append( count );
+        buf.append( "\n" );
+        buf.append( "filter:\n" );
+        buf.append( filter );
+        buf.append( "\n" );
+        results.setFilter( buf.toString() );
+
+        TreeNode astRoot = new ASTNode( null, root );
+        TreeModel treeModel = new DefaultTreeModel( astRoot, true );
+        results.setTreeModel( treeModel );
+        results.setTableModel( tableModel );
+        centerOnScreen( results );
+        results.setVisible( true );
+        return true;
+    }
+
+
+    public void doDebug( String filter, String scope, String base, String limit )
+    {
+        if ( LOG.isDebugEnabled() )
+        {
+            LOG.debug( "debug attempt using base '" + base + "' filter '" + filter + "' " + "with scope '" + scope
+                + "' and a return limit of '" + limit + "'" );
+        }
+
+        LOG.warn( "NOT IMPLMENTED YET" );
+    }
+
+
+    public void selectTreeNode( Long id )
+    {
+        Stack<TreeNode> stack = new Stack<TreeNode>();
+        Object[] comps;
+        TreeNode parent = nodes.get( id );
+
+        while ( parent != null && ( parent != parent.getParent() ) )
+        {
+            stack.push( parent );
+            parent = parent.getParent();
+        }
+
+        if ( stack.size() == 0 )
+        {
+            comps = new Object[1];
+            comps[0] = root;
+        }
+        else
+        {
+            comps = new Object[stack.size()];
+        }
+
+        for ( int ii = 0; stack.size() > 0 && ii < comps.length; ii++ )
+        {
+            comps[ii] = stack.pop();
+        }
+
+        TreePath path = new TreePath( comps );
+        tree.scrollPathToVisible( path );
+        tree.getSelectionModel().setSelectionPath( path );
+        tree.validate();
+    }
+
+
+    public boolean doAnnotate( String filter ) throws Exception
+    {
+        ExprNode root;
+
+        try
+        {
+            root = FilterParser.parse( filter );
+        }
+        catch ( Exception e )
+        {
+            JTextArea text = new JTextArea();
+            String msg = e.getMessage();
+
+            if ( msg.length() > 1024 )
+            {
+                msg = msg.substring( 0, 1024 ) + "\n. . . truncated . . .";
+            }
+
+            text.setText( msg );
+            text.setEnabled( false );
+            JOptionPane.showMessageDialog( null, text, "Syntax Error", JOptionPane.ERROR_MESSAGE );
+            return false;
+        }
+
+        AnnotatedFilterTreeDialog treeDialog = new AnnotatedFilterTreeDialog( PartitionFrame.this, false );
+        treeDialog.setFilter( filter );
+
+        partition.getSearchEngine().getOptimizer().annotate( root );
+        TreeNode astRoot = new ASTNode( null, root );
+        TreeModel model = new DefaultTreeModel( astRoot, true );
+        treeDialog.setModel( model );
+        treeDialog.setVisible( true );
+        return true;
+    }
+
+
+    /**
+     * Shows a dialog to display and scan indices.
+     * 
+     * @param idxAttr the name of the index or its attribute
+     * @throws Exception if the indices cannot be accessed
+     */
+    public void showIndexDialog( String idxAttr ) throws Exception
+    {
+        Index index;
+        boolean isSystem = partition.hasSystemIndexOn( idxAttr );
+
+        if ( isSystem )
+        {
+            index = partition.getSystemIndex( idxAttr );
+        }
+        else
+        {
+            index = partition.getUserIndex( idxAttr );
+        }
+
+        if ( index != null )
+        {
+            IndexDialog dialog = new IndexDialog( this, false, index );
+            centerOnScreen( dialog );
+            dialog.setEnabled( true );
+            dialog.setVisible( true );
+        }
+    }
+
+
+    public void buildIndicesMenu( BTreePartition partition )
+    {
+        JMenuItem item;
+
+        ActionListener listener = new ActionListener()
+        {
+            public void actionPerformed( ActionEvent event )
+            {
+                try
+                {
+                    showIndexDialog( event.getActionCommand() );
+                }
+                catch ( Exception e )
+                {
+                    e.printStackTrace();
+                }
+            }
+        };
+
+        Iterator list = partition.getSystemIndices();
+        while ( list.hasNext() )
+        {
+            String idx = ( String ) list.next();
+            item = new JMenuItem();
+            item.setBackground( new java.awt.Color( 205, 205, 205 ) );
+            indices.add( item );
+            item.setText( idx );
+            item.setActionCommand( idx );
+            item.addActionListener( listener );
+        }
+
+        indices.add( new JSeparator() );
+        list = partition.getUserIndices();
+        while ( list.hasNext() )
+        {
+            String idx = ( String ) list.next();
+            item = new JMenuItem();
+            item.setBackground( new java.awt.Color( 205, 205, 205 ) );
+            indices.add( item );
+            item.setText( idx );
+            item.setActionCommand( idx );
+            item.addActionListener( listener );
+        }
+    }
+
+
+    void displayEntry( Long id, ServerEntry entry ) throws Exception
+    {
+        String dn = partition.getEntryUpdn( id );
+        AttributesTableModel model = new AttributesTableModel( entry, id, dn, false );
+        entryTbl.setModel( model );
+
+        model = new AttributesTableModel( partition.getIndices( id ), id, dn, false );
+        idxTbl.setModel( model );
+
+        validate();
+    }
+
+
+    private void load() throws NamingException
+    {
+        // boolean doFiltered = false;
+        nodes = new HashMap<Long, EntryNode>();
+
+        ServerEntry suffix = partition.getSuffixEntry();
+        Long id = partition.getEntryId( partition.getSuffixDn().toString() );
+        root = new EntryNode( id, null, partition, suffix, nodes );
+
+        /*
+         int option = JOptionPane.showConfirmDialog( null,
+         "Would you like to filter leaf nodes on load?", "Use Filter?",
+         JOptionPane.OK_CANCEL_OPTION );
+         doFiltered = option == JOptionPane.OK_OPTION;
+
+         if(doFiltered) {
+         SearchEngine engine = new SearchEngine();
+         final FilterDialog dialog =
+         new FilterDialog(FilterDialog.LOAD_MODE, this, true);
+         dialog.addActionListener(new ActionListener() {
+         public void actionPerformed(ActionEvent e) {
+         dialog.setVisible(false);
+         dialog.dispose();
+         }
+         });
+
+         dialog.setBase(database.getSuffixDn().toString());
+         dialog.setScope(FilterDialog.SUBTREE_SCOPE);
+
+         //Center the frame on screen
+         dialog.setSize(456, 256);
+         this.centerOnScreen( dialog );
+         dialog.setEnabled(true);
+         dialog.setVisible(true);
+
+         FilterParser parser = new FilterParserImpl();
+         parser.enableLogging(logger);
+         ExprNode exprNode = parser.parse(dialog.getFilter());
+
+         int scope = -1;
+         String scopeStr = dialog.getScope();
+         if(scopeStr == FilterDialog.BASE_SCOPE) {
+         scope = Backend.BASE_SCOPE;
+         } else if(scopeStr == FilterDialog.SINGLE_SCOPE) {
+         scope = Backend.SINGLE_SCOPE;
+         } else if(scopeStr == FilterDialog.SUBTREE_SCOPE) {
+         scope = Backend.SUBTREE_SCOPE;
+         } else {
+         throw new RuntimeException("Unrecognized scope");
+         }
+
+         exprNode =
+         engine.addScopeNode(exprNode, dialog.getBase(), scope);
+         root = new EntryNode(null, database,
+         database.getSuffixEntry(), nodes, exprNode, engine);
+         } else {
+         root = new EntryNode(null, database,
+         database.getSuffixEntry(), nodes);
+         }
+         */
+
+        DefaultTreeModel model = new DefaultTreeModel( root );
+        tree.setModel( model );
+
+        if ( isVisible() )
+        {
+            tree.validate();
+        }
+    }
+
+
+    public void setDoCleanUp( boolean doCleanUp )
+    {
+        this.doCleanUp = doCleanUp;
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/gui/PartitionViewer.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/gui/PartitionViewer.java
new file mode 100644
index 0000000..a44cfae
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/gui/PartitionViewer.java
@@ -0,0 +1,88 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree.gui;
+
+
+import java.awt.Dimension;
+import java.awt.Toolkit;
+
+import org.apache.directory.server.core.partition.impl.btree.BTreePartition;
+import org.apache.directory.server.schema.registries.Registries;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.naming.NamingException;
+
+
+/**
+ * A partition database viewer.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class PartitionViewer
+{
+    private static final Logger LOG = LoggerFactory.getLogger( PartitionViewer.class );
+
+    /** A handle on the atomic partition */
+    private BTreePartition partition;
+    
+    /** A handle on the global registries */
+    private Registries registries;
+
+
+    public PartitionViewer( BTreePartition db, Registries registries )
+    {
+        this.partition = db;
+        this.registries = registries;
+    }
+
+
+    // added return value so expressions in debugger does not freak with void
+    public int execute() throws NamingException
+    {
+        Thread t = new Thread( new Runnable() {
+            public void run()
+            {
+                PartitionFrame frame = null;
+                try
+                {
+                    frame = new PartitionFrame( PartitionViewer.this.partition, registries );
+                }
+                catch ( NamingException e )
+                {
+                    e.printStackTrace();
+                    return;
+                }
+                Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
+                Dimension frameSize = frame.getSize();
+                frameSize.height = ( ( frameSize.height > screenSize.height ) ? screenSize.height : frameSize.height );
+                frameSize.width = ( ( frameSize.width > screenSize.width ) ? screenSize.width : frameSize.width );
+                frame.setLocation( ( screenSize.width - frameSize.width ) / 2, ( screenSize.height - frameSize.height ) / 2 );
+
+                frame.setVisible( true );
+                LOG.debug( frameSize + "" );
+            }
+        });
+
+        t.run();
+        return 0;
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/gui/SearchResultDialog.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/gui/SearchResultDialog.java
new file mode 100644
index 0000000..399ce47
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/gui/SearchResultDialog.java
@@ -0,0 +1,192 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree.gui;
+
+
+import java.awt.Frame;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.WindowEvent;
+
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.JTextArea;
+import javax.swing.JTree;
+import javax.swing.ListSelectionModel;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.table.TableModel;
+import javax.swing.tree.TreeModel;
+
+
+/**
+ * Dialog showing the search results.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class SearchResultDialog extends JDialog implements ListSelectionListener
+{
+    private static final long serialVersionUID = 3256999964914757684L;
+
+    private JPanel jPanel1 = new JPanel();
+    private JTree jTree1 = new JTree();
+    private JPanel jPanel2 = new JPanel();
+    private JPanel jPanel3 = new JPanel();
+    private JTextArea jTextArea1 = new JTextArea();
+    private JScrollPane jScrollPane1 = new JScrollPane();
+    private JButton jButton1 = new JButton();
+    private JPanel jPanel4 = new JPanel();
+    private JScrollPane jScrollPane2 = new JScrollPane();
+    private JTable m_resultsTbl = new JTable();
+
+
+    /** Creates new form JDialog */
+    public SearchResultDialog(Frame parent, boolean modal)
+    {
+        super( parent, modal );
+        initGUI();
+    }
+
+
+    /**
+     * This method is called from within the constructor to initialize the form.
+     */
+    private void initGUI()
+    {
+        addWindowListener( new java.awt.event.WindowAdapter()
+        {
+            public void windowClosing( java.awt.event.WindowEvent evt )
+            {
+                closeDialog( evt );
+            }
+        } );
+        pack();
+        getContentPane().setLayout( new java.awt.GridBagLayout() );
+        getContentPane().add(
+            jPanel1,
+            new java.awt.GridBagConstraints( 0, 0, 1, 1, 1.0, 0.1, java.awt.GridBagConstraints.NORTH,
+                java.awt.GridBagConstraints.BOTH, new java.awt.Insets( 10, 5, 5, 5 ), 0, 0 ) );
+        getContentPane().add(
+            jPanel2,
+            new java.awt.GridBagConstraints( 0, 1, 1, 1, 1.0, 0.4, java.awt.GridBagConstraints.CENTER,
+                java.awt.GridBagConstraints.BOTH, new java.awt.Insets( 5, 5, 5, 5 ), 0, 0 ) );
+        getContentPane().add(
+            jPanel3,
+            new java.awt.GridBagConstraints( 0, 3, 1, 1, 1.0, 0.1, java.awt.GridBagConstraints.SOUTH,
+                java.awt.GridBagConstraints.BOTH, new java.awt.Insets( 0, 0, 0, 0 ), 0, 0 ) );
+        getContentPane().add(
+            jPanel4,
+            new java.awt.GridBagConstraints( 0, 2, 1, 1, 1.0, 0.4, java.awt.GridBagConstraints.CENTER,
+                java.awt.GridBagConstraints.BOTH, new java.awt.Insets( 5, 5, 5, 5 ), 0, 0 ) );
+        jPanel1.setLayout( new java.awt.BorderLayout( 10, 10 ) );
+        jPanel1.setBorder( javax.swing.BorderFactory.createTitledBorder( javax.swing.BorderFactory.createLineBorder(
+            new java.awt.Color( 153, 153, 153 ), 1 ), "Specifications", javax.swing.border.TitledBorder.LEADING,
+            javax.swing.border.TitledBorder.TOP, new java.awt.Font( "SansSerif", 0, 14 ), new java.awt.Color( 60, 60,
+                60 ) ) );
+        jPanel1.add( jTextArea1, java.awt.BorderLayout.CENTER );
+        jScrollPane1.getViewport().add( jTree1 );
+        jTree1.setBounds( new java.awt.Rectangle( 238, 142, 82, 80 ) );
+        jTextArea1.setText( "" );
+        jTextArea1.setEditable( false );
+        setBounds( new java.awt.Rectangle( 0, 0, 485, 434 ) );
+        setTitle( "Search Results" );
+        jPanel2.setLayout( new java.awt.BorderLayout() );
+        jPanel2.setBorder( javax.swing.BorderFactory.createTitledBorder( javax.swing.BorderFactory.createLineBorder(
+            new java.awt.Color( 153, 153, 153 ), 1 ), "Filter Expression Tree",
+            javax.swing.border.TitledBorder.LEADING, javax.swing.border.TitledBorder.TOP, new java.awt.Font(
+                "SansSerif", 0, 14 ), new java.awt.Color( 60, 60, 60 ) ) );
+        jPanel2.add( jScrollPane1, java.awt.BorderLayout.CENTER );
+        jButton1.setText( "Done" );
+        jButton1.setActionCommand( "Done" );
+        jButton1.addActionListener( new ActionListener()
+        {
+            public void actionPerformed( ActionEvent event )
+            {
+                SearchResultDialog.this.setVisible( false );
+                SearchResultDialog.this.dispose();
+            }
+        } );
+        jButton1.setHorizontalAlignment( javax.swing.SwingConstants.CENTER );
+        jButton1.setAlignmentX( 0.5f );
+        jButton1.setHorizontalTextPosition( javax.swing.SwingConstants.CENTER );
+        jPanel3.setPreferredSize( new java.awt.Dimension( 79, 41 ) );
+        jPanel3.setMinimumSize( new java.awt.Dimension( 79, 41 ) );
+        jPanel3.setSize( new java.awt.Dimension( 471, 35 ) );
+        jPanel3.setToolTipText( "" );
+        jPanel3.add( jButton1 );
+        jPanel4.setBorder( javax.swing.BorderFactory.createTitledBorder( javax.swing.BorderFactory.createLineBorder(
+            new java.awt.Color( 153, 153, 153 ), 1 ), "Search Results", javax.swing.border.TitledBorder.LEADING,
+            javax.swing.border.TitledBorder.TOP, new java.awt.Font( "SansSerif", 0, 14 ), new java.awt.Color( 60, 60,
+                60 ) ) );
+        jPanel4.setLayout( new java.awt.BorderLayout() );
+        jPanel4.add( jScrollPane2, java.awt.BorderLayout.CENTER );
+        jScrollPane2.getViewport().add( m_resultsTbl );
+        m_resultsTbl.setSize( new java.awt.Dimension( 450, 10 ) );
+        m_resultsTbl.getSelectionModel().addListSelectionListener( this );
+    }
+
+
+    public void valueChanged( ListSelectionEvent an_event )
+    {
+        ListSelectionModel selectionModel = ( ListSelectionModel ) an_event.getSource();
+        int minIndex = selectionModel.getMinSelectionIndex();
+        int maxIndex = selectionModel.getMaxSelectionIndex();
+
+        for ( int ii = minIndex; ii <= maxIndex; ii++ )
+        {
+            if ( selectionModel.isSelectedIndex( ii ) && !an_event.getValueIsAdjusting() )
+            {
+                Long id = ( Long ) m_resultsTbl.getModel().getValueAt( ii, 0 );
+                ( ( PartitionFrame ) getParent() ).selectTreeNode( id );
+            }
+        }
+    }
+
+
+    /** Closes the dialog */
+    private void closeDialog( WindowEvent evt )
+    {
+        evt.getWindow();
+        setVisible( false );
+        dispose();
+    }
+
+
+    public void setTreeModel( TreeModel model )
+    {
+        this.jTree1.setModel( model );
+    }
+
+
+    public void setFilter( String filter )
+    {
+        this.jTextArea1.setText( filter );
+    }
+
+
+    public void setTableModel( TableModel model )
+    {
+        m_resultsTbl.setModel( model );
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/BigIntegerSerializer.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/BigIntegerSerializer.java
new file mode 100644
index 0000000..2bd396d
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/BigIntegerSerializer.java
@@ -0,0 +1,56 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree.jdbm;
+
+
+import java.io.IOException;
+import java.math.BigInteger;
+
+import jdbm.helper.Serializer;
+
+
+/**
+ * A custom BigInteger serializer to [de]serialize BigIntegers.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class BigIntegerSerializer implements Serializer
+{
+    private static final long serialVersionUID = 6768192848157685880L;
+
+
+    /* (non-Javadoc)
+     * @see jdbm.helper.Serializer#deserialize(byte[])
+     */
+    public Object deserialize( byte[] bigIntBytes ) throws IOException
+    {
+        return new BigInteger( bigIntBytes );
+    }
+
+
+    /* (non-Javadoc)
+     * @see jdbm.helper.Serializer#serialize(java.lang.Object)
+     */
+    public byte[] serialize( Object bigInt ) throws IOException
+    {
+        return ( ( BigInteger ) bigInt ).toByteArray();
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmPartition.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmPartition.java
new file mode 100644
index 0000000..32f99be
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmPartition.java
@@ -0,0 +1,587 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree.jdbm;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.interceptor.context.AddOperationContext;
+import org.apache.directory.server.core.interceptor.context.BindOperationContext;
+import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
+import org.apache.directory.server.core.interceptor.context.MoveAndRenameOperationContext;
+import org.apache.directory.server.core.interceptor.context.MoveOperationContext;
+import org.apache.directory.server.core.interceptor.context.RenameOperationContext;
+import org.apache.directory.server.core.interceptor.context.UnbindOperationContext;
+import org.apache.directory.server.core.partition.Oid;
+import org.apache.directory.server.core.partition.Partition;
+import org.apache.directory.server.core.partition.impl.btree.BTreePartition;
+import org.apache.directory.server.core.partition.impl.btree.DefaultOptimizer;
+import org.apache.directory.server.core.partition.impl.btree.DefaultSearchEngine;
+import org.apache.directory.server.core.partition.impl.btree.ExpressionEnumerator;
+import org.apache.directory.server.core.partition.impl.btree.ExpressionEvaluator;
+import org.apache.directory.server.core.partition.impl.btree.Index;
+import org.apache.directory.server.core.partition.impl.btree.IndexNotFoundException;
+import org.apache.directory.server.core.partition.impl.btree.NoOpOptimizer;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.exception.LdapAuthenticationNotSupportedException;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+
+import java.io.File;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+
+/**
+ * A {@link Partition} that stores entries in
+ * <a href="http://jdbm.sourceforge.net/">JDBM</a> database.
+ *
+ * @org.apache.xbean.XBean
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class JdbmPartition extends BTreePartition
+{
+    private JdbmStore store;
+    private boolean optimizerEnabled = true;
+    private Set<Index> indexedAttributes;
+
+    
+    // ------------------------------------------------------------------------
+    // C O N S T R U C T O R S
+    // ------------------------------------------------------------------------
+
+
+    /**
+     * Creates a store based on JDBM B+Trees.
+     */
+    public JdbmPartition()
+    {
+        store = new JdbmStore();
+        indexedAttributes = new HashSet<Index>();
+    }
+
+
+    // ------------------------------------------------------------------------
+    // C O N F I G U R A T I O N   M E T H O D S
+    // ------------------------------------------------------------------------
+
+
+    public String getSuffix()
+    {
+        return super.suffix;
+    }
+
+
+    public void setSuffix( String suffix )
+    {
+        super.suffix = suffix;
+    }
+
+
+    public void setIndexedAttributes( Set<Index> indexedAttributes )
+    {
+        this.indexedAttributes = indexedAttributes;
+    }
+
+
+    public Set<Index> getIndexedAttributes()
+    {
+        return indexedAttributes;
+    }
+
+
+    public boolean isOptimizerEnabled()
+    {
+        return optimizerEnabled;
+    }
+
+
+    public void setOptimizerEnabled( boolean optimizerEnabled )
+    {
+        this.optimizerEnabled = optimizerEnabled;
+    }
+
+
+    public void setSyncOnWrite( boolean syncOnWrite )
+    {
+        store.setSyncOnWrite( syncOnWrite );
+    }
+
+
+    public boolean isSyncOnWrite()
+    {
+        return store.isSyncOnWrite();
+    }
+
+
+    // ------------------------------------------------------------------------
+    // E N D   C O N F I G U R A T I O N   M E T H O D S
+    // ------------------------------------------------------------------------
+
+
+    public void setRegistries( Registries registries )
+    {
+        initRegistries( registries );
+    }
+
+
+    protected void initRegistries( Registries registries )
+    {
+        this.registries = registries;
+        ExpressionEvaluator evaluator = new ExpressionEvaluator( this, registries );
+        ExpressionEnumerator enumerator = new ExpressionEnumerator( this, registries.getAttributeTypeRegistry(), evaluator );
+        this.searchEngine = new DefaultSearchEngine( this, evaluator, enumerator, optimizer );
+        store.initRegistries( registries );
+    }
+
+
+    public final void init( DirectoryService directoryService ) throws NamingException
+    {
+        // setup optimizer and registries for parent
+        if ( ! optimizerEnabled )
+        {
+            optimizer = new NoOpOptimizer();
+        }
+        else
+        {
+            optimizer = new DefaultOptimizer( this );
+        }
+
+        initRegistries( directoryService.getRegistries() );
+        
+        // initialize the store
+        store.setCacheSize( cacheSize );
+        store.setContextEntry( contextEntry );
+        store.setName( id );
+        store.setSuffixDn( suffix );
+        store.setWorkingDirectory( new File( directoryService.getWorkingDirectory().getPath() + File.separator + id ) );
+
+        Set<JdbmIndex> userIndices = new HashSet<JdbmIndex>();
+        for ( Index obj : indexedAttributes )
+        {
+            JdbmIndex index;
+
+            if ( obj instanceof JdbmIndex )
+            {
+                index = ( JdbmIndex ) obj;
+            }
+            else
+            {
+                index = new JdbmIndex();
+                index.setAttributeId( obj.getAttributeId() );
+                index.setCacheSize( obj.getCacheSize() );
+                index.setWkDirPath( obj.getWkDirPath() );
+            }
+
+            String oid = registries.getOidRegistry().getOid( index.getAttributeId() );
+            if ( SYS_INDEX_OIDS.contains( registries.getOidRegistry().getOid( index.getAttributeId() ) ) )
+            {
+                if ( oid.equals( Oid.ALIAS ) )
+                {
+                    store.setAliasIndex( index );
+                }
+                else if ( oid.equals( Oid.EXISTANCE ) )
+                {
+                    store.setExistanceIndex( index );
+                }
+                else if ( oid.equals( Oid.HIERARCHY ) )
+                {
+                    store.setHierarchyIndex( index );
+                }
+                else if ( oid.equals( Oid.NDN ) )
+                {
+                    store.setNdnIndex( index );
+                }
+                else if ( oid.equals( Oid.ONEALIAS ) )
+                {
+                    store.setOneAliasIndex( index );
+                }
+                else if ( oid.equals( Oid.SUBALIAS ) )
+                {
+                    store.setSubAliasIndex( index );
+                }
+                else if ( oid.equals( Oid.UPDN ) )
+                {
+                    store.setUpdnIndex( index );
+                }
+                else
+                {
+                    throw new IllegalStateException( "Unrecognized system index " + oid );
+                }
+            }
+            else
+            {
+                userIndices.add( index );
+            }
+            store.setUserIndices( userIndices );
+            store.setEnableOptimizer( isOptimizerEnabled() );
+        }
+
+        store.init( registries );
+    }
+
+
+    public final void destroy()
+    {
+        store.destroy();
+    }
+
+
+    public final boolean isInitialized()
+    {
+        return store.isInitialized();
+    }
+
+
+    public final void sync() throws NamingException
+    {
+        store.sync();
+    }
+
+
+    // ------------------------------------------------------------------------
+    // I N D E X   M E T H O D S
+    // ------------------------------------------------------------------------
+
+
+    public final void addIndexOn( Index index ) throws NamingException
+    {
+        if ( index instanceof JdbmIndex )
+        {
+            store.addIndex( ( JdbmIndex ) index );
+        }
+    }
+
+
+    public final Index getExistanceIndex()
+    {
+        return store.getExistanceIndex();
+    }
+
+
+    public final void setExistanceIndexOn( Index index ) throws NamingException
+    {
+        if ( index instanceof JdbmIndex )
+        {
+            store.setExistanceIndex( ( JdbmIndex ) index );
+        }
+    }
+
+
+    public final Index getHierarchyIndex()
+    {
+        return store.getHierarchyIndex();
+    }
+
+
+    public final void setHierarchyIndexOn( Index index ) throws NamingException
+    {
+        if ( index instanceof JdbmIndex )
+        {
+            store.setHierarchyIndex( ( JdbmIndex ) index );
+        }
+    }
+
+
+    public final Index getAliasIndex()
+    {
+        return store.getAliasIndex();
+    }
+
+
+    public final void setAliasIndexOn( Index index ) throws NamingException
+    {
+        if ( index instanceof JdbmIndex )
+        {
+            store.setAliasIndex( ( JdbmIndex ) index );
+        }
+    }
+
+
+    public final Index getOneAliasIndex()
+    {
+        return store.getOneAliasIndex();
+    }
+
+
+    public final void setOneAliasIndexOn( Index index ) throws NamingException
+    {
+        if ( index instanceof JdbmIndex )
+        {
+            store.setOneAliasIndex( ( JdbmIndex ) index );
+        }
+    }
+
+
+    public final Index getSubAliasIndex()
+    {
+        return store.getSubAliasIndex();
+    }
+
+
+    public final void setSubAliasIndexOn( Index index ) throws NamingException
+    {
+        if ( index instanceof JdbmIndex )
+        {
+            store.setSubAliasIndex( ( JdbmIndex ) index );
+        }
+    }
+
+
+    public final Index getUpdnIndex()
+    {
+        return store.getUpdnIndex();
+    }
+
+
+    public final void setUpdnIndexOn( Index index ) throws NamingException
+    {
+        if ( index instanceof JdbmIndex )
+        {
+            store.setUpdnIndex( ( JdbmIndex ) index );
+        }
+    }
+
+
+    public final Index getNdnIndex()
+    {
+        return store.getNdnIndex();
+    }
+
+
+    public final void setNdnIndexOn( Index index ) throws NamingException
+    {
+        if ( index instanceof JdbmIndex )
+        {
+            store.setNdnIndex( ( JdbmIndex ) index );
+        }
+    }
+
+
+    public final Iterator<String> getUserIndices()
+    {
+        return store.userIndices();
+    }
+
+
+    public final Iterator<String> getSystemIndices()
+    {
+        return store.systemIndices();
+    }
+
+
+    public final boolean hasUserIndexOn( String id ) throws NamingException
+    {
+        return store.hasUserIndexOn( id );
+    }
+
+
+    public final boolean hasSystemIndexOn( String id ) throws NamingException
+    {
+        return store.hasSystemIndexOn( id );
+    }
+
+
+    /**
+     * @see org.apache.directory.server.core.partition.impl.btree.BTreePartition#getUserIndex(String)
+     */
+    public final Index getUserIndex( String id ) throws IndexNotFoundException
+    {
+        return store.getUserIndex( id );
+    }
+
+
+    /**
+     * @see BTreePartition#getEntryId(String)
+     */
+    public final Index getSystemIndex( String id ) throws IndexNotFoundException
+    {
+        return store.getSystemIndex( id );
+    }
+
+
+    public final Long getEntryId( String dn ) throws NamingException
+    {
+        return store.getEntryId( dn );
+    }
+
+
+    public final String getEntryDn( Long id ) throws NamingException
+    {
+        return store.getEntryDn( id );
+    }
+
+
+    public final Long getParentId( String dn ) throws NamingException
+    {
+        return store.getParentId( dn );
+    }
+
+
+    public final Long getParentId( Long childId ) throws NamingException
+    {
+        return store.getParentId( childId );
+    }
+
+
+    public final String getEntryUpdn( Long id ) throws NamingException
+    {
+        return store.getEntryUpdn( id );
+    }
+
+
+    public final String getEntryUpdn( String dn ) throws NamingException
+    {
+        return store.getEntryUpdn( dn );
+    }
+
+
+    public final int count() throws NamingException
+    {
+        return store.count();
+    }
+
+    
+    public final void add( AddOperationContext addContext ) throws NamingException
+    {
+        store.add( addContext.getDn(), addContext.getEntry() );
+    }
+
+
+    public final ServerEntry lookup( Long id ) throws NamingException
+    {
+        return store.lookup( id );
+    }
+
+
+    public final void delete( Long id ) throws NamingException
+    {
+        store.delete( id );
+    }
+
+
+    public final NamingEnumeration list( Long id ) throws NamingException
+    {
+        return store.list( id );
+    }
+
+
+    public final int getChildCount( Long id ) throws NamingException
+    {
+        return store.getChildCount( id );
+    }
+
+
+    public final LdapDN getSuffixDn()
+    {
+        return store.getSuffix();
+    }
+
+    public final LdapDN getUpSuffixDn()
+    {
+        return store.getUpSuffix();
+    }
+
+
+    public final ServerEntry getSuffixEntry() throws NamingException
+    {
+        return store.getSuffixEntry();
+    }
+
+
+    public final void setProperty( String propertyName, String propertyValue ) throws NamingException
+    {
+        store.setProperty( propertyName, propertyValue );
+    }
+
+
+    public final String getProperty( String propertyName ) throws NamingException
+    {
+        return store.getProperty( propertyName );
+    }
+
+
+    public final ServerEntry getIndices( Long id ) throws NamingException
+    {
+        return store.getIndices( id );
+    }
+
+    
+    public final void modify( ModifyOperationContext modifyContext ) throws NamingException
+    {
+        store.modify( modifyContext.getDn(), modifyContext.getModItems() );
+    }
+
+    public final void rename( RenameOperationContext renameContext ) throws NamingException
+    {
+        store.rename( renameContext.getDn(), renameContext.getNewRdn(), renameContext.getDelOldDn() );
+    }
+
+
+    public final void moveAndRename( MoveAndRenameOperationContext moveAndRenameContext ) throws NamingException
+    {
+        store.move( moveAndRenameContext.getDn(), 
+            moveAndRenameContext.getParent(), 
+            moveAndRenameContext.getNewRdn(), 
+            moveAndRenameContext.getDelOldDn() );
+    }
+
+
+    public final void move( MoveOperationContext moveContext ) throws NamingException
+    {
+        store.move( moveContext.getDn(), moveContext.getParent() );
+    }
+
+
+    public final void bind( LdapDN bindDn, byte[] credentials, List<String> mechanisms, String saslAuthId ) throws NamingException
+    {
+        if ( bindDn == null || credentials == null || mechanisms == null ||  saslAuthId == null )
+        {
+            // do nothing just using variables to prevent yellow lights : bad :)
+        }
+        
+        // does nothing
+        throw new LdapAuthenticationNotSupportedException(
+                "Bind requests only tunnel down into partitions if there are no authenticators to handle the mechanism.\n"
+                        + "Check to see if you have correctly configured authenticators for the server.",
+                ResultCodeEnum.AUTH_METHOD_NOT_SUPPORTED );
+    }
+    
+
+    public final void bind( BindOperationContext bindContext ) throws NamingException
+    {
+        // does nothing
+        throw new LdapAuthenticationNotSupportedException(
+            "Bind requests only tunnel down into partitions if there are no authenticators to handle the mechanism.\n"
+                + "Check to see if you have correctly configured authenticators for the server.",
+            ResultCodeEnum.AUTH_METHOD_NOT_SUPPORTED );
+    }
+
+
+    public final void unbind( UnbindOperationContext unbindContext ) throws NamingException
+    {
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/StringSerializer.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/StringSerializer.java
new file mode 100644
index 0000000..6ff30de
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/StringSerializer.java
@@ -0,0 +1,82 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree.jdbm;
+
+
+import java.io.IOException;
+
+import jdbm.helper.Serializer;
+
+
+/**
+ * A custom String serializer to [de]serialize Strings.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class StringSerializer implements Serializer
+{
+    private static final long serialVersionUID = -173163945773783649L;
+
+
+    /* (non-Javadoc)
+     * @see jdbm.helper.Serializer#deserialize(byte[])
+     */
+    public Object deserialize( byte[] bites ) throws IOException
+    {
+        if ( bites.length == 0 )
+        {
+            return "";
+        }
+
+        char[] strchars = new char[bites.length >> 1];
+        for ( int ii = 0, jj = 0; ii < strchars.length; ii++, jj = ii << 1 )
+        {
+            int ch = bites[jj] << 8 & 0x0000FF00;
+            ch |= bites[jj+1] & 0x000000FF;
+            strchars[ii] = ( char ) ch;
+        }
+        return new String( strchars );
+    }
+
+
+    private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
+    
+    /* (non-Javadoc)
+     * @see jdbm.helper.Serializer#serialize(java.lang.Object)
+     */
+    public byte[] serialize( Object str ) throws IOException
+    {
+        if ( ( ( String ) str ).length() == 0 )
+        {
+            return EMPTY_BYTE_ARRAY;
+        }
+        
+        char[] strchars = ( ( String ) str ).toCharArray();
+        byte[] bites = new byte[strchars.length<<1];
+        for ( int ii = 0, jj = 0; ii < strchars.length; ii++, jj = ii << 1 )
+        {
+            bites[jj] = ( byte ) ( strchars[ii] >> 8 & 0x00FF );
+            bites[jj+1] = ( byte ) ( strchars[ii] & 0x00FF );
+        }
+        
+        return bites;
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/tree/BranchNode.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/tree/BranchNode.java
new file mode 100644
index 0000000..9931917
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/tree/BranchNode.java
@@ -0,0 +1,187 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.tree;
+
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.core.partition.Partition;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * 
+ * The Partition Container holds entries which can be either Partitions or 
+ * Containers. 
+ * 
+ * We can see them as directories, where Partitions are the files.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class BranchNode implements Node
+{
+    private static final Logger LOG = LoggerFactory.getLogger( BranchNode.class );
+
+    /** Stores the list of all the descendant partitions and containers */
+    private Map<String, Node> children;
+    
+    
+    /**
+     * Creates a new instance of a BranchNode.
+     */
+    public BranchNode()
+    {
+        children = new HashMap<String, Node>(3);
+    }
+
+    
+    /**
+     * @see Node#isLeaf()
+     */
+    public boolean isLeaf()
+    {
+        return false;
+    }
+    
+    
+    /**
+     * Recursively adds new nodes to the partition lookup tree data structure.  
+     * When called it will add a partition to the tree in the appropriate leaf 
+     * node position based on the DN passed in as an argument.
+     *
+     * @param current The current node having a partition added to it
+     * @param dn The DN associated with the partition
+     * @param index The index of the current RDN being processed 
+     * @param partition The associated partition to add as a tree node
+     * @return The modified tree structure.
+     */
+    public BranchNode recursivelyAddPartition( BranchNode current, LdapDN dn, int index, Partition partition ) throws NamingException
+    {
+        String rdnAtIndex = dn.getRdn( index ).toString();
+        
+        if ( index == dn.size() - 1 )
+        {
+            return current.addNode( rdnAtIndex, new LeafNode( partition ) );
+        }
+        else
+        {
+            Node newNode = current.getChild( rdnAtIndex );
+            
+            if ( newNode instanceof LeafNode )
+            {
+                String message = "Overlapping partitions are not allowed";
+                LOG.error( message );
+                throw new NamingException( message );
+            }
+        
+            if ( newNode == null )
+            {
+                newNode = new BranchNode();
+            }
+
+            Node child = recursivelyAddPartition( (BranchNode)newNode, dn, index + 1, partition );
+            return current.addNode( rdnAtIndex, child );
+        }
+    }
+    
+    
+    /**
+     * Directly adds a new child Node to the current BranchNode.
+     *
+     * @param rdn The rdn of the child node to add 
+     * @param child The child node to add
+     * @return The modified branch node after the insertion
+     */
+    public BranchNode addNode( String rdn, Node child )
+    {
+        children.put( rdn, child );
+        return this;
+    }
+    
+    
+    /**
+     * Tells if the current BranchNode contains another node associated 
+     * with an rdn.
+     *
+     * @param rdn The name we are looking for
+     * @return <code>true</code> if the PartitionStructure instance contains this name
+     */
+    public boolean contains( String rdn )
+    {
+        return children.containsKey( rdn );
+    }
+
+    
+    /**
+     * Get's a child using an rdn string.
+     * 
+     * @param rdn the rdn to use as the node key
+     * @return the child node corresponding to the rdn.
+     */
+    public Node getChild( String rdn )
+    {
+        if ( children.containsKey( rdn ) )
+        {
+            return children.get( rdn );
+        }
+
+        return null;
+    }
+    
+    
+    /**
+     * @see Object#toString()
+     */
+    public String toString()
+    {
+        StringBuilder sb = new StringBuilder();
+        sb.append( "{" );
+        boolean isFirst = true;
+        
+        for ( Node child:children.values() )
+        {
+            if ( isFirst )
+            {
+                isFirst = false;
+            }
+            else
+            {
+                sb.append(  ", " );
+            }
+
+            if ( child instanceof BranchNode )
+            {
+                sb.append( "Branch: ").append( child.toString() );
+            }
+            else
+            {
+                sb.append( "Leaf: " ).append( "'" ).append( child.toString() ).append( "'" );
+            }
+        }
+
+        sb.append( "}" );
+        return sb.toString();
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/tree/LeafNode.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/tree/LeafNode.java
new file mode 100644
index 0000000..97fb758
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/tree/LeafNode.java
@@ -0,0 +1,70 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.tree;
+
+
+import org.apache.directory.server.core.partition.Partition;
+
+
+/**
+ * A leaf node which stores a Partition. These objects are stored in BranchNodes.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class LeafNode implements Node
+{
+    /** The stored partition */
+    private Partition partition;
+
+    
+    /**
+     * Creates a new instance of LeafNode.
+     *
+     * @param partition the partition to store
+     */
+    public LeafNode( Partition partition )
+    {
+        this.partition = partition;
+    }
+
+    
+    /**
+     * @see Node#isLeaf()
+     */
+    public boolean isLeaf()
+    {
+        return true;
+    }
+    
+
+    public Partition getPartition()
+    {
+        return partition;
+    }
+
+    
+    /**
+     * @see Object#toString()
+     */
+    public String toString()
+    {
+        return partition.getSuffix();
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/tree/Node.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/tree/Node.java
new file mode 100644
index 0000000..bfe61bc
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/tree/Node.java
@@ -0,0 +1,40 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.tree;
+
+
+/**
+ * An interface for nodes in a tree designed to quickly lookup partitions.
+ * Branch nodes in this tree contain other nodes.  Leaf nodes in the tree
+ * contain a referrence to a partition whose suffix is the path through the 
+ * nodes of the tree from the root.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public interface Node
+{
+    /**
+     * Tells if the implementation is a leaf node. If it's a branch node
+     * then false is returned.
+     *
+     * @return <code>true</code> if the class is a leaf node, false otherwise.
+     */
+    boolean isLeaf();
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/tree/package-info.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/tree/package-info.java
new file mode 100644
index 0000000..400aafd
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/partition/tree/package-info.java
@@ -0,0 +1,36 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.
+ *
+ */
+
+/**
+ * <pre>
+ * <p>
+ * Set of classes and interfaces for a special data tree structure used to quickly
+ * find partitions corresponding to an entry by DN.  The data structure contains
+ * branch nodes which store other nodes as well as leaf nodes which referrence 
+ * partitions whose suffix is the path from the root of the tree down to the node.
+ * Each node contains an RDN used for it's position.
+ * </p>
+ * </pre>
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+
+package org.apache.directory.server.core.partition.tree;
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/prefs/PreferencesUtils.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/prefs/PreferencesUtils.java
new file mode 100644
index 0000000..390901d
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/prefs/PreferencesUtils.java
@@ -0,0 +1,62 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.prefs;
+
+ 
+import javax.naming.Name;
+import javax.naming.NamingException;
+
+import org.apache.directory.server.constants.ServerDNConstants;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+
+/**
+ * Document this class.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+class PreferencesUtils
+{
+    /**
+     * Translates an absolute system preferences node name into the distinguished
+     * name of the entry corresponding to the preferences node.
+     *
+     * @param absPrefPath the absolute path to the system preferences node
+     * @return the distinguished name of the entry representing the system preferences node
+     * @throws NamingException if there are namespace problems while translating the path
+     */
+    public static Name toSysDn( String absPrefPath ) throws NamingException
+    {
+        LdapDN dn = new LdapDN( ServerDNConstants.SYSPREFROOT_SYSTEM_DN );
+
+        String[] comps = absPrefPath.split( "/" );
+
+        for ( int ii = 0; ii < comps.length; ii++ )
+        {
+            if ( comps[ii] != null && !comps[ii].trim().equals( "" ) )
+            {
+                dn.add( "prefNodeName=" + comps[ii] );
+            }
+        }
+
+        return dn;
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/prefs/ServerPreferencesFactory.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/prefs/ServerPreferencesFactory.java
new file mode 100644
index 0000000..82105d0
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/prefs/ServerPreferencesFactory.java
@@ -0,0 +1,59 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.prefs;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.shared.ldap.NotImplementedException;
+
+import java.util.prefs.Preferences;
+import java.util.prefs.PreferencesFactory;
+
+
+/**
+ * A preferences factory implementation.  Currently the userRoot() preferences
+ * are not available and will throw NotImplementedExceptions.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class ServerPreferencesFactory implements PreferencesFactory
+{
+    private final DirectoryService directoryService;
+
+
+    public ServerPreferencesFactory( DirectoryService directoryService )
+    {
+        this.directoryService = directoryService;
+    }
+
+
+    public Preferences systemRoot()
+    {
+        return new ServerSystemPreferences( directoryService );
+    }
+
+
+    public Preferences userRoot()
+    {
+        throw new NotImplementedException(
+            "userRoot() in org.apache.directory.server.prefs.ServerPreferencesFactory not implemented!" );
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/prefs/ServerSystemPreferenceException.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/prefs/ServerSystemPreferenceException.java
new file mode 100644
index 0000000..758d095
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/prefs/ServerSystemPreferenceException.java
@@ -0,0 +1,57 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.prefs;
+
+
+/**
+ * A {@link RuntimeException} that is thrown when accessing
+ * {@link ServerSystemPreferences} failed.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ServerSystemPreferenceException extends RuntimeException
+{
+    private static final long serialVersionUID = -2042269063779317751L;
+
+
+    public ServerSystemPreferenceException()
+    {
+        super();
+    }
+
+
+    public ServerSystemPreferenceException(String message)
+    {
+        super( message );
+    }
+
+
+    public ServerSystemPreferenceException(String message, Throwable cause)
+    {
+        super( message, cause );
+    }
+
+
+    public ServerSystemPreferenceException(Throwable cause)
+    {
+        super( cause );
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/prefs/ServerSystemPreferences.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/prefs/ServerSystemPreferences.java
new file mode 100644
index 0000000..d03a25a
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/prefs/ServerSystemPreferences.java
@@ -0,0 +1,414 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.prefs;
+
+
+import org.apache.directory.server.constants.ApacheSchemaConstants;
+import org.apache.directory.server.constants.ServerDNConstants;
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.jndi.CoreContextFactory;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.message.ModificationItemImpl;
+import org.apache.directory.shared.ldap.util.PreferencesDictionary;
+
+import javax.naming.Context;
+import javax.naming.NameClassPair;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.ModificationItem;
+import javax.naming.ldap.InitialLdapContext;
+import javax.naming.ldap.LdapContext;
+
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.prefs.AbstractPreferences;
+import java.util.prefs.BackingStoreException;
+import java.util.prefs.Preferences;
+
+
+/**
+ * A server side system {@link Preferences} implementation.  This implementation
+ * presumes the creation of a root system preferences node in advance.  This
+ * should be included with the system.ldif that is packaged with the server.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class ServerSystemPreferences extends AbstractPreferences
+{
+    /** an empty array of ModificationItems used to get array from list */
+    private static final ModificationItemImpl[] EMPTY_MODS = new ModificationItemImpl[0];
+
+    /** an empty array of Strings used to get array from list */
+    private static final String[] EMPTY_STRINGS = new String[0];
+
+    /** the LDAP context representing this preferences object */
+    private LdapContext ctx;
+
+    /** the changes (ModificationItems) representing cached alterations to preferences */
+    private List<ModificationItem> changes = new ArrayList<ModificationItem>( 3 );
+
+    /** maps changes based on key: key->list of mods (on same key) */
+    private HashMap<String, List<ModificationItem>> keyToChange = new HashMap<String, List<ModificationItem>>( 3 );
+
+
+    /**
+     * Creates a preferences object for the system preferences root.
+     * @param service the directory service core
+     */
+    public ServerSystemPreferences( DirectoryService service )
+    {
+        super( null, "" );
+        super.newNode = false;
+
+        Hashtable<String, Object> env = new Hashtable<String, Object>();
+        env.put( DirectoryService.JNDI_KEY, service );
+        env.put( Context.INITIAL_CONTEXT_FACTORY, CoreContextFactory.class.getName() );
+        env.put( Context.PROVIDER_URL, ServerDNConstants.SYSPREFROOT_SYSTEM_DN );
+
+        try
+        {
+            ctx = new InitialLdapContext( env, null );
+        }
+        catch ( Exception e )
+        {
+            throw new ServerSystemPreferenceException( "Failed to open.", e );
+        }
+    }
+
+
+    public synchronized void close() throws NamingException
+    {
+        if ( this.parent() != null )
+        {
+            throw new ServerSystemPreferenceException( "Cannot close child preferences." );
+        }
+
+        this.ctx.close();
+    }
+
+
+    /**
+     * Creates a preferences object using a relative name.
+     * 
+     * @param name the name of the preference node to create
+     * @param parent the parent of the preferences node to create
+     */
+    public ServerSystemPreferences( ServerSystemPreferences parent, String name )
+    {
+        super( parent, name );
+        LdapContext parentCtx = parent.getLdapContext();
+
+        try
+        {
+            ctx = ( LdapContext ) parentCtx.lookup( "prefNodeName=" + name );
+            super.newNode = false;
+        }
+        catch ( NamingException e )
+        {
+            super.newNode = true;
+        }
+
+        if ( super.newNode )
+        {
+            try
+            {
+                setUpNode( name );
+            }
+            catch ( Exception e )
+            {
+                throw new ServerSystemPreferenceException( "Failed to set up node.", e );
+            }
+        }
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Utility Methods
+    // ------------------------------------------------------------------------
+
+    /**
+     * Wrapps this ServerPreferences object as a Dictionary.
+     *
+     * @return a Dictionary that uses this ServerPreferences object as the underlying backing store
+     */
+    public Dictionary wrapAsDictionary()
+    {
+        return new PreferencesDictionary( this );
+    }
+
+
+    /**
+     * Gets access to the LDAP context associated with this ServerPreferences node.
+     *
+     * @return the LDAP context associate with this ServerPreferences node
+     */
+    LdapContext getLdapContext()
+    {
+        return ctx;
+    }
+
+
+    /**
+     * Sets up a new ServerPreferences node by injecting the required information
+     * such as the node name attribute and the objectClass attribute.
+     *
+     * @param name the name of the new ServerPreferences node
+     * @throws NamingException if we fail to created the new node
+     */
+    private void setUpNode( String name ) throws NamingException
+    {
+        Attributes attrs = new AttributesImpl();
+        Attribute attr = new AttributeImpl( SchemaConstants.OBJECT_CLASS_AT );
+        attr.add( SchemaConstants.TOP_OC );
+        attr.add( ApacheSchemaConstants.PREF_NODE_OC );
+        attr.add( SchemaConstants.EXTENSIBLE_OBJECT_OC );
+        attrs.put( attr );
+        attr = new AttributeImpl( "prefNodeName" );
+        attr.add( name );
+        attrs.put( attr );
+
+        LdapContext parent = ( ( ServerSystemPreferences ) parent() ).getLdapContext();
+        parent.bind( "prefNodeName=" + name, null, attrs );
+        ctx = ( LdapContext ) parent.lookup( "prefNodeName=" + name );
+        super.newNode = false;
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Protected SPI Methods
+    // ------------------------------------------------------------------------
+
+    protected void flushSpi() throws BackingStoreException
+    {
+        if ( ctx == null )
+        {
+            throw new BackingStoreException( "Ldap context not available for " + super.absolutePath() );
+        }
+
+        if ( changes.isEmpty() )
+        {
+            return;
+        }
+
+        try
+        {
+            //noinspection SuspiciousToArrayCall
+            ctx.modifyAttributes( "", changes.toArray( EMPTY_MODS ) );
+        }
+        catch ( NamingException e )
+        {
+            throw new BackingStoreException( e );
+        }
+
+        changes.clear();
+        keyToChange.clear();
+    }
+
+
+    protected void removeNodeSpi() throws BackingStoreException
+    {
+        try
+        {
+            ctx.destroySubcontext( "" );
+        }
+        catch ( NamingException e )
+        {
+            throw new BackingStoreException( e );
+        }
+
+        ctx = null;
+        changes.clear();
+        keyToChange.clear();
+    }
+
+
+    protected void syncSpi() throws BackingStoreException
+    {
+        if ( ctx == null )
+        {
+            throw new BackingStoreException( "Ldap context not available for " + super.absolutePath() );
+        }
+
+        if ( changes.isEmpty() )
+        {
+            return;
+        }
+
+        try
+        {
+            //noinspection SuspiciousToArrayCall
+            ctx.modifyAttributes( "", changes.toArray( EMPTY_MODS ) );
+        }
+        catch ( NamingException e )
+        {
+            throw new BackingStoreException( e );
+        }
+
+        changes.clear();
+        keyToChange.clear();
+    }
+
+
+    protected String[] childrenNamesSpi() throws BackingStoreException
+    {
+        List<String> children = new ArrayList<String>();
+        NamingEnumeration list;
+
+        try
+        {
+            list = ctx.list( "" );
+            
+            while ( list.hasMore() )
+            {
+                NameClassPair ncp = ( NameClassPair ) list.next();
+
+                children.add( ncp.getName() );
+            }
+        }
+        catch ( NamingException e )
+        {
+            throw new BackingStoreException( e );
+        }
+
+        return children.toArray( EMPTY_STRINGS );
+    }
+
+
+    protected String[] keysSpi() throws BackingStoreException
+    {
+        Attributes attrs;
+        List<String> keys = new ArrayList<String>();
+
+        try
+        {
+            attrs = ctx.getAttributes( "" );
+            NamingEnumeration ids = attrs.getIDs();
+            
+            while ( ids.hasMore() )
+            {
+                String id = ( String ) ids.next();
+                
+                if ( id.equals( SchemaConstants.OBJECT_CLASS_AT ) || id.equals( "prefNodeName" ) )
+                {
+                    continue;
+                }
+                
+                keys.add( id );
+            }
+        }
+        catch ( NamingException e )
+        {
+            throw new BackingStoreException( e );
+        }
+
+        return keys.toArray( EMPTY_STRINGS );
+    }
+
+
+    protected void removeSpi( String key )
+    {
+        Attribute attr = new AttributeImpl( key );
+        ModificationItemImpl mi = new ModificationItemImpl( DirContext.REMOVE_ATTRIBUTE, attr );
+        addDelta( mi );
+    }
+
+
+    private void addDelta( ModificationItemImpl mi )
+    {
+        String key = mi.getAttribute().getID();
+        List<ModificationItem> deltas;
+        changes.add( mi );
+        
+        if ( keyToChange.containsKey( key ) )
+        {
+            deltas = keyToChange.get( key );
+        }
+        else
+        {
+            deltas = new ArrayList<ModificationItem>();
+        }
+
+        deltas.add( mi );
+        keyToChange.put( key, deltas );
+    }
+
+
+    protected String getSpi( String key )
+    {
+        String value;
+
+        try
+        {
+            Attribute attr = ctx.getAttributes( "" ).get( key );
+            if ( keyToChange.containsKey( key ) )
+            {
+                List<ModificationItem> mods = keyToChange.get( key );
+                for ( ModificationItem mod : mods )
+                {
+                    if ( mod.getModificationOp() == DirContext.REMOVE_ATTRIBUTE )
+                    {
+                        attr = null;
+                    }
+                    else
+                    {
+                        attr = mod.getAttribute();
+                    }
+                }
+            }
+
+            if ( attr == null )
+            {
+                return null;
+            }
+
+            value = ( String ) attr.get();
+        }
+        catch ( Exception e )
+        {
+            throw new ServerSystemPreferenceException( "Failed to get SPI.", e );
+        }
+
+        return value;
+    }
+
+
+    protected void putSpi( String key, String value )
+    {
+        Attribute attr = new AttributeImpl( key );
+        attr.add( value );
+        ModificationItemImpl mi = new ModificationItemImpl( DirContext.REPLACE_ATTRIBUTE, attr );
+        addDelta( mi );
+    }
+
+
+    protected AbstractPreferences childSpi( String name )
+    {
+        return new ServerSystemPreferences( this, name );
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/prefs/package-info.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/prefs/package-info.java
new file mode 100644
index 0000000..ea4625f
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/prefs/package-info.java
@@ -0,0 +1,39 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.
+ *
+ */
+
+/**
+ * <pre>
+ * <p>
+ * Platform independent server side Preferences implementation based on ApacheDS.
+ * The data is backed by the directory using a specific LDAP schema to map
+ * preferences to the LDAP/X.500 namespace.  To make sure you're preferences are
+ * using the right preferences factory implementation please check to see the
+ * following property is set to our implementation:
+ * </p>
+ * <p>
+ * java.util.prefs.PreferencesFactory=org.apache.ldap.server.prefs.ServerPreferencesFactory
+ * <p>
+ * </pre>
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+
+package org.apache.directory.server.core.prefs;
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/referral/ReferralInterceptor.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/referral/ReferralInterceptor.java
new file mode 100644
index 0000000..a360006
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/referral/ReferralInterceptor.java
@@ -0,0 +1,1216 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.referral;
+
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.authn.AuthenticationInterceptor;
+import org.apache.directory.server.core.authz.AciAuthorizationInterceptor;
+import org.apache.directory.server.core.authz.DefaultAuthorizationInterceptor;
+import org.apache.directory.server.core.entry.ServerAttribute;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerSearchResult;
+import org.apache.directory.server.core.entry.ServerStringValue;
+import org.apache.directory.server.core.enumeration.ReferralHandlingEnumeration;
+import org.apache.directory.server.core.enumeration.SearchResultFilter;
+import org.apache.directory.server.core.enumeration.SearchResultFilteringEnumeration;
+import org.apache.directory.server.core.event.EventInterceptor;
+import org.apache.directory.server.core.interceptor.BaseInterceptor;
+import org.apache.directory.server.core.interceptor.NextInterceptor;
+import org.apache.directory.server.core.interceptor.context.AddContextPartitionOperationContext;
+import org.apache.directory.server.core.interceptor.context.AddOperationContext;
+import org.apache.directory.server.core.interceptor.context.CompareOperationContext;
+import org.apache.directory.server.core.interceptor.context.DeleteOperationContext;
+import org.apache.directory.server.core.interceptor.context.LookupOperationContext;
+import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
+import org.apache.directory.server.core.interceptor.context.MoveAndRenameOperationContext;
+import org.apache.directory.server.core.interceptor.context.MoveOperationContext;
+import org.apache.directory.server.core.interceptor.context.RemoveContextPartitionOperationContext;
+import org.apache.directory.server.core.interceptor.context.RenameOperationContext;
+import org.apache.directory.server.core.interceptor.context.SearchOperationContext;
+import org.apache.directory.server.core.invocation.Invocation;
+import org.apache.directory.server.core.invocation.InvocationStack;
+import org.apache.directory.server.core.jndi.ServerLdapContext;
+import org.apache.directory.server.core.normalization.NormalizationInterceptor;
+import org.apache.directory.server.core.operational.OperationalAttributeInterceptor;
+import org.apache.directory.server.core.partition.Partition;
+import org.apache.directory.server.core.partition.PartitionNexus;
+import org.apache.directory.server.core.partition.PartitionNexusProxy;
+import org.apache.directory.server.core.schema.SchemaInterceptor;
+import org.apache.directory.server.core.subtree.SubentryInterceptor;
+import org.apache.directory.server.core.trigger.TriggerInterceptor;
+import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
+import org.apache.directory.server.schema.registries.OidRegistry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.NotImplementedException;
+import org.apache.directory.shared.ldap.codec.util.LdapURL;
+import org.apache.directory.shared.ldap.codec.util.LdapURLEncodingException;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.entry.Modification;
+import org.apache.directory.shared.ldap.entry.Value;
+import org.apache.directory.shared.ldap.entry.client.ClientStringValue;
+import org.apache.directory.shared.ldap.exception.LdapNamingException;
+import org.apache.directory.shared.ldap.exception.LdapReferralException;
+import org.apache.directory.shared.ldap.filter.EqualityNode;
+import org.apache.directory.shared.ldap.filter.ExprNode;
+import org.apache.directory.shared.ldap.message.AliasDerefMode;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.util.StringTools;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.naming.Context;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.SearchControls;
+
+
+/**
+ * An service which is responsible referral handling behavoirs.  It manages 
+ * referral handling behavoir when the {@link Context#REFERRAL} is implicitly
+ * or explicitly set to "ignore", when set to "throw" and when set to "follow". 
+ * 
+ * @org.apache.xbean.XBean
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class ReferralInterceptor extends BaseInterceptor
+{
+    private static final Logger LOG = LoggerFactory.getLogger( ReferralInterceptor.class );
+    private static final String IGNORE = "ignore";
+    private static final String THROW_FINDING_BASE = "throw-finding-base";
+    private static final String THROW = "throw";
+    private static final String FOLLOW = "follow";
+    private static final Collection<String> SEARCH_BYPASS;
+
+    private ReferralLut lut = new ReferralLut();
+    private PartitionNexus nexus;
+
+    /** The attributeType registry */
+    private AttributeTypeRegistry atRegistry;
+
+    /** Thre global registries */
+    private Registries registries;
+
+    /** The OID registry */
+    private OidRegistry oidRegistry;
+
+    static
+    {
+        /*
+         * These are the services that we will bypass while searching for referrals in
+         * partitions of the system during startup and during add/remove partition ops
+         */
+        Collection<String> c = new HashSet<String>();
+        c.add( NormalizationInterceptor.class.getName() );
+        c.add( AuthenticationInterceptor.class.getName() );
+        c.add( ReferralInterceptor.class.getName() );
+        c.add( AciAuthorizationInterceptor.class.getName() );
+        c.add( DefaultAuthorizationInterceptor.class.getName() );
+        //        c.add( ExceptionInterceptor.class.getName() );
+        c.add( OperationalAttributeInterceptor.class.getName() );
+        c.add( SchemaInterceptor.class.getName() );
+        c.add( SubentryInterceptor.class.getName() );
+        //        c.add( CollectiveAttributeInterceptor.class.getName() );
+        c.add( EventInterceptor.class.getName() );
+        c.add( TriggerInterceptor.class.getName() );
+        SEARCH_BYPASS = Collections.unmodifiableCollection( c );
+    }
+
+
+    static boolean isReferral( ServerEntry entry ) throws NamingException
+    {
+        EntryAttribute oc = entry.get( SchemaConstants.OBJECT_CLASS_AT );
+
+        if ( oc == null )
+        {
+            LOG.warn( "could not find objectClass attribute in entry: " + entry );
+            return false;
+        }
+
+        if ( oc.contains( SchemaConstants.REFERRAL_OC ) )
+        {
+            //We have a referral ObjectClass, let's check that the ref is
+            // valid, accordingly to the RFC
+
+            // Get the 'ref' attributeType
+            EntryAttribute refAttr = entry.get( SchemaConstants.REF_AT );
+
+            if ( refAttr == null )
+            {
+                // very unlikely, as we have already checked the entry in SchemaInterceptor
+                String message = "An entry with a 'referral' ObjectClass must contains a 'ref' Attribute";
+                LOG.error( message );
+                throw new NamingException( message );
+            }
+
+            for ( Value<?> value : refAttr )
+            {
+                ServerStringValue ref = ( ServerStringValue ) value;
+
+                String refVal = ref.get();
+
+                try
+                {
+                    LdapURL ldapUrl = new LdapURL( refVal );
+
+                    // We have a LDAP URL, we have to check that :
+                    // - we don't have scope specifier
+                    // - we don't have filters
+                    // - we don't have attribute description list
+                    // - we don't have extensions
+                    // - the DN is not empty
+
+                    if ( ldapUrl.getScope() != SearchControls.OBJECT_SCOPE )
+                    {
+                        // This is the default value if we don't have any scope
+                        // Let's assume that it's incorrect if we get something
+                        // else in the LdapURL
+                        String message = "An LDAPURL should not contains a scope";
+                        LOG.error( message );
+                        throw new NamingException( message );
+                    }
+
+                    if ( !StringTools.isEmpty( ldapUrl.getFilter() ) )
+                    {
+                        String message = "An LDAPURL should not contains filters";
+                        LOG.error( message );
+                        throw new NamingException( message );
+                    }
+
+                    if ( ( ldapUrl.getAttributes() != null ) && ( ldapUrl.getAttributes().size() != 0 ) )
+                    {
+                        String message = "An LDAPURL should not contains any description attribute list";
+                        LOG.error( message );
+                        throw new NamingException( message );
+                    }
+
+                    if ( ( ldapUrl.getExtensions() != null ) && ( ldapUrl.getExtensions().size() != 0 ) )
+                    {
+                        String message = "An LDAPURL should not contains any extension";
+                        LOG.error( message );
+                        throw new NamingException( message );
+                    }
+
+                    if ( ( ldapUrl.getCriticalExtensions() != null ) && ( ldapUrl.getCriticalExtensions().size() != 0 ) )
+                    {
+                        String message = "An LDAPURL should not contains any critical extension";
+                        LOG.error( message );
+                        throw new NamingException( message );
+                    }
+
+                    LdapDN dn = ldapUrl.getDn();
+
+                    if ( ( dn == null ) || dn.isEmpty() )
+                    {
+                        String message = "An LDAPURL should contains a non-empty DN";
+                        LOG.error( message );
+                        throw new NamingException( message );
+                    }
+                }
+                catch ( LdapURLEncodingException luee )
+                {
+                    // Either the URL is invalid, or it's not a LDAP URL.
+                    // we will just ignore this LdapURL.
+                }
+            }
+
+            return true;
+        }
+
+        return false;
+    }
+
+
+    public void init( DirectoryService directoryService ) throws NamingException
+    {
+        nexus = directoryService.getPartitionNexus();
+        registries = directoryService.getRegistries();
+        atRegistry = registries.getAttributeTypeRegistry();
+        oidRegistry = registries.getOidRegistry();
+
+        Iterator<String> suffixes = nexus.listSuffixes( null );
+
+        while ( suffixes.hasNext() )
+        {
+            LdapDN suffix = new LdapDN( suffixes.next() );
+            addReferrals( nexus.search( new SearchOperationContext( registries, suffix, AliasDerefMode.DEREF_ALWAYS,
+                getReferralFilter(), getControls() ) ), suffix );
+        }
+    }
+
+
+    public void doReferralException( LdapDN farthest, LdapDN targetUpdn, EntryAttribute refs ) throws NamingException
+    {
+        // handle referral here
+        List<String> list = new ArrayList<String>( refs.size() );
+
+        for ( Value<?> value : refs )
+        {
+            String val = ( String ) value.get();
+
+            // need to add non-ldap URLs as-is
+            if ( !val.startsWith( "ldap" ) )
+            {
+                list.add( val );
+                continue;
+            }
+
+            // parse the ref value and normalize the DN according to schema 
+            LdapURL ldapUrl = new LdapURL();
+            try
+            {
+                ldapUrl.parse( val.toCharArray() );
+            }
+            catch ( LdapURLEncodingException e )
+            {
+                LOG.error( "Bad URL (" + val + ") for ref in " + farthest + ".  Reference will be ignored." );
+            }
+
+            LdapDN urlDn = new LdapDN( ldapUrl.getDn().toNormName() );
+            urlDn.normalize( atRegistry.getNormalizerMapping() );
+
+            if ( urlDn.equals( farthest ) )
+            {
+                // according to the protocol there is no need for the dn since it is the same as this request
+                StringBuilder buf = new StringBuilder();
+                buf.append( ldapUrl.getScheme() );
+                buf.append( ldapUrl.getHost() );
+
+                if ( ldapUrl.getPort() > 0 )
+                {
+                    buf.append( ":" );
+                    buf.append( ldapUrl.getPort() );
+                }
+
+                list.add( buf.toString() );
+                continue;
+            }
+
+            /*
+             * If we get here then the DN of the referral was not the same as the 
+             * DN of the ref LDAP URL.  We must calculate the remaining (difference)
+             * name past the farthest referral DN which the target name extends.
+             */
+            int diff = targetUpdn.size() - farthest.size();
+            LdapDN extra = new LdapDN();
+
+            for ( int jj = 0; jj < diff; jj++ )
+            {
+                extra.add( targetUpdn.get( farthest.size() + jj ) );
+            }
+
+            urlDn.addAll( extra );
+
+            StringBuilder buf = new StringBuilder();
+            buf.append( ldapUrl.getScheme() );
+            buf.append( ldapUrl.getHost() );
+
+            if ( ldapUrl.getPort() > 0 )
+            {
+                buf.append( ":" );
+                buf.append( ldapUrl.getPort() );
+            }
+
+            buf.append( "/" );
+            buf.append( LdapURL.urlEncode( urlDn.getUpName(), false ) );
+            list.add( buf.toString() );
+        }
+
+        throw new LdapReferralException( list );
+    }
+
+
+    public void add( NextInterceptor next, AddOperationContext opContext ) throws NamingException
+    {
+        Invocation invocation = InvocationStack.getInstance().peek();
+        ServerLdapContext caller = ( ServerLdapContext ) invocation.getCaller();
+        String refval = ( String ) caller.getEnvironment().get( Context.REFERRAL );
+        LdapDN name = opContext.getDn();
+        ServerEntry entry = opContext.getEntry();
+
+        // handle a normal add without following referrals
+        if ( ( refval == null ) || refval.equals( IGNORE ) )
+        {
+            next.add( opContext );
+
+            if ( isReferral( entry ) )
+            {
+                lut.referralAdded( name );
+            }
+        }
+        else if ( refval.equals( THROW ) )
+        {
+            LdapDN farthest = lut.getFarthestReferralAncestor( name );
+
+            if ( farthest == null )
+            {
+                next.add( opContext );
+
+                if ( isReferral( entry ) )
+                {
+                    lut.referralAdded( name );
+                }
+                return;
+            }
+
+            ServerEntry referral = invocation.getProxy().lookup( new LookupOperationContext( registries, farthest ),
+                PartitionNexusProxy.LOOKUP_BYPASS );
+
+            AttributeType refsType = atRegistry.lookup( oidRegistry.getOid( SchemaConstants.REF_AT ) );
+            EntryAttribute refs = referral.get( refsType );
+            doReferralException( farthest, new LdapDN( name.getUpName() ), refs );
+        }
+        else if ( refval.equals( FOLLOW ) )
+        {
+            throw new NotImplementedException( FOLLOW + " referral handling mode not implemented" );
+        }
+        else
+        {
+            throw new LdapNamingException( "Undefined value for " + Context.REFERRAL + " key: " + refval,
+                ResultCodeEnum.OTHER );
+        }
+    }
+
+
+    public boolean compare( NextInterceptor next, CompareOperationContext opContext ) throws NamingException
+    {
+        LdapDN name = opContext.getDn();
+
+        Invocation invocation = InvocationStack.getInstance().peek();
+        ServerLdapContext caller = ( ServerLdapContext ) invocation.getCaller();
+        String refval = ( String ) caller.getEnvironment().get( Context.REFERRAL );
+
+        // handle a normal add without following referrals
+        if ( refval == null || refval.equals( IGNORE ) )
+        {
+            return next.compare( opContext );
+        }
+
+        if ( refval.equals( THROW ) )
+        {
+            LdapDN farthest = lut.getFarthestReferralAncestor( name );
+
+            if ( farthest == null )
+            {
+                return next.compare( opContext );
+            }
+
+            ServerEntry referral = invocation.getProxy().lookup( new LookupOperationContext( registries, farthest ),
+                PartitionNexusProxy.LOOKUP_BYPASS );
+
+            EntryAttribute refs = referral.get( SchemaConstants.REF_AT );
+            doReferralException( farthest, new LdapDN( name.getUpName() ), refs );
+
+            // we really can't get here since doReferralException will throw an exception
+            return false;
+        }
+        else if ( refval.equals( FOLLOW ) )
+        {
+            throw new NotImplementedException( FOLLOW + " referral handling mode not implemented" );
+        }
+        else
+        {
+            throw new LdapNamingException( "Undefined value for " + Context.REFERRAL + " key: " + refval,
+                ResultCodeEnum.OTHER );
+        }
+    }
+
+
+    public void delete( NextInterceptor next, DeleteOperationContext opContext ) throws NamingException
+    {
+        LdapDN name = opContext.getDn();
+        Invocation invocation = InvocationStack.getInstance().peek();
+        ServerLdapContext caller = ( ServerLdapContext ) invocation.getCaller();
+        String refval = ( String ) caller.getEnvironment().get( Context.REFERRAL );
+
+        // handle a normal delete without following referrals
+        if ( refval == null || refval.equals( IGNORE ) )
+        {
+            next.delete( opContext );
+
+            if ( lut.isReferral( name ) )
+            {
+                lut.referralDeleted( name );
+            }
+
+            return;
+        }
+
+        if ( refval.equals( THROW ) )
+        {
+            LdapDN farthest = lut.getFarthestReferralAncestor( name );
+
+            if ( farthest == null )
+            {
+                next.delete( opContext );
+
+                if ( lut.isReferral( name ) )
+                {
+                    lut.referralDeleted( name );
+                }
+
+                return;
+            }
+
+            ServerEntry referral = invocation.getProxy().lookup( new LookupOperationContext( registries, farthest ),
+                PartitionNexusProxy.LOOKUP_BYPASS );
+
+            EntryAttribute refs = referral.get( SchemaConstants.REF_AT );
+            doReferralException( farthest, new LdapDN( name.getUpName() ), refs );
+
+        }
+        else if ( refval.equals( FOLLOW ) )
+        {
+            throw new NotImplementedException( FOLLOW + " referral handling mode not implemented" );
+        }
+        else
+        {
+            throw new LdapNamingException( "Undefined value for " + Context.REFERRAL + " key: " + refval,
+                ResultCodeEnum.OTHER );
+        }
+    }
+
+
+    /* -----------------------------------------------------------------------
+     * Special handling instructions for ModifyDn operations:
+     * ======================================================
+     * 
+     * From RFC 3296 here => http://www.ietf.org/rfc/rfc3296.txt
+     * 
+     * 5.6.2 Modify DN
+     *
+     * If the newSuperior is a referral object or is subordinate to a
+     * referral object, the server SHOULD return affectsMultipleDSAs.  If
+     * the newRDN already exists but is a referral object, the server SHOULD
+     * return affectsMultipleDSAs instead of entryAlreadyExists.
+     * -----------------------------------------------------------------------
+     */
+
+    public void move( NextInterceptor next, MoveOperationContext opContext ) throws NamingException
+    {
+        LdapDN oldName = opContext.getDn();
+
+        Invocation invocation = InvocationStack.getInstance().peek();
+        ServerLdapContext caller = ( ServerLdapContext ) invocation.getCaller();
+        String refval = ( String ) caller.getEnvironment().get( Context.REFERRAL );
+        LdapDN newName = ( LdapDN ) opContext.getParent().clone();
+        newName.add( oldName.get( oldName.size() - 1 ) );
+
+        // handle a normal modify without following referrals
+        if ( refval == null || refval.equals( IGNORE ) )
+        {
+            next.move( opContext );
+
+            if ( lut.isReferral( oldName ) )
+            {
+                lut.referralChanged( oldName, newName );
+            }
+
+            return;
+        }
+
+        if ( refval.equals( THROW ) )
+        {
+            LdapDN farthestSrc = lut.getFarthestReferralAncestor( oldName );
+            LdapDN farthestDst = lut.getFarthestReferralAncestor( newName ); // note will not return newName so safe
+
+            if ( farthestSrc == null && farthestDst == null && !lut.isReferral( newName ) )
+            {
+                next.move( opContext );
+
+                if ( lut.isReferral( oldName ) )
+                {
+                    lut.referralChanged( oldName, newName );
+                }
+
+                return;
+            }
+            else if ( farthestSrc != null )
+            {
+                ServerEntry referral = invocation.getProxy().lookup(
+                    new LookupOperationContext( registries, farthestSrc ), PartitionNexusProxy.LOOKUP_BYPASS );
+
+                EntryAttribute refs = referral.get( SchemaConstants.REF_AT );
+                doReferralException( farthestSrc, new LdapDN( oldName.getUpName() ), refs );
+            }
+            else if ( farthestDst != null )
+            {
+                throw new LdapNamingException( farthestDst + " ancestor is a referral for modifyDn on " + newName
+                    + " so it affects multiple DSAs", ResultCodeEnum.AFFECTS_MULTIPLE_DSAS );
+            }
+            else if ( lut.isReferral( newName ) )
+            {
+                throw new LdapNamingException( newName
+                    + " exists and is a referral for modifyDn destination so it affects multiple DSAs",
+                    ResultCodeEnum.AFFECTS_MULTIPLE_DSAS );
+            }
+
+            throw new IllegalStateException( "If you get this exception the server's logic was flawed in handling a "
+                + "modifyDn operation while processing referrals.  Report this as a bug!" );
+        }
+        else if ( refval.equals( FOLLOW ) )
+        {
+            throw new NotImplementedException( FOLLOW + " referral handling mode not implemented" );
+        }
+        else
+        {
+            throw new LdapNamingException( "Undefined value for " + Context.REFERRAL + " key: " + refval,
+                ResultCodeEnum.OTHER );
+        }
+    }
+
+
+    public void moveAndRename( NextInterceptor next, MoveAndRenameOperationContext opContext ) throws NamingException
+    {
+        LdapDN oldName = opContext.getDn();
+
+        Invocation invocation = InvocationStack.getInstance().peek();
+        ServerLdapContext caller = ( ServerLdapContext ) invocation.getCaller();
+        String refval = ( String ) caller.getEnvironment().get( Context.REFERRAL );
+        LdapDN newName = ( LdapDN ) opContext.getParent().clone();
+        newName.add( opContext.getNewRdn() );
+
+        // handle a normal modify without following referrals
+        if ( refval == null || refval.equals( IGNORE ) )
+        {
+            next.moveAndRename( opContext );
+
+            if ( lut.isReferral( oldName ) )
+            {
+                lut.referralChanged( oldName, newName );
+            }
+            return;
+        }
+
+        if ( refval.equals( THROW ) )
+        {
+            LdapDN farthestSrc = lut.getFarthestReferralAncestor( oldName );
+            LdapDN farthestDst = lut.getFarthestReferralAncestor( newName ); // safe to use - does not return newName
+
+            if ( farthestSrc == null && farthestDst == null && !lut.isReferral( newName ) )
+            {
+                next.moveAndRename( opContext );
+
+                if ( lut.isReferral( oldName ) )
+                {
+                    lut.referralChanged( oldName, newName );
+                }
+                return;
+            }
+            else if ( farthestSrc != null )
+            {
+                ServerEntry referral = invocation.getProxy().lookup(
+                    new LookupOperationContext( registries, farthestSrc ), PartitionNexusProxy.LOOKUP_BYPASS );
+
+                EntryAttribute refs = referral.get( SchemaConstants.REF_AT );
+                doReferralException( farthestSrc, new LdapDN( oldName.getUpName() ), refs );
+
+            }
+            else if ( farthestDst != null )
+            {
+                throw new LdapNamingException( farthestDst + " ancestor is a referral for modifyDn on " + newName
+                    + " so it affects multiple DSAs", ResultCodeEnum.AFFECTS_MULTIPLE_DSAS );
+            }
+            else if ( lut.isReferral( newName ) )
+            {
+                throw new LdapNamingException( newName
+                    + " exists and is a referral for modifyDn destination so it affects multiple DSAs",
+                    ResultCodeEnum.AFFECTS_MULTIPLE_DSAS );
+            }
+
+            throw new IllegalStateException( "If you get this exception the server's logic was flawed in handling a "
+                + "modifyDn operation while processing referrals.  Report this as a bug!" );
+        }
+        else if ( refval.equals( FOLLOW ) )
+        {
+            throw new NotImplementedException( FOLLOW + " referral handling mode not implemented" );
+        }
+        else
+        {
+            throw new LdapNamingException( "Undefined value for " + Context.REFERRAL + " key: " + refval,
+                ResultCodeEnum.OTHER );
+        }
+    }
+
+
+    public void rename( NextInterceptor next, RenameOperationContext opContext ) throws NamingException
+    {
+        LdapDN oldName = opContext.getDn();
+
+        Invocation invocation = InvocationStack.getInstance().peek();
+        ServerLdapContext caller = ( ServerLdapContext ) invocation.getCaller();
+        String refval = ( String ) caller.getEnvironment().get( Context.REFERRAL );
+        LdapDN newName = ( LdapDN ) oldName.clone();
+        newName.remove( oldName.size() - 1 );
+
+        newName.add( opContext.getNewRdn() );
+
+        // handle a normal modify without following referrals
+        if ( refval == null || refval.equals( IGNORE ) )
+        {
+            next.rename( opContext );
+
+            if ( lut.isReferral( oldName ) )
+            {
+                lut.referralChanged( oldName, newName );
+            }
+
+            return;
+        }
+
+        if ( refval.equals( THROW ) )
+        {
+            LdapDN farthestSrc = lut.getFarthestReferralAncestor( oldName );
+            LdapDN farthestDst = lut.getFarthestReferralAncestor( newName );
+
+            if ( farthestSrc == null && farthestDst == null && !lut.isReferral( newName ) )
+            {
+                next.rename( opContext );
+
+                if ( lut.isReferral( oldName ) )
+                {
+                    lut.referralChanged( oldName, newName );
+                }
+
+                return;
+            }
+
+            if ( farthestSrc != null )
+            {
+                ServerEntry referral = invocation.getProxy().lookup(
+                    new LookupOperationContext( registries, farthestSrc ), PartitionNexusProxy.LOOKUP_BYPASS );
+
+                EntryAttribute refs = referral.get( SchemaConstants.REF_AT );
+                doReferralException( farthestSrc, new LdapDN( oldName.getUpName() ), refs );
+
+            }
+            else if ( farthestDst != null )
+            {
+                throw new LdapNamingException( farthestDst + " ancestor is a referral for modifyDn on " + newName
+                    + " so it affects multiple DSAs", ResultCodeEnum.AFFECTS_MULTIPLE_DSAS );
+            }
+            else if ( lut.isReferral( newName ) )
+            {
+                throw new LdapNamingException( newName
+                    + " exists and is a referral for modifyDn destination so it affects multiple DSAs",
+                    ResultCodeEnum.AFFECTS_MULTIPLE_DSAS );
+            }
+
+            throw new IllegalStateException( "If you get this exception the server's logic was flawed in handling a "
+                + "modifyDn operation while processing referrals.  Report this as a bug!" );
+        }
+        else if ( refval.equals( FOLLOW ) )
+        {
+            throw new NotImplementedException( FOLLOW + " referral handling mode not implemented" );
+        }
+        else
+        {
+            throw new LdapNamingException( "Undefined value for " + Context.REFERRAL + " key: " + refval,
+                ResultCodeEnum.OTHER );
+        }
+    }
+
+
+    private void checkModify( LdapDN name, List<Modification> mods ) throws NamingException
+    {
+        boolean isTargetReferral = lut.isReferral( name );
+
+        // -------------------------------------------------------------------
+        // Check and update lut if we change the objectClass 
+        // -------------------------------------------------------------------
+
+        for ( Modification mod : mods )
+        {
+            if ( mod.getAttribute().getId().equalsIgnoreCase( SchemaConstants.OBJECT_CLASS_AT ) )
+            {
+                boolean modsOcHasReferral = ( ( ServerAttribute ) mod.getAttribute() )
+                    .contains( SchemaConstants.REFERRAL_OC );
+
+                switch ( mod.getOperation() )
+                {
+                    /* 
+                     * if ADD op where refferal is added to objectClass of a
+                     * non-referral entry then we add a new referral to lut
+                     */
+                    case ADD_ATTRIBUTE:
+                        if ( modsOcHasReferral && !isTargetReferral )
+                        {
+                            lut.referralAdded( name );
+                        }
+
+                        break;
+
+                    /*
+                    * if REMOVE op where refferal is removed from objectClass of a
+                    * referral entry then we remove the referral from lut
+                    */
+                    case REMOVE_ATTRIBUTE:
+                        if ( modsOcHasReferral && isTargetReferral )
+                        {
+                            lut.referralDeleted( name );
+                        }
+
+                        break;
+
+                    /*
+                    * if REPLACE op on referral has new set of OC values which does
+                    * not contain a referral value then we remove the referral from
+                    * the lut
+                    *
+                    * if REPLACE op on non-referral has new set of OC values with
+                    * referral value then we add the new referral to the lut
+                    */
+                    case REPLACE_ATTRIBUTE:
+                        if ( isTargetReferral && !modsOcHasReferral )
+                        {
+                            lut.referralDeleted( name );
+                        }
+                        else if ( !isTargetReferral && modsOcHasReferral )
+                        {
+                            lut.referralAdded( name );
+                        }
+
+                        break;
+
+                    default:
+                        throw new IllegalStateException( "undefined modification operation" );
+                }
+
+                break;
+            }
+        }
+    }
+
+
+    public void modify( NextInterceptor next, ModifyOperationContext opContext ) throws NamingException
+    {
+        Invocation invocation = InvocationStack.getInstance().peek();
+        ServerLdapContext caller = ( ServerLdapContext ) invocation.getCaller();
+        String refval = ( String ) caller.getEnvironment().get( Context.REFERRAL );
+        LdapDN name = opContext.getDn();
+        List<Modification> mods = opContext.getModItems();
+
+        // handle a normal modify without following referrals
+        if ( refval == null || refval.equals( IGNORE ) )
+        {
+            next.modify( opContext );
+            checkModify( name, mods );
+            return;
+        }
+
+        if ( refval.equals( THROW ) )
+        {
+            LdapDN farthest = lut.getFarthestReferralAncestor( name );
+
+            if ( farthest == null )
+            {
+                next.modify( opContext );
+                checkModify( name, mods );
+                return;
+            }
+
+            ServerEntry referral = invocation.getProxy().lookup( new LookupOperationContext( registries, farthest ),
+                PartitionNexusProxy.LOOKUP_BYPASS );
+
+            EntryAttribute refs = referral.get( SchemaConstants.REF_AT );
+            doReferralException( farthest, new LdapDN( name.getUpName() ), refs );
+        }
+        else if ( refval.equals( FOLLOW ) )
+        {
+            throw new NotImplementedException( FOLLOW + " referral handling mode not implemented" );
+        }
+        else
+        {
+            throw new LdapNamingException( "Undefined value for " + Context.REFERRAL + " key: " + refval,
+                ResultCodeEnum.OTHER );
+        }
+    }
+
+
+    static ExprNode getReferralFilter()
+    {
+        return new EqualityNode( SchemaConstants.OBJECT_CLASS_AT, new ClientStringValue( SchemaConstants.REFERRAL_OC ) );
+    }
+
+
+    static SearchControls getControls()
+    {
+        SearchControls controls = new SearchControls();
+        controls.setReturningObjFlag( false );
+        controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        return controls;
+    }
+
+
+    public void addContextPartition( NextInterceptor next, AddContextPartitionOperationContext opContext )
+        throws NamingException
+    {
+        next.addContextPartition( opContext );
+
+        // add referrals immediately after adding the new partition
+        Partition partition = opContext.getPartition();
+        LdapDN suffix = partition.getSuffixDn();
+        Invocation invocation = InvocationStack.getInstance().peek();
+        NamingEnumeration<ServerSearchResult> list = invocation.getProxy().search(
+            new SearchOperationContext( registries, suffix, AliasDerefMode.DEREF_ALWAYS, getReferralFilter(),
+                getControls() ), SEARCH_BYPASS );
+        addReferrals( list, suffix );
+    }
+
+
+    public void removeContextPartition( NextInterceptor next, RemoveContextPartitionOperationContext opContext )
+        throws NamingException
+    {
+        // remove referrals immediately before removing the partition
+        Invocation invocation = InvocationStack.getInstance().peek();
+        NamingEnumeration<ServerSearchResult> list = invocation.getProxy().search(
+            new SearchOperationContext( registries, opContext.getDn(), AliasDerefMode.DEREF_ALWAYS,
+                getReferralFilter(), getControls() ), SEARCH_BYPASS );
+
+        deleteReferrals( list, opContext.getDn() );
+        next.removeContextPartition( opContext );
+    }
+
+
+    private void addReferrals( NamingEnumeration<ServerSearchResult> referrals, LdapDN base ) throws NamingException
+    {
+        while ( referrals.hasMore() )
+        {
+            ServerSearchResult r = referrals.next();
+            LdapDN referral;
+            LdapDN result = new LdapDN( r.getDn() );
+            result.normalize( atRegistry.getNormalizerMapping() );
+
+            if ( r.isRelative() )
+            {
+                referral = ( LdapDN ) base.clone();
+                referral.addAll( result );
+            }
+
+            // Now, add the referral to the cache
+            lut.referralAdded( result );
+        }
+    }
+
+
+    private void deleteReferrals( NamingEnumeration<ServerSearchResult> referrals, LdapDN base ) throws NamingException
+    {
+        while ( referrals.hasMore() )
+        {
+            ServerSearchResult r = referrals.next();
+            LdapDN referral;
+            LdapDN result = new LdapDN( r.getDn() );
+            result.normalize( atRegistry.getNormalizerMapping() );
+
+            if ( r.isRelative() )
+            {
+                referral = ( LdapDN ) base.clone();
+                referral.addAll( result );
+            }
+
+            // Now, remove the referral from the cache
+            lut.referralDeleted( result );
+        }
+    }
+
+
+    public NamingEnumeration<ServerSearchResult> search( NextInterceptor next, SearchOperationContext opContext )
+        throws NamingException
+    {
+        Invocation invocation = InvocationStack.getInstance().peek();
+        ServerLdapContext caller = ( ServerLdapContext ) invocation.getCaller();
+        String refval = ( String ) caller.getEnvironment().get( Context.REFERRAL );
+
+        // handle a normal search without following referrals
+        if ( refval == null || refval.equals( IGNORE ) )
+        {
+            return next.search( opContext );
+        }
+
+        LdapDN base = opContext.getDn();
+        SearchControls controls = opContext.getSearchControls();
+
+        /**
+         * THROW_FINDING_BASE is a special setting which allows for finding base to 
+         * throw exceptions but not when searching.  While search all results are 
+         * returned as if they are regular entries.
+         */
+        if ( refval.equals( THROW_FINDING_BASE ) )
+        {
+            if ( lut.isReferral( base ) )
+            {
+                ServerEntry referral = invocation.getProxy().lookup( new LookupOperationContext( registries, base ),
+                    PartitionNexusProxy.LOOKUP_BYPASS );
+                EntryAttribute refs = referral.get( SchemaConstants.REF_AT );
+                doReferralExceptionOnSearchBase( base, refs, controls.getSearchScope() );
+            }
+
+            LdapDN farthest = lut.getFarthestReferralAncestor( base );
+
+            if ( farthest == null )
+            {
+                return next.search( opContext );
+            }
+
+            ServerEntry referral = invocation.getProxy().lookup( new LookupOperationContext( registries, farthest ),
+                PartitionNexusProxy.LOOKUP_BYPASS );
+            EntryAttribute refs = referral.get( SchemaConstants.REF_AT );
+            doReferralExceptionOnSearchBase( farthest, new LdapDN( base.getUpName() ), refs, controls.getSearchScope() );
+            throw new IllegalStateException( "Should never get here: shutting up compiler" );
+        }
+
+        if ( refval.equals( THROW ) )
+        {
+            if ( lut.isReferral( base ) )
+            {
+                ServerEntry referral = invocation.getProxy().lookup( new LookupOperationContext( registries, base ),
+                    PartitionNexusProxy.LOOKUP_BYPASS );
+                EntryAttribute refs = referral.get( SchemaConstants.REF_AT );
+                doReferralExceptionOnSearchBase( base, refs, controls.getSearchScope() );
+            }
+
+            LdapDN farthest = lut.getFarthestReferralAncestor( base );
+
+            if ( farthest == null )
+            {
+                SearchResultFilteringEnumeration srfe = ( SearchResultFilteringEnumeration ) next.search( opContext );
+                return new ReferralHandlingEnumeration( srfe, lut, opContext.getRegistries(), nexus, controls
+                    .getSearchScope(), true );
+            }
+
+            ServerEntry referral = invocation.getProxy().lookup( new LookupOperationContext( registries, farthest ),
+                PartitionNexusProxy.LOOKUP_BYPASS );
+            EntryAttribute refs = referral.get( SchemaConstants.REF_AT );
+            doReferralExceptionOnSearchBase( farthest, new LdapDN( base.getUpName() ), refs, controls.getSearchScope() );
+            throw new IllegalStateException( "Should never get here: shutting up compiler" );
+        }
+        else if ( refval.equals( FOLLOW ) )
+        {
+            throw new NotImplementedException( FOLLOW + " referral handling mode not implemented" );
+        }
+        else
+        {
+            throw new LdapNamingException( "Undefined value for " + Context.REFERRAL + " key: " + refval,
+                ResultCodeEnum.OTHER );
+        }
+    }
+
+    class ReferralFilter implements SearchResultFilter//, SearchResultEnumerationAppender 
+    {
+        public boolean accept( Invocation invocation, ServerSearchResult result, SearchControls controls )
+            throws NamingException
+        {
+            return false;
+        }
+    }
+
+
+    public void doReferralExceptionOnSearchBase( LdapDN base, EntryAttribute refs, int scope ) throws NamingException
+    {
+        // handle referral here
+        List<String> list = new ArrayList<String>( refs.size() );
+
+        for ( Value<?> value : refs )
+        {
+            String val = ( String ) value.get();
+
+            // need to add non-ldap URLs as-is
+            if ( !val.startsWith( "ldap" ) )
+            {
+                list.add( val );
+                continue;
+            }
+
+            // parse the ref value and normalize the DN according to schema 
+            LdapURL ldapUrl = new LdapURL();
+            try
+            {
+                ldapUrl.parse( val.toCharArray() );
+            }
+            catch ( LdapURLEncodingException e )
+            {
+                LOG.error( "Bad URL (" + val + ") for ref in " + base + ".  Reference will be ignored." );
+            }
+
+            StringBuilder buf = new StringBuilder();
+            buf.append( ldapUrl.getScheme() );
+            buf.append( ldapUrl.getHost() );
+
+            if ( ldapUrl.getPort() > 0 )
+            {
+                buf.append( ":" );
+                buf.append( ldapUrl.getPort() );
+            }
+
+            buf.append( "/" );
+            buf.append( LdapURL.urlEncode( ldapUrl.getDn().getUpName(), false ) );
+            buf.append( "??" );
+
+            switch ( scope )
+            {
+                case ( SearchControls.SUBTREE_SCOPE   ):
+                    buf.append( "sub" );
+                    break;
+
+                case ( SearchControls.ONELEVEL_SCOPE   ):
+                    buf.append( "one" );
+                    break;
+
+                case ( SearchControls.OBJECT_SCOPE   ):
+                    buf.append( "base" );
+                    break;
+
+                default:
+                    throw new IllegalStateException( "Unknown recognized search scope: " + scope );
+            }
+
+            list.add( buf.toString() );
+        }
+
+        throw new LdapReferralException( list );
+    }
+
+
+    public void doReferralExceptionOnSearchBase( LdapDN farthest, LdapDN targetUpdn, EntryAttribute refs, int scope )
+        throws NamingException
+    {
+        // handle referral here
+        List<String> list = new ArrayList<String>( refs.size() );
+
+        for ( Value<?> value : refs )
+        {
+            String val = ( String ) value.get();
+
+            // need to add non-ldap URLs as-is
+            if ( !val.startsWith( "ldap" ) )
+            {
+                list.add( val );
+                continue;
+            }
+
+            // parse the ref value and normalize the DN according to schema 
+            LdapURL ldapUrl = new LdapURL();
+
+            try
+            {
+                ldapUrl.parse( val.toCharArray() );
+            }
+            catch ( LdapURLEncodingException e )
+            {
+                LOG.error( "Bad URL (" + val + ") for ref in " + farthest + ".  Reference will be ignored." );
+            }
+
+            LdapDN urlDn = new LdapDN( ldapUrl.getDn().toNormName() );
+            urlDn.normalize( atRegistry.getNormalizerMapping() );
+            int diff = targetUpdn.size() - farthest.size();
+            LdapDN extra = new LdapDN();
+
+            for ( int jj = 0; jj < diff; jj++ )
+            {
+                extra.add( targetUpdn.get( farthest.size() + jj ) );
+            }
+
+            urlDn.addAll( extra );
+
+            StringBuilder buf = new StringBuilder();
+            buf.append( ldapUrl.getScheme() );
+            buf.append( ldapUrl.getHost() );
+
+            if ( ldapUrl.getPort() > 0 )
+            {
+                buf.append( ":" );
+                buf.append( ldapUrl.getPort() );
+            }
+
+            buf.append( "/" );
+            buf.append( LdapURL.urlEncode( urlDn.getUpName(), false ) );
+            buf.append( "??" );
+
+            switch ( scope )
+            {
+                case ( SearchControls.SUBTREE_SCOPE   ):
+                    buf.append( "sub" );
+                    break;
+
+                case ( SearchControls.ONELEVEL_SCOPE   ):
+                    buf.append( "one" );
+                    break;
+
+                case ( SearchControls.OBJECT_SCOPE   ):
+                    buf.append( "base" );
+                    break;
+
+                default:
+                    throw new IllegalStateException( "Unknown recognized search scope: " + scope );
+            }
+
+            list.add( buf.toString() );
+        }
+
+        throw new LdapReferralException( list );
+    }
+
+
+    /**
+     * Check if the given name is a referral or not.
+     * 
+     * @param name The DN to check
+     * @return <code>true</code> if the DN is a referral
+     * @throws NamingException I fthe DN is incorrect
+     */
+    public boolean isReferral( String name ) throws NamingException
+    {
+        if ( lut.isReferral( name ) )
+        {
+            return true;
+        }
+
+        LdapDN dn = new LdapDN( name );
+        dn.normalize( atRegistry.getNormalizerMapping() );
+
+        return lut.isReferral( dn );
+    }
+
+
+    /**
+     * Check if the given name is a referral or not.
+     * 
+     * @param name The DN to check
+     * @return <code>true</code> if the DN is a referral
+     * @throws NamingException I fthe DN is incorrect
+     */
+    public boolean isReferral( LdapDN name ) throws NamingException
+    {
+        return lut
+            .isReferral( name.isNormalized() ? name : LdapDN.normalize( name, atRegistry.getNormalizerMapping() ) );
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/referral/ReferralLut.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/referral/ReferralLut.java
new file mode 100644
index 0000000..e1ab28d
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/referral/ReferralLut.java
@@ -0,0 +1,346 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.referral;
+
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.naming.InvalidNameException;
+
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * A simple lookup table of normalized referral distinguished names.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class ReferralLut
+{
+    /** the logger for this class */
+    private static final Logger log = LoggerFactory.getLogger( ReferralLut.class );
+    
+    /** the set of names in the LUT */
+    private Set<String> names = new HashSet<String>();
+
+
+    // -----------------------------------------------------------------------
+    // Methods to access the LUT: all names are expected to be normalized
+    // -----------------------------------------------------------------------
+
+    /**
+     * Checks if a the entry at a name is a referral.
+     * 
+     * @param dn the normalized name of the referral
+     */
+    public boolean isReferral( LdapDN dn )
+    {
+        if ( dn == null )
+        {
+            throw new IllegalArgumentException( "dn cannot be null" );
+        }
+        
+        return names.contains( dn.getNormName() );
+    }
+
+
+    /**
+     * Checks if a the entry at a name is a referral.
+     * 
+     * @param dn the normalized name of the referral
+     */
+    public boolean isReferral( String dn )
+    {
+        if ( dn == null )
+        {
+            throw new IllegalArgumentException( "dn cannot be null" );
+        }
+        
+        return names.contains( dn );
+    }
+
+
+    /**
+     * Gets the normalized name of the farthest ancestor that is a referral. If the argument 
+     * is a referral it will not be returned.  Only ancestor's (includes parent) are considered.
+     * 
+     * @param dn the name to get the farthest ancestor referral name for
+     * @return the farthest referral ancestor
+     */
+    public LdapDN getFarthestReferralAncestor( LdapDN dn )
+    {
+        if ( dn == null )
+        {
+            throw new IllegalArgumentException( "dn cannot be null" );
+        }
+        
+        LdapDN farthest = new LdapDN();
+        
+        for ( int ii = 0; ii < dn.size(); ii++ )
+        {
+            farthest.addNormalized( dn.getRdn( ii ) );
+
+            // do not return dn if it is the farthest referral
+            if ( isReferral( farthest ) && ( farthest.size() != dn.size() ) )
+            {
+                return farthest;
+            }
+        }
+        
+        return null;
+    }
+
+
+    /**
+     * Gets the normalized name of the nearest ancestor that is a referral.  If the argument
+     * is a referral it will not be returned.  Only ancestor's (includes parent) are considered.
+     * 
+     * @param dn the name to get the nearest ancestor referral name for
+     * @return the nearest referral ancestor or null if one does not exist
+     */
+    public LdapDN getNearestReferralAncestor( LdapDN dn )
+    {
+        if ( dn == null )
+        {
+            throw new IllegalArgumentException( "dn cannot be null" );
+        }
+        
+        LdapDN cloned = ( LdapDN ) dn.clone();
+
+        // do not return the argument dn if it is a referral (skip it)
+        if ( cloned.size() > 0 )
+        {
+            try
+            {
+                cloned.remove( cloned.size() - 1 );
+            }
+            catch ( InvalidNameException e )
+            {
+                log.error( "Should never get this when removing from a cloned normalized name!", e );
+            }
+        }
+        else
+        {
+            return null;
+        }
+
+        while ( !isReferral( cloned ) && ( cloned.size() > 0 ) )
+        {
+            try
+            {
+                cloned.remove( cloned.size() - 1 );
+            }
+            catch ( InvalidNameException e )
+            {
+                log.error( "Should never get this when removing from a cloned normalized name!", e );
+            }
+        }
+        
+        return cloned.isEmpty() ? null : cloned;
+    }
+
+
+    // -----------------------------------------------------------------------
+    // Methods that notify this lookup table of changes to referrals
+    // -----------------------------------------------------------------------
+
+    /**
+     * Called to add an entry to the LUT when a referral is added.
+     * 
+     * @param dn the normalized name of the added referral
+     */
+    public void referralAdded( LdapDN dn )
+    {
+        if ( dn == null )
+        {
+            throw new IllegalArgumentException( "dn cannot be null" );
+        }
+        
+        if ( !names.add( dn.getNormName() ) && log.isWarnEnabled() )
+        {
+            log.warn( "found " + dn.getUpName() + " in refname lut while adding it" );
+        }
+    }
+
+
+    /**
+     * Called to add an entry to the LUT when a referral is added.
+     * 
+     * @param dn the normalized name of the added referral
+     */
+    public void referralAdded( String dn )
+    {
+        if ( dn == null )
+        {
+            throw new IllegalArgumentException( "dn cannot be null" );
+        }
+        
+        if ( !names.add( dn ) && log.isWarnEnabled() )
+        {
+            log.warn( "found " + dn + " in refname lut while adding it" );
+        }
+    }
+
+
+    /**
+     * Called delete an entry from the LUT when a referral is deleted.
+     * 
+     * @param dn the normalized name of the deleted referral
+     */
+    public void referralDeleted( LdapDN dn )
+    {
+        if ( dn == null )
+        {
+            throw new IllegalArgumentException( "dn cannot be null" );
+        }
+        
+        if ( !names.remove( dn.getNormName() ) && log.isWarnEnabled() )
+        {
+            log.warn( "cound not find " + dn.getUpName() + " in refname lut while deleting it" );
+        }
+    }
+
+
+    /**
+     * Called delete an entry from the LUT when a referral is deleted.
+     * 
+     * @param dn the normalized name of the deleted referral
+     */
+    public void referralDeleted( String dn )
+    {
+        if ( dn == null )
+        {
+            throw new IllegalArgumentException( "dn cannot be null" );
+        }
+        
+        if ( !names.remove( dn ) && log.isWarnEnabled() )
+        {
+            log.warn( "cound not find " + dn + " in refname lut while deleting it" );
+        }
+    }
+
+
+    /**
+     * Called to update the LUT when the name of the referral changes due to 
+     * a rename or move in the DIT.
+     * 
+     * @param oldDn the normalized old name for the referral
+     * @param newDn the normalized new name for the referral
+     */
+    public void referralChanged( LdapDN oldDn, LdapDN newDn )
+    {
+        if ( ( oldDn == null ) || ( newDn == null ) )
+        {
+            throw new IllegalArgumentException( "old or new dn cannot be null" );
+        }
+        
+        if ( !names.remove( oldDn.getNormName() ) && log.isWarnEnabled() )
+        {
+            log.warn( "cound not find old name (" + oldDn.getUpName() + ") in refname lut while moving or renaming it" );
+        }
+
+        if ( !names.add( newDn.getNormName() ) && log.isWarnEnabled() )
+        {
+            log.warn( "found new name (" + newDn.getUpName() + ") in refname lut while moving or renaming " + oldDn );
+        }
+    }
+
+
+    /**
+     * Called to update the LUT when the name of the referral changes due to 
+     * a rename or move in the DIT.
+     * 
+     * @param oldDn the normalized old name for the referral
+     * @param newDn the normalized new name for the referral
+     */
+    public void referralChanged( String oldDn, String newDn )
+    {
+        if ( ( oldDn == null ) || ( newDn == null ) )
+        {
+            throw new IllegalArgumentException( "old or new dn cannot be null" );
+        }
+
+        if ( !names.remove( oldDn ) && log.isWarnEnabled() )
+        {
+            log.warn( "cound not find old name (" + oldDn + ") in refname lut while moving or renaming it" );
+        }
+
+        if ( !names.add( newDn ) && log.isWarnEnabled() )
+        {
+            log.warn( "found new name (" + newDn + ") in refname lut while moving or renaming " + oldDn );
+        }
+    }
+
+
+    /**
+     * Called to update the LUT when the name of the referral changes due to 
+     * a rename or move in the DIT.
+     * 
+     * @param oldDn the normalized old name for the referral
+     * @param newDn the normalized new name for the referral
+     */
+    public void referralChanged( LdapDN oldDn, String newDn )
+    {
+        if ( ( oldDn == null ) || ( newDn == null ) )
+        {
+            throw new IllegalArgumentException( "old or new dn cannot be null" );
+        }
+
+        if ( !names.remove( oldDn.getNormName() ) && log.isWarnEnabled() )
+        {
+            log.warn( "cound not find old name (" + oldDn.getUpName() + ") in refname lut while moving or renaming it" );
+        }
+        
+        if ( !names.add( newDn ) && log.isWarnEnabled() )
+        {
+            log.warn( "found new name (" + newDn + ") in refname lut while moving or renaming " + oldDn );
+        }
+    }
+
+
+    /**
+     * Called to update the LUT when the name of the referral changes due to 
+     * a rename or move in the DIT.
+     * 
+     * @param oldDn the normalized old name for the referral
+     * @param newDn the normalized new name for the referral
+     */
+    public void referralChanged( String oldDn, LdapDN newDn )
+    {
+        if ( ( oldDn == null ) || ( newDn == null ) )
+        {
+            throw new IllegalArgumentException( "old or new dn cannot be null" );
+        }
+        
+        if ( !names.remove( oldDn ) && log.isWarnEnabled() )
+        {
+            log.warn( "cound not find old name (" + oldDn + ") in refname lut while moving or renaming it" );
+        }
+        
+        if ( !names.add( newDn.getNormName() ) && log.isWarnEnabled() )
+        {
+            log.warn( "found new name (" + newDn.getUpName() + ") in refname lut while moving or renaming " + oldDn );
+        }
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/AbstractSchema.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/AbstractSchema.java
new file mode 100644
index 0000000..e849944
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/AbstractSchema.java
@@ -0,0 +1,117 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.schema;
+
+
+import org.apache.directory.server.constants.ServerDNConstants;
+import org.apache.directory.server.schema.bootstrap.Schema;
+
+
+/**
+ * An abstract schema class.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public abstract class AbstractSchema implements Schema
+{
+    private static final String[] NONE = new String[0];
+    private static final String DEFAULT_OWNER = ServerDNConstants.ADMIN_SYSTEM_DN;
+    
+    private boolean disabled;
+    private String[] dependencies;
+    private String owner;
+    private String name;
+    
+    
+    public AbstractSchema( String name )
+    {
+        this( name, null, null, false );
+    }
+    
+        
+    public AbstractSchema( String name, String owner )
+    {
+        this( name, owner, null, false );
+    }
+    
+        
+    public AbstractSchema( String name, String owner, String[] dependencies )
+    {
+        this( name, owner, dependencies, false );
+    }
+    
+        
+    public AbstractSchema( String name, String owner, String[] dependencies, boolean disabled )
+    {
+        if ( name == null )
+        {
+            throw new NullPointerException( "name cannot be null" );
+        }
+        
+        this.name = name;
+        
+        if ( owner != null )
+        {
+            this.owner = owner;
+        }
+        else
+        {
+            this.owner = DEFAULT_OWNER;
+        }
+        
+        if ( dependencies != null )
+        {
+            this.dependencies = dependencies;
+        }
+        else
+        {
+            this.dependencies = NONE;
+        }
+        
+        this.disabled = disabled;
+    }
+    
+    
+    public String[] getDependencies()
+    {
+        String[] copy = new String[dependencies.length];
+        System.arraycopy( dependencies, 0, copy, 0, dependencies.length );
+        return copy;
+    }
+
+    
+    public String getOwner()
+    {
+        return owner;
+    }
+
+    
+    public String getSchemaName()
+    {
+        return name;
+    }
+    
+    
+    public boolean isDisabled()
+    {
+        return disabled;
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/AbstractSchemaChangeHandler.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/AbstractSchemaChangeHandler.java
new file mode 100644
index 0000000..cf5f02a
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/AbstractSchemaChangeHandler.java
@@ -0,0 +1,178 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.schema;
+
+
+import org.apache.directory.server.constants.MetaSchemaConstants;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerSearchResult;
+import org.apache.directory.server.schema.bootstrap.Schema;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.entry.Modification;
+import org.apache.directory.shared.ldap.entry.ModificationOperation;
+import org.apache.directory.shared.ldap.exception.LdapNamingException;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.schema.SchemaObject;
+
+import javax.naming.NamingException;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+
+/**
+ * An abstract schema change handler with some reused functionality.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public abstract class AbstractSchemaChangeHandler implements SchemaChangeHandler
+{
+    protected final Registries targetRegistries;
+    protected final PartitionSchemaLoader loader;
+    protected final AttributeType m_oidAT;
+    protected final SchemaEntityFactory factory;
+
+    
+    protected AbstractSchemaChangeHandler( Registries targetRegistries, PartitionSchemaLoader loader ) throws NamingException
+    {
+        this.targetRegistries = targetRegistries;
+        this.loader = loader;
+        this.m_oidAT = targetRegistries.getAttributeTypeRegistry().lookup( MetaSchemaConstants.M_OID_AT );
+        this.factory = new SchemaEntityFactory( targetRegistries );
+    }
+    
+    
+    protected void checkOidIsUnique( ServerEntry entry ) throws NamingException
+    {
+        String oid = getOid( entry );
+
+        if ( targetRegistries.getOidRegistry().hasOid( oid ) )
+        {
+            throw new LdapNamingException( "Oid " + oid + " for new schema entity is not unique.",
+                ResultCodeEnum.OTHER );
+        }
+    }
+
+
+    protected void checkOidIsUnique( SchemaObject schemaObject ) throws NamingException
+    {
+        String oid = schemaObject.getOid();
+
+        if ( targetRegistries.getOidRegistry().hasOid( oid ) )
+        {
+            throw new LdapNamingException( "Oid " + oid + " for new schema entity is not unique.",
+                ResultCodeEnum.OTHER );
+        }
+    }
+
+
+    protected void checkOidIsUnique( String oid ) throws NamingException
+    {
+        if ( targetRegistries.getOidRegistry().hasOid( oid ) )
+        {
+            throw new LdapNamingException( "Oid " + oid + " for new schema entity is not unique.",
+                ResultCodeEnum.OTHER );
+        }
+    }
+    
+    
+    protected abstract void modify( LdapDN name, ServerEntry entry, ServerEntry targetEntry, boolean cascade ) 
+        throws NamingException;
+    
+    
+    public final void modify( LdapDN name, ModificationOperation modOp, ServerEntry mods, ServerEntry entry, ServerEntry targetEntry, 
+        boolean cascade ) throws NamingException
+    {
+        modify( name, entry, targetEntry, cascade );
+    }
+
+
+    public final void modify( LdapDN name, List<Modification> mods, ServerEntry entry,
+        ServerEntry targetEntry, boolean cascade ) throws NamingException
+    {
+        modify( name, entry, targetEntry, cascade );
+    }
+
+    
+    protected Set<String> getOids( Set<ServerSearchResult> results ) throws NamingException
+    {
+        Set<String> oids = new HashSet<String>( results.size() );
+        
+        for ( ServerSearchResult result : results )
+        {
+            LdapDN dn = result.getDn();
+            dn.normalize( this.targetRegistries.getAttributeTypeRegistry().getNormalizerMapping() );
+            oids.add( ( String ) dn.getRdn().getValue() );
+        }
+        
+        return oids;
+    }
+    
+    
+    protected String getOid( ServerEntry entry ) throws NamingException
+    {
+        EntryAttribute oid = entry.get( m_oidAT );
+        
+        if ( oid == null )
+        {
+            return null;
+        }
+        
+        return oid.getString();
+    }
+    
+    
+    protected String getSchemaName( LdapDN name ) throws NamingException
+    {
+        return MetaSchemaUtils.getSchemaName( name );
+    }
+    
+    
+    protected Schema getSchema( LdapDN name ) throws NamingException
+    {
+        return loader.getSchema( MetaSchemaUtils.getSchemaName( name ) );
+    }
+    
+    
+    protected void unregisterOids( String oid ) throws NamingException
+    {
+        targetRegistries.getOidRegistry().unregister( oid );
+    }
+    
+    
+    protected void registerOids( SchemaObject obj ) throws NamingException
+    {
+        String[] names = obj.getNamesRef();
+        
+        if ( names != null )
+        {
+            for ( String name:names )
+            {
+                targetRegistries.getOidRegistry().register( name, obj.getOid() );
+            }
+        }
+        
+        targetRegistries.getOidRegistry().register( obj.getOid(), obj.getOid() );
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/AttributeClassLoader.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/AttributeClassLoader.java
new file mode 100644
index 0000000..aa98aaf
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/AttributeClassLoader.java
@@ -0,0 +1,79 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+
+package org.apache.directory.server.core.schema;
+
+
+import javax.naming.NamingException;
+import javax.naming.directory.InvalidAttributeValueException;
+
+import org.apache.directory.server.core.entry.ServerAttribute;
+import org.apache.directory.server.core.entry.ServerBinaryValue;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.entry.Value;
+
+
+/**
+ * A class loader that loads classes from an attribute within an entry.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$ $Date$
+ */
+public class AttributeClassLoader extends ClassLoader
+{
+    public ServerAttribute attribute;
+    
+
+    public AttributeClassLoader()
+    {
+        super( AttributeClassLoader.class.getClassLoader() );
+    }
+    
+    
+    public void setAttribute( EntryAttribute attribute ) throws NamingException
+    {
+        if ( ((ServerAttribute)attribute).getAttributeType().getSyntax().isHumanReadable() )
+        {
+            throw new InvalidAttributeValueException( "The attribute must be binary" );
+        }
+        
+        this.attribute = (ServerAttribute)attribute;
+    }
+
+    
+    public Class<?> findClass( String name ) throws ClassNotFoundException
+    {
+        byte[] classBytes = null;
+        
+        Value<?> value = attribute.get();
+        
+        if ( value instanceof ServerBinaryValue )
+        {
+            classBytes = ((ServerBinaryValue)value).get();
+
+            return defineClass( name, classBytes, 0, classBytes.length );
+        }
+        else
+        {
+            throw new ClassNotFoundException( "Failed to access attribute bytes." );
+        }
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/AttributeTypeImpl.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/AttributeTypeImpl.java
new file mode 100644
index 0000000..6914847
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/AttributeTypeImpl.java
@@ -0,0 +1,365 @@
+/*
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *   or more contributor license agreements.  See the NOTICE file
+ *   distributed with this work for additional information
+ *   regarding copyright ownership.  The ASF licenses this file
+ *   to you 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.
+ *
+ */
+package org.apache.directory.server.core.schema;
+
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.exception.LdapNamingException;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.schema.AbstractAttributeType;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.schema.MatchingRule;
+import org.apache.directory.shared.ldap.schema.MutableSchemaObject;
+import org.apache.directory.shared.ldap.schema.Syntax;
+import org.apache.directory.shared.ldap.schema.UsageEnum;
+
+
+/**
+ * An AttributeType implementation.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+class AttributeTypeImpl extends AbstractAttributeType implements MutableSchemaObject
+{
+    private static final long serialVersionUID = 1L;
+
+    private final Registries registries;
+    
+    /** The syntax OID associated with this AttributeType */
+    private String syntaxOid;
+    
+    /** The syntax associated with the syntaxID */
+    private Syntax syntax;
+    
+    /** The equality OID associated with this AttributeType */
+    private String equalityOid;
+
+    /** The equality MatchingRule associated with the equalityID */
+    private MatchingRule equalityMR;
+    
+    /** The substring OID associated with this AttributeType */
+    private String substrOid;
+
+    /** The substring MatchingRule associated with the substringID */
+    private MatchingRule substringMR;
+    
+    /** The ordering OID associated with this AttributeType */
+    private String orderingOid;
+    
+    /** The ordering MatchingRule associated with the orderingID */
+    private MatchingRule orderingMR;
+    
+    private String superiorOid;
+    
+    
+    public AttributeTypeImpl( String oid, Registries registries )
+    {
+        super( oid );
+        this.registries = registries;
+    }
+
+
+    /* (non-Javadoc)
+     * @see org.apache.directory.shared.ldap.schema.AttributeType#getEquality()
+     */
+    public MatchingRule getEquality() throws NamingException
+    {
+        if ( equalityMR == null )
+        {
+            if ( equalityOid == null )
+            {
+                equalityMR = findEquality( getSuperior() );
+            }
+            else
+            {
+                equalityMR = registries.getMatchingRuleRegistry().lookup( equalityOid );
+            }
+        }
+        
+        return equalityMR;
+    }
+
+
+    /**
+     * Recursively the equality matchingRule if one exists within the attribute heirarchy.
+     * 
+     * @param at the attribute to find a equality matchingRule for
+     * @return the equality MatchingRule or null if none exists for the attributeType
+     * @throws NamingException if there are problems accessing the attribute heirarchy
+     */
+    private MatchingRule findEquality( AttributeType at ) throws NamingException
+    {
+        if ( at == null )
+        {
+            return null;
+        }
+        
+        MatchingRule mr = at.getEquality();
+        
+        if ( mr == null )
+        {
+            return findEquality( at.getSuperior() );
+        }
+        else
+        {
+            return mr;
+        }
+    }
+
+
+    /* (non-Javadoc)
+     * @see org.apache.directory.shared.ldap.schema.AttributeType#getOrdering()
+     */
+    public MatchingRule getOrdering() throws NamingException
+    {
+        if ( orderingMR == null )
+        {
+            if ( orderingOid == null )
+            {
+                orderingMR = findOrdering( getSuperior() );
+            }
+            else
+            {
+                orderingMR = registries.getMatchingRuleRegistry().lookup( orderingOid );
+            }
+        }
+        
+        return orderingMR;
+    }
+
+
+    /**
+     * Recursively the ordering matchingRule if one exists within the attribute heirarchy.
+     * 
+     * @param at the attribute to find a ordering matchingRule for
+     * @return the ordering MatchingRule or null if none exists for the attributeType
+     * @throws NamingException if there are problems accessing the attribute heirarchy
+     */
+    private MatchingRule findOrdering( AttributeType at ) throws NamingException
+    {
+        if ( at == null )
+        {
+            return null;
+        }
+        
+        MatchingRule mr = at.getOrdering();
+        if ( mr == null )
+        {
+            return findOrdering( at.getSuperior() );
+        }
+        else
+        {
+            return mr;
+        }
+    }
+
+
+    /* (non-Javadoc)
+     * @see org.apache.directory.shared.ldap.schema.AttributeType#getSubstr()
+     */
+    public MatchingRule getSubstr() throws NamingException
+    {
+        if ( substringMR == null )
+        {
+            if ( substrOid == null )
+            {
+                substringMR = findSubstr( getSuperior() );
+            }
+            else
+            {
+                substringMR = registries.getMatchingRuleRegistry().lookup( substrOid );
+            }
+        }
+        
+        return substringMR;
+    }
+
+
+    /**
+     * Recursively gets the substring matchingRule if one exists within the attribute heirarchy.
+     * 
+     * @param at the attribute to find a substring matchingRule for
+     * @return the substring MatchingRule or null if none exists for the attributeType
+     * @throws NamingException if there are problems accessing the attribute heirarchy
+     */
+    private MatchingRule findSubstr( AttributeType at ) throws NamingException
+    {
+        if ( at == null )
+        {
+            return null;
+        }
+        
+        MatchingRule mr = at.getSubstr();
+        if ( mr == null )
+        {
+            return findSubstr( at.getSuperior() );
+        }
+        else
+        {
+            return mr;
+        }
+    }
+
+
+    /* (non-Javadoc)
+     * @see org.apache.directory.shared.ldap.schema.AttributeType#getSuperior()
+     */
+    public AttributeType getSuperior() throws NamingException
+    {
+        if ( superiorOid == null )
+        {
+            return null;
+        }
+        
+        return registries.getAttributeTypeRegistry().lookup( superiorOid );
+    }
+
+
+    /* (non-Javadoc)
+     * @see org.apache.directory.shared.ldap.schema.AttributeType#getSyntax()
+     */
+    public Syntax getSyntax() throws NamingException
+    {
+        if ( syntax == null )
+        {
+            if ( syntaxOid == null )
+            {
+                syntax = findSyntax( getSuperior() );
+            }
+            else
+            {
+                syntax = registries.getSyntaxRegistry().lookup( syntaxOid );
+            }
+        }
+        
+        return syntax;
+    }
+    
+    
+    /**
+     * Recursively walks up the ancestors to find the syntax for an attributeType.
+     * 
+     * @param at the attributeType to get the syntax for
+     * @return the Syntax for the attributeType
+     * @throws NamingException if no syntax can be found for the attributeType
+     */
+    private Syntax findSyntax( AttributeType at ) throws NamingException
+    {
+        if ( at == null )
+        {
+            throw new LdapNamingException( "Cannot find syntax for attributeType " + getName() 
+                + " after walking ancestors.", ResultCodeEnum.OTHER );
+        }
+        
+        if ( at.getSyntax() != null )
+        {
+            return at.getSyntax();
+        }
+        
+        return findSyntax( at.getSuperior() );
+    }
+    
+
+    public void setSyntaxOid( String syntaxOid )
+    {
+        this.syntaxOid = syntaxOid;
+    }
+    
+    
+    public void setSchema( String schema )
+    {
+        super.setSchema( schema );
+    }
+    
+    
+    public void setSuperiorOid( String superiorOid )
+    {
+        this.superiorOid = superiorOid;
+    }
+
+    
+    public void setEqualityOid( String equalityOid )
+    {
+        this.equalityOid = equalityOid;
+    }
+
+    
+    public void setSubstrOid( String substrOid )
+    {
+        this.substrOid = substrOid;
+    }
+    
+    
+    public void setOrderingOid( String orderingOid )
+    {
+        this.orderingOid = orderingOid;
+    }
+    
+    
+    public void setDescription( String description )
+    {
+        super.setDescription( description );
+    }
+    
+    
+    public void setNames( String[] names )
+    {
+        super.setNames( names );
+    }
+    
+    
+    public void setObsolete( boolean obsolete )
+    {
+        super.setObsolete( obsolete );
+    }
+    
+    
+    public void setCollective( boolean collective )
+    {
+        super.setCollective( collective );
+    }
+    
+    
+    public void setCanUserModify( boolean canUserModify )
+    {
+        super.setCanUserModify( canUserModify );
+    }
+    
+    
+    public void setLength( int length )
+    {
+        super.setLength( length );
+    }
+    
+    
+    public void setSingleValue( boolean singleValue )
+    {
+        super.setSingleValue( singleValue );
+    }
+    
+    
+    public void setUsage( UsageEnum usage )
+    {
+        super.setUsage( usage );
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/DescriptionParsers.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/DescriptionParsers.java
new file mode 100644
index 0000000..9e1c9ca
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/DescriptionParsers.java
@@ -0,0 +1,840 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.schema;
+
+
+import java.text.ParseException;
+import java.util.List;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.constants.MetaSchemaConstants;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.entry.Value;
+import org.apache.directory.shared.ldap.exception.LdapInvalidAttributeValueException;
+import org.apache.directory.shared.ldap.exception.LdapOperationNotSupportedException;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.schema.DITContentRule;
+import org.apache.directory.shared.ldap.schema.DITStructureRule;
+import org.apache.directory.shared.ldap.schema.MatchingRule;
+import org.apache.directory.shared.ldap.schema.MatchingRuleUse;
+import org.apache.directory.shared.ldap.schema.MutableSchemaObject;
+import org.apache.directory.shared.ldap.schema.NameForm;
+import org.apache.directory.shared.ldap.schema.ObjectClass;
+import org.apache.directory.shared.ldap.schema.Syntax;
+import org.apache.directory.shared.ldap.schema.syntax.AbstractSchemaDescription;
+import org.apache.directory.shared.ldap.schema.syntax.AttributeTypeDescription;
+import org.apache.directory.shared.ldap.schema.syntax.ComparatorDescription;
+import org.apache.directory.shared.ldap.schema.syntax.DITContentRuleDescription;
+import org.apache.directory.shared.ldap.schema.syntax.DITStructureRuleDescription;
+import org.apache.directory.shared.ldap.schema.syntax.LdapSyntaxDescription;
+import org.apache.directory.shared.ldap.schema.syntax.MatchingRuleDescription;
+import org.apache.directory.shared.ldap.schema.syntax.MatchingRuleUseDescription;
+import org.apache.directory.shared.ldap.schema.syntax.NameFormDescription;
+import org.apache.directory.shared.ldap.schema.syntax.NormalizerDescription;
+import org.apache.directory.shared.ldap.schema.syntax.ObjectClassDescription;
+import org.apache.directory.shared.ldap.schema.syntax.SyntaxCheckerDescription;
+import org.apache.directory.shared.ldap.schema.syntax.parser.AttributeTypeDescriptionSchemaParser;
+import org.apache.directory.shared.ldap.schema.syntax.parser.ComparatorDescriptionSchemaParser;
+import org.apache.directory.shared.ldap.schema.syntax.parser.DITContentRuleDescriptionSchemaParser;
+import org.apache.directory.shared.ldap.schema.syntax.parser.DITStructureRuleDescriptionSchemaParser;
+import org.apache.directory.shared.ldap.schema.syntax.parser.LdapSyntaxDescriptionSchemaParser;
+import org.apache.directory.shared.ldap.schema.syntax.parser.MatchingRuleDescriptionSchemaParser;
+import org.apache.directory.shared.ldap.schema.syntax.parser.MatchingRuleUseDescriptionSchemaParser;
+import org.apache.directory.shared.ldap.schema.syntax.parser.NameFormDescriptionSchemaParser;
+import org.apache.directory.shared.ldap.schema.syntax.parser.NormalizerDescriptionSchemaParser;
+import org.apache.directory.shared.ldap.schema.syntax.parser.ObjectClassDescriptionSchemaParser;
+import org.apache.directory.shared.ldap.schema.syntax.parser.SyntaxCheckerDescriptionSchemaParser;
+
+
+/**
+ * Parses descriptions using a number of different parsers for schema descriptions.
+ * Also checks to make sure some things are valid as it's parsing paramters of
+ * certain entity types.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class DescriptionParsers
+{
+    private static final String OTHER_SCHEMA = "other";
+    private static final String[] EMPTY = new String[0];
+    private static final Integer[] EMPTY_INT_ARRAY = new Integer[0];
+
+    private static final ComparatorDescription[] EMPTY_COMPARATORS = new ComparatorDescription[0];
+    private static final NormalizerDescription[] EMPTY_NORMALIZERS = new NormalizerDescription[0];
+    private static final SyntaxCheckerDescription[] EMPTY_SYNTAX_CHECKERS = new SyntaxCheckerDescription[0];
+    private static final Syntax[] EMPTY_SYNTAXES = new Syntax[0];
+    private static final MatchingRule[] EMPTY_MATCHING_RULES = new MatchingRule[0];
+    private static final AttributeType[] EMPTY_ATTRIBUTE_TYPES = new AttributeType[0];
+    private static final ObjectClass[] EMPTY_OBJECT_CLASSES = new ObjectClass[0];
+    private static final MatchingRuleUse[] EMPTY_MATCHING_RULE_USES = new MatchingRuleUse[0];
+    private static final DITStructureRule[] EMPTY_DIT_STRUCTURE_RULES = new DITStructureRule[0];
+    private static final DITContentRule[] EMPTY_DIT_CONTENT_RULES = new DITContentRule[0];
+    private static final NameForm[] EMPTY_NAME_FORMS = new NameForm[0];
+
+    private final Registries globalRegistries;
+    
+    private final ComparatorDescriptionSchemaParser comparatorParser =
+        new ComparatorDescriptionSchemaParser();
+    private final NormalizerDescriptionSchemaParser normalizerParser =
+        new NormalizerDescriptionSchemaParser();
+    private final SyntaxCheckerDescriptionSchemaParser syntaxCheckerParser =
+        new SyntaxCheckerDescriptionSchemaParser();
+    private final LdapSyntaxDescriptionSchemaParser syntaxParser =
+        new LdapSyntaxDescriptionSchemaParser();
+    private final MatchingRuleDescriptionSchemaParser matchingRuleParser =
+        new MatchingRuleDescriptionSchemaParser();
+    private final AttributeTypeDescriptionSchemaParser attributeTypeParser = 
+        new AttributeTypeDescriptionSchemaParser();
+    private final ObjectClassDescriptionSchemaParser objectClassParser = 
+        new ObjectClassDescriptionSchemaParser();
+    private final MatchingRuleUseDescriptionSchemaParser matchingRuleUseParser = 
+        new MatchingRuleUseDescriptionSchemaParser();
+    private final DITStructureRuleDescriptionSchemaParser ditStructureRuleParser =
+        new DITStructureRuleDescriptionSchemaParser();
+    private final DITContentRuleDescriptionSchemaParser ditContentRuleParser =
+        new DITContentRuleDescriptionSchemaParser();
+    private final NameFormDescriptionSchemaParser nameFormParser =
+        new NameFormDescriptionSchemaParser();
+    
+    private final SchemaPartitionDao dao;
+    
+    
+    /**
+     * Creates a description parser.
+     * 
+     * @param globalRegistries the registries to use while creating new schema entities
+     */
+    public DescriptionParsers( Registries globalRegistries, SchemaPartitionDao dao )
+    {
+        this.globalRegistries = globalRegistries;
+        this.dao = dao;
+    }
+
+    
+    public SyntaxCheckerDescription[] parseSyntaxCheckers( EntryAttribute attr ) throws NamingException
+    {
+        if ( attr == null || attr.size() == 0 )
+        {
+            return EMPTY_SYNTAX_CHECKERS;
+        }
+        
+        SyntaxCheckerDescription[] syntaxCheckerDescriptions = new SyntaxCheckerDescription[attr.size()];
+        
+        int pos = 0;
+        
+        for ( Value<?> value:attr )
+        {
+            try
+            {
+                syntaxCheckerDescriptions[pos++] = 
+                    syntaxCheckerParser.parseSyntaxCheckerDescription( (String)value.get() );
+            }
+            catch ( ParseException e )
+            {
+                LdapInvalidAttributeValueException iave = new LdapInvalidAttributeValueException( 
+                    "The following does not conform to the syntaxCheckerDescription syntax: " + value, 
+                    ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX );
+                iave.setRootCause( e );
+                throw iave;
+            }
+        }
+        
+        return syntaxCheckerDescriptions;
+    }
+    
+    
+    public NormalizerDescription[] parseNormalizers( EntryAttribute attr ) throws NamingException
+    {
+        if ( attr == null || attr.size() == 0 )
+        {
+            return EMPTY_NORMALIZERS;
+        }
+        
+        NormalizerDescription[] normalizerDescriptions = new NormalizerDescription[attr.size()];
+        
+        int pos = 0;
+        
+        for ( Value<?> value:attr )
+        {
+            try
+            {
+                normalizerDescriptions[pos++] = normalizerParser.parseNormalizerDescription( (String)value.get() );
+            }
+            catch ( ParseException e )
+            {
+                LdapInvalidAttributeValueException iave = new LdapInvalidAttributeValueException( 
+                    "The following does not conform to the normalizerDescription syntax: " + value.get(), 
+                    ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX );
+                iave.setRootCause( e );
+                throw iave;
+            }
+        }
+        
+        return normalizerDescriptions;
+    }
+    
+
+    public ComparatorDescription[] parseComparators( EntryAttribute attr ) throws NamingException
+    {
+        if ( attr == null || attr.size() == 0 )
+        {
+            return EMPTY_COMPARATORS;
+        }
+        
+        ComparatorDescription[] comparatorDescriptions = new ComparatorDescription[attr.size()];
+        
+        int pos = 0;
+        
+        for ( Value<?> value:attr )
+        {
+            try
+            {
+                comparatorDescriptions[pos++] = comparatorParser.parseComparatorDescription( ( String ) value.get() );
+            }
+            catch ( ParseException e )
+            {
+                LdapInvalidAttributeValueException iave = new LdapInvalidAttributeValueException( 
+                    "The following does not conform to the comparatorDescription syntax: " + value.get(), 
+                    ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX );
+                iave.setRootCause( e );
+                throw iave;
+            }
+        }
+        
+        return comparatorDescriptions;
+    }
+    
+
+    /**
+     * Parses a set of attributeTypeDescriptions held within an attribute into 
+     * schema entities.
+     * 
+     * @param attr the attribute containing attributeTypeDescriptions
+     * @return the set of attributeType objects for the descriptions 
+     * @throws NamingException if there are problems parsing the descriptions
+     */
+    public AttributeType[] parseAttributeTypes( EntryAttribute attr ) throws NamingException
+    {
+        if ( attr == null || attr.size() == 0 )
+        {
+            return EMPTY_ATTRIBUTE_TYPES;
+        }
+        
+        AttributeType[] attributeTypes = new AttributeType[attr.size()];
+        
+        int pos = 0;
+        
+        for ( Value<?> value:attr )
+        {
+            AttributeTypeDescription desc = null;
+            
+            try
+            {
+                desc = attributeTypeParser.parseAttributeTypeDescription( ( String ) value.get() );
+            }
+            catch ( ParseException e )
+            {
+                LdapInvalidAttributeValueException iave = new LdapInvalidAttributeValueException( 
+                    "The following does not conform to the attributeTypeDescription syntax: " + value.get(), 
+                    ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX );
+                iave.setRootCause( e );
+                throw iave;
+            }
+
+            // if the supertype is provided make sure it exists in some schema
+            if ( desc.getSuperType() != null && ! dao.hasAttributeType( desc.getSuperType() ) )
+            {
+                throw new LdapOperationNotSupportedException(
+                    "Cannot permit the addition of an attributeType with an invalid super type: " 
+                        + desc.getSuperType(), 
+                    ResultCodeEnum.UNWILLING_TO_PERFORM );
+            }
+
+            // if the syntax is provided by the description make sure it exists in some schema
+            if ( desc.getSyntax() != null && ! dao.hasSyntax( desc.getSyntax() ) )
+            {
+                throw new LdapOperationNotSupportedException(
+                    "Cannot permit the addition of an attributeType with an invalid syntax: " + desc.getSyntax(), 
+                    ResultCodeEnum.UNWILLING_TO_PERFORM );
+            }
+            
+            // if the matchingRule is provided make sure it exists in some schema
+            if ( desc.getEqualityMatchingRule() != null && ! dao.hasMatchingRule( desc.getEqualityMatchingRule() ) )
+            {
+                throw new LdapOperationNotSupportedException(
+                    "Cannot permit the addition of an attributeType with an invalid EQUALITY matchingRule: " 
+                        + desc.getEqualityMatchingRule(), 
+                    ResultCodeEnum.UNWILLING_TO_PERFORM );
+            }
+
+            // if the matchingRule is provided make sure it exists in some schema
+            if ( desc.getOrderingMatchingRule() != null && ! dao.hasMatchingRule( desc.getOrderingMatchingRule() ) )
+            {
+                throw new LdapOperationNotSupportedException(
+                    "Cannot permit the addition of an attributeType with an invalid ORDERING matchingRule: " 
+                        + desc.getOrderingMatchingRule(), 
+                    ResultCodeEnum.UNWILLING_TO_PERFORM );
+            }
+
+            // if the matchingRule is provided make sure it exists in some schema
+            if ( desc.getSubstringsMatchingRule() != null && ! dao.hasMatchingRule( desc.getSubstringsMatchingRule() ) )
+            {
+                throw new LdapOperationNotSupportedException(
+                    "Cannot permit the addition of an attributeType with an invalid SUBSTRINGS matchingRule: " 
+                        + desc.getSubstringsMatchingRule(), 
+                    ResultCodeEnum.UNWILLING_TO_PERFORM );
+            }
+
+            // if the equality matching rule is null and no super type is specified then there is
+            // definitely no equality matchingRule that can be resolved.  We cannot use an attribute
+            // without a matchingRule for search or for building indices not to mention lookups.
+            if ( desc.getEqualityMatchingRule() == null && desc.getSuperType() == null )
+            {
+                throw new LdapOperationNotSupportedException(
+                    "Cannot permit the addition of an attributeType with an no EQUALITY matchingRule " +
+                    "\nand no super type from which to derive an EQUALITY matchingRule.", 
+                    ResultCodeEnum.UNWILLING_TO_PERFORM );
+            }
+            else if ( desc.getEqualityMatchingRule() == null )
+            {
+                AttributeType superType = globalRegistries.getAttributeTypeRegistry().lookup( desc.getSuperType() );
+                if ( superType.getEquality() == null )
+                {
+                    throw new LdapOperationNotSupportedException(
+                        "Cannot permit the addition of an attributeType with which cannot resolve an " +
+                        "EQUALITY matchingRule from it's super type.", 
+                        ResultCodeEnum.UNWILLING_TO_PERFORM );
+                }
+            }
+            
+            // a syntax is manditory for an attributeType and if not provided by the description 
+            // must be provided from some ancestor in the attributeType hierarchy; without either
+            // of these the description definitely cannot resolve a syntax and cannot be allowed.
+            // if a supertype exists then it must resolve a proper syntax somewhere in the hierarchy.
+            if ( desc.getSyntax() == null && desc.getSuperType() == null )
+            {
+                throw new LdapOperationNotSupportedException(
+                    "Cannot permit the addition of an attributeType with an no syntax " +
+                    "\nand no super type from which to derive a syntax.", 
+                    ResultCodeEnum.UNWILLING_TO_PERFORM );
+            }
+            
+
+            AttributeTypeImpl at = new AttributeTypeImpl( desc.getNumericOid(), globalRegistries );
+            at.setCanUserModify( desc.isUserModifiable() );
+            at.setCollective( desc.isCollective() );
+            at.setEqualityOid( desc.getEqualityMatchingRule() );
+            at.setOrderingOid( desc.getOrderingMatchingRule() );
+            at.setSingleValue( desc.isSingleValued() );
+            at.setSubstrOid( desc.getSubstringsMatchingRule() );
+            at.setSuperiorOid( desc.getSuperType() );
+            at.setSyntaxOid( desc.getSyntax() );
+            at.setUsage( desc.getUsage() );
+            
+            setSchemaObjectProperties( desc, at );
+
+            attributeTypes[pos++] = at;
+        }
+        
+        return attributeTypes;
+    }
+    
+    
+    /**
+     * Parses a set of objectClassDescriptions held within an attribute into 
+     * schema entities.
+     * 
+     * @param attr the attribute containing objectClassDescriptions
+     * @return the set of objectClass objects for the descriptions 
+     * @throws NamingException if there are problems parsing the descriptions
+     */
+    public ObjectClass[] parseObjectClasses( EntryAttribute attr ) throws NamingException
+    {
+        if ( attr == null || attr.size() == 0 )
+        {
+            return EMPTY_OBJECT_CLASSES;
+        }
+        
+        ObjectClass[] objectClasses = new ObjectClass[attr.size()];
+        
+        int pos = 0;
+        
+        for ( Value<?> value:attr )
+        {
+            ObjectClassDescription desc = null;
+            
+            try
+            {
+                desc = objectClassParser.parseObjectClassDescription( ( String ) value.get() );
+            }
+            catch ( ParseException e )
+            {
+                LdapInvalidAttributeValueException iave = new LdapInvalidAttributeValueException( 
+                    "The following does not conform to the objectClassDescription syntax: " + value.get(), 
+                    ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX );
+                iave.setRootCause( e );
+                throw iave;
+            }
+            
+            // if the super objectClasses are provided make sure it exists in some schema
+            if ( desc.getSuperiorObjectClasses() != null && desc.getSuperiorObjectClasses().size() > 0 )
+            {
+                for ( String superior : desc.getSuperiorObjectClasses() )
+                {
+                    if ( superior.equals( SchemaConstants.TOP_OC_OID ) || 
+                        superior.equalsIgnoreCase( SchemaConstants.TOP_OC ) )
+                    {
+                        continue;
+                    }
+                    
+                    if ( ! dao.hasObjectClass( superior ) )
+                    {
+                        throw new LdapOperationNotSupportedException(
+                            "Cannot permit the addition of an objectClass with an invalid superior objectClass: " 
+                                + superior, 
+                            ResultCodeEnum.UNWILLING_TO_PERFORM );
+                    }
+                }
+            }
+            
+            // if the may list is provided make sure attributes exists in some schema
+            if ( desc.getMayAttributeTypes() != null && desc.getMayAttributeTypes().size() > 0 )
+            {
+                for ( String mayAttr : desc.getMayAttributeTypes() )
+                {
+                    if ( ! dao.hasAttributeType( mayAttr ) )
+                    {
+                        throw new LdapOperationNotSupportedException(
+                            "Cannot permit the addition of an objectClass with an invalid " +
+                            "attributeType in the mayList: " + mayAttr, 
+                            ResultCodeEnum.UNWILLING_TO_PERFORM );
+                    }
+                }
+            }
+            
+            // if the must list is provided make sure attributes exists in some schema
+            if ( desc.getMustAttributeTypes() != null && desc.getMustAttributeTypes().size() > 0 )
+            {
+                for ( String mustAttr : desc.getMustAttributeTypes() )
+                {
+                    if ( ! dao.hasAttributeType( mustAttr ) )
+                    {
+                        throw new LdapOperationNotSupportedException(
+                            "Cannot permit the addition of an objectClass with an invalid " +
+                            "attributeType in the mustList: " + mustAttr, 
+                            ResultCodeEnum.UNWILLING_TO_PERFORM );
+                    }
+                }
+            }
+            
+            ObjectClassImpl oc = new ObjectClassImpl( desc.getNumericOid(), globalRegistries );
+            oc.setMayListOids( desc.getMayAttributeTypes().toArray( EMPTY) );
+            oc.setMustListOids( desc.getMustAttributeTypes().toArray( EMPTY ) );
+            oc.setSuperClassOids( desc.getSuperiorObjectClasses().toArray( EMPTY ) );
+            oc.setType( desc.getKind() );
+            setSchemaObjectProperties( desc, oc );
+            
+            objectClasses[pos++] = oc;
+        }
+        
+        return objectClasses;
+    }
+
+
+    /**
+     * Parses a set of matchingRuleUseDescriptions held within an attribute into 
+     * schema entities.
+     * 
+     * @param attr the attribute containing matchingRuleUseDescriptions
+     * @return the set of matchingRuleUse objects for the descriptions 
+     * @throws NamingException if there are problems parsing the descriptions
+     */
+    public MatchingRuleUse[] parseMatchingRuleUses( EntryAttribute attr ) throws NamingException
+    {
+        if ( attr == null || attr.size() == 0 )
+        {
+            return EMPTY_MATCHING_RULE_USES;
+        }
+        
+        MatchingRuleUse[] matchingRuleUses = new MatchingRuleUse[attr.size()];
+        
+        int pos = 0;
+        
+        for ( Value<?> value:attr )
+        {
+            MatchingRuleUseDescription desc = null;
+            
+            try
+            {
+                desc = matchingRuleUseParser.parseMatchingRuleUseDescription( ( String ) value.get() );
+            }
+            catch ( ParseException e )
+            {
+                LdapInvalidAttributeValueException iave = new LdapInvalidAttributeValueException( 
+                    "The following does not conform to the matchingRuleUseDescription syntax: " + value.get(), 
+                    ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX );
+                iave.setRootCause( e );
+                throw iave;
+            }
+            
+            MatchingRuleUseImpl mru = new MatchingRuleUseImpl( desc.getNumericOid(), globalRegistries );
+            mru.setApplicableAttributesOids( desc.getApplicableAttributes().toArray( EMPTY ) );
+            setSchemaObjectProperties( desc, mru );
+            
+            matchingRuleUses[pos++] = mru;
+        }
+
+        return matchingRuleUses;
+    }
+
+
+    /**
+     * Parses a set of ldapSyntaxDescriptions held within an attribute into 
+     * schema entities.
+     * 
+     * @param attr the attribute containing ldapSyntaxDescriptions
+     * @return the set of Syntax objects for the descriptions 
+     * @throws NamingException if there are problems parsing the descriptions
+     */
+    public Syntax[] parseSyntaxes( EntryAttribute attr ) throws NamingException
+    {
+        if ( attr == null || attr.size() == 0 )
+        {
+            return EMPTY_SYNTAXES;
+        }
+        
+        Syntax[] syntaxes = new Syntax[attr.size()];
+
+        int pos = 0;
+        
+        for ( Value<?> value:attr )
+        {
+            LdapSyntaxDescription desc = null;
+            
+            try
+            {
+                desc = syntaxParser.parseLdapSyntaxDescription( ( String ) value.get() );
+            }
+            catch ( ParseException e )
+            {
+                LdapInvalidAttributeValueException iave = new LdapInvalidAttributeValueException( 
+                    "The following does not conform to the ldapSyntaxDescription syntax: " + value.get(), 
+                    ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX );
+                iave.setRootCause( e );
+                throw iave;
+            }
+            
+            if ( ! dao.hasSyntaxChecker( desc.getNumericOid() ) )
+            {
+                throw new LdapOperationNotSupportedException(
+                    "Cannot permit the addition of a syntax without the prior creation of a " +
+                    "\nsyntaxChecker with the same object identifier of the syntax!",
+                    ResultCodeEnum.UNWILLING_TO_PERFORM );
+            }
+
+            SyntaxImpl syntax = new SyntaxImpl( desc.getNumericOid(), globalRegistries.getSyntaxCheckerRegistry() );
+            setSchemaObjectProperties( desc, syntax );
+            syntax.setHumanReadable( isHumanReadable( desc ) );
+            syntaxes[pos++] = syntax;
+        }
+        
+        return syntaxes;
+    }
+
+
+    /**
+     * Parses a set of matchingRuleDescriptions held within an attribute into 
+     * schema entities.
+     * 
+     * @param attr the attribute containing matchingRuleDescriptions
+     * @return the set of matchingRule objects for the descriptions 
+     * @throws NamingException if there are problems parsing the descriptions
+     */
+    public MatchingRule[] parseMatchingRules( EntryAttribute attr ) throws NamingException
+    {
+        if ( attr == null || attr.size() == 0 )
+        {
+            return EMPTY_MATCHING_RULES;
+        }
+        
+        MatchingRule[] matchingRules = new MatchingRule[attr.size()];
+
+        int pos = 0;
+        
+        for ( Value<?> value:attr )
+        {
+            MatchingRuleDescription desc = null;
+
+            try
+            {
+                desc = matchingRuleParser.parseMatchingRuleDescription( ( String ) value.get() );
+            }
+            catch ( ParseException e )
+            {
+                LdapInvalidAttributeValueException iave = new LdapInvalidAttributeValueException( 
+                    "The following does not conform to the matchingRuleDescription syntax: " + value.get(), 
+                    ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX );
+                iave.setRootCause( e );
+                throw iave;
+            }
+            
+            if ( ! dao.hasSyntax( desc.getSyntax() )  )
+            {
+                throw new LdapOperationNotSupportedException(
+                    "Cannot create a matchingRule that depends on non-existant syntax: " + desc.getSyntax(),
+                    ResultCodeEnum.UNWILLING_TO_PERFORM );
+            }
+            
+            MatchingRuleImpl mr = new MatchingRuleImpl( desc.getNumericOid(), desc.getSyntax(), globalRegistries );
+            setSchemaObjectProperties( desc, mr );
+            
+            matchingRules[pos++] = mr;
+        }
+        
+        return matchingRules;
+    }
+    
+
+    /**
+     * Parses a set of dITStructureRuleDescriptions held within an attribute into 
+     * schema entities.
+     * 
+     * @param attr the attribute containing dITStructureRuleDescriptions
+     * @return the set of DITStructureRule objects for the descriptions 
+     * @throws NamingException if there are problems parsing the descriptions
+     */
+    public DITStructureRule[] parseDitStructureRules( EntryAttribute attr ) throws NamingException
+    {
+        if ( attr == null || attr.size() == 0 )
+        {
+            return EMPTY_DIT_STRUCTURE_RULES;
+        }
+        
+        DITStructureRule[] ditStructureRules = new DITStructureRule[attr.size()];
+        
+        int pos = 0;
+        
+        for ( Value<?> value:attr )
+        {
+            DITStructureRuleDescription desc = null;
+     
+            try
+            {
+                desc = ditStructureRuleParser.parseDITStructureRuleDescription( ( String ) value.get() );
+            }
+            catch ( ParseException e )
+            {
+                LdapInvalidAttributeValueException iave = new LdapInvalidAttributeValueException( 
+                    "The following does not conform to the ditStructureRuleDescription syntax: " + value.get(), 
+                    ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX );
+                iave.setRootCause( e );
+                throw iave;
+            }
+            
+            DitStructureRuleImpl dsr = new DitStructureRuleImpl( desc.getNumericOid(), 
+                desc.getRuleId(), globalRegistries );
+            dsr.setSuperClassRuleIds( desc.getSuperRules().toArray( EMPTY_INT_ARRAY ) );
+            
+            setSchemaObjectProperties( desc, dsr );
+
+            ditStructureRules[pos++] = dsr;
+        }
+        
+        return ditStructureRules;
+    }
+
+    
+    /**
+     * Parses a set of dITContentRuleDescriptions held within an attribute into 
+     * schema entities.
+     * 
+     * @param attr the attribute containing dITContentRuleDescriptions
+     * @return the set of DITContentRule objects for the descriptions 
+     * @throws NamingException if there are problems parsing the descriptions
+     */
+    public DITContentRule[] parseDitContentRules( EntryAttribute attr ) throws NamingException
+    {
+        if ( attr == null || attr.size() == 0 )
+        {
+            return EMPTY_DIT_CONTENT_RULES;
+        }
+        
+        DITContentRule[] ditContentRules = new DITContentRule[attr.size()];
+
+        int pos = 0;
+        
+        for ( Value<?> value:attr )
+        {
+            DITContentRuleDescription desc = null;
+     
+            try
+            {
+                desc = ditContentRuleParser.parseDITContentRuleDescription( ( String ) value.get() );
+            }
+            catch ( ParseException e )
+            {
+                LdapInvalidAttributeValueException iave = new LdapInvalidAttributeValueException( 
+                    "The following does not conform to the ditContentRuleDescription syntax: " + value.get(), 
+                    ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX );
+                iave.setRootCause( e );
+                throw iave;
+            }
+            
+            DitContentRuleImpl dcr = new DitContentRuleImpl( desc.getNumericOid(), globalRegistries );
+            dcr.setAuxObjectClassOids( desc.getAuxiliaryObjectClasses().toArray( EMPTY ) );
+            dcr.setMayNameOids( desc.getMayAttributeTypes().toArray( EMPTY ) );
+            dcr.setMustNameOids( desc.getMustAttributeTypes().toArray( EMPTY ) );
+            dcr.setNotNameOids( desc.getNotAttributeTypes().toArray( EMPTY ) );
+            
+            setSchemaObjectProperties( desc, dcr );
+
+            ditContentRules[pos++] = dcr;
+        }
+        
+        return ditContentRules;
+    }
+
+    
+    /**
+     * Parses a set of nameFormDescriptions held within an attribute into 
+     * schema entities.
+     * 
+     * @param attr the attribute containing nameFormDescriptions
+     * @return the set of NameFormRule objects for the descriptions 
+     * @throws NamingException if there are problems parsing the descriptions
+     */
+    public NameForm[] parseNameForms( EntryAttribute attr ) throws NamingException
+    {
+        if ( attr == null || attr.size() == 0 )
+        {
+            return EMPTY_NAME_FORMS;
+        }
+        
+        NameForm[] nameForms = new NameForm[attr.size()];
+
+        int pos = 0;
+        
+        for ( Value<?> value:attr )
+        {
+            NameFormDescription desc = null;
+            
+            try
+            {
+                desc = nameFormParser.parseNameFormDescription( ( String  ) value.get() );
+            }
+            catch ( ParseException e )
+            {
+                LdapInvalidAttributeValueException iave = new LdapInvalidAttributeValueException( 
+                    "The following does not conform to the nameFormDescription syntax: " + value.get(), 
+                    ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX );
+                iave.setRootCause( e );
+                throw iave;
+            }
+            
+            NameFormImpl nf = new NameFormImpl( desc.getNumericOid(), globalRegistries );
+            nf.setMayUseOids( desc.getMayAttributeTypes().toArray( EMPTY ) );
+            nf.setMustUseOids( desc.getMustAttributeTypes().toArray( EMPTY ) );
+            nf.setObjectClassOid( desc.getStructuralObjectClass() );
+            
+            setSchemaObjectProperties( desc, nf );
+            
+            nameForms[pos++] = nf;
+        }
+        
+        return nameForms;
+    }
+    
+    
+    /**
+     * Called to populate the common schema object properties using an abstract 
+     * description object.
+     *   
+     * @param desc the source description object to copy properties from
+     * @param obj the mutable schema object to copy properites to
+     */
+    private void setSchemaObjectProperties( AbstractSchemaDescription desc, MutableSchemaObject obj )
+    {
+        obj.setDescription( desc.getDescription() );
+        obj.setSchema( getSchema( desc ) );
+
+        if ( ! ( desc instanceof LdapSyntaxDescription ) )
+        {
+            obj.setNames( desc.getNames().toArray( EMPTY ) );
+            obj.setObsolete( desc.isObsolete() );
+        }
+    }
+    
+    
+    /**
+     * Checks to see if the syntax description is human readable by checking 
+     * for the presence of the X-IS-HUMAN_READABLE schema extension.
+     * 
+     * @param desc the ldapSyntaxDescription 
+     * @return true if the syntax is human readable, false otherwise
+     */
+    private boolean isHumanReadable( LdapSyntaxDescription desc )
+    {
+        List<String> values = desc.getExtensions().get( MetaSchemaConstants.X_IS_HUMAN_READABLE );
+        
+        if ( values == null || values.size() == 0 )
+        {
+            return false;
+        }
+        else
+        {
+            String value = values.get( 0 );
+            if ( value.equals( "TRUE" ) )
+            {
+                return true;
+            }
+            else
+            {
+                return false;
+            }
+        }
+    }
+    
+    
+    /**
+     * Gets the schema name for the schema description by looking up the value 
+     * of the X-SCHEMA schema extension of the description. 
+     * 
+     * @param desc the schema description 
+     * @return the schema name for the schema entity
+     */
+    private String getSchema( AbstractSchemaDescription desc ) 
+    {
+        List<String> values = desc.getExtensions().get( MetaSchemaConstants.X_SCHEMA );
+        
+        if ( values == null )
+        {
+            return OTHER_SCHEMA;
+        }
+        else 
+        {
+            return values.get( 0 );
+        }
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/DitContentRuleImpl.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/DitContentRuleImpl.java
new file mode 100644
index 0000000..7f29fe4
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/DitContentRuleImpl.java
@@ -0,0 +1,236 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.schema;
+
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.schema.AbstractSchemaObject;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.schema.DITContentRule;
+import org.apache.directory.shared.ldap.schema.MutableSchemaObject;
+import org.apache.directory.shared.ldap.schema.ObjectClass;
+
+
+/**
+ * A DitContentRule bean implementation that uses a registries object to dynamically
+ * resolve it's dependencies.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class DitContentRuleImpl extends AbstractSchemaObject implements MutableSchemaObject, DITContentRule
+{
+    private static final long serialVersionUID = 1L;
+    private static final String[] EMPTY_STR_ARRAY = new String[0];
+    private static final ObjectClass[] EMPTY_OC_ARRAY = new ObjectClass[0];
+    private static final AttributeType[] EMPTY_ATTR_ARRAY = new AttributeType[0];
+
+    private final Registries registries;
+    
+    private String[] auxObjectClassOids = EMPTY_STR_ARRAY;
+    private ObjectClass[] auxObjectClasses = EMPTY_OC_ARRAY;
+    
+    private String[] mustNameOids = EMPTY_STR_ARRAY;
+    private AttributeType[] mustNames = EMPTY_ATTR_ARRAY;
+    
+    private String[] mayNameOids = EMPTY_STR_ARRAY;
+    private AttributeType[] mayNames = EMPTY_ATTR_ARRAY;
+    
+    private String[] notNameOids = EMPTY_STR_ARRAY;
+    private AttributeType[] notNames = EMPTY_ATTR_ARRAY;
+    
+    
+    protected DitContentRuleImpl( String oid, Registries registries )
+    {
+        super( oid );
+        this.registries = registries;
+    }
+
+    
+    public void setAuxObjectClassOids( String[] auxObjectClassOids )
+    {
+        if ( auxObjectClassOids == null )
+        {
+            this.auxObjectClassOids = EMPTY_STR_ARRAY;
+            this.auxObjectClasses = EMPTY_OC_ARRAY;
+        }
+        else
+        {
+            this.auxObjectClassOids = auxObjectClassOids;
+            this.auxObjectClasses = new ObjectClass[auxObjectClassOids.length];
+        }
+    }
+
+    
+    /* (non-Javadoc)
+     * @see org.apache.directory.shared.ldap.schema.DITContentRule#getAuxObjectClasses()
+     */
+    public ObjectClass[] getAuxObjectClasses() throws NamingException
+    {
+        if ( auxObjectClassOids == null || auxObjectClassOids.length == 0 )
+        {
+            return EMPTY_OC_ARRAY;
+        }
+        
+        for ( int ii = 0; ii < auxObjectClassOids.length; ii++ )
+        {
+            auxObjectClasses[ii] = registries.getObjectClassRegistry().lookup( auxObjectClassOids[ii] );
+        }
+        
+        return auxObjectClasses;
+    }
+
+    
+    public void setMayNameOids( String[] mayNameOids )
+    {
+        if ( mayNameOids == null )
+        {
+            this.mayNameOids = EMPTY_STR_ARRAY;
+            this.mayNames = EMPTY_ATTR_ARRAY;
+        }
+        else
+        {
+            this.mayNameOids = mayNameOids;
+            this.mayNames = new AttributeType[mayNameOids.length];
+        }
+    }
+    
+
+    /* (non-Javadoc)
+     * @see org.apache.directory.shared.ldap.schema.DITContentRule#getMayNames()
+     */
+    public AttributeType[] getMayNames() throws NamingException
+    {
+        if ( mayNameOids == null || mayNameOids.length == 0 )
+        {
+            return EMPTY_ATTR_ARRAY;
+        }
+        
+        for ( int ii = 0; ii < mayNameOids.length; ii++ )
+        {
+            mayNames[ii] = registries.getAttributeTypeRegistry().lookup( mayNameOids[ii] );
+        }
+        
+        return mayNames;
+    }
+
+    
+    public void setMustNameOids( String[] mustNameOids )
+    {
+        if ( mustNameOids == null )
+        {
+            this.mustNameOids = EMPTY_STR_ARRAY;
+            this.mustNames = EMPTY_ATTR_ARRAY;
+        }
+        else
+        {
+            this.mustNameOids = mustNameOids;
+            this.mustNames = new AttributeType[mustNameOids.length];
+        }
+    }
+    
+
+    /* (non-Javadoc)
+     * @see org.apache.directory.shared.ldap.schema.DITContentRule#getMustNames()
+     */
+    public AttributeType[] getMustNames() throws NamingException
+    {
+        if ( mustNameOids == null || mustNameOids.length == 0 )
+        {
+            return EMPTY_ATTR_ARRAY;
+        }
+        
+        for ( int ii = 0; ii < mustNameOids.length; ii++ )
+        {
+            mustNames[ii] = registries.getAttributeTypeRegistry().lookup( mustNameOids[ii] );
+        }
+        
+        return mustNames;
+    }
+
+
+    public void setNotNameOids( String[] notNameOids )
+    {
+        if ( notNameOids == null )
+        {
+            this.notNameOids = EMPTY_STR_ARRAY;
+            this.notNames = EMPTY_ATTR_ARRAY;
+        }
+        else
+        {
+            this.notNameOids = notNameOids;
+            this.notNames = new AttributeType[notNameOids.length];
+        }
+    }
+    
+
+    /* (non-Javadoc)
+     * @see org.apache.directory.shared.ldap.schema.DITContentRule#getNotNames()
+     */
+    public AttributeType[] getNotNames() throws NamingException
+    {
+        if ( notNameOids == null || notNameOids.length == 0 )
+        {
+            return EMPTY_ATTR_ARRAY;
+        }
+        
+        for ( int ii = 0; ii < notNameOids.length; ii++ )
+        {
+            notNames[ii] = registries.getAttributeTypeRegistry().lookup( notNameOids[ii] );
+        }
+        
+        return notNames;
+    }
+
+    
+    /* (non-Javadoc)
+     * @see org.apache.directory.shared.ldap.schema.DITContentRule#getObjectClass()
+     */
+    public ObjectClass getObjectClass() throws NamingException
+    {
+        return registries.getObjectClassRegistry().lookup( getOid() );
+    }
+
+
+    public void setDescription( String description )
+    {
+        super.setDescription( description );
+    }
+    
+    
+    public void setObsolete( boolean obsolete )
+    {
+        super.setObsolete( obsolete );
+    }
+    
+    
+    public void setNames( String[] names )
+    {
+        super.setNames( names );
+    }
+    
+    
+    public void setSchema( String schema )
+    {
+        super.setSchema( schema );
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/DitStructureRuleImpl.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/DitStructureRuleImpl.java
new file mode 100644
index 0000000..c61f375
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/DitStructureRuleImpl.java
@@ -0,0 +1,130 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.schema;
+
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.schema.AbstractSchemaObject;
+import org.apache.directory.shared.ldap.schema.DITStructureRule;
+import org.apache.directory.shared.ldap.schema.MutableSchemaObject;
+import org.apache.directory.shared.ldap.schema.NameForm;
+
+
+/**
+ * A ditStructureRule bean implementation which dynamically looks up dependencies using 
+ * a resgistries object.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class DitStructureRuleImpl extends AbstractSchemaObject implements DITStructureRule, MutableSchemaObject
+{
+    private static final long serialVersionUID = 1L;
+    private final Integer[] EMPTY_INT_ARRAY = new Integer[0];
+    private final DITStructureRule[] EMPTY_DSR_ARRAY = new DITStructureRule[0];
+
+    private final Registries registries;
+    private String nameFormOid;
+    private Integer[] superClassRuleIds;
+    private DITStructureRule[] superClasses;
+    
+    
+    public DitStructureRuleImpl( String nameFormOid, Integer ruleId, Registries registries )
+    {
+        super( nameFormOid + "." + ruleId.toString() );
+        this.nameFormOid = nameFormOid;
+        this.registries = registries;
+    }
+
+
+    /* (non-Javadoc)
+     * @see org.apache.directory.shared.ldap.schema.DITStructureRule#getNameForm()
+     */
+    public NameForm getNameForm() throws NamingException
+    {
+        return registries.getNameFormRegistry().lookup( nameFormOid );
+    }
+
+    
+    /* (non-Javadoc)
+     * @see org.apache.directory.shared.ldap.schema.DITStructureRule#getSuperClasses()
+     */
+    public DITStructureRule[] getSuperClasses() throws NamingException
+    {
+        if ( this.superClassRuleIds == null )
+        {
+            return EMPTY_DSR_ARRAY;
+        }
+        
+        for ( int ii = 0; ii < superClassRuleIds.length; ii++ )
+        {
+            superClasses[ii] = registries.getDitStructureRuleRegistry().lookup( superClassRuleIds[ii] );
+        }
+        
+        return superClasses;
+    }
+    
+    
+    public void setSuperClassRuleIds( Integer[] superClassRuleIds )
+    {
+        if ( superClassRuleIds == null )
+        {
+            this.superClassRuleIds = EMPTY_INT_ARRAY;
+            this.superClasses = EMPTY_DSR_ARRAY;
+        }
+        else
+        {
+            this.superClassRuleIds = superClassRuleIds;
+            this.superClasses = new DITStructureRule[superClassRuleIds.length];
+        }
+    }
+    
+    
+    public void setObsolete( boolean obsolete )
+    {
+        super.setObsolete( obsolete );
+    }
+    
+    
+    public void setNames( String[] names )
+    {
+        super.setNames( names );
+    }
+    
+    
+    public void setSchema( String schema )
+    {
+        super.setSchema( schema );
+    }
+    
+    
+    public void setDescription( String description )
+    {
+        super.setDescription( description );
+    }
+
+
+    public Integer getRuleId()
+    {
+        return null;
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/MatchingRuleImpl.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/MatchingRuleImpl.java
new file mode 100644
index 0000000..8de2b62
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/MatchingRuleImpl.java
@@ -0,0 +1,100 @@
+/*
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *   or more contributor license agreements.  See the NOTICE file
+ *   distributed with this work for additional information
+ *   regarding copyright ownership.  The ASF licenses this file
+ *   to you 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.
+ *
+ */
+package org.apache.directory.server.core.schema;
+
+
+import java.util.Comparator;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.schema.registries.ComparatorRegistry;
+import org.apache.directory.server.schema.registries.NormalizerRegistry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.server.schema.registries.SyntaxRegistry;
+import org.apache.directory.shared.ldap.schema.AbstractMatchingRule;
+import org.apache.directory.shared.ldap.schema.MutableSchemaObject;
+import org.apache.directory.shared.ldap.schema.Normalizer;
+import org.apache.directory.shared.ldap.schema.Syntax;
+
+
+class MatchingRuleImpl extends AbstractMatchingRule implements MutableSchemaObject
+{
+    private static final long serialVersionUID = 1L;
+    private final SyntaxRegistry syntaxRegistry;
+    private final ComparatorRegistry comparatorRegistry;
+    private final NormalizerRegistry normalizerRegistry;
+    private final String syntaxOid;
+    
+    /**
+     * Creates a MatchingRule using the minimal set of required information.
+     *
+     * @param oid the object identifier for this matching rule
+     */
+    protected MatchingRuleImpl( String oid, String syntaxOid, Registries registries )
+    {
+        super( oid );
+        this.syntaxOid = syntaxOid;
+        syntaxRegistry = registries.getSyntaxRegistry();
+        normalizerRegistry = registries.getNormalizerRegistry();
+        comparatorRegistry = registries.getComparatorRegistry();
+    }
+
+
+    public Syntax getSyntax() throws NamingException
+    {
+        return syntaxRegistry.lookup( syntaxOid );
+    }
+
+
+    public Comparator getComparator() throws NamingException
+    {
+        return comparatorRegistry.lookup( oid );
+    }
+
+
+    public Normalizer getNormalizer() throws NamingException
+    {
+        return normalizerRegistry.lookup( oid );
+    }
+    
+    
+    public void setSchema( String schema )
+    {
+        super.setSchema( schema );
+    }
+    
+
+    public void setDescription( String description )
+    {
+        super.setDescription( description );
+    }
+    
+    
+    public void setNames( String[] names )
+    {
+        super.setNames( names );
+    }
+    
+    
+    public void setObsolete( boolean obsolete )
+    {
+        super.setObsolete( obsolete );
+    }
+}
\ No newline at end of file
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/MatchingRuleUseImpl.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/MatchingRuleUseImpl.java
new file mode 100644
index 0000000..da2eb62
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/MatchingRuleUseImpl.java
@@ -0,0 +1,148 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.schema;
+
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.schema.AbstractSchemaObject;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.schema.MatchingRule;
+import org.apache.directory.shared.ldap.schema.MatchingRuleUse;
+import org.apache.directory.shared.ldap.schema.MutableSchemaObject;
+
+
+/**
+ * A machingRuleUse implementation which dynamically pull applicable attributeTypes 
+ * and it's matchingRule from the registries associated with it.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class MatchingRuleUseImpl extends AbstractSchemaObject implements MatchingRuleUse, MutableSchemaObject
+{
+    private static final long serialVersionUID = 1L;
+
+    private static final AttributeType[] EMPTY_ATTRIBUTES = new AttributeType[0];
+    private static final String[] EMPTY_STRINGS = new String[0];
+
+    private final Registries registries;
+    private AttributeType[] applicableAttributes = EMPTY_ATTRIBUTES;
+    private String[] applicableAttributesOids;
+    
+
+    /**
+     * Creates a new matchingRuleUse.
+     * 
+     * @param oid the numeric oid of the matchingRule associated with this matchingRuleUse
+     * @param registries the registries used to resolve the matchingRule and the applicable attributes
+     */
+    protected MatchingRuleUseImpl( String oid, Registries registries )
+    {
+        super( oid );
+        this.registries = registries;
+    }
+
+
+    /* (non-Javadoc)
+     * @see org.apache.directory.shared.ldap.schema.MatchingRuleUse#getMatchingRule()
+     */
+    public MatchingRule getMatchingRule() throws NamingException
+    {
+        return registries.getMatchingRuleRegistry().lookup( getOid() );
+    }
+
+
+    /* (non-Javadoc)
+     * @see org.apache.directory.shared.ldap.schema.MatchingRuleUse#getApplicableAttributes()
+     */
+    public AttributeType[] getApplicableAttributes() throws NamingException
+    {
+        if ( applicableAttributesOids == null || applicableAttributesOids == EMPTY_STRINGS )
+        {
+            return EMPTY_ATTRIBUTES;
+        }
+        
+        for ( int ii = 0; ii < applicableAttributesOids.length; ii++ )
+        {
+            applicableAttributes[ii] = registries.getAttributeTypeRegistry().lookup( applicableAttributesOids[ii] );
+        }
+        
+        return applicableAttributes;
+    }
+    
+    
+    /**
+     * Sets the oids used to look up the applicable AttributeTypes.
+     * 
+     * @param applicableAttributesOids the String[] of attributeType oids
+     */
+    public void setApplicableAttributesOids( final String[] applicableAttributesOids )
+    {
+        this.applicableAttributesOids = applicableAttributesOids;
+        if ( applicableAttributesOids == null )
+        {
+            this.applicableAttributesOids = EMPTY_STRINGS;
+            this.applicableAttributes = EMPTY_ATTRIBUTES;
+        }
+        else
+        {
+            this.applicableAttributesOids = applicableAttributesOids;
+            this.applicableAttributes = new AttributeType[applicableAttributesOids.length];
+        }
+    }
+    
+    
+    /**
+     * Sets the names associated with this matchingRuleUse.
+     */
+    public void setNames( String[] names )
+    {
+        super.setNames( names );
+    }
+    
+    
+    /**
+     * Sets the description associated with this matchingRuleUse.
+     */
+    public void setDescription( String description )
+    {
+        super.setDescription( description );
+    }
+    
+    
+    /**
+     * Sets whether or not this matchingRuleUse is obsolete.
+     */
+    public void setObsolete( boolean obsolete )
+    {
+        super.setObsolete( obsolete );
+    }
+    
+    
+    /**
+     * Sets the schema this matchingRuleUse is defined under.
+     */
+    public void setSchema( String schemaName )
+    {
+        super.setSchema( schemaName );
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/MetaAttributeTypeHandler.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/MetaAttributeTypeHandler.java
new file mode 100644
index 0000000..eb023df
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/MetaAttributeTypeHandler.java
@@ -0,0 +1,269 @@
+/*
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *   or more contributor license agreements.  See the NOTICE file
+ *   distributed with this work for additional information
+ *   regarding copyright ownership.  The ASF licenses this file
+ *   to you 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.
+ *
+ */
+package org.apache.directory.server.core.schema;
+
+
+import java.util.Set;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.constants.MetaSchemaConstants;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerSearchResult;
+import org.apache.directory.server.schema.bootstrap.Schema;
+import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.exception.LdapInvalidNameException;
+import org.apache.directory.shared.ldap.exception.LdapOperationNotSupportedException;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.name.Rdn;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+
+
+/**
+ * A handler for operations peformed to add, delete, modify, rename and 
+ * move schema normalizers.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class MetaAttributeTypeHandler extends AbstractSchemaChangeHandler
+{
+    private final SchemaPartitionDao dao;
+    private final AttributeTypeRegistry attributeTypeRegistry;
+
+    
+
+    public MetaAttributeTypeHandler( Registries targetRegistries, PartitionSchemaLoader loader, SchemaPartitionDao dao ) 
+        throws NamingException
+    {
+        super( targetRegistries, loader );
+        
+        this.dao = dao;
+        this.attributeTypeRegistry = targetRegistries.getAttributeTypeRegistry();
+    }
+
+
+    protected void modify( LdapDN name, ServerEntry entry, ServerEntry targetEntry, boolean cascade ) 
+        throws NamingException
+    {
+        String oid = getOid( entry );
+        Schema schema = getSchema( name );
+        AttributeType at = factory.getAttributeType( targetEntry, targetRegistries, schema.getSchemaName() );
+        
+        if ( ! schema.isDisabled() )
+        {
+            attributeTypeRegistry.unregister( oid );
+            attributeTypeRegistry.register( at );
+        }
+    }
+    
+    
+    public void add( AttributeType at ) throws NamingException
+    {
+        Schema schema = dao.getSchema( at.getSchema() );
+        if ( ! schema.isDisabled() )
+        {
+            attributeTypeRegistry.register( at );
+        }
+        else
+        {
+            registerOids( at );
+        }
+    }
+
+
+    public void add( LdapDN name, ServerEntry entry ) throws NamingException
+    {
+        LdapDN parentDn = ( LdapDN ) name.clone();
+        parentDn.remove( parentDn.size() - 1 );
+        checkNewParent( parentDn );
+        checkOidIsUnique( entry );
+        
+        Schema schema = getSchema( name );
+        AttributeType at = factory.getAttributeType( entry, targetRegistries, schema.getSchemaName() );
+        add( at );
+    }
+
+
+    public void delete( LdapDN name, ServerEntry entry, boolean cascade ) throws NamingException
+    {
+        String schemaName = getSchemaName( name );
+        AttributeType at = factory.getAttributeType( entry, targetRegistries, schemaName );
+        Set<ServerSearchResult> dependees = dao.listAttributeTypeDependents( at );
+        
+        if ( dependees != null && dependees.size() > 0 )
+        {
+            throw new LdapOperationNotSupportedException( "The attributeType with OID " + at.getOid() 
+                + " cannot be deleted until all entities" 
+                + " using this attributeType have also been deleted.  The following dependees exist: " 
+                + getOids( dependees ), 
+                ResultCodeEnum.UNWILLING_TO_PERFORM );
+        }
+        
+        delete( at, cascade );
+    }
+
+
+    public void delete( AttributeType at, boolean cascade ) throws NamingException
+    {
+        Schema schema = loader.getSchema( at.getSchema() );
+        if ( ! schema.isDisabled() )
+        {
+            attributeTypeRegistry.unregister( at.getOid() );
+        }
+        unregisterOids( at.getOid() );
+    }
+
+
+    public void rename( LdapDN name, ServerEntry entry, Rdn newRdn, boolean cascade ) throws NamingException
+    {
+        Schema schema = getSchema( name );
+        AttributeType oldAt = factory.getAttributeType( entry, targetRegistries, schema.getSchemaName() );
+        Set<ServerSearchResult> dependees = dao.listAttributeTypeDependents( oldAt );
+        
+        if ( dependees != null && dependees.size() > 0 )
+        {
+            throw new LdapOperationNotSupportedException( "The attributeType with OID " + oldAt.getOid()
+                + " cannot be deleted until all entities" 
+                + " using this attributeType have also been deleted.  The following dependees exist: " 
+                + getOids( dependees ), 
+                ResultCodeEnum.UNWILLING_TO_PERFORM );
+        }
+
+        ServerEntry targetEntry = ( ServerEntry ) entry.clone();
+        String newOid = ( String ) newRdn.getValue();
+        checkOidIsUnique( newOid );
+        targetEntry.put( MetaSchemaConstants.M_OID_AT, newOid );
+        AttributeType at = factory.getAttributeType( targetEntry, targetRegistries, schema.getSchemaName() );
+
+        if ( ! schema.isDisabled() )
+        {
+            attributeTypeRegistry.unregister( oldAt.getOid() );
+            attributeTypeRegistry.register( at );
+        }
+        else
+        {
+            registerOids( at );
+        }
+        
+        unregisterOids( oldAt.getOid() );
+    }
+
+
+    public void move( LdapDN oriChildName, LdapDN newParentName, Rdn newRn, boolean deleteOldRn,
+        ServerEntry entry, boolean cascade ) throws NamingException
+    {
+        checkNewParent( newParentName );
+        Schema oldSchema = getSchema( oriChildName );
+        AttributeType oldAt = factory.getAttributeType( entry, targetRegistries, oldSchema.getSchemaName() );
+        Set<ServerSearchResult> dependees = dao.listAttributeTypeDependents( oldAt );
+        
+        if ( dependees != null && dependees.size() > 0 )
+        {
+            throw new LdapOperationNotSupportedException( "The attributeType with OID " + oldAt.getOid()
+                + " cannot be deleted until all entities" 
+                + " using this attributeType have also been deleted.  The following dependees exist: " 
+                + getOids( dependees ), 
+                ResultCodeEnum.UNWILLING_TO_PERFORM );
+        }
+
+        Schema newSchema = getSchema( newParentName );
+        ServerEntry targetEntry = ( ServerEntry ) entry.clone();
+        String newOid = ( String ) newRn.getValue();
+        targetEntry.put( MetaSchemaConstants.M_OID_AT, newOid );
+        checkOidIsUnique( newOid );
+        AttributeType at = factory.getAttributeType( targetEntry, targetRegistries, newSchema.getSchemaName() );
+
+        if ( ! oldSchema.isDisabled() )
+        {
+            attributeTypeRegistry.unregister( oldAt.getOid() );
+        }
+        unregisterOids( oldAt.getOid() );
+
+        if ( ! newSchema.isDisabled() )
+        {
+            attributeTypeRegistry.register( at );
+        }
+        else
+        {
+            registerOids( at );
+        }
+    }
+
+
+    public void replace( LdapDN oriChildName, LdapDN newParentName, ServerEntry entry, boolean cascade ) 
+        throws NamingException
+    {
+        checkNewParent( newParentName );
+        Schema oldSchema = getSchema( oriChildName );
+        AttributeType oldAt = factory.getAttributeType( entry, targetRegistries, oldSchema.getSchemaName() );
+        Set<ServerSearchResult> dependees = dao.listAttributeTypeDependents( oldAt );
+        
+        if ( dependees != null && dependees.size() > 0 )
+        {
+            throw new LdapOperationNotSupportedException( "The attributeType with OID " + oldAt.getOid() 
+                + " cannot be deleted until all entities" 
+                + " using this attributeType have also been deleted.  The following dependees exist: " 
+                + getOids( dependees ), 
+                ResultCodeEnum.UNWILLING_TO_PERFORM );
+        }
+
+        Schema newSchema = getSchema( newParentName );
+        AttributeType at = factory.getAttributeType( entry, targetRegistries, newSchema.getSchemaName() );
+        
+        if ( ! oldSchema.isDisabled() )
+        {
+            attributeTypeRegistry.unregister( oldAt.getOid() );
+        }
+        
+        if ( ! newSchema.isDisabled() )
+        {
+            attributeTypeRegistry.register( at );
+        }
+    }
+    
+    
+    private void checkNewParent( LdapDN newParent ) throws NamingException
+    {
+        if ( newParent.size() != 3 )
+        {
+            throw new LdapInvalidNameException( 
+                "The parent dn of a attributeType should be at most 3 name components in length.", 
+                ResultCodeEnum.NAMING_VIOLATION );
+        }
+        
+        Rdn rdn = newParent.getRdn();
+        if ( ! targetRegistries.getOidRegistry().getOid( rdn.getNormType() ).equals( SchemaConstants.OU_AT_OID ) )
+        {
+            throw new LdapInvalidNameException( "The parent entry of a attributeType should be an organizationalUnit.", 
+                ResultCodeEnum.NAMING_VIOLATION );
+        }
+        
+        if ( ! ( ( String ) rdn.getValue() ).equalsIgnoreCase( SchemaConstants.ATTRIBUTE_TYPES_AT ) )
+        {
+            throw new LdapInvalidNameException( 
+                "The parent entry of a attributeType should have a relative name of ou=attributeTypes.", 
+                ResultCodeEnum.NAMING_VIOLATION );
+        }
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/MetaComparatorHandler.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/MetaComparatorHandler.java
new file mode 100644
index 0000000..ba39cf5
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/MetaComparatorHandler.java
@@ -0,0 +1,322 @@
+/*
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *   or more contributor license agreements.  See the NOTICE file
+ *   distributed with this work for additional information
+ *   regarding copyright ownership.  The ASF licenses this file
+ *   to you 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.
+ *
+ */
+package org.apache.directory.server.core.schema;
+
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.constants.MetaSchemaConstants;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.schema.bootstrap.Schema;
+import org.apache.directory.server.schema.registries.ComparatorRegistry;
+import org.apache.directory.server.schema.registries.MatchingRuleRegistry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.exception.LdapInvalidNameException;
+import org.apache.directory.shared.ldap.exception.LdapNamingException;
+import org.apache.directory.shared.ldap.exception.LdapOperationNotSupportedException;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.name.Rdn;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.schema.syntax.ComparatorDescription;
+import org.apache.directory.shared.ldap.util.Base64;
+
+
+/**
+ * A handler for operations peformed to add, delete, modify, rename and 
+ * move schema comparators.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class MetaComparatorHandler extends AbstractSchemaChangeHandler
+{
+    private final SchemaEntityFactory factory;
+    private final ComparatorRegistry comparatorRegistry;
+    private final MatchingRuleRegistry matchingRuleRegistry;
+    private final AttributeType byteCodeAT;
+    private final AttributeType descAT;
+    private final AttributeType fqcnAT;
+
+    
+
+    public MetaComparatorHandler( Registries targetRegistries, PartitionSchemaLoader loader ) throws NamingException
+    {
+        super( targetRegistries, loader );
+        this.comparatorRegistry = targetRegistries.getComparatorRegistry();
+        this.matchingRuleRegistry = targetRegistries.getMatchingRuleRegistry();
+        this.factory = new SchemaEntityFactory( targetRegistries );
+        this.byteCodeAT = targetRegistries.getAttributeTypeRegistry().lookup( MetaSchemaConstants.M_BYTECODE_AT );
+        this.descAT = targetRegistries.getAttributeTypeRegistry().lookup( MetaSchemaConstants.M_DESCRIPTION_AT );
+        this.fqcnAT = targetRegistries.getAttributeTypeRegistry().lookup( MetaSchemaConstants.M_FQCN_AT );
+    }
+
+    
+    protected void modify( LdapDN name, ServerEntry entry, ServerEntry targetEntry, boolean cascade ) throws NamingException
+    {
+        String oid = getOid( entry );
+        Comparator comparator = factory.getComparator( targetEntry, targetRegistries );
+        Schema schema = getSchema( name );
+        
+        if ( ! schema.isDisabled() )
+        {
+            comparatorRegistry.unregister( oid );
+            ComparatorDescription description = getComparatorDescription( schema.getSchemaName(), targetEntry );
+            comparatorRegistry.register( description, comparator );
+        }
+    }
+
+    
+    private ComparatorDescription getComparatorDescription( String schemaName, ServerEntry entry ) throws NamingException
+    {
+        ComparatorDescription description = new ComparatorDescription();
+        description.setNumericOid( getOid( entry ) );
+        List<String> values = new ArrayList<String>();
+        values.add( schemaName );
+        description.addExtension( MetaSchemaConstants.X_SCHEMA, values );
+        description.setFqcn( entry.get( fqcnAT ).getString() );
+        
+        EntryAttribute desc = entry.get( descAT );
+        
+        if ( desc != null && desc.size() > 0 )
+        {
+            description.setDescription( desc.getString() );
+        }
+        
+        EntryAttribute bytecode = entry.get( byteCodeAT );
+
+        if ( bytecode != null && bytecode.size() > 0 )
+        {
+            byte[] bytes = bytecode.getBytes();
+            description.setBytecode( new String( Base64.encode( bytes ) ) );
+        }
+
+        return description;
+    }
+    
+
+    public void add( LdapDN name, ServerEntry entry ) throws NamingException
+    {
+        LdapDN parentDn = ( LdapDN ) name.clone();
+        parentDn.remove( parentDn.size() - 1 );
+        checkNewParent( parentDn );
+        checkOidIsUniqueForComparator( entry );
+        
+        Comparator comparator = factory.getComparator( entry, targetRegistries );
+        Schema schema = getSchema( name );
+        
+        if ( ! schema.isDisabled() )
+        {
+            ComparatorDescription comparatorDescription = getComparatorDescription( schema.getSchemaName(), entry );
+            comparatorRegistry.register( comparatorDescription, comparator );
+        }
+    }
+    
+    
+    public void add( ComparatorDescription comparatorDescription ) throws NamingException
+    {
+        Comparator comparator = factory.getComparator( comparatorDescription, targetRegistries );
+        String schemaName = MetaSchemaConstants.SCHEMA_OTHER;
+        
+        if ( comparatorDescription.getExtensions().get( MetaSchemaConstants.X_SCHEMA ) != null )
+        {
+            schemaName = comparatorDescription.getExtensions().get( MetaSchemaConstants.X_SCHEMA ).get( 0 );
+        }
+        
+        Schema schema = loader.getSchema( schemaName );
+        
+        if ( ! schema.isDisabled() )
+        {
+            comparatorRegistry.register( comparatorDescription, comparator );
+        }
+    }
+
+
+    public void delete( LdapDN name, ServerEntry entry, boolean cascade ) throws NamingException
+    {
+        String oid = getOid( entry );
+        delete( oid, cascade );
+    }
+
+
+    public void delete( String oid, boolean cascade ) throws NamingException
+    {
+        if ( matchingRuleRegistry.hasMatchingRule( oid ) )
+        {
+            throw new LdapOperationNotSupportedException( "The comparator with OID " + oid 
+                + " cannot be deleted until all " 
+                + "matchingRules using that comparator have also been deleted.", 
+                ResultCodeEnum.UNWILLING_TO_PERFORM );
+        }
+        
+        if ( comparatorRegistry.hasComparator( oid ) )
+        {
+            comparatorRegistry.unregister( oid );
+        }
+    }
+
+    
+    public void rename( LdapDN name, ServerEntry entry, Rdn newRdn, boolean cascade ) throws NamingException
+    {
+        String oldOid = getOid( entry );
+
+        if ( matchingRuleRegistry.hasMatchingRule( oldOid ) )
+        {
+            throw new LdapOperationNotSupportedException( "The comparator with OID " + oldOid 
+                + " cannot have it's OID changed until all " 
+                + "matchingRules using that comparator have been deleted.", 
+                ResultCodeEnum.UNWILLING_TO_PERFORM );
+        }
+
+        String oid = ( String ) newRdn.getValue();
+        checkOidIsUniqueForComparator( oid );
+        Schema schema = getSchema( name );
+        
+        if ( ! schema.isDisabled() )
+        {
+            Comparator comparator = factory.getComparator( entry, targetRegistries );
+            comparatorRegistry.unregister( oldOid );
+            ComparatorDescription comparatorDescription = getComparatorDescription( schema.getSchemaName(), entry );
+            comparatorDescription.setNumericOid( oid );
+            comparatorRegistry.register( comparatorDescription, comparator );
+        }
+    }
+
+
+    public void move( LdapDN oriChildName, LdapDN newParentName, Rdn newRdn, boolean deleteOldRn,
+        ServerEntry entry, boolean cascade ) throws NamingException
+    {
+        checkNewParent( newParentName );
+        String oldOid = getOid( entry );
+
+        if ( matchingRuleRegistry.hasMatchingRule( oldOid ) )
+        {
+            throw new LdapOperationNotSupportedException( "The comparator with OID " + oldOid 
+                + " cannot have it's OID changed until all " 
+                + "matchingRules using that comparator have been deleted.", 
+                ResultCodeEnum.UNWILLING_TO_PERFORM );
+        }
+
+        String oid = ( String ) newRdn.getValue();
+        checkOidIsUniqueForComparator( oid );
+        
+        Schema oldSchema = getSchema( oriChildName );
+        Schema newSchema = getSchema( newParentName );
+        
+        Comparator comparator = factory.getComparator( entry, targetRegistries );
+
+        if ( ! oldSchema.isDisabled() )
+        {
+            comparatorRegistry.unregister( oldOid );
+        }
+
+        if ( ! newSchema.isDisabled() )
+        {
+            ComparatorDescription comparatorDescription = getComparatorDescription( newSchema.getSchemaName(), entry );
+            comparatorDescription.setNumericOid( oid );
+            comparatorRegistry.register( comparatorDescription, comparator );
+        }
+    }
+
+
+    public void replace( LdapDN oriChildName, LdapDN newParentName, ServerEntry entry, boolean cascade ) 
+        throws NamingException
+    {
+        checkNewParent( newParentName );
+        String oid = getOid( entry );
+
+        if ( matchingRuleRegistry.hasMatchingRule( oid ) )
+        {
+            throw new LdapOperationNotSupportedException( "The comparator with OID " + oid 
+                + " cannot be moved to another schema until all " 
+                + "matchingRules using that comparator have been deleted.", 
+                ResultCodeEnum.UNWILLING_TO_PERFORM );
+        }
+
+        Schema oldSchema = getSchema( oriChildName );
+        Schema newSchema = getSchema( newParentName );
+        
+        Comparator comparator = factory.getComparator( entry, targetRegistries );
+        
+        if ( ! oldSchema.isDisabled() )
+        {
+            comparatorRegistry.unregister( oid );
+        }
+        
+        if ( ! newSchema.isDisabled() )
+        {
+            ComparatorDescription comparatorDescription = getComparatorDescription( newSchema.getSchemaName(), entry );
+            comparatorRegistry.register( comparatorDescription, comparator );
+        }
+    }
+    
+    
+    private void checkOidIsUniqueForComparator( String oid ) throws NamingException
+    {
+        if ( super.targetRegistries.getComparatorRegistry().hasComparator( oid ) )
+        {
+            throw new LdapNamingException( "Oid " + oid + " for new schema comparator is not unique.", 
+                ResultCodeEnum.OTHER );
+        }
+    }
+
+
+    private void checkOidIsUniqueForComparator( ServerEntry entry ) throws NamingException
+    {
+        String oid = getOid( entry );
+        
+        if ( super.targetRegistries.getComparatorRegistry().hasComparator( oid ) )
+        {
+            throw new LdapNamingException( "Oid " + oid + " for new schema comparator is not unique.", 
+                ResultCodeEnum.OTHER );
+        }
+    }
+
+
+    private void checkNewParent( LdapDN newParent ) throws NamingException
+    {
+        if ( newParent.size() != 3 )
+        {
+            throw new LdapInvalidNameException( 
+                "The parent dn of a comparator should be at most 3 name components in length.", 
+                ResultCodeEnum.NAMING_VIOLATION );
+        }
+        
+        Rdn rdn = newParent.getRdn();
+        if ( ! targetRegistries.getOidRegistry().getOid( rdn.getNormType() ).equals( SchemaConstants.OU_AT_OID ) )
+        {
+            throw new LdapInvalidNameException( "The parent entry of a comparator should be an organizationalUnit.", 
+                ResultCodeEnum.NAMING_VIOLATION );
+        }
+        
+        if ( ! ( ( String ) rdn.getValue() ).equalsIgnoreCase( SchemaConstants.COMPARATORS_AT ) )
+        {
+            throw new LdapInvalidNameException( 
+                "The parent entry of a comparator should have a relative name of ou=comparators.", 
+                ResultCodeEnum.NAMING_VIOLATION );
+        }
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/MetaDitContentRuleHandler.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/MetaDitContentRuleHandler.java
new file mode 100644
index 0000000..ff366aa
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/MetaDitContentRuleHandler.java
@@ -0,0 +1,122 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.schema;
+
+
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.name.Rdn;
+import org.apache.directory.shared.ldap.schema.DITContentRule;
+
+import javax.naming.NamingException;
+
+
+/**
+ * A schema entity change handler for DitContentRules.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class MetaDitContentRuleHandler extends AbstractSchemaChangeHandler
+{
+
+    protected MetaDitContentRuleHandler( Registries targetRegistries, PartitionSchemaLoader loader ) throws NamingException
+    {
+        super( targetRegistries, loader );
+        // TODO Auto-generated constructor stub
+    }
+
+
+    /* (non-Javadoc)
+     * @see org.apache.directory.server.core.schema.AbstractSchemaChangeHandler#modify(org.apache.directory.shared.ldap.name.LdapDN, javax.naming.directory.Attributes, javax.naming.directory.Attributes)
+     */
+    @Override
+    protected void modify( LdapDN name, ServerEntry entry, ServerEntry targetEntry, boolean cascade ) 
+        throws NamingException
+    {
+        // TODO Auto-generated method stub
+
+    }
+
+
+    /* (non-Javadoc)
+     * @see org.apache.directory.server.core.schema.SchemaChangeHandler#add(org.apache.directory.shared.ldap.name.LdapDN, javax.naming.directory.Attributes)
+     */
+    public void add( LdapDN name, ServerEntry entry ) throws NamingException
+    {
+        // TODO Auto-generated method stub
+
+    }
+
+
+    /* (non-Javadoc)
+     * @see org.apache.directory.server.core.schema.SchemaChangeHandler#delete(org.apache.directory.shared.ldap.name.LdapDN, javax.naming.directory.Attributes)
+     */
+    public void delete( LdapDN name, ServerEntry entry, boolean cascade ) throws NamingException
+    {
+        // TODO Auto-generated method stub
+
+    }
+
+
+    /* (non-Javadoc)
+     * @see org.apache.directory.server.core.schema.SchemaChangeHandler#move(org.apache.directory.shared.ldap.name.LdapDN, org.apache.directory.shared.ldap.name.LdapDN, java.lang.String, boolean, javax.naming.directory.Attributes)
+     */
+    public void move( LdapDN oriChildName, LdapDN newParentName, Rdn newRn, boolean deleteOldRn,
+        ServerEntry entry, boolean cascade ) throws NamingException
+    {
+        // TODO Auto-generated method stub
+
+    }
+
+
+    /* (non-Javadoc)
+     * @see org.apache.directory.server.core.schema.SchemaChangeHandler#move(org.apache.directory.shared.ldap.name.LdapDN, org.apache.directory.shared.ldap.name.LdapDN, javax.naming.directory.Attributes)
+     */
+    public void replace( LdapDN oriChildName, LdapDN newParentName, ServerEntry entry, 
+        boolean cascade ) throws NamingException
+    {
+        // TODO Auto-generated method stub
+
+    }
+
+
+    /* (non-Javadoc)
+     * @see org.apache.directory.server.core.schema.SchemaChangeHandler#rename(org.apache.directory.shared.ldap.name.LdapDN, javax.naming.directory.Attributes, java.lang.String)
+     */
+    public void rename( LdapDN name, ServerEntry entry, Rdn newRdn, boolean cascade ) throws NamingException
+    {
+        // TODO Auto-generated method stub
+
+    }
+
+
+    public void add( DITContentRule dcr )
+    {
+        // TODO Auto-generated method stub
+    }
+
+
+    public void delete( DITContentRule dcr, boolean cascade )
+    {
+        // TODO Auto-generated method stub
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/MetaDitStructureRuleHandler.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/MetaDitStructureRuleHandler.java
new file mode 100644
index 0000000..2fa6321
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/MetaDitStructureRuleHandler.java
@@ -0,0 +1,133 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.schema;
+
+
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.name.Rdn;
+import org.apache.directory.shared.ldap.schema.DITStructureRule;
+
+import javax.naming.NamingException;
+
+
+/**
+ * A schema entity change handler for DitStructureRules.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class MetaDitStructureRuleHandler extends AbstractSchemaChangeHandler
+{
+
+    protected MetaDitStructureRuleHandler( Registries targetRegistries, PartitionSchemaLoader loader ) throws NamingException
+    {
+        super( targetRegistries, loader );
+        // TODO Auto-generated constructor stub
+    }
+
+
+    /* (non-Javadoc)
+     * @see org.apache.directory.server.core.schema.AbstractSchemaChangeHandler#modify(
+     * org.apache.directory.shared.ldap.name.LdapDN, javax.naming.directory.Attributes, 
+     * javax.naming.directory.Attributes)
+     */
+    @Override
+    protected void modify( LdapDN name, ServerEntry entry, ServerEntry targetEntry, 
+        boolean cascade ) throws NamingException
+    {
+        // TODO Auto-generated method stub
+
+    }
+
+
+    /* (non-Javadoc)
+     * @see org.apache.directory.server.core.schema.SchemaChangeHandler#add(
+     * org.apache.directory.shared.ldap.name.LdapDN, javax.naming.directory.Attributes)
+     */
+    public void add( LdapDN name, ServerEntry entry ) throws NamingException
+    {
+        // TODO Auto-generated method stub
+
+    }
+
+
+    /* (non-Javadoc)
+     * @see org.apache.directory.server.core.schema.SchemaChangeHandler#delete(
+     * org.apache.directory.shared.ldap.name.LdapDN, javax.naming.directory.Attributes)
+     */
+    public void delete( LdapDN name, ServerEntry entry, boolean cascade ) throws NamingException
+    {
+        // TODO Auto-generated method stub
+
+    }
+
+
+    /* (non-Javadoc)
+     * @see org.apache.directory.server.core.schema.SchemaChangeHandler#move(
+     * org.apache.directory.shared.ldap.name.LdapDN, 
+     * org.apache.directory.shared.ldap.name.LdapDN, 
+     * java.lang.String, boolean, javax.naming.directory.Attributes)
+     */
+    public void move( LdapDN oriChildName, LdapDN newParentName, Rdn newRn, boolean deleteOldRn,
+        ServerEntry entry, boolean cascade ) throws NamingException
+    {
+        // TODO Auto-generated method stub
+
+    }
+
+
+    /* (non-Javadoc)
+     * @see org.apache.directory.server.core.schema.SchemaChangeHandler#move(
+     * org.apache.directory.shared.ldap.name.LdapDN, 
+     * org.apache.directory.shared.ldap.name.LdapDN, 
+     * javax.naming.directory.Attributes)
+     */
+    public void replace( LdapDN oriChildName, LdapDN newParentName, ServerEntry entry, 
+        boolean cascade ) throws NamingException
+    {
+        // TODO Auto-generated method stub
+
+    }
+
+
+    /* (non-Javadoc)
+     * @see org.apache.directory.server.core.schema.SchemaChangeHandler#rename(
+     * org.apache.directory.shared.ldap.name.LdapDN, javax.naming.directory.Attributes, java.lang.String)
+     */
+    public void rename( LdapDN name, ServerEntry entry, Rdn newRdn, boolean cascade ) throws NamingException
+    {
+        // TODO Auto-generated method stub
+
+    }
+
+
+    public void add( DITStructureRule dsr )
+    {
+        // TODO Auto-generated method stub
+    }
+
+
+    public void delete( DITStructureRule dsr, boolean cascade )
+    {
+        // TODO Auto-generated method stub
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/MetaMatchingRuleHandler.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/MetaMatchingRuleHandler.java
new file mode 100644
index 0000000..4b72366
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/MetaMatchingRuleHandler.java
@@ -0,0 +1,271 @@
+/*
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *   or more contributor license agreements.  See the NOTICE file
+ *   distributed with this work for additional information
+ *   regarding copyright ownership.  The ASF licenses this file
+ *   to you 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.
+ *
+ */
+package org.apache.directory.server.core.schema;
+
+
+import java.util.Set;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.constants.MetaSchemaConstants;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerSearchResult;
+import org.apache.directory.server.schema.bootstrap.Schema;
+import org.apache.directory.server.schema.registries.MatchingRuleRegistry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.exception.LdapInvalidNameException;
+import org.apache.directory.shared.ldap.exception.LdapOperationNotSupportedException;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.name.Rdn;
+import org.apache.directory.shared.ldap.schema.MatchingRule;
+
+
+/**
+ * A handler for operations peformed to add, delete, modify, rename and 
+ * move schema normalizers.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class MetaMatchingRuleHandler extends AbstractSchemaChangeHandler
+{
+    private final SchemaPartitionDao dao;
+    private final MatchingRuleRegistry matchingRuleRegistry;
+
+    
+    public MetaMatchingRuleHandler( Registries targetRegistries, PartitionSchemaLoader loader, SchemaPartitionDao dao ) 
+        throws NamingException
+    {
+        super( targetRegistries, loader );
+        
+        this.dao = dao;
+        this.matchingRuleRegistry = targetRegistries.getMatchingRuleRegistry();
+    }
+
+
+    protected void modify( LdapDN name, ServerEntry entry, ServerEntry targetEntry, 
+        boolean cascade ) throws NamingException
+    {
+        String oid = getOid( entry );
+        Schema schema = getSchema( name );
+        MatchingRule mr = factory.getMatchingRule( targetEntry, targetRegistries, schema.getSchemaName() );
+        
+        if ( ! schema.isDisabled() )
+        {
+            matchingRuleRegistry.unregister( oid );
+            matchingRuleRegistry.register( mr );
+        }
+    }
+
+
+    public void add( LdapDN name, ServerEntry entry ) throws NamingException
+    {
+        LdapDN parentDn = ( LdapDN ) name.clone();
+        parentDn.remove( parentDn.size() - 1 );
+        checkNewParent( parentDn );
+        checkOidIsUnique( entry );
+        
+        String schemaName = getSchemaName( name );
+        MatchingRule mr = factory.getMatchingRule( entry, targetRegistries, schemaName );
+        add( mr );
+    }
+
+
+    public void delete( LdapDN name, ServerEntry entry, boolean cascade ) throws NamingException
+    {
+        String schemaName = getSchemaName( name );
+        MatchingRule mr = factory.getMatchingRule( entry, targetRegistries, schemaName );
+        Set<ServerSearchResult> dependees = dao.listMatchingRuleDependents( mr );
+        
+        if ( dependees != null && dependees.size() > 0 )
+        {
+            throw new LdapOperationNotSupportedException( "The matchingRule with OID " + mr.getOid() 
+                + " cannot be deleted until all entities" 
+                + " using this matchingRule have also been deleted.  The following dependees exist: " 
+                + getOids( dependees ), 
+                ResultCodeEnum.UNWILLING_TO_PERFORM );
+        }
+        
+        delete( mr, cascade );
+    }
+
+
+    public void delete( MatchingRule mr, boolean cascade ) throws NamingException
+    {
+        Schema schema = loader.getSchema( mr.getSchema() );
+        if ( ! schema.isDisabled() )
+        {
+            matchingRuleRegistry.unregister( mr.getOid() );
+        }
+        unregisterOids( mr.getOid() );
+    }
+
+    
+    public void rename( LdapDN name, ServerEntry entry, Rdn newRdn, boolean cascade ) throws NamingException
+    {
+        Schema schema = getSchema( name );
+        MatchingRule oldMr = factory.getMatchingRule( entry, targetRegistries, schema.getSchemaName() );
+        Set<ServerSearchResult> dependees = dao.listMatchingRuleDependents( oldMr );
+        
+        if ( dependees != null && dependees.size() > 0 )
+        {
+            throw new LdapOperationNotSupportedException( "The matchingRule with OID " + oldMr.getOid()
+                + " cannot be deleted until all entities" 
+                + " using this matchingRule have also been deleted.  The following dependees exist: " 
+                + getOids( dependees ), 
+                ResultCodeEnum.UNWILLING_TO_PERFORM );
+        }
+
+        ServerEntry targetEntry = ( ServerEntry ) entry.clone();
+        String newOid = ( String ) newRdn.getValue();
+        checkOidIsUnique( newOid );
+        
+        targetEntry.put( MetaSchemaConstants.M_OID_AT, newOid );
+        MatchingRule mr = factory.getMatchingRule( targetEntry, targetRegistries, schema.getSchemaName() );
+
+        if ( ! schema.isDisabled() )
+        {
+            matchingRuleRegistry.unregister( oldMr.getOid() );
+            matchingRuleRegistry.register( mr );
+        }
+        else
+        {
+            registerOids( mr );
+        }
+
+        unregisterOids( oldMr.getOid() );
+    }
+
+
+    public void move( LdapDN oriChildName, LdapDN newParentName, Rdn newRdn, boolean deleteOldRn, 
+        ServerEntry entry, boolean cascade ) throws NamingException
+    {
+        checkNewParent( newParentName );
+        Schema oldSchema = getSchema( oriChildName );
+        MatchingRule oldMr = factory.getMatchingRule( entry, targetRegistries, oldSchema.getSchemaName() );
+        Set<ServerSearchResult> dependees = dao.listMatchingRuleDependents( oldMr );
+        
+        if ( dependees != null && dependees.size() > 0 )
+        {
+            throw new LdapOperationNotSupportedException( "The matchingRule with OID " + oldMr.getOid()
+                + " cannot be deleted until all entities" 
+                + " using this matchingRule have also been deleted.  The following dependees exist: " 
+                + getOids( dependees ), 
+                ResultCodeEnum.UNWILLING_TO_PERFORM );
+        }
+
+        Schema newSchema = getSchema( newParentName );
+        ServerEntry targetEntry = ( ServerEntry ) entry.clone();
+        String newOid = ( String ) newRdn.getValue();
+        checkOidIsUnique( newOid );
+        
+        targetEntry.put( MetaSchemaConstants.M_OID_AT, newOid );
+        MatchingRule mr = factory.getMatchingRule( targetEntry, targetRegistries, newSchema.getSchemaName() );
+
+        if ( ! oldSchema.isDisabled() )
+        {
+            matchingRuleRegistry.unregister( oldMr.getOid() );
+        }
+        unregisterOids( oldMr.getOid() );
+
+        if ( ! newSchema.isDisabled() )
+        {
+            matchingRuleRegistry.register( mr );
+        }
+        else
+        {
+            registerOids( mr );
+        }
+    }
+
+
+    public void replace( LdapDN oriChildName, LdapDN newParentName, ServerEntry entry, boolean cascade ) 
+        throws NamingException
+    {
+        checkNewParent( newParentName );
+        Schema oldSchema = getSchema( oriChildName );
+        MatchingRule oldMr = factory.getMatchingRule( entry, targetRegistries, oldSchema.getSchemaName() );
+        Set<ServerSearchResult> dependees = dao.listMatchingRuleDependents( oldMr );
+        
+        if ( dependees != null && dependees.size() > 0 )
+        {
+            throw new LdapOperationNotSupportedException( "The matchingRule with OID " + oldMr.getOid() 
+                + " cannot be deleted until all entities" 
+                + " using this matchingRule have also been deleted.  The following dependees exist: " 
+                + getOids( dependees ), 
+                ResultCodeEnum.UNWILLING_TO_PERFORM );
+        }
+
+        Schema newSchema = getSchema( newParentName );
+        MatchingRule mr = factory.getMatchingRule( entry, targetRegistries, newSchema.getSchemaName() );
+        
+        if ( ! oldSchema.isDisabled() )
+        {
+            matchingRuleRegistry.unregister( oldMr.getOid() );
+        }
+        
+        if ( ! newSchema.isDisabled() )
+        {
+            matchingRuleRegistry.register( mr );
+        }
+    }
+    
+    
+    private void checkNewParent( LdapDN newParent ) throws NamingException
+    {
+        if ( newParent.size() != 3 )
+        {
+            throw new LdapInvalidNameException( 
+                "The parent dn of a matchingRule should be at most 3 name components in length.", 
+                ResultCodeEnum.NAMING_VIOLATION );
+        }
+        
+        Rdn rdn = newParent.getRdn();
+        if ( ! targetRegistries.getOidRegistry().getOid( rdn.getNormType() ).equals( SchemaConstants.OU_AT_OID ) )
+        {
+            throw new LdapInvalidNameException( "The parent entry of a matchingRule should be an organizationalUnit.", 
+                ResultCodeEnum.NAMING_VIOLATION );
+        }
+        
+        if ( ! ( ( String ) rdn.getValue() ).equalsIgnoreCase( SchemaConstants.MATCHING_RULES_AT ) )
+        {
+            throw new LdapInvalidNameException( 
+                "The parent entry of a syntax should have a relative name of ou=matchingRules.", 
+                ResultCodeEnum.NAMING_VIOLATION );
+        }
+    }
+
+
+    public void add( MatchingRule mr ) throws NamingException
+    {
+        Schema schema = loader.getSchema( mr.getSchema() );
+        
+        if ( ! schema.isDisabled() )
+        {
+            matchingRuleRegistry.register( mr );
+        }
+        else
+        {
+            registerOids( mr );
+        }
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/MetaMatchingRuleUseHandler.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/MetaMatchingRuleUseHandler.java
new file mode 100644
index 0000000..76d408e
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/MetaMatchingRuleUseHandler.java
@@ -0,0 +1,98 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.schema;
+
+
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.name.Rdn;
+import org.apache.directory.shared.ldap.schema.MatchingRuleUse;
+
+import javax.naming.NamingException;
+
+
+/**
+ * A schema entity change handler for DitMatchingRuleUses.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class MetaMatchingRuleUseHandler extends AbstractSchemaChangeHandler
+{
+
+    protected MetaMatchingRuleUseHandler( Registries targetRegistries, PartitionSchemaLoader loader ) throws NamingException
+    {
+        super( targetRegistries, loader );
+        // TODO Auto-generated constructor stub
+    }
+
+
+    @Override
+    protected void modify( LdapDN name, ServerEntry entry, ServerEntry targetEntry, 
+        boolean cascade ) throws NamingException
+    {
+        // TODO Auto-generated method stub
+    }
+
+
+    public void add( LdapDN name, ServerEntry entry ) throws NamingException
+    {
+        // TODO Auto-generated method stub
+    }
+
+
+    public void delete( LdapDN name, ServerEntry entry, boolean cascade ) throws NamingException
+    {
+        // TODO Auto-generated method stub
+    }
+
+
+    public void move( LdapDN oriChildName, LdapDN newParentName, Rdn newRn, boolean deleteOldRn,
+        ServerEntry entry, boolean cascade ) throws NamingException
+    {
+        // TODO Auto-generated method stub
+    }
+
+
+    public void replace( LdapDN oriChildName, LdapDN newParentName,
+        ServerEntry entry, boolean cascade ) throws NamingException
+    {
+        // TODO Auto-generated method stub
+    }
+
+
+    public void rename( LdapDN name, ServerEntry entry, Rdn newRdn, boolean cascade ) throws NamingException
+    {
+        // TODO Auto-generated method stub
+    }
+
+
+    public void add( MatchingRuleUse mru ) throws NamingException
+    {
+        // TODO Auto-generated method stub
+    }
+
+
+    public void delete( MatchingRuleUse mru, boolean cascade ) throws NamingException
+    {
+        // TODO Auto-generated method stub
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/MetaNameFormHandler.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/MetaNameFormHandler.java
new file mode 100644
index 0000000..a409790
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/MetaNameFormHandler.java
@@ -0,0 +1,131 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.schema;
+
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.name.Rdn;
+import org.apache.directory.shared.ldap.schema.NameForm;
+
+
+/**
+ * A schema entity change handler for NameForms.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class MetaNameFormHandler extends AbstractSchemaChangeHandler
+{
+
+    protected MetaNameFormHandler( Registries targetRegistries, PartitionSchemaLoader loader ) throws NamingException
+    {
+        super( targetRegistries, loader );
+        // TODO Auto-generated constructor stub
+    }
+
+
+    /* (non-Javadoc)
+     * @see org.apache.directory.server.core.schema.AbstractSchemaChangeHandler#modify(
+     * org.apache.directory.shared.ldap.name.LdapDN, javax.naming.directory.Attributes, 
+     * javax.naming.directory.Attributes)
+     */
+    @Override
+    protected void modify( LdapDN name, ServerEntry entry, ServerEntry targetEntry, 
+        boolean cascade ) throws NamingException
+    {
+        // TODO Auto-generated method stub
+
+    }
+
+
+    /* (non-Javadoc)
+     * @see org.apache.directory.server.core.schema.SchemaChangeHandler#add(
+     * org.apache.directory.shared.ldap.name.LdapDN, javax.naming.directory.Attributes)
+     */
+    public void add( LdapDN name, ServerEntry entry ) throws NamingException
+    {
+        // TODO Auto-generated method stub
+
+    }
+
+
+    /* (non-Javadoc)
+     * @see org.apache.directory.server.core.schema.SchemaChangeHandler#delete(
+     * org.apache.directory.shared.ldap.name.LdapDN, javax.naming.directory.Attributes)
+     */
+    public void delete( LdapDN name, ServerEntry entry, boolean cascade ) throws NamingException
+    {
+        // TODO Auto-generated method stub
+
+    }
+
+
+    /* (non-Javadoc)
+     * @see org.apache.directory.server.core.schema.SchemaChangeHandler#move(
+     * org.apache.directory.shared.ldap.name.LdapDN, org.apache.directory.shared.ldap.name.LdapDN, 
+     * Rdn, boolean, javax.naming.directory.Attributes)
+     */
+    public void move( LdapDN oriChildName, LdapDN newParentName, Rdn newRn,
+        boolean deleteOldRn, ServerEntry entry, boolean cascade ) throws NamingException
+    {
+        // TODO Auto-generated method stub
+
+    }
+
+
+    /* (non-Javadoc)
+     * @see org.apache.directory.server.core.schema.SchemaChangeHandler#move(
+     * org.apache.directory.shared.ldap.name.LdapDN, 
+     * org.apache.directory.shared.ldap.name.LdapDN, javax.naming.directory.Attributes)
+     */
+    public void replace( LdapDN oriChildName, LdapDN newParentName, ServerEntry entry, 
+        boolean cascade ) throws NamingException
+    {
+        // TODO Auto-generated method stub
+
+    }
+
+
+    /* (non-Javadoc)
+     * @see org.apache.directory.server.core.schema.SchemaChangeHandler#rename(
+     * org.apache.directory.shared.ldap.name.LdapDN, javax.naming.directory.Attributes, Rdn, boolean)
+     */
+    public void rename( LdapDN name, ServerEntry entry, Rdn newRdn, boolean cascade ) throws NamingException
+    {
+        // TODO Auto-generated method stub
+
+    }
+
+
+    public void add( NameForm nf )
+    {
+        // TODO Auto-generated method stub
+    }
+
+
+    public void delete( NameForm nf, boolean cascade )
+    {
+        // TODO Auto-generated method stub
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/MetaNormalizerHandler.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/MetaNormalizerHandler.java
new file mode 100644
index 0000000..e933b78
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/MetaNormalizerHandler.java
@@ -0,0 +1,325 @@
+/*
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *   or more contributor license agreements.  See the NOTICE file
+ *   distributed with this work for additional information
+ *   regarding copyright ownership.  The ASF licenses this file
+ *   to you 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.
+ *
+ */
+package org.apache.directory.server.core.schema;
+
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.constants.MetaSchemaConstants;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.schema.bootstrap.Schema;
+import org.apache.directory.server.schema.registries.MatchingRuleRegistry;
+import org.apache.directory.server.schema.registries.NormalizerRegistry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.exception.LdapInvalidNameException;
+import org.apache.directory.shared.ldap.exception.LdapNamingException;
+import org.apache.directory.shared.ldap.exception.LdapOperationNotSupportedException;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.name.Rdn;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.schema.Normalizer;
+import org.apache.directory.shared.ldap.schema.syntax.NormalizerDescription;
+import org.apache.directory.shared.ldap.util.Base64;
+
+
+/**
+ * A handler for operations peformed to add, delete, modify, rename and 
+ * move schema normalizers.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class MetaNormalizerHandler extends AbstractSchemaChangeHandler
+{
+    private final SchemaEntityFactory factory;
+    private final NormalizerRegistry normalizerRegistry;
+    private final MatchingRuleRegistry matchingRuleRegistry;
+    private final AttributeType byteCodeAT;
+    private final AttributeType descAT;
+    private final AttributeType fqcnAT;
+    
+
+    public MetaNormalizerHandler( Registries targetRegistries, PartitionSchemaLoader loader ) throws NamingException
+    {
+        super( targetRegistries, loader );
+        this.normalizerRegistry = targetRegistries.getNormalizerRegistry();
+        this.matchingRuleRegistry = targetRegistries.getMatchingRuleRegistry();
+        this.factory = new SchemaEntityFactory( targetRegistries );
+        this.byteCodeAT = targetRegistries.getAttributeTypeRegistry().lookup( MetaSchemaConstants.M_BYTECODE_AT );
+        this.descAT = targetRegistries.getAttributeTypeRegistry().lookup( MetaSchemaConstants.M_DESCRIPTION_AT );
+        this.fqcnAT = targetRegistries.getAttributeTypeRegistry().lookup( MetaSchemaConstants.M_FQCN_AT );
+    }
+
+
+    
+    
+    private NormalizerDescription getNormalizerDescription( String schemaName, ServerEntry entry ) throws NamingException
+    {
+        NormalizerDescription description = new NormalizerDescription();
+        description.setNumericOid( getOid( entry ) );
+        List<String> values = new ArrayList<String>();
+        values.add( schemaName );
+        description.addExtension( MetaSchemaConstants.X_SCHEMA, values );
+        description.setFqcn( entry.get( fqcnAT ).getString() );
+        
+        EntryAttribute desc = entry.get( descAT );
+        
+        if ( ( desc != null ) && ( desc.size() > 0 ) )
+        {
+            description.setDescription( desc.getString() );
+        }
+        
+        EntryAttribute bytecode =  entry.get( byteCodeAT );
+        
+        if ( ( bytecode != null ) && ( bytecode.size() > 0 ) )
+        {
+            byte[] bytes = bytecode.getBytes();
+            description.setBytecode( new String( Base64.encode( bytes ) ) );
+        }
+
+        return description;
+    }
+    
+    
+    protected void modify( LdapDN name, ServerEntry entry, ServerEntry targetEntry, boolean cascade ) throws NamingException
+    {
+        String oid = getOid( entry );
+        Normalizer normalizer = factory.getNormalizer( targetEntry, targetRegistries );
+        Schema schema = getSchema( name );
+        
+        if ( ! schema.isDisabled() )
+        {
+            normalizerRegistry.unregister( oid );
+            NormalizerDescription normalizerDescription = getNormalizerDescription( schema.getSchemaName(), 
+                targetEntry );
+            normalizerRegistry.register( normalizerDescription, normalizer );
+        }
+    }
+
+
+    public void add( LdapDN name, ServerEntry entry ) throws NamingException
+    {
+        LdapDN parentDn = ( LdapDN ) name.clone();
+        parentDn.remove( parentDn.size() - 1 );
+        checkNewParent( parentDn );
+        checkOidIsUniqueForNormalizer( entry );
+        
+        Normalizer normalizer = factory.getNormalizer( entry, targetRegistries );
+        Schema schema = getSchema( name );
+        
+        if ( ! schema.isDisabled() )
+        {
+            NormalizerDescription normalizerDescription = getNormalizerDescription( schema.getSchemaName(), entry );
+            normalizerRegistry.register( normalizerDescription, normalizer );
+        }
+    }
+
+    
+    public void add( NormalizerDescription normalizerDescription ) throws NamingException
+    {
+        Normalizer normalizer = factory.getNormalizer( normalizerDescription, targetRegistries );
+        String schemaName = MetaSchemaConstants.SCHEMA_OTHER;
+        
+        if ( normalizerDescription.getExtensions().get( MetaSchemaConstants.X_SCHEMA ) != null )
+        {
+            schemaName = normalizerDescription.getExtensions().get( MetaSchemaConstants.X_SCHEMA ).get( 0 );
+        }
+        
+        Schema schema = loader.getSchema( schemaName );
+        
+        if ( ! schema.isDisabled() )
+        {
+            normalizerRegistry.register( normalizerDescription, normalizer );
+        }
+    }
+
+
+    public void delete( LdapDN name, ServerEntry entry, boolean cascade ) throws NamingException
+    {
+        delete( getOid( entry ), cascade );
+    }
+
+
+    public void delete( String oid, boolean cascade ) throws NamingException
+    {
+        if ( matchingRuleRegistry.hasMatchingRule( oid ) )
+        {
+            throw new LdapOperationNotSupportedException( "The normalizer with OID " + oid 
+                + " cannot be deleted until all " 
+                + "matchingRules using that normalizer have also been deleted.", 
+                ResultCodeEnum.UNWILLING_TO_PERFORM );
+        }
+        
+        if ( normalizerRegistry.hasNormalizer( oid ) )
+        {
+            normalizerRegistry.unregister( oid );
+        }
+    }
+    
+
+    public void rename( LdapDN name, ServerEntry entry, Rdn newRdn, boolean cascade ) throws NamingException
+    {
+        String oldOid = getOid( entry );
+
+        if ( matchingRuleRegistry.hasMatchingRule( oldOid ) )
+        {
+            throw new LdapOperationNotSupportedException( "The normalizer with OID " + oldOid 
+                + " cannot have it's OID changed until all " 
+                + "matchingRules using that normalizer have been deleted.", 
+                ResultCodeEnum.UNWILLING_TO_PERFORM );
+        }
+
+        String oid = ( String ) newRdn.getValue();
+        checkOidIsUniqueForNormalizer( oid );
+        
+        Schema schema = getSchema( name );
+        
+        if ( ! schema.isDisabled() )
+        {
+            Normalizer normalizer = factory.getNormalizer( entry, targetRegistries );
+            normalizerRegistry.unregister( oldOid );
+            
+            NormalizerDescription normalizerDescription = getNormalizerDescription( schema.getSchemaName(), entry );
+            normalizerDescription.setNumericOid( oid );
+            normalizerRegistry.register( normalizerDescription, normalizer );
+        }
+    }
+
+
+    public void move( LdapDN oriChildName, LdapDN newParentName, Rdn newRdn, boolean deleteOldRn,
+        ServerEntry entry, boolean cascade ) throws NamingException
+    {
+        checkNewParent( newParentName );
+        String oldOid = getOid( entry );
+
+        if ( matchingRuleRegistry.hasMatchingRule( oldOid ) )
+        {
+            throw new LdapOperationNotSupportedException( "The normalizer with OID " + oldOid 
+                + " cannot have it's OID changed until all " 
+                + "matchingRules using that normalizer have been deleted.", 
+                ResultCodeEnum.UNWILLING_TO_PERFORM );
+        }
+
+        String oid = ( String ) newRdn.getValue();
+        checkOidIsUniqueForNormalizer( oid );
+        
+        Schema oldSchema = getSchema( oriChildName );
+        Schema newSchema = getSchema( newParentName );
+        
+        Normalizer normalizer = factory.getNormalizer( entry, targetRegistries );
+
+        if ( ! oldSchema.isDisabled() )
+        {
+            normalizerRegistry.unregister( oldOid );
+        }
+
+        if ( ! newSchema.isDisabled() )
+        {
+            NormalizerDescription normalizerDescription = getNormalizerDescription( newSchema.getSchemaName(), entry );
+            normalizerDescription.setNumericOid( oid );
+            normalizerRegistry.register( normalizerDescription, normalizer );
+        }
+    }
+
+
+    public void replace( LdapDN oriChildName, LdapDN newParentName, ServerEntry entry, boolean cascade ) 
+        throws NamingException
+    {
+        checkNewParent( newParentName );
+        String oid = getOid( entry );
+
+        if ( matchingRuleRegistry.hasMatchingRule( oid ) )
+        {
+            throw new LdapOperationNotSupportedException( "The normalizer with OID " + oid 
+                + " cannot be moved to another schema until all " 
+                + "matchingRules using that normalizer have been deleted.", 
+                ResultCodeEnum.UNWILLING_TO_PERFORM );
+        }
+
+        Schema oldSchema = getSchema( oriChildName );
+        Schema newSchema = getSchema( newParentName );
+        
+        Normalizer normalizer = factory.getNormalizer( entry, targetRegistries );
+        
+        if ( ! oldSchema.isDisabled() )
+        {
+            normalizerRegistry.unregister( oid );
+        }
+        
+        if ( ! newSchema.isDisabled() )
+        {
+            NormalizerDescription normalizerDescription = getNormalizerDescription( newSchema.getSchemaName(), entry );
+            normalizerRegistry.register( normalizerDescription, normalizer );
+        }
+    }
+
+    
+    private void checkOidIsUniqueForNormalizer( String oid ) throws NamingException
+    {
+        if ( super.targetRegistries.getNormalizerRegistry().hasNormalizer( oid ) )
+        {
+            throw new LdapNamingException( "Oid " + oid + " for new schema normalizer is not unique.", 
+                ResultCodeEnum.OTHER );
+        }
+    }
+
+
+    private void checkOidIsUniqueForNormalizer( ServerEntry entry ) throws NamingException
+    {
+        String oid = getOid( entry );
+        
+        if ( super.targetRegistries.getNormalizerRegistry().hasNormalizer( oid ) )
+        {
+            throw new LdapNamingException( "Oid " + oid + " for new schema normalizer is not unique.", 
+                ResultCodeEnum.OTHER );
+        }
+    }
+
+
+    private void checkNewParent( LdapDN newParent ) throws NamingException
+    {
+        if ( newParent.size() != 3 )
+        {
+            throw new LdapInvalidNameException( 
+                "The parent dn of a normalizer should be at most 3 name components in length.", 
+                ResultCodeEnum.NAMING_VIOLATION );
+        }
+        
+        Rdn rdn = newParent.getRdn();
+        if ( ! targetRegistries.getOidRegistry().getOid( rdn.getNormType() ).equals( SchemaConstants.OU_AT_OID ) )
+        {
+            throw new LdapInvalidNameException( "The parent entry of a normalizer should be an organizationalUnit.", 
+                ResultCodeEnum.NAMING_VIOLATION );
+        }
+        
+        if ( ! ( ( String ) rdn.getValue() ).equalsIgnoreCase( SchemaConstants.NORMALIZERS_AT ) )
+        {
+            throw new LdapInvalidNameException( 
+                "The parent entry of a normalizer should have a relative name of ou=normalizers.", 
+                ResultCodeEnum.NAMING_VIOLATION );
+        }
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/MetaObjectClassHandler.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/MetaObjectClassHandler.java
new file mode 100644
index 0000000..d5dc986
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/MetaObjectClassHandler.java
@@ -0,0 +1,270 @@
+/*
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *   or more contributor license agreements.  See the NOTICE file
+ *   distributed with this work for additional information
+ *   regarding copyright ownership.  The ASF licenses this file
+ *   to you 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.
+ *
+ */
+package org.apache.directory.server.core.schema;
+
+
+import java.util.Set;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.constants.MetaSchemaConstants;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerSearchResult;
+import org.apache.directory.server.schema.bootstrap.Schema;
+import org.apache.directory.server.schema.registries.ObjectClassRegistry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.exception.LdapInvalidNameException;
+import org.apache.directory.shared.ldap.exception.LdapOperationNotSupportedException;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.name.Rdn;
+import org.apache.directory.shared.ldap.schema.ObjectClass;
+
+
+/**
+ * A handler for operations peformed to add, delete, modify, rename and 
+ * move schema normalizers.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class MetaObjectClassHandler extends AbstractSchemaChangeHandler
+{
+    private final SchemaPartitionDao dao;
+    private final ObjectClassRegistry objectClassRegistry;
+
+
+    public MetaObjectClassHandler( Registries targetRegistries, PartitionSchemaLoader loader, SchemaPartitionDao dao ) 
+        throws NamingException
+    {
+        super( targetRegistries, loader );
+        
+        this.dao = dao;
+        this.objectClassRegistry = targetRegistries.getObjectClassRegistry();
+    }
+
+
+    protected void modify( LdapDN name, ServerEntry entry, ServerEntry targetEntry, 
+        boolean cascade ) throws NamingException
+    {
+        String oid = getOid( entry );
+        Schema schema = getSchema( name );
+        ObjectClass oc = factory.getObjectClass( targetEntry, targetRegistries, schema.getSchemaName() );
+
+        if ( ! schema.isDisabled() )
+        {
+            objectClassRegistry.unregister( oid );
+            objectClassRegistry.register( oc );
+        }
+    }
+
+
+    public void add( LdapDN name, ServerEntry entry ) throws NamingException
+    {
+        LdapDN parentDn = ( LdapDN ) name.clone();
+        parentDn.remove( parentDn.size() - 1 );
+        checkNewParent( parentDn );
+        checkOidIsUnique( entry );
+        
+        String schemaName = getSchemaName( name );
+        ObjectClass oc = factory.getObjectClass( entry, targetRegistries, schemaName );
+        add( oc );
+    }
+
+
+    public void delete( LdapDN name, ServerEntry entry, boolean cascade ) throws NamingException
+    {
+        String schemaName = getSchemaName( name );
+        ObjectClass oc = factory.getObjectClass( entry, targetRegistries, schemaName );
+        Set<ServerSearchResult> dependees = dao.listObjectClassDependents( oc );
+        
+        if ( dependees != null && dependees.size() > 0 )
+        {
+            throw new LdapOperationNotSupportedException( "The objectClass with OID " + oc.getOid() 
+                + " cannot be deleted until all entities" 
+                + " using this objectClass have also been deleted.  The following dependees exist: " 
+                + getOids( dependees ), 
+                ResultCodeEnum.UNWILLING_TO_PERFORM );
+        }
+
+        delete( oc, cascade );
+    }
+
+
+    public void delete( ObjectClass oc, boolean cascade ) throws NamingException
+    {
+        Schema schema = loader.getSchema( oc.getSchema() );
+        
+        if ( ! schema.isDisabled() )
+        {
+            objectClassRegistry.unregister( oc.getOid() );
+        }
+        
+        unregisterOids( oc.getOid() );
+    }
+
+
+    public void rename( LdapDN name, ServerEntry entry, Rdn newRdn, boolean cascade ) throws NamingException
+    {
+        Schema schema = getSchema( name );
+        ObjectClass oldOc = factory.getObjectClass( entry, targetRegistries, schema.getSchemaName() );
+        Set<ServerSearchResult> dependees = dao.listObjectClassDependents( oldOc );
+        
+        if ( dependees != null && dependees.size() > 0 )
+        {
+            throw new LdapOperationNotSupportedException( "The objectClass with OID " + oldOc.getOid()
+                + " cannot be deleted until all entities" 
+                + " using this objectClass have also been deleted.  The following dependees exist: " 
+                + getOids( dependees ), 
+                ResultCodeEnum.UNWILLING_TO_PERFORM );
+        }
+
+        ServerEntry targetEntry = ( ServerEntry ) entry.clone();
+        String newOid = ( String ) newRdn.getValue();
+        targetEntry.put( MetaSchemaConstants.M_OID_AT, newOid );
+        checkOidIsUnique( newOid );
+        ObjectClass oc = factory.getObjectClass( targetEntry, targetRegistries, schema.getSchemaName() );
+
+        if ( ! schema.isDisabled() )
+        {
+            objectClassRegistry.unregister( oldOc.getOid() );
+            objectClassRegistry.register( oc );
+        }
+        else
+        {
+            registerOids( oc );
+        }
+        
+        unregisterOids( oldOc.getOid() );
+    }
+
+
+    public void move( LdapDN oriChildName, LdapDN newParentName, Rdn newRdn, boolean deleteOldRn,
+        ServerEntry entry, boolean cascade ) throws NamingException
+    {
+        checkNewParent( newParentName );
+        Schema oldSchema = getSchema( oriChildName );
+        ObjectClass oldOc = factory.getObjectClass( entry, targetRegistries, oldSchema.getSchemaName() );
+        Set<ServerSearchResult> dependees = dao.listObjectClassDependents( oldOc );
+        
+        if ( dependees != null && dependees.size() > 0 )
+        {
+            throw new LdapOperationNotSupportedException( "The objectClass with OID " + oldOc.getOid()
+                + " cannot be deleted until all entities" 
+                + " using this objectClass have also been deleted.  The following dependees exist: " 
+                + getOids( dependees ), 
+                ResultCodeEnum.UNWILLING_TO_PERFORM );
+        }
+
+        Schema newSchema = getSchema( newParentName );
+        ServerEntry targetEntry = ( ServerEntry ) entry.clone();
+        String newOid = ( String ) newRdn.getValue();
+        checkOidIsUnique( newOid );
+        targetEntry.put( MetaSchemaConstants.M_OID_AT, newOid );
+        ObjectClass oc = factory.getObjectClass( targetEntry, targetRegistries, newSchema.getSchemaName() );
+
+        if ( ! oldSchema.isDisabled() )
+        {
+            objectClassRegistry.unregister( oldOc.getOid() );
+        }
+        unregisterOids( oldOc.getOid() );
+        
+        if ( ! newSchema.isDisabled() )
+        {
+            objectClassRegistry.register( oc );
+        }
+        else
+        {
+            registerOids( oc );
+        }
+    }
+
+
+    public void replace( LdapDN oriChildName, LdapDN newParentName, ServerEntry entry, boolean cascade ) 
+        throws NamingException
+    {
+        checkNewParent( newParentName );
+        Schema oldSchema = getSchema( oriChildName );
+        ObjectClass oldAt = factory.getObjectClass( entry, targetRegistries, oldSchema.getSchemaName() );
+        Set<ServerSearchResult> dependees = dao.listObjectClassDependents( oldAt );
+        
+        if ( dependees != null && dependees.size() > 0 )
+        {
+            throw new LdapOperationNotSupportedException( "The objectClass with OID " + oldAt.getOid() 
+                + " cannot be deleted until all entities" 
+                + " using this objectClass have also been deleted.  The following dependees exist: " 
+                + getOids( dependees ), 
+                ResultCodeEnum.UNWILLING_TO_PERFORM );
+        }
+
+        Schema newSchema = getSchema( newParentName );
+        ObjectClass oc = factory.getObjectClass( entry, targetRegistries, newSchema.getSchemaName() );
+        
+        if ( ! oldSchema.isDisabled() )
+        {
+            objectClassRegistry.unregister( oldAt.getOid() );
+        }
+        
+        if ( ! newSchema.isDisabled() )
+        {
+            objectClassRegistry.register( oc );
+        }
+    }
+
+
+    private void checkNewParent( LdapDN newParent ) throws NamingException
+    {
+        if ( newParent.size() != 3 )
+        {
+            throw new LdapInvalidNameException( 
+                "The parent dn of a objectClass should be at most 3 name components in length.", 
+                ResultCodeEnum.NAMING_VIOLATION );
+        }
+        
+        Rdn rdn = newParent.getRdn();
+        if ( ! targetRegistries.getOidRegistry().getOid( rdn.getNormType() ).equals( SchemaConstants.OU_AT_OID ) )
+        {
+            throw new LdapInvalidNameException( "The parent entry of a objectClass should be an organizationalUnit.", 
+                ResultCodeEnum.NAMING_VIOLATION );
+        }
+        
+        if ( ! ( ( String ) rdn.getValue() ).equalsIgnoreCase( SchemaConstants.OBJECT_CLASSES_AT ) )
+        {
+            throw new LdapInvalidNameException( 
+                "The parent entry of a attributeType should have a relative name of ou=objectClasses.", 
+                ResultCodeEnum.NAMING_VIOLATION );
+        }
+    }
+
+
+    public void add( ObjectClass oc ) throws NamingException
+    {
+        Schema schema = loader.getSchema( oc.getSchema() );
+        if ( ! schema.isDisabled() )
+        {
+            objectClassRegistry.register( oc );
+        }
+        else
+        {
+            registerOids( oc );
+        }
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/MetaSchemaHandler.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/MetaSchemaHandler.java
new file mode 100644
index 0000000..cec0fcf
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/MetaSchemaHandler.java
@@ -0,0 +1,554 @@
+/*
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *   or more contributor license agreements.  See the NOTICE file
+ *   distributed with this work for additional information
+ *   regarding copyright ownership.  The ASF licenses this file
+ *   to you 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.
+ *
+ */
+package org.apache.directory.server.core.schema;
+
+
+import org.apache.directory.server.constants.MetaSchemaConstants;
+import org.apache.directory.server.core.entry.ServerAttribute;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerEntryUtils;
+import org.apache.directory.server.schema.bootstrap.Schema;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.server.schema.registries.SchemaObjectRegistry;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.entry.Modification;
+import org.apache.directory.shared.ldap.entry.ModificationOperation;
+import org.apache.directory.shared.ldap.entry.Value;
+import org.apache.directory.shared.ldap.exception.LdapInvalidNameException;
+import org.apache.directory.shared.ldap.exception.LdapOperationNotSupportedException;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.name.Rdn;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.schema.SchemaObject;
+
+import javax.naming.NamingException;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+
+/**
+ * Handles events where entries of objectClass metaSchema are modified.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class MetaSchemaHandler implements SchemaChangeHandler
+{
+    private final SchemaEntityFactory factory;
+    private final PartitionSchemaLoader loader;
+    private final Registries globalRegistries;
+    private final AttributeType disabledAT;
+    private final String OU_OID;
+    private final AttributeType cnAT;
+    private final AttributeType dependenciesAT;
+
+
+    public MetaSchemaHandler( Registries globalRegistries, PartitionSchemaLoader loader ) throws NamingException
+    {
+        this.globalRegistries = globalRegistries;
+        this.disabledAT = globalRegistries.getAttributeTypeRegistry().lookup( MetaSchemaConstants.M_DISABLED_AT );
+        this.loader = loader;
+        this.OU_OID = globalRegistries.getOidRegistry().getOid( SchemaConstants.OU_AT );
+        this.factory = new SchemaEntityFactory( globalRegistries );
+        this.cnAT = globalRegistries.getAttributeTypeRegistry().lookup( SchemaConstants.CN_AT );
+        this.dependenciesAT = globalRegistries.getAttributeTypeRegistry()
+            .lookup( MetaSchemaConstants.M_DEPENDENCIES_AT );
+    }
+
+
+    /**
+     * Reacts to modification of a metaSchema object.  At this point the 
+     * only considerable changes are to the m-disabled and the 
+     * m-dependencies attributes.
+     * 
+     * @param name the dn of the metaSchema object modified
+     * @param modOp the type of modification operation being performed
+     * @param mods the attribute modifications as an Attributes object
+     * @param entry the entry after the modifications have been applied
+     */
+    public void modify( LdapDN name, ModificationOperation modOp, ServerEntry mods, ServerEntry entry, 
+        ServerEntry targetEntry, boolean cascade ) throws NamingException
+    {
+        EntryAttribute disabledInMods = mods.get( disabledAT );
+        
+        if ( disabledInMods != null )
+        {
+            disable( name, modOp, disabledInMods, entry.get( disabledAT ) );
+        }
+        
+        // check if the new schema is enabled or disabled
+        boolean isEnabled = false;
+        EntryAttribute disabled = targetEntry.get( this.disabledAT );
+        
+        if ( disabled == null )
+        {
+            isEnabled = true;
+        }
+        else if ( ! disabled.getString().equals( "TRUE" ) )
+        {
+            isEnabled = true;
+        }
+
+        EntryAttribute dependencies = mods.get( dependenciesAT );
+        
+        if ( dependencies != null )
+        {
+            checkForDependencies( isEnabled, targetEntry );
+        }
+    }
+
+
+    /**
+     * Reacts to modification of a metaSchema object.  At this point the 
+     * only considerable changes are to the m-disabled and the 
+     * m-dependencies attributes.
+     * 
+     * @param name the dn of the metaSchema object modified
+     * @param mods the attribute modifications as an ModificationItem arry
+     * @param entry the entry after the modifications have been applied
+     */
+    public void modify( LdapDN name, List<Modification> mods, ServerEntry entry,
+        ServerEntry targetEntry, boolean cascade ) throws NamingException
+    {
+        EntryAttribute disabledInEntry = entry.get( disabledAT );
+        Modification disabledModification = ServerEntryUtils.getModificationItem( mods, disabledAT );
+        
+        if ( disabledModification != null )
+        {
+            disable( name, 
+                     disabledModification.getOperation(), 
+                     (ServerAttribute)disabledModification.getAttribute(), 
+                     disabledInEntry );
+        }
+
+        // check if the new schema is enabled or disabled
+        boolean isEnabled = false;
+        EntryAttribute disabled = targetEntry.get( disabledAT );
+        
+        if ( disabled == null )
+        {
+            isEnabled = true;
+        }
+        else if ( ! disabled.contains( "TRUE" ) )
+        {
+            isEnabled = true;
+        }
+
+        ServerAttribute dependencies = ServerEntryUtils.getAttribute( mods, dependenciesAT );
+        
+        if ( dependencies != null )
+        {
+            checkForDependencies( isEnabled, targetEntry );
+        }
+    }
+
+
+    public void move( LdapDN oriChildName, LdapDN newParentName, Rdn newRn, boolean deleteOldRn, ServerEntry entry, boolean cascaded ) throws NamingException
+    {
+
+    }
+
+
+    /**
+     * Handles the addition of a metaSchema object to the schema partition.
+     * 
+     * @param name the dn of the new metaSchema object
+     * @param entry the attributes of the new metaSchema object
+     */
+    public void add( LdapDN name, ServerEntry entry ) throws NamingException
+    {
+        LdapDN parentDn = ( LdapDN ) name.clone();
+        parentDn.remove( parentDn.size() - 1 );
+        parentDn.normalize( globalRegistries.getAttributeTypeRegistry().getNormalizerMapping() );
+        if ( !parentDn.toNormName().equals( OU_OID + "=schema" ) )
+        {
+            throw new LdapInvalidNameException( "The parent dn of a schema should be " + OU_OID + "=schema and not: "
+                + parentDn.toNormName(), ResultCodeEnum.NAMING_VIOLATION );
+        }
+
+        // check if the new schema is enabled or disabled
+        boolean isEnabled = false;
+        EntryAttribute disabled = entry.get( disabledAT );
+        
+        if ( disabled == null )
+        {
+            isEnabled = true;
+        }
+        else if ( ! disabled.contains( "TRUE" ) )
+        {
+            isEnabled = true;
+        }
+        
+        // check to see that all dependencies are resolved and loaded if this
+        // schema is enabled, otherwise check that the dependency schemas exist
+        checkForDependencies( isEnabled, entry );
+        
+        /*
+         * There's a slight problem that may result when adding a metaSchema
+         * object if the addition of the physical entry fails.  If the schema
+         * is enabled when added in the condition tested below, that schema
+         * is added to the global registries.  We need to add this so subsequent
+         * schema entity additions are loaded into the registries as they are
+         * added to the schema partition.  However if the metaSchema object 
+         * addition fails then we're left with this schema object looking like
+         * it is enabled in the registries object's schema hash.  The effects
+         * of this are unpredicatable.
+         * 
+         * This whole problem is due to the inability of these handlers to 
+         * react to a failed operation.  To fix this we would need some way
+         * for these handlers to respond to failed operations and revert their
+         * effects on the registries.
+         * 
+         * TODO: might want to add a set of failedOnXXX methods to the adapter
+         * where on failure the schema service calls the schema manager and it
+         * calls the appropriate methods on the respective handler.  This way
+         * the schema manager can rollback registry changes when LDAP operations
+         * fail.
+         */
+
+        if ( isEnabled )
+        {
+            Schema schema = factory.getSchema( entry );
+            globalRegistries.addToLoadedSet( schema );
+        }
+    }
+
+
+    /**
+     * Called to react to the deletion of a metaSchema object.  This method
+     * simply removes the schema from the loaded schema map of the global 
+     * registries.  
+     * 
+     * @param name the dn of the metaSchema object being deleted
+     * @param entry the attributes of the metaSchema object 
+     */
+    public void delete( LdapDN name, ServerEntry entry, boolean cascade ) throws NamingException
+    {
+        EntryAttribute cn = entry.get( cnAT );
+        String schemaName = cn.getString();
+
+        // Before allowing a schema object to be deleted we must check
+        // to make sure it's not depended upon by another schema
+        Set<String> dependents = loader.listDependentSchemaNames( schemaName );
+        if ( ! dependents.isEmpty() )
+        {
+            throw new LdapOperationNotSupportedException(
+                "Cannot delete schema that has dependents: " + dependents,
+                ResultCodeEnum.UNWILLING_TO_PERFORM );
+        }
+        
+        // no need to check if schema is enabled or disabled here
+        // if not in the loaded set there will be no negative effect
+        globalRegistries.removeFromLoadedSet( schemaName );
+    }
+
+
+
+    /**
+     * Responds to the rdn (commonName) of the metaSchema object being 
+     * changed.  Changes all the schema entities associated with the 
+     * renamed schema so they now map to a new schema name.
+     * 
+     * @param name the dn of the metaSchema object renamed
+     * @param entry the entry of the metaSchema object before the rename
+     * @param newRdn the new commonName of the metaSchema object
+     */
+    public void rename( LdapDN name, ServerEntry entry, Rdn newRdn, boolean cascade ) throws NamingException
+    {
+        String rdnAttribute = newRdn.getUpType();
+        String rdnAttributeOid = globalRegistries.getOidRegistry().getOid( rdnAttribute );
+        if ( ! rdnAttributeOid.equals( cnAT.getOid() ) )
+        {
+            throw new LdapOperationNotSupportedException( 
+                "Cannot allow rename with rdnAttribute set to " 
+                + rdnAttribute + ": cn must be used instead." ,
+                ResultCodeEnum.UNWILLING_TO_PERFORM );
+        }
+
+        /*
+         * This operation has to do the following:
+         * 
+         * [1] check and make sure there are no dependent schemas on the 
+         *     one being renamed - if so an exception should result
+         *      
+         * [2] make non-schema object registries modify the mapping 
+         *     for their entities: non-schema object registries contain
+         *     objects that are not SchemaObjects and hence do not carry
+         *     their schema within the object as a property
+         *     
+         * [3] make schema object registries do the same but the way
+         *     they do them will be different since these objects will
+         *     need to be replaced or will require a setter for the 
+         *     schema name
+         */
+        
+        // step [1]
+        String schemaName = getSchemaName( name );
+        Set<String> dependents = loader.listDependentSchemaNames( schemaName );
+        if ( ! dependents.isEmpty() )
+        {
+            throw new LdapOperationNotSupportedException( 
+                "Cannot allow a rename on " + schemaName + " schema while it has depentents.",
+                ResultCodeEnum.UNWILLING_TO_PERFORM );
+        }
+
+        // check if the new schema is enabled or disabled
+        boolean isEnabled = false;
+        EntryAttribute disabled = entry.get( disabledAT );
+        
+        if ( disabled == null )
+        {
+            isEnabled = true;
+        }
+        else if ( ! disabled.get().equals( "TRUE" ) )
+        {
+            isEnabled = true;
+        }
+
+        if ( ! isEnabled )
+        {
+            return;
+        }
+
+        // do steps 2 and 3 if the schema has been enabled and is loaded
+        
+        // step [2] 
+        String newSchemaName = ( String ) newRdn.getUpValue();
+        globalRegistries.getComparatorRegistry().renameSchema( schemaName, newSchemaName );
+        globalRegistries.getNormalizerRegistry().renameSchema( schemaName, newSchemaName );
+        globalRegistries.getSyntaxCheckerRegistry().renameSchema( schemaName, newSchemaName );
+        
+        // step [3]
+        renameSchema( globalRegistries.getAttributeTypeRegistry(), schemaName, newSchemaName );
+        renameSchema( globalRegistries.getDitContentRuleRegistry(), schemaName, newSchemaName );
+        renameSchema( globalRegistries.getDitStructureRuleRegistry(), schemaName, newSchemaName );
+        renameSchema( globalRegistries.getMatchingRuleRegistry(), schemaName, newSchemaName );
+        renameSchema( globalRegistries.getMatchingRuleUseRegistry(), schemaName, newSchemaName );
+        renameSchema( globalRegistries.getNameFormRegistry(), schemaName, newSchemaName );
+        renameSchema( globalRegistries.getObjectClassRegistry(), schemaName, newSchemaName );
+        renameSchema( globalRegistries.getSyntaxRegistry(), schemaName, newSchemaName );
+    }
+    
+
+    /**
+     * Moves are not allowed for metaSchema objects so this always throws an
+     * UNWILLING_TO_PERFORM LdapException.
+     */
+    public void move( LdapDN oriChildName, LdapDN newParentName, String newRn, boolean deleteOldRn, 
+        ServerEntry entry, boolean cascade ) throws NamingException
+    {
+        throw new LdapOperationNotSupportedException( "Moving around schemas is not allowed.",
+            ResultCodeEnum.UNWILLING_TO_PERFORM );
+    }
+
+
+    /**
+     * Moves are not allowed for metaSchema objects so this always throws an
+     * UNWILLING_TO_PERFORM LdapException.
+     */
+    public void replace( LdapDN oriChildName, LdapDN newParentName, 
+        ServerEntry entry, boolean cascade ) throws NamingException
+    {
+        throw new LdapOperationNotSupportedException( "Moving around schemas is not allowed.",
+            ResultCodeEnum.UNWILLING_TO_PERFORM );
+    }
+
+    
+    // -----------------------------------------------------------------------
+    // private utility methods
+    // -----------------------------------------------------------------------
+
+    
+    private void disable( LdapDN name, ModificationOperation modOp, EntryAttribute disabledInMods, EntryAttribute disabledInEntry )
+        throws NamingException
+    {
+        switch ( modOp )
+        {
+            /*
+             * If the user is adding a new m-disabled attribute to an enabled schema, 
+             * we check that the value is "TRUE" and disable that schema if so.
+             */
+            case ADD_ATTRIBUTE :
+                if ( disabledInEntry == null )
+                {
+                    if ( "TRUE".equalsIgnoreCase( disabledInMods.getString() ) )
+                    {
+                        disableSchema( getSchemaName( name ) );
+                    }
+                }
+                
+                break;
+
+            /*
+             * If the user is removing the m-disabled attribute we check if the schema is currently 
+             * disabled.  If so we enable the schema.
+             */
+            case REMOVE_ATTRIBUTE :
+                if ( "TRUE".equalsIgnoreCase( disabledInEntry.getString() ) )
+                {
+                    enableSchema( getSchemaName( name ) );
+                }
+                
+                break;
+
+            /*
+             * If the user is replacing the m-disabled attribute we check if the schema is 
+             * currently disabled and enable it if the new state has it as enabled.  If the
+             * schema is not disabled we disable it if the mods set m-disabled to true.
+             */
+            case REPLACE_ATTRIBUTE :
+                boolean isCurrentlyDisabled = "TRUE".equalsIgnoreCase( disabledInEntry.getString() );
+                boolean isNewStateDisabled = "TRUE".equalsIgnoreCase( disabledInMods.getString() );
+
+                if ( isCurrentlyDisabled && !isNewStateDisabled )
+                {
+                    enableSchema( getSchemaName( name ) );
+                    break;
+                }
+
+                if ( !isCurrentlyDisabled && isNewStateDisabled )
+                {
+                    disableSchema( getSchemaName( name ) );
+                }
+                
+                break;
+                
+            default:
+                throw new IllegalArgumentException( "Unknown modify operation type: " + modOp );
+        }
+    }
+
+
+    private String getSchemaName( LdapDN schema )
+    {
+        return ( String ) schema.getRdn().getValue();
+    }
+
+
+    private void disableSchema( String schemaName ) throws NamingException
+    {
+        Set<String> dependents = loader.listEnabledDependentSchemaNames( schemaName );
+        if ( ! dependents.isEmpty() )
+        {
+            throw new LdapOperationNotSupportedException(
+                "Cannot disable schema with enabled dependents: " + dependents,
+                ResultCodeEnum.UNWILLING_TO_PERFORM );
+        }
+        
+        globalRegistries.unload( schemaName );
+    }
+
+
+    /**
+     * TODO - for now we're just going to add the schema to the global 
+     * registries ... we may need to add it to more than that though later.
+     */
+    private void enableSchema( String schemaName ) throws NamingException
+    {
+        if ( globalRegistries.getLoadedSchemas().containsKey( schemaName ) )
+        {
+            // TODO log warning: schemaName + " was already loaded"
+            return;
+        }
+
+        Schema schema = loader.getSchema( schemaName );
+        loader.loadWithDependencies( schema, globalRegistries );
+    }
+
+
+    /**
+     * Checks to make sure the dependencies either exist for disabled metaSchemas,
+     * or exist and are loaded (enabled) for enabled metaSchemas.
+     * 
+     * @param isEnabled whether or not the new metaSchema is enabled
+     * @param entry the Attributes for the new metaSchema object
+     * @throws NamingException if the dependencies do not resolve or are not
+     * loaded (enabled)
+     */
+    private void checkForDependencies( boolean isEnabled, ServerEntry entry ) throws NamingException
+    {
+        EntryAttribute dependencies = entry.get( this.dependenciesAT );
+
+        if ( dependencies == null )
+        {
+            return;
+        }
+        
+        if ( isEnabled )
+        {
+            // check to make sure all the dependencies are also enabled
+            Map<String,Schema> loaded = globalRegistries.getLoadedSchemas();
+            
+            for ( Value<?> value:dependencies )
+            {
+                String dependency = ( String ) value.get();
+                
+                if ( ! loaded.containsKey( dependency ) )
+                {
+                    throw new LdapOperationNotSupportedException( 
+                        "Unwilling to perform operation on enabled schema with disabled or missing dependencies: " 
+                        + dependency, ResultCodeEnum.UNWILLING_TO_PERFORM );
+                }
+            }
+        }
+        else
+        {
+            Set<String> allSchemas = loader.getSchemaNames();
+            
+            for ( Value<?> value:dependencies )
+            {
+                String dependency = ( String ) value.get();
+                
+                if ( ! allSchemas.contains( dependency ) )
+                {
+                    throw new LdapOperationNotSupportedException( 
+                        "Unwilling to perform operation on schema with missing dependencies: " + dependency, 
+                        ResultCodeEnum.UNWILLING_TO_PERFORM );
+                }
+            }
+        }
+    }
+
+    
+    /**
+     * Used to iterate through SchemaObjects in a SchemaObjectRegistry and rename
+     * their schema property to a new schema name.
+     * 
+     * @param registry the registry whose objects are changed
+     * @param originalSchemaName the original schema name
+     * @param newSchemaName the new schema name
+     */
+    private void renameSchema( SchemaObjectRegistry registry, String originalSchemaName, String newSchemaName ) 
+    {
+        Iterator<? extends SchemaObject> list = registry.iterator();
+        while ( list.hasNext() )
+        {
+            SchemaObject obj = list.next();
+            if ( obj.getSchema().equalsIgnoreCase( originalSchemaName ) )
+            {
+                obj.setSchema( newSchemaName );
+            }
+        }
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/MetaSchemaUtils.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/MetaSchemaUtils.java
new file mode 100644
index 0000000..05cbe30
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/MetaSchemaUtils.java
@@ -0,0 +1,47 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.schema;
+
+
+import javax.naming.NamingException;
+
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.name.Rdn;
+
+
+/**
+ * Some useful methods used by meta schema handling code.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class MetaSchemaUtils
+{
+    public static String getSchemaName( LdapDN dn ) throws NamingException
+    {
+        if ( dn.size() < 2 )
+        {
+            throw new NamingException( "At least two name components are expected for the dn" );
+        }
+        
+        Rdn rdn = dn.getRdn( 1 );
+        return ( String ) rdn.getValue();
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/MetaSyntaxCheckerHandler.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/MetaSyntaxCheckerHandler.java
new file mode 100644
index 0000000..ad67821
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/MetaSyntaxCheckerHandler.java
@@ -0,0 +1,323 @@
+/*
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *   or more contributor license agreements.  See the NOTICE file
+ *   distributed with this work for additional information
+ *   regarding copyright ownership.  The ASF licenses this file
+ *   to you 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.
+ *
+ */
+package org.apache.directory.server.core.schema;
+
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.constants.MetaSchemaConstants;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.schema.bootstrap.Schema;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.server.schema.registries.SyntaxCheckerRegistry;
+import org.apache.directory.server.schema.registries.SyntaxRegistry;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.exception.LdapInvalidNameException;
+import org.apache.directory.shared.ldap.exception.LdapNamingException;
+import org.apache.directory.shared.ldap.exception.LdapOperationNotSupportedException;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.name.Rdn;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.schema.syntax.SyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.SyntaxCheckerDescription;
+import org.apache.directory.shared.ldap.util.Base64;
+
+
+/**
+ * A handler for operations peformed to add, delete, modify, rename and 
+ * move schema normalizers.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class MetaSyntaxCheckerHandler extends AbstractSchemaChangeHandler
+{
+    private final SchemaEntityFactory factory;
+    private final SyntaxCheckerRegistry syntaxCheckerRegistry;
+    private final SyntaxRegistry syntaxRegistry;
+    private final AttributeType byteCodeAT;
+    private final AttributeType descAT;
+    private final AttributeType fqcnAT;
+    
+
+    public MetaSyntaxCheckerHandler( Registries targetRegistries, PartitionSchemaLoader loader ) throws NamingException
+    {
+        super( targetRegistries, loader );
+        this.syntaxCheckerRegistry = targetRegistries.getSyntaxCheckerRegistry();
+        this.syntaxRegistry = targetRegistries.getSyntaxRegistry();
+        this.factory = new SchemaEntityFactory( targetRegistries );
+        this.byteCodeAT = targetRegistries.getAttributeTypeRegistry().lookup( MetaSchemaConstants.M_BYTECODE_AT );
+        this.descAT = targetRegistries.getAttributeTypeRegistry().lookup( MetaSchemaConstants.M_DESCRIPTION_AT );
+        this.fqcnAT = targetRegistries.getAttributeTypeRegistry().lookup( MetaSchemaConstants.M_FQCN_AT );
+    }
+
+
+    private SyntaxCheckerDescription getSyntaxCheckerDescription( String schemaName, ServerEntry entry ) 
+        throws NamingException
+    {
+        SyntaxCheckerDescription description = new SyntaxCheckerDescription();
+        description.setNumericOid( getOid( entry ) );
+        List<String> values = new ArrayList<String>();
+        values.add( schemaName );
+        description.addExtension( MetaSchemaConstants.X_SCHEMA, values );
+        description.setFqcn( entry.get( fqcnAT ).getString() );
+        
+        EntryAttribute desc = entry.get( descAT );
+        
+        if ( desc != null && desc.size() > 0 )
+        {
+            description.setDescription( desc.getString() );
+        }
+        
+        EntryAttribute bytecode = entry.get( byteCodeAT );
+        
+        if ( bytecode != null && bytecode.size() > 0 )
+        {
+            byte[] bytes = bytecode.getBytes();
+            description.setBytecode( new String( Base64.encode( bytes ) ) );
+        }
+
+        return description;
+    }
+
+    
+    protected void modify( LdapDN name, ServerEntry entry, ServerEntry targetEntry, boolean cascade ) throws NamingException
+    {
+        String oid = getOid( entry );
+        SyntaxChecker syntaxChecker = factory.getSyntaxChecker( targetEntry, targetRegistries );
+        
+        Schema schema = getSchema( name );
+        
+        if ( ! schema.isDisabled() )
+        {
+            syntaxCheckerRegistry.unregister( oid );
+            SyntaxCheckerDescription syntaxCheckerDescription = 
+                getSyntaxCheckerDescription( schema.getSchemaName(), targetEntry );
+            syntaxCheckerRegistry.register( syntaxCheckerDescription, syntaxChecker );
+        }
+    }
+
+
+    public void add( LdapDN name, ServerEntry entry ) throws NamingException
+    {
+        LdapDN parentDn = ( LdapDN ) name.clone();
+        parentDn.remove( parentDn.size() - 1 );
+        checkNewParent( parentDn );
+        String oid = getOid( entry );
+        if ( super.targetRegistries.getSyntaxCheckerRegistry().hasSyntaxChecker( oid ) )
+        {
+            throw new LdapNamingException( "Oid " + oid + " for new schema syntaxChecker is not unique.", 
+                ResultCodeEnum.OTHER );
+        }
+        
+        SyntaxChecker syntaxChecker = factory.getSyntaxChecker( entry, targetRegistries );
+        Schema schema = getSchema( name );
+        
+        if ( ! schema.isDisabled() )
+        {
+            SyntaxCheckerDescription syntaxCheckerDescription = 
+                getSyntaxCheckerDescription( schema.getSchemaName(), entry );
+            syntaxCheckerRegistry.register( syntaxCheckerDescription, syntaxChecker );
+        }
+    }
+
+
+    public void add( SyntaxCheckerDescription syntaxCheckerDescription ) throws NamingException
+    {
+        SyntaxChecker syntaxChecker = factory.getSyntaxChecker( syntaxCheckerDescription, targetRegistries );
+        String schemaName = MetaSchemaConstants.SCHEMA_OTHER;
+        
+        if ( syntaxCheckerDescription.getExtensions().get( MetaSchemaConstants.X_SCHEMA ) != null )
+        {
+            schemaName = syntaxCheckerDescription.getExtensions()
+                .get( MetaSchemaConstants.X_SCHEMA ).get( 0 );
+        }
+        
+        Schema schema = loader.getSchema( schemaName );
+        
+        if ( ! schema.isDisabled() )
+        {
+            syntaxCheckerRegistry.register( syntaxCheckerDescription, syntaxChecker );
+        }
+    }
+
+
+    public void delete( LdapDN name, ServerEntry entry, boolean cascade ) throws NamingException
+    {
+        delete( getOid( entry ), cascade );
+    }
+
+
+    public void delete( String oid, boolean cascade ) throws NamingException
+    {
+        if ( syntaxRegistry.hasSyntax( oid ) )
+        {
+            throw new LdapOperationNotSupportedException( "The syntaxChecker with OID " + oid 
+                + " cannot be deleted until all " 
+                + "syntaxes using this syntaxChecker have also been deleted.", 
+                ResultCodeEnum.UNWILLING_TO_PERFORM );
+        }
+        
+        if ( syntaxCheckerRegistry.hasSyntaxChecker( oid ) )
+        {
+            syntaxCheckerRegistry.unregister( oid );
+        }
+    }
+
+
+    public void rename( LdapDN name, ServerEntry entry, Rdn newRdn, boolean cascade ) throws NamingException
+    {
+        String oldOid = getOid( entry );
+
+        if ( syntaxRegistry.hasSyntax( oldOid ) )
+        {
+            throw new LdapOperationNotSupportedException( "The syntaxChecker with OID " + oldOid 
+                + " cannot have it's OID changed until all " 
+                + "syntaxes using that syntaxChecker have been deleted.", 
+                ResultCodeEnum.UNWILLING_TO_PERFORM );
+        }
+
+        Schema schema = getSchema( name );
+        ServerEntry targetEntry = ( ServerEntry ) entry.clone();
+        String newOid = ( String ) newRdn.getValue();
+        if ( super.targetRegistries.getSyntaxCheckerRegistry().hasSyntaxChecker( newOid ) )
+        {
+            throw new LdapNamingException( "Oid " + newOid + " for new schema syntaxChecker is not unique.", 
+                ResultCodeEnum.OTHER );
+        }
+
+        targetEntry.put( MetaSchemaConstants.M_OID_AT, newOid );
+        if ( ! schema.isDisabled() )
+        {
+            SyntaxChecker syntaxChecker = factory.getSyntaxChecker( targetEntry, targetRegistries );
+            syntaxCheckerRegistry.unregister( oldOid );
+            SyntaxCheckerDescription syntaxCheckerDescription = 
+                getSyntaxCheckerDescription( schema.getSchemaName(), entry );
+            syntaxCheckerDescription.setNumericOid( newOid );
+            syntaxCheckerRegistry.register( syntaxCheckerDescription, syntaxChecker );
+        }
+    }
+
+
+    public void move( LdapDN oriChildName, LdapDN newParentName, Rdn newRdn, boolean deleteOldRn, 
+        ServerEntry entry, boolean cascade ) throws NamingException
+    {
+        checkNewParent( newParentName );
+        String oldOid = getOid( entry );
+
+        if ( syntaxRegistry.hasSyntax( oldOid ) )
+        {
+            throw new LdapOperationNotSupportedException( "The syntaxChecker with OID " + oldOid 
+                + " cannot have it's OID changed until all " 
+                + "syntaxes using that syntaxChecker have been deleted.", 
+                ResultCodeEnum.UNWILLING_TO_PERFORM );
+        }
+
+        Schema oldSchema = getSchema( oriChildName );
+        Schema newSchema = getSchema( newParentName );
+        ServerEntry targetEntry = ( ServerEntry ) entry.clone();
+        
+        String newOid = ( String ) newRdn.getValue();
+        if ( super.targetRegistries.getSyntaxCheckerRegistry().hasSyntaxChecker( newOid ) )
+        {
+            throw new LdapNamingException( "Oid " + newOid + " for new schema syntaxChecker is not unique.", 
+                ResultCodeEnum.OTHER );
+        }
+
+        targetEntry.put( MetaSchemaConstants.M_OID_AT, newOid );
+        SyntaxChecker syntaxChecker = factory.getSyntaxChecker( targetEntry, targetRegistries );
+
+        if ( ! oldSchema.isDisabled() )
+        {
+            syntaxCheckerRegistry.unregister( oldOid );
+        }
+
+        if ( ! newSchema.isDisabled() )
+        {
+            SyntaxCheckerDescription syntaxCheckerDescription = 
+                getSyntaxCheckerDescription( newSchema.getSchemaName(), entry );
+            syntaxCheckerDescription.setNumericOid( newOid );
+            syntaxCheckerRegistry.register( syntaxCheckerDescription, syntaxChecker );
+        }
+    }
+
+
+    public void replace( LdapDN oriChildName, LdapDN newParentName, ServerEntry entry, boolean cascade ) 
+        throws NamingException
+    {
+        checkNewParent( newParentName );
+        String oid = getOid( entry );
+
+        if ( syntaxRegistry.hasSyntax( oid ) )
+        {
+            throw new LdapOperationNotSupportedException( "The syntaxChecker with OID " + oid 
+                + " cannot be moved to another schema until all " 
+                + "syntax using that syntaxChecker have been deleted.", 
+                ResultCodeEnum.UNWILLING_TO_PERFORM );
+        }
+
+        Schema oldSchema = getSchema( oriChildName );
+        Schema newSchema = getSchema( newParentName );
+        
+        SyntaxChecker syntaxChecker = factory.getSyntaxChecker( entry, targetRegistries );
+        
+        if ( ! oldSchema.isDisabled() )
+        {
+            syntaxCheckerRegistry.unregister( oid );
+        }
+        
+        if ( ! newSchema.isDisabled() )
+        {
+            SyntaxCheckerDescription syntaxCheckerDescription = 
+                getSyntaxCheckerDescription( newSchema.getSchemaName(), entry );
+            syntaxCheckerRegistry.register( syntaxCheckerDescription, syntaxChecker );
+        }
+    }
+    
+    
+    private void checkNewParent( LdapDN newParent ) throws NamingException
+    {
+        if ( newParent.size() != 3 )
+        {
+            throw new LdapInvalidNameException( 
+                "The parent dn of a syntaxChecker should be at most 3 name components in length.", 
+                ResultCodeEnum.NAMING_VIOLATION );
+        }
+        
+        Rdn rdn = newParent.getRdn();
+        if ( ! targetRegistries.getOidRegistry().getOid( rdn.getNormType() ).equals( SchemaConstants.OU_AT_OID ) )
+        {
+            throw new LdapInvalidNameException( "The parent entry of a syntaxChecker should be an organizationalUnit.", 
+                ResultCodeEnum.NAMING_VIOLATION );
+        }
+        
+        if ( ! ( ( String ) rdn.getValue() ).equalsIgnoreCase( SchemaConstants.SYNTAX_CHECKERS_AT ) )
+        {
+            throw new LdapInvalidNameException( 
+                "The parent entry of a normalizer should have a relative name of ou=syntaxCheckers.", 
+                ResultCodeEnum.NAMING_VIOLATION );
+        }
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/MetaSyntaxHandler.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/MetaSyntaxHandler.java
new file mode 100644
index 0000000..cfe4035
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/MetaSyntaxHandler.java
@@ -0,0 +1,293 @@
+/*
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *   or more contributor license agreements.  See the NOTICE file
+ *   distributed with this work for additional information
+ *   regarding copyright ownership.  The ASF licenses this file
+ *   to you 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.
+ *
+ */
+package org.apache.directory.server.core.schema;
+
+
+import java.util.Set;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.constants.MetaSchemaConstants;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerSearchResult;
+import org.apache.directory.server.schema.bootstrap.Schema;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.server.schema.registries.SyntaxRegistry;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.exception.LdapInvalidNameException;
+import org.apache.directory.shared.ldap.exception.LdapOperationNotSupportedException;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.name.Rdn;
+import org.apache.directory.shared.ldap.schema.Syntax;
+
+
+/**
+ * A handler for operations peformed to add, delete, modify, rename and 
+ * move schema normalizers.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class MetaSyntaxHandler extends AbstractSchemaChangeHandler
+{
+    private final SchemaPartitionDao dao;
+    private final SyntaxRegistry syntaxRegistry;
+
+    
+    public MetaSyntaxHandler( Registries targetRegistries, PartitionSchemaLoader loader, SchemaPartitionDao dao ) 
+        throws NamingException
+    {
+        super( targetRegistries, loader );
+
+        this.dao = dao;
+        this.syntaxRegistry = targetRegistries.getSyntaxRegistry();
+    }
+
+    
+    protected void modify( LdapDN name, ServerEntry entry, ServerEntry targetEntry, 
+        boolean cascade ) throws NamingException
+    {
+        String oid = getOid( entry );
+        Schema schema = getSchema( name );
+        Syntax syntax = factory.getSyntax( targetEntry, targetRegistries, schema.getSchemaName() );
+        
+        if ( ! schema.isDisabled() )
+        {
+            syntaxRegistry.unregister( oid );
+            syntaxRegistry.register( syntax );
+        }
+    }
+
+    
+    public void add( LdapDN name, ServerEntry entry ) throws NamingException
+    {
+        LdapDN parentDn = ( LdapDN ) name.clone();
+        parentDn.remove( parentDn.size() - 1 );
+        checkNewParent( parentDn );
+        checkOidIsUnique( entry );
+        
+        String schemaName = getSchemaName( name );
+        Syntax syntax = factory.getSyntax( entry, targetRegistries, schemaName );
+        add( syntax );
+    }
+
+
+    public void delete( LdapDN name, ServerEntry entry, boolean cascade ) throws NamingException
+    {
+        String oid = getOid( entry );
+        
+        Set<ServerSearchResult> dependees = dao.listSyntaxDependents( oid );
+        
+        if ( dependees != null && dependees.size() > 0 )
+        {
+            throw new LdapOperationNotSupportedException( "The syntax with OID " + oid 
+                + " cannot be deleted until all entities" 
+                + " using this syntax have also been deleted.  The following dependees exist: " 
+                + getOids( dependees ), 
+                ResultCodeEnum.UNWILLING_TO_PERFORM );
+        }
+        
+        
+        String schemaName = getSchemaName( name );
+        Syntax syntax = factory.getSyntax( entry, targetRegistries, schemaName );
+        delete( syntax, cascade );
+    }
+
+
+    public void delete( Syntax syntax, boolean cascade ) throws NamingException
+    {
+        Schema schema = loader.getSchema( syntax.getSchema() );
+        if ( ! schema.isDisabled() )
+        {
+            syntaxRegistry.unregister( syntax.getOid() );
+        }
+
+        // no matter what we remove OID for deleted syntaxes
+        unregisterOids( syntax.getOid() );
+    }
+
+    
+    public void rename( LdapDN name, ServerEntry entry, Rdn newRdn, boolean cascade ) throws NamingException
+    {
+        String oldOid = getOid( entry );
+
+        Set<ServerSearchResult> dependees = dao.listSyntaxDependents( oldOid );
+        
+        if ( dependees != null && dependees.size() > 0 )
+        {
+            throw new LdapOperationNotSupportedException( "The syntax with OID " + oldOid
+                + " cannot be deleted until all entities" 
+                + " using this syntax have also been deleted.  The following dependees exist: " 
+                + getOids( dependees ), 
+                ResultCodeEnum.UNWILLING_TO_PERFORM );
+        }
+
+        Schema schema = getSchema( name );
+        ServerEntry targetEntry = ( ServerEntry ) entry.clone();
+        String newOid = ( String ) newRdn.getValue();
+        checkOidIsUnique( newOid );
+        
+        targetEntry.put( MetaSchemaConstants.M_OID_AT, newOid );
+        Syntax syntax = factory.getSyntax( targetEntry, targetRegistries, schema.getSchemaName() );
+        
+        if ( ! schema.isDisabled() )
+        {
+            syntaxRegistry.unregister( oldOid );
+            syntaxRegistry.register( syntax );
+        }
+        else
+        {
+            // even for disabled schemas add OIDs
+            registerOids( syntax );
+        }
+        
+        // always remove old OIDs that are not in schema anymore
+        unregisterOids( oldOid );
+    }
+
+
+    public void move( LdapDN oriChildName, LdapDN newParentName, Rdn newRn, boolean deleteOldRn,
+        ServerEntry entry, boolean cascade ) throws NamingException
+    {
+        checkNewParent( newParentName );
+        String oldOid = getOid( entry );
+
+        Set<ServerSearchResult> dependees = dao.listSyntaxDependents( oldOid );
+        
+        if ( dependees != null && dependees.size() > 0 )
+        {
+            throw new LdapOperationNotSupportedException( "The syntax with OID " + oldOid 
+                + " cannot be deleted until all entities" 
+                + " using this syntax have also been deleted.  The following dependees exist: " 
+                + getOids( dependees ), 
+                ResultCodeEnum.UNWILLING_TO_PERFORM );
+        }
+
+        Schema oldSchema = getSchema( oriChildName );
+        Schema newSchema = getSchema( newParentName );
+        ServerEntry targetEntry = ( ServerEntry ) entry.clone();
+        String newOid = ( String ) newRn.getValue();
+        checkOidIsUnique( newOid );
+        
+        targetEntry.put( MetaSchemaConstants.M_OID_AT, newOid );
+        Syntax syntax = factory.getSyntax( targetEntry, targetRegistries, newSchema.getSchemaName() );
+
+        if ( ! oldSchema.isDisabled() )
+        {
+            syntaxRegistry.unregister( oldOid );
+        }
+        // always remove old OIDs that are not in schema anymore
+        unregisterOids( oldOid );
+
+        if ( ! newSchema.isDisabled() )
+        {
+            syntaxRegistry.register( syntax );
+        }
+        else
+        {
+            // register new syntax OIDs even if schema is disabled 
+            registerOids( syntax );
+        }
+    }
+
+
+    public void replace( LdapDN oriChildName, LdapDN newParentName, ServerEntry entry, boolean cascade ) 
+        throws NamingException
+    {
+        checkNewParent( newParentName );
+        String oid = getOid( entry );
+        
+        Set<ServerSearchResult> dependees = dao.listSyntaxDependents( oid );
+        
+        if ( dependees != null && dependees.size() > 0 )
+        {
+            throw new LdapOperationNotSupportedException( "The syntax with OID " + oid 
+                + " cannot be deleted until all entities" 
+                + " using this syntax have also been deleted.  The following dependees exist: " 
+                + getOids( dependees ), 
+                ResultCodeEnum.UNWILLING_TO_PERFORM );
+        }
+
+        Schema oldSchema = getSchema( oriChildName );
+        Schema newSchema = getSchema( newParentName );
+        
+        Syntax syntax = factory.getSyntax( entry, targetRegistries, newSchema.getSchemaName() );
+        
+        if ( ! oldSchema.isDisabled() )
+        {
+            syntaxRegistry.unregister( oid );
+        }
+        
+        if ( ! newSchema.isDisabled() )
+        {
+            syntaxRegistry.register( syntax );
+        }
+    }
+    
+    
+    private void checkNewParent( LdapDN newParent ) throws NamingException
+    {
+        if ( newParent.size() != 3 )
+        {
+            throw new LdapInvalidNameException( 
+                "The parent dn of a syntax should be at most 3 name components in length.", 
+                ResultCodeEnum.NAMING_VIOLATION );
+        }
+        
+        Rdn rdn = newParent.getRdn();
+        if ( ! targetRegistries.getOidRegistry().getOid( rdn.getNormType() ).equals( SchemaConstants.OU_AT_OID ) )
+        {
+            throw new LdapInvalidNameException( "The parent entry of a syntax should be an organizationalUnit.", 
+                ResultCodeEnum.NAMING_VIOLATION );
+        }
+        
+        if ( ! ( ( String ) rdn.getValue() ).equalsIgnoreCase( "syntaxes" ) )
+        {
+            throw new LdapInvalidNameException( 
+                "The parent entry of a syntax should have a relative name of ou=syntaxes.", 
+                ResultCodeEnum.NAMING_VIOLATION );
+        }
+    }
+
+
+    /**
+     * Adds a syntax to this handler's registries if it's schema is enabled.  The
+     * OID is always registered with the OidRegistry regardless of the enabled state
+     * of the schema.   
+     * 
+     * @param syntax the syntax that is to be added to this handler's registries
+     * @throws NamingException if there are problems access schema data
+     */
+    public void add( Syntax syntax ) throws NamingException
+    {
+        Schema schema = loader.getSchema( syntax.getSchema() );
+        
+        if ( ! schema.isDisabled() )
+        {
+            syntaxRegistry.register( syntax );
+        }
+        else
+        {
+            // even for disabled schemas add OIDs
+            registerOids( syntax );
+        }
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/NameFormImpl.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/NameFormImpl.java
new file mode 100644
index 0000000..c4abe99
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/NameFormImpl.java
@@ -0,0 +1,173 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.schema;
+
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.schema.AbstractSchemaObject;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.schema.MutableSchemaObject;
+import org.apache.directory.shared.ldap.schema.NameForm;
+import org.apache.directory.shared.ldap.schema.ObjectClass;
+
+
+/**
+ * A nameForm bean implementation that uses a set of registries to dynamically
+ * resolve it's dependencies.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class NameFormImpl extends AbstractSchemaObject implements NameForm, MutableSchemaObject
+{
+    private static final long serialVersionUID = 1L;
+    private static final String[] EMPTY_STR_ARRAY = new String[0];
+    private static final AttributeType[] EMPTY_ATTR_ARRAY = new AttributeType[0];
+
+    
+    private final Registries registries;
+    
+    private String objectClassOid;
+
+    private String[] mayUseOids = EMPTY_STR_ARRAY;
+    private AttributeType[] mayUse = EMPTY_ATTR_ARRAY;
+    
+    private String[] mustUseOids = EMPTY_STR_ARRAY;
+    private AttributeType[] mustUse = EMPTY_ATTR_ARRAY;
+    
+
+    /**
+     * @param oid
+     */
+    public NameFormImpl( String oid, Registries registries )
+    {
+        super( oid );
+        this.registries = registries;
+    }
+
+    
+    public void setMayUseOids( String[] mayUseOids )
+    {
+        if ( mayUseOids == null )
+        {
+            this.mayUse = EMPTY_ATTR_ARRAY;
+            this.mayUseOids = EMPTY_STR_ARRAY;
+        }
+        else
+        {
+            this.mayUse = new AttributeType[mayUseOids.length];
+            this.mayUseOids = mayUseOids;
+        }
+    }
+    
+    
+    /* (non-Javadoc)
+     * @see org.apache.directory.shared.ldap.schema.NameForm#getMaytUse()
+     */
+    public AttributeType[] getMayUse() throws NamingException
+    {
+        if ( mayUseOids == null || mayUseOids.length == 0 )
+        {
+            return EMPTY_ATTR_ARRAY;
+        }
+        
+        for ( int ii = 0; ii < mayUseOids.length; ii++ )
+        {
+            mayUse[ii] = registries.getAttributeTypeRegistry().lookup( mayUseOids[ii] );
+        }
+        
+        return mayUse;
+    }
+
+
+    public void setMustUseOids( String[] mustUseOids )
+    {
+        if ( mustUseOids == null )
+        {
+            this.mustUse = EMPTY_ATTR_ARRAY;
+            this.mustUseOids = EMPTY_STR_ARRAY;
+        }
+        else
+        {
+            this.mustUse = new AttributeType[mustUseOids.length];
+            this.mustUseOids = mustUseOids;
+        }
+    }
+    
+    
+    /* (non-Javadoc)
+     * @see org.apache.directory.shared.ldap.schema.NameForm#getMustUse()
+     */
+    public AttributeType[] getMustUse() throws NamingException
+    {
+        if ( mustUseOids == null || mustUseOids.length == 0 )
+        {
+            return EMPTY_ATTR_ARRAY;
+        }
+        
+        for ( int ii = 0; ii < mustUseOids.length; ii++ )
+        {
+            mustUse[ii] = registries.getAttributeTypeRegistry().lookup( mustUseOids[ii] );
+        }
+        
+        return mustUse;
+    }
+    
+    
+    public void setObjectClassOid( String objectClassOid )
+    {
+        this.objectClassOid = objectClassOid;
+    }
+
+
+    /* (non-Javadoc)
+     * @see org.apache.directory.shared.ldap.schema.NameForm#getObjectClass()
+     */
+    public ObjectClass getObjectClass() throws NamingException
+    {
+        return registries.getObjectClassRegistry().lookup( objectClassOid );
+    }
+
+    
+    public void setDescription( String description )
+    {
+        super.setDescription( description );
+    }
+    
+    
+    public void setObsolete( boolean obsolete )
+    {
+        super.setObsolete( obsolete );
+    }
+    
+    
+    public void setNames( String[] names )
+    {
+        super.setNames( names );
+    }
+    
+    
+    public void setSchema( String schema )
+    {
+        super.setSchema( schema );
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/ObjectClassImpl.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/ObjectClassImpl.java
new file mode 100644
index 0000000..4a5a560
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/ObjectClassImpl.java
@@ -0,0 +1,228 @@
+/*
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *   or more contributor license agreements.  See the NOTICE file
+ *   distributed with this work for additional information
+ *   regarding copyright ownership.  The ASF licenses this file
+ *   to you 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.
+ *
+ */
+package org.apache.directory.server.core.schema;
+
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.schema.AbstractSchemaObject;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.schema.MutableSchemaObject;
+import org.apache.directory.shared.ldap.schema.ObjectClass;
+import org.apache.directory.shared.ldap.schema.ObjectClassTypeEnum;
+
+
+/**
+ * An ObjectClass implementation used by the server.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+class ObjectClassImpl extends AbstractSchemaObject implements MutableSchemaObject, ObjectClass
+{
+    private static final long serialVersionUID = 1L;
+    private final ObjectClass[] EMPTY_OC_ARRAY = new ObjectClass[0];
+    private final String[] EMPTY_STR_ARRAY = new String[0];
+    private final AttributeType[] EMPTY_AT_ARRAY = new AttributeType[0];
+    
+    private final Registries registries;
+
+    private ObjectClassTypeEnum objectClassType;
+    private ObjectClass[] superClasses;
+
+    private String[] mayListOids;
+    private AttributeType[] mayList = EMPTY_AT_ARRAY;
+    private boolean mayListReloaded;
+    
+    private String[] mustListOids;
+    private AttributeType[] mustList = EMPTY_AT_ARRAY;
+    private boolean mustListReloaded;
+
+    private String[] superClassOids;
+    
+    
+    protected ObjectClassImpl( String oid, Registries registries )
+    {
+        super( oid );
+        this.registries = registries;
+    }
+
+
+    public void setDescription( String description )
+    {
+        super.setDescription( description );
+    }
+
+
+    public void setNames( String[] names )
+    {
+        super.setNames( names );
+    }
+
+
+    public void setObsolete( boolean obsolete )
+    {
+        super.setObsolete( obsolete );
+    }
+
+    
+    public void setSchema( String schema )
+    {
+        super.setSchema( schema );
+    }
+    
+    
+    public AttributeType[] getMayList() throws NamingException
+    {
+        if ( this.mayListOids == null )
+        {
+            return EMPTY_AT_ARRAY;
+        }
+
+        if ( mayListReloaded )
+        {
+            for ( int ii = 0; ii < mayListOids.length; ii++ )
+            {
+                mayList[ii] = registries.getAttributeTypeRegistry().lookup( mayListOids[ii] );
+            }
+            
+            mayListReloaded = false;
+        }
+
+        return mayList;
+    }
+    
+    
+    public void setMayListOids( String[] mayListOids ) throws NamingException
+    {
+        if ( mayListOids == null )
+        {
+            this.mayListOids = EMPTY_STR_ARRAY;
+            mayList = EMPTY_AT_ARRAY;
+        }
+        else
+        {
+            this.mayListOids = mayListOids;
+            mayList = new AttributeType[mayListOids.length];
+        }
+
+        mayListReloaded = true;
+    }
+
+
+    public AttributeType[] getMustList() throws NamingException
+    {
+        if ( mustListOids == null )
+        {
+            return EMPTY_AT_ARRAY;
+        }
+        
+        if ( mustListReloaded )
+        {
+            for ( int ii = 0; ii < mustListOids.length; ii++ )
+            {
+                mustList[ii] = registries.getAttributeTypeRegistry().lookup( mustListOids[ii] );
+            }
+            
+            mustListReloaded = false;
+        }
+        
+        return mustList;
+    }
+    
+    
+    public void setMustListOids( String[] mustListOids ) throws NamingException
+    {
+        if ( mustListOids == null )
+        {
+            this.mustListOids = EMPTY_STR_ARRAY;
+            mustList = EMPTY_AT_ARRAY;
+        }
+        else
+        {
+            this.mustListOids = mustListOids;
+            mustList = new AttributeType[mustListOids.length];
+        }
+        
+        mustListReloaded = true;
+    }
+
+
+    public ObjectClass[] getSuperClasses() throws NamingException
+    {
+        if ( superClassOids == null )
+        {
+            return EMPTY_OC_ARRAY;
+        }
+        
+        for ( int ii = 0; ii < superClassOids.length; ii++ )
+        {
+            superClasses[ii] = registries.getObjectClassRegistry().lookup( superClassOids[ii] );
+        }
+        
+        return superClasses;
+    }
+
+    
+    void setSuperClassOids( String[] superClassOids )
+    {
+        if ( superClassOids == null || superClassOids.length == 0 )
+        {
+            this.superClassOids = EMPTY_STR_ARRAY;
+            this.superClasses = EMPTY_OC_ARRAY;
+        }
+        else
+        {
+            this.superClassOids = superClassOids;
+            this.superClasses = new ObjectClass[superClassOids.length];
+        }
+    }
+    
+
+    public ObjectClassTypeEnum getType()
+    {
+        return objectClassType;
+    }
+    
+    
+    public boolean isStructural()
+    {
+        return objectClassType == ObjectClassTypeEnum.STRUCTURAL;
+    }
+
+
+    public boolean isAbstract()
+    {
+        return objectClassType == ObjectClassTypeEnum.ABSTRACT;
+    }
+
+    
+    public boolean isAuxiliary()
+    {
+        return objectClassType == ObjectClassTypeEnum.AUXILIARY;
+    }
+
+    
+    void setType( ObjectClassTypeEnum objectClassType )
+    {
+        this.objectClassType = objectClassType;
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/PartitionSchemaLoader.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/PartitionSchemaLoader.java
new file mode 100644
index 0000000..d782050
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/PartitionSchemaLoader.java
@@ -0,0 +1,883 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.schema;
+
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.Stack;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+
+import org.apache.directory.server.constants.MetaSchemaConstants;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerSearchResult;
+import org.apache.directory.server.core.interceptor.context.EntryOperationContext;
+import org.apache.directory.server.core.interceptor.context.ListOperationContext;
+import org.apache.directory.server.core.interceptor.context.LookupOperationContext;
+import org.apache.directory.server.core.partition.Partition;
+import org.apache.directory.server.schema.bootstrap.Schema;
+import org.apache.directory.server.schema.registries.AbstractSchemaLoader;
+import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.server.schema.registries.SchemaLoader;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.entry.Value;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.schema.Normalizer;
+import org.apache.directory.shared.ldap.schema.ObjectClass;
+import org.apache.directory.shared.ldap.schema.Syntax;
+import org.apache.directory.shared.ldap.schema.MatchingRule;
+import org.apache.directory.shared.ldap.schema.syntax.ComparatorDescription;
+import org.apache.directory.shared.ldap.schema.syntax.NormalizerDescription;
+import org.apache.directory.shared.ldap.schema.syntax.SyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.SyntaxCheckerDescription;
+import org.apache.directory.shared.ldap.util.Base64;
+    
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * A class that loads schemas from a partition.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class PartitionSchemaLoader extends AbstractSchemaLoader
+{
+    /** static class logger */
+    private static final Logger LOG = LoggerFactory.getLogger( PartitionSchemaLoader.class );
+
+    private final SchemaPartitionDao dao;
+    private SchemaEntityFactory factory;
+    private Partition partition;
+    
+    /** The attributeType registry */
+    private AttributeTypeRegistry atRegistry;
+    
+    /** The global registries */
+    private Registries registries;
+    
+    private final AttributeType mOidAT;
+    private final AttributeType mNameAT;
+    private final AttributeType cnAT;
+    private final AttributeType byteCodeAT;
+    private final AttributeType descAT;
+    private final AttributeType fqcnAT;
+
+    private static Map<String, LdapDN> staticAttributeTypeDNs = new HashMap<String, LdapDN>();
+    private static Map<String, LdapDN> staticMatchingRulesDNs = new HashMap<String, LdapDN>();
+    private static Map<String, LdapDN> staticObjectClassesDNs = new HashMap<String, LdapDN>();
+    private static Map<String, LdapDN> staticComparatorsDNs = new HashMap<String, LdapDN>();
+    private static Map<String, LdapDN> staticNormalizersDNs = new HashMap<String, LdapDN>();
+    private static Map<String, LdapDN> staticSyntaxCheckersDNs = new HashMap<String, LdapDN>();
+    private static Map<String, LdapDN> staticSyntaxesDNs = new HashMap<String, LdapDN>();
+    
+    public PartitionSchemaLoader( Partition partition, Registries registries ) throws NamingException
+    {
+        this.factory = new SchemaEntityFactory( registries );
+        this.partition = partition;
+        this.registries = registries;
+        atRegistry = registries.getAttributeTypeRegistry();
+        
+        dao = new SchemaPartitionDao( this.partition, registries );
+        mOidAT = atRegistry.lookup( MetaSchemaConstants.M_OID_AT );
+        mNameAT = atRegistry.lookup( MetaSchemaConstants.M_NAME_AT );
+        cnAT = atRegistry.lookup( SchemaConstants.CN_AT );
+        byteCodeAT = atRegistry.lookup( MetaSchemaConstants.M_BYTECODE_AT );
+        descAT = atRegistry.lookup( MetaSchemaConstants.M_DESCRIPTION_AT );
+        fqcnAT = atRegistry.lookup( MetaSchemaConstants.M_FQCN_AT );
+        
+        initStaticDNs( "system" );
+        initStaticDNs( "core" );
+        initStaticDNs( "apache" );
+        initStaticDNs( "apachemeta" );
+        initStaticDNs( "other" );
+        initStaticDNs( "collective" );
+        initStaticDNs( "java" );
+        initStaticDNs( "cosine" );
+        initStaticDNs( "inetorgperson" );
+    }
+    
+    private void initStaticDNs( String schemaName ) throws NamingException
+    {
+        
+        // Initialize AttributeType Dns
+        LdapDN dn = new LdapDN( "ou=attributeTypes,cn=" + schemaName + ",ou=schema" );
+        dn.normalize( atRegistry.getNormalizerMapping() );
+        staticAttributeTypeDNs.put( schemaName, dn );
+
+        // Initialize ObjectClasses Dns
+        dn = new LdapDN( "ou=objectClasses,cn=" + schemaName + ",ou=schema" );
+        dn.normalize( atRegistry.getNormalizerMapping() );
+        staticObjectClassesDNs.put( schemaName, dn );
+
+        // Initialize MatchingRules Dns
+        dn = new LdapDN( "ou=matchingRules,cn=" + schemaName + ",ou=schema" );
+        dn.normalize( atRegistry.getNormalizerMapping() );
+        staticMatchingRulesDNs.put( schemaName, dn );
+
+        // Initialize Comparators Dns
+        dn = new LdapDN( "ou=comparators,cn=" + schemaName + ",ou=schema" );
+        dn.normalize( atRegistry.getNormalizerMapping() );
+        staticComparatorsDNs.put( schemaName, dn );
+        
+        // Initialize Normalizers Dns
+        dn = new LdapDN( "ou=normalizers,cn=" + schemaName + ",ou=schema" );
+        dn.normalize( atRegistry.getNormalizerMapping() );
+        staticNormalizersDNs.put( schemaName, dn );
+
+        // Initialize SyntaxCheckers Dns
+        dn = new LdapDN( "ou=syntaxCheckers,cn=" + schemaName + ",ou=schema" );
+        dn.normalize( atRegistry.getNormalizerMapping() );
+        staticSyntaxCheckersDNs.put( schemaName, dn );
+
+        // Initialize Syntaxes Dns
+        dn = new LdapDN( "ou=syntaxes,cn=" + schemaName + ",ou=schema" );
+        dn.normalize( atRegistry.getNormalizerMapping() );
+        staticSyntaxesDNs.put( schemaName, dn );
+
+    }
+    
+    /**
+     * Utility method to load all enabled schemas into this registry.
+     * 
+     * @param targetRegistries
+     * @throws NamingException
+     */
+    public void loadEnabled( Registries targetRegistries ) throws NamingException
+    {
+        /* 
+         * We need to load all names and oids into the oid registry regardless of
+         * the entity being in an enabled schema.  This is necessary because we 
+         * search for values in the schema partition that represent matchingRules
+         * and other entities that are not loaded.  While searching these values
+         * in disabled schemas normalizers will attempt to equate names with oids
+         * and if there is an unrecognized value by a normalizer then the search 
+         * will fail.
+         * 
+         * For example there is a NameOrNumericOidNormalizer that will reduce a 
+         * numeric OID or a non-numeric OID to it's numeric form using the OID 
+         * registry.  While searching the schema partition for attributeTypes we
+         * might find values of matchingRules in the m-ordering, m-equality, and
+         * m-substr attributes of metaAttributeType definitions.  Now if an entry
+         * references a matchingRule that has not been loaded then the 
+         * NameOrNumericOidNormalizer will bomb out when it tries to resolve 
+         * names of matchingRules in unloaded schemas to OID values using the 
+         * OID registry.  To prevent this we need to load all the OID's in advance
+         * regardless of whether they are used or not.
+         */
+        NamingEnumeration<ServerSearchResult> ne = dao.listAllNames();
+        
+        while ( ne.hasMore() )
+        {
+            ServerEntry entry = ne.next().getServerEntry();
+            String oid = entry.get( mOidAT ).getString();
+            EntryAttribute names = entry.get( mNameAT );
+            targetRegistries.getOidRegistry().register( oid, oid );
+            
+            for ( Value<?> value:names )
+            {
+                targetRegistries.getOidRegistry().register( ( String ) value.get(), oid );
+            }
+        }
+        
+        ne.close();
+        
+        
+        Map<String, Schema> allSchemaMap = getSchemas();
+        Set<Schema> enabledSchemaSet = new HashSet<Schema>();
+
+        for ( Schema schema: allSchemaMap.values() )
+        {
+            if ( ! schema.isDisabled() )
+            {
+                LOG.debug( "will attempt to load enabled schema: {}", schema.getSchemaName() );
+                    
+                enabledSchemaSet.add( schema );
+            }
+            else
+            {
+                LOG.debug( "will NOT attempt to load disabled schema: {}", schema.getSchemaName() );
+            }
+        }
+
+        loadWithDependencies( enabledSchemaSet, targetRegistries );
+    }
+    
+    
+    /**
+     * Lists the names of the schemas that depend on the schema name provided.
+     * 
+     * @param schemaName the name of the schema to find dependents for
+     * @return a set of schemas (String names) that depend on the schema
+     * @throws NamingException if there are problems searching the schema partition
+     */
+    public Set<String> listDependentSchemaNames( String schemaName ) throws NamingException
+    {
+        Set<String> dependees = new HashSet<String>();
+        Set<ServerSearchResult> results = dao.listSchemaDependents( schemaName );
+        
+        if ( results.isEmpty() )
+        {
+            return dependees;
+        }
+        
+        for ( ServerSearchResult sr: results )
+        {
+            EntryAttribute cn = sr.getServerEntry().get( cnAT );
+            dependees.add( cn.getString() );
+        }
+        
+        return dependees;
+    }
+
+    
+    /**
+     * Lists the names of the enabled schemas that depend on the schema name 
+     * provided.
+     * 
+     * @param schemaName the name of the schema to find dependents for
+     * @return a set of enabled schemas (String names) that depend on the schema
+     * @throws NamingException if there are problems searching the schema partition
+     */
+    public Set<String> listEnabledDependentSchemaNames( String schemaName ) throws NamingException
+    {
+        Set<String> dependees = new HashSet<String>();
+        Set<ServerSearchResult> results = dao.listEnabledSchemaDependents( schemaName );
+        
+        if ( results.isEmpty() )
+        {
+            return dependees;
+        }
+        
+        for ( ServerSearchResult sr: results )
+        {
+            EntryAttribute cn = sr.getServerEntry().get( cnAT );
+            dependees.add( cn.getString() );
+        }
+        
+        return dependees;
+    }
+
+    
+    public Map<String,Schema> getSchemas() throws NamingException
+    {
+        return dao.getSchemas();
+    }
+
+    
+    public Set<String> getSchemaNames() throws NamingException
+    {
+        return dao.getSchemaNames();
+    }
+    
+    
+    public Schema getSchema( String schemaName ) throws NamingException
+    {
+        return dao.getSchema( schemaName );
+    }
+
+
+    public Schema getSchema( String schemaName, Properties schemaProperties ) throws NamingException
+    {
+        return getSchema( schemaName );
+    }
+
+
+    public final void loadWithDependencies( Collection<Schema> schemas, Registries targetRegistries ) throws NamingException
+    {
+        HashMap<String,Schema> notLoaded = new HashMap<String,Schema>();
+
+        for ( Schema schema : schemas )
+        {
+            notLoaded.put( schema.getSchemaName(), schema );
+        }
+
+        Iterator<Schema> list = notLoaded.values().iterator();
+        while ( list.hasNext() )
+        {
+            Schema schema = list.next();
+            loadDepsFirst( schema, new Stack<String>(), notLoaded, schema, targetRegistries, null );
+            list = notLoaded.values().iterator();
+        }
+    }
+
+    /**
+     * {@link SchemaLoader#load(Schema, Registries, boolean)}
+     */
+    public final void load( Schema schema, Registries targetRegistries, boolean isDepLoad ) throws NamingException
+    {
+        // if we're loading a dependency and it has not been enabled on 
+        // disk then enable it on disk before we proceed to load it
+        if ( schema.isDisabled() && isDepLoad )
+        {
+            dao.enableSchema( schema.getSchemaName() );
+        }
+        
+        if ( targetRegistries.getLoadedSchemas().containsKey( schema.getSchemaName() ) )
+        {
+            LOG.debug( "schema {} already seems to be loaded", schema.getSchemaName() );
+            return;
+        }
+        
+        LOG.debug( "loading {} schema ...", schema.getSchemaName() );
+        
+        loadComparators( schema, targetRegistries );
+        loadNormalizers( schema, targetRegistries );
+        loadSyntaxCheckers( schema, targetRegistries );
+        loadSyntaxes( schema, targetRegistries );
+        loadMatchingRules( schema, targetRegistries );
+        loadAttributeTypes( schema, targetRegistries );
+        loadObjectClasses( schema, targetRegistries );
+        loadMatchingRuleUses( schema, targetRegistries );
+        loadDitContentRules( schema, targetRegistries );
+        loadNameForms( schema, targetRegistries );
+        
+        // order does matter here so some special trickery is needed
+        // we cannot load a DSR before the DSRs it depends on are loaded?
+        // TODO need ot confirm this ( or we must make the class for this and use deferred 
+        // resolution until everything is available?
+        
+        loadDitStructureRules( schema, targetRegistries );
+        
+        
+        notifyListenerOrRegistries( schema, targetRegistries );
+    }
+
+    
+    private void loadMatchingRuleUses( Schema schema, Registries targetRegistries )
+    {
+        // TODO Auto-generated method stub
+    }
+
+
+    private void loadDitStructureRules( Schema schema, Registries targetRegistries )
+    {
+        // TODO Auto-generated method stub
+    }
+
+
+    private void loadNameForms( Schema schema, Registries targetRegistries )
+    {
+        // TODO Auto-generated method stub
+    }
+
+
+    private void loadDitContentRules( Schema schema, Registries targetRegistries )
+    {
+        // TODO Auto-generated method stub
+    }
+
+
+    private void loadObjectClasses( Schema schema, Registries targetRegistries ) throws NamingException
+    {
+        /**
+         * Sometimes search may return child objectClasses before their superiors have
+         * been registered like with attributeTypes.  To prevent this from bombing out
+         * the loader we will defer the registration of elements until later.
+         */
+        LinkedList<ObjectClass> deferred = new LinkedList<ObjectClass>();
+
+        LdapDN dn = staticObjectClassesDNs.get( schema.getSchemaName() );
+        
+        if ( dn == null )
+        {
+            dn = new LdapDN( "ou=objectClasses,cn=" + schema.getSchemaName() + ",ou=schema" );
+            dn.normalize( atRegistry.getNormalizerMapping() );
+            staticObjectClassesDNs.put( schema.getSchemaName(), dn );
+        }
+        
+        if ( ! partition.hasEntry( new EntryOperationContext( registries, dn ) ) )
+        {
+            return;
+        }
+        
+        LOG.debug( "{} schema: loading objectClasses", schema.getSchemaName() );
+        
+        NamingEnumeration<ServerSearchResult> list = partition.list( new ListOperationContext( registries, dn ) );
+        
+        while ( list.hasMore() )
+        {
+            ServerSearchResult result = list.next();
+            LdapDN resultDN = result.getDn();
+            resultDN.normalize( atRegistry.getNormalizerMapping() );
+            ServerEntry attrs = lookupPartition( resultDN );
+            ObjectClass oc = factory.getObjectClass( attrs, targetRegistries, schema.getSchemaName() );
+            
+            try
+            {
+                targetRegistries.getObjectClassRegistry().register( oc );
+            }
+            catch ( NamingException ne )
+            {
+                deferred.add( oc );
+            }
+        }
+        
+        LOG.debug( "Deferred queue size = {}", deferred.size() );
+        if ( LOG.isDebugEnabled() )
+        {
+            StringBuffer buf = new StringBuffer();
+            buf.append( "Deferred queue contains: " );
+            
+            for ( ObjectClass extra : deferred )
+            {
+                buf.append( extra.getName() );
+                buf.append( '[' );
+                buf.append( extra.getOid() );
+                buf.append( "]" );
+                buf.append( "\n" );
+            }
+        }
+        
+        int lastCount = deferred.size();
+        while ( ! deferred.isEmpty() )
+        {
+            LOG.debug( "Deferred queue size = {}", deferred.size() );
+            ObjectClass oc = deferred.removeFirst();
+            NamingException lastException = null;
+            
+            try
+            {
+                targetRegistries.getObjectClassRegistry().register( oc );
+            }
+            catch ( NamingException ne )
+            {
+                deferred.addLast( oc );
+                lastException = ne;
+            }
+            
+            // if we shrank the deferred list we're doing good and can continue
+            if ( deferred.size() < lastCount )
+            {
+                lastCount = deferred.size();
+            }
+            else
+            {
+                StringBuffer buf = new StringBuffer();
+                buf.append( "A cycle must exist somewhere within the objectClasses of the " );
+                buf.append( schema.getSchemaName() );
+                buf.append( " schema.  We cannot seem to register the following objectClasses:\n" );
+                
+                for ( ObjectClass extra : deferred )
+                {
+                    buf.append( extra.getName() );
+                    buf.append( '[' );
+                    buf.append( extra.getOid() );
+                    buf.append( "]" );
+                    buf.append( "\n" );
+                }
+                
+                NamingException ne = new NamingException( buf.toString() );
+                ne.setRootCause( lastException );
+            }
+        }
+    }
+
+
+    private void loadAttributeTypes( Schema schema, Registries targetRegistries ) throws NamingException
+    {
+        LinkedList<AttributeType> deferred = new LinkedList<AttributeType>();
+        
+        LdapDN dn = staticAttributeTypeDNs.get( schema.getSchemaName() );
+        
+        if ( dn == null )
+        {
+            dn = new LdapDN( "ou=attributeTypes,cn=" + schema.getSchemaName() + ",ou=schema" );
+            dn.normalize( atRegistry.getNormalizerMapping() );
+            staticAttributeTypeDNs.put( schema.getSchemaName(), dn );
+        }
+        
+        if ( ! partition.hasEntry( new EntryOperationContext( registries, dn ) ) )
+        {
+            return;
+        }
+        
+        LOG.debug( "{} schema: loading attributeTypes", schema.getSchemaName() );
+        
+        NamingEnumeration<ServerSearchResult> list = partition.list( new ListOperationContext( registries, dn ) );
+        
+        while ( list.hasMore() )
+        {
+            ServerSearchResult result = list.next();
+            LdapDN resultDN = result.getDn();
+            resultDN.normalize( atRegistry.getNormalizerMapping() );
+            ServerEntry attrs = lookupPartition( resultDN );
+            AttributeType at = factory.getAttributeType( attrs, targetRegistries, schema.getSchemaName() );
+            try
+            {
+                targetRegistries.getAttributeTypeRegistry().register( at );
+            }
+            catch ( NamingException ne )
+            {
+                deferred.add( at );
+            }
+        }
+
+        LOG.debug( "Deferred queue size = {}", deferred.size() );
+        if ( LOG.isDebugEnabled() )
+        {
+            StringBuffer buf = new StringBuffer();
+            buf.append( "Deferred queue contains: " );
+            
+            for ( AttributeType extra : deferred )
+            {
+                buf.append( extra.getName() );
+                buf.append( '[' );
+                buf.append( extra.getOid() );
+                buf.append( "]" );
+                buf.append( "\n" );
+            }
+        }
+        
+        int lastCount = deferred.size();
+        while ( ! deferred.isEmpty() )
+        {
+            LOG.debug( "Deferred queue size = {}", deferred.size() );
+            AttributeType at = deferred.removeFirst();
+            NamingException lastException = null;
+            
+            try
+            {
+                targetRegistries.getAttributeTypeRegistry().register( at );
+            }
+            catch ( NamingException ne )
+            {
+                deferred.addLast( at );
+                lastException = ne;
+            }
+            
+            // if we shrank the deferred list we're doing good and can continue
+            if ( deferred.size() < lastCount )
+            {
+                lastCount = deferred.size();
+            }
+            else
+            {
+                StringBuffer buf = new StringBuffer();
+                buf.append( "A cycle must exist somewhere within the attributeTypes of the " );
+                buf.append( schema.getSchemaName() );
+                buf.append( " schema.  We cannot seem to register the following attributeTypes:\n" );
+                
+                for ( AttributeType extra : deferred )
+                {
+                    buf.append( extra.getName() );
+                    buf.append( '[' );
+                    buf.append( extra.getOid() );
+                    buf.append( "]" );
+                    buf.append( "\n" );
+                }
+                
+                NamingException ne = new NamingException( buf.toString() );
+                ne.setRootCause( lastException );
+            }
+        }
+    }
+
+
+    private void loadMatchingRules( Schema schema, Registries targetRegistries ) throws NamingException
+    {
+        LdapDN dn = staticMatchingRulesDNs.get( schema.getSchemaName() );
+        
+        if ( dn == null )
+        {
+            dn = new LdapDN( "ou=matchingRules,cn=" + schema.getSchemaName() + ",ou=schema" );
+            dn.normalize( atRegistry.getNormalizerMapping() );
+            staticMatchingRulesDNs.put( schema.getSchemaName(), dn );
+        }
+        
+        if ( ! partition.hasEntry( new EntryOperationContext( registries, dn ) ) )
+        {
+            return;
+        }
+        
+        LOG.debug( "{} schema: loading matchingRules", schema.getSchemaName() );
+        
+        NamingEnumeration<ServerSearchResult> list = partition.list( new ListOperationContext( registries, dn ) );
+        
+        while ( list.hasMore() )
+        {
+            ServerSearchResult result = list.next();
+            LdapDN resultDN = result.getDn();
+            resultDN.normalize( atRegistry.getNormalizerMapping() );
+            ServerEntry attrs = lookupPartition( resultDN );
+            MatchingRule mrule = factory.getMatchingRule( attrs, targetRegistries, schema.getSchemaName() );
+            targetRegistries.getMatchingRuleRegistry().register( mrule );
+
+        }
+    }
+
+
+    private void loadSyntaxes( Schema schema, Registries targetRegistries ) throws NamingException
+    {
+        LdapDN dn = staticSyntaxesDNs.get( schema.getSchemaName() );
+        
+        if ( dn == null )
+        {
+            dn = new LdapDN( "ou=syntaxes,cn=" + schema.getSchemaName() + ",ou=schema" );
+            dn.normalize( atRegistry.getNormalizerMapping() );
+            staticSyntaxesDNs.put( schema.getSchemaName(), dn );
+        }
+        
+        if ( ! partition.hasEntry( new EntryOperationContext( registries, dn ) ) )
+        {
+            return;
+        }
+        
+        LOG.debug( "{} schema: loading syntaxes", schema.getSchemaName() );
+        
+        NamingEnumeration<ServerSearchResult> list = partition.list( new ListOperationContext( registries, dn ) );
+        
+        while ( list.hasMore() )
+        {
+            ServerSearchResult result = list.next();
+            LdapDN resultDN = result.getDn();
+            resultDN.normalize( atRegistry.getNormalizerMapping() );
+            ServerEntry attrs = lookupPartition( resultDN );
+            Syntax syntax = factory.getSyntax( attrs, targetRegistries, schema.getSchemaName() );
+            targetRegistries.getSyntaxRegistry().register( syntax );
+        }
+    }
+
+
+    private void loadSyntaxCheckers( Schema schema, Registries targetRegistries ) throws NamingException
+    {
+        LdapDN dn = staticSyntaxCheckersDNs.get( schema.getSchemaName() );
+        
+        if ( dn == null )
+        {
+            dn = new LdapDN( "ou=syntaxCheckers,cn=" + schema.getSchemaName() + ",ou=schema" );
+            dn.normalize( atRegistry.getNormalizerMapping() );
+            staticSyntaxCheckersDNs.put( schema.getSchemaName(), dn );
+        }
+        
+        if ( ! partition.hasEntry( new EntryOperationContext( registries, dn ) ) )
+        {
+            return;
+        }
+        
+        LOG.debug( "{} schema: loading syntaxCheckers", schema.getSchemaName() );
+        
+        NamingEnumeration<ServerSearchResult> list = partition.list( new ListOperationContext( registries, dn ) );
+        
+        while ( list.hasMore() )
+        {
+            ServerSearchResult result = list.next();
+            LdapDN resultDN = result.getDn();
+            resultDN.normalize( atRegistry.getNormalizerMapping() );
+            ServerEntry attrs = lookupPartition( resultDN );
+            SyntaxChecker sc = factory.getSyntaxChecker( attrs, targetRegistries );
+            SyntaxCheckerDescription syntaxCheckerDescription = 
+                getSyntaxCheckerDescription( schema.getSchemaName(), attrs );
+            targetRegistries.getSyntaxCheckerRegistry().register( syntaxCheckerDescription, sc );
+        }
+    }
+
+
+    private void loadNormalizers( Schema schema, Registries targetRegistries ) throws NamingException
+    {
+        LdapDN dn = staticNormalizersDNs.get( schema.getSchemaName() );
+        
+        if ( dn == null )
+        {
+            dn = new LdapDN( "ou=normalizers,cn=" + schema.getSchemaName() + ",ou=schema" );
+            dn.normalize( atRegistry.getNormalizerMapping() );
+            staticNormalizersDNs.put( schema.getSchemaName(), dn );
+        }
+        
+        if ( ! partition.hasEntry( new EntryOperationContext( registries, dn ) ) )
+        {
+            return;
+        }
+        
+        LOG.debug( "{} schema: loading normalizers", schema.getSchemaName() );
+        
+        NamingEnumeration<ServerSearchResult> list = partition.list( new ListOperationContext( registries, dn ) );
+        
+        while ( list.hasMore() )
+        {
+            ServerSearchResult result = list.next();
+            LdapDN resultDN = result.getDn();
+            resultDN.normalize( atRegistry.getNormalizerMapping() );
+            ServerEntry attrs = lookupPartition( resultDN );
+            Normalizer normalizer = factory.getNormalizer( attrs, targetRegistries );
+            NormalizerDescription normalizerDescription = getNormalizerDescription( schema.getSchemaName(), attrs );
+            targetRegistries.getNormalizerRegistry().register( normalizerDescription, normalizer );
+        }
+    }
+
+
+    private String getOid( ServerEntry entry ) throws NamingException
+    {
+        EntryAttribute oid = entry.get( mOidAT );
+        
+        if ( oid == null )
+        {
+            return null;
+        }
+        
+        return oid.getString();
+    }
+
+    
+    private NormalizerDescription getNormalizerDescription( String schemaName, ServerEntry entry ) throws NamingException
+    {
+        NormalizerDescription description = new NormalizerDescription();
+        description.setNumericOid( getOid( entry ) );
+        List<String> values = new ArrayList<String>();
+        values.add( schemaName );
+        description.addExtension( MetaSchemaConstants.X_SCHEMA, values );
+        description.setFqcn( entry.get( fqcnAT ).getString() );
+        
+        EntryAttribute desc = entry.get( descAT );
+        if ( desc != null && desc.size() > 0 )
+        {
+            description.setDescription( desc.getString() );
+        }
+        
+        EntryAttribute bytecode = entry.get( byteCodeAT );
+        
+        if ( bytecode != null && bytecode.size() > 0 )
+        {
+            byte[] bytes = bytecode.getBytes();
+            description.setBytecode( new String( Base64.encode( bytes ) ) );
+        }
+
+        return description;
+    }
+
+    
+    private ServerEntry lookupPartition( LdapDN dn ) throws NamingException
+    {
+        return partition.lookup( new LookupOperationContext( registries, dn ) );
+    }
+    
+    private void loadComparators( Schema schema, Registries targetRegistries ) throws NamingException
+    {
+        LdapDN dn = staticComparatorsDNs.get( schema.getSchemaName() );
+        
+        if ( dn == null )
+        {
+            dn = new LdapDN( "ou=comparators,cn=" + schema.getSchemaName() + ",ou=schema" );
+            dn.normalize( atRegistry.getNormalizerMapping() );
+            staticComparatorsDNs.put( schema.getSchemaName(), dn );
+        }
+
+        if ( ! partition.hasEntry( new EntryOperationContext( registries, dn ) ) )
+        {
+            return;
+        }
+        
+        LOG.debug( "{} schema: loading comparators", schema.getSchemaName() );
+        
+        NamingEnumeration<ServerSearchResult> list = partition.list( new ListOperationContext( registries, dn ) );
+        
+        while ( list.hasMore() )
+        {
+            ServerSearchResult result = list.next();
+            LdapDN resultDN = result.getDn();
+            resultDN.normalize( atRegistry.getNormalizerMapping() );
+            ServerEntry attrs = lookupPartition( resultDN );
+            Comparator comparator = factory.getComparator( attrs, targetRegistries );
+            ComparatorDescription comparatorDescription = getComparatorDescription( schema.getSchemaName(), attrs );
+            targetRegistries.getComparatorRegistry().register( comparatorDescription, comparator );
+        }
+    }
+
+
+    private ComparatorDescription getComparatorDescription( String schemaName, ServerEntry entry ) throws NamingException
+    {
+        ComparatorDescription description = new ComparatorDescription();
+        description.setNumericOid( getOid( entry ) );
+        List<String> values = new ArrayList<String>();
+        values.add( schemaName );
+        description.addExtension( MetaSchemaConstants.X_SCHEMA, values );
+        description.setFqcn( entry.get( fqcnAT ).getString() );
+        
+        EntryAttribute desc = entry.get( descAT );
+        
+        if ( desc != null && desc.size() > 0 )
+        {
+            description.setDescription( desc.getString() );
+        }
+        
+        EntryAttribute bytecode = entry.get( byteCodeAT );
+        
+        if ( bytecode != null && bytecode.size() > 0 )
+        {
+            byte[] bytes = bytecode.getBytes();
+            description.setBytecode( new String( Base64.encode( bytes ) ) );
+        }
+
+        return description;
+    }
+
+    
+    private SyntaxCheckerDescription getSyntaxCheckerDescription( String schemaName, ServerEntry entry ) 
+        throws NamingException
+    {
+        SyntaxCheckerDescription description = new SyntaxCheckerDescription();
+        description.setNumericOid( getOid( entry ) );
+        List<String> values = new ArrayList<String>();
+        values.add( schemaName );
+        description.addExtension( MetaSchemaConstants.X_SCHEMA, values );
+        description.setFqcn( entry.get( fqcnAT ).getString() );
+        
+        EntryAttribute desc = entry.get( descAT );
+        
+        if ( desc != null && desc.size() > 0 )
+        {
+            description.setDescription( desc.getString() );
+        }
+        
+        EntryAttribute bytecode = entry.get( byteCodeAT );
+        
+        if ( bytecode != null && bytecode.size() > 0 )
+        {
+            byte[] bytes = bytecode.getBytes();
+            description.setBytecode( new String( Base64.encode( bytes ) ) );
+        }
+
+        return description;
+    }
+
+    
+    public void loadWithDependencies( Schema schema, Registries registries ) throws NamingException
+    {
+        HashMap<String,Schema> notLoaded = new HashMap<String,Schema>();
+        notLoaded.put( schema.getSchemaName(), schema );                        
+        Properties props = new Properties();
+        loadDepsFirst( schema, new Stack<String>(), notLoaded, schema, registries, props );
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/SchemaChangeHandler.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/SchemaChangeHandler.java
new file mode 100644
index 0000000..345c003
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/SchemaChangeHandler.java
@@ -0,0 +1,58 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.schema;
+
+
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.shared.ldap.entry.Modification;
+import org.apache.directory.shared.ldap.entry.ModificationOperation;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.name.Rdn;
+
+import javax.naming.NamingException;
+import java.util.List;
+
+
+/**
+ * A common interface used by schema change handlers which react to 
+ * changes performed on schema entities.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public interface SchemaChangeHandler
+{
+    void add( LdapDN name, ServerEntry entry ) throws NamingException;
+    
+    void delete( LdapDN name, ServerEntry entry, boolean cascaded ) throws NamingException;
+    
+    void rename( LdapDN name, ServerEntry entry, Rdn newRdn, boolean cascaded ) throws NamingException;
+    
+    void modify( LdapDN name, ModificationOperation modOp, ServerEntry mods, ServerEntry entry, ServerEntry targetEntry, boolean cascaded ) 
+        throws NamingException;
+    
+    void modify( LdapDN name, List<Modification> mods, ServerEntry entry, ServerEntry targetEntry, boolean cascaded )
+        throws NamingException;
+    
+    void move( LdapDN oriChildName, LdapDN newParentName, Rdn newRn, boolean deleteOldRn, ServerEntry entry,
+        boolean cascaded ) throws NamingException;
+    
+    void replace( LdapDN oriChildName, LdapDN newParentName, ServerEntry entry, boolean cascaded ) throws NamingException;
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/SchemaChecker.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/SchemaChecker.java
new file mode 100644
index 0000000..1b9e7c6
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/SchemaChecker.java
@@ -0,0 +1,735 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.schema;
+
+
+import org.apache.directory.server.core.entry.ServerAttribute;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.schema.registries.ObjectClassRegistry;
+import org.apache.directory.server.schema.registries.OidRegistry;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.entry.ModificationOperation;
+import org.apache.directory.shared.ldap.entry.Value;
+import org.apache.directory.shared.ldap.exception.LdapSchemaViolationException;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.schema.ObjectClass;
+import org.apache.directory.shared.ldap.schema.ObjectClassTypeEnum;
+import org.apache.directory.shared.ldap.util.NamespaceTools;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.naming.NamingException;
+import javax.naming.Name;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.Attribute;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.HashSet;
+
+
+/**
+ * Performs schema checks on behalf of the SchemaInterceptor.
+ *
+ * TODO: we really need to refactor this code since there's much duplication
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class SchemaChecker
+{
+    /** the SLF4J logger for this class */
+    private static Logger log = LoggerFactory.getLogger( SchemaChecker.class );
+
+
+    /**
+     * Makes sure modify operations do not leave the entry without a STRUCTURAL
+     * objectClass.  At least one STRUCTURAL objectClass must be specified for
+     * the entry after modifications take effect.
+     *
+     * @param registry the objectClass registry to lookup ObjectClass specifications
+     * @param name the name of the entry being modified
+     * @param mod the type of modification operation being performed (should be
+     * REMOVE_ATTRIBUTE)
+     * @param attribute the attribute being modified
+     * @throws NamingException if modify operations leave the entry inconsistent
+     * without a STRUCTURAL objectClass
+     */
+    public static void preventStructuralClassRemovalOnModifyReplace( ObjectClassRegistry registry, LdapDN name, ModificationOperation mod,
+        ServerAttribute attribute ) throws NamingException
+    {
+        if ( mod != ModificationOperation.REPLACE_ATTRIBUTE )
+        {
+            return;
+        }
+
+        if ( !SchemaConstants.OBJECT_CLASS_AT.equalsIgnoreCase( attribute.getUpId() ) )
+        {
+            return;
+        }
+
+        // whoever issued the modify operation is insane they want to delete
+        // all the objectClass values in which case we must throw an exception
+        if ( attribute.size() == 0 )
+        {
+            String msg = "Modify operation leaves no structural objectClass for entry " + name;
+            
+            if ( log.isInfoEnabled() )
+            {
+                log.info( msg + ".  Raising LdapSchemaViolationException." );
+            }
+            
+            throw new LdapSchemaViolationException( msg, ResultCodeEnum.OBJECT_CLASS_MODS_PROHIBITED );
+        }
+
+        // check that there is at least one structural objectClass in the replacement set
+        for ( Value<?> value:attribute )
+        {
+            ObjectClass ocType = registry.lookup( ( String ) value.get() );
+
+            if ( ocType.getType() == ObjectClassTypeEnum.STRUCTURAL )
+            {
+                return;
+            }
+        }
+
+        // no structural object classes exist for the entry in the replacement
+        // set for the objectClass attribute so we need to complain about that
+        String msg = "Modify operation leaves no structural objectClass for entry " + name;
+        if ( log.isInfoEnabled() )
+        {
+            log.info( msg + ".  Raising LdapSchemaViolationException." );
+        }
+        throw new LdapSchemaViolationException( msg, ResultCodeEnum.OBJECT_CLASS_MODS_PROHIBITED );
+    }
+
+
+    /**
+     * Makes sure modify operations do not leave the entry without a STRUCTURAL
+     * objectClass.  At least one STRUCTURAL objectClass must be specified for
+     * the entry after modifications take effect.
+     *
+     * @param registry the objectClass registry to lookup ObjectClass specifications
+     * @param name the name of the entry being modified
+     * @param mod the type of modification operation being performed (should be
+     * REMOVE_ATTRIBUTE)
+     * @param attributes the attributes being modified
+     * @throws NamingException if modify operations leave the entry inconsistent
+     * without a STRUCTURAL objectClass
+     */
+    public static void preventStructuralClassRemovalOnModifyReplace( ObjectClassRegistry registry, Name name, int mod,
+        Attributes attributes ) throws NamingException
+    {
+        if ( mod != DirContext.REPLACE_ATTRIBUTE )
+        {
+            return;
+        }
+
+        Attribute objectClass = attributes.get( SchemaConstants.OBJECT_CLASS_AT );
+        if ( objectClass == null )
+        {
+            return;
+        }
+
+        // whoever issued the modify operation is insane they want to delete
+        // all the objectClass values in which case we must throw an exception
+        if ( objectClass.size() == 0 )
+        {
+            String msg = "Modify operation leaves no structural objectClass for entry " + name;
+            if ( log.isInfoEnabled() )
+            {
+                log.info( msg + ".  Raising LdapSchemaViolationException." );
+            }
+            throw new LdapSchemaViolationException( msg, ResultCodeEnum.OBJECT_CLASS_MODS_PROHIBITED );
+        }
+
+        // check that there is at least one structural objectClass in the replacement set
+        for ( int ii = 0; ii < objectClass.size(); ii++ )
+        {
+            ObjectClass ocType = registry.lookup( ( String ) objectClass.get( ii ) );
+            if ( ocType.getType() == ObjectClassTypeEnum.STRUCTURAL )
+            {
+                return;
+            }
+        }
+
+        // no structural object classes exist for the entry in the replacement
+        // set for the objectClass attribute so we need to complain about that
+        String msg = "Modify operation leaves no structural objectClass for entry " + name;
+        if ( log.isInfoEnabled() )
+        {
+            log.info( msg + ".  Raising LdapSchemaViolationException." );
+        }
+        throw new LdapSchemaViolationException( msg, ResultCodeEnum.OBJECT_CLASS_MODS_PROHIBITED );
+    }
+
+
+    /**
+     * Makes sure modify operations do not leave the entry without a STRUCTURAL
+     * objectClass.  At least one STRUCTURAL objectClass must be specified for
+     * the entry after modifications take effect.
+     *
+     * @param registry the objectClass registry to lookup ObjectClass specifications
+     * @param name the name of the entry being modified
+     * @param mod the type of modification operation being performed (should be
+     * REMOVE_ATTRIBUTE)
+     * @param attribute the attribute being modified
+     * @param entryObjectClasses the entry being modified
+     * @throws NamingException if modify operations leave the entry inconsistent
+     * without a STRUCTURAL objectClass
+     */
+    public static void preventStructuralClassRemovalOnModifyRemove( ObjectClassRegistry registry, LdapDN name, ModificationOperation mod,
+        EntryAttribute attribute, EntryAttribute entryObjectClasses ) throws NamingException
+    {
+        if ( mod != ModificationOperation.REMOVE_ATTRIBUTE )
+        {
+            return;
+        }
+
+        if ( !((ServerAttribute)attribute).instanceOf( SchemaConstants.OBJECT_CLASS_AT ) )
+        {
+            return;
+        }
+        
+        // check if there is any attribute value as "".
+        // if there is remove it so that it will be considered as not even provided.
+        List<Value<?>> removed = new ArrayList<Value<?>>();
+        
+        // Fist gather the value to remove
+        for ( Value<?> value:attribute )
+        {
+            if ( ((String)value.get()).length() == 0 )
+            {
+                removed.add( value );
+            }
+        }
+        
+        // Now remove the values from the attribute
+        for ( Value<?> value:removed )
+        {
+            attribute.remove( value );
+        }
+
+        // whoever issued the modify operation is insane they want to delete
+        // all the objectClass values in which case we must throw an exception
+        if ( attribute.size() == 0 )
+        {
+            String msg = "Modify operation leaves no structural objectClass for entry " + name;
+            
+            if ( log.isInfoEnabled() )
+            {
+                log.info( msg + ".  Raising LdapSchemaViolationException." );
+            }
+            
+            throw new LdapSchemaViolationException( msg, ResultCodeEnum.OBJECT_CLASS_MODS_PROHIBITED );
+        }
+
+        // remove all the objectClass attribute values from a cloned copy and then
+        // we can analyze what remains in this attribute to make sure a structural
+        // objectClass is present for the entry
+
+        ServerAttribute cloned = ( ServerAttribute ) entryObjectClasses.clone();
+        
+        for ( Value<?> value:attribute )
+        {
+            cloned.remove( value );
+        }
+
+        // check resultant set of objectClass values for a structural objectClass
+        for ( Value<?> objectClass:cloned )
+        {
+            ObjectClass oc = registry.lookup( (String)objectClass.get() );
+            
+            if ( oc.getType() == ObjectClassTypeEnum.STRUCTURAL )
+            {
+                return;
+            }
+        }
+
+        // no structural object classes exist for the entry after the modifications
+        // to the objectClass attribute so we need to complain about that
+        String msg = "Modify operation leaves no structural objectClass for entry " + name;
+
+        if ( log.isInfoEnabled() )
+        {
+            log.info( msg + ".  Raising LdapSchemaViolationException." );
+        }
+        
+        throw new LdapSchemaViolationException( msg, ResultCodeEnum.OBJECT_CLASS_MODS_PROHIBITED );
+    }
+
+
+    /**
+     * Makes sure modify operations do not leave the entry without a STRUCTURAL
+     * objectClass.  At least one STRUCTURAL objectClass must be specified for
+     * the entry after modifications take effect.
+     *
+     * @param registry the objectClass registry to lookup ObjectClass specifications
+     * @param name the name of the entry being modified
+     * @param mod the type of modification operation being performed (should be
+     * REMOVE_ATTRIBUTE)
+     * @param attributes the attributes being modified
+     * @param entryObjectClasses the entry being modified
+     * @throws NamingException if modify operations leave the entry inconsistent
+     * without a STRUCTURAL objectClass
+     *
+    public static void preventStructuralClassRemovalOnModifyRemove( ObjectClassRegistry registry, Name name, int mod,
+        Attributes attributes, Attribute entryObjectClasses ) throws NamingException
+    {
+        if ( mod != DirContext.REMOVE_ATTRIBUTE )
+        {
+            return;
+        }
+
+        Attribute objectClass = attributes.get( SchemaConstants.OBJECT_CLASS_AT );
+        if ( objectClass == null )
+        {
+            return;
+        }
+        
+        // check if there is any attribute value as "".
+        // if there is remove it so that it will be considered as not even provided.
+        for( int ii = 0; ii < objectClass.size(); ii++ )
+        {
+            Object value = objectClass.get( ii );
+            if ( "".equals( value ) )
+            {
+                objectClass.remove( ii );
+            }
+        }
+
+        // whoever issued the modify operation is insane they want to delete
+        // all the objectClass values in which case we must throw an exception
+        if ( objectClass.size() == 0 )
+        {
+            String msg = "Modify operation leaves no structural objectClass for entry " + name;
+            if ( log.isInfoEnabled() )
+            {
+                log.info( msg + ".  Raising LdapSchemaViolationException." );
+            }
+            throw new LdapSchemaViolationException( msg, ResultCodeEnum.OBJECT_CLASS_MODS_PROHIBITED );
+        }
+
+        // remove all the objectClass attribute values from a cloned copy and then
+        // we can analyze what remains in this attribute to make sure a structural
+        // objectClass is present for the entry
+
+        Attribute cloned = ( Attribute ) entryObjectClasses.clone();
+        for ( int ii = 0; ii < objectClass.size(); ii++ )
+        {
+            cloned.remove( objectClass.get( ii ) );
+        }
+
+        // check resultant set of objectClass values for a structural objectClass
+        for ( int ii = 0; ii < cloned.size(); ii++ )
+        {
+            ObjectClass ocType = registry.lookup( ( String ) cloned.get( ii ) );
+            if ( ocType.getType() == ObjectClassTypeEnum.STRUCTURAL )
+            {
+                return;
+            }
+        }
+
+        // no structural object classes exist for the entry after the modifications
+        // to the objectClass attribute so we need to complain about that
+        String msg = "Modify operation leaves no structural objectClass for entry " + name;
+        if ( log.isInfoEnabled() )
+        {
+            log.info( msg + ".  Raising LdapSchemaViolationException." );
+        }
+        throw new LdapSchemaViolationException( msg, ResultCodeEnum.OBJECT_CLASS_MODS_PROHIBITED );
+    }
+    */
+
+    /**
+     * Makes sure a modify operation does not replace RDN attributes or their value.
+     * According to section 4.6 of <a href="http://rfc.net/rfc2251.html#s4.6.">
+     * RFC 2251</a> a modify operation cannot be used to remove Rdn attributes as
+     * seen below:
+     * <p/>
+     * <pre>
+     *     The Modify Operation cannot be used to remove from an entry any of
+     *     its distinguished values, those values which form the entry's
+     *     relative distinguished name.  An attempt to do so will result in the
+     *     server returning the error notAllowedOnRDN.  The Modify DN Operation
+     *     described in section 4.9 is used to rename an entry.
+     * </pre>
+     *
+     * @param name the distinguished name of the attribute being modified
+     * @param mod the modification operation being performed (should be REPLACE_ATTRIBUTE )
+     * @param attribute the attribute being modified
+     * @param oidRegistry
+     * @throws NamingException if the modify operation is removing an Rdn attribute
+     */
+    public static void preventRdnChangeOnModifyReplace( LdapDN name, ModificationOperation mod, ServerAttribute attribute, OidRegistry oidRegistry )
+        throws NamingException
+    {
+        if ( mod != ModificationOperation.REPLACE_ATTRIBUTE )
+        {
+            return;
+        }
+
+        Set<String> rdnAttributes = getRdnAttributes( name );
+        String id = oidRegistry.getOid( attribute.getUpId() );
+
+        if ( !rdnAttributes.contains( id ) )
+        {
+            return;
+        }
+
+        // if the attribute values to delete are not specified then all values
+        // for the attribute are to be deleted in which case we must just throw
+        // a schema violation exception with the notAllowedOnRdn result code
+        if ( attribute.size() == 0 )
+        {
+            String msg = "Modify operation attempts to delete RDN attribute ";
+            msg += id + " on entry " + name + " violates schema constraints";
+
+            if ( log.isInfoEnabled() )
+            {
+                log.info( msg + ". SchemaChecker is throwing a schema violation exception." );
+            }
+            throw new LdapSchemaViolationException( msg, ResultCodeEnum.NOT_ALLOWED_ON_RDN );
+        }
+
+        // from here on the modify operation replaces specific values
+        // of the Rdn attribute so we must check to make sure all the old
+        // rdn attribute values are present in the replacement set
+        String rdnValue = getRdnValue( id, name, oidRegistry );
+        for ( int ii = 0; ii < attribute.size(); ii++ )
+        {
+            // if the old rdn value is not in the rdn attribute then
+            // we must complain with a schema violation
+            if ( !attribute.contains( rdnValue ) )
+            {
+                String msg = "Modify operation attempts to delete RDN attribute values in use for ";
+                msg += id + " on entry " + name + " and violates schema constraints";
+
+                if ( log.isInfoEnabled() )
+                {
+                    log.info( msg + ". SchemaChecker is throwing a schema violation exception." );
+                }
+                throw new LdapSchemaViolationException( msg, ResultCodeEnum.NOT_ALLOWED_ON_RDN );
+            }
+        }
+    }
+
+
+    /**
+     * Makes sure a modify operation does not replace RDN attributes or their value.
+     * According to section 4.6 of <a href="http://rfc.net/rfc2251.html#s4.6.">
+     * RFC 2251</a> a modify operation cannot be used to remove Rdn attributes as
+     * seen below:
+     * <p/>
+     * <pre>
+     *     The Modify Operation cannot be used to remove from an entry any of
+     *     its distinguished values, those values which form the entry's
+     *     relative distinguished name.  An attempt to do so will result in the
+     *     server returning the error notAllowedOnRDN.  The Modify DN Operation
+     *     described in section 4.9 is used to rename an entry.
+     * </pre>
+     *
+     * @param name the distinguished name of the attribute being modified
+     * @param mod the modification operation being performed (should be REPLACE_ATTRIBUTE )
+     * @param entry
+     * @param oidRegistry
+     * @throws NamingException if the modify operation is removing an Rdn attribute
+     */
+    public static void preventRdnChangeOnModifyReplace( LdapDN name, int mod, ServerEntry entry, OidRegistry oidRegistry )
+        throws NamingException
+    {
+        if ( mod != DirContext.REPLACE_ATTRIBUTE )
+        {
+            return;
+        }
+
+        Set<String> rdnAttributes = getRdnAttributes( name );
+        
+        for ( AttributeType attributeType:entry.getAttributeTypes() )
+        {
+            String id = attributeType.getName();
+
+            if ( rdnAttributes.contains( id ) )
+            {
+                EntryAttribute rdnAttr = entry.get( id );
+
+                // if the attribute values to delete are not specified then all values
+                // for the attribute are to be deleted in which case we must just throw
+                // a schema violation exception with the notAllowedOnRdn result code
+                if ( rdnAttr.size() == 0 )
+                {
+                    String msg = "Modify operation attempts to delete RDN attribute ";
+                    msg += id + " on entry " + name + " violates schema constraints";
+
+                    if ( log.isInfoEnabled() )
+                    {
+                        log.info( msg + ". SchemaChecker is throwing a schema violation exception." );
+                    }
+                    
+                    throw new LdapSchemaViolationException( msg, ResultCodeEnum.NOT_ALLOWED_ON_RDN );
+                }
+
+                // from here on the modify operation replaces specific values
+                // of the Rdn attribute so we must check to make sure all the old
+                // rdn attribute values are present in the replacement set
+                String rdnValue = getRdnValue( id, name, oidRegistry );
+
+                // if the old rdn value is not in the rdn attribute then
+                // we must complain with a schema violation
+                if ( !rdnAttr.contains( rdnValue ) )
+                {
+                    String msg = "Modify operation attempts to delete RDN attribute values in use for ";
+                    msg += id + " on entry " + name + " and violates schema constraints";
+
+                    if ( log.isInfoEnabled() )
+                    {
+                        log.info( msg + ". SchemaChecker is throwing a schema violation exception." );
+                    }
+                    
+                    throw new LdapSchemaViolationException( msg, ResultCodeEnum.NOT_ALLOWED_ON_RDN );
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Makes sure a modify operation does not delete RDN attributes or their value.
+     * According to section 4.6 of <a href="http://rfc.net/rfc2251.html#s4.6.">
+     * RFC 2251</a> a modify operation cannot be used to remove Rdn attributes as
+     * seen below:
+     * <p/>
+     * <pre>
+     *     The Modify Operation cannot be used to remove from an entry any of
+     *     its distinguished values, those values which form the entry's
+     *     relative distinguished name.  An attempt to do so will result in the
+     *     server returning the error notAllowedOnRDN.  The Modify DN Operation
+     *     described in section 4.9 is used to rename an entry.
+     * </pre>
+     *
+     * @param name the distinguished name of the attribute being modified
+     * @param mod the modification operation being performed (should be REMOVE_ATTRIBUTE )
+     * @param attribute the attribute being modified
+     * @throws NamingException if the modify operation is removing an Rdn attribute
+     */
+    public static void preventRdnChangeOnModifyRemove( LdapDN name, ModificationOperation mod, ServerAttribute attribute, 
+        OidRegistry oidRegistry ) throws NamingException
+    {
+        if ( mod != ModificationOperation.REMOVE_ATTRIBUTE )
+        {
+            return;
+        }
+
+        Set<String> rdnAttributes = getRdnAttributes( name );
+        String id = attribute.getUpId();
+
+        if ( !rdnAttributes.contains( oidRegistry.getOid( id ) ) )
+        {
+            return;
+        }
+
+        // if the attribute values to delete are not specified then all values
+        // for the attribute are to be deleted in which case we must just throw
+        // a schema violation exception with the notAllowedOnRdn result code
+        if ( attribute.size() == 0 )
+        {
+            String msg = "Modify operation attempts to delete RDN attribute ";
+            msg += id + " on entry " + name + " violates schema constraints";
+
+            if ( log.isInfoEnabled() )
+            {
+                log.info( msg + ". SchemaChecker is throwing a schema violation exception." );
+            }
+            
+            throw new LdapSchemaViolationException( msg, ResultCodeEnum.NOT_ALLOWED_ON_RDN );
+        }
+
+        // from here on the modify operation only deletes specific values
+        // of the Rdn attribute so we must check if one of those values
+        // are used by the Rdn attribute value pair for the name of the entry
+        String rdnValue = getRdnValue( id, name, oidRegistry );
+        
+        for ( Value<?> value:attribute )
+        {
+            if ( rdnValue.equals( (String)value.get() ) )
+            {
+                String msg = "Modify operation attempts to delete RDN attribute values in use for ";
+                msg += id + " on entry " + name + " and violates schema constraints";
+
+                if ( log.isInfoEnabled() )
+                {
+                    log.info( msg + ". SchemaChecker is throwing a schema violation exception." );
+                }
+                
+                throw new LdapSchemaViolationException( msg, ResultCodeEnum.NOT_ALLOWED_ON_RDN );
+            }
+        }
+    }
+
+
+    /**
+     * Makes sure a modify operation does not delete RDN attributes or their value.
+     * According to section 4.6 of <a href="http://rfc.net/rfc2251.html#s4.6.">
+     * RFC 2251</a> a modify operation cannot be used to remove Rdn attributes as
+     * seen below:
+     * <p/>
+     * <pre>
+     *     The Modify Operation cannot be used to remove from an entry any of
+     *     its distinguished values, those values which form the entry's
+     *     relative distinguished name.  An attempt to do so will result in the
+     *     server returning the error notAllowedOnRDN.  The Modify DN Operation
+     *     described in section 4.9 is used to rename an entry.
+     * </pre>
+     *
+     * @param name the distinguished name of the attribute being modified
+     * @param mod the modification operation being performed (should be REMOVE_ATTRIBUTE )
+     * @param entry
+     * @param oidRegistry
+     * @throws NamingException if the modify operation is removing an Rdn attribute
+     */
+    public static void preventRdnChangeOnModifyRemove( LdapDN name, int mod, ServerEntry entry, OidRegistry oidRegistry )
+        throws NamingException
+    {
+        if ( mod != DirContext.REMOVE_ATTRIBUTE )
+        {
+            return;
+        }
+
+        Set<String> rdnAttributes = getRdnAttributes( name );
+        
+        for ( AttributeType attributeType:entry.getAttributeTypes() )
+        {
+            String id = attributeType.getName();
+
+            if ( rdnAttributes.contains( id ) )
+            {
+                // if the attribute values to delete are not specified then all values
+                // for the attribute are to be deleted in which case we must just throw
+                // a schema violation exception with the notAllowedOnRdn result code
+                if ( entry.get( id ).size() == 0 )
+                {
+                    String msg = "Modify operation attempts to delete RDN attribute ";
+                    msg += id + " on entry " + name + " violates schema constraints";
+
+                    if ( log.isInfoEnabled() )
+                    {
+                        log.info( msg + ". SchemaChecker is throwing a schema violation exception." );
+                    }
+                    throw new LdapSchemaViolationException( msg, ResultCodeEnum.NOT_ALLOWED_ON_RDN );
+                }
+
+                // from here on the modify operation only deletes specific values
+                // of the Rdn attribute so we must check if one of those values
+                // are used by the Rdn attribute value pair for the name of the entry
+                String rdnValue = getRdnValue( id, name, oidRegistry );
+                EntryAttribute rdnAttr = entry.get( id );
+                
+                for ( Value<?> value:rdnAttr )
+                {
+                    if ( rdnValue.equals( (String)value.get() ) )
+                    {
+                        String msg = "Modify operation attempts to delete RDN attribute values in use for ";
+                        msg += id + " on entry " + name + " and violates schema constraints";
+
+                        if ( log.isInfoEnabled() )
+                        {
+                            log.info( msg + ". SchemaChecker is throwing a schema violation exception." );
+                        }
+                        throw new LdapSchemaViolationException( msg, ResultCodeEnum.NOT_ALLOWED_ON_RDN );
+                    }
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Gets the Rdn attribute value. This method works even if the Rdn is
+     * composed of multiple attributes.
+     *
+     * @param id the attribute id of the Rdn attribute to return
+     * @param name the distinguished name of the entry
+     * @param oidRegistry the OID registry
+     * @return the Rdn attribute value corresponding to the id, or null if the
+     * attribute is not an rdn attribute
+     * @throws NamingException if the name is malformed in any way
+     */
+    private static String getRdnValue( String id, Name name, OidRegistry oidRegistry ) throws NamingException
+    {
+        // Transform the rdnAttrId to it's OID counterPart
+        String idOid = oidRegistry.getOid( id );
+
+        if ( idOid == null )
+        {
+            log.error( "The id {} does not have any OID. It should be a wrong AttributeType.", id);
+            throw new NamingException( "Wrong AttributeType, does not have an associated OID : " + id );
+        }
+
+        String[] comps = NamespaceTools.getCompositeComponents( name.get( name.size() - 1 ) );
+
+        for ( int ii = 0; ii < comps.length; ii++ )
+        {
+            String rdnAttrId = NamespaceTools.getRdnAttribute( comps[ii] );
+            
+            // Transform the rdnAttrId to it's OID counterPart
+            String rdnAttrOid = oidRegistry.getOid( rdnAttrId );
+
+            if ( rdnAttrOid == null )
+            {
+                log.error( "The id {} does not have any OID. It should be a wrong AttributeType.", rdnAttrOid);
+                throw new NamingException( "Wrong AttributeType, does not have an associated OID : " + rdnAttrOid );
+            }
+
+            if ( rdnAttrOid.equalsIgnoreCase( idOid ) )
+            {
+                return NamespaceTools.getRdnValue( comps[ii] );
+            }
+        }
+
+        return null;
+    }
+
+
+    /**
+     * Collects the set of Rdn attributes whether or not the Rdn is based on a
+     * single attribute or multiple attributes.
+     *
+     * @param name the distinguished name of an entry
+     * @return the set of attributes composing the Rdn for the name
+     * @throws NamingException if the syntax of the Rdn is incorrect
+     */
+    private static Set<String> getRdnAttributes( LdapDN name ) throws NamingException
+    {
+        String[] comps = NamespaceTools.getCompositeComponents( name.get( name.size() - 1 ) );
+        Set<String> attributes = new HashSet<String>();
+
+        for ( int ii = 0; ii < comps.length; ii++ )
+        {
+            attributes.add( NamespaceTools.getRdnAttribute( comps[ii] ) );
+        }
+
+        return attributes;
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/SchemaEntityFactory.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/SchemaEntityFactory.java
new file mode 100644
index 0000000..22467ca
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/SchemaEntityFactory.java
@@ -0,0 +1,725 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.schema;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.constants.MetaSchemaConstants;
+import org.apache.directory.server.core.entry.DefaultServerAttribute;
+import org.apache.directory.server.core.entry.ServerAttribute;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.schema.bootstrap.Schema;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.entry.Value;
+import org.apache.directory.shared.ldap.exception.LdapNamingException;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.schema.MatchingRule;
+import org.apache.directory.shared.ldap.schema.MutableSchemaObject;
+import org.apache.directory.shared.ldap.schema.Normalizer;
+import org.apache.directory.shared.ldap.schema.ObjectClass;
+import org.apache.directory.shared.ldap.schema.ObjectClassTypeEnum;
+import org.apache.directory.shared.ldap.schema.Syntax;
+import org.apache.directory.shared.ldap.schema.UsageEnum;
+import org.apache.directory.shared.ldap.schema.syntax.ComparatorDescription;
+import org.apache.directory.shared.ldap.schema.syntax.NormalizerDescription;
+import org.apache.directory.shared.ldap.schema.syntax.SyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.SyntaxCheckerDescription;
+import org.apache.directory.shared.ldap.util.Base64;
+
+
+/**
+ * Showing how it's done ...
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class SchemaEntityFactory
+{
+    /** Used for looking up the setRegistries(Registries) method */
+    private final static Class<?>[] parameterTypes = new Class[] { Registries.class };
+    
+    /** Used for looking up the setSyntaxOid(String) method */
+    private final static Class<?>[] setOidParameterTypes = new Class[] { String.class };
+    
+    private static final String[] EMPTY = new String[0];
+    
+    /** Used for dependency injection of Registries via setter into schema objects */
+    private final Registries bootstrapRegistries;
+    /** A special ClassLoader that loads a class from the bytecode attribute */
+    private final AttributeClassLoader classLoader;
+    private final AttributeType oidAT;
+    private final AttributeType byteCodeAT;
+    
+    
+    public SchemaEntityFactory( Registries bootstrapRegistries ) throws NamingException
+    {
+        this.bootstrapRegistries = bootstrapRegistries;
+        this.classLoader = new AttributeClassLoader();
+        this.oidAT = bootstrapRegistries.getAttributeTypeRegistry().lookup( MetaSchemaConstants.M_OID_AT );
+        this.byteCodeAT = bootstrapRegistries.getAttributeTypeRegistry().lookup( MetaSchemaConstants.M_BYTECODE_AT );
+    }
+
+    
+    public Schema getSchema( ServerEntry entry ) throws NamingException
+    {
+        String name;
+        String owner;
+        String[] dependencies = EMPTY;
+        boolean isDisabled = false;
+        
+        if ( entry == null )
+        {
+            throw new NullPointerException( "entry cannot be null" );
+        }
+        
+        if ( entry.get( SchemaConstants.CN_AT ) == null )
+        {
+            throw new NullPointerException( "entry must have a valid cn attribute" );
+        }
+        
+        name = entry.get( SchemaConstants.CN_AT ).getString();
+        
+        if ( entry.get( SchemaConstants.CREATORS_NAME_AT ) == null )
+        {
+            throw new NullPointerException( "entry must have a valid " 
+                + SchemaConstants.CREATORS_NAME_AT + " attribute" );
+        }
+        
+        owner = entry.get( SchemaConstants.CREATORS_NAME_AT ).getString();
+        
+        if ( entry.get( MetaSchemaConstants.M_DISABLED_AT ) != null )
+        {
+            String value = entry.get( MetaSchemaConstants.M_DISABLED_AT ).getString();
+            value = value.toUpperCase();
+            isDisabled = value.equals( "TRUE" );
+        }
+        
+        if ( entry.get( MetaSchemaConstants.M_DEPENDENCIES_AT ) != null )
+        {
+            Set<String> depsSet = new HashSet<String>();
+            EntryAttribute depsAttr = entry.get( MetaSchemaConstants.M_DEPENDENCIES_AT );
+            
+            for ( Value<?> value:depsAttr )
+            {
+                depsSet.add( (String)value.get() );
+            }
+
+            dependencies = depsSet.toArray( EMPTY );
+        }
+        
+        return new AbstractSchema( name, owner, dependencies, isDisabled ){};
+    }
+    
+    
+    private SyntaxChecker getSyntaxChecker( String syntaxOid, String className, EntryAttribute bytecode, Registries targetRegistries )
+        throws NamingException
+    {
+        Class<?> clazz = null;
+        SyntaxChecker syntaxChecker = null;
+        
+        try
+        {
+            if ( bytecode == null )
+            {
+                clazz = Class.forName( className );
+            }
+            else
+            {
+                classLoader.setAttribute( bytecode );
+                clazz = classLoader.loadClass( className );
+            }
+        }
+        catch ( ClassNotFoundException e )
+        {
+            LdapNamingException ne = new LdapNamingException( 
+                "Normalizer class "+ className + " was not found", ResultCodeEnum.OTHER );
+            ne.setRootCause( e );
+            throw ne;
+        }
+        
+        try
+        {
+            syntaxChecker = ( SyntaxChecker ) clazz.newInstance();
+        }
+        catch ( InstantiationException e )
+        {
+            LdapNamingException ne = new LdapNamingException( "Failed to instantiate SyntaxChecker class "+ className 
+                + ".\nCheck that a default constructor exists for the class.", ResultCodeEnum.OTHER );
+            ne.setRootCause( e );
+            throw ne;
+        }
+        catch ( IllegalAccessException e )
+        {
+            LdapNamingException ne = new LdapNamingException( "Failed to instantiate SyntaxChecker class "+ className 
+                + ".\nCheck that a **PUBLIC** accessible default constructor exists for the class.", 
+                ResultCodeEnum.OTHER );
+            ne.setRootCause( e );
+            throw ne;
+        }
+
+        // try now before returning to check if we can inject a Registries object
+        injectRegistries( syntaxChecker, targetRegistries );
+        injectOid( syntaxOid, syntaxChecker );
+        return syntaxChecker;
+    }
+    
+    
+    /**
+     * Retrieve and load a syntaxChecker class from the DIT.
+     * 
+     * @param entry the entry to load the syntaxChecker from
+     * @return the loaded SyntaxChecker
+     * @throws NamingException if anything fails during loading
+     */
+    public SyntaxChecker getSyntaxChecker( ServerEntry entry, Registries targetRegistries ) throws NamingException
+    {
+        if ( entry == null )
+        {
+            throw new NullPointerException( "entry cannot be null" );
+        }
+        
+        if ( entry.get( MetaSchemaConstants.M_FQCN_AT ) == null )
+        {
+            throw new NullPointerException( "entry must have a valid "
+                + MetaSchemaConstants.M_FQCN_AT + " attribute" );
+        }
+
+        String className = ( String ) entry.get( MetaSchemaConstants.M_FQCN_AT ).get().get();
+        String syntaxOid = ( String ) entry.get( oidAT ).get().get();
+        return getSyntaxChecker( syntaxOid, className, entry.get( byteCodeAT ), 
+            targetRegistries );
+    }
+    
+    
+    public SyntaxChecker getSyntaxChecker( SyntaxCheckerDescription syntaxCheckerDescription, 
+        Registries targetRegistries ) throws NamingException
+    {
+        ServerAttribute attr = null;
+        
+        if ( syntaxCheckerDescription.getBytecode() != null )
+        {
+            byte[] bytecode = Base64.decode( syntaxCheckerDescription.getBytecode().toCharArray() );
+            AttributeType byteCodeAT = targetRegistries.getAttributeTypeRegistry().lookup( MetaSchemaConstants.M_BYTECODE_AT );
+            attr = new DefaultServerAttribute( byteCodeAT, bytecode );
+        }
+        
+        return getSyntaxChecker( syntaxCheckerDescription.getNumericOid(), 
+            syntaxCheckerDescription.getFqcn(), attr, targetRegistries );
+    }
+    
+    
+    private Comparator getComparator( String className, EntryAttribute bytecode, Registries targetRegistries ) 
+        throws NamingException
+    {
+        Comparator comparator = null;
+        Class<?> clazz = null;
+        
+        if ( bytecode == null ) 
+        {
+            try
+            {
+                clazz = Class.forName( className );
+            }
+            catch ( ClassNotFoundException e )
+            {
+                LdapNamingException ne = new LdapNamingException( "Comparator class "+ className + " was not found",
+                    ResultCodeEnum.OTHER );
+                ne.setRootCause( e );
+                throw ne;
+            }
+        }
+        else
+        {
+            classLoader.setAttribute( bytecode );
+            
+            try
+            {
+                clazz = classLoader.loadClass( className );
+            }
+            catch ( ClassNotFoundException e )
+            {
+                LdapNamingException ne = new LdapNamingException( "Comparator class "+ className + " was not found",
+                    ResultCodeEnum.OTHER );
+                ne.setRootCause( e );
+                throw ne;
+            }
+        }
+        
+        try
+        {
+            comparator = ( Comparator ) clazz.newInstance();
+        }
+        catch ( InstantiationException e )
+        {
+            NamingException ne = new NamingException( "Failed to instantiate comparator class "+ className 
+                + ".\nCheck that a default constructor exists for the class." );
+            ne.setRootCause( e );
+            throw ne;
+        }
+        catch ( IllegalAccessException e )
+        {
+            NamingException ne = new NamingException( "Failed to instantiate comparator class "+ className 
+                + ".\nCheck that a **PUBLIC** accessible default constructor exists for the class." );
+            ne.setRootCause( e );
+            throw ne;
+        }
+        
+        injectRegistries( comparator, targetRegistries );
+        return comparator;
+    }
+    
+    
+    public Comparator getComparator( ComparatorDescription comparatorDescription, Registries targetRegistries ) 
+        throws NamingException
+    {
+        ServerAttribute attr = null;
+        
+        if ( comparatorDescription.getBytecode() != null )
+        { 
+            byte[] bytecode = Base64.decode( comparatorDescription.getBytecode().toCharArray() );
+            AttributeType byteCodeAT = targetRegistries.getAttributeTypeRegistry().lookup( MetaSchemaConstants.M_BYTECODE_AT );
+            attr = new DefaultServerAttribute( byteCodeAT, bytecode );
+        }
+        
+        return getComparator( comparatorDescription.getFqcn(), attr, targetRegistries );
+    }
+    
+    
+    /**
+     * Retrieve and load a Comparator class from the DIT.
+     * 
+     * @param entry the entry to load the Comparator from
+     * @return the loaded Comparator
+     * @throws NamingException if anything fails during loading
+     */
+    public Comparator getComparator( ServerEntry entry, Registries targetRegistries ) throws NamingException
+    {
+        if ( entry == null )
+        {
+            throw new NullPointerException( "entry cannot be null" );
+        }
+        
+        if ( entry.get( MetaSchemaConstants.M_FQCN_AT ) == null )
+        {
+            throw new NullPointerException( "entry must have a valid " 
+                + MetaSchemaConstants.M_FQCN_AT + " attribute" );
+        }
+        
+        String className = ( String ) entry.get( MetaSchemaConstants.M_FQCN_AT ).get().get();
+        return getComparator( className, entry.get( MetaSchemaConstants.M_BYTECODE_AT ), targetRegistries );
+    }
+    
+    
+    private Normalizer getNormalizer( String className, EntryAttribute bytecode, Registries targetRegistries ) 
+        throws NamingException
+    {
+        Class<?> clazz = null;
+        Normalizer normalizer = null;
+        
+        try
+        {
+            if ( bytecode == null )
+            {
+                clazz = Class.forName( className );
+            }
+            else
+            {
+                classLoader.setAttribute( bytecode );
+                clazz = classLoader.loadClass( className );
+            }
+        }
+        catch ( ClassNotFoundException e )
+        {
+            LdapNamingException ne = new LdapNamingException( 
+                "Normalizer class "+ className + " was not found", ResultCodeEnum.OTHER );
+            ne.setRootCause( e );
+            throw ne;
+        }
+        
+        try
+        {
+            normalizer = ( Normalizer ) clazz.newInstance();
+        }
+        catch ( InstantiationException e )
+        {
+            LdapNamingException ne = new LdapNamingException( "Failed to instantiate normalizer class "+ className 
+                + ".\nCheck that a default constructor exists for the class.", ResultCodeEnum.OTHER );
+            ne.setRootCause( e );
+            throw ne;
+        }
+        catch ( IllegalAccessException e )
+        {
+            LdapNamingException ne = new LdapNamingException( "Failed to instantiate normalizer class "+ className 
+                + ".\nCheck that a **PUBLIC** accessible default constructor exists for the class.", 
+                ResultCodeEnum.OTHER );
+            ne.setRootCause( e );
+            throw ne;
+        }
+
+        // try now before returning to check if we can inject a Registries object
+        injectRegistries( normalizer, targetRegistries );
+        return normalizer;
+    }
+
+    
+    public Normalizer getNormalizer( NormalizerDescription normalizerDescription, Registries targetRegistries )
+        throws NamingException
+    {
+        ServerAttribute attr = null;
+        
+        if ( normalizerDescription.getBytecode() != null )
+        {
+            byte[] bytecode = Base64.decode( normalizerDescription.getBytecode().toCharArray() );
+            AttributeType byteCodeAT = targetRegistries.getAttributeTypeRegistry().lookup( MetaSchemaConstants.M_BYTECODE_AT );
+            attr = new DefaultServerAttribute( byteCodeAT, bytecode );
+        }
+        
+        return getNormalizer( normalizerDescription.getFqcn(), attr, targetRegistries );
+    }
+    
+    
+    /**
+     * Retrieve and load a Normalizer class from the DIT.
+     * 
+     * @param entry the entry to load the Normalizer from
+     * @return the loaded Normalizer
+     * @throws NamingException if anything fails during loading
+     */
+    public Normalizer getNormalizer( ServerEntry entry, Registries targetRegistries ) throws NamingException
+    {
+        if ( entry == null )
+        {
+            throw new NullPointerException( "entry cannot be null" );
+        }
+        
+        if ( entry.get( MetaSchemaConstants.M_FQCN_AT ) == null )
+        {
+            throw new NullPointerException( "entry must have a valid " 
+                + MetaSchemaConstants.M_FQCN_AT + " attribute" );
+        }
+        
+        String className = ( String ) entry.get( MetaSchemaConstants.M_FQCN_AT ).get().get();
+        return getNormalizer( className, entry.get( MetaSchemaConstants.M_BYTECODE_AT ), targetRegistries );
+    }
+    
+    
+    /**
+     * Uses reflection to see if a setRegistries( Registries ) method exists on the
+     * object's class.  If so then the registries are dependency injected into the 
+     * new schema object.
+     * 
+     * @param obj a schema object to have a Registries dependency injected.
+     */
+    private void injectRegistries( Object obj, Registries targetRegistries ) throws NamingException
+    {
+        String className = obj.getClass().getName();
+        
+        try
+        {
+            Method method = obj.getClass().getMethod( "setRegistries", parameterTypes );
+            
+            if ( method == null )
+            {
+                return;
+            }
+            
+            Object[] args = new Object[] { this.bootstrapRegistries };
+            method.invoke( obj, args );
+        }
+        catch ( SecurityException e )
+        {
+            NamingException ne = new NamingException( "SyntaxChecker class "+ className 
+                + " could not have the Registries dependency injected." );
+            ne.setRootCause( e );
+            throw ne;
+        }
+        catch ( NoSuchMethodException e )
+        {
+            // this is ok since not every object may have setRegistries()
+        }
+        catch ( IllegalArgumentException e )
+        {
+            NamingException ne = new NamingException( "SyntaxChecker class "+ className 
+                + " could not have the Registries dependency injected." );
+            ne.setRootCause( e );
+            throw ne;
+        }
+        catch ( IllegalAccessException e )
+        {
+            NamingException ne = new NamingException( "SyntaxChecker class "+ className 
+                + " could not have the Registries dependency injected." );
+            ne.setRootCause( e );
+            throw ne;
+        }
+        catch ( InvocationTargetException e )
+        {
+            NamingException ne = new NamingException( "SyntaxChecker class "+ className 
+                + " could not have the Registries dependency injected." );
+            ne.setRootCause( e );
+            throw ne;
+        }
+    }
+
+
+    /**
+     * Uses reflection to see if a setSyntaxOid( String ) method exists 
+     * on the object's class.  If so then the oid dependency is injected into the 
+     * new SyntaxChecker.
+     * 
+     * @param obj a schema object to have a oid dependency injected.
+     */
+    private void injectOid( String syntaxOid, SyntaxChecker checker ) throws NamingException
+    {
+        String className = checker.getClass().getName();
+        
+        try
+        {
+            Method method = checker.getClass().getMethod( "setSyntaxOid", setOidParameterTypes );
+            
+            if ( method == null )
+            {
+                return;
+            }
+            
+            Object[] args = new Object[] { syntaxOid};
+            method.invoke( checker, args );
+        }
+        catch ( SecurityException e )
+        {
+            NamingException ne = new NamingException( "SyntaxChecker class "+ className 
+                + " could not have the oid dependency injected." );
+            ne.setRootCause( e );
+            throw ne;
+        }
+        catch ( NoSuchMethodException e )
+        {
+            // this is ok since not every object may have setSyntaxOid()
+        }
+        catch ( IllegalArgumentException e )
+        {
+            NamingException ne = new NamingException( "SyntaxChecker class "+ className 
+                + " could not have the oid dependency injected." );
+            ne.setRootCause( e );
+            throw ne;
+        }
+        catch ( IllegalAccessException e )
+        {
+            NamingException ne = new NamingException( "SyntaxChecker class "+ className 
+                + " could not have the oid dependency injected." );
+            ne.setRootCause( e );
+            throw ne;
+        }
+        catch ( InvocationTargetException e )
+        {
+            NamingException ne = new NamingException( "SyntaxChecker class "+ className 
+                + " could not have the oid dependency injected." );
+            ne.setRootCause( e );
+            throw ne;
+        }
+    }
+
+
+    public Syntax getSyntax( ServerEntry entry, Registries targetRegistries, String schema ) throws NamingException
+    {
+        String oid = entry.get( MetaSchemaConstants.M_OID_AT ).getString();
+        SyntaxImpl syntax = new SyntaxImpl( oid, targetRegistries.getSyntaxCheckerRegistry() );
+        syntax.setSchema( schema );
+        
+        if ( entry.get( MetaSchemaConstants.X_HUMAN_READABLE_AT ) != null )
+        {
+            String val = entry.get( MetaSchemaConstants.X_HUMAN_READABLE_AT ).getString();
+            syntax.setHumanReadable( val.toUpperCase().equals( "TRUE" ) );
+        }
+        
+        if ( entry.get( MetaSchemaConstants.M_DESCRIPTION_AT ) != null )
+        {
+            syntax.setDescription( entry.get( MetaSchemaConstants.M_DESCRIPTION_AT ).getString() ); 
+        }
+        
+        return syntax;
+    }
+
+    
+    public MatchingRule getMatchingRule( ServerEntry entry, Registries targetRegistries, String schema ) throws NamingException
+    {
+        String oid = entry.get( MetaSchemaConstants.M_OID_AT ).getString();
+        String syntaxOid = entry.get( MetaSchemaConstants.M_SYNTAX_AT ).getString();
+        MatchingRuleImpl mr = new MatchingRuleImpl( oid, syntaxOid, targetRegistries );
+        mr.setSchema( schema );
+        setSchemaObjectProperties( mr, entry );
+        return mr;
+    }
+    
+    
+    private String[] getStrings( EntryAttribute attr ) throws NamingException
+    {
+        if ( attr == null )
+        {
+            return EMPTY;
+        }
+        
+        String[] strings = new String[attr.size()];
+        
+        int pos = 0;
+        
+        for ( Value<?> value:attr )
+        {
+            strings[pos++] = (String)value.get();
+        }
+        
+        return strings;
+    }
+    
+    
+    public ObjectClass getObjectClass( ServerEntry entry, Registries targetRegistries, String schema ) throws NamingException
+    {
+        String oid = entry.get( MetaSchemaConstants.M_OID_AT ).getString();
+        ObjectClassImpl oc = new ObjectClassImpl( oid, targetRegistries );
+        oc.setSchema( schema );
+        
+        if ( entry.get( MetaSchemaConstants.M_SUP_OBJECT_CLASS_AT ) != null )
+        {
+            oc.setSuperClassOids( getStrings( entry.get( MetaSchemaConstants.M_SUP_OBJECT_CLASS_AT ) ) );
+        }
+        
+        if ( entry.get( MetaSchemaConstants.M_MAY_AT ) != null )
+        {
+            oc.setMayListOids( getStrings( entry.get( MetaSchemaConstants.M_MAY_AT ) ) );
+        }
+        
+        if ( entry.get( MetaSchemaConstants.M_MUST_AT ) != null )
+        {
+            oc.setMustListOids( getStrings( entry.get( MetaSchemaConstants.M_MUST_AT ) ) );
+        }
+        
+        if ( entry.get( MetaSchemaConstants.M_TYPE_OBJECT_CLASS_AT ) != null )
+        {
+            String type = entry.get( MetaSchemaConstants.M_TYPE_OBJECT_CLASS_AT ).getString();
+            oc.setType( ObjectClassTypeEnum.getClassType( type ) );
+        }
+        else
+        {
+            oc.setType( ObjectClassTypeEnum.STRUCTURAL );
+        }
+        
+        setSchemaObjectProperties( oc, entry );
+        
+        return oc;
+    }
+    
+    
+    public AttributeType getAttributeType( ServerEntry entry, Registries targetRegistries, String schema ) throws NamingException
+    {
+        String oid = entry.get( MetaSchemaConstants.M_OID_AT ).getString();
+        AttributeTypeImpl at = new AttributeTypeImpl( oid, targetRegistries );
+        at.setSchema( schema );
+        setSchemaObjectProperties( at, entry );
+        
+        if ( entry.get( MetaSchemaConstants.M_SYNTAX_AT ) != null )
+        {
+            at.setSyntaxOid( entry.get( MetaSchemaConstants.M_SYNTAX_AT ).getString() );
+        }
+        
+        if ( entry.get( MetaSchemaConstants.M_EQUALITY_AT ) != null )
+        {
+            at.setEqualityOid( entry.get( MetaSchemaConstants.M_EQUALITY_AT ).getString() );
+        }
+        
+        if ( entry.get( MetaSchemaConstants.M_ORDERING_AT ) != null )
+        {
+            at.setOrderingOid( entry.get( MetaSchemaConstants.M_ORDERING_AT ).getString() );
+        }
+        
+        if ( entry.get( MetaSchemaConstants.M_SUBSTR_AT ) != null )
+        {
+            at.setSubstrOid( entry.get( MetaSchemaConstants.M_SUBSTR_AT ).getString() );
+        }
+        
+        if ( entry.get( MetaSchemaConstants.M_SUP_ATTRIBUTE_TYPE_AT ) != null )
+        {
+            at.setSuperiorOid( entry.get( MetaSchemaConstants.M_SUP_ATTRIBUTE_TYPE_AT ).getString() );
+        }
+        
+        if ( entry.get( MetaSchemaConstants.M_COLLECTIVE_AT ) != null )
+        {
+            String val = entry.get( MetaSchemaConstants.M_COLLECTIVE_AT ).getString();
+            at.setCollective( val.equalsIgnoreCase( "TRUE" ) );
+        }
+        
+        if ( entry.get( MetaSchemaConstants.M_SINGLE_VALUE_AT ) != null )
+        {
+            String val = entry.get( MetaSchemaConstants.M_SINGLE_VALUE_AT ).getString();
+            at.setSingleValue( val.equalsIgnoreCase( "TRUE" ) );
+        }
+        
+        if ( entry.get( MetaSchemaConstants.M_NO_USER_MODIFICATION_AT ) != null )
+        {
+            String val = entry.get( MetaSchemaConstants.M_NO_USER_MODIFICATION_AT ).getString();
+            at.setCanUserModify( ! val.equalsIgnoreCase( "TRUE" ) );
+        }
+        
+        if ( entry.get( MetaSchemaConstants.M_USAGE_AT ) != null )
+        {
+            at.setUsage( UsageEnum.getUsage( entry.get( MetaSchemaConstants.M_USAGE_AT ).getString() ) );
+        }
+        
+        return at;
+    }
+    
+
+    private void setSchemaObjectProperties( MutableSchemaObject mso, ServerEntry entry ) throws NamingException
+    {
+        if ( entry.get( MetaSchemaConstants.M_OBSOLETE_AT ) != null )
+        {
+            String val = entry.get( MetaSchemaConstants.M_OBSOLETE_AT ).getString();
+            mso.setObsolete( val.equalsIgnoreCase( "TRUE" ) );
+        }
+        
+        if ( entry.get( MetaSchemaConstants.M_DESCRIPTION_AT ) != null )
+        {
+            mso.setDescription( entry.get( MetaSchemaConstants.M_DESCRIPTION_AT ).getString() ); 
+        }
+
+        EntryAttribute names = entry.get( MetaSchemaConstants.M_NAME_AT );
+        
+        if ( names != null )
+        {
+            List<String> values = new ArrayList<String>();
+            
+            for ( Value<?> name:names )
+            {
+                values.add( (String)name.get() );
+            }
+            
+            mso.setNames( values.toArray( EMPTY ) );
+        }
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/SchemaInterceptor.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/SchemaInterceptor.java
new file mode 100644
index 0000000..22c946e
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/SchemaInterceptor.java
@@ -0,0 +1,2031 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.schema;
+
+
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.NoPermissionException;
+import javax.naming.directory.InvalidAttributeValueException;
+import javax.naming.directory.SearchControls;
+
+import org.apache.directory.server.constants.ServerDNConstants;
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.entry.DefaultServerAttribute;
+import org.apache.directory.server.core.entry.ServerAttribute;
+import org.apache.directory.server.core.entry.ServerBinaryValue;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerEntryUtils;
+import org.apache.directory.server.core.entry.ServerSearchResult;
+import org.apache.directory.server.core.entry.ServerStringValue;
+import org.apache.directory.server.core.enumeration.SearchResultFilter;
+import org.apache.directory.server.core.enumeration.SearchResultFilteringEnumeration;
+import org.apache.directory.server.core.interceptor.BaseInterceptor;
+import org.apache.directory.server.core.interceptor.NextInterceptor;
+import org.apache.directory.server.core.interceptor.context.AddOperationContext;
+import org.apache.directory.server.core.interceptor.context.DeleteOperationContext;
+import org.apache.directory.server.core.interceptor.context.ListOperationContext;
+import org.apache.directory.server.core.interceptor.context.LookupOperationContext;
+import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
+import org.apache.directory.server.core.interceptor.context.MoveAndRenameOperationContext;
+import org.apache.directory.server.core.interceptor.context.MoveOperationContext;
+import org.apache.directory.server.core.interceptor.context.RenameOperationContext;
+import org.apache.directory.server.core.interceptor.context.SearchOperationContext;
+import org.apache.directory.server.core.invocation.Invocation;
+import org.apache.directory.server.core.invocation.InvocationStack;
+import org.apache.directory.server.core.partition.PartitionNexus;
+import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
+import org.apache.directory.server.schema.registries.ObjectClassRegistry;
+import org.apache.directory.server.schema.registries.OidRegistry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.entry.Entry;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.entry.Modification;
+import org.apache.directory.shared.ldap.entry.ModificationOperation;
+import org.apache.directory.shared.ldap.entry.Value;
+import org.apache.directory.shared.ldap.entry.client.ClientBinaryValue;
+import org.apache.directory.shared.ldap.entry.client.ClientStringValue;
+import org.apache.directory.shared.ldap.exception.LdapAttributeInUseException;
+import org.apache.directory.shared.ldap.exception.LdapInvalidAttributeIdentifierException;
+import org.apache.directory.shared.ldap.exception.LdapInvalidAttributeValueException;
+import org.apache.directory.shared.ldap.exception.LdapNameNotFoundException;
+import org.apache.directory.shared.ldap.exception.LdapNoSuchAttributeException;
+import org.apache.directory.shared.ldap.exception.LdapSchemaViolationException;
+import org.apache.directory.shared.ldap.filter.ApproximateNode;
+import org.apache.directory.shared.ldap.filter.AssertionNode;
+import org.apache.directory.shared.ldap.filter.BranchNode;
+import org.apache.directory.shared.ldap.filter.EqualityNode;
+import org.apache.directory.shared.ldap.filter.ExprNode;
+import org.apache.directory.shared.ldap.filter.ExtensibleNode;
+import org.apache.directory.shared.ldap.filter.GreaterEqNode;
+import org.apache.directory.shared.ldap.filter.LessEqNode;
+import org.apache.directory.shared.ldap.filter.PresenceNode;
+import org.apache.directory.shared.ldap.filter.ScopeNode;
+import org.apache.directory.shared.ldap.filter.SimpleNode;
+import org.apache.directory.shared.ldap.filter.SubstringNode;
+import org.apache.directory.shared.ldap.message.CascadeControl;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.name.AttributeTypeAndValue;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.name.Rdn;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.schema.ObjectClass;
+import org.apache.directory.shared.ldap.schema.SchemaUtils;
+import org.apache.directory.shared.ldap.schema.UsageEnum;
+import org.apache.directory.shared.ldap.schema.syntax.AcceptAllSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.SyntaxChecker;
+import org.apache.directory.shared.ldap.util.EmptyEnumeration;
+import org.apache.directory.shared.ldap.util.SingletonEnumeration;
+import org.apache.directory.shared.ldap.util.StringTools;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * An {@link org.apache.directory.server.core.interceptor.Interceptor} that manages and enforces schemas.
+ *
+ * @todo Better interceptor description required.
+
+ * @org.apache.xbean.XBean
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class SchemaInterceptor extends BaseInterceptor
+{
+    /** The LoggerFactory used by this Interceptor */
+    private static Logger LOG = LoggerFactory.getLogger( SchemaInterceptor.class );
+
+    private static final String[] SCHEMA_SUBENTRY_RETURN_ATTRIBUTES = new String[]
+        { SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES, SchemaConstants.ALL_USER_ATTRIBUTES };
+
+    /** Speedup for logs */
+    private static final boolean IS_DEBUG = LOG.isDebugEnabled();
+
+    /**
+     * the root nexus to all database partitions
+     */
+    private PartitionNexus nexus;
+
+    /**
+     * a binary attribute tranforming filter: String -> byte[]
+     */
+    private BinaryAttributeFilter binaryAttributeFilter;
+
+    private TopFilter topFilter;
+
+    private List<SearchResultFilter> filters = new ArrayList<SearchResultFilter>();
+
+    /**
+     * the global schema object registries
+     */
+    private Registries registries;
+
+    /** A global reference to the ObjectClass attributeType */
+    private AttributeType OBJECT_CLASS;
+    /**
+     * the global attributeType registry
+     */
+    private AttributeTypeRegistry atRegistry;
+
+    /** A normalized form for the SubschemaSubentry DN */
+    private String subschemaSubentryDnNorm;
+
+    /**
+     * the normalized name for the schema modification attributes
+     */
+    private LdapDN schemaModificationAttributesDN;
+
+    private SchemaOperationControl schemaManager;
+
+    private SchemaService schemaService;
+
+    // the base DN (normalized) of the schema partition
+    private LdapDN schemaBaseDN;
+
+    /** A map used to store all the objectClasses superiors */
+    private Map<String, List<ObjectClass>> superiors;
+
+    /** A map used to store all the objectClasses may attributes */
+    private Map<String, List<AttributeType>> allMay;
+
+    /** A map used to store all the objectClasses must */
+    private Map<String, List<AttributeType>> allMust;
+
+    /** A map used to store all the objectClasses allowed attributes (may + must) */
+    private Map<String, List<AttributeType>> allowed;
+
+
+    /**
+     * Initialize the Schema Service
+     *
+     * @param directoryService the directory service core
+     * @throws NamingException if there are problems during initialization
+     */
+    public void init( DirectoryService directoryService ) throws NamingException
+    {
+        if ( IS_DEBUG )
+        {
+            LOG.debug( "Initializing SchemaInterceptor..." );
+        }
+
+        nexus = directoryService.getPartitionNexus();
+        registries = directoryService.getRegistries();
+        atRegistry = registries.getAttributeTypeRegistry();
+        OBJECT_CLASS = atRegistry.lookup( SchemaConstants.OBJECT_CLASS_AT );
+        binaryAttributeFilter = new BinaryAttributeFilter();
+        topFilter = new TopFilter();
+        filters.add( binaryAttributeFilter );
+        filters.add( topFilter );
+
+        schemaBaseDN = new LdapDN( ServerDNConstants.OU_SCHEMA_DN );
+        schemaBaseDN.normalize( atRegistry.getNormalizerMapping() );
+        schemaService = directoryService.getSchemaService();
+        schemaManager = directoryService.getSchemaService().getSchemaControl();
+
+        // stuff for dealing with subentries (garbage for now)
+        Value<?> subschemaSubentry = nexus.getRootDSE( null ).get( SchemaConstants.SUBSCHEMA_SUBENTRY_AT ).get();
+        LdapDN subschemaSubentryDn = new LdapDN( ( String ) ( subschemaSubentry.get() ) );
+        subschemaSubentryDn.normalize( atRegistry.getNormalizerMapping() );
+        subschemaSubentryDnNorm = subschemaSubentryDn.getNormName();
+
+        schemaModificationAttributesDN = new LdapDN( "cn=schemaModifications,ou=schema" );
+        schemaModificationAttributesDN.normalize( atRegistry.getNormalizerMapping() );
+
+        computeSuperiors();
+
+        if ( IS_DEBUG )
+        {
+            LOG.debug( "SchemaInterceptor Initialized !" );
+        }
+    }
+
+
+    /**
+     * Compute the MUST attributes for an objectClass. This method gather all the
+     * MUST from all the objectClass and its superors.
+     *
+     * @param atSeen ???
+     * @param objectClass the object class to gather MUST attributes for
+     * @throws NamingException if there are problems resolving schema entitites
+     */
+    private void computeMustAttributes( ObjectClass objectClass, Set<String> atSeen ) throws NamingException
+    {
+        List<ObjectClass> parents = superiors.get( objectClass.getOid() );
+
+        List<AttributeType> mustList = new ArrayList<AttributeType>();
+        List<AttributeType> allowedList = new ArrayList<AttributeType>();
+        Set<String> mustSeen = new HashSet<String>();
+
+        allMust.put( objectClass.getOid(), mustList );
+        allowed.put( objectClass.getOid(), allowedList );
+
+        for ( ObjectClass parent : parents )
+        {
+            AttributeType[] mustParent = parent.getMustList();
+
+            if ( ( mustParent != null ) && ( mustParent.length != 0 ) )
+            {
+                for ( AttributeType attributeType : mustParent )
+                {
+                    String oid = attributeType.getOid();
+
+                    if ( !mustSeen.contains( oid ) )
+                    {
+                        mustSeen.add( oid );
+                        mustList.add( attributeType );
+                        allowedList.add( attributeType );
+                        atSeen.add( attributeType.getOid() );
+                    }
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Compute the MAY attributes for an objectClass. This method gather all the
+     * MAY from all the objectClass and its superors.
+     *
+     * The allowed attributes is also computed, it's the union of MUST and MAY
+     *
+     * @param atSeen ???
+     * @param objectClass the object class to get all the MAY attributes for
+     * @throws NamingException with problems accessing registries
+     */
+    private void computeMayAttributes( ObjectClass objectClass, Set<String> atSeen ) throws NamingException
+    {
+        List<ObjectClass> parents = superiors.get( objectClass.getOid() );
+
+        List<AttributeType> mayList = new ArrayList<AttributeType>();
+        Set<String> maySeen = new HashSet<String>();
+        List<AttributeType> allowedList = allowed.get( objectClass.getOid() );
+
+        allMay.put( objectClass.getOid(), mayList );
+
+        for ( ObjectClass parent : parents )
+        {
+            AttributeType[] mustParent = parent.getMustList();
+
+            if ( ( mustParent != null ) && ( mustParent.length != 0 ) )
+            {
+                for ( AttributeType attributeType : mustParent )
+                {
+                    String oid = attributeType.getOid();
+
+                    if ( !maySeen.contains( oid ) )
+                    {
+                        maySeen.add( oid );
+                        mayList.add( attributeType );
+
+                        if ( !atSeen.contains( oid ) )
+                        {
+                            allowedList.add( attributeType );
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Recursively compute all the superiors of an object class. For instance, considering
+     * 'inetOrgPerson', it's direct superior is 'organizationalPerson', which direct superior
+     * is 'Person', which direct superior is 'top'.
+     *
+     * As a result, we will gather all of these three ObjectClasses in 'inetOrgPerson' ObjectClasse
+     * superiors.
+     */
+    private void computeOCSuperiors( ObjectClass objectClass, List<ObjectClass> superiors, Set<String> ocSeen )
+        throws NamingException
+    {
+        ObjectClass[] parents = objectClass.getSuperClasses();
+
+        // Loop on all the objectClass superiors
+        if ( ( parents != null ) && ( parents.length != 0 ) )
+        {
+            for ( ObjectClass parent : parents )
+            {
+                // Top is not added
+                if ( SchemaConstants.TOP_OC.equals( parent.getName() ) )
+                {
+                    continue;
+                }
+
+                // For each one, recurse
+                computeOCSuperiors( parent, superiors, ocSeen );
+
+                String oid = parent.getOid();
+
+                if ( !ocSeen.contains( oid ) )
+                {
+                    superiors.add( parent );
+                    ocSeen.add( oid );
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Compute all ObjectClasses superiors, MAY and MUST attributes.
+     * @throws NamingException
+     */
+    private void computeSuperiors() throws NamingException
+    {
+        Iterator<ObjectClass> objectClasses = registries.getObjectClassRegistry().iterator();
+        superiors = new HashMap<String, List<ObjectClass>>();
+        allMust = new HashMap<String, List<AttributeType>>();
+        allMay = new HashMap<String, List<AttributeType>>();
+        allowed = new HashMap<String, List<AttributeType>>();
+
+        while ( objectClasses.hasNext() )
+        {
+            List<ObjectClass> ocSuperiors = new ArrayList<ObjectClass>();
+
+            ObjectClass objectClass = objectClasses.next();
+            superiors.put( objectClass.getOid(), ocSuperiors );
+
+            computeOCSuperiors( objectClass, ocSuperiors, new HashSet<String>() );
+
+            Set<String> atSeen = new HashSet<String>();
+            computeMustAttributes( objectClass, atSeen );
+            computeMayAttributes( objectClass, atSeen );
+
+            superiors.put( objectClass.getName(), ocSuperiors );
+        }
+    }
+
+
+    /**
+     *
+     */
+    public NamingEnumeration<ServerSearchResult> list( NextInterceptor nextInterceptor, ListOperationContext opContext )
+        throws NamingException
+    {
+        NamingEnumeration<ServerSearchResult> result = nextInterceptor.list( opContext );
+        Invocation invocation = InvocationStack.getInstance().peek();
+        return new SearchResultFilteringEnumeration( result, new SearchControls(), invocation, binaryAttributeFilter,
+            "List Schema Filter" );
+    }
+
+
+    /**
+     * Remove all unknown attributes from the searchControls, to avoid an exception.
+     *
+     * RFC 2251 states that :
+     * " Attributes MUST be named at most once in the list, and are returned "
+     * " at most once in an entry. "
+     * " If there are attribute descriptions in "
+     * " the list which are not recognized, they are ignored by the server."
+     *
+     * @param searchCtls The SearchControls we will filter
+     */
+    private void filterAttributesToReturn( SearchControls searchCtls )
+    {
+        String[] attributes = searchCtls.getReturningAttributes();
+
+        if ( ( attributes == null ) || ( attributes.length == 0 ) )
+        {
+            // We have no attributes, that means "*" (all users attributes)
+            searchCtls.setReturningAttributes( SchemaConstants.ALL_USER_ATTRIBUTES_ARRAY );
+            return;
+        }
+
+        Map<String, String> filteredAttrs = new HashMap<String, String>();
+        boolean hasNoAttribute = false;
+        boolean hasAttributes = false;
+
+        for ( String attribute : attributes )
+        {
+            // Skip special attributes
+            if ( ( SchemaConstants.ALL_USER_ATTRIBUTES.equals( attribute ) )
+                || ( SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES.equals( attribute ) )
+                || ( SchemaConstants.NO_ATTRIBUTE.equals( attribute ) ) )
+            {
+                if ( !filteredAttrs.containsKey( attribute ) )
+                {
+                    filteredAttrs.put( attribute, attribute );
+                }
+
+                if ( SchemaConstants.NO_ATTRIBUTE.equals( attribute ) )
+                {
+                    hasNoAttribute = true;
+                }
+                else
+                {
+                    hasAttributes = true;
+                }
+
+                continue;
+            }
+
+            try
+            {
+                // Deal with the special ;binary 
+                if ( attribute.endsWith( ";binary" ) )
+                {
+                    attribute = attribute.substring( 0, attribute.length() - ";binary".length() );
+                }
+                
+                // Check that the attribute is declared
+                if ( registries.getOidRegistry().hasOid( attribute ) )
+                {
+                    String oid = registries.getOidRegistry().getOid( attribute );
+
+                    // The attribute must be an AttributeType
+                    if ( atRegistry.hasAttributeType( oid ) )
+                    {
+                        if ( !filteredAttrs.containsKey( oid ) )
+                        {
+                            // Ok, we can add the attribute to the list of filtered attributes
+                            filteredAttrs.put( oid, attribute );
+                        }
+                    }
+                }
+
+                hasAttributes = true;
+            }
+            catch ( NamingException ne )
+            {
+                /* Do nothing, the attribute does not exist */
+            }
+        }
+
+        // Treat a special case : if we have an attribute and "1.1", then discard "1.1"
+        if ( hasAttributes && hasNoAttribute )
+        {
+            filteredAttrs.remove( SchemaConstants.NO_ATTRIBUTE );
+        }
+
+        // If we still have the same attribute number, then we can just get out the method
+        if ( filteredAttrs.size() == attributes.length )
+        {
+            return;
+        }
+
+        // Deal with the special case where the attribute list is now empty
+        if ( filteredAttrs.size() == 0 )
+        {
+            // We just have to pass the special 1.1 attribute,
+            // as we don't want to return any attribute
+            searchCtls.setReturningAttributes( SchemaConstants.NO_ATTRIBUTE_ARRAY );
+            return;
+        }
+
+        // Some attributes have been removed. let's modify the searchControl
+        String[] newAttributesList = new String[filteredAttrs.size()];
+
+        int pos = 0;
+
+        for ( String key : filteredAttrs.keySet() )
+        {
+            newAttributesList[pos++] = filteredAttrs.get( key );
+        }
+
+        searchCtls.setReturningAttributes( newAttributesList );
+    }
+
+
+    private Value<?> convert( String id, Object value ) throws NamingException
+    {
+        AttributeType at = atRegistry.lookup( id );
+
+        if ( at.getSyntax().isHumanReadable() )
+        {
+            if ( value instanceof byte[] )
+            {
+                try
+                {
+                    return new ClientStringValue( new String( ( byte[] ) value, "UTF-8" ) );
+                }
+                catch ( UnsupportedEncodingException uee )
+                {
+                    String message = "The value stored in an Human Readable attribute as a byte[] should be convertible to a String";
+                    LOG.error( message );
+                    throw new NamingException( message );
+                }
+            }
+        }
+        else
+        {
+            if ( value instanceof String )
+            {
+                try
+                {
+                    return new ClientBinaryValue( ( ( String ) value ).getBytes( "UTF-8" ) );
+                }
+                catch ( UnsupportedEncodingException uee )
+                {
+                    String message = "The value stored in a non Human Readable attribute as a String should be convertible to a byte[]";
+                    LOG.error( message );
+                    throw new NamingException( message );
+                }
+            }
+        }
+
+        return null;
+    }
+
+
+    /**
+     * Check that the filter values are compatible with the AttributeType. Typically,
+     * a HumanReadible filter should have a String value. The substring filter should
+     * not be used with binary attributes.
+     */
+    private void checkFilter( ExprNode filter ) throws NamingException
+    {
+        if ( filter == null )
+        {
+            String message = "A filter should not be null";
+            LOG.error( message );
+            throw new NamingException( message );
+        }
+
+        if ( filter.isLeaf() )
+        {
+            if ( filter instanceof EqualityNode )
+            {
+                EqualityNode node = ( ( EqualityNode ) filter );
+                Object value = node.getValue();
+
+                Value<?> newValue = convert( node.getAttribute(), value );
+
+                if ( newValue != null )
+                {
+                    node.setValue( newValue );
+                }
+            }
+            else if ( filter instanceof SubstringNode )
+            {
+                SubstringNode node = ( ( SubstringNode ) filter );
+
+                if ( !atRegistry.lookup( node.getAttribute() ).getSyntax().isHumanReadable() )
+                {
+                    String message = "A Substring filter should be used only on Human Readable attributes";
+                    LOG.error( message );
+                    throw new NamingException( message );
+                }
+            }
+            else if ( filter instanceof PresenceNode )
+            {
+                // Nothing to do
+            }
+            else if ( filter instanceof GreaterEqNode )
+            {
+                GreaterEqNode node = ( ( GreaterEqNode ) filter );
+                Object value = node.getValue();
+
+                Value<?> newValue = convert( node.getAttribute(), value );
+
+                if ( newValue != null )
+                {
+                    node.setValue( newValue );
+                }
+
+            }
+            else if ( filter instanceof LessEqNode )
+            {
+                LessEqNode node = ( ( LessEqNode ) filter );
+                Object value = node.getValue();
+
+                Value<?> newValue = convert( node.getAttribute(), value );
+
+                if ( newValue != null )
+                {
+                    node.setValue( newValue );
+                }
+            }
+            else if ( filter instanceof ExtensibleNode )
+            {
+                ExtensibleNode node = ( ( ExtensibleNode ) filter );
+
+                if ( !atRegistry.lookup( node.getAttribute() ).getSyntax().isHumanReadable() )
+                {
+                    String message = "A Extensible filter should be used only on Human Readable attributes";
+                    LOG.error( message );
+                    throw new NamingException( message );
+                }
+            }
+            else if ( filter instanceof ApproximateNode )
+            {
+                ApproximateNode node = ( ( ApproximateNode ) filter );
+                Object value = node.getValue();
+
+                Value<?> newValue = convert( node.getAttribute(), value );
+
+                if ( newValue != null )
+                {
+                    node.setValue( newValue );
+                }
+            }
+            else if ( filter instanceof AssertionNode )
+            {
+                // Nothing to do
+                return;
+            }
+            else if ( filter instanceof ScopeNode )
+            {
+                // Nothing to do
+                return;
+            }
+        }
+        else
+        {
+            // Recursively iterate through all the children.
+            for ( ExprNode child : ( ( BranchNode ) filter ).getChildren() )
+            {
+                checkFilter( child );
+            }
+        }
+    }
+
+
+    /**
+     *
+     */
+    public NamingEnumeration<ServerSearchResult> search( NextInterceptor nextInterceptor,
+        SearchOperationContext opContext ) throws NamingException
+    {
+        LdapDN base = opContext.getDn();
+        SearchControls searchCtls = opContext.getSearchControls();
+        ExprNode filter = opContext.getFilter();
+
+        // We have to eliminate bad attributes from the request, accordingly
+        // to RFC 2251, chap. 4.5.1. Basically, all unknown attributes are removed
+        // from the list
+        filterAttributesToReturn( searchCtls );
+
+        // We also have to check the H/R flag for the filter attributes
+        checkFilter( filter );
+
+        String baseNormForm = ( base.isNormalized() ? base.getNormName() : base.toNormName() );
+
+        // Deal with the normal case : searching for a normal value (not subSchemaSubEntry)
+        if ( !subschemaSubentryDnNorm.equals( baseNormForm ) )
+        {
+            NamingEnumeration<ServerSearchResult> result = nextInterceptor.search( opContext );
+
+            Invocation invocation = InvocationStack.getInstance().peek();
+
+            if ( searchCtls.getReturningAttributes() != null )
+            {
+                return new SearchResultFilteringEnumeration( result, new SearchControls(), invocation, topFilter,
+                    "Search Schema Filter top" );
+            }
+
+            return new SearchResultFilteringEnumeration( result, searchCtls, invocation, filters,
+                "Search Schema Filter" );
+        }
+
+        // The user was searching into the subSchemaSubEntry
+        // This kind of search _must_ be limited to OBJECT scope (the subSchemaSubEntry
+        // does not have any sub level)
+        if ( searchCtls.getSearchScope() == SearchControls.OBJECT_SCOPE )
+        {
+            // The filter can be an equality or a presence, but nothing else
+            if ( filter instanceof SimpleNode )
+            {
+                // We should get the value for the filter.
+                // only 'top' and 'subSchema' are valid values
+                SimpleNode node = ( SimpleNode ) filter;
+                String objectClass;
+
+                if ( node.getValue() instanceof ClientStringValue )
+                {
+                    objectClass = ( String ) node.getValue().get();
+                }
+                else
+                {
+                    objectClass = node.getValue().get().toString();
+                }
+
+                String objectClassOid = null;
+
+                if ( registries.getObjectClassRegistry().hasObjectClass( objectClass ) )
+                {
+                    objectClassOid = registries.getObjectClassRegistry().lookup( objectClass ).getOid();
+                }
+                else
+                {
+                    return new EmptyEnumeration<ServerSearchResult>();
+                }
+
+                String nodeOid = registries.getOidRegistry().getOid( node.getAttribute() );
+
+                // see if node attribute is objectClass
+                if ( nodeOid.equals( SchemaConstants.OBJECT_CLASS_AT_OID )
+                    && ( objectClassOid.equals( SchemaConstants.TOP_OC_OID ) || objectClassOid
+                        .equals( SchemaConstants.SUBSCHEMA_OC_OID ) ) && ( node instanceof EqualityNode ) )
+                {
+                    // call.setBypass( true );
+                    ServerEntry serverEntry = schemaService.getSubschemaEntry( searchCtls.getReturningAttributes() );
+                    ServerSearchResult result = new ServerSearchResult( base, null, serverEntry );
+                    return new SingletonEnumeration<ServerSearchResult>( result );
+                }
+                else
+                {
+                    return new EmptyEnumeration<ServerSearchResult>();
+                }
+            }
+            else if ( filter instanceof PresenceNode )
+            {
+                PresenceNode node = ( PresenceNode ) filter;
+
+                // see if node attribute is objectClass
+                if ( node.getAttribute().equals( SchemaConstants.OBJECT_CLASS_AT_OID ) )
+                {
+                    // call.setBypass( true );
+                    ServerEntry serverEntry = schemaService.getSubschemaEntry( searchCtls.getReturningAttributes() );
+                    ServerSearchResult result = new ServerSearchResult( base, null, serverEntry, false );
+                    return new SingletonEnumeration<ServerSearchResult>( result );
+                }
+            }
+        }
+
+        // In any case not handled previously, just return an empty result
+        return new EmptyEnumeration<ServerSearchResult>();
+    }
+
+
+    /**
+     * Search for an entry, using its DN. Binary attributes and ObjectClass attribute are removed.
+     */
+    public ServerEntry lookup( NextInterceptor nextInterceptor, LookupOperationContext opContext )
+        throws NamingException
+    {
+        ServerEntry result = nextInterceptor.lookup( opContext );
+
+        if ( result == null )
+        {
+            return null;
+        }
+
+        filterBinaryAttributes( result );
+        filterObjectClass( result );
+
+        return result;
+    }
+
+
+    private void getSuperiors( ObjectClass oc, Set<String> ocSeen, List<ObjectClass> result ) throws NamingException
+    {
+        for ( ObjectClass parent : oc.getSuperClasses() )
+        {
+            // Skip 'top'
+            if ( SchemaConstants.TOP_OC.equals( parent.getName() ) )
+            {
+                continue;
+            }
+
+            if ( !ocSeen.contains( parent.getOid() ) )
+            {
+                ocSeen.add( parent.getOid() );
+                result.add( parent );
+            }
+
+            // Recurse on the parent
+            getSuperiors( parent, ocSeen, result );
+        }
+    }
+
+
+    /**
+     * Checks to see if an attribute is required by as determined from an entry's
+     * set of objectClass attribute values.
+     *
+     * @param attrId the attribute to test if required by a set of objectClass values
+     * @param objectClass the objectClass values
+     * @return true if the objectClass values require the attribute, false otherwise
+     * @throws NamingException if the attribute is not recognized
+     */
+    private boolean isRequired( String attrId, EntryAttribute objectClasses ) throws NamingException
+    {
+        OidRegistry oidRegistry = registries.getOidRegistry();
+        ObjectClassRegistry registry = registries.getObjectClassRegistry();
+
+        if ( !oidRegistry.hasOid( attrId ) )
+        {
+            return false;
+        }
+
+        String attrOid = oidRegistry.getOid( attrId );
+
+        for ( Value<?> objectClass : objectClasses )
+        {
+            ObjectClass ocSpec = registry.lookup( ( String ) objectClass.get() );
+
+            for ( AttributeType must : ocSpec.getMustList() )
+            {
+                if ( must.getOid().equals( attrOid ) )
+                {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+
+    /**
+     * Checks to see if removing a set of attributes from an entry completely removes
+     * that attribute's values.  If change has zero size then all attributes are
+     * presumed to be removed.
+     *
+     * @param change
+     * @param entry
+     * @return
+     * @throws NamingException
+     */
+    private boolean isCompleteRemoval( ServerAttribute change, ServerEntry entry ) throws NamingException
+    {
+        // if change size is 0 then all values are deleted then we're in trouble
+        if ( change.size() == 0 )
+        {
+            return true;
+        }
+
+        // can't do math to figure our if all values are removed since some
+        // values in the modify request may not be in the entry.  we need to
+        // remove the values from a cloned version of the attribute and see
+        // if nothing is left.
+        ServerAttribute changedEntryAttr = ( ServerAttribute ) entry.get( change.getUpId() ).clone();
+
+        for ( Value<?> value : change )
+        {
+            changedEntryAttr.remove( value );
+        }
+
+        return changedEntryAttr.size() == 0;
+    }
+
+
+    /**
+     * 
+     * @param modOp
+     * @param changes
+     * @param existing
+     * @return
+     * @throws NamingException
+     */
+    private EntryAttribute getResultantObjectClasses( ModificationOperation modOp, EntryAttribute changes,
+        EntryAttribute existing ) throws NamingException
+    {
+        if ( ( changes == null ) && ( existing == null ) )
+        {
+            return new DefaultServerAttribute( SchemaConstants.OBJECT_CLASS_AT, OBJECT_CLASS );
+        }
+
+        if ( changes == null )
+        {
+            return existing;
+        }
+
+        if ( ( existing == null ) && ( modOp == ModificationOperation.ADD_ATTRIBUTE ) )
+        {
+            return changes;
+        }
+        else if ( existing == null )
+        {
+            return new DefaultServerAttribute( SchemaConstants.OBJECT_CLASS_AT, OBJECT_CLASS );
+        }
+
+        switch ( modOp )
+        {
+            case ADD_ATTRIBUTE:
+                for ( Value<?> value : changes )
+                {
+                    existing.add( value );
+                }
+
+                return existing;
+
+            case REPLACE_ATTRIBUTE:
+                return ( ServerAttribute ) changes.clone();
+
+            case REMOVE_ATTRIBUTE:
+                for ( Value<?> value : changes )
+                {
+                    existing.remove( value );
+                }
+
+                return existing;
+
+            default:
+                throw new InternalError( "" );
+        }
+    }
+
+
+    private boolean getObjectClasses( EntryAttribute objectClasses, List<ObjectClass> result ) throws NamingException
+    {
+        Set<String> ocSeen = new HashSet<String>();
+        ObjectClassRegistry registry = registries.getObjectClassRegistry();
+
+        // We must select all the ObjectClasses, except 'top',
+        // but including all the inherited ObjectClasses
+        boolean hasExtensibleObject = false;
+
+        for ( Value<?> objectClass : objectClasses )
+        {
+            String objectClassName = ( String ) objectClass.get();
+
+            if ( SchemaConstants.TOP_OC.equals( objectClassName ) )
+            {
+                continue;
+            }
+
+            if ( SchemaConstants.EXTENSIBLE_OBJECT_OC.equalsIgnoreCase( objectClassName ) )
+            {
+                hasExtensibleObject = true;
+            }
+
+            ObjectClass oc = registry.lookup( objectClassName );
+
+            // Add all unseen objectClasses to the list, except 'top'
+            if ( !ocSeen.contains( oc.getOid() ) )
+            {
+                ocSeen.add( oc.getOid() );
+                result.add( oc );
+            }
+
+            // Find all current OC parents
+            getSuperiors( oc, ocSeen, result );
+        }
+
+        return hasExtensibleObject;
+    }
+
+
+    private Set<String> getAllMust( EntryAttribute objectClasses ) throws NamingException
+    {
+        Set<String> must = new HashSet<String>();
+
+        // Loop on all objectclasses
+        for ( Value<?> value : objectClasses )
+        {
+            String ocName = ( String ) value.get();
+            ObjectClass oc = registries.getObjectClassRegistry().lookup( ocName );
+
+            AttributeType[] types = oc.getMustList();
+
+            // For each objectClass, loop on all MUST attributeTypes, if any
+            if ( ( types != null ) && ( types.length > 0 ) )
+            {
+                for ( AttributeType type : types )
+                {
+                    must.add( type.getOid() );
+                }
+            }
+        }
+
+        return must;
+    }
+
+
+    private Set<String> getAllAllowed( EntryAttribute objectClasses, Set<String> must ) throws NamingException
+    {
+        Set<String> allowed = new HashSet<String>( must );
+
+        // Add the 'ObjectClass' attribute ID
+        allowed.add( registries.getOidRegistry().getOid( SchemaConstants.OBJECT_CLASS_AT ) );
+
+        // Loop on all objectclasses
+        for ( Value<?> objectClass : objectClasses )
+        {
+            String ocName = ( String ) objectClass.get();
+            ObjectClass oc = registries.getObjectClassRegistry().lookup( ocName );
+
+            AttributeType[] types = oc.getMayList();
+
+            // For each objectClass, loop on all MAY attributeTypes, if any
+            if ( ( types != null ) && ( types.length > 0 ) )
+            {
+                for ( AttributeType type : types )
+                {
+                    String oid = type.getOid();
+
+                    allowed.add( oid );
+                }
+            }
+        }
+
+        return allowed;
+    }
+
+
+    /**
+     * Given the objectClasses for an entry, this method adds missing ancestors 
+     * in the hierarchy except for top which it removes.  This is used for this
+     * solution to DIREVE-276.  More information about this solution can be found
+     * <a href="http://docs.safehaus.org:8080/x/kBE">here</a>.
+     * 
+     * @param objectClassAttr the objectClass attribute to modify
+     * @throws NamingException if there are problems 
+     */
+    private void alterObjectClasses( EntryAttribute objectClassAttr ) throws NamingException
+    {
+        Set<String> objectClasses = new HashSet<String>();
+        Set<String> objectClassesUP = new HashSet<String>();
+
+        // Init the objectClass list with 'top'
+        objectClasses.add( SchemaConstants.TOP_OC );
+        objectClassesUP.add( SchemaConstants.TOP_OC );
+
+        // Construct the new list of ObjectClasses
+        for ( Value<?> ocValue : objectClassAttr )
+        {
+            String ocName = ( String ) ocValue.get();
+
+            if ( !ocName.equalsIgnoreCase( SchemaConstants.TOP_OC ) )
+            {
+                String ocLowerName = ocName.toLowerCase();
+
+                ObjectClass objectClass = registries.getObjectClassRegistry().lookup( ocLowerName );
+
+                if ( !objectClasses.contains( ocLowerName ) )
+                {
+                    objectClasses.add( ocLowerName );
+                    objectClassesUP.add( ocName );
+                }
+
+                List<ObjectClass> ocSuperiors = superiors.get( objectClass.getOid() );
+
+                if ( ocSuperiors != null )
+                {
+                    for ( ObjectClass oc : ocSuperiors )
+                    {
+                        if ( !objectClasses.contains( oc.getName().toLowerCase() ) )
+                        {
+                            objectClasses.add( oc.getName() );
+                            objectClassesUP.add( oc.getName() );
+                        }
+                    }
+                }
+            }
+        }
+
+        // Now, reset the ObjectClass attribute and put the new list into it
+        objectClassAttr.clear();
+
+        for ( String attribute : objectClassesUP )
+        {
+            objectClassAttr.add( attribute );
+        }
+    }
+
+
+    public void moveAndRename( NextInterceptor next, MoveAndRenameOperationContext opContext ) throws NamingException
+    {
+        LdapDN oriChildName = opContext.getDn();
+
+        ServerEntry entry = nexus.lookup( new LookupOperationContext( registries, oriChildName ) );
+
+        if ( oriChildName.startsWith( schemaBaseDN ) )
+        {
+            schemaManager.move( oriChildName, opContext.getParent(), opContext.getNewRdn(), opContext.getDelOldDn(),
+                entry, opContext.hasRequestControl( CascadeControl.CONTROL_OID ) );
+        }
+
+        next.moveAndRename( opContext );
+    }
+
+
+    public void move( NextInterceptor next, MoveOperationContext opContext ) throws NamingException
+    {
+        LdapDN oriChildName = opContext.getDn();
+
+        ServerEntry entry = nexus.lookup( new LookupOperationContext( registries, oriChildName ) );
+
+        if ( oriChildName.startsWith( schemaBaseDN ) )
+        {
+            schemaManager.replace( oriChildName, opContext.getParent(), entry, opContext
+                .hasRequestControl( CascadeControl.CONTROL_OID ) );
+        }
+
+        next.move( opContext );
+    }
+
+
+    public void rename( NextInterceptor next, RenameOperationContext opContext ) throws NamingException
+    {
+        LdapDN name = opContext.getDn();
+        Rdn newRdn = opContext.getNewRdn();
+        boolean deleteOldRn = opContext.getDelOldDn();
+        ServerEntry entry = nexus.lookup( new LookupOperationContext( registries, name ) );
+
+        if ( deleteOldRn )
+        {
+            ServerEntry tmpEntry = ( ServerEntry ) entry.clone();
+            Rdn oldRDN = name.getRdn();
+
+            // Delete the old RDN means we remove some attributes and values.
+            // We must make sure that after this operation all must attributes
+            // are still present in the entry.
+            for ( AttributeTypeAndValue atav : oldRDN )
+            {
+                AttributeType type = atRegistry.lookup( atav.getUpType() );
+                String value = ( String ) atav.getNormValue();
+                tmpEntry.remove( type, value );
+            }
+            for ( AttributeTypeAndValue atav : newRdn )
+            {
+                AttributeType type = atRegistry.lookup( atav.getUpType() );
+                String value = ( String ) atav.getNormValue();
+                if ( !tmpEntry.contains( type, value ) )
+                {
+                    tmpEntry.add( new DefaultServerAttribute( type, value ) );
+                }
+            }
+            check( name, tmpEntry );
+
+            // Check that no operational attributes are removed
+            for ( AttributeTypeAndValue atav : oldRDN )
+            {
+                AttributeType attributeType = atRegistry.lookup( atav.getUpType() );
+                if ( !attributeType.isCanUserModify() )
+                {
+                    throw new NoPermissionException( "Cannot modify the attribute '" + atav.getUpType() + "'" );
+                }
+            }
+        }
+
+        if ( name.startsWith( schemaBaseDN ) )
+        {
+            schemaManager.modifyRn( name, newRdn, deleteOldRn, entry, opContext
+                .hasRequestControl( CascadeControl.CONTROL_OID ) );
+        }
+
+        next.rename( opContext );
+    }
+
+
+    public void modify( NextInterceptor next, ModifyOperationContext opContext ) throws NamingException
+    {
+        ServerEntry entry;
+        LdapDN name = opContext.getDn();
+        List<Modification> mods = opContext.getModItems();
+
+        // handle operations against the schema subentry in the schema service
+        // and never try to look it up in the nexus below
+        if ( name.getNormName().equalsIgnoreCase( subschemaSubentryDnNorm ) )
+        {
+            entry = schemaService.getSubschemaEntry( SCHEMA_SUBENTRY_RETURN_ATTRIBUTES );
+        }
+        else
+        {
+            entry = nexus.lookup( new LookupOperationContext( registries, name ) );
+        }
+
+        // First, we get the entry from the backend. If it does not exist, then we throw an exception
+        ServerEntry targetEntry = ServerEntryUtils.toServerEntry( SchemaUtils.getTargetEntry( ServerEntryUtils
+            .toModificationItemImpl( mods ), ServerEntryUtils.toAttributesImpl( entry ) ), name, registries );
+
+        if ( entry == null )
+        {
+            LOG.error( "No entry with this name :{}", name );
+            throw new LdapNameNotFoundException( "The entry which name is " + name + " is not found." );
+        }
+
+        // We will use this temporary entry to check that the modifications
+        // can be applied as atomic operations
+        ServerEntry tmpEntry = ( ServerEntry ) entry.clone();
+
+        Set<String> modset = new HashSet<String>();
+        Modification objectClassMod = null;
+
+        // Check that we don't have two times the same modification.
+        // This is somehow useless, as modification operations are supposed to
+        // be atomic, so we may have a sucession of Add, DEL, ADD operations
+        // for the same attribute, and this will be legal.
+        // @TODO : check if we can remove this test.
+        for ( Modification mod : mods )
+        {
+            if ( mod.getAttribute().getId().equalsIgnoreCase( SchemaConstants.OBJECT_CLASS_AT ) )
+            {
+                objectClassMod = mod;
+            }
+
+            // Freak out under some weird cases
+            if ( mod.getAttribute().size() == 0 )
+            {
+                // not ok for add but ok for replace and delete
+                if ( mod.getOperation() == ModificationOperation.ADD_ATTRIBUTE )
+                {
+                    throw new LdapInvalidAttributeValueException( "No value is not a valid value for an attribute.",
+                        ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX );
+                }
+            }
+
+            StringBuffer keybuf = new StringBuffer();
+            keybuf.append( mod.getOperation() );
+            keybuf.append( mod.getAttribute().getId() );
+
+            for ( Value<?> value : ( ServerAttribute ) mod.getAttribute() )
+            {
+                keybuf.append( value.get() );
+            }
+
+            if ( !modset.add( keybuf.toString() ) && ( mod.getOperation() == ModificationOperation.ADD_ATTRIBUTE ) )
+            {
+                throw new LdapAttributeInUseException( "found two copies of the following modification item: " + mod );
+            }
+        }
+
+        // Get the objectClass attribute.
+        EntryAttribute objectClass;
+
+        if ( objectClassMod == null )
+        {
+            objectClass = entry.get( SchemaConstants.OBJECT_CLASS_AT );
+
+            if ( objectClass == null )
+            {
+                objectClass = new DefaultServerAttribute( SchemaConstants.OBJECT_CLASS_AT, OBJECT_CLASS );
+            }
+        }
+        else
+        {
+            objectClass = getResultantObjectClasses( objectClassMod.getOperation(), objectClassMod.getAttribute(),
+                tmpEntry.get( SchemaConstants.OBJECT_CLASS_AT ) );
+        }
+
+        ObjectClassRegistry ocRegistry = this.registries.getObjectClassRegistry();
+
+        // Now, apply the modifications on the cloned entry before applying it on the
+        // real object.
+        for ( Modification mod : mods )
+        {
+            ModificationOperation modOp = mod.getOperation();
+            ServerAttribute change = ( ServerAttribute ) mod.getAttribute();
+
+            if ( !atRegistry.hasAttributeType( change.getUpId() )
+                && !objectClass.contains( SchemaConstants.EXTENSIBLE_OBJECT_OC ) )
+            {
+                throw new LdapInvalidAttributeIdentifierException();
+            }
+
+            // We will forbid modification of operational attributes which are not
+            // user modifiable.
+            AttributeType attributeType = atRegistry.lookup( change.getUpId() );
+
+            if ( !attributeType.isCanUserModify() )
+            {
+                throw new NoPermissionException( "Cannot modify the attribute '" + change.getUpId() + "'" );
+            }
+
+            switch ( modOp )
+            {
+                case ADD_ATTRIBUTE:
+                    EntryAttribute attr = tmpEntry.get( change.getUpId() );
+
+                    if ( attr != null )
+                    {
+                        for ( Value<?> value : change )
+                        {
+                            attr.add( value );
+                        }
+                    }
+                    else
+                    {
+                        attr = new DefaultServerAttribute( change.getUpId(), attributeType );
+
+                        for ( Value<?> value : change )
+                        {
+                            attr.add( value );
+                        }
+
+                        tmpEntry.put( attr );
+                    }
+
+                    break;
+
+                case REMOVE_ATTRIBUTE:
+                    // First check that the attributeType exists
+                    if ( tmpEntry.get( change.getUpId() ) == null )
+                    {
+                        LOG.error( "Trying to remove an non-existant attribute: " + change.getUpId() );
+                        throw new LdapNoSuchAttributeException();
+                    }
+                    
+                    // Then check that all the values exists
+                    /* TODO reactivate this code when ChangeLog is fixed
+                    EntryAttribute toBeRemoved = tmpEntry.get( change.getUpId() );
+                    
+                    for ( Value<?> val:change )
+                    {
+                        if ( !toBeRemoved.contains( val ) )
+                        {
+                            LOG.error( "Trying to remove an non-existant value '{}' for attribute '{}': ", val, change.getUpId() );
+                            throw new LdapNoSuchAttributeException();
+                        }
+                    }
+                    */
+
+                    // We may have to remove the attribute or only some values
+                    if ( change.size() == 0 )
+                    {
+                        // No value : we have to remove the entire attribute
+                        // Check that we aren't removing a MUST attribute
+                        if ( isRequired( change.getUpId(), objectClass ) )
+                        {
+                            LOG.error( "Trying to remove a required attribute: " + change.getUpId() );
+                            throw new LdapSchemaViolationException( ResultCodeEnum.OBJECT_CLASS_VIOLATION );
+                        }
+                    }
+                    else
+                    {
+                        // for required attributes we need to check if all values are removed
+                        // if so then we have a schema violation that must be thrown
+                        if ( isRequired( change.getUpId(), objectClass ) && isCompleteRemoval( change, entry ) )
+                        {
+                            LOG.error( "Trying to remove a required attribute: " + change.getUpId() );
+                            throw new LdapSchemaViolationException( ResultCodeEnum.OBJECT_CLASS_VIOLATION );
+                        }
+
+                        // Now remove the attribute and all its values
+                        EntryAttribute modified = tmpEntry.removeAttributes( change.getUpId() ).get( 0 );
+
+                        // And inject back the values except the ones to remove
+                        for ( Value<?> value : change )
+                        {
+                            modified.remove( value );
+                        }
+
+                        // ok, done. Last check : if the attribute does not content any more value;
+                        // and if it's a MUST one, we should thow an exception
+                        if ( ( modified.size() == 0 ) && isRequired( change.getUpId(), objectClass ) )
+                        {
+                            LOG.error( "Trying to remove a required attribute: " + change.getUpId() );
+                            throw new LdapSchemaViolationException( ResultCodeEnum.OBJECT_CLASS_VIOLATION );
+                        }
+
+                        // Put back the attribute in the entry only if it has values left in it
+                        if ( modified.size() > 0 )
+                        {
+                            tmpEntry.put( modified );
+                        }
+                    }
+
+                    SchemaChecker
+                        .preventRdnChangeOnModifyRemove( name, modOp, change, this.registries.getOidRegistry() );
+                    SchemaChecker.preventStructuralClassRemovalOnModifyRemove( ocRegistry, name, modOp, change,
+                        objectClass );
+                    break;
+
+                case REPLACE_ATTRIBUTE:
+                    SchemaChecker.preventRdnChangeOnModifyReplace( name, modOp, change, registries.getOidRegistry() );
+                    SchemaChecker.preventStructuralClassRemovalOnModifyReplace( ocRegistry, name, modOp, change );
+
+                    attr = tmpEntry.get( change.getUpId() );
+
+                    if ( attr != null )
+                    {
+                        tmpEntry.removeAttributes( change.getUpId() );
+                    }
+
+                    attr = new DefaultServerAttribute( change.getUpId(), attributeType );
+
+                    if ( change.size() != 0 )
+                    {
+                        for ( Value<?> value : change )
+                        {
+                            attr.add( value );
+                        }
+
+                        tmpEntry.put( attr );
+                    }
+
+                    break;
+            }
+        }
+
+        check( name, tmpEntry );
+
+        // let's figure out if we need to add or take away from mods to maintain 
+        // the objectClass attribute with it's hierarchy of ancestors 
+        if ( objectClassMod != null )
+        {
+            ServerAttribute alteredObjectClass = ( ServerAttribute ) objectClass.clone();
+            alterObjectClasses( alteredObjectClass );
+
+            if ( !alteredObjectClass.equals( objectClass ) )
+            {
+                ServerAttribute ocMods = ( ServerAttribute ) objectClassMod.getAttribute();
+
+                switch ( objectClassMod.getOperation() )
+                {
+                    case ADD_ATTRIBUTE:
+                        if ( ocMods.contains( SchemaConstants.TOP_OC ) )
+                        {
+                            ocMods.remove( SchemaConstants.TOP_OC );
+                        }
+
+                        for ( Value<?> value : alteredObjectClass )
+                        {
+                            if ( !objectClass.contains( value ) )
+                            {
+                                ocMods.add( value );
+                            }
+                        }
+
+                        break;
+
+                    case REMOVE_ATTRIBUTE:
+                        for ( Value<?> value : alteredObjectClass )
+                        {
+                            if ( !objectClass.contains( value ) )
+                            {
+                                ocMods.remove( value );
+                            }
+                        }
+
+                        break;
+
+                    case REPLACE_ATTRIBUTE:
+                        for ( Value<?> value : alteredObjectClass )
+                        {
+                            if ( !objectClass.contains( value ) )
+                            {
+                                ocMods.add( value );
+                            }
+                        }
+
+                        break;
+
+                    default:
+                }
+            }
+        }
+
+        if ( name.startsWith( schemaBaseDN ) )
+        {
+            LOG.debug( "Modification attempt on schema partition {}: \n{}", name, opContext );
+
+            schemaManager.modify( name, mods, entry, targetEntry, opContext
+                .hasRequestControl( CascadeControl.CONTROL_OID ) );
+        }
+        else if ( subschemaSubentryDnNorm.equals( name.getNormName() ) )
+        {
+            LOG.debug( "Modification attempt on schema subentry {}: \n{}", name, opContext );
+
+            schemaManager.modifySchemaSubentry( name, mods, entry, targetEntry, opContext
+                .hasRequestControl( CascadeControl.CONTROL_OID ) );
+            return;
+        }
+
+        next.modify( opContext );
+    }
+
+
+    private void filterObjectClass( ServerEntry entry ) throws NamingException
+    {
+        List<ObjectClass> objectClasses = new ArrayList<ObjectClass>();
+        EntryAttribute oc = entry.get( SchemaConstants.OBJECT_CLASS_AT );
+
+        if ( oc != null )
+        {
+            getObjectClasses( oc, objectClasses );
+
+            entry.removeAttributes( SchemaConstants.OBJECT_CLASS_AT );
+
+            ServerAttribute newOc = new DefaultServerAttribute( ( ( ServerAttribute ) oc ).getAttributeType() );
+
+            for ( ObjectClass currentOC : objectClasses )
+            {
+                newOc.add( currentOC.getName() );
+            }
+
+            newOc.add( SchemaConstants.TOP_OC );
+            entry.put( newOc );
+        }
+    }
+
+
+    private void filterBinaryAttributes( ServerEntry entry ) throws NamingException
+    {
+        /*
+         * start converting values of attributes to byte[]s which are not
+         * human readable and those that are in the binaries set
+         */
+        for ( EntryAttribute attribute : entry )
+        {
+            if ( !( ( ServerAttribute ) attribute ).getAttributeType().getSyntax().isHumanReadable() )
+            {
+                List<Value<?>> binaries = new ArrayList<Value<?>>();
+
+                for ( Value<?> value : attribute )
+                {
+                    Object attrValue = value.get();
+
+                    if ( attrValue instanceof String )
+                    {
+                        binaries.add( new ServerBinaryValue( ( ( ServerAttribute ) attribute ).getAttributeType(),
+                            StringTools.getBytesUtf8( ( String ) attrValue ) ) );
+                    }
+                    else
+                    {
+                        binaries.add( new ServerBinaryValue( ( ( ServerAttribute ) attribute ).getAttributeType(),
+                            ( byte[] ) attrValue ) );
+                    }
+                }
+
+                attribute.clear();
+                attribute.put( binaries );
+            }
+        }
+    }
+
+    /**
+     * A special filter over entry attributes which replaces Attribute String values with their respective byte[]
+     * representations using schema information and the value held in the JNDI environment property:
+     * <code>java.naming.ldap.attributes.binary</code>.
+     *
+     * @see <a href= "http://java.sun.com/j2se/1.4.2/docs/guide/jndi/jndi-ldap-gl.html#binary">
+     *      java.naming.ldap.attributes.binary</a>
+     */
+    private class BinaryAttributeFilter implements SearchResultFilter
+    {
+        public boolean accept( Invocation invocation, ServerSearchResult result, SearchControls controls )
+            throws NamingException
+        {
+            filterBinaryAttributes( result.getServerEntry() );
+            return true;
+        }
+    }
+
+    /**
+     * Filters objectClass attribute to inject top when not present.
+     */
+    private class TopFilter implements SearchResultFilter
+    {
+        public boolean accept( Invocation invocation, ServerSearchResult result, SearchControls controls )
+            throws NamingException
+        {
+            filterObjectClass( result.getServerEntry() );
+
+            return true;
+        }
+    }
+
+
+    /**
+     * Check that all the attributes exist in the schema for this entry.
+     * 
+     * We also check the syntaxes
+     */
+    private void check( LdapDN dn, ServerEntry entry ) throws NamingException
+    {
+        // ---------------------------------------------------------------
+        // First, make sure all attributes are valid schema defined attributes
+        // ---------------------------------------------------------------
+
+        for ( AttributeType attributeType : entry.getAttributeTypes() )
+        {
+            if ( !atRegistry.hasAttributeType( attributeType.getName() ) )
+            {
+                throw new LdapInvalidAttributeIdentifierException( attributeType.getName()
+                    + " not found in attribute registry!" );
+            }
+        }
+
+        // We will check some elements :
+        // 1) the entry must have all the MUST attributes of all its ObjectClass
+        // 2) The SingleValued attributes must be SingleValued
+        // 3) No attributes should be used if they are not part of MUST and MAY
+        // 3-1) Except if the extensibleObject ObjectClass is used
+        // 3-2) or if the AttributeType is COLLECTIVE
+        // 4) We also check that for H-R attributes, we have a valid String in the values
+        EntryAttribute objectClassAttr = entry.get( SchemaConstants.OBJECT_CLASS_AT );
+
+        // Protect the server against a null objectClassAttr
+        // It can be the case if the user forgot to add it to the entry ...
+        // In this case, we create an new one, empty
+        if ( objectClassAttr == null )
+        {
+            objectClassAttr = new DefaultServerAttribute( SchemaConstants.OBJECT_CLASS_AT, OBJECT_CLASS );
+        }
+
+        List<ObjectClass> ocs = new ArrayList<ObjectClass>();
+
+        alterObjectClasses( objectClassAttr );
+
+        // Now we can process the MUST and MAY attributes
+        Set<String> must = getAllMust( objectClassAttr );
+        Set<String> allowed = getAllAllowed( objectClassAttr, must );
+
+        boolean hasExtensibleObject = getObjectClasses( objectClassAttr, ocs );
+
+        // As we now have all the ObjectClasses updated, we have
+        // to check that we don't have conflicting ObjectClasses
+        assertObjectClasses( dn, ocs );
+
+        assertRequiredAttributesPresent( dn, entry, must );
+        assertNumberOfAttributeValuesValid( entry );
+
+        if ( !hasExtensibleObject )
+        {
+            assertAllAttributesAllowed( dn, entry, allowed );
+        }
+
+        // Check the attributes values and transform them to String if necessary
+        assertHumanReadable( entry );
+
+        // Now check the syntaxes
+        assertSyntaxes( entry );
+    }
+
+
+    /**
+     * Check that all the attributes exist in the schema for this entry.
+     */
+    public void add( NextInterceptor next, AddOperationContext addContext ) throws NamingException
+    {
+        LdapDN name = addContext.getDn();
+        ServerEntry entry = addContext.getEntry();
+
+        check( name, entry );
+
+        if ( name.startsWith( schemaBaseDN ) )
+        {
+            schemaManager.add( name, entry );
+        }
+
+        next.add( addContext );
+    }
+
+
+    /**
+     * Checks to see if an attribute is required by as determined from an entry's
+     * set of objectClass attribute values.
+     *
+     * @return true if the objectClass values require the attribute, false otherwise
+     * @throws NamingException if the attribute is not recognized
+     */
+    private void assertAllAttributesAllowed( LdapDN dn, ServerEntry entry, Set<String> allowed ) throws NamingException
+    {
+        // Never check the attributes if the extensibleObject objectClass is
+        // declared for this entry
+        EntryAttribute objectClass = entry.get( SchemaConstants.OBJECT_CLASS_AT );
+
+        if ( objectClass.contains( SchemaConstants.EXTENSIBLE_OBJECT_OC ) )
+        {
+            return;
+        }
+
+        for ( EntryAttribute attribute : entry )
+        {
+            String attrOid = ( ( ServerAttribute ) attribute ).getAttributeType().getOid();
+
+            AttributeType attributeType = ( ( ServerAttribute ) attribute ).getAttributeType();
+
+            if ( !attributeType.isCollective() && ( attributeType.getUsage() == UsageEnum.USER_APPLICATIONS ) )
+            {
+                if ( !allowed.contains( attrOid ) )
+                {
+                    throw new LdapSchemaViolationException( "Attribute " + attribute.getUpId()
+                        + " not declared in objectClasses of entry " + dn.getUpName(),
+                        ResultCodeEnum.OBJECT_CLASS_VIOLATION );
+                }
+            }
+        }
+    }
+
+
+    public void delete( NextInterceptor next, DeleteOperationContext opContext ) throws NamingException
+    {
+        LdapDN name = opContext.getDn();
+
+        if ( name.startsWith( schemaBaseDN ) )
+        {
+            ServerEntry entry = nexus.lookup( new LookupOperationContext( registries, name ) );
+            schemaManager.delete( name, entry, opContext.hasRequestControl( CascadeControl.CONTROL_OID ) );
+        }
+
+        next.delete( opContext );
+    }
+
+
+    /**
+     * Checks to see number of values of an attribute conforms to the schema
+     */
+    private void assertNumberOfAttributeValuesValid( Entry entry ) throws InvalidAttributeValueException,
+        NamingException
+    {
+        for ( EntryAttribute attribute : entry )
+        {
+            assertNumberOfAttributeValuesValid( attribute );
+        }
+    }
+
+
+    /**
+     * Checks to see numbers of values of attributes conforms to the schema
+     */
+    private void assertNumberOfAttributeValuesValid( EntryAttribute attribute ) throws InvalidAttributeValueException,
+        NamingException
+    {
+        if ( attribute.size() > 1 && ( ( ServerAttribute ) attribute ).getAttributeType().isSingleValue() )
+        {
+            throw new LdapInvalidAttributeValueException( "More than one value has been provided "
+                + "for the single-valued attribute: " + attribute.getUpId(), ResultCodeEnum.CONSTRAINT_VIOLATION );
+        }
+    }
+
+
+    /**
+     * Checks to see the presence of all required attributes within an entry.
+     */
+    private void assertRequiredAttributesPresent( LdapDN dn, Entry entry, Set<String> must ) throws NamingException
+    {
+        for ( EntryAttribute attribute : entry )
+        {
+            must.remove( ( ( ServerAttribute ) attribute ).getAttributeType().getOid() );
+        }
+
+        if ( must.size() != 0 )
+        {
+            throw new LdapSchemaViolationException( "Required attributes " + must + " not found within entry "
+                + dn.getUpName(), ResultCodeEnum.OBJECT_CLASS_VIOLATION );
+        }
+    }
+
+
+    /**
+     * Checck that OC does not conflict :
+     * - we can't have more than one STRUCTURAL OC unless they are in the same
+     * inheritance tree
+     * - we must have at least one STRUCTURAL OC
+     */
+    private void assertObjectClasses( LdapDN dn, List<ObjectClass> ocs ) throws NamingException
+    {
+        Set<ObjectClass> structuralObjectClasses = new HashSet<ObjectClass>();
+
+        /*
+         * Since the number of ocs present in an entry is small it's not 
+         * so expensive to take two passes while determining correctness
+         * since it will result in clear simple code instead of a deep nasty
+         * for loop with nested loops.  Plus after the first pass we can
+         * quickly know if there are no structural object classes at all.
+         */
+
+        // --------------------------------------------------------------------
+        // Extract all structural objectClasses within the entry
+        // --------------------------------------------------------------------
+        for ( ObjectClass oc : ocs )
+        {
+            if ( oc.isStructural() )
+            {
+                structuralObjectClasses.add( oc );
+            }
+        }
+
+        // --------------------------------------------------------------------
+        // Throw an error if no STRUCTURAL objectClass are found.
+        // --------------------------------------------------------------------
+
+        if ( structuralObjectClasses.isEmpty() )
+        {
+            String message = "Entry " + dn + " does not contain a STRUCTURAL ObjectClass";
+            LOG.error( message );
+            throw new LdapSchemaViolationException( message, ResultCodeEnum.OBJECT_CLASS_VIOLATION );
+        }
+
+        // --------------------------------------------------------------------
+        // Put all structural object classes into new remaining container and
+        // start removing any which are superiors of others in the set.  What
+        // is left in the remaining set will be unrelated structural 
+        /// objectClasses.  If there is more than one then we have a problem.
+        // --------------------------------------------------------------------
+
+        Set<ObjectClass> remaining = new HashSet<ObjectClass>( structuralObjectClasses.size() );
+        remaining.addAll( structuralObjectClasses );
+        for ( ObjectClass oc : structuralObjectClasses )
+        {
+            if ( oc.getSuperClasses() != null )
+            {
+                for ( ObjectClass superClass : oc.getSuperClasses() )
+                {
+                    if ( superClass.isStructural() )
+                    {
+                        remaining.remove( superClass );
+                    }
+                }
+            }
+        }
+
+        // Like the highlander there can only be one :).
+        if ( remaining.size() > 1 )
+        {
+            String message = "Entry " + dn + " contains more than one STRUCTURAL ObjectClass: " + remaining;
+            LOG.error( message );
+            throw new LdapSchemaViolationException( message, ResultCodeEnum.OBJECT_CLASS_VIOLATION );
+        }
+    }
+
+
+    /**
+     * Check the entry attributes syntax, using the syntaxCheckers
+     */
+    private void assertSyntaxes( Entry entry ) throws NamingException
+    {
+        // First, loop on all attributes
+        for ( EntryAttribute attribute : entry )
+        {
+            AttributeType attributeType = ( ( ServerAttribute ) attribute ).getAttributeType();
+            SyntaxChecker syntaxChecker = attributeType.getSyntax().getSyntaxChecker();
+
+            if ( syntaxChecker instanceof AcceptAllSyntaxChecker )
+            {
+                // This is a speedup : no need to check the syntax of any value
+                // if all the syntaxes are accepted...
+                continue;
+            }
+
+            // Then loop on all values
+            for ( Value<?> value : attribute )
+            {
+                try
+                {
+                    syntaxChecker.assertSyntax( value.get() );
+                }
+                catch ( NamingException ne )
+                {
+                    String message = "Attribute value '"
+                        + ( value instanceof ServerStringValue ? value.get() : StringTools.dumpBytes( ( byte[] ) value
+                            .get() ) ) + "' for attribute '" + attribute.getUpId() + "' is syntactically incorrect";
+                    LOG.info( message );
+
+                    throw new LdapInvalidAttributeValueException( message, ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX );
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Check a String attribute to see if there is some byte[] value in it.
+     * 
+     * If this is the case, try to change it to a String value.
+     */
+    private boolean checkHumanReadable( EntryAttribute attribute ) throws NamingException
+    {
+        boolean isModified = false;
+
+        // Loop on each values
+        for ( Value<?> value : attribute )
+        {
+            if ( value instanceof ServerStringValue )
+            {
+                continue;
+            }
+            else if ( value instanceof ServerBinaryValue )
+            {
+                // we have a byte[] value. It should be a String UTF-8 encoded
+                // Let's transform it
+                try
+                {
+                    String valStr = new String( ( byte[] ) value.get(), "UTF-8" );
+                    attribute.remove( value );
+                    attribute.add( valStr );
+                    isModified = true;
+                }
+                catch ( UnsupportedEncodingException uee )
+                {
+                    throw new NamingException( "The value is not a valid String" );
+                }
+            }
+            else
+            {
+                throw new NamingException( "The value stored in an Human Readable attribute is not a String" );
+            }
+        }
+
+        return isModified;
+    }
+
+
+    /**
+     * Check a binary attribute to see if there is some String value in it.
+     * 
+     * If this is the case, try to change it to a binary value.
+     */
+    private boolean checkNotHumanReadable( EntryAttribute attribute ) throws NamingException
+    {
+        boolean isModified = false;
+
+        // Loop on each values
+        for ( Value<?> value : attribute )
+        {
+            if ( value instanceof ServerBinaryValue )
+            {
+                continue;
+            }
+            else if ( value instanceof ServerStringValue )
+            {
+                // We have a String value. It should be a byte[]
+                // Let's transform it
+                try
+                {
+                    byte[] valBytes = ( ( String ) value.get() ).getBytes( "UTF-8" );
+
+                    attribute.remove( value );
+                    attribute.add( valBytes );
+                    isModified = true;
+                }
+                catch ( UnsupportedEncodingException uee )
+                {
+                    String message = "The value stored in a not Human Readable attribute as a String should be convertible to a byte[]";
+                    LOG.error( message );
+                    throw new NamingException( message );
+                }
+            }
+            else
+            {
+                String message = "The value is not valid. It should be a String or a byte[]";
+                LOG.error( message );
+                throw new NamingException( message );
+            }
+        }
+
+        return isModified;
+    }
+
+
+    /**
+     * Check that all the attribute's values which are Human Readable can be transformed
+     * to valid String if they are stored as byte[], and that non Human Readable attributes
+     * stored as String can be transformed to byte[]
+     */
+    private void assertHumanReadable( ServerEntry entry ) throws NamingException
+    {
+        boolean isModified = false;
+
+        ServerEntry clonedEntry = null;
+
+        // Loops on all attributes
+        for ( EntryAttribute attribute : entry )
+        {
+            AttributeType attributeType = ( ( ServerAttribute ) attribute ).getAttributeType();
+
+            // If the attributeType is H-R, check all of its values
+            if ( attributeType.getSyntax().isHumanReadable() )
+            {
+                isModified = checkHumanReadable( attribute );
+            }
+            else
+            {
+                isModified = checkNotHumanReadable( attribute );
+            }
+
+            // If we have a returned attribute, then we need to store it
+            // into a new entry
+            if ( isModified )
+            {
+                if ( clonedEntry == null )
+                {
+                    clonedEntry = ( ServerEntry ) entry.clone();
+                }
+
+                // Switch the attributes
+                clonedEntry.put( attribute );
+
+                isModified = false;
+            }
+        }
+
+        if ( clonedEntry != null )
+        {
+            entry = clonedEntry;
+        }
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/SchemaOperationControl.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/SchemaOperationControl.java
new file mode 100644
index 0000000..28122e8
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/SchemaOperationControl.java
@@ -0,0 +1,963 @@
+/*
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *   or more contributor license agreements.  See the NOTICE file
+ *   distributed with this work for additional information
+ *   regarding copyright ownership.  The ASF licenses this file
+ *   to you 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.
+ *
+ */
+package org.apache.directory.server.core.schema;
+
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.directory.server.constants.ApacheSchemaConstants;
+import org.apache.directory.server.constants.MetaSchemaConstants;
+import org.apache.directory.server.core.authn.AuthenticationInterceptor;
+import org.apache.directory.server.core.authz.AciAuthorizationInterceptor;
+import org.apache.directory.server.core.authz.DefaultAuthorizationInterceptor;
+import org.apache.directory.server.core.collective.CollectiveAttributeInterceptor;
+import org.apache.directory.server.core.entry.DefaultServerAttribute;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerAttribute;
+import org.apache.directory.server.core.entry.ServerModification;
+import org.apache.directory.server.core.exception.ExceptionInterceptor;
+import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
+import org.apache.directory.server.core.invocation.Invocation;
+import org.apache.directory.server.core.invocation.InvocationStack;
+import org.apache.directory.server.core.jndi.ServerLdapContext;
+import org.apache.directory.server.core.normalization.NormalizationInterceptor;
+import org.apache.directory.server.core.referral.ReferralInterceptor;
+import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
+import org.apache.directory.server.schema.registries.ObjectClassRegistry;
+import org.apache.directory.server.schema.registries.OidRegistry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.NotImplementedException;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.entry.Modification;
+import org.apache.directory.shared.ldap.entry.ModificationOperation;
+import org.apache.directory.shared.ldap.entry.Value;
+import org.apache.directory.shared.ldap.exception.LdapInvalidNameException;
+import org.apache.directory.shared.ldap.exception.LdapNamingException;
+import org.apache.directory.shared.ldap.exception.LdapOperationNotSupportedException;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.name.Rdn;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.schema.DITContentRule;
+import org.apache.directory.shared.ldap.schema.DITStructureRule;
+import org.apache.directory.shared.ldap.schema.MatchingRule;
+import org.apache.directory.shared.ldap.schema.MatchingRuleUse;
+import org.apache.directory.shared.ldap.schema.NameForm;
+import org.apache.directory.shared.ldap.schema.ObjectClass;
+import org.apache.directory.shared.ldap.schema.Syntax;
+import org.apache.directory.shared.ldap.schema.syntax.AbstractSchemaDescription;
+import org.apache.directory.shared.ldap.schema.syntax.ComparatorDescription;
+import org.apache.directory.shared.ldap.schema.syntax.NormalizerDescription;
+import org.apache.directory.shared.ldap.schema.syntax.SyntaxCheckerDescription;
+import org.apache.directory.shared.ldap.util.DateUtils;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.naming.NamingException;
+import javax.naming.directory.DirContext;
+
+
+/**
+ * Central point of control for schemas enforced by the server.  The 
+ * following duties are presently performed by this class:
+ * 
+ * <ul>
+ *   <li>Provide central point of access for all registries: global and SAA specific registries</li>
+ *   <li>Manage enabling and disabling schemas</li>
+ *   <li>Responding to specific schema object changes</li>
+ * </ul>
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class SchemaOperationControl
+{
+    private static final Logger LOG = LoggerFactory.getLogger( SchemaOperationControl.class );
+
+    // indices of handlers and object ids into arrays
+    private static final int COMPARATOR_INDEX = 0;
+    private static final int NORMALIZER_INDEX = 1;
+    private static final int SYNTAX_CHECKER_INDEX = 2;
+    private static final int SYNTAX_INDEX = 3;
+    private static final int MATCHING_RULE_INDEX = 4;
+    private static final int ATTRIBUTE_TYPE_INDEX = 5;
+    private static final int OBJECT_CLASS_INDEX = 6;
+    private static final int MATCHING_RULE_USE_INDEX = 7;
+    private static final int DIT_STRUCTURE_RULE_INDEX = 8;
+    private static final int DIT_CONTENT_RULE_INDEX = 9;
+    private static final int NAME_FORM_INDEX = 10;
+
+    private static final Set<String> VALID_OU_VALUES = new HashSet<String>();
+    private static final String[] OP_ATTRS = new String[] {
+        SchemaConstants.COMPARATORS_AT,
+        SchemaConstants.NORMALIZERS_AT,
+        SchemaConstants.SYNTAX_CHECKERS_AT,
+        SchemaConstants.LDAP_SYNTAXES_AT,
+        SchemaConstants.MATCHING_RULES_AT,
+        SchemaConstants.ATTRIBUTE_TYPES_AT,
+        SchemaConstants.OBJECT_CLASSES_AT,
+        SchemaConstants.MATCHING_RULE_USE_AT,
+        SchemaConstants.DIT_STRUCTURE_RULES_AT,
+        SchemaConstants.DIT_CONTENT_RULES_AT,
+        SchemaConstants.NAME_FORMS_AT
+    };
+    private static final String[] META_OBJECT_CLASSES = new String[] {
+        "metaComparator",
+        "metaNormalizer",
+        "metaSyntaxChecker",
+        "metaSyntax",
+        "metaMatchingRule",
+        "metaAttributeType",
+        "metaObjectClass",
+        "metaMatchingRuleUse",
+        "metaDITStructureRule",
+        "metaDITContentRule",
+        "metaNameForm"
+    };
+    private static final java.util.Collection<String> SCHEMA_MODIFICATION_ATTRIBUTES_UPDATE_BYPASS;
+
+    private final MetaSchemaHandler metaSchemaHandler;
+    private final Registries registries;
+    private final AttributeType objectClassAT;
+    private final SchemaSubentryModifier subentryModifier;
+    private final SchemaChangeHandler[] schemaObjectHandlers = new SchemaChangeHandler[11];
+
+    private final DescriptionParsers parsers;
+    
+    private final Map<String, SchemaChangeHandler> opAttr2handlerMap = new HashMap<String, SchemaChangeHandler>();
+    private final Map<String, SchemaChangeHandler> objectClass2handlerMap = new HashMap<String, SchemaChangeHandler>();
+    
+    /** 
+     * Maps the OID of a subschemaSubentry operational attribute to the index of 
+     * the handler in the schemaObjectHandlers array.
+     */ 
+    private final Map<String, Integer> opAttr2handlerIndex = new HashMap<String, Integer>( 11 );
+    private static final String CASCADING_ERROR =
+            "Cascading has not yet been implemented: standard operation is in effect.";
+
+
+    static 
+    {
+        VALID_OU_VALUES.add( SchemaConstants.NORMALIZERS_AT.toLowerCase() );
+        VALID_OU_VALUES.add( SchemaConstants.COMPARATORS_AT.toLowerCase() );
+        VALID_OU_VALUES.add( SchemaConstants.SYNTAX_CHECKERS_AT.toLowerCase() );
+        VALID_OU_VALUES.add( "syntaxes".toLowerCase() );
+        VALID_OU_VALUES.add( SchemaConstants.MATCHING_RULES_AT.toLowerCase() );
+        VALID_OU_VALUES.add( SchemaConstants.MATCHING_RULE_USE_AT.toLowerCase() );
+        VALID_OU_VALUES.add( SchemaConstants.ATTRIBUTE_TYPES_AT.toLowerCase() );
+        VALID_OU_VALUES.add( SchemaConstants.OBJECT_CLASSES_AT.toLowerCase() );
+        VALID_OU_VALUES.add( SchemaConstants.NAME_FORMS_AT.toLowerCase() );
+        VALID_OU_VALUES.add( SchemaConstants.DIT_CONTENT_RULES_AT.toLowerCase() );
+        VALID_OU_VALUES.add( SchemaConstants.DIT_STRUCTURE_RULES_AT.toLowerCase() );
+        
+        HashSet<String> c = new HashSet<String>();
+        c.add( NormalizationInterceptor.class.getName() );
+        c.add( AuthenticationInterceptor.class.getName() );
+        c.add( ReferralInterceptor.class.getName() );
+        c.add( AciAuthorizationInterceptor.class.getName() );
+        c.add( DefaultAuthorizationInterceptor.class.getName() );
+        c.add( ExceptionInterceptor.class.getName() );
+//        c.add( ChangeLogInterceptor.class.getName() );
+//        c.add( OperationalAttributeInterceptor.class.getName() );
+        c.add( SchemaInterceptor.class.getName() );
+//        c.add( SubentryInterceptor.class.getName() );
+        c.add( CollectiveAttributeInterceptor.class.getName() );
+//        c.add( EventInterceptor.class.getName() );
+//        c.add( TriggerInterceptor.class.getName() );
+        SCHEMA_MODIFICATION_ATTRIBUTES_UPDATE_BYPASS = Collections.unmodifiableCollection( c );
+    }
+
+
+    public SchemaOperationControl( Registries registries, PartitionSchemaLoader loader, SchemaPartitionDao dao )
+        throws NamingException
+    {
+        this.registries = registries;
+        this.objectClassAT = this.registries.getAttributeTypeRegistry()
+            .lookup( SchemaConstants.OBJECT_CLASS_AT );
+        
+        this.metaSchemaHandler = new MetaSchemaHandler( this.registries, loader );
+        
+        this.schemaObjectHandlers[COMPARATOR_INDEX] = new MetaComparatorHandler( registries, loader ); 
+        this.schemaObjectHandlers[NORMALIZER_INDEX] = new MetaNormalizerHandler( registries, loader );
+        this.schemaObjectHandlers[SYNTAX_CHECKER_INDEX] = new MetaSyntaxCheckerHandler( registries, loader );
+        this.schemaObjectHandlers[SYNTAX_INDEX] = new MetaSyntaxHandler( registries, loader, dao );
+        this.schemaObjectHandlers[MATCHING_RULE_INDEX] = new MetaMatchingRuleHandler( registries, loader, dao );
+        this.schemaObjectHandlers[ATTRIBUTE_TYPE_INDEX] = new MetaAttributeTypeHandler( registries, loader, dao );
+        this.schemaObjectHandlers[OBJECT_CLASS_INDEX] = new MetaObjectClassHandler( registries, loader, dao );
+        this.schemaObjectHandlers[MATCHING_RULE_USE_INDEX] = new MetaMatchingRuleUseHandler( registries, loader );
+        this.schemaObjectHandlers[DIT_STRUCTURE_RULE_INDEX] = new MetaDitStructureRuleHandler( registries, loader ); 
+        this.schemaObjectHandlers[DIT_CONTENT_RULE_INDEX] = new MetaDitContentRuleHandler( registries, loader ); 
+        this.schemaObjectHandlers[NAME_FORM_INDEX] = new MetaNameFormHandler( registries, loader ); 
+
+        this.subentryModifier = new SchemaSubentryModifier( dao );
+        this.parsers = new DescriptionParsers( registries, dao );
+        
+        OidRegistry oidRegistry = registries.getOidRegistry();
+
+        String comparatorsOid = oidRegistry.getOid( SchemaConstants.COMPARATORS_AT );
+        opAttr2handlerIndex.put( comparatorsOid, COMPARATOR_INDEX );
+
+        String normalizersOid = oidRegistry.getOid( SchemaConstants.NORMALIZERS_AT );
+        opAttr2handlerIndex.put( normalizersOid, NORMALIZER_INDEX );
+
+        String syntaxCheckersOid = oidRegistry.getOid( SchemaConstants.SYNTAX_CHECKERS_AT );
+        opAttr2handlerIndex.put( syntaxCheckersOid, SYNTAX_CHECKER_INDEX );
+
+        String ldapSyntaxesOid = oidRegistry.getOid( SchemaConstants.LDAP_SYNTAXES_AT );
+        opAttr2handlerIndex.put( ldapSyntaxesOid, SYNTAX_INDEX );
+
+        String matchingRulesOid = oidRegistry.getOid( SchemaConstants.MATCHING_RULES_AT );
+        opAttr2handlerIndex.put( matchingRulesOid, MATCHING_RULE_INDEX );
+
+        String attributeTypesOid = oidRegistry.getOid( SchemaConstants.ATTRIBUTE_TYPES_AT );
+        opAttr2handlerIndex.put( attributeTypesOid, ATTRIBUTE_TYPE_INDEX );
+
+        String objectClassesOid = oidRegistry.getOid( SchemaConstants.OBJECT_CLASSES_AT );
+        opAttr2handlerIndex.put( objectClassesOid, OBJECT_CLASS_INDEX );
+
+        String matchingRuleUseOid = oidRegistry.getOid( SchemaConstants.MATCHING_RULE_USE_AT );
+        opAttr2handlerIndex.put( matchingRuleUseOid, MATCHING_RULE_USE_INDEX );
+
+        String ditStructureRulesOid = oidRegistry.getOid( SchemaConstants.DIT_STRUCTURE_RULES_AT );
+        opAttr2handlerIndex.put( ditStructureRulesOid, DIT_STRUCTURE_RULE_INDEX );
+
+        String ditContentRulesOid = oidRegistry.getOid( SchemaConstants.DIT_CONTENT_RULES_AT );
+        opAttr2handlerIndex.put( ditContentRulesOid, DIT_CONTENT_RULE_INDEX );
+
+        String nameFormsOid = oidRegistry.getOid( SchemaConstants.NAME_FORMS_AT );
+        opAttr2handlerIndex.put( nameFormsOid, NAME_FORM_INDEX );
+        
+        initHandlerMaps();
+    }
+
+    
+    private void initHandlerMaps() throws NamingException
+    {
+        AttributeTypeRegistry atReg = registries.getAttributeTypeRegistry();
+        for ( int ii = 0; ii < OP_ATTRS.length; ii++ )
+        {
+            AttributeType at = atReg.lookup( OP_ATTRS[ii] );
+            opAttr2handlerMap.put( at.getOid(), schemaObjectHandlers[ii] );
+        }
+
+        ObjectClassRegistry ocReg = registries.getObjectClassRegistry();
+        for ( int ii = 0; ii < META_OBJECT_CLASSES.length; ii++ )
+        {
+            ObjectClass oc = ocReg.lookup( META_OBJECT_CLASSES[ii] );
+            objectClass2handlerMap.put( oc.getOid(), schemaObjectHandlers[ii] );
+        }
+    }
+    
+    
+    public Registries getGlobalRegistries()
+    {
+        return registries;
+    }
+    
+    
+    public Registries getRegistries( LdapDN dn )
+    {
+        LOG.error( "Ignoring request for specific registries under dn {}", dn );
+        throw new NotImplementedException();
+    }
+
+
+    public void add( LdapDN name, ServerEntry entry ) throws NamingException
+    {
+        EntryAttribute oc = entry.get( objectClassAT );
+        
+        for ( Value<?> value:oc )
+        {
+
+            String oid = registries.getOidRegistry().getOid( ( String ) value.get() );
+            
+            if ( objectClass2handlerMap.containsKey( oid ) )
+            {
+                SchemaChangeHandler handler = objectClass2handlerMap.get( oid );
+                handler.add( name, entry );
+                updateSchemaModificationAttributes();
+                return;
+            }
+        }
+        
+        if ( oc.contains( MetaSchemaConstants.META_SCHEMA_OC ) )
+        {
+            metaSchemaHandler.add( name, entry );
+            updateSchemaModificationAttributes();
+            return;
+        }
+        
+        if ( oc.contains( SchemaConstants.ORGANIZATIONAL_UNIT_OC ) )
+        {
+            if ( name.size() != 3 )
+            {
+                throw new LdapInvalidNameException( 
+                    "Schema entity containers of objectClass organizationalUnit should be 3 name components in length.", 
+                    ResultCodeEnum.NAMING_VIOLATION );
+            }
+            
+            String ouValue = ( String ) name.getRdn().getValue();
+            ouValue = ouValue.trim().toLowerCase();
+            if ( ! VALID_OU_VALUES.contains( ouValue ) )
+            {
+                throw new LdapInvalidNameException( 
+                    "Expecting organizationalUnit with one of the following names: " + VALID_OU_VALUES, 
+                    ResultCodeEnum.NAMING_VIOLATION );
+            }
+            return;
+        }
+
+        throw new LdapOperationNotSupportedException( ResultCodeEnum.UNWILLING_TO_PERFORM );
+    }
+    
+
+    public void delete( LdapDN name, ServerEntry entry, boolean doCascadeDelete ) throws NamingException
+    {
+        EntryAttribute oc = entry.get( objectClassAT );
+        
+        for ( Value<?> value:oc )
+        {
+            String oid = registries.getOidRegistry().getOid( ( String ) value.get() );
+            
+            if ( objectClass2handlerMap.containsKey( oid ) )
+            {
+                SchemaChangeHandler handler = objectClass2handlerMap.get( oid );
+                handler.delete( name, entry, doCascadeDelete );
+                updateSchemaModificationAttributes();
+                return;
+            }
+        }
+
+        if ( oc.contains( MetaSchemaConstants.META_SCHEMA_OC ) )
+        {
+            metaSchemaHandler.delete( name, entry, doCascadeDelete );
+            updateSchemaModificationAttributes();
+            return;
+        }
+        
+        if ( oc.contains( SchemaConstants.ORGANIZATIONAL_UNIT_OC ) )
+        {
+            if ( name.size() != 3 )
+            {
+                throw new LdapNamingException( 
+                    "Only schema entity containers of objectClass organizationalUnit with 3 name components in length" +
+                    " can be deleted.", ResultCodeEnum.UNWILLING_TO_PERFORM );
+            }
+            
+            String ouValue = ( String ) name.getRdn().getValue();
+            ouValue = ouValue.trim().toLowerCase();
+            if ( ! VALID_OU_VALUES.contains( ouValue ) )
+            {
+                throw new LdapInvalidNameException( 
+                    "Can only delete organizationalUnit entity containers with one of the following names: " 
+                    + VALID_OU_VALUES, ResultCodeEnum.NAMING_VIOLATION );
+            }
+            return;
+        }
+
+        throw new LdapOperationNotSupportedException( ResultCodeEnum.UNWILLING_TO_PERFORM );
+    }
+    
+
+    public void modify( LdapDN name, ModificationOperation modOp, ServerEntry mods, ServerEntry entry, 
+        ServerEntry targetEntry, boolean cascade ) throws NamingException
+    {
+        EntryAttribute oc = entry.get( objectClassAT );
+        
+        for ( Value<?> value:oc )
+        {
+            String oid = registries.getOidRegistry().getOid( ( String ) value.get() );
+            
+            if ( objectClass2handlerMap.containsKey( oid ) )
+            {
+                SchemaChangeHandler handler = objectClass2handlerMap.get( oid );
+                handler.modify( name, modOp, mods, entry, targetEntry, cascade );
+                updateSchemaModificationAttributes();
+                return;
+            }
+        }
+
+        if ( oc.contains( MetaSchemaConstants.META_SCHEMA_OC ) )
+        {
+            metaSchemaHandler.modify( name, modOp, mods, entry, targetEntry, cascade );
+            updateSchemaModificationAttributes();
+            return;
+        }
+        
+        throw new LdapOperationNotSupportedException( ResultCodeEnum.UNWILLING_TO_PERFORM );
+    }
+
+
+    public void modify( LdapDN name, List<Modification> mods, ServerEntry entry, ServerEntry targetEntry,
+        boolean doCascadeModify ) throws NamingException
+    {
+        EntryAttribute oc = entry.get( objectClassAT );
+        
+        for ( Value<?> value:oc )
+        {
+            String oid = registries.getOidRegistry().getOid( ( String ) value.get() );
+            
+            if ( objectClass2handlerMap.containsKey( oid ) )
+            {
+                SchemaChangeHandler handler = objectClass2handlerMap.get( oid );
+                handler.modify( name, mods, entry, targetEntry, doCascadeModify );
+                updateSchemaModificationAttributes();
+                return;
+            }
+        }
+
+        if ( oc.contains( MetaSchemaConstants.META_SCHEMA_OC ) )
+        {
+            metaSchemaHandler.modify( name, mods, entry, targetEntry, doCascadeModify );
+            updateSchemaModificationAttributes();
+            return;
+        }
+
+        LOG.error( String.format( "Unwilling to perform modify on %s:\n\nEntry:\n%s\n\nModifications:\n%s", name,
+                entry, mods ) );
+        throw new LdapOperationNotSupportedException( ResultCodeEnum.UNWILLING_TO_PERFORM );
+    }
+
+
+    public void modifyRn( LdapDN name, Rdn newRdn, boolean deleteOldRn, ServerEntry entry, boolean doCascadeModify ) 
+        throws NamingException
+    {
+        EntryAttribute oc = entry.get( objectClassAT );
+        
+        for ( Value<?> value:oc )
+        {
+            String oid = registries.getOidRegistry().getOid( ( String ) value.get() );
+            
+            if ( objectClass2handlerMap.containsKey( oid ) )
+            {
+                SchemaChangeHandler handler = objectClass2handlerMap.get( oid );
+                handler.rename( name, entry, newRdn, doCascadeModify );
+                updateSchemaModificationAttributes();
+                return;
+            }
+        }
+
+        if ( oc.contains( MetaSchemaConstants.META_SCHEMA_OC ) )
+        {
+            metaSchemaHandler.rename( name, entry, newRdn, doCascadeModify );
+            updateSchemaModificationAttributes();
+            return;
+        }
+        
+        throw new LdapOperationNotSupportedException( ResultCodeEnum.UNWILLING_TO_PERFORM );
+    }
+
+
+    public void replace( LdapDN oriChildName, LdapDN newParentName, ServerEntry entry, 
+        boolean cascade ) throws NamingException
+    {
+        EntryAttribute oc = entry.get( objectClassAT );
+        
+        for ( Value<?> value:oc )
+        {
+            String oid = registries.getOidRegistry().getOid( ( String ) value.get() );
+            
+            if ( objectClass2handlerMap.containsKey( oid ) )
+            {
+                SchemaChangeHandler handler = objectClass2handlerMap.get( oid );
+                handler.replace( oriChildName, newParentName, entry, cascade );
+                updateSchemaModificationAttributes();
+                return;
+            }
+        }
+
+        if ( oc.contains( MetaSchemaConstants.META_SCHEMA_OC ) )
+        {
+            metaSchemaHandler.replace( oriChildName, newParentName, entry, cascade );
+            updateSchemaModificationAttributes();
+            return;
+        }
+        
+        throw new LdapOperationNotSupportedException( ResultCodeEnum.UNWILLING_TO_PERFORM );
+    }
+
+
+    public void move( LdapDN oriChildName, LdapDN newParentName, Rdn newRn, boolean deleteOldRn,
+        ServerEntry entry, boolean cascade ) throws NamingException
+    {
+        EntryAttribute oc = entry.get( objectClassAT );
+        
+        for ( Value<?> value:oc )
+        {
+            String oid = registries.getOidRegistry().getOid( ( String ) value.get() );
+            
+            if ( objectClass2handlerMap.containsKey( oid ) )
+            {
+                SchemaChangeHandler handler = objectClass2handlerMap.get( oid );
+                handler.move( oriChildName, newParentName, newRn, deleteOldRn, entry, cascade );
+                updateSchemaModificationAttributes();
+                return;
+            }
+        }
+
+        if ( oc.contains( MetaSchemaConstants.META_SCHEMA_OC ) )
+        {
+            metaSchemaHandler.move( oriChildName, newParentName, newRn, deleteOldRn, entry, cascade );
+            updateSchemaModificationAttributes();
+            return;
+        }
+        
+        throw new LdapOperationNotSupportedException( ResultCodeEnum.UNWILLING_TO_PERFORM );
+    }
+
+    
+    /**
+     * Translates modify operations on schema subentries into one or more operations 
+     * on meta schema entities within the ou=schema partition and updates the registries
+     * accordingly.  This uses direct access to the partition to bypass all interceptors.
+     * 
+     * @param name the name of the subentry
+     * @param mods the modification operations performed on the subentry
+     * @param subentry the attributes of the subentry
+     * @param targetSubentry the target subentry after being modified
+     * @param doCascadeModify determines if a cascading operation should be performed
+     * to effect all dependents on the changed entity
+     * @throws NamingException if the operation fails
+     */
+    public void modifySchemaSubentry( LdapDN name, List<Modification> mods, ServerEntry subentry, 
+        ServerEntry targetSubentry, boolean doCascadeModify ) throws NamingException 
+    {
+        for ( Modification mod : mods )
+        {
+            String opAttrOid = registries.getOidRegistry().getOid( mod.getAttribute().getId() );
+            
+            ServerAttribute serverAttribute = (ServerAttribute)mod.getAttribute();
+
+            switch ( mod.getOperation() )
+            {
+                case ADD_ATTRIBUTE :
+
+                    modifyAddOperation( opAttrOid, serverAttribute, doCascadeModify );
+                    break;
+                    
+                case REMOVE_ATTRIBUTE :
+                    modifyRemoveOperation( opAttrOid, serverAttribute, doCascadeModify );
+                    break; 
+                    
+                case REPLACE_ATTRIBUTE :
+                    throw new LdapOperationNotSupportedException( 
+                        "Modify REPLACE operations on schema subentries are not allowed: " +
+                        "it's just silly to destroy and recreate so many \nschema entities " +
+                        "that reside in schema operational attributes.  Instead use \na " +
+                        "targeted combination of modify ADD and REMOVE operations.", 
+                        ResultCodeEnum.UNWILLING_TO_PERFORM );
+                
+                default:
+                    throw new IllegalStateException( "Undefined modify operation: " + mod.getOperation() );
+            }
+        }
+        
+        if ( mods.size() > 0 )
+        {
+            updateSchemaModificationAttributes();
+        }
+    }
+    
+    
+    /**
+     * Translates modify operations on schema subentries into one or more operations 
+     * on meta schema entities within the ou=schema partition and updates the registries
+     * accordingly.  This uses direct access to the partition to bypass all interceptors.
+     * 
+     * @param name the name of the subentry
+     * @param modOp the modification operation performed on the subentry
+     * @param mods the modification operations performed on the subentry
+     * @param subentry the attributes of the subentry
+     * @param targetSubentry the target subentry after being modified
+     * @param doCascadeModify determines if a cascading operation should be performed
+     * to effect all dependents on the changed entity
+     * @throws NamingException if the modify fails
+     */
+    public void modifySchemaSubentry( LdapDN name, int modOp, ServerEntry mods, ServerEntry subentry, 
+        ServerEntry targetSubentry, boolean doCascadeModify ) throws NamingException
+    {
+        Set<AttributeType> attributeTypes = mods.getAttributeTypes();
+        
+        switch ( modOp )
+        {
+            case( DirContext.ADD_ATTRIBUTE ):
+                for ( AttributeType attributeType:attributeTypes )
+                {
+                    modifyAddOperation( attributeType.getOid(), 
+                        mods.get( attributeType ), doCascadeModify );
+                }
+            
+                break;
+                
+            case( DirContext.REMOVE_ATTRIBUTE ):
+                for ( AttributeType attributeType:attributeTypes )
+                {
+                    modifyRemoveOperation( attributeType.getOid(), 
+                        mods.get( attributeType ), doCascadeModify );
+                }
+            
+                break;
+                
+            case( DirContext.REPLACE_ATTRIBUTE ):
+                throw new LdapOperationNotSupportedException( 
+                    "Modify REPLACE operations on schema subentries are not allowed: " +
+                    "it's just silly to destroy and recreate so many \nschema entities " +
+                    "that reside in schema operational attributes.  Instead use \na " +
+                    "targeted combination of modify ADD and REMOVE operations.", 
+                    ResultCodeEnum.UNWILLING_TO_PERFORM );
+            
+            default:
+                throw new IllegalStateException( "Undefined modify operation: " + modOp );
+        }
+        
+        updateSchemaModificationAttributes();
+    }
+
+    
+    public String getSchema( AbstractSchemaDescription desc ) 
+    {
+        if ( desc.getExtensions().containsKey( MetaSchemaConstants.X_SCHEMA ) )
+        {
+            return desc.getExtensions().get( MetaSchemaConstants.X_SCHEMA ).get( 0 );
+        }
+        
+        return MetaSchemaConstants.SCHEMA_OTHER;
+    }
+    
+
+    /**
+     * Handles the modify remove operation on the subschemaSubentry for schema entities. 
+     * 
+     * @param opAttrOid the numeric id of the operational attribute modified
+     * @param mods the attribute with the modifications
+     * @param doCascadeModify determines if a cascading operation should be performed
+     * to effect all dependents on the changed entity
+     * @throws NamingException if there are problems updating the registries and the
+     * schema partition
+     */
+    private void modifyRemoveOperation( String opAttrOid, EntryAttribute mods, boolean doCascadeModify ) 
+        throws NamingException
+    {
+        int index = opAttr2handlerIndex.get( opAttrOid );
+        SchemaChangeHandler handler = opAttr2handlerMap.get( opAttrOid );
+        
+        switch( index )
+        {
+            case( COMPARATOR_INDEX ):
+                MetaComparatorHandler comparatorHandler = ( MetaComparatorHandler ) handler;
+                ComparatorDescription[] comparatorDescriptions = parsers.parseComparators( mods );
+                
+                for ( ComparatorDescription comparatorDescription : comparatorDescriptions )
+                {
+                    comparatorHandler.delete( comparatorDescription.getNumericOid(), doCascadeModify );
+                    subentryModifier.delete( registries, comparatorDescription );
+                }
+                break;
+            case( NORMALIZER_INDEX ):
+                MetaNormalizerHandler normalizerHandler = ( MetaNormalizerHandler ) handler;
+                NormalizerDescription[] normalizerDescriptions = parsers.parseNormalizers( mods );
+                
+                for ( NormalizerDescription normalizerDescription : normalizerDescriptions )
+                {
+                    normalizerHandler.delete( normalizerDescription.getNumericOid(), doCascadeModify );
+                    subentryModifier.delete( registries, normalizerDescription );
+                }
+                break;
+            case( SYNTAX_CHECKER_INDEX ):
+                MetaSyntaxCheckerHandler syntaxCheckerHandler = ( MetaSyntaxCheckerHandler ) handler;
+                SyntaxCheckerDescription[] syntaxCheckerDescriptions = parsers.parseSyntaxCheckers( mods );
+                
+                for ( SyntaxCheckerDescription syntaxCheckerDescription : syntaxCheckerDescriptions )
+                {
+                    syntaxCheckerHandler.delete( syntaxCheckerDescription.getNumericOid(), doCascadeModify );
+                    subentryModifier.delete( registries, syntaxCheckerDescription );
+                }
+                break;
+            case( SYNTAX_INDEX ):
+                MetaSyntaxHandler syntaxHandler = ( MetaSyntaxHandler ) handler;
+                Syntax[] syntaxes = parsers.parseSyntaxes( mods );
+                
+                for ( Syntax syntax : syntaxes )
+                {
+                    syntaxHandler.delete( syntax, doCascadeModify );
+                    subentryModifier.deleteSchemaObject( registries, syntax );
+                }
+                break;
+            case( MATCHING_RULE_INDEX ):
+                MetaMatchingRuleHandler matchingRuleHandler = ( MetaMatchingRuleHandler ) handler;
+                MatchingRule[] mrs = parsers.parseMatchingRules( mods );
+                
+                for ( MatchingRule mr : mrs )
+                {
+                    matchingRuleHandler.delete( mr, doCascadeModify );
+                    subentryModifier.deleteSchemaObject( registries, mr );
+                }
+                break;
+            case( ATTRIBUTE_TYPE_INDEX ):
+                MetaAttributeTypeHandler atHandler = ( MetaAttributeTypeHandler ) handler;
+                AttributeType[] ats = parsers.parseAttributeTypes( mods );
+                
+                for ( AttributeType at : ats )
+                {
+                    atHandler.delete( at, doCascadeModify );
+                    subentryModifier.deleteSchemaObject( registries, at );
+                }
+                break;
+            case( OBJECT_CLASS_INDEX ):
+                MetaObjectClassHandler ocHandler = ( MetaObjectClassHandler ) handler;
+                ObjectClass[] ocs = parsers.parseObjectClasses( mods );
+
+                for ( ObjectClass oc : ocs )
+                {
+                    ocHandler.delete( oc, doCascadeModify );
+                    subentryModifier.deleteSchemaObject( registries, oc );
+                }
+                break;
+            case( MATCHING_RULE_USE_INDEX ):
+                MetaMatchingRuleUseHandler mruHandler = ( MetaMatchingRuleUseHandler ) handler;
+                MatchingRuleUse[] mrus = parsers.parseMatchingRuleUses( mods );
+                
+                for ( MatchingRuleUse mru : mrus )
+                {
+                    mruHandler.delete( mru, doCascadeModify );
+                    subentryModifier.deleteSchemaObject( registries, mru );
+                }
+                break;
+            case( DIT_STRUCTURE_RULE_INDEX ):
+                MetaDitStructureRuleHandler dsrHandler = ( MetaDitStructureRuleHandler ) handler;
+                DITStructureRule[] dsrs = parsers.parseDitStructureRules( mods );
+                
+                for ( DITStructureRule dsr : dsrs )
+                {
+                    dsrHandler.delete( dsr, doCascadeModify );
+                    subentryModifier.deleteSchemaObject( registries, dsr );
+                }
+                break;
+            case( DIT_CONTENT_RULE_INDEX ):
+                MetaDitContentRuleHandler dcrHandler = ( MetaDitContentRuleHandler ) handler;
+                DITContentRule[] dcrs = parsers.parseDitContentRules( mods );
+                
+                for ( DITContentRule dcr : dcrs )
+                {
+                    dcrHandler.delete( dcr, doCascadeModify );
+                    subentryModifier.deleteSchemaObject( registries, dcr );
+                }
+                break;
+            case( NAME_FORM_INDEX ):
+                MetaNameFormHandler nfHandler = ( MetaNameFormHandler ) handler;
+                NameForm[] nfs = parsers.parseNameForms( mods );
+                
+                for ( NameForm nf : nfs )
+                {
+                    nfHandler.delete( nf, doCascadeModify );
+                    subentryModifier.deleteSchemaObject( registries, nf );
+                }
+                break;
+            default:
+                throw new IllegalStateException( "Unknown index into handler array: " + index );
+        }
+    }
+    
+    
+    /**
+     * Handles the modify add operation on the subschemaSubentry for schema entities. 
+     * 
+     * @param opAttrOid the numeric id of the operational attribute modified
+     * @param mods the attribute with the modifications
+     * @param doCascadeModify determines if a cascading operation should be performed
+     * to effect all dependents on the changed entity
+     * @throws NamingException if there are problems updating the registries and the
+     * schema partition
+     */
+    private void modifyAddOperation( String opAttrOid, EntryAttribute mods, boolean doCascadeModify ) throws NamingException
+    {
+        if ( doCascadeModify )
+        {
+            LOG.error( CASCADING_ERROR );
+        }
+
+        int index = opAttr2handlerIndex.get( opAttrOid );
+        SchemaChangeHandler handler = opAttr2handlerMap.get( opAttrOid );
+        
+        switch( index )
+        {
+            case( COMPARATOR_INDEX ):
+                MetaComparatorHandler comparatorHandler = ( MetaComparatorHandler ) handler;
+                ComparatorDescription[] comparatorDescriptions = parsers.parseComparators( mods );
+                
+                for ( ComparatorDescription comparatorDescription : comparatorDescriptions )
+                {
+                    comparatorHandler.add( comparatorDescription );
+                    subentryModifier.add( registries, comparatorDescription );
+                }
+                break;
+            case( NORMALIZER_INDEX ):
+                MetaNormalizerHandler normalizerHandler = ( MetaNormalizerHandler ) handler;
+                NormalizerDescription[] normalizerDescriptions = parsers.parseNormalizers( mods );
+                
+                for ( NormalizerDescription normalizerDescription : normalizerDescriptions )
+                {
+                    normalizerHandler.add( normalizerDescription );
+                    subentryModifier.add( registries, normalizerDescription );
+                }
+                break;
+            case( SYNTAX_CHECKER_INDEX ):
+                MetaSyntaxCheckerHandler syntaxCheckerHandler = ( MetaSyntaxCheckerHandler ) handler;
+                SyntaxCheckerDescription[] syntaxCheckerDescriptions = parsers.parseSyntaxCheckers( mods );
+                
+                for ( SyntaxCheckerDescription syntaxCheckerDescription : syntaxCheckerDescriptions )
+                {
+                    syntaxCheckerHandler.add( syntaxCheckerDescription );
+                    subentryModifier.add( registries, syntaxCheckerDescription );
+                }
+                break;
+            case( SYNTAX_INDEX ):
+                MetaSyntaxHandler syntaxHandler = ( MetaSyntaxHandler ) handler;
+                Syntax[] syntaxes = parsers.parseSyntaxes( mods );
+                
+                for ( Syntax syntax : syntaxes )
+                {
+                    syntaxHandler.add( syntax );
+                    subentryModifier.addSchemaObject( registries, syntax );
+                }
+                break;
+            case( MATCHING_RULE_INDEX ):
+                MetaMatchingRuleHandler matchingRuleHandler = ( MetaMatchingRuleHandler ) handler;
+                MatchingRule[] mrs = parsers.parseMatchingRules( mods );
+                
+                for ( MatchingRule mr : mrs )
+                {
+                    matchingRuleHandler.add( mr );
+                    subentryModifier.addSchemaObject( registries, mr );
+                }
+                break;
+            case( ATTRIBUTE_TYPE_INDEX ):
+                MetaAttributeTypeHandler atHandler = ( MetaAttributeTypeHandler ) handler;
+                AttributeType[] ats = parsers.parseAttributeTypes( mods );
+                
+                for ( AttributeType at : ats )
+                {
+                    atHandler.add( at );
+                    subentryModifier.addSchemaObject( registries, at );
+                }
+                break;
+            case( OBJECT_CLASS_INDEX ):
+                MetaObjectClassHandler ocHandler = ( MetaObjectClassHandler ) handler;
+                ObjectClass[] ocs = parsers.parseObjectClasses( mods );
+
+                for ( ObjectClass oc : ocs )
+                {
+                    ocHandler.add( oc );
+                    subentryModifier.addSchemaObject( registries, oc );
+                }
+                break;
+            case( MATCHING_RULE_USE_INDEX ):
+                MetaMatchingRuleUseHandler mruHandler = ( MetaMatchingRuleUseHandler ) handler;
+                MatchingRuleUse[] mrus = parsers.parseMatchingRuleUses( mods );
+                
+                for ( MatchingRuleUse mru : mrus )
+                {
+                    mruHandler.add( mru );
+                    subentryModifier.addSchemaObject( registries, mru );
+                }
+                break;
+            case( DIT_STRUCTURE_RULE_INDEX ):
+                MetaDitStructureRuleHandler dsrHandler = ( MetaDitStructureRuleHandler ) handler;
+                DITStructureRule[] dsrs = parsers.parseDitStructureRules( mods );
+                
+                for ( DITStructureRule dsr : dsrs )
+                {
+                    dsrHandler.add( dsr );
+                    subentryModifier.addSchemaObject( registries, dsr );
+                }
+                break;
+            case( DIT_CONTENT_RULE_INDEX ):
+                MetaDitContentRuleHandler dcrHandler = ( MetaDitContentRuleHandler ) handler;
+                DITContentRule[] dcrs = parsers.parseDitContentRules( mods );
+                
+                for ( DITContentRule dcr : dcrs )
+                {
+                    dcrHandler.add( dcr );
+                    subentryModifier.addSchemaObject( registries, dcr );
+                }
+                break;
+            case( NAME_FORM_INDEX ):
+                MetaNameFormHandler nfHandler = ( MetaNameFormHandler ) handler;
+                NameForm[] nfs = parsers.parseNameForms( mods );
+                
+                for ( NameForm nf : nfs )
+                {
+                    nfHandler.add( nf );
+                    subentryModifier.addSchemaObject( registries, nf );
+                }
+                break;
+            default:
+                throw new IllegalStateException( "Unknown index into handler array: " + index );
+        }
+    }
+    
+    
+    /**
+     * Updates the schemaModifiersName and schemaModifyTimestamp attributes of
+     * the schemaModificationAttributes entry for the global schema at 
+     * ou=schema,cn=schemaModifications.  This entry is hardcoded at that 
+     * position for now.
+     * 
+     * The current time is used to set the timestamp and the DN of current user
+     * is set for the modifiersName.
+     * 
+     * @throws NamingException if the update fails
+     */
+    private void updateSchemaModificationAttributes() throws NamingException
+    {
+        Invocation invocation = InvocationStack.getInstance().peek();
+        ServerLdapContext ctx = ( ServerLdapContext ) invocation.getCaller();
+        String modifiersName = ctx.getPrincipal().getJndiName().getNormName();
+        String modifyTimestamp = DateUtils.getGeneralizedTime();
+        
+        List<Modification> mods = new ArrayList<Modification>( 2 );
+        
+        mods.add( new ServerModification( ModificationOperation.REPLACE_ATTRIBUTE, 
+            new DefaultServerAttribute( 
+                ApacheSchemaConstants.SCHEMA_MODIFY_TIMESTAMP_AT,
+                registries.getAttributeTypeRegistry().lookup( ApacheSchemaConstants.SCHEMA_MODIFY_TIMESTAMP_AT ),
+                modifyTimestamp ) ) );
+        
+        mods.add( new ServerModification( ModificationOperation.REPLACE_ATTRIBUTE,
+            new DefaultServerAttribute( 
+                ApacheSchemaConstants.SCHEMA_MODIFIERS_NAME_AT, 
+                registries.getAttributeTypeRegistry().lookup( ApacheSchemaConstants.SCHEMA_MODIFIERS_NAME_AT ),
+                modifiersName ) ) );
+        
+        LdapDN name = new LdapDN( "cn=schemaModifications,ou=schema" );
+        name.normalize( registries.getAttributeTypeRegistry().getNormalizerMapping() );
+        
+        invocation.getProxy().modify( new ModifyOperationContext( registries, name, mods, true ),
+                SCHEMA_MODIFICATION_ATTRIBUTES_UPDATE_BYPASS );
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/SchemaPartitionDao.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/SchemaPartitionDao.java
new file mode 100644
index 0000000..b363686
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/SchemaPartitionDao.java
@@ -0,0 +1,949 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.schema;
+
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.directory.server.constants.MetaSchemaConstants;
+import org.apache.directory.server.constants.ServerDNConstants;
+import org.apache.directory.server.core.entry.DefaultServerAttribute;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerModification;
+import org.apache.directory.server.core.entry.ServerSearchResult;
+import org.apache.directory.server.core.interceptor.context.LookupOperationContext;
+import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
+import org.apache.directory.server.core.interceptor.context.SearchOperationContext;
+import org.apache.directory.server.core.partition.Partition;
+import org.apache.directory.server.schema.bootstrap.Schema;
+import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
+import org.apache.directory.server.schema.registries.OidRegistry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.entry.Modification;
+import org.apache.directory.shared.ldap.entry.client.ClientStringValue;
+import org.apache.directory.shared.ldap.filter.AndNode;
+import org.apache.directory.shared.ldap.filter.BranchNode;
+import org.apache.directory.shared.ldap.filter.EqualityNode;
+import org.apache.directory.shared.ldap.filter.ExprNode;
+import org.apache.directory.shared.ldap.filter.OrNode;
+import org.apache.directory.shared.ldap.filter.PresenceNode;
+import org.apache.directory.shared.ldap.filter.SimpleNode;
+import org.apache.directory.shared.ldap.message.AliasDerefMode;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.name.Rdn;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.schema.MatchingRule;
+import org.apache.directory.shared.ldap.schema.ObjectClass;
+import org.apache.directory.shared.ldap.schema.syntax.NumericOidSyntaxChecker;
+import org.apache.directory.shared.ldap.util.DateUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.SearchControls;
+
+
+/**
+ * A specialized data access object for managing schema objects in the
+ * schema partition.  
+ * 
+ * WARNING:
+ * This dao operates directly on a partition.  Hence no interceptors are available
+ * to perform the various expected services of respective interceptors.  Take care
+ * to normalize all filters and distinguished names.
+ * 
+ * A single write operation exists for enabling schemas needed for operating indices
+ * in partitions and enabling schemas that are dependencies of other schemas that 
+ * are enabled.  In both these limited cases there is no need to worry about issues
+ * with a lack of replication propagation because these same updates will take place
+ * on replicas when the original operation is propagated or when replicas start up.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class SchemaPartitionDao
+{
+    /** static class logger */
+    private final Logger LOG = LoggerFactory.getLogger( getClass() );
+    private static final NumericOidSyntaxChecker NUMERIC_OID_CHECKER = new NumericOidSyntaxChecker();
+    private static final String[] SCHEMA_ATTRIBUTES = new String[]
+        { SchemaConstants.CREATORS_NAME_AT, "m-dependencies", SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.CN_AT,
+            "m-disabled" };
+
+    private final Partition partition;
+    private final Registries registries;
+    private final SchemaEntityFactory factory;
+    private final OidRegistry oidRegistry;
+    private final AttributeTypeRegistry attrRegistry;
+
+    private final String M_NAME_OID;
+    private final String CN_OID;
+    private final String M_OID_OID;
+    private final String OBJECTCLASS_OID;
+    private final String M_SYNTAX_OID;
+    private final String M_ORDERING_OID;
+    private final String M_SUBSTRING_OID;
+    private final String M_EQUALITY_OID;
+    private final String M_SUP_ATTRIBUTE_TYPE_OID;
+    private final String M_MUST_OID;
+    private final String M_MAY_OID;
+    private final String M_AUX_OID;
+    private final String M_OC_OID;
+    private final String M_SUP_OBJECT_CLASS_OID;
+    private final String M_DEPENDENCIES_OID;
+
+    private final AttributeType disabledAttributeType;
+
+
+    /**
+     * Creates a schema dao object backing information within a schema partition.
+     * 
+     * @param partition the schema partition
+     * @param registries the bootstrap registries that were used to start up the schema partition
+     * @throws NamingException if there are problems initializing this schema partion dao
+     */
+    public SchemaPartitionDao( Partition partition, Registries registries ) throws NamingException
+    {
+        this.partition = partition;
+        this.registries = registries;
+        this.factory = new SchemaEntityFactory( registries );
+        this.oidRegistry = registries.getOidRegistry();
+        this.attrRegistry = registries.getAttributeTypeRegistry();
+
+        this.M_NAME_OID = oidRegistry.getOid( MetaSchemaConstants.M_NAME_AT );
+        this.CN_OID = oidRegistry.getOid( SchemaConstants.CN_AT );
+        this.disabledAttributeType = attrRegistry.lookup( MetaSchemaConstants.M_DISABLED_AT );
+        this.M_OID_OID = oidRegistry.getOid( MetaSchemaConstants.M_OID_AT );
+        this.OBJECTCLASS_OID = oidRegistry.getOid( SchemaConstants.OBJECT_CLASS_AT );
+        this.M_SYNTAX_OID = oidRegistry.getOid( MetaSchemaConstants.M_SYNTAX_AT );
+        this.M_ORDERING_OID = oidRegistry.getOid( MetaSchemaConstants.M_ORDERING_AT );
+        this.M_EQUALITY_OID = oidRegistry.getOid( MetaSchemaConstants.M_EQUALITY_AT );
+        this.M_SUBSTRING_OID = oidRegistry.getOid( MetaSchemaConstants.M_SUBSTR_AT );
+        this.M_SUP_ATTRIBUTE_TYPE_OID = oidRegistry.getOid( MetaSchemaConstants.M_SUP_ATTRIBUTE_TYPE_AT );
+        this.M_MUST_OID = oidRegistry.getOid( MetaSchemaConstants.M_MUST_AT );
+        this.M_MAY_OID = oidRegistry.getOid( MetaSchemaConstants.M_MAY_AT );
+        this.M_AUX_OID = oidRegistry.getOid( MetaSchemaConstants.M_AUX_AT );
+        this.M_OC_OID = oidRegistry.getOid( MetaSchemaConstants.M_OC_AT );
+        this.M_SUP_OBJECT_CLASS_OID = oidRegistry.getOid( MetaSchemaConstants.M_SUP_OBJECT_CLASS_AT );
+        this.M_DEPENDENCIES_OID = oidRegistry.getOid( MetaSchemaConstants.M_DEPENDENCIES_AT );
+    }
+
+
+    public Map<String, Schema> getSchemas() throws NamingException
+    {
+        Map<String, Schema> schemas = new HashMap<String, Schema>();
+        NamingEnumeration<ServerSearchResult> list = listSchemas();
+
+        while ( list.hasMore() )
+        {
+            ServerSearchResult sr = list.next();
+            Schema schema = factory.getSchema( sr.getServerEntry() );
+            schemas.put( schema.getSchemaName(), schema );
+        }
+
+        return schemas;
+    }
+
+
+    public Set<String> getSchemaNames() throws NamingException
+    {
+        Set<String> schemaNames = new HashSet<String>();
+        NamingEnumeration<ServerSearchResult> list = listSchemas();
+
+        while ( list.hasMore() )
+        {
+            ServerSearchResult sr = list.next();
+            schemaNames.add( sr.getServerEntry().get( SchemaConstants.CN_AT ).getString() );
+        }
+
+        return schemaNames;
+    }
+
+
+    private NamingEnumeration<ServerSearchResult> listSchemas() throws NamingException
+    {
+        LdapDN base = new LdapDN( ServerDNConstants.OU_SCHEMA_DN );
+        base.normalize( attrRegistry.getNormalizerMapping() );
+        ExprNode filter = new EqualityNode( oidRegistry.getOid( SchemaConstants.OBJECT_CLASS_AT ),
+            new ClientStringValue( MetaSchemaConstants.META_SCHEMA_OC ) );
+        SearchControls searchControls = new SearchControls();
+        searchControls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+        searchControls.setReturningAttributes( SCHEMA_ATTRIBUTES );
+        return partition.search( new SearchOperationContext( registries, base, AliasDerefMode.DEREF_ALWAYS, filter,
+            searchControls ) );
+    }
+
+
+    public Schema getSchema( String schemaName ) throws NamingException
+    {
+        LdapDN dn = new LdapDN( "cn=" + schemaName + ",ou=schema" );
+        dn.normalize( attrRegistry.getNormalizerMapping() );
+        return factory.getSchema( partition.lookup( new LookupOperationContext( registries, dn ) ) );
+    }
+
+
+    public boolean hasMatchingRule( String oid ) throws NamingException
+    {
+        BranchNode filter = new AndNode();
+        filter.addNode( new EqualityNode( OBJECTCLASS_OID, new ClientStringValue(
+            MetaSchemaConstants.META_MATCHING_RULE_OC ) ) );
+
+        if ( NUMERIC_OID_CHECKER.isValidSyntax( oid ) )
+        {
+            filter.addNode( new EqualityNode( M_OID_OID, new ClientStringValue( oid ) ) );
+        }
+        else
+        {
+            filter.addNode( new EqualityNode( M_NAME_OID, new ClientStringValue( oid.toLowerCase() ) ) );
+        }
+
+        SearchControls searchControls = new SearchControls();
+        searchControls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        NamingEnumeration<ServerSearchResult> ne = null;
+
+        try
+        {
+            ne = partition.search( new SearchOperationContext( registries, partition.getSuffixDn(),
+                AliasDerefMode.DEREF_ALWAYS, filter, searchControls ) );
+
+            if ( !ne.hasMore() )
+            {
+                return false;
+            }
+
+            ne.next();
+            if ( ne.hasMore() )
+            {
+                throw new NamingException( "Got more than one matchingRule for oid of " + oid );
+            }
+
+            return true;
+        }
+        finally
+        {
+            if ( ne != null )
+            {
+                ne.close();
+            }
+        }
+    }
+
+
+    public boolean hasAttributeType( String oid ) throws NamingException
+    {
+        BranchNode filter = new AndNode();
+        filter.addNode( new EqualityNode( OBJECTCLASS_OID, new ClientStringValue(
+            MetaSchemaConstants.META_ATTRIBUTE_TYPE_OC ) ) );
+
+        if ( NUMERIC_OID_CHECKER.isValidSyntax( oid ) )
+        {
+            filter.addNode( new EqualityNode( M_OID_OID, new ClientStringValue( oid ) ) );
+        }
+        else
+        {
+            filter.addNode( new EqualityNode( M_NAME_OID, new ClientStringValue( oid.toLowerCase() ) ) );
+        }
+
+        SearchControls searchControls = new SearchControls();
+        searchControls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        NamingEnumeration<ServerSearchResult> ne = null;
+
+        try
+        {
+            ne = partition.search( new SearchOperationContext( registries, partition.getSuffixDn(),
+                AliasDerefMode.DEREF_ALWAYS, filter, searchControls ) );
+
+            if ( !ne.hasMore() )
+            {
+                return false;
+            }
+
+            ne.next();
+            if ( ne.hasMore() )
+            {
+                throw new NamingException( "Got more than one attributeType for oid of " + oid );
+            }
+
+            return true;
+        }
+        finally
+        {
+            if ( ne != null )
+            {
+                ne.close();
+            }
+        }
+    }
+
+
+    public boolean hasObjectClass( String oid ) throws NamingException
+    {
+        BranchNode filter = new AndNode();
+        filter.addNode( new EqualityNode( OBJECTCLASS_OID, new ClientStringValue(
+            MetaSchemaConstants.META_OBJECT_CLASS_OC ) ) );
+
+        if ( NUMERIC_OID_CHECKER.isValidSyntax( oid ) )
+        {
+            filter.addNode( new EqualityNode( M_OID_OID, new ClientStringValue( oid ) ) );
+        }
+        else
+        {
+            filter.addNode( new EqualityNode( M_NAME_OID, new ClientStringValue( oid.toLowerCase() ) ) );
+        }
+
+        SearchControls searchControls = new SearchControls();
+        searchControls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        NamingEnumeration<ServerSearchResult> ne = null;
+
+        try
+        {
+            ne = partition.search( new SearchOperationContext( registries, partition.getSuffixDn(),
+                AliasDerefMode.DEREF_ALWAYS, filter, searchControls ) );
+
+            if ( !ne.hasMore() )
+            {
+                return false;
+            }
+
+            ne.next();
+            if ( ne.hasMore() )
+            {
+                throw new NamingException( "Got more than one attributeType for oid of " + oid );
+            }
+
+            return true;
+        }
+        finally
+        {
+            if ( ne != null )
+            {
+                ne.close();
+            }
+        }
+    }
+
+
+    public boolean hasSyntax( String oid ) throws NamingException
+    {
+        BranchNode filter = new AndNode();
+        filter
+            .addNode( new EqualityNode( OBJECTCLASS_OID, new ClientStringValue( MetaSchemaConstants.META_SYNTAX_OC ) ) );
+
+        if ( NUMERIC_OID_CHECKER.isValidSyntax( oid ) )
+        {
+            filter.addNode( new EqualityNode( M_OID_OID, new ClientStringValue( oid ) ) );
+        }
+        else
+        {
+            filter.addNode( new EqualityNode( M_NAME_OID, new ClientStringValue( oid.toLowerCase() ) ) );
+        }
+
+        SearchControls searchControls = new SearchControls();
+        searchControls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        NamingEnumeration<ServerSearchResult> ne = null;
+
+        try
+        {
+            ne = partition.search( new SearchOperationContext( registries, partition.getSuffixDn(),
+                AliasDerefMode.DEREF_ALWAYS, filter, searchControls ) );
+
+            if ( !ne.hasMore() )
+            {
+                return false;
+            }
+
+            ne.next();
+            if ( ne.hasMore() )
+            {
+                throw new NamingException( "Got more than one syntax for oid of " + oid );
+            }
+
+            return true;
+        }
+        finally
+        {
+            if ( ne != null )
+            {
+                ne.close();
+            }
+        }
+    }
+
+
+    public boolean hasSyntaxChecker( String oid ) throws NamingException
+    {
+        BranchNode filter = new AndNode();
+        filter.addNode( new EqualityNode( OBJECTCLASS_OID, new ClientStringValue(
+            MetaSchemaConstants.META_SYNTAX_CHECKER_OC ) ) );
+
+        if ( NUMERIC_OID_CHECKER.isValidSyntax( oid ) )
+        {
+            filter.addNode( new EqualityNode( M_OID_OID, new ClientStringValue( oid ) ) );
+        }
+        else
+        {
+            filter.addNode( new EqualityNode( M_NAME_OID, new ClientStringValue( oid.toLowerCase() ) ) );
+        }
+
+        SearchControls searchControls = new SearchControls();
+        searchControls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        NamingEnumeration<ServerSearchResult> ne = null;
+
+        try
+        {
+            ne = partition.search( new SearchOperationContext( registries, partition.getSuffixDn(),
+                AliasDerefMode.DEREF_ALWAYS, filter, searchControls ) );
+
+            if ( !ne.hasMore() )
+            {
+                return false;
+            }
+
+            ne.next();
+            if ( ne.hasMore() )
+            {
+                throw new NamingException( "Got more than one syntaxChecker for oid of " + oid );
+            }
+
+            return true;
+        }
+        finally
+        {
+            if ( ne != null )
+            {
+                ne.close();
+            }
+        }
+    }
+
+
+    /**
+     * Given the non-normalized name (alias) or the OID for a schema entity.  This 
+     * method finds the schema under which that entity is located. 
+     * 
+     * NOTE: this method presumes that all alias names across schemas are unique.  
+     * This should be the case for LDAP but this can potentially be violated so 
+     * we should make sure this is a unique name.
+     * 
+     * @param entityName one of the names of the entity or it's numeric id
+     * @return the name of the schema that contains that entity or null if no entity with 
+     * that alias name exists
+     * @throws NamingException if more than one entity has the name, or if there 
+     * are underlying data access problems
+     */
+    public String findSchema( String entityName ) throws NamingException
+    {
+        LdapDN dn = findDn( entityName );
+        if ( dn == null )
+        {
+            return null;
+        }
+
+        Rdn rdn = dn.getRdn( 1 );
+        if ( !rdn.getNormType().equalsIgnoreCase( CN_OID ) )
+        {
+            throw new NamingException( "Attribute of second rdn in dn '" + dn.toNormName()
+                + "' expected to be CN oid of " + CN_OID + " but was " + rdn.getNormType() );
+        }
+
+        return ( String ) rdn.getValue();
+    }
+
+
+    public LdapDN findDn( String entityName ) throws NamingException
+    {
+        ServerSearchResult sr = find( entityName );
+        LdapDN dn = sr.getDn();
+        dn.normalize( attrRegistry.getNormalizerMapping() );
+        return dn;
+    }
+
+
+    /**
+     * Given the non-normalized name (alias) or the OID for a schema entity.  This 
+     * method finds the entry of the schema entity. 
+     * 
+     * NOTE: this method presumes that all alias names across schemas are unique.  
+     * This should be the case for LDAP but this can potentially be violated so 
+     * we should make sure this is a unique name.
+     * 
+     * @param entityName one of the names of the entity or it's numeric id
+     * @return the search result for the entity or null if no such entity exists with 
+     * that alias or numeric oid
+     * @throws NamingException if more than one entity has the name, or if there 
+     * are underlying data access problems
+     */
+    public ServerSearchResult find( String entityName ) throws NamingException
+    {
+        BranchNode filter = new OrNode();
+        SimpleNode nameAVA = new EqualityNode( M_NAME_OID, new ClientStringValue( entityName.toLowerCase() ) );
+        SimpleNode oidAVA = new EqualityNode( M_OID_OID, new ClientStringValue( entityName.toLowerCase() ) );
+        filter.addNode( nameAVA );
+        filter.addNode( oidAVA );
+        SearchControls searchControls = new SearchControls();
+        searchControls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        NamingEnumeration<ServerSearchResult> ne = null;
+
+        try
+        {
+            ne = partition.search( new SearchOperationContext( registries, partition.getSuffixDn(),
+                AliasDerefMode.DEREF_ALWAYS, filter, searchControls ) );
+
+            if ( !ne.hasMore() )
+            {
+                return null;
+            }
+
+            ServerSearchResult sr = ne.next();
+            if ( ne.hasMore() )
+            {
+                throw new NamingException( "Got more than one result for the entity name: " + entityName );
+            }
+
+            return sr;
+        }
+        finally
+        {
+            if ( ne != null )
+            {
+                ne.close();
+            }
+        }
+    }
+
+
+    /**
+     * Enables a schema by removing it's m-disabled attribute if present.
+     * 
+     * NOTE:
+     * This is a write operation and great care must be taken to make sure it
+     * is used in a limited capacity.  This method is called in two places 
+     * currently.  
+     * 
+     * (1) Within the initialization sequence to enable schemas required
+     *     for the correct operation of indices in other partitions.
+     * (2) Within the partition schema loader to auto enable schemas that are
+     *     depended on by other schemas which are enabled.
+     * 
+     * In both cases, the modifier is effectively the administrator since the 
+     * server is performing the operation directly or on behalf of a user.  In 
+     * case (1) during intialization there is no other user involved so naturally
+     * the modifier is the administrator.  In case (2) when a user enables a 
+     * schema with a dependency that is not enabled the server enables that 
+     * dependency on behalf of the user.  Again effectively it is the server that
+     * is modifying the schema entry and hence the admin is the modifier.
+     * 
+     * No need to worry about a lack of replication propagation in both cases.  In 
+     * case (1) all replicas will enable these schemas anyway on startup.  In case
+     * (2) the original operation that enabled the schema depending on the on that
+     * enableSchema() is called for itself will be replicated.  Hence the same chain 
+     * reaction will occur in a replica.
+     * 
+     * @param schemaName the name of the schema to enable
+     * @throws NamingException if there is a problem updating the schema entry
+     */
+    public void enableSchema( String schemaName ) throws NamingException
+    {
+        LdapDN dn = new LdapDN( "cn=" + schemaName + ",ou=schema" );
+        dn.normalize( attrRegistry.getNormalizerMapping() );
+        ServerEntry entry = partition.lookup( new LookupOperationContext( registries, dn ) );
+        EntryAttribute disabledAttr = entry.get( disabledAttributeType );
+        List<Modification> mods = new ArrayList<Modification>( 3 );
+
+        if ( disabledAttr == null )
+        {
+            LOG.warn( "Does not make sense: you're trying to enable {} schema which is already enabled", schemaName );
+            return;
+        }
+
+        boolean isDisabled = disabledAttr.contains( "TRUE" );
+        if ( !isDisabled )
+        {
+            LOG.warn( "Does not make sense: you're trying to enable {} schema which is already enabled", schemaName );
+            return;
+        }
+
+        mods.add( new ServerModification( DirContext.REMOVE_ATTRIBUTE, new DefaultServerAttribute(
+            MetaSchemaConstants.M_DISABLED_AT, attrRegistry.lookup( MetaSchemaConstants.M_DISABLED_AT ) ) ) );
+
+        mods.add( new ServerModification( DirContext.ADD_ATTRIBUTE, new DefaultServerAttribute(
+            SchemaConstants.MODIFIERS_NAME_AT, attrRegistry.lookup( SchemaConstants.MODIFIERS_NAME_AT ),
+            ServerDNConstants.ADMIN_SYSTEM_DN ) ) );
+
+        mods.add( new ServerModification( DirContext.ADD_ATTRIBUTE, new DefaultServerAttribute(
+            SchemaConstants.MODIFY_TIMESTAMP_AT, attrRegistry.lookup( SchemaConstants.MODIFY_TIMESTAMP_AT ), DateUtils
+                .getGeneralizedTime() ) ) );
+
+        partition.modify( new ModifyOperationContext( registries, dn, mods ) );
+    }
+
+
+    /**
+     * Returns the set of matchingRules and attributeTypes which depend on the 
+     * provided syntax.
+     *
+     * @param numericOid the numeric identifier for the entity
+     * @return the set of matchingRules and attributeTypes depending on a syntax
+     * @throws NamingException if the dao fails to perform search operations
+     */
+    public Set<ServerSearchResult> listSyntaxDependents( String numericOid ) throws NamingException
+    {
+        Set<ServerSearchResult> set = new HashSet<ServerSearchResult>();
+        BranchNode filter = new AndNode();
+
+        // subfilter for (| (objectClass=metaMatchingRule) (objectClass=metaAttributeType))  
+        BranchNode or = new OrNode();
+        or.addNode( new EqualityNode( OBJECTCLASS_OID, new ClientStringValue( MetaSchemaConstants.META_MATCHING_RULE_OC
+            .toLowerCase() ) ) );
+        or.addNode( new EqualityNode( OBJECTCLASS_OID, new ClientStringValue(
+            MetaSchemaConstants.META_ATTRIBUTE_TYPE_OC.toLowerCase() ) ) );
+
+        filter.addNode( or );
+        filter.addNode( new EqualityNode( M_SYNTAX_OID, new ClientStringValue( numericOid.toLowerCase() ) ) );
+
+        SearchControls searchControls = new SearchControls();
+        searchControls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        NamingEnumeration<ServerSearchResult> ne = null;
+
+        try
+        {
+            ne = partition.search( new SearchOperationContext( registries, partition.getSuffixDn(),
+                AliasDerefMode.DEREF_ALWAYS, filter, searchControls ) );
+            while ( ne.hasMore() )
+            {
+                set.add( ne.next() );
+            }
+        }
+        finally
+        {
+            if ( ne != null )
+            {
+                ne.close();
+            }
+        }
+
+        return set;
+    }
+
+
+    public Set<ServerSearchResult> listMatchingRuleDependents( MatchingRule mr ) throws NamingException
+    {
+        Set<ServerSearchResult> set = new HashSet<ServerSearchResult>();
+        BranchNode filter = new AndNode();
+
+        // ( objectClass = metaAttributeType )
+        filter.addNode( new EqualityNode( OBJECTCLASS_OID, new ClientStringValue(
+            MetaSchemaConstants.META_ATTRIBUTE_TYPE_OC.toLowerCase() ) ) );
+
+        BranchNode or = new OrNode();
+        or.addNode( new EqualityNode( M_ORDERING_OID, new ClientStringValue( mr.getOid() ) ) );
+        or.addNode( new EqualityNode( M_SUBSTRING_OID, new ClientStringValue( mr.getOid() ) ) );
+        or.addNode( new EqualityNode( M_EQUALITY_OID, new ClientStringValue( mr.getOid() ) ) );
+        filter.addNode( or );
+
+        String[] names = mr.getNamesRef();
+        
+        if ( ( names != null ) || ( names.length > 0 ) )
+        {
+            for ( String name : names )
+            {
+                or.addNode( new EqualityNode( M_ORDERING_OID, new ClientStringValue( name.toLowerCase() ) ) );
+                or.addNode( new EqualityNode( M_SUBSTRING_OID, new ClientStringValue( name.toLowerCase() ) ) );
+                or.addNode( new EqualityNode( M_EQUALITY_OID, new ClientStringValue( name.toLowerCase() ) ) );
+            }
+        }
+
+        SearchControls searchControls = new SearchControls();
+        searchControls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        NamingEnumeration<ServerSearchResult> ne = null;
+
+        try
+        {
+            ne = partition.search( new SearchOperationContext( registries, partition.getSuffixDn(),
+                AliasDerefMode.DEREF_ALWAYS, filter, searchControls ) );
+            while ( ne.hasMore() )
+            {
+                set.add( ne.next() );
+            }
+        }
+        finally
+        {
+            if ( ne != null )
+            {
+                ne.close();
+            }
+        }
+
+        return set;
+    }
+
+
+    public NamingEnumeration<ServerSearchResult> listAllNames() throws NamingException
+    {
+        SearchControls searchControls = new SearchControls();
+        searchControls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        BranchNode filter = new AndNode();
+
+        // (& (m-oid=*) (m-name=*) )
+        filter.addNode( new PresenceNode( M_OID_OID ) );
+        filter.addNode( new PresenceNode( M_NAME_OID ) );
+        return partition.search( new SearchOperationContext( registries, partition.getSuffixDn(),
+            AliasDerefMode.DEREF_ALWAYS, filter, searchControls ) );
+    }
+
+
+    public Set<ServerSearchResult> listAttributeTypeDependents( AttributeType at ) throws NamingException
+    {
+        /*
+         * Right now the following inefficient filter is being used:
+         * 
+         * ( & 
+         *      ( | ( objectClass = metaAttributeType ) ( objectClass = metaObjectClass ) )
+         *      ( | ( m-oid = $oid ) ( m-must = $oid ) ( m-supAttributeType = $oid ) )
+         * )
+         * 
+         * the reason why this is inefficient is because the or terms have large scan counts
+         * and several loops are going to be required.  The following search is better because
+         * it constrains the results better:
+         * 
+         * ( |
+         *      ( & ( objectClass = metaAttributeType ) ( m-supAttributeType = $oid ) )
+         *      ( & ( objectClass = metaObjectClass ) ( | ( m-may = $oid ) ( m-must = $oid ) ) )
+         * )
+         */
+
+        Set<ServerSearchResult> set = new HashSet<ServerSearchResult>();
+        BranchNode filter = new AndNode();
+
+        // ( objectClass = metaAttributeType )
+        BranchNode or = new OrNode();
+        or.addNode( new EqualityNode( OBJECTCLASS_OID, new ClientStringValue(
+            MetaSchemaConstants.META_ATTRIBUTE_TYPE_OC.toLowerCase() ) ) );
+        or.addNode( new EqualityNode( OBJECTCLASS_OID, new ClientStringValue( MetaSchemaConstants.META_OBJECT_CLASS_OC
+            .toLowerCase() ) ) );
+        filter.addNode( or );
+
+        or = new OrNode();
+        or.addNode( new EqualityNode( M_MAY_OID, new ClientStringValue( at.getOid() ) ) );
+        or.addNode( new EqualityNode( M_MUST_OID, new ClientStringValue( at.getOid() ) ) );
+        or.addNode( new EqualityNode( M_SUP_ATTRIBUTE_TYPE_OID, new ClientStringValue( at.getOid() ) ) );
+        filter.addNode( or );
+
+        SearchControls searchControls = new SearchControls();
+        searchControls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        NamingEnumeration<ServerSearchResult> ne = null;
+
+        try
+        {
+            ne = partition.search( new SearchOperationContext( registries, partition.getSuffixDn(),
+                AliasDerefMode.DEREF_ALWAYS, filter, searchControls ) );
+            while ( ne.hasMore() )
+            {
+                set.add( ne.next() );
+            }
+        }
+        finally
+        {
+            if ( ne != null )
+            {
+                ne.close();
+            }
+        }
+
+        return set;
+    }
+
+
+    /**
+     * Lists the SearchResults of metaSchema objects that depend on a schema.
+     * 
+     * @param schemaName the name of the schema to search for dependees
+     * @return a set of SearchResults over the schemas whose m-dependency attribute contains schemaName
+     * @throws NamingException if there is a problem while searching the schema partition
+     */
+    public Set<ServerSearchResult> listSchemaDependents( String schemaName ) throws NamingException
+    {
+        /*
+         * The following filter is being used:
+         * 
+         * ( & ( objectClass = metaSchema ) ( m-dependencies = $schemaName ) )
+         */
+
+        Set<ServerSearchResult> set = new HashSet<ServerSearchResult>();
+        BranchNode filter = new AndNode();
+
+        filter.addNode( new EqualityNode( OBJECTCLASS_OID, new ClientStringValue( MetaSchemaConstants.META_SCHEMA_OC
+            .toLowerCase() ) ) );
+        filter.addNode( new EqualityNode( M_DEPENDENCIES_OID, new ClientStringValue( schemaName.toLowerCase() ) ) );
+
+        SearchControls searchControls = new SearchControls();
+        searchControls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+        NamingEnumeration<ServerSearchResult> ne = null;
+
+        try
+        {
+            ne = partition.search( new SearchOperationContext( registries, partition.getSuffixDn(),
+                AliasDerefMode.DEREF_ALWAYS, filter, searchControls ) );
+            while ( ne.hasMore() )
+            {
+                set.add( ne.next() );
+            }
+        }
+        finally
+        {
+            if ( ne != null )
+            {
+                ne.close();
+            }
+        }
+
+        return set;
+    }
+
+
+    /**
+     * Lists the SearchResults of metaSchema objects that depend on a schema.
+     * 
+     * @param schemaName the name of the schema to search for dependees
+     * @return a set of SearchResults over the schemas whose m-dependency attribute contains schemaName
+     * @throws NamingException if there is a problem while searching the schema partition
+     */
+    public Set<ServerSearchResult> listEnabledSchemaDependents( String schemaName ) throws NamingException
+    {
+        Set<ServerSearchResult> set = new HashSet<ServerSearchResult>();
+        BranchNode filter = new AndNode();
+
+        filter.addNode( new EqualityNode( OBJECTCLASS_OID, new ClientStringValue( MetaSchemaConstants.META_SCHEMA_OC
+            .toLowerCase() ) ) );
+        filter.addNode( new EqualityNode( M_DEPENDENCIES_OID, new ClientStringValue( schemaName.toLowerCase() ) ) );
+
+        SearchControls searchControls = new SearchControls();
+        searchControls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+        NamingEnumeration<ServerSearchResult> ne = null;
+
+        try
+        {
+            ne = partition.search( new SearchOperationContext( registries, partition.getSuffixDn(),
+                AliasDerefMode.DEREF_ALWAYS, filter, searchControls ) );
+
+            while ( ne.hasMore() )
+            {
+                ServerSearchResult sr = ne.next();
+                EntryAttribute disabled = sr.getServerEntry().get( disabledAttributeType );
+
+                if ( disabled == null )
+                {
+                    set.add( sr );
+                }
+                else if ( disabled.get().equals( "FALSE" ) )
+                {
+                    set.add( sr );
+                }
+            }
+        }
+        finally
+        {
+            if ( ne != null )
+            {
+                ne.close();
+            }
+        }
+
+        return set;
+    }
+
+
+    public Set<ServerSearchResult> listObjectClassDependents( ObjectClass oc ) throws NamingException
+    {
+        /*
+         * Right now the following inefficient filter is being used:
+         * 
+         * ( & 
+         *      ( | ( objectClass = metaObjectClass ) ( objectClass = metaDITContentRule ) 
+         *          ( objectClass = metaNameForm ) )
+         *      ( | ( m-oc = $oid ) ( m-aux = $oid ) ( m-supObjectClass = $oid ) )
+         * )
+         * 
+         * The reason why this is inefficient is because the or terms have large scan counts
+         * and several loops are going to be required.  For example all the objectClasses and 
+         * all the metaDITContentRules and all the metaNameForm candidates will be a massive 
+         * number.  This is probably going to be bigger than the 2nd term where a candidate 
+         * satisfies one of the terms.
+         * 
+         * The following search is better because it constrains the results better:
+         * 
+         * ( |
+         *      ( & ( objectClass = metaNameForm ) ( m-oc = $oid ) )
+         *      ( & ( objectClass = metaObjectClass ) ( m-supObjectClass = $oid ) )
+         *      ( & ( objectClass = metaDITContentRule ) ( m-aux = $oid ) )
+         * )
+         */
+
+        Set<ServerSearchResult> set = new HashSet<ServerSearchResult>();
+        BranchNode filter = new AndNode();
+
+        BranchNode or = new OrNode();
+        or.addNode( new EqualityNode( OBJECTCLASS_OID, new ClientStringValue( MetaSchemaConstants.META_NAME_FORM_OC
+            .toLowerCase() ) ) );
+        or.addNode( new EqualityNode( OBJECTCLASS_OID, new ClientStringValue( MetaSchemaConstants.META_OBJECT_CLASS_OC
+            .toLowerCase() ) ) );
+        or.addNode( new EqualityNode( OBJECTCLASS_OID, new ClientStringValue(
+            MetaSchemaConstants.META_DIT_CONTENT_RULE_OC.toLowerCase() ) ) );
+        filter.addNode( or );
+
+        or = new OrNode();
+        or.addNode( new EqualityNode( M_AUX_OID, new ClientStringValue( oc.getOid() ) ) );
+        or.addNode( new EqualityNode( M_OC_OID, new ClientStringValue( oc.getOid() ) ) );
+        or.addNode( new EqualityNode( M_SUP_OBJECT_CLASS_OID, new ClientStringValue( oc.getOid() ) ) );
+        filter.addNode( or );
+
+        SearchControls searchControls = new SearchControls();
+        searchControls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        NamingEnumeration<ServerSearchResult> ne = null;
+
+        try
+        {
+            ne = partition.search( new SearchOperationContext( registries, partition.getSuffixDn(),
+                AliasDerefMode.DEREF_ALWAYS, filter, searchControls ) );
+            while ( ne.hasMore() )
+            {
+                set.add( ne.next() );
+            }
+        }
+        finally
+        {
+            if ( ne != null )
+            {
+                ne.close();
+            }
+        }
+
+        return set;
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/SchemaService.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/SchemaService.java
new file mode 100644
index 0000000..bf12996
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/SchemaService.java
@@ -0,0 +1,619 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements.  See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership.  The ASF licenses this file

+ * to you 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.

+ */

+package org.apache.directory.server.core.schema;

+

+

+import org.apache.directory.server.constants.ApacheSchemaConstants;

+import org.apache.directory.server.constants.ServerDNConstants;

+import org.apache.directory.server.core.entry.DefaultServerAttribute;

+import org.apache.directory.server.core.entry.DefaultServerEntry;

+import org.apache.directory.server.core.entry.ServerAttribute;

+import org.apache.directory.server.core.entry.ServerEntry;

+import org.apache.directory.server.core.interceptor.context.LookupOperationContext;

+import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmPartition;

+import org.apache.directory.server.schema.registries.Registries;

+import org.apache.directory.shared.ldap.constants.SchemaConstants;

+import org.apache.directory.shared.ldap.entry.EntryAttribute;

+import org.apache.directory.shared.ldap.name.LdapDN;

+import org.apache.directory.shared.ldap.schema.AttributeType;

+import org.apache.directory.shared.ldap.schema.DITContentRule;

+import org.apache.directory.shared.ldap.schema.DITStructureRule;

+import org.apache.directory.shared.ldap.schema.MatchingRule;

+import org.apache.directory.shared.ldap.schema.MatchingRuleUse;

+import org.apache.directory.shared.ldap.schema.NameForm;

+import org.apache.directory.shared.ldap.schema.ObjectClass;

+import org.apache.directory.shared.ldap.schema.SchemaUtils;

+import org.apache.directory.shared.ldap.schema.Syntax;

+import org.apache.directory.shared.ldap.schema.syntax.ComparatorDescription;

+import org.apache.directory.shared.ldap.schema.syntax.NormalizerDescription;

+import org.apache.directory.shared.ldap.schema.syntax.SyntaxCheckerDescription;

+

+import javax.naming.NamingException;

+import java.util.HashSet;

+import java.util.Iterator;

+import java.util.Set;

+

+

+/**

+ * Document me!

+ *

+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>

+ * @version $Rev$, $Date$

+ */

+public class SchemaService

+{

+    private static final String[] EMPTY_STRING_ARRAY = new String[0];

+    private static final String SCHEMA_TIMESTAMP_ENTRY_DN = "cn=schemaModifications,ou=schema";

+

+

+    /** cached version of the schema subentry with all attributes in it */

+    private ServerEntry schemaSubentry;

+    private final Object lock = new Object();

+

+    /** a handle on the registries */

+    private Registries registries;

+

+    /** a handle on the schema partition */

+    private JdbmPartition schemaPartition;

+

+    /** schema operation control */

+    private SchemaOperationControl schemaControl;

+

+    /**

+     * the normalized name for the schema modification attributes

+     */

+    private LdapDN schemaModificationAttributesDN;

+

+

+

+    public SchemaService( Registries registries, JdbmPartition schemaPartition, SchemaOperationControl schemaControl ) throws NamingException

+    {

+        this.registries = registries;

+        this.schemaPartition = schemaPartition;

+        this.schemaControl = schemaControl;

+

+        schemaModificationAttributesDN = new LdapDN( SCHEMA_TIMESTAMP_ENTRY_DN );

+        schemaModificationAttributesDN.normalize( registries.getAttributeTypeRegistry().getNormalizerMapping() );

+    }

+

+

+    public boolean isSchemaSubentry( String dnString ) throws NamingException

+    {

+        if ( dnString.equalsIgnoreCase( ServerDNConstants.CN_SCHEMA_DN ) ||

+             dnString.equalsIgnoreCase( ServerDNConstants.CN_SCHEMA_DN_NORMALIZED ) )

+        {

+            return true;

+        }

+

+        LdapDN dn = new LdapDN( dnString ).normalize( registries.getAttributeTypeRegistry().getNormalizerMapping() );

+        return dn.getNormName().equals( ServerDNConstants.CN_SCHEMA_DN_NORMALIZED );

+    }

+

+

+    public Registries getRegistries()

+    {

+        return registries;

+    }

+

+

+    private ServerAttribute generateComparators() throws NamingException

+    {

+        ServerAttribute attr = new DefaultServerAttribute( 

+            registries.getAttributeTypeRegistry().lookup( SchemaConstants.COMPARATORS_AT ) );

+

+        Iterator<ComparatorDescription> list = registries.getComparatorRegistry().comparatorDescriptionIterator();

+        

+        while ( list.hasNext() )

+        {

+            ComparatorDescription description = list.next();

+            attr.add( SchemaUtils.render( description ).toString() );

+        }

+

+        return attr;

+    }

+

+

+    private ServerAttribute generateNormalizers() throws NamingException

+    {

+        ServerAttribute attr = new DefaultServerAttribute( 

+            registries.getAttributeTypeRegistry().lookup( SchemaConstants.NORMALIZERS_AT ) );

+

+        Iterator<NormalizerDescription> list = registries.getNormalizerRegistry().normalizerDescriptionIterator();

+

+        while ( list.hasNext() )

+        {

+            NormalizerDescription normalizer = list.next();

+            attr.add( SchemaUtils.render( normalizer ).toString() );

+        }

+        

+        return attr;

+    }

+

+

+    private ServerAttribute generateSyntaxCheckers() throws NamingException

+    {

+        ServerAttribute attr = new DefaultServerAttribute( 

+            registries.getAttributeTypeRegistry().lookup( SchemaConstants.SYNTAX_CHECKERS_AT ) );

+

+        Iterator<SyntaxCheckerDescription> list =

+            registries.getSyntaxCheckerRegistry().syntaxCheckerDescriptionIterator();

+

+        while ( list.hasNext() )

+        {

+            SyntaxCheckerDescription syntaxCheckerDescription = list.next();

+            attr.add( SchemaUtils.render( syntaxCheckerDescription ).toString() );

+        }

+        

+        return attr;

+    }

+

+

+    private ServerAttribute generateObjectClasses() throws NamingException

+    {

+        ServerAttribute attr = new DefaultServerAttribute( 

+            registries.getAttributeTypeRegistry().lookup( SchemaConstants.OBJECT_CLASSES_AT ) );

+

+        Iterator<ObjectClass> list = registries.getObjectClassRegistry().iterator();

+

+        while ( list.hasNext() )

+        {

+            ObjectClass oc = list.next();

+            attr.add( SchemaUtils.render( oc ).toString() );

+        }

+        

+        return attr;

+    }

+

+

+    private ServerAttribute generateAttributeTypes() throws NamingException

+    {

+        ServerAttribute attr = new DefaultServerAttribute( 

+            registries.getAttributeTypeRegistry().lookup( SchemaConstants.ATTRIBUTE_TYPES_AT ) );

+

+        Iterator<AttributeType> list = registries.getAttributeTypeRegistry().iterator();

+

+        while ( list.hasNext() )

+        {

+            AttributeType at = list.next();

+            attr.add( SchemaUtils.render( at ).toString() );

+        }

+

+        return attr;

+    }

+

+

+    private ServerAttribute generateMatchingRules() throws NamingException

+    {

+        ServerAttribute attr = new DefaultServerAttribute( 

+            registries.getAttributeTypeRegistry().lookup( SchemaConstants.MATCHING_RULES_AT ) );

+

+        Iterator<MatchingRule> list = registries.getMatchingRuleRegistry().iterator();

+

+        while ( list.hasNext() )

+        {

+            MatchingRule mr = list.next();

+            attr.add( SchemaUtils.render( mr ).toString() );

+        }

+

+        return attr;

+    }

+

+

+    private ServerAttribute generateMatchingRuleUses() throws NamingException

+    {

+        ServerAttribute attr = new DefaultServerAttribute( 

+            registries.getAttributeTypeRegistry().lookup( SchemaConstants.MATCHING_RULE_USE_AT ) );

+

+        Iterator<MatchingRuleUse> list = registries.getMatchingRuleUseRegistry().iterator();

+

+        while ( list.hasNext() )

+        {

+            MatchingRuleUse mru = list.next();

+            attr.add( SchemaUtils.render( mru ).toString() );

+        }

+

+        return attr;

+    }

+

+

+    private ServerAttribute generateSyntaxes() throws NamingException

+    {

+        ServerAttribute attr = new DefaultServerAttribute( 

+            registries.getAttributeTypeRegistry().lookup( SchemaConstants.LDAP_SYNTAXES_AT ) );

+

+        Iterator<Syntax> list = registries.getSyntaxRegistry().iterator();

+

+        while ( list.hasNext() )

+        {

+            Syntax syntax = list.next();

+            attr.add( SchemaUtils.render( syntax ).toString() );

+        }

+

+        return attr;

+    }

+

+

+    private ServerAttribute generateDitContextRules() throws NamingException

+    {

+        ServerAttribute attr = new DefaultServerAttribute( 

+            registries.getAttributeTypeRegistry().lookup( SchemaConstants.DIT_CONTENT_RULES_AT ) );

+

+        Iterator<DITContentRule> list = registries.getDitContentRuleRegistry().iterator();

+

+        while ( list.hasNext() )

+        {

+            DITContentRule dcr = list.next();

+            attr.add( SchemaUtils.render( dcr ).toString() );

+        }

+        

+        return attr;

+    }

+

+

+    private ServerAttribute generateDitStructureRules() throws NamingException

+    {

+        ServerAttribute attr = new DefaultServerAttribute( 

+            registries.getAttributeTypeRegistry().lookup( SchemaConstants.DIT_STRUCTURE_RULES_AT ) );

+

+        Iterator<DITStructureRule> list = registries.getDitStructureRuleRegistry().iterator();

+

+        while ( list.hasNext() )

+        {

+            DITStructureRule dsr = list.next();

+            attr.add( SchemaUtils.render( dsr ).toString() );

+        }

+        

+        return attr;

+    }

+

+

+    private ServerAttribute generateNameForms() throws NamingException

+    {

+        ServerAttribute attr = new DefaultServerAttribute( 

+            registries.getAttributeTypeRegistry().lookup( SchemaConstants.NAME_FORMS_AT ) );

+

+        Iterator<NameForm> list = registries.getNameFormRegistry().iterator();

+

+        while ( list.hasNext() )

+        {

+            NameForm nf = list.next();

+            attr.add( SchemaUtils.render( nf ).toString() );

+        }

+        

+        return attr;

+    }

+

+

+    private void generateSchemaSubentry( ServerEntry mods ) throws NamingException

+    {

+        ServerEntry attrs = new DefaultServerEntry( registries, mods.getDn() );

+

+        // add the objectClass attribute

+        attrs.put( SchemaConstants.OBJECT_CLASS_AT, 

+            SchemaConstants.TOP_OC,

+            SchemaConstants.SUBSCHEMA_OC,

+            SchemaConstants.SUBENTRY_OC,

+            ApacheSchemaConstants.APACHE_SUBSCHEMA_OC

+            );

+

+        // add the cn attribute as required for the RDN

+        attrs.put( SchemaConstants.CN_AT, "schema" );

+

+        // generate all the other operational attributes

+        attrs.put( generateComparators() );

+        attrs.put( generateNormalizers() );

+        attrs.put( generateSyntaxCheckers() );

+        attrs.put( generateObjectClasses() );

+        attrs.put( generateAttributeTypes() );

+        attrs.put( generateMatchingRules() );

+        attrs.put( generateMatchingRuleUses() );

+        attrs.put( generateSyntaxes() );

+        attrs.put( generateDitContextRules() );

+        attrs.put( generateDitStructureRules() );

+        attrs.put( generateNameForms() );

+        attrs.put( SchemaConstants.SUBTREE_SPECIFICATION_AT, "{}" );

+

+        // -------------------------------------------------------------------

+        // set standard operational attributes for the subentry

+        // -------------------------------------------------------------------

+

+        // Add the createTimestamp

+        AttributeType createTimestampAT = registries.

+            getAttributeTypeRegistry().lookup( SchemaConstants.CREATE_TIMESTAMP_AT );

+        EntryAttribute createTimestamp = mods.get( createTimestampAT );

+        attrs.put( SchemaConstants.CREATE_TIMESTAMP_AT, createTimestamp.get() );

+

+        // Add the creatorsName

+        attrs.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN );

+

+        // Add the modifyTimestamp

+        AttributeType schemaModifyTimestampAT = registries.

+            getAttributeTypeRegistry().lookup( ApacheSchemaConstants.SCHEMA_MODIFY_TIMESTAMP_AT );

+        EntryAttribute schemaModifyTimestamp = mods.get( schemaModifyTimestampAT );

+        attrs.put( SchemaConstants.MODIFY_TIMESTAMP_AT, schemaModifyTimestamp.get() );

+

+        // Add the modifiersName

+        AttributeType schemaModifiersNameAT = registries.

+            getAttributeTypeRegistry().lookup( ApacheSchemaConstants.SCHEMA_MODIFIERS_NAME_AT );

+        EntryAttribute schemaModifiersName = mods.get( schemaModifiersNameAT );

+        attrs.put( SchemaConstants.MODIFIERS_NAME_AT, schemaModifiersName.get() );

+

+        // don't swap out if a request for the subentry is in progress or we

+        // can give back an inconsistent schema back to the client so we block

+        synchronized ( lock )

+        {

+            schemaSubentry = attrs;

+        }

+    }

+

+

+    private void addAttribute( ServerEntry attrs, String id ) throws NamingException

+    {

+        EntryAttribute attr = schemaSubentry.get( id );

+

+        if ( attr != null )

+        {

+            attrs.put( attr );

+        }

+    }

+

+

+    /**

+     * A seriously unsafe (unsynchronized) means to access the schemaSubentry.

+     *

+     * @return the schemaSubentry

+     * @throws NamingException if there is a failure to access schema timestamps

+     */

+    public ServerEntry getSubschemaEntryImmutable() throws NamingException

+    {

+        if ( schemaSubentry == null )

+        {

+            generateSchemaSubentry( schemaPartition.lookup(

+                    new LookupOperationContext( registries, schemaModificationAttributesDN ) ) );

+        }

+

+        return (ServerEntry)schemaSubentry.clone();

+    }

+

+

+    /**

+     * A seriously unsafe (unsynchronized) means to access the schemaSubentry.

+     *

+     * @return the schemaSubentry

+     * @throws NamingException if there is a failure to access schema timestamps

+     */

+    public ServerEntry getSubschemaEntryCloned() throws NamingException

+    {

+        if ( schemaSubentry == null )

+        {

+            generateSchemaSubentry( schemaPartition.lookup(

+                    new LookupOperationContext( registries, schemaModificationAttributesDN ) ) );

+        }

+

+        return ( ServerEntry ) schemaSubentry.clone();

+    }

+

+

+    /**

+     * Gets the schemaSubentry based on specific search id parameters which

+     * include the special '*' and '+' operators.

+     *

+     * @param ids the ids of the attributes that should be returned from a search

+     * @return the subschema entry with the ids provided

+     * @throws NamingException if there are failures during schema info access

+     */

+    public ServerEntry getSubschemaEntry( String[] ids ) throws NamingException

+    {

+        if ( ids == null )

+        {

+            ids = EMPTY_STRING_ARRAY;

+        }

+

+        Set<String> setOids = new HashSet<String>();

+        ServerEntry attrs = new DefaultServerEntry( registries, LdapDN.EMPTY_LDAPDN );

+        boolean returnAllOperationalAttributes = false;

+

+        synchronized( lock )

+        {

+            // ---------------------------------------------------------------

+            // Check if we need an update by looking at timestamps on disk

+            // ---------------------------------------------------------------

+

+            ServerEntry mods = schemaPartition.lookup( new LookupOperationContext( registries, schemaModificationAttributesDN ) );

+// @todo enable this optimization at some point but for now it

+// is causing some problems so I will just turn it off

+//          Attribute modifyTimeDisk = mods.get( SchemaConstants.MODIFY_TIMESTAMP_AT );

+//

+//          Attribute modifyTimeMemory = null;

+//

+//            if ( schemaSubentry != null )

+//            {

+//                modifyTimeMemory = schemaSubentry.get( SchemaConstants.MODIFY_TIMESTAMP_AT );

+//                if ( modifyTimeDisk == null && modifyTimeMemory == null )

+//                {

+//                    // do nothing!

+//                }

+//                else if ( modifyTimeDisk != null && modifyTimeMemory != null )

+//                {

+//                    Date disk = DateUtils.getDate( ( String ) modifyTimeDisk.get() );

+//                    Date mem = DateUtils.getDate( ( String ) modifyTimeMemory.get() );

+//                    if ( disk.after( mem ) )

+//                    {

+//                        generateSchemaSubentry( mods );

+//                    }

+//                }

+//                else

+//                {

+//                    generateSchemaSubentry( mods );

+//                }

+//            }

+//            else

+//            {

+                generateSchemaSubentry( mods );

+//            }

+

+            // ---------------------------------------------------------------

+            // Prep Work: Transform the attributes to their OID counterpart

+            // ---------------------------------------------------------------

+

+            for ( String id:ids )

+            {

+                // Check whether the set contains a plus, and use it below to include all

+                // operational attributes.  Due to RFC 3673, and issue DIREVE-228 in JIRA

+                if ( SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES.equals( id ) )

+                {

+                    returnAllOperationalAttributes = true;

+                }

+                else if ( SchemaConstants.ALL_USER_ATTRIBUTES.equals(  id ) )

+                {

+                    setOids.add( id );

+                }

+                else

+                {

+                    setOids.add( registries.getOidRegistry().getOid( id ) );

+                }

+            }

+

+            if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.COMPARATORS_AT_OID ) )

+            {

+                addAttribute( attrs, SchemaConstants.COMPARATORS_AT );

+            }

+

+            if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.NORMALIZERS_AT_OID ) )

+            {

+                addAttribute( attrs, SchemaConstants.NORMALIZERS_AT );

+            }

+

+            if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.SYNTAX_CHECKERS_AT_OID ) )

+            {

+                addAttribute( attrs, SchemaConstants.SYNTAX_CHECKERS_AT );

+            }

+

+            if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.OBJECT_CLASSES_AT_OID ) )

+            {

+                addAttribute( attrs, SchemaConstants.OBJECT_CLASSES_AT );

+            }

+

+            if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.ATTRIBUTE_TYPES_AT_OID ) )

+            {

+                addAttribute( attrs, SchemaConstants.ATTRIBUTE_TYPES_AT );

+            }

+

+            if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.MATCHING_RULES_AT_OID ) )

+            {

+                addAttribute( attrs, SchemaConstants.MATCHING_RULES_AT );

+            }

+

+            if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.MATCHING_RULE_USE_AT_OID ) )

+            {

+                addAttribute( attrs, SchemaConstants.MATCHING_RULE_USE_AT );

+            }

+

+            if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.LDAP_SYNTAXES_AT_OID ) )

+            {

+                addAttribute( attrs, SchemaConstants.LDAP_SYNTAXES_AT );

+            }

+

+            if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.DIT_CONTENT_RULES_AT_OID ) )

+            {

+                addAttribute( attrs, SchemaConstants.DIT_CONTENT_RULES_AT );

+            }

+

+            if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.DIT_STRUCTURE_RULES_AT_OID ) )

+            {

+                addAttribute( attrs, SchemaConstants.DIT_STRUCTURE_RULES_AT );

+            }

+

+            if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.NAME_FORMS_AT_OID ) )

+            {

+                addAttribute( attrs, SchemaConstants.NAME_FORMS_AT );

+            }

+

+            if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.SUBTREE_SPECIFICATION_AT_OID ) )

+            {

+                addAttribute( attrs, SchemaConstants.SUBTREE_SPECIFICATION_AT );

+            }

+

+            int minSetSize = 0;

+            if ( setOids.contains( SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES ) )

+            {

+                minSetSize++;

+            }

+

+            if ( setOids.contains( SchemaConstants.ALL_USER_ATTRIBUTES ) )

+            {

+                minSetSize++;

+            }

+

+            if ( setOids.contains( SchemaConstants.REF_AT_OID ) )

+            {

+                minSetSize++;

+            }

+

+            // add the objectClass attribute

+            if ( setOids.contains( SchemaConstants.ALL_USER_ATTRIBUTES ) ||

+                 setOids.contains( SchemaConstants.OBJECT_CLASS_AT_OID ) ||

+                 setOids.size() == minSetSize )

+            {

+                addAttribute( attrs, SchemaConstants.OBJECT_CLASS_AT );

+            }

+

+            // add the cn attribute as required for the RDN

+            if ( setOids.contains( SchemaConstants.ALL_USER_ATTRIBUTES ) ||

+                 setOids.contains( SchemaConstants.CN_AT_OID ) ||

+                 setOids.size() == minSetSize )

+            {

+                addAttribute( attrs, SchemaConstants.CN_AT );

+            }

+

+            // -------------------------------------------------------------------

+            // set standard operational attributes for the subentry

+            // -------------------------------------------------------------------

+

+

+            if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.CREATE_TIMESTAMP_AT_OID ) )

+            {

+                addAttribute( attrs, SchemaConstants.CREATE_TIMESTAMP_AT );

+            }

+

+            if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.CREATORS_NAME_AT_OID ) )

+            {

+                addAttribute( attrs, SchemaConstants.CREATORS_NAME_AT );

+            }

+

+            if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.MODIFY_TIMESTAMP_AT_OID ) )

+            {

+                addAttribute( attrs, SchemaConstants.MODIFY_TIMESTAMP_AT );

+            }

+

+            if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.MODIFIERS_NAME_AT_OID ) )

+            {

+                addAttribute( attrs, SchemaConstants.MODIFIERS_NAME_AT );

+            }

+        }

+

+        return attrs;

+    }

+

+

+    SchemaOperationControl getSchemaControl()

+    {

+        return schemaControl;

+    }

+}

diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/SchemaSubentryModifier.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/SchemaSubentryModifier.java
new file mode 100644
index 0000000..7ed3098
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/SchemaSubentryModifier.java
@@ -0,0 +1,312 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.schema;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.naming.NamingException;
+import javax.naming.directory.Attributes;
+
+import org.apache.directory.server.constants.MetaSchemaConstants;
+import org.apache.directory.server.core.authn.AuthenticationInterceptor;
+import org.apache.directory.server.core.authz.AciAuthorizationInterceptor;
+import org.apache.directory.server.core.authz.DefaultAuthorizationInterceptor;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerEntryUtils;
+import org.apache.directory.server.core.exception.ExceptionInterceptor;
+import org.apache.directory.server.core.interceptor.context.AddOperationContext;
+import org.apache.directory.server.core.interceptor.context.DeleteOperationContext;
+import org.apache.directory.server.core.invocation.InvocationStack;
+import org.apache.directory.server.core.partition.PartitionNexusProxy;
+import org.apache.directory.server.core.referral.ReferralInterceptor;
+import org.apache.directory.server.schema.bootstrap.Schema;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.server.utils.AttributesFactory;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.schema.DITContentRule;
+import org.apache.directory.shared.ldap.schema.DITStructureRule;
+import org.apache.directory.shared.ldap.schema.MatchingRule;
+import org.apache.directory.shared.ldap.schema.MatchingRuleUse;
+import org.apache.directory.shared.ldap.schema.NameForm;
+import org.apache.directory.shared.ldap.schema.ObjectClass;
+import org.apache.directory.shared.ldap.schema.SchemaObject;
+import org.apache.directory.shared.ldap.schema.Syntax;
+import org.apache.directory.shared.ldap.schema.syntax.AbstractSchemaDescription;
+import org.apache.directory.shared.ldap.schema.syntax.ComparatorDescription;
+import org.apache.directory.shared.ldap.schema.syntax.NormalizerDescription;
+import org.apache.directory.shared.ldap.schema.syntax.SyntaxCheckerDescription;
+import org.apache.directory.shared.ldap.util.Base64;
+
+
+/**
+ * Responsible for translating modify operations on the subschemaSubentry into 
+ * operations against entries within the schema partition.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class SchemaSubentryModifier
+{
+    private static final Collection<String> BYPASS;
+    
+    static
+    {
+        Set<String> c = new HashSet<String>();
+//        c.add( NormalizationInterceptor.class.getName() );
+        c.add( AuthenticationInterceptor.class.getName() );
+        c.add( ReferralInterceptor.class.getName() );
+        c.add( AciAuthorizationInterceptor.class.getName() );
+        c.add( DefaultAuthorizationInterceptor.class.getName() );
+        c.add( ExceptionInterceptor.class.getName() );
+//        c.add( OperationalAttributeInterceptor.class.getName() );
+        c.add( SchemaInterceptor.class.getName() );
+//        c.add( SubentryInterceptor.class.getName() );
+//        c.add( CollectiveAttributeInterceptor.class.getName() );
+//        c.add( EventInterceptor.class.getName() );
+//        c.add( TriggerInterceptor.class.getName() );
+        BYPASS = Collections.unmodifiableCollection( c );
+    }
+    
+    private AttributesFactory factory = new AttributesFactory();
+    private final SchemaPartitionDao dao;
+
+    
+    public SchemaSubentryModifier( SchemaPartitionDao dao )
+    {
+        this.dao = dao;
+    }
+    
+    
+    private LdapDN getDn( SchemaObject obj ) throws NamingException
+    {
+        StringBuffer buf = new StringBuffer();
+        buf.append( "m-oid=" ).append( obj.getOid() ).append( ",ou=" );
+
+        if ( obj instanceof Syntax )
+        {
+            buf.append( "syntaxes" );
+        }
+        else if ( obj instanceof MatchingRule )
+        {
+            buf.append( SchemaConstants.MATCHING_RULES_AT );
+        }
+        else if ( obj instanceof AttributeType )
+        {
+            buf.append( SchemaConstants.ATTRIBUTE_TYPES_AT );
+        }
+        else if ( obj instanceof ObjectClass )
+        {
+            buf.append( SchemaConstants.OBJECT_CLASSES_AT );
+        }
+        else if ( obj instanceof MatchingRuleUse )
+        {
+            buf.append( SchemaConstants.MATCHING_RULE_USE_AT );
+        }
+        else if ( obj instanceof DITStructureRule )
+        {
+            buf.append( SchemaConstants.DIT_STRUCTURE_RULES_AT );
+        }
+        else if ( obj instanceof DITContentRule )
+        {
+            buf.append( SchemaConstants.DIT_CONTENT_RULES_AT );
+        }
+        else if ( obj instanceof NameForm )
+        {
+            buf.append( SchemaConstants.NAME_FORMS_AT );
+        }
+
+        buf.append( ",cn=" ).append( obj.getSchema() ).append( ",ou=schema" );
+        return new LdapDN( buf.toString() );
+    }
+    
+
+    public void addSchemaObject( Registries registries, SchemaObject obj ) throws NamingException
+    {
+        PartitionNexusProxy proxy = InvocationStack.getInstance().peek().getProxy();
+        Schema schema = dao.getSchema( obj.getSchema() );
+        LdapDN dn = getDn( obj );
+        ServerEntry entry = factory.getAttributes( obj, schema, registries );
+        entry.setDn( dn );
+
+        proxy.add( new AddOperationContext( registries, dn, entry, true ), BYPASS );
+    }
+
+
+    public void deleteSchemaObject( Registries registries, SchemaObject obj ) throws NamingException
+    {
+        PartitionNexusProxy proxy = InvocationStack.getInstance().peek().getProxy();
+        LdapDN dn = getDn( obj );
+        proxy.delete( new DeleteOperationContext( registries, dn, true ), BYPASS );
+    }
+
+    
+    public void delete( Registries registries, NormalizerDescription normalizerDescription ) throws NamingException
+    {
+        String schemaName = getSchema( normalizerDescription );
+        PartitionNexusProxy proxy = InvocationStack.getInstance().peek().getProxy();
+        LdapDN dn = new LdapDN( "m-oid=" + normalizerDescription.getNumericOid() + ",ou=normalizers,cn=" 
+            + schemaName + ",ou=schema" );
+        proxy.delete( new DeleteOperationContext( registries, dn, true ), BYPASS );
+    }
+
+
+    public void delete( Registries registries, SyntaxCheckerDescription syntaxCheckerDescription ) throws NamingException
+    {
+        String schemaName = getSchema( syntaxCheckerDescription );
+        PartitionNexusProxy proxy = InvocationStack.getInstance().peek().getProxy();
+        LdapDN dn = new LdapDN( "m-oid=" + syntaxCheckerDescription.getNumericOid() + ",ou=syntaxCheckers,cn=" 
+            + schemaName + ",ou=schema" );
+        proxy.delete( new DeleteOperationContext( registries, dn, true ), BYPASS );
+    }
+
+
+    public void delete( Registries registries, ComparatorDescription comparatorDescription ) throws NamingException
+    {
+        String schemaName = getSchema( comparatorDescription );
+        PartitionNexusProxy proxy = InvocationStack.getInstance().peek().getProxy();
+        LdapDN dn = new LdapDN( "m-oid=" + comparatorDescription.getNumericOid() + ",ou=comparators,cn=" 
+            + schemaName + ",ou=schema" );
+        proxy.delete( new DeleteOperationContext( registries, dn, true ), BYPASS );
+    }
+
+
+    public void add( Registries registries, ComparatorDescription comparatorDescription ) throws NamingException
+    {
+        String schemaName = getSchema( comparatorDescription );   
+        PartitionNexusProxy proxy = InvocationStack.getInstance().peek().getProxy();
+        LdapDN dn = new LdapDN( "m-oid=" + comparatorDescription.getNumericOid() + ",ou=comparators,cn=" 
+            + schemaName + ",ou=schema" );
+        Attributes attrs = getAttributes( comparatorDescription );
+        ServerEntry entry = ServerEntryUtils.toServerEntry( attrs, dn, registries );
+
+        proxy.add( new AddOperationContext( registries, dn, entry, true ), BYPASS );
+    }
+    
+    
+    private Attributes getAttributes( ComparatorDescription comparatorDescription )
+    {
+        AttributesImpl attributes = new AttributesImpl( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC, true );
+        attributes.get( SchemaConstants.OBJECT_CLASS_AT ).add( "metaTop" );
+        attributes.get( SchemaConstants.OBJECT_CLASS_AT ).add( "metaComparator" );
+        attributes.put( MetaSchemaConstants.M_OID_AT, comparatorDescription.getNumericOid() );
+        attributes.put( MetaSchemaConstants.M_FQCN_AT, comparatorDescription.getFqcn() );
+
+        if ( comparatorDescription.getBytecode() != null )
+        {
+            attributes.put( MetaSchemaConstants.M_BYTECODE_AT, 
+                Base64.decode( comparatorDescription.getBytecode().toCharArray() ) );
+        }
+        
+        if ( comparatorDescription.getDescription() != null )
+        {
+            attributes.put( MetaSchemaConstants.M_DESCRIPTION_AT, comparatorDescription.getDescription() );
+        }
+        
+        return attributes;
+    }
+
+
+    public void add( Registries registries, NormalizerDescription normalizerDescription ) throws NamingException
+    {
+        String schemaName = getSchema( normalizerDescription );
+        PartitionNexusProxy proxy = InvocationStack.getInstance().peek().getProxy();
+        LdapDN dn = new LdapDN( "m-oid=" + normalizerDescription.getNumericOid() + ",ou=normalizers,cn=" 
+            + schemaName + ",ou=schema" );
+        Attributes attrs = getAttributes( normalizerDescription );
+        ServerEntry entry = ServerEntryUtils.toServerEntry( attrs, dn, registries );
+
+        proxy.add( new AddOperationContext( registries, dn, entry, true ), BYPASS );
+    }
+    
+    
+    private Attributes getAttributes( NormalizerDescription normalizerDescription )
+    {
+        AttributesImpl attributes = new AttributesImpl( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC, true );
+        attributes.get( SchemaConstants.OBJECT_CLASS_AT ).add( "metaTop" );
+        attributes.get( SchemaConstants.OBJECT_CLASS_AT ).add( "metaNormalizer" );
+        attributes.put( MetaSchemaConstants.M_OID_AT, normalizerDescription.getNumericOid() );
+        attributes.put( MetaSchemaConstants.M_FQCN_AT, normalizerDescription.getFqcn() );
+
+        if ( normalizerDescription.getBytecode() != null )
+        {
+            attributes.put( MetaSchemaConstants.M_BYTECODE_AT, 
+                Base64.decode( normalizerDescription.getBytecode().toCharArray() ) );
+        }
+        
+        if ( normalizerDescription.getDescription() != null )
+        {
+            attributes.put( MetaSchemaConstants.M_DESCRIPTION_AT, normalizerDescription.getDescription() );
+        }
+        
+        return attributes;
+    }
+
+
+    public void add( Registries registries, SyntaxCheckerDescription syntaxCheckerDescription ) throws NamingException
+    {
+        String schemaName = getSchema( syntaxCheckerDescription );
+        PartitionNexusProxy proxy = InvocationStack.getInstance().peek().getProxy();
+        LdapDN dn = new LdapDN( "m-oid=" + syntaxCheckerDescription.getNumericOid() + ",ou=syntaxCheckers,cn=" 
+            + schemaName + ",ou=schema" );
+        Attributes attrs = getAttributes( syntaxCheckerDescription );
+        ServerEntry entry = ServerEntryUtils.toServerEntry( attrs, dn, registries );
+        proxy.add( new AddOperationContext( registries, dn, entry, true ), BYPASS );
+    }
+    
+    
+    private String getSchema( AbstractSchemaDescription desc ) 
+    {
+        if ( desc.getExtensions().containsKey( MetaSchemaConstants.X_SCHEMA ) )
+        {
+            return desc.getExtensions().get( MetaSchemaConstants.X_SCHEMA ).get( 0 );
+        }
+        
+        return MetaSchemaConstants.SCHEMA_OTHER;
+    }
+    
+    
+    private Attributes getAttributes( SyntaxCheckerDescription syntaxCheckerDescription )
+    {
+        AttributesImpl attributes = new AttributesImpl( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC, true );
+        attributes.get( SchemaConstants.OBJECT_CLASS_AT ).add( "metaTop" );
+        attributes.get( SchemaConstants.OBJECT_CLASS_AT ).add( "metaSyntaxChecker" );
+        attributes.put( MetaSchemaConstants.M_OID_AT, syntaxCheckerDescription.getNumericOid() );
+        attributes.put( MetaSchemaConstants.M_FQCN_AT, syntaxCheckerDescription.getFqcn() );
+
+        if ( syntaxCheckerDescription.getBytecode() != null )
+        {
+            attributes.put( MetaSchemaConstants.M_BYTECODE_AT, 
+                Base64.decode( syntaxCheckerDescription.getBytecode().toCharArray() ) );
+        }
+        
+        if ( syntaxCheckerDescription.getDescription() != null )
+        {
+            attributes.put( MetaSchemaConstants.M_DESCRIPTION_AT, syntaxCheckerDescription.getDescription() );
+        }
+        
+        return attributes;
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/SyntaxImpl.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/SyntaxImpl.java
new file mode 100644
index 0000000..88de415
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/SyntaxImpl.java
@@ -0,0 +1,78 @@
+/*
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *   or more contributor license agreements.  See the NOTICE file
+ *   distributed with this work for additional information
+ *   regarding copyright ownership.  The ASF licenses this file
+ *   to you 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.
+ *
+ */
+package org.apache.directory.server.core.schema;
+
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.schema.registries.SyntaxCheckerRegistry;
+import org.apache.directory.shared.ldap.schema.AbstractSyntax;
+import org.apache.directory.shared.ldap.schema.MutableSchemaObject;
+import org.apache.directory.shared.ldap.schema.syntax.SyntaxChecker;
+
+
+class SyntaxImpl extends AbstractSyntax implements MutableSchemaObject
+{
+    private static final long serialVersionUID = 1L;
+    private final SyntaxCheckerRegistry registry;
+
+
+    protected SyntaxImpl( String oid, SyntaxCheckerRegistry registry )
+    {
+        super( oid );
+        this.registry = registry;
+    }
+
+    
+    public SyntaxChecker getSyntaxChecker() throws NamingException
+    {
+        return registry.lookup( oid );
+    }
+    
+    
+    public void setDescription( String description )
+    {
+        super.setDescription( description );
+    }
+    
+    
+    public void setHumanReadable( boolean humanReadable )
+    {
+        super.setHumanReadable( humanReadable );
+    }
+    
+    
+    public void setSchema( String schema )
+    {
+        super.setSchema( schema );
+    }
+    
+    
+    public void setObsolete( boolean obsolete )
+    {
+        super.setObsolete( obsolete );
+    }
+    
+    
+    public void setNames( String[] names )
+    {
+        super.setNames( names );
+    }
+}
\ No newline at end of file
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/package-info.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/package-info.java
new file mode 100644
index 0000000..7c7a14f
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/schema/package-info.java
@@ -0,0 +1,56 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.
+ *
+ */
+
+/**
+ * <pre>
+ * <p>
+ * Contains interfaces for schema object registry services and simple POJO
+ * implementations of these services.  Other helper interfaces and classes are
+ * included for handling monitoring of these services.
+ * </p>
+ * <p>
+ * These services and their POJO implementations are purposefully kept really
+ * simple here for a reason.  When one looks at these interfaces they stop and
+ * think why even bother having them when you can just use a map of objects
+ * somewhere.  These simple services can and will get more complex as other
+ * facilities come into play namely the object builders that populate these
+ * registries.  There might also be caching going on as well as disk based
+ * store access.  Finally dependencies become an issue and sometime bootstrap
+ * instances of these components are required by the system.  So these simple
+ * watered down interfaces and their POJO's have been pruned from previously
+ * complex environment specific versions of them.
+ * </p>
+ * <p>
+ * Some key points to apply to services and their POJO impls in this package:
+ * <ul>
+ * <li>registries only register and allow for lookups: its that simple!</li>
+ * <li>don't worry if they change over time</li>
+ * <li>don't worry about how they get populated</li>
+ * <li>don't worry who or what does the populating</li>
+ * <li>don't worry about where the information comes from</li>
+ * </ul>
+ * </p>
+ * </pre>
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+
+package org.apache.directory.server.core.schema;
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/security/CoreKeyStoreSpi.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/security/CoreKeyStoreSpi.java
new file mode 100644
index 0000000..67ae38f
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/security/CoreKeyStoreSpi.java
@@ -0,0 +1,332 @@
+/*
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *   or more contributor license agreements.  See the NOTICE file
+ *   distributed with this work for additional information
+ *   regarding copyright ownership.  The ASF licenses this file
+ *   to you 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.
+ *
+ */
+package org.apache.directory.server.core.security;
+
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.Key;
+import java.security.KeyPair;
+import java.security.KeyStoreException;
+import java.security.KeyStoreSpi;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.Date;
+import java.util.Enumeration;
+
+import javax.naming.NamingException;
+import javax.naming.directory.Attributes;
+
+import org.apache.commons.lang.ArrayUtils;
+import org.apache.commons.lang.NotImplementedException;
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.authn.LdapPrincipal;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerEntryUtils;
+import org.apache.directory.server.core.partition.PartitionNexus;
+import org.apache.directory.shared.ldap.constants.AuthenticationLevel;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.util.SingletonEnumeration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * A read only key store facility designed specifically for TLS/CA operations.
+ * It is only intended for accessing the 'apacheds' private/public key pairs
+ * as well as the self signed certificate.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class CoreKeyStoreSpi extends KeyStoreSpi
+{
+    private static final String APACHEDS_ALIAS = "apacheds";
+
+    private static final Logger LOG = LoggerFactory.getLogger( CoreKeyStoreSpi.class );
+    
+    private DirectoryService directoryService;
+    
+
+    /**
+     * Creates a new instance of LocalKeyStore.
+     */
+    public CoreKeyStoreSpi( DirectoryService directoryService )
+    {
+        LOG.debug( "Constructor called." );
+        this.directoryService = directoryService;
+    }
+
+
+    private ServerEntry getTlsEntry() throws NamingException
+    {
+        LdapDN adminDn = PartitionNexus.getAdminName();
+        LdapPrincipal principal = new LdapPrincipal( adminDn, AuthenticationLevel.SIMPLE );
+        Attributes attrs = directoryService.getJndiContext( principal ).getAttributes( adminDn );
+        return ServerEntryUtils.toServerEntry( attrs, adminDn, directoryService.getRegistries() );
+    }
+    
+    
+    /* (non-Javadoc)
+     * @see java.security.KeyStoreSpi#engineAliases()
+     */
+    @Override
+    public Enumeration<String> engineAliases()
+    {
+        LOG.debug( "engineAliases() called." );
+        return new SingletonEnumeration<String>( APACHEDS_ALIAS );
+    }
+
+
+    /* (non-Javadoc)
+     * @see java.security.KeyStoreSpi#engineContainsAlias(java.lang.String)
+     */
+    @Override
+    public boolean engineContainsAlias( String alias )
+    {
+        LOG.debug( "engineContainsAlias({}) called.", alias );
+        
+        if ( alias.equalsIgnoreCase( APACHEDS_ALIAS ) )
+        {
+            return true;
+        }
+        
+        return false;
+    }
+
+
+    /* (non-Javadoc)
+     * @see java.security.KeyStoreSpi#engineDeleteEntry(java.lang.String)
+     */
+    @Override
+    public void engineDeleteEntry( String alias ) throws KeyStoreException
+    {
+        LOG.debug( "engineDeleteEntry({}) called.", alias );
+        throw new UnsupportedOperationException();
+    }
+
+
+    /* (non-Javadoc)
+     * @see java.security.KeyStoreSpi#engineGetCertificate(java.lang.String)
+     */
+    @Override
+    public Certificate engineGetCertificate( String alias )
+    {
+        LOG.debug( "engineGetCertificate({}) called.", alias );
+        if ( alias.equalsIgnoreCase( APACHEDS_ALIAS ) )
+        {
+            try
+            {
+                ServerEntry entry = getTlsEntry();
+                return TlsKeyGenerator.getCertificate( entry );
+            }
+            catch ( NamingException e )
+            {
+                LOG.error( "Failed to access certificate in DIT.", e );
+            }
+        }
+        
+        return null;
+    }
+
+
+    /* (non-Javadoc)
+     * @see java.security.KeyStoreSpi#engineGetCertificateAlias(java.security.cert.Certificate)
+     */
+    @Override
+    public String engineGetCertificateAlias( Certificate cert )
+    {
+        LOG.debug( "engineGetCertificateAlias({}) called.", cert );
+        
+        if ( cert instanceof X509Certificate )
+        {
+            LOG.debug( "Certificate in alias request is X.509 based." );
+            X509Certificate xcert = ( X509Certificate ) cert;
+            if ( xcert.getSubjectDN().toString().equals( TlsKeyGenerator.CERTIFICATE_PRINCIPAL_DN ) )
+            {
+                return APACHEDS_ALIAS;
+            }
+        }
+        
+        try
+        {
+            ServerEntry entry = getTlsEntry();
+            if ( ArrayUtils.isEquals( cert.getEncoded(), entry.get( TlsKeyGenerator.USER_CERTIFICATE_AT ).getBytes() ) )
+            {
+                return APACHEDS_ALIAS;
+            }
+        }
+        catch ( Exception e )
+        {
+            LOG.error( "Failed on attempt to compare certificate bytes to determine alias.", e );
+        }
+        
+        return null;
+    }
+
+
+    /* (non-Javadoc)
+     * @see java.security.KeyStoreSpi#engineGetCertificateChain(java.lang.String)
+     */
+    @Override
+    public Certificate[] engineGetCertificateChain( String alias )
+    {
+        LOG.debug( "engineGetCertificateChain({}) called.", alias );
+        try
+        {
+            ServerEntry entry = getTlsEntry();
+            LOG.debug( "Entry:\n{}", entry );
+            return new Certificate[] { TlsKeyGenerator.getCertificate( entry ) };
+        }
+        catch ( Exception e )
+        {
+            LOG.error( "Failed on attempt to compare certificate bytes to determine alias.", e );
+        }
+        
+        return new Certificate[0];
+    }
+
+
+    /* (non-Javadoc)
+     * @see java.security.KeyStoreSpi#engineGetCreationDate(java.lang.String)
+     */
+    @Override
+    public Date engineGetCreationDate( String alias )
+    {
+        LOG.debug( "engineGetCreationDate({}) called.", alias );
+        return new Date();
+    }
+
+
+    /* (non-Javadoc)
+     * @see java.security.KeyStoreSpi#engineGetKey(java.lang.String, char[])
+     */
+    @Override
+    public Key engineGetKey( String alias, char[] password ) throws NoSuchAlgorithmException, UnrecoverableKeyException
+    {
+        LOG.debug( "engineGetKey({}, {}) called.", alias, password );
+        
+        try
+        {
+            ServerEntry entry = getTlsEntry();
+            KeyPair keyPair = TlsKeyGenerator.getKeyPair( entry );
+            return keyPair.getPrivate();
+        }
+        catch ( Exception e )
+        {
+            LOG.error( "Failed on attempt to extract key.", e );
+        }
+        
+        return null;
+    }
+
+
+    /* (non-Javadoc)
+     * @see java.security.KeyStoreSpi#engineIsCertificateEntry(java.lang.String)
+     */
+    @Override
+    public boolean engineIsCertificateEntry( String alias )
+    {
+        LOG.debug( "engineIsCertificateEntry({}) called.", alias );
+        return false;
+    }
+
+
+    /* (non-Javadoc)
+     * @see java.security.KeyStoreSpi#engineIsKeyEntry(java.lang.String)
+     */
+    @Override
+    public boolean engineIsKeyEntry( String alias )
+    {
+        LOG.debug( "engineIsKeyEntry({}) called.", alias );
+        return true;
+    }
+
+
+    /* (non-Javadoc)
+     * @see java.security.KeyStoreSpi#engineLoad(java.io.InputStream, char[])
+     */
+    @Override
+    public void engineLoad( InputStream stream, char[] password ) throws IOException, NoSuchAlgorithmException,
+        CertificateException
+    {
+        LOG.debug( "engineLoad({}, {}) called.", stream, password );
+    }
+
+
+    /* (non-Javadoc)
+     * @see java.security.KeyStoreSpi#engineSetCertificateEntry(java.lang.String, java.security.cert.Certificate)
+     */
+    @Override
+    public void engineSetCertificateEntry( String alias, Certificate cert ) throws KeyStoreException
+    {
+        LOG.debug( "engineSetCertificateEntry({}, {}) called.", alias, cert );
+        throw new NotImplementedException();
+    }
+
+
+    /* (non-Javadoc)
+     * @see java.security.KeyStoreSpi#engineSetKeyEntry(java.lang.String, byte[], java.security.cert.Certificate[])
+     */
+    @Override
+    public void engineSetKeyEntry( String alias, byte[] key, Certificate[] chain ) throws KeyStoreException
+    {
+        LOG.debug( "engineSetKeyEntry({}, key, {}) called.", alias, chain );
+        throw new NotImplementedException();
+    }
+
+
+    /* (non-Javadoc)
+     * @see java.security.KeyStoreSpi#engineSetKeyEntry(java.lang.String, java.security.Key, char[], java.security.cert.Certificate[])
+     */
+    @Override
+    public void engineSetKeyEntry( String alias, Key key, char[] password, Certificate[] chain )
+        throws KeyStoreException
+    {
+        LOG.debug( "engineSetKeyEntry({}, key, {}, chain) called.", alias, new String( password ) );
+        throw new NotImplementedException();
+    }
+
+
+    /* (non-Javadoc)
+     * @see java.security.KeyStoreSpi#engineSize()
+     */
+    @Override
+    public int engineSize()
+    {
+        LOG.debug( "engineSize() called." );
+        return 1;
+    }
+
+
+    /* (non-Javadoc)
+     * @see java.security.KeyStoreSpi#engineStore(java.io.OutputStream, char[])
+     */
+    @Override
+    public void engineStore( OutputStream stream, char[] password ) throws IOException, NoSuchAlgorithmException,
+        CertificateException
+    {
+        LOG.debug( "engineStore(stream, {}) called.", new String( password ) );
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/security/TlsKeyGenerator.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/security/TlsKeyGenerator.java
new file mode 100644
index 0000000..3f8a226
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/security/TlsKeyGenerator.java
@@ -0,0 +1,280 @@
+/*
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *   or more contributor license agreements.  See the NOTICE file
+ *   distributed with this work for additional information
+ *   regarding copyright ownership.  The ASF licenses this file
+ *   to you 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.
+ *
+ */
+package org.apache.directory.server.core.security;
+
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.math.BigInteger;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.Security;
+import java.util.Date;
+
+import javax.naming.NamingException;
+import javax.security.auth.x500.X500Principal;
+
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.security.spec.EncodedKeySpec;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.x509.X509V1CertificateGenerator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * Generates the default RSA key pair for the server.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class TlsKeyGenerator
+{
+    private static final Logger LOG = LoggerFactory.getLogger( TlsKeyGenerator.class );
+    
+    public static final String TLS_KEY_INFO_OC = "tlsKeyInfo";
+    public static final String PRIVATE_KEY_AT = "privateKey";
+    public static final String PUBLIC_KEY_AT = "publicKey";
+    public static final String KEY_ALGORITHM_AT = "keyAlgorithm";
+    public static final String PRIVATE_KEY_FORMAT_AT = "privateKeyFormat";
+    public static final String PUBLIC_KEY_FORMAT_AT = "publicKeyFormat";
+    public static final String USER_CERTIFICATE_AT = "userCertificate";
+
+    public static final String CERTIFICATE_PRINCIPAL_DN =
+        "CN=ApacheDS, OU=Directory, O=ASF, C=US";
+    private static final String ALGORITHM = "RSA";
+    
+    /* 
+     * Eventually we have to make several of these parameters configurable,
+     * however note to pass export restrictions we must use a key size of
+     * 512 or less here as the default.  Users can configure this setting
+     * later based on their own legal situations.  This is required to 
+     * classify ApacheDS in the ECCN 5D002 category.  Please see the following
+     * page for more information:
+     * 
+     *    http://www.apache.org/dev/crypto.html
+     * 
+     * Also ApacheDS must be classified on the following page:
+     * 
+     *    http://www.apache.org/licenses/exports
+     */ 
+    private static final int KEY_SIZE = 512;
+    private static final long YEAR_MILLIS = 365*24*3600*1000;
+    
+
+    static
+    {
+        Security.addProvider( new BouncyCastleProvider() );
+    }
+
+    
+    /**
+     * Gets the certificate associated with the self signed TLS private/public 
+     * key pair.
+     *
+     * @param entry the TLS key/cert entry
+     * @return the X509 certificate associated with that entry
+     * @throws NamingException if there are problems accessing or decoding
+     */
+    public static X509Certificate getCertificate( ServerEntry entry ) throws NamingException
+    {
+        X509Certificate cert = null;
+        CertificateFactory certFactory = null;
+        
+        try
+        {
+            certFactory = CertificateFactory.getInstance( "X.509", "BC" );
+        }
+        catch ( Exception e )
+        {
+            NamingException ne = new NamingException( "Failed to get BC Certificate factory for algorithm: X.509" );
+            ne.setRootCause( e );
+            throw ne;
+        }
+
+        byte[] certBytes = entry.get( USER_CERTIFICATE_AT ).getBytes();
+        InputStream in = new ByteArrayInputStream( certBytes );
+
+        try
+        {
+            cert = ( X509Certificate ) certFactory.generateCertificate( in );
+        }
+        catch ( CertificateException e )
+        {
+            NamingException ne = new NamingException( "Bad certificate format." );
+            ne.setRootCause( e );
+            throw ne;
+        }
+        
+        return cert;
+    }
+    
+    
+    /**
+     * Extracts the public private key pair from the tlsKeyInfo entry.
+     *
+     * @param entry an entry of the tlsKeyInfo objectClass
+     * @return the private and public key pair
+     * @throws NamingException if there are format or access issues
+     */
+    public static KeyPair getKeyPair( ServerEntry entry ) throws NamingException
+    {
+        PublicKey publicKey = null;
+        PrivateKey privateKey = null;
+        
+        KeyFactory keyFactory = null;
+        try
+        {
+            keyFactory = KeyFactory.getInstance( ALGORITHM );
+        }
+        catch ( Exception e )
+        {
+            NamingException ne = new NamingException( "Failed to get key factory for algorithm: " + ALGORITHM );
+            ne.setRootCause( e );
+            throw ne;
+        }
+        
+        EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec( entry.get( PRIVATE_KEY_AT ).getBytes() );
+        try
+        {
+            privateKey = keyFactory.generatePrivate( privateKeySpec );
+        }
+        catch ( Exception e )
+        {
+            NamingException ne = new NamingException( "Bad private key format." );
+            ne.setRootCause( e );
+            throw ne;
+        }
+    
+        EncodedKeySpec publicKeySpec = new X509EncodedKeySpec( entry.get( PUBLIC_KEY_AT ).getBytes() );
+        try
+        {
+            publicKey = keyFactory.generatePublic( publicKeySpec );
+        }
+        catch ( InvalidKeySpecException e )
+        {
+            NamingException ne = new NamingException( "Bad public key format." );
+            ne.setRootCause( e );
+            throw ne;
+        }
+        
+        return new KeyPair( publicKey, privateKey );
+    }
+    
+
+    /**
+     * Adds a private key pair along with a self signed certificate to an 
+     * entry making sure it contains the objectClasses and attributes needed
+     * to support the additions.  This function is intended for creating a TLS
+     * key value pair and self signed certificate for use by the server to 
+     * authenticate itself during SSL handshakes in the course of establishing
+     * an LDAPS connection or a secure LDAP connection using StartTLS. Usually
+     * this information is added to the administrator user's entry so the 
+     * administrator (effectively the server) can manage these security 
+     * concerns.
+     * 
+     * @param entry the entry to add security attributes to
+     * @throws NamingException on problems generating the content in the entry
+     */
+    public static void addKeyPair( ServerEntry entry ) throws NamingException
+    {
+        EntryAttribute objectClass = entry.get( SchemaConstants.OBJECT_CLASS_AT );
+        
+        if ( objectClass == null )
+        {
+            entry.put( SchemaConstants.OBJECT_CLASS_AT, TLS_KEY_INFO_OC, SchemaConstants.INET_ORG_PERSON_OC );
+        }
+        else
+        {
+            objectClass.add( TLS_KEY_INFO_OC, SchemaConstants.INET_ORG_PERSON_OC );
+        }
+        
+        KeyPairGenerator generator = null;
+        try
+        {
+            generator = KeyPairGenerator.getInstance( ALGORITHM );
+        }
+        catch ( NoSuchAlgorithmException e )
+        {
+            NamingException ne = new NamingException( "Cannot generate key pair for TLS" );
+            ne.setRootCause( e );
+            throw ne;
+        }
+
+        generator.initialize( KEY_SIZE );
+        KeyPair keypair = generator.genKeyPair();
+        entry.put( KEY_ALGORITHM_AT, ALGORITHM );
+        
+        // Generate the private key attributes 
+        PrivateKey privateKey = keypair.getPrivate();
+        entry.put( PRIVATE_KEY_AT, privateKey.getEncoded() );
+        entry.put( PRIVATE_KEY_FORMAT_AT, privateKey.getFormat() );
+        LOG.debug( "PrivateKey: {}", privateKey );
+        
+        PublicKey publicKey = keypair.getPublic();
+        entry.put( PUBLIC_KEY_AT, publicKey.getEncoded() );
+        entry.put( PUBLIC_KEY_FORMAT_AT, publicKey.getFormat() );
+        LOG.debug( "PublicKey: {}", publicKey );
+        
+        // Generate the self-signed certificate
+        Date startDate = new Date(); 
+        Date expiryDate = new Date( System.currentTimeMillis() + YEAR_MILLIS ); 
+        BigInteger serialNumber = BigInteger.valueOf( System.currentTimeMillis() );
+
+        X509V1CertificateGenerator certGen = new X509V1CertificateGenerator();
+        X500Principal dnName = new X500Principal( CERTIFICATE_PRINCIPAL_DN );
+
+        certGen.setSerialNumber( serialNumber );
+        certGen.setIssuerDN( dnName );
+        certGen.setNotBefore( startDate );
+        certGen.setNotAfter( expiryDate );
+        certGen.setSubjectDN( dnName );
+        certGen.setPublicKey( publicKey );
+        certGen.setSignatureAlgorithm( "SHA1With" + ALGORITHM );
+
+        try
+        {
+            X509Certificate cert = certGen.generate( privateKey, "BC" );
+            entry.put( USER_CERTIFICATE_AT, cert.getEncoded() );
+            LOG.debug( "X509 Certificate: {}", cert );
+        }
+        catch ( Exception e )
+        {
+            NamingException ne = new NamingException( "Cannot generate self signed certificate." );
+            ne.setRootCause( e );
+            throw ne;
+        }
+        
+        LOG.info( "Keys and self signed certificate successfully generated." );
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/sp/LdapClassLoader.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/sp/LdapClassLoader.java
new file mode 100644
index 0000000..5e28e26
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/sp/LdapClassLoader.java
@@ -0,0 +1,184 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+
+package org.apache.directory.server.core.sp;
+
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+
+import org.apache.directory.server.constants.ApacheSchemaConstants;
+import org.apache.directory.server.core.jndi.ServerLdapContext;
+import org.apache.directory.shared.ldap.entry.client.ClientStringValue;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.filter.AndNode;
+import org.apache.directory.shared.ldap.filter.BranchNode;
+import org.apache.directory.shared.ldap.filter.EqualityNode;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * A class loader that loads classes from an LDAP DIT.
+ * 
+ * <p>
+ * This loader looks for an configuration entry whose DN is
+ * determined by defaultSearchContextsConfig variable. If there is such
+ * an entry it gets the search contexts from the entry and searches the 
+ * class to be loaded in those contexts.
+ * If there is no default search context configuration entry it searches
+ * the class in the whole DIT. 
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$ $Date$
+ */
+public class LdapClassLoader extends ClassLoader
+{
+    private static final Logger log = LoggerFactory.getLogger( LdapClassLoader.class );
+    public static String defaultSearchContextsConfig = "cn=classLoaderDefaultSearchContext,ou=configuration,ou=system";
+    private ServerLdapContext RootDSE;
+
+    public LdapClassLoader( ServerLdapContext RootDSE ) throws NamingException
+    {
+        super( LdapClassLoader.class.getClassLoader() );
+        this.RootDSE = ( ( ServerLdapContext ) RootDSE.lookup( "" ) );
+    }
+
+    private byte[] findClassInDIT( NamingEnumeration searchContexts, String name ) throws ClassNotFoundException
+    {
+        String currentSearchContextName = null;
+        ServerLdapContext currentSearchContext = null;
+        NamingEnumeration javaClassEntries = null;
+        byte[] classBytes = null;
+        
+        BranchNode filter = new AndNode( );
+        filter.addNode( new EqualityNode( "fullyQualifiedJavaClassName", new ClientStringValue( name ) ) );
+        filter.addNode( new EqualityNode( SchemaConstants.OBJECT_CLASS_AT, new ClientStringValue( ApacheSchemaConstants.JAVA_CLASS_OC ) ) );
+        
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        
+        try
+        {
+            while( searchContexts.hasMore() )
+            {
+                currentSearchContextName = ( String ) searchContexts.next();
+                
+                if ( currentSearchContextName == null )
+                {
+                    continue;
+                }
+                
+                currentSearchContext = ( ServerLdapContext ) RootDSE.lookup( currentSearchContextName );
+                
+                javaClassEntries = currentSearchContext.search( LdapDN.EMPTY_LDAPDN, filter, controls );
+                if ( javaClassEntries.hasMore() ) // there should be only one!
+                {
+                    log.debug( "Class " + name + " found under " + currentSearchContextName + " search context." );
+                    SearchResult javaClassEntry = ( SearchResult ) javaClassEntries.next();
+                    Attribute byteCode = javaClassEntry.getAttributes().get( "javaClassByteCode" );
+                    classBytes = ( byte[] ) byteCode.get();
+                    break; // exit on first hit!
+                }
+            }
+        }
+        catch ( NamingException e )
+        {
+            throw new ClassNotFoundException();
+        }
+        
+        return classBytes;
+    }
+    
+    public Class<?> findClass( String name ) throws ClassNotFoundException
+    {
+        byte[] classBytes = null;
+
+        NamingEnumeration defaultSearchContexts = null;
+        NamingEnumeration namingContexts = null;
+        
+        ServerLdapContext defaultSearchContextsConfigContext = null;
+        
+        try 
+        {   
+            try
+            {
+                defaultSearchContextsConfigContext = 
+                    ( ServerLdapContext ) RootDSE.lookup( defaultSearchContextsConfig );
+            }
+            catch ( NamingException e )
+            {
+                log.debug( "No configuration data found for class loader default search contexts." );
+            }
+            
+            if ( defaultSearchContextsConfigContext != null )
+            {
+                defaultSearchContexts = defaultSearchContextsConfigContext
+                    .getAttributes( "", new String[] { "classLoaderDefaultSearchContext" } )
+                    .get( "classLoaderDefaultSearchContext" ).getAll();
+                
+                try
+                {
+                    classBytes = findClassInDIT( defaultSearchContexts, name );
+                    
+                    log.debug( "Class " + name + " found under default search contexts." );
+                }
+                catch ( ClassNotFoundException e )
+                {
+                    log.debug( "Class " + name + " could not be found under default search contexts." );
+                }
+            }
+            
+            if ( classBytes == null )
+            {
+                namingContexts = RootDSE
+                    .getAttributes( "", new String[] { "namingContexts" } )
+                    .get( "namingContexts" ).getAll();
+                
+                classBytes = findClassInDIT( namingContexts, name );
+            }
+        } 
+        catch ( NamingException e ) 
+        {
+            String msg = "Encountered JNDI failure while searching directory for class: " + name;
+            log.error( msg + e );
+            throw new ClassNotFoundException( msg );
+        }
+        catch ( ClassNotFoundException e )
+        {
+            String msg = "Class " + name + " not found in DIT.";
+            log.debug( msg );
+            throw new ClassNotFoundException( msg );
+        }
+        finally
+        {
+            if ( defaultSearchContexts != null ) { try { defaultSearchContexts.close(); } catch( Exception e ) {} }
+            if ( namingContexts != null ) { try { namingContexts.close(); } catch( Exception e ) {} }
+        }
+        
+        return defineClass( name, classBytes, 0, classBytes.length );
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/sp/StoredProcEngine.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/sp/StoredProcEngine.java
new file mode 100644
index 0000000..36fd4ef
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/sp/StoredProcEngine.java
@@ -0,0 +1,66 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+
+package org.apache.directory.server.core.sp;
+
+import javax.naming.NamingException;
+import javax.naming.ldap.LdapContext;
+
+import org.apache.directory.server.core.entry.ServerEntry;
+
+
+/**
+ * An abstraction over stored procedure execution depending on the type of the language supported.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$ $Date$
+ */
+public interface StoredProcEngine
+{
+    
+    /**
+     * Returns the unique identifier of the supported stored procedure language.
+     * 
+     */
+    public String getSPLangId();
+    
+    
+    /**
+     * Registers an entry found to be contaning a stored procedure unit which this engine can operate on.
+     *
+     * <p>
+     * This method should be called before an attempt to invoke a stored procedure via this Engine.
+     */
+    public void setSPUnitEntry( final ServerEntry spUnit );
+    
+    
+    /**
+     * Invokes the stored procedure handled by the engine.
+     * 
+     * @param rootDSE A handle on Root DSE to invoke the stored procedure over.
+     * @param fullSPName A fully qualified name of the stored procedure including its unit name.
+     * @param spArgs A list or arguments to be passed to the stored procedure. It should be an empty array if there aren't any parameters defined.
+     * @return The value obtained from invoked procedure. The client should know what will return exactly so that it can downcast to the appropriate type.
+     * @throws NamingException If an error occurs during invocation.
+     */
+    public Object invokeProcedure( LdapContext rootDSE, String fullSPName, Object[] spArgs ) throws NamingException;
+
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/sp/StoredProcEngineConfig.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/sp/StoredProcEngineConfig.java
new file mode 100644
index 0000000..ddc90d4
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/sp/StoredProcEngineConfig.java
@@ -0,0 +1,46 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+
+package org.apache.directory.server.core.sp;
+
+
+/**
+ * A configuration wrapper for {@link StoredProcEngine}s.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$ $Date$
+ */
+public interface StoredProcEngineConfig
+{
+    /**
+     * Returns the type of the associated {@link StoredProcEngine}.
+     * 
+     */
+    public Class<? extends StoredProcEngine> getStoredProcEngineType();
+    
+    
+    /**
+     * Returns the unique language identifier of the {@link StoredProcEngine}.
+     * 
+     */
+    public String getStoredProcLangId();
+
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/sp/StoredProcExecutionManager.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/sp/StoredProcExecutionManager.java
new file mode 100644
index 0000000..52de19f
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/sp/StoredProcExecutionManager.java
@@ -0,0 +1,132 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+
+package org.apache.directory.server.core.sp;
+
+
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerEntryUtils;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.LdapContext;
+import java.util.List;
+
+/**
+ * A Factory type class which holds a registry of supported {@link StoredProcEngineConfig}s. A container reference
+ * as the base for Stored Procedure storage on the DIT is also handled by this class.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$ $Date$
+ */
+public class StoredProcExecutionManager
+{
+    
+    private final String storedProcContainer;
+
+    private final List<StoredProcEngineConfig> storedProcEngineConfigs;
+
+
+    /**
+     * Creates a {@link StoredProcExecutionManager} instance.
+     * 
+     * @param storedProcContainer The base of the DIT subtree used for storing stored procedure units.
+     * @param storedProcEngineConfigs A list of {@link StoredProcEngineConfig}s to register different {@link StoredProcEngine}s with this manager.
+     */
+    public StoredProcExecutionManager( final String storedProcContainer, final List<StoredProcEngineConfig> storedProcEngineConfigs )
+    {
+        this.storedProcContainer = storedProcContainer;
+        this.storedProcEngineConfigs = storedProcEngineConfigs;
+    }
+    
+    /**
+     * Finds and returns a stored procedure unit entry whose identifier name
+     * is extracted from fullSPName.
+     * 
+     * @param rootDSE A handle on the root DSE to be used for searching the SP Unit over.
+     * @param fullSPName Full name of the Stored Procedure including the unit name.
+     * @return The entry associated with the SP Unit.
+     * @throws NamingException If the unit cannot be located or any other error occurs.
+     */
+    public ServerEntry findStoredProcUnit( LdapContext rootDSE, String fullSPName, Registries registries ) throws NamingException
+    {
+        SearchControls controls = new SearchControls();
+        controls.setReturningAttributes( SchemaConstants.ALL_USER_ATTRIBUTES_ARRAY );
+        controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        String spUnitName = StoredProcUtils.extractStoredProcUnitName( fullSPName );
+        String filter = "(storedProcUnitName=" + spUnitName + ")";
+        NamingEnumeration<SearchResult> results = rootDSE.search( storedProcContainer, filter, controls );
+        return ServerEntryUtils.toServerEntry( results.nextElement().getAttributes(), LdapDN.EMPTY_LDAPDN, registries );
+    }
+
+
+    /**
+     * Initializes and returns a {@link StoredProcEngine} instance which can operate on spUnitEntry
+     * considering its specific stored procedure language.
+     * 
+     * @param spUnitEntry The entry which a {@link StoredProcEngine} type will be mathched with respect to the language identifier.
+     * @return A {@link StoredProcEngine} associated with spUnitEntry.
+     * @throws NamingException If no {@link StoredProcEngine} that can be associated with the language identifier in spUnitEntry can be found.
+     */
+    public StoredProcEngine getStoredProcEngineInstance( ServerEntry spUnitEntry ) throws NamingException
+    {
+        String spLangId = ( String ) spUnitEntry.get( "storedProcLangId" ).getString();
+
+        for ( StoredProcEngineConfig engineConfig : storedProcEngineConfigs )
+        {
+            if ( engineConfig.getStoredProcLangId().equalsIgnoreCase( spLangId ) )
+            {
+                Class<? extends StoredProcEngine> engineType = engineConfig.getStoredProcEngineType();
+                StoredProcEngine engine;
+                
+                try
+                {
+                    engine = engineType.newInstance();
+                }
+                catch ( InstantiationException e )
+                {
+                    NamingException ne = new NamingException();
+                    ne.setRootCause( e );
+                    throw ne;
+                }
+                catch ( IllegalAccessException e )
+                {
+                    NamingException ne = new NamingException();
+                    ne.setRootCause( e );
+                    throw ne;
+                }
+                
+                engine.setSPUnitEntry( spUnitEntry );
+                return engine;
+            }
+
+        }
+
+        throw new NamingException( "Stored Procedure Language, " + spLangId + " is not supported." );
+
+    }
+
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/sp/StoredProcUtils.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/sp/StoredProcUtils.java
new file mode 100644
index 0000000..5b67ce7
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/sp/StoredProcUtils.java
@@ -0,0 +1,51 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+
+package org.apache.directory.server.core.sp;
+
+
+/**
+ * A utility class for working with Stored Procedures.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$ $Date$
+ */
+public class StoredProcUtils
+{
+    
+    /** The delimiter used to tokenize a full SP name into the unit and SP name */
+    public static final String SPUnitDelimiter = ":";
+
+    public static String extractStoredProcName( String fullSPName )
+    {
+        int delimiter = fullSPName.lastIndexOf( SPUnitDelimiter );
+        String spName = fullSPName.substring( delimiter + SPUnitDelimiter.length() );
+        return spName;
+    }
+    
+    public static String extractStoredProcUnitName( String fullSPName )
+    {
+        int delimiter = fullSPName.lastIndexOf( SPUnitDelimiter );
+        String className = fullSPName.substring( 0, delimiter );
+        return className;
+    }
+    
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/sp/java/JavaStoredProcEngine.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/sp/java/JavaStoredProcEngine.java
new file mode 100644
index 0000000..9d4d6d2
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/sp/java/JavaStoredProcEngine.java
@@ -0,0 +1,148 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+
+package org.apache.directory.server.core.sp.java;
+
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.naming.NamingException;
+import javax.naming.ldap.LdapContext;
+
+import org.apache.directory.server.core.entry.ServerAttribute;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.sp.StoredProcEngine;
+import org.apache.directory.server.core.sp.StoredProcUtils;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.util.DirectoryClassUtils;
+
+
+/**
+ * A {@link StoredProcEngine} implementation specific to Java stored procedures.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$ $Date$
+ */
+public class JavaStoredProcEngine implements StoredProcEngine
+{
+
+    public static final String STORED_PROC_LANG_ID = "Java";
+
+    private ServerEntry spUnit;
+
+
+    /* (non-Javadoc)
+     * @see org.apache.directory.server.core.sp.StoredProcEngine#invokeProcedure(javax.naming.ldap.LdapContext, java.lang.String, java.lang.Object[])
+     */
+    public Object invokeProcedure( LdapContext rootCtx, String fullSPName, Object[] spArgs ) throws NamingException
+    {
+        EntryAttribute javaByteCode = spUnit.get( "javaByteCode" );
+        String spName = StoredProcUtils.extractStoredProcName( fullSPName );
+        String className = StoredProcUtils.extractStoredProcUnitName( fullSPName );
+
+        ClassLoader loader = new LdapJavaStoredProcClassLoader( javaByteCode );
+        Class<?> clazz;
+        
+        try
+        {
+            clazz = loader.loadClass( className );
+        }
+        catch ( ClassNotFoundException e )
+        {
+            NamingException ne = new NamingException();
+            ne.setRootCause( e );
+            throw ne;
+        }
+
+        Class<?>[] types = getTypesFromValues( spArgs );
+
+        Method proc;
+        try
+        {
+            proc = DirectoryClassUtils.getAssignmentCompatibleMethod( clazz, spName, types );
+        }
+        catch ( NoSuchMethodException e )
+        {
+            NamingException ne = new NamingException();
+            ne.setRootCause( e );
+            throw ne;
+        }
+        try
+        {
+            return proc.invoke( null, spArgs );
+        }
+        catch ( IllegalArgumentException e )
+        {
+            NamingException ne = new NamingException();
+            ne.setRootCause( e );
+            throw ne;
+        }
+        catch ( IllegalAccessException e )
+        {
+            NamingException ne = new NamingException();
+            ne.setRootCause( e );
+            throw ne;
+        }
+        catch ( InvocationTargetException e )
+        {
+            NamingException ne = new NamingException();
+            ne.setRootCause( e );
+            throw ne;
+        }
+    }
+
+
+    /* (non-Javadoc)
+     * @see org.apache.directory.server.core.sp.StoredProcEngine#getSPLangId()
+     */
+    public String getSPLangId()
+    {
+        return STORED_PROC_LANG_ID;
+    }
+
+
+    /* (non-Javadoc)
+     * @see org.apache.directory.server.core.sp.StoredProcEngine#setSPUnitEntry(javax.naming.directory.Attributes)
+     */
+    public void setSPUnitEntry( ServerEntry spUnit )
+    {
+        this.spUnit = spUnit;
+    }
+
+
+    private Class<?>[] getTypesFromValues( Object[] values )
+    {
+        List<Class<?>> types = new ArrayList<Class<?>>();
+
+        for ( Object obj : values )
+        {
+            types.add( obj.getClass() );
+        }
+
+        return types.toArray( EMPTY_CLASS_ARRAY );
+    }
+
+    private static Class<?>[] EMPTY_CLASS_ARRAY = new Class[ 0 ];
+
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/sp/java/JavaStoredProcEngineConfig.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/sp/java/JavaStoredProcEngineConfig.java
new file mode 100644
index 0000000..94a806f
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/sp/java/JavaStoredProcEngineConfig.java
@@ -0,0 +1,54 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+
+package org.apache.directory.server.core.sp.java;
+
+import org.apache.directory.server.core.sp.StoredProcEngine;
+import org.apache.directory.server.core.sp.StoredProcEngineConfig;
+
+
+/**
+ * A {@link StoredProcEngineConfig} implementation specific to Java stored procedures.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$ $Date$
+ */
+public class JavaStoredProcEngineConfig implements StoredProcEngineConfig
+{
+
+    /* (non-Javadoc)
+     * @see org.apache.directory.server.core.sp.StoredProcEngineConfig#getStoredProcEngineType()
+     */
+    public Class<? extends StoredProcEngine> getStoredProcEngineType()
+    {
+        return JavaStoredProcEngine.class;
+    }
+
+
+    /* (non-Javadoc)
+     * @see org.apache.directory.server.core.sp.StoredProcEngineConfig#getStoredProcLangId()
+     */
+    public String getStoredProcLangId()
+    {
+        return JavaStoredProcEngine.STORED_PROC_LANG_ID;
+    }
+
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/sp/java/LdapJavaStoredProcClassLoader.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/sp/java/LdapJavaStoredProcClassLoader.java
new file mode 100644
index 0000000..8cdc1a2
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/sp/java/LdapJavaStoredProcClassLoader.java
@@ -0,0 +1,64 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.core.sp.java;
+
+
+import javax.naming.NamingException;
+
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+
+
+/**
+ * A class loader that loads a class from an attribute.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$ $Date$
+ */
+public class LdapJavaStoredProcClassLoader extends ClassLoader
+{
+    private EntryAttribute javaByteCodeAttr;
+
+
+    public LdapJavaStoredProcClassLoader( EntryAttribute javaByteCodeAttr )
+    {
+        // Critical call to super class constructor. Required for true plumbing of class loaders.
+        super( LdapJavaStoredProcClassLoader.class.getClassLoader() );
+
+        this.javaByteCodeAttr = javaByteCodeAttr;
+    }
+
+
+    public Class<?> findClass( String name ) throws ClassNotFoundException
+    {
+        byte[] classBytes;
+        
+        try
+        {
+            classBytes = javaByteCodeAttr.getBytes();
+        }
+        catch ( NamingException e )
+        {
+            throw new ClassNotFoundException();
+        }
+
+        return defineClass( name, classBytes, 0, classBytes.length );
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/subtree/RefinementEvaluator.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/subtree/RefinementEvaluator.java
new file mode 100644
index 0000000..7fef441
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/subtree/RefinementEvaluator.java
@@ -0,0 +1,122 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.subtree;
+
+
+import org.apache.directory.server.core.entry.ServerAttribute;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.filter.AndNode;
+import org.apache.directory.shared.ldap.filter.BranchNode;
+import org.apache.directory.shared.ldap.filter.ExprNode;
+import org.apache.directory.shared.ldap.filter.NotNode;
+import org.apache.directory.shared.ldap.filter.OrNode;
+import org.apache.directory.shared.ldap.filter.SimpleNode;
+
+import javax.naming.NamingException;
+
+
+/**
+ * The top level evaluation node for a refinement.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class RefinementEvaluator
+{
+    /** Leaf Evaluator flyweight use for leaf filter assertions */
+    private RefinementLeafEvaluator leafEvaluator;
+
+
+    // ------------------------------------------------------------------------
+    // C O N S T R U C T O R S
+    // ------------------------------------------------------------------------
+
+    public RefinementEvaluator(RefinementLeafEvaluator leafEvaluator)
+    {
+        this.leafEvaluator = leafEvaluator;
+    }
+
+
+    public boolean evaluate( ExprNode node, EntryAttribute objectClasses ) throws NamingException
+    {
+        if ( node == null )
+        {
+            throw new IllegalArgumentException( "node cannot be null" );
+        }
+        
+        if ( objectClasses == null )
+        {
+            throw new IllegalArgumentException( "objectClasses cannot be null" );
+        }
+        
+        if ( !((ServerAttribute)objectClasses).instanceOf( SchemaConstants.OBJECT_CLASS_AT ) )
+        {
+            throw new IllegalArgumentException( "Attribute objectClasses should be of id 'objectClass'" );
+        }
+        
+        if ( node.isLeaf() )
+        {
+            return leafEvaluator.evaluate( ( SimpleNode ) node, objectClasses );
+        }
+
+        BranchNode bnode = ( BranchNode ) node;
+
+        if ( node instanceof OrNode )
+        {
+            for ( ExprNode child:bnode.getChildren() )
+            {
+                if ( evaluate( child, objectClasses ) )
+                {
+                    return true;
+                }
+            }
+
+            return false;
+        }
+        else if ( node instanceof AndNode )
+        {
+            for ( ExprNode child:bnode.getChildren() )
+            {
+                if ( !evaluate( child, objectClasses ) )
+                {
+                    return false;
+                }
+            }
+
+            return true;
+            
+        }
+        else if ( node instanceof NotNode )
+        {
+            if ( null != bnode.getFirstChild() )
+            {
+                return !evaluate( bnode.getFirstChild(), objectClasses );
+            }
+
+            throw new IllegalArgumentException( "Negation has no child: " + node );
+            
+        }
+        else
+        {
+            throw new IllegalArgumentException( "Unrecognized branch node operator: " + bnode );
+        }
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/subtree/RefinementLeafEvaluator.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/subtree/RefinementLeafEvaluator.java
new file mode 100644
index 0000000..b44a851
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/subtree/RefinementLeafEvaluator.java
@@ -0,0 +1,137 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.subtree;
+
+
+import org.apache.directory.server.core.entry.ServerAttribute;
+import org.apache.directory.server.schema.registries.OidRegistry;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.entry.client.ClientBinaryValue;
+import org.apache.directory.shared.ldap.entry.client.ClientStringValue;
+import org.apache.directory.shared.ldap.filter.EqualityNode;
+import org.apache.directory.shared.ldap.filter.SimpleNode;
+import org.apache.directory.shared.ldap.util.StringTools;
+
+import javax.naming.NamingException;
+import java.util.Iterator;
+
+
+/**
+ * A refinement leaf node evaluator.  This evaluator checks to see if the
+ * objectClass attribute of a candidate entry is matched by a leaf node in
+ * a refinement filter expression tree.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class RefinementLeafEvaluator
+{
+    /** an OID to name and vice versa registry */
+    private final OidRegistry registry;
+
+
+    /**
+     * Creates a refinement filter's leaf node evaluator.
+     *
+     * @param registry the OID registry used to lookup names for objectClass OIDs
+     */
+    public RefinementLeafEvaluator(OidRegistry registry)
+    {
+        this.registry = registry;
+    }
+
+
+    /**
+     * Evaluates whether or not a simple leaf node of a refinement filter selects an
+     * entry based on the entry's objectClass attribute values.
+     *
+     * @param node the leaf node of the refinement filter
+     * @param objectClasses the objectClass attribute's values
+     * @return true if the leaf node selects the entry based on objectClass values, false
+     * if it rejects the entry
+     * @throws NamingException
+     */
+    public boolean evaluate( SimpleNode node, EntryAttribute objectClasses ) throws NamingException
+    {
+        if ( node == null )
+        {
+            throw new IllegalArgumentException( "node cannot be null" );
+        }
+        
+        if ( !( node instanceof EqualityNode ) )
+        {
+            throw new NamingException( "Unrecognized assertion type for refinement node: " + node );
+        }
+        
+        if ( !node.getAttribute().equalsIgnoreCase( SchemaConstants.OBJECT_CLASS_AT ) )
+        {
+            throw new NamingException( "Refinement leaf node attribute was " + node.getAttribute() );
+        }
+
+        if ( null == objectClasses )
+        {
+            throw new IllegalArgumentException( "objectClasses argument cannot be null" );
+        }
+        
+        if ( !((ServerAttribute)objectClasses).instanceOf( SchemaConstants.OBJECT_CLASS_AT ) )
+        {
+            throw new IllegalArgumentException( "objectClasses attribute must be for ID 'objectClass'" );
+        }
+
+        // check if AVA value exists in attribute
+        if ( objectClasses.contains( node.getValue() ) )
+        {
+            return true;
+        }
+
+        // If the filter value for the objectClass is an OID we need to resolve a name
+        String value = null;
+        if ( node.getValue() instanceof ClientStringValue )
+        {
+            value = ( String ) node.getValue().get();
+        }
+        else if ( node.getValue() instanceof ClientBinaryValue )
+        {
+            value = "#" + StringTools.toHexString( ( byte[] ) node.getValue().get() );
+        }
+        else
+        {
+            value = node.getValue().toString();
+        }
+        
+        if ( Character.isDigit( value.charAt( 0 ) ) )
+        {
+            Iterator<String> list = registry.getNameSet( value ).iterator();
+            
+            while ( list.hasNext() )
+            {
+                String objectClass = ( String ) list.next();
+                if ( objectClasses.contains( objectClass ) )
+                {
+                    return true;
+                }
+            }
+        }
+
+        // no match so return false
+        return false;
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/subtree/Subentry.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/subtree/Subentry.java
new file mode 100644
index 0000000..1d39bf3
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/subtree/Subentry.java
@@ -0,0 +1,89 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.subtree;
+
+
+import org.apache.directory.shared.ldap.subtree.SubtreeSpecification;
+
+
+/**
+ * An operational view of a subentry within the system.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class Subentry
+{
+    static final int COLLECTIVE_SUBENTRY = 1;
+    static final int SCHEMA_SUBENTRY = 2;
+    static final int ACCESS_CONTROL_SUBENTRY = 4;
+    static final int TRIGGER_SUBENTRY = 8;
+    
+    private SubtreeSpecification ss;
+    private int type;
+    
+    
+    final void setSubtreeSpecification( SubtreeSpecification ss )
+    {
+        this.ss = ss;
+    }
+    
+
+    final SubtreeSpecification getSubtreeSpecification()
+    {
+        return ss;
+    }
+
+
+    final void setTypes( int type )
+    {
+        this.type = type;
+    }
+
+
+    final int getTypes()
+    {
+        return type;
+    }
+    
+    
+    final boolean isCollectiveSubentry()
+    {
+        return ( COLLECTIVE_SUBENTRY & type ) == COLLECTIVE_SUBENTRY;
+    }
+    
+    
+    final boolean isSchemaSubentry()
+    {
+        return ( SCHEMA_SUBENTRY & type ) == SCHEMA_SUBENTRY;
+    }
+    
+    
+    final boolean isAccessControlSubentry()
+    {
+        return ( ACCESS_CONTROL_SUBENTRY & type ) == ACCESS_CONTROL_SUBENTRY;
+    }
+    
+    
+    final boolean isTriggerSubentry()
+    {
+        return ( TRIGGER_SUBENTRY & type ) == TRIGGER_SUBENTRY;
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/subtree/SubentryCache.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/subtree/SubentryCache.java
new file mode 100644
index 0000000..22a087d
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/subtree/SubentryCache.java
@@ -0,0 +1,74 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.subtree;
+
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.directory.shared.ldap.subtree.SubtreeSpecification;
+
+
+/**
+ * A cache for subtree specifications.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class SubentryCache
+{
+    private final Map<String, Subentry> name2subentry = new HashMap<String, Subentry>();
+    
+    
+    final Subentry getSubentry( String normalizedName )
+    {
+        return name2subentry.get( normalizedName );
+    }
+    
+    
+    final Subentry removeSubentry( String normalizedName )
+    {
+        return  name2subentry.remove( normalizedName );
+    }
+    
+    
+    final Subentry setSubentry( String normalizedName, SubtreeSpecification ss, int types )
+    {
+        Subentry old = name2subentry.get( normalizedName );
+        Subentry subentry = new Subentry();
+        subentry.setSubtreeSpecification( ss );
+        subentry.setTypes( types );
+        name2subentry.put( normalizedName, subentry );
+        return old;
+    }
+    
+    
+    final boolean hasSubentry( String normalizedName )
+    {
+        return name2subentry.containsKey( normalizedName );
+    }
+    
+    
+    final Iterator<String> nameIterator()
+    {
+        return name2subentry.keySet().iterator();
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/subtree/SubentryInterceptor.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/subtree/SubentryInterceptor.java
new file mode 100644
index 0000000..647d1ef
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/subtree/SubentryInterceptor.java
@@ -0,0 +1,1508 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.subtree;
+
+
+import org.apache.directory.server.core.interceptor.BaseInterceptor;
+
+import org.apache.directory.server.constants.ApacheSchemaConstants;
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.entry.DefaultServerAttribute;
+import org.apache.directory.server.core.entry.DefaultServerEntry;
+import org.apache.directory.server.core.entry.ServerAttribute;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerModification;
+import org.apache.directory.server.core.entry.ServerSearchResult;
+import org.apache.directory.server.core.enumeration.SearchResultFilter;
+import org.apache.directory.server.core.enumeration.SearchResultFilteringEnumeration;
+import org.apache.directory.server.core.interceptor.NextInterceptor;
+import org.apache.directory.server.core.interceptor.context.AddOperationContext;
+import org.apache.directory.server.core.interceptor.context.DeleteOperationContext;
+import org.apache.directory.server.core.interceptor.context.ListOperationContext;
+import org.apache.directory.server.core.interceptor.context.LookupOperationContext;
+import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
+import org.apache.directory.server.core.interceptor.context.MoveAndRenameOperationContext;
+import org.apache.directory.server.core.interceptor.context.MoveOperationContext;
+import org.apache.directory.server.core.interceptor.context.RenameOperationContext;
+import org.apache.directory.server.core.interceptor.context.SearchOperationContext;
+import org.apache.directory.server.core.invocation.Invocation;
+import org.apache.directory.server.core.invocation.InvocationStack;
+import org.apache.directory.server.core.partition.PartitionNexus;
+import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
+import org.apache.directory.server.schema.registries.OidRegistry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.entry.Modification;
+import org.apache.directory.shared.ldap.entry.ModificationOperation;
+import org.apache.directory.shared.ldap.entry.Value;
+import org.apache.directory.shared.ldap.entry.client.ClientStringValue;
+import org.apache.directory.shared.ldap.exception.LdapInvalidAttributeValueException;
+import org.apache.directory.shared.ldap.exception.LdapNoSuchAttributeException;
+import org.apache.directory.shared.ldap.exception.LdapSchemaViolationException;
+import org.apache.directory.shared.ldap.filter.EqualityNode;
+import org.apache.directory.shared.ldap.filter.ExprNode;
+import org.apache.directory.shared.ldap.filter.PresenceNode;
+import org.apache.directory.shared.ldap.message.AliasDerefMode;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.message.SubentriesControl;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.schema.NormalizerMappingResolver;
+import org.apache.directory.shared.ldap.schema.OidNormalizer;
+import org.apache.directory.shared.ldap.subtree.SubtreeSpecification;
+import org.apache.directory.shared.ldap.subtree.SubtreeSpecificationParser;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.naming.Name;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.SearchControls;
+import javax.naming.ldap.Control;
+import javax.naming.ldap.LdapContext;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * The Subentry interceptor service which is responsible for filtering
+ * out subentries on search operations and injecting operational attributes
+ *
+ * @org.apache.xbean.XBean
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class SubentryInterceptor extends BaseInterceptor
+{
+    /** the subentry control OID */
+    private static final String SUBENTRY_CONTROL = SubentriesControl.CONTROL_OID;
+
+    public static final String AC_AREA = "accessControlSpecificArea";
+    public static final String AC_INNERAREA = "accessControlInnerArea";
+
+    public static final String SCHEMA_AREA = "subschemaAdminSpecificArea";
+
+    public static final String COLLECTIVE_AREA = "collectiveAttributeSpecificArea";
+    public static final String COLLECTIVE_INNERAREA = "collectiveAttributeInnerArea";
+
+    public static final String TRIGGER_AREA = "triggerExecutionSpecificArea";
+    public static final String TRIGGER_INNERAREA = "triggerExecutionInnerArea";
+
+    public static final String[] SUBENTRY_OPATTRS =
+        { SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT, SchemaConstants.SUBSCHEMA_SUBENTRY_AT,
+            SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT, SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT };
+
+    private static final Logger LOG = LoggerFactory.getLogger( SubentryInterceptor.class );
+
+    /** the hash mapping the DN of a subentry to its SubtreeSpecification/types */
+    private final SubentryCache subentryCache = new SubentryCache();
+
+    private SubtreeSpecificationParser ssParser;
+    private SubtreeEvaluator evaluator;
+    private PartitionNexus nexus;
+
+    /** The global registries */
+    private Registries registries;
+
+    /** The AttributeType registry */
+    private AttributeTypeRegistry atRegistry;
+
+    /** The OID registry */
+    private OidRegistry oidRegistry;
+
+    private AttributeType objectClassType;
+
+
+    public void init( DirectoryService directoryService ) throws NamingException
+    {
+        super.init( directoryService );
+        nexus = directoryService.getPartitionNexus();
+        registries = directoryService.getRegistries();
+        atRegistry = registries.getAttributeTypeRegistry();
+        oidRegistry = registries.getOidRegistry();
+
+        // setup various attribute type values
+        objectClassType = atRegistry.lookup( oidRegistry.getOid( SchemaConstants.OBJECT_CLASS_AT ) );
+
+        ssParser = new SubtreeSpecificationParser( new NormalizerMappingResolver()
+        {
+            public Map<String, OidNormalizer> getNormalizerMapping() throws NamingException
+            {
+                return atRegistry.getNormalizerMapping();
+            }
+        }, atRegistry.getNormalizerMapping() );
+        evaluator = new SubtreeEvaluator( oidRegistry, atRegistry );
+
+        // prepare to find all subentries in all namingContexts
+        Iterator<String> suffixes = this.nexus.listSuffixes( null );
+        ExprNode filter = new EqualityNode( SchemaConstants.OBJECT_CLASS_AT, new ClientStringValue(
+            SchemaConstants.SUBENTRY_OC ) );
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        controls.setReturningAttributes( new String[]
+            { SchemaConstants.SUBTREE_SPECIFICATION_AT, SchemaConstants.OBJECT_CLASS_AT } );
+
+        // search each namingContext for subentries
+        while ( suffixes.hasNext() )
+        {
+            LdapDN suffix = new LdapDN( suffixes.next() );
+            //suffix = LdapDN.normalize( suffix, registry.getNormalizerMapping() );
+            suffix.normalize( atRegistry.getNormalizerMapping() );
+
+            NamingEnumeration<ServerSearchResult> subentries = nexus.search( new SearchOperationContext( registries,
+                suffix, AliasDerefMode.NEVER_DEREF_ALIASES, filter, controls ) );
+
+            while ( subentries.hasMore() )
+            {
+                ServerSearchResult result = subentries.next();
+                LdapDN dnName = new LdapDN( result.getDn() );
+
+                ServerEntry subentry = result.getServerEntry();
+                String subtree = subentry.get( SchemaConstants.SUBTREE_SPECIFICATION_AT ).getString();
+                SubtreeSpecification ss;
+
+                try
+                {
+                    ss = ssParser.parse( subtree );
+                }
+                catch ( Exception e )
+                {
+                    LOG.warn( "Failed while parsing subtreeSpecification for " + dnName );
+                    continue;
+                }
+
+                dnName.normalize( atRegistry.getNormalizerMapping() );
+                subentryCache.setSubentry( dnName.toString(), ss, getSubentryTypes( subentry ) );
+            }
+        }
+    }
+
+
+    private int getSubentryTypes( ServerEntry subentry ) throws NamingException
+    {
+        int types = 0;
+
+        EntryAttribute oc = subentry.get( SchemaConstants.OBJECT_CLASS_AT );
+
+        if ( oc == null )
+        {
+            throw new LdapSchemaViolationException( "A subentry must have an objectClass attribute",
+                ResultCodeEnum.OBJECT_CLASS_VIOLATION );
+        }
+
+        if ( oc.contains( SchemaConstants.ACCESS_CONTROL_SUBENTRY_OC ) )
+        {
+            types |= Subentry.ACCESS_CONTROL_SUBENTRY;
+        }
+
+        if ( oc.contains( "subschema" ) )
+        {
+            types |= Subentry.SCHEMA_SUBENTRY;
+        }
+
+        if ( oc.contains( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRY_OC ) )
+        {
+            types |= Subentry.COLLECTIVE_SUBENTRY;
+        }
+
+        if ( oc.contains( ApacheSchemaConstants.TRIGGER_EXECUTION_SUBENTRY_OC ) )
+        {
+            types |= Subentry.TRIGGER_SUBENTRY;
+        }
+
+        return types;
+    }
+
+
+    // -----------------------------------------------------------------------
+    // Methods/Code dealing with Subentry Visibility
+    // -----------------------------------------------------------------------
+
+    public NamingEnumeration<ServerSearchResult> list( NextInterceptor nextInterceptor, ListOperationContext opContext )
+        throws NamingException
+    {
+        NamingEnumeration<ServerSearchResult> result = nextInterceptor.list( opContext );
+        Invocation invocation = InvocationStack.getInstance().peek();
+
+        if ( !isSubentryVisible( invocation ) )
+        {
+            return new SearchResultFilteringEnumeration( result, new SearchControls(), invocation,
+                new HideSubentriesFilter(), "List Subentry filter" );
+        }
+
+        return result;
+    }
+
+
+    public NamingEnumeration<ServerSearchResult> search( NextInterceptor nextInterceptor,
+        SearchOperationContext opContext ) throws NamingException
+    {
+        NamingEnumeration<ServerSearchResult> result = nextInterceptor.search( opContext );
+        Invocation invocation = InvocationStack.getInstance().peek();
+        SearchControls searchCtls = opContext.getSearchControls();
+
+        // object scope searches by default return subentries
+        if ( searchCtls.getSearchScope() == SearchControls.OBJECT_SCOPE )
+        {
+            return result;
+        }
+
+        // for subtree and one level scope we filter
+        if ( !isSubentryVisible( invocation ) )
+        {
+            return new SearchResultFilteringEnumeration( result, searchCtls, invocation, new HideSubentriesFilter(),
+                "Search Subentry filter hide subentries" );
+        }
+        else
+        {
+            return new SearchResultFilteringEnumeration( result, searchCtls, invocation, new HideEntriesFilter(),
+                "Search Subentry filter hide entries" );
+        }
+    }
+
+
+    /**
+     * Checks to see if subentries for the search and list operations should be
+     * made visible based on the availability of the search request control
+     *
+     * @param invocation the invocation object to use for determining subentry visibility
+     * @return true if subentries should be visible, false otherwise
+     * @throws NamingException if there are problems accessing request controls
+     */
+    private boolean isSubentryVisible( Invocation invocation ) throws NamingException
+    {
+        Control[] reqControls = ( ( LdapContext ) invocation.getCaller() ).getRequestControls();
+
+        if ( reqControls == null || reqControls.length <= 0 )
+        {
+            return false;
+        }
+
+        // check all request controls to see if subentry control is present
+        for ( Control reqControl : reqControls )
+        {
+            // found the subentry request control so we return its value
+            if ( reqControl.getID().equals( SUBENTRY_CONTROL ) )
+            {
+                SubentriesControl subentriesControl = ( SubentriesControl ) reqControl;
+                return subentriesControl.isVisible();
+            }
+        }
+
+        return false;
+    }
+
+
+    // -----------------------------------------------------------------------
+    // Methods dealing with entry and subentry addition
+    // -----------------------------------------------------------------------
+
+    /**
+     * Evaluates the set of subentry subtrees upon an entry and returns the
+     * operational subentry attributes that will be added to the entry if
+     * added at the dn specified.
+     *
+     * @param dn the normalized distinguished name of the entry
+     * @param entryAttrs the entry attributes are generated for
+     * @return the set of subentry op attrs for an entry
+     * @throws NamingException if there are problems accessing entry information
+     */
+    public ServerEntry getSubentryAttributes( LdapDN dn, ServerEntry entryAttrs ) throws NamingException
+    {
+        ServerEntry subentryAttrs = new DefaultServerEntry( registries, dn );
+        Iterator<String> list = subentryCache.nameIterator();
+
+        while ( list.hasNext() )
+        {
+            String subentryDnStr = list.next();
+            LdapDN subentryDn = new LdapDN( subentryDnStr );
+            LdapDN apDn = ( LdapDN ) subentryDn.clone();
+            apDn.remove( apDn.size() - 1 );
+            Subentry subentry = subentryCache.getSubentry( subentryDnStr );
+            SubtreeSpecification ss = subentry.getSubtreeSpecification();
+
+            if ( evaluator.evaluate( ss, apDn, dn, entryAttrs ) )
+            {
+                EntryAttribute operational;
+
+                if ( subentry.isAccessControlSubentry() )
+                {
+                    operational = subentryAttrs.get( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT );
+
+                    if ( operational == null )
+                    {
+                        operational = new DefaultServerAttribute( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT,
+                            atRegistry.lookup( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT ) );
+                        subentryAttrs.put( operational );
+                    }
+
+                    operational.add( subentryDn.toString() );
+                }
+                if ( subentry.isSchemaSubentry() )
+                {
+                    operational = subentryAttrs.get( SchemaConstants.SUBSCHEMA_SUBENTRY_AT );
+
+                    if ( operational == null )
+                    {
+                        operational = new DefaultServerAttribute( SchemaConstants.SUBSCHEMA_SUBENTRY_AT, atRegistry
+                            .lookup( SchemaConstants.SUBSCHEMA_SUBENTRY_AT ) );
+                        subentryAttrs.put( operational );
+                    }
+
+                    operational.add( subentryDn.toString() );
+                }
+                if ( subentry.isCollectiveSubentry() )
+                {
+                    operational = subentryAttrs.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT );
+
+                    if ( operational == null )
+                    {
+                        operational = new DefaultServerAttribute( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT,
+                            atRegistry.lookup( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
+                        subentryAttrs.put( operational );
+                    }
+
+                    operational.add( subentryDn.toString() );
+                }
+                if ( subentry.isTriggerSubentry() )
+                {
+                    operational = subentryAttrs.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT );
+
+                    if ( operational == null )
+                    {
+                        operational = new DefaultServerAttribute( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT,
+                            atRegistry.lookup( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ) );
+                        subentryAttrs.put( operational );
+                    }
+
+                    operational.add( subentryDn.toString() );
+                }
+            }
+        }
+
+        return subentryAttrs;
+    }
+
+
+    public void add( NextInterceptor next, AddOperationContext addContext ) throws NamingException
+    {
+        LdapDN name = addContext.getDn();
+        ServerEntry entry = addContext.getEntry();
+
+        EntryAttribute objectClasses = entry.get( SchemaConstants.OBJECT_CLASS_AT );
+
+        if ( objectClasses.contains( SchemaConstants.SUBENTRY_OC ) )
+        {
+            // get the name of the administrative point and its administrativeRole attributes
+            LdapDN apName = ( LdapDN ) name.clone();
+            apName.remove( name.size() - 1 );
+            ServerEntry ap = nexus.lookup( new LookupOperationContext( registries, apName ) );
+            EntryAttribute administrativeRole = ap.get( "administrativeRole" );
+
+            // check that administrativeRole has something valid in it for us
+            if ( administrativeRole == null || administrativeRole.size() <= 0 )
+            {
+                throw new LdapNoSuchAttributeException( "Administration point " + apName
+                    + " does not contain an administrativeRole attribute! An"
+                    + " administrativeRole attribute in the administrative point is"
+                    + " required to add a subordinate subentry." );
+            }
+
+            /* ----------------------------------------------------------------
+             * Build the set of operational attributes to be injected into
+             * entries that are contained within the subtree repesented by this
+             * new subentry.  In the process we make sure the proper roles are
+             * supported by the administrative point to allow the addition of
+             * this new subentry.
+             * ----------------------------------------------------------------
+             */
+            Subentry subentry = new Subentry();
+            subentry.setTypes( getSubentryTypes( entry ) );
+            ServerEntry operational = getSubentryOperatationalAttributes( name, subentry );
+
+            /* ----------------------------------------------------------------
+             * Parse the subtreeSpecification of the subentry and add it to the
+             * SubtreeSpecification cache.  If the parse succeeds we continue
+             * to add the entry to the DIT.  Thereafter we search out entries
+             * to modify the subentry operational attributes of.
+             * ----------------------------------------------------------------
+             */
+            String subtree = entry.get( SchemaConstants.SUBTREE_SPECIFICATION_AT ).getString();
+            SubtreeSpecification ss;
+
+            try
+            {
+                ss = ssParser.parse( subtree );
+            }
+            catch ( Exception e )
+            {
+                String msg = "Failed while parsing subtreeSpecification for " + name.getUpName();
+                LOG.warn( msg );
+                throw new LdapInvalidAttributeValueException( msg, ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX );
+            }
+
+            subentryCache.setSubentry( name.getNormName(), ss, getSubentryTypes( entry ) );
+
+            next.add( addContext );
+
+            /* ----------------------------------------------------------------
+             * Find the baseDn for the subentry and use that to search the tree
+             * while testing each entry returned for inclusion within the
+             * subtree of the subentry's subtreeSpecification.  All included
+             * entries will have their operational attributes merged with the
+             * operational attributes calculated above.
+             * ----------------------------------------------------------------
+             */
+            LdapDN baseDn = ( LdapDN ) apName.clone();
+            baseDn.addAll( ss.getBase() );
+
+            ExprNode filter = new PresenceNode( SchemaConstants.OBJECT_CLASS_AT_OID ); // (objectClass=*)
+            SearchControls controls = new SearchControls();
+            controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+            controls.setReturningAttributes( new String[]
+                { SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES, SchemaConstants.ALL_USER_ATTRIBUTES } );
+
+            NamingEnumeration<ServerSearchResult> subentries = nexus.search( new SearchOperationContext( registries,
+                baseDn, AliasDerefMode.NEVER_DEREF_ALIASES, filter, controls ) );
+
+            while ( subentries.hasMore() )
+            {
+                ServerSearchResult result = subentries.next();
+                LdapDN dn = result.getDn();
+                dn.normalize( atRegistry.getNormalizerMapping() );
+                ServerEntry candidate = result.getServerEntry();
+
+                if ( evaluator.evaluate( ss, apName, dn, candidate ) )
+                {
+                    nexus.modify( new ModifyOperationContext( registries, dn, getOperationalModsForAdd( candidate,
+                        operational ) ) );
+                }
+            }
+
+            addContext.setEntry( entry );
+        }
+        else
+        {
+            Iterator<String> list = subentryCache.nameIterator();
+
+            while ( list.hasNext() )
+            {
+                String subentryDnStr = list.next();
+                LdapDN subentryDn = new LdapDN( subentryDnStr );
+                LdapDN apDn = ( LdapDN ) subentryDn.clone();
+                apDn.remove( apDn.size() - 1 );
+                Subentry subentry = subentryCache.getSubentry( subentryDnStr );
+                SubtreeSpecification ss = subentry.getSubtreeSpecification();
+
+                if ( evaluator.evaluate( ss, apDn, name, entry ) )
+                {
+                    EntryAttribute operational;
+
+                    if ( subentry.isAccessControlSubentry() )
+                    {
+                        operational = entry.get( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT );
+
+                        if ( operational == null )
+                        {
+                            operational = new DefaultServerAttribute( atRegistry
+                                .lookup( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT ) );
+                            entry.put( operational );
+                        }
+
+                        operational.add( subentryDn.toString() );
+                    }
+
+                    if ( subentry.isSchemaSubentry() )
+                    {
+                        operational = entry.get( SchemaConstants.SUBSCHEMA_SUBENTRY_AT );
+
+                        if ( operational == null )
+                        {
+                            operational = new DefaultServerAttribute( atRegistry
+                                .lookup( SchemaConstants.SUBSCHEMA_SUBENTRY_AT ) );
+                            entry.put( operational );
+                        }
+
+                        operational.add( subentryDn.toString() );
+                    }
+
+                    if ( subentry.isCollectiveSubentry() )
+                    {
+                        operational = entry.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT );
+
+                        if ( operational == null )
+                        {
+                            operational = new DefaultServerAttribute( atRegistry
+                                .lookup( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
+                            entry.put( operational );
+                        }
+
+                        operational.add( subentryDn.toString() );
+                    }
+
+                    if ( subentry.isTriggerSubentry() )
+                    {
+                        operational = entry.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT );
+
+                        if ( operational == null )
+                        {
+                            operational = new DefaultServerAttribute( atRegistry
+                                .lookup( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ) );
+                            entry.put( operational );
+                        }
+
+                        operational.add( subentryDn.toString() );
+                    }
+                }
+            }
+
+            addContext.setEntry( entry );
+
+            next.add( addContext );
+        }
+    }
+
+
+    // -----------------------------------------------------------------------
+    // Methods dealing subentry deletion
+    // -----------------------------------------------------------------------
+
+    public void delete( NextInterceptor next, DeleteOperationContext opContext ) throws NamingException
+    {
+        LdapDN name = opContext.getDn();
+        ServerEntry entry = nexus.lookup( new LookupOperationContext( registries, name ) );
+        EntryAttribute objectClasses = entry.get( objectClassType );
+
+        if ( objectClasses.contains( SchemaConstants.SUBENTRY_OC ) )
+        {
+            SubtreeSpecification ss = subentryCache.removeSubentry( name.toNormName() ).getSubtreeSpecification();
+            next.delete( opContext );
+
+            /* ----------------------------------------------------------------
+             * Find the baseDn for the subentry and use that to search the tree
+             * for all entries included by the subtreeSpecification.  Then we
+             * check the entry for subentry operational attribute that contain
+             * the DN of the subentry.  These are the subentry operational
+             * attributes we remove from the entry in a modify operation.
+             * ----------------------------------------------------------------
+             */
+            LdapDN apName = ( LdapDN ) name.clone();
+            apName.remove( name.size() - 1 );
+            LdapDN baseDn = ( LdapDN ) apName.clone();
+            baseDn.addAll( ss.getBase() );
+
+            ExprNode filter = new PresenceNode( oidRegistry.getOid( SchemaConstants.OBJECT_CLASS_AT ) );
+            SearchControls controls = new SearchControls();
+            controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+            controls.setReturningAttributes( new String[]
+                { SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES, SchemaConstants.ALL_USER_ATTRIBUTES } );
+
+            NamingEnumeration<ServerSearchResult> subentries = nexus.search( new SearchOperationContext( registries,
+                baseDn, AliasDerefMode.NEVER_DEREF_ALIASES, filter, controls ) );
+
+            while ( subentries.hasMore() )
+            {
+                ServerSearchResult result = subentries.next();
+                LdapDN dn = new LdapDN( result.getDn() );
+                dn.normalize( atRegistry.getNormalizerMapping() );
+                ServerEntry candidate = result.getServerEntry();
+
+                if ( evaluator.evaluate( ss, apName, dn, candidate ) )
+                {
+                    nexus.modify( new ModifyOperationContext( registries, dn, getOperationalModsForRemove( name,
+                        candidate ) ) );
+                }
+            }
+        }
+        else
+        {
+            next.delete( opContext );
+        }
+    }
+
+
+    // -----------------------------------------------------------------------
+    // Methods dealing subentry name changes
+    // -----------------------------------------------------------------------
+
+    /**
+     * Checks to see if an entry being renamed has a descendant that is an
+     * administrative point.
+     *
+     * @param name the name of the entry which is used as the search base
+     * @return true if name is an administrative point or one of its descendants
+     * are, false otherwise
+     * @throws NamingException if there are errors while searching the directory
+     */
+    private boolean hasAdministrativeDescendant( LdapDN name ) throws NamingException
+    {
+        ExprNode filter = new PresenceNode( "administrativeRole" );
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        NamingEnumeration<ServerSearchResult> aps = nexus.search( new SearchOperationContext( registries, name,
+            AliasDerefMode.NEVER_DEREF_ALIASES, filter, controls ) );
+
+        if ( aps.hasMore() )
+        {
+            aps.close();
+            return true;
+        }
+
+        return false;
+    }
+
+
+    private List<Modification> getModsOnEntryRdnChange( Name oldName, Name newName, ServerEntry entry )
+        throws NamingException
+    {
+        List<Modification> modList = new ArrayList<Modification>();
+
+        /*
+         * There are two different situations warranting action.  Firt if
+         * an ss evalutating to true with the old name no longer evalutates
+         * to true with the new name.  This would be caused by specific chop
+         * exclusions that effect the new name but did not effect the old
+         * name. In this case we must remove subentry operational attribute
+         * values associated with the dn of that subentry.
+         *
+         * In the second case an ss selects the entry with the new name when
+         * it did not previously with the old name.  Again this situation
+         * would be caused by chop exclusions. In this case we must add subentry
+         * operational attribute values with the dn of this subentry.
+         */
+        Iterator<String> subentries = subentryCache.nameIterator();
+
+        while ( subentries.hasNext() )
+        {
+            String subentryDn = subentries.next();
+            Name apDn = new LdapDN( subentryDn );
+            apDn.remove( apDn.size() - 1 );
+            SubtreeSpecification ss = subentryCache.getSubentry( subentryDn ).getSubtreeSpecification();
+            boolean isOldNameSelected = evaluator.evaluate( ss, apDn, oldName, entry );
+            boolean isNewNameSelected = evaluator.evaluate( ss, apDn, newName, entry );
+
+            if ( isOldNameSelected == isNewNameSelected )
+            {
+                continue;
+            }
+
+            // need to remove references to the subentry
+            if ( isOldNameSelected && !isNewNameSelected )
+            {
+                for ( String aSUBENTRY_OPATTRS : SUBENTRY_OPATTRS )
+                {
+                    ModificationOperation op = ModificationOperation.REPLACE_ATTRIBUTE;
+                    EntryAttribute opAttr = entry.get( aSUBENTRY_OPATTRS );
+
+                    if ( opAttr != null )
+                    {
+                        opAttr = ( ServerAttribute ) opAttr.clone();
+                        opAttr.remove( subentryDn );
+
+                        if ( opAttr.size() < 1 )
+                        {
+                            op = ModificationOperation.REMOVE_ATTRIBUTE;
+                        }
+
+                        modList.add( new ServerModification( op, opAttr ) );
+                    }
+                }
+            }
+            // need to add references to the subentry
+            else if ( isNewNameSelected && !isOldNameSelected )
+            {
+                for ( String aSUBENTRY_OPATTRS : SUBENTRY_OPATTRS )
+                {
+                    ModificationOperation op = ModificationOperation.ADD_ATTRIBUTE;
+                    ServerAttribute opAttr = new DefaultServerAttribute( aSUBENTRY_OPATTRS, atRegistry
+                        .lookup( aSUBENTRY_OPATTRS ) );
+                    opAttr.add( subentryDn );
+                    modList.add( new ServerModification( op, opAttr ) );
+                }
+            }
+        }
+
+        return modList;
+    }
+
+
+    public void rename( NextInterceptor next, RenameOperationContext opContext ) throws NamingException
+    {
+        LdapDN name = opContext.getDn();
+
+        ServerEntry entry = nexus.lookup( new LookupOperationContext( registries, name ) );
+
+        EntryAttribute objectClasses = entry.get( objectClassType );
+
+        if ( objectClasses.contains( SchemaConstants.SUBENTRY_OC ) )
+        {
+            Subentry subentry = subentryCache.getSubentry( name.toNormName() );
+            SubtreeSpecification ss = subentry.getSubtreeSpecification();
+            LdapDN apName = ( LdapDN ) name.clone();
+            apName.remove( apName.size() - 1 );
+            LdapDN baseDn = ( LdapDN ) apName.clone();
+            baseDn.addAll( ss.getBase() );
+            LdapDN newName = ( LdapDN ) name.clone();
+            newName.remove( newName.size() - 1 );
+
+            newName.add( opContext.getNewRdn() );
+
+            String newNormName = newName.toNormName();
+            subentryCache.setSubentry( newNormName, ss, subentry.getTypes() );
+            next.rename( opContext );
+
+            subentry = subentryCache.getSubentry( newNormName );
+            ExprNode filter = new PresenceNode( oidRegistry.getOid( SchemaConstants.OBJECT_CLASS_AT ) );
+            SearchControls controls = new SearchControls();
+            controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+            controls.setReturningAttributes( new String[]
+                { SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES, SchemaConstants.ALL_USER_ATTRIBUTES } );
+            NamingEnumeration<ServerSearchResult> subentries = nexus.search( new SearchOperationContext( registries,
+                baseDn, AliasDerefMode.NEVER_DEREF_ALIASES, filter, controls ) );
+
+            while ( subentries.hasMore() )
+            {
+                ServerSearchResult result = subentries.next();
+                LdapDN dn = result.getDn();
+                dn.normalize( atRegistry.getNormalizerMapping() );
+
+                ServerEntry candidate = result.getServerEntry();
+
+                if ( evaluator.evaluate( ss, apName, dn, candidate ) )
+                {
+                    nexus.modify( new ModifyOperationContext( registries, dn, getOperationalModsForReplace( name,
+                        newName, subentry, candidate ) ) );
+                }
+            }
+        }
+        else
+        {
+            if ( hasAdministrativeDescendant( name ) )
+            {
+                String msg = "Will not allow rename operation on entries with administrative descendants.";
+                LOG.warn( msg );
+                throw new LdapSchemaViolationException( msg, ResultCodeEnum.NOT_ALLOWED_ON_RDN );
+            }
+
+            next.rename( opContext );
+
+            // calculate the new DN now for use below to modify subentry operational
+            // attributes contained within this regular entry with name changes
+            LdapDN newName = ( LdapDN ) name.clone();
+            newName.remove( newName.size() - 1 );
+            newName.add( opContext.getNewRdn() );
+            newName.normalize( atRegistry.getNormalizerMapping() );
+            List<Modification> mods = getModsOnEntryRdnChange( name, newName, entry );
+
+            if ( mods.size() > 0 )
+            {
+                nexus.modify( new ModifyOperationContext( registries, newName, mods ) );
+            }
+        }
+    }
+
+
+    public void moveAndRename( NextInterceptor next, MoveAndRenameOperationContext opContext ) throws NamingException
+    {
+        LdapDN oriChildName = opContext.getDn();
+        LdapDN parent = opContext.getParent();
+
+        ServerEntry entry = nexus.lookup( new LookupOperationContext( registries, oriChildName ) );
+
+        EntryAttribute objectClasses = entry.get( objectClassType );
+
+        if ( objectClasses.contains( SchemaConstants.SUBENTRY_OC ) )
+        {
+            Subentry subentry = subentryCache.getSubentry( oriChildName.toNormName() );
+            SubtreeSpecification ss = subentry.getSubtreeSpecification();
+            LdapDN apName = ( LdapDN ) oriChildName.clone();
+            apName.remove( apName.size() - 1 );
+            LdapDN baseDn = ( LdapDN ) apName.clone();
+            baseDn.addAll( ss.getBase() );
+            LdapDN newName = ( LdapDN ) parent.clone();
+            newName.remove( newName.size() - 1 );
+
+            newName.add( opContext.getNewRdn() );
+
+            String newNormName = newName.toNormName();
+            subentryCache.setSubentry( newNormName, ss, subentry.getTypes() );
+            next.moveAndRename( opContext );
+
+            subentry = subentryCache.getSubentry( newNormName );
+
+            ExprNode filter = new PresenceNode( oidRegistry.getOid( SchemaConstants.OBJECT_CLASS_AT ) );
+            SearchControls controls = new SearchControls();
+            controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+            controls.setReturningAttributes( new String[]
+                { SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES, SchemaConstants.ALL_USER_ATTRIBUTES } );
+            NamingEnumeration<ServerSearchResult> subentries = nexus.search( new SearchOperationContext( registries,
+                baseDn, AliasDerefMode.NEVER_DEREF_ALIASES, filter, controls ) );
+
+            while ( subentries.hasMore() )
+            {
+                ServerSearchResult result = subentries.next();
+                LdapDN dn = result.getDn();
+                dn.normalize( atRegistry.getNormalizerMapping() );
+                ServerEntry candidate = result.getServerEntry();
+
+                if ( evaluator.evaluate( ss, apName, dn, candidate ) )
+                {
+                    nexus.modify( new ModifyOperationContext( registries, dn, getOperationalModsForReplace(
+                        oriChildName, newName, subentry, candidate ) ) );
+                }
+            }
+        }
+        else
+        {
+            if ( hasAdministrativeDescendant( oriChildName ) )
+            {
+                String msg = "Will not allow rename operation on entries with administrative descendants.";
+                LOG.warn( msg );
+                throw new LdapSchemaViolationException( msg, ResultCodeEnum.NOT_ALLOWED_ON_RDN );
+            }
+
+            next.moveAndRename( opContext );
+
+            // calculate the new DN now for use below to modify subentry operational
+            // attributes contained within this regular entry with name changes
+            LdapDN newName = ( LdapDN ) parent.clone();
+            newName.add( opContext.getNewRdn() );
+            newName.normalize( atRegistry.getNormalizerMapping() );
+            List<Modification> mods = getModsOnEntryRdnChange( oriChildName, newName, entry );
+
+            if ( mods.size() > 0 )
+            {
+                nexus.modify( new ModifyOperationContext( registries, newName, mods ) );
+            }
+        }
+    }
+
+
+    public void move( NextInterceptor next, MoveOperationContext opContext ) throws NamingException
+    {
+        LdapDN oriChildName = opContext.getDn();
+        LdapDN newParentName = opContext.getParent();
+
+        ServerEntry entry = nexus.lookup( new LookupOperationContext( registries, oriChildName ) );
+
+        EntryAttribute objectClasses = entry.get( SchemaConstants.OBJECT_CLASS_AT );
+
+        if ( objectClasses.contains( SchemaConstants.SUBENTRY_OC ) )
+        {
+            Subentry subentry = subentryCache.getSubentry( oriChildName.toString() );
+            SubtreeSpecification ss = subentry.getSubtreeSpecification();
+            LdapDN apName = ( LdapDN ) oriChildName.clone();
+            apName.remove( apName.size() - 1 );
+            LdapDN baseDn = ( LdapDN ) apName.clone();
+            baseDn.addAll( ss.getBase() );
+            LdapDN newName = ( LdapDN ) newParentName.clone();
+            newName.remove( newName.size() - 1 );
+            newName.add( newParentName.get( newParentName.size() - 1 ) );
+
+            String newNormName = newName.toNormName();
+            subentryCache.setSubentry( newNormName, ss, subentry.getTypes() );
+            next.move( opContext );
+
+            subentry = subentryCache.getSubentry( newNormName );
+
+            ExprNode filter = new PresenceNode( SchemaConstants.OBJECT_CLASS_AT );
+            SearchControls controls = new SearchControls();
+            controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+            controls.setReturningAttributes( new String[]
+                { SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES, SchemaConstants.ALL_USER_ATTRIBUTES } );
+            NamingEnumeration<ServerSearchResult> subentries = nexus.search( new SearchOperationContext( registries,
+                baseDn, AliasDerefMode.NEVER_DEREF_ALIASES, filter, controls ) );
+
+            while ( subentries.hasMore() )
+            {
+                ServerSearchResult result = subentries.next();
+                LdapDN dn = result.getDn();
+                dn.normalize( atRegistry.getNormalizerMapping() );
+                ServerEntry candidate = result.getServerEntry();
+
+                if ( evaluator.evaluate( ss, apName, dn, candidate ) )
+                {
+                    nexus.modify( new ModifyOperationContext( registries, dn, getOperationalModsForReplace(
+                        oriChildName, newName, subentry, candidate ) ) );
+                }
+            }
+        }
+        else
+        {
+            if ( hasAdministrativeDescendant( oriChildName ) )
+            {
+                String msg = "Will not allow rename operation on entries with administrative descendants.";
+                LOG.warn( msg );
+                throw new LdapSchemaViolationException( msg, ResultCodeEnum.NOT_ALLOWED_ON_RDN );
+            }
+
+            next.move( opContext );
+
+            // calculate the new DN now for use below to modify subentry operational
+            // attributes contained within this regular entry with name changes
+            LdapDN newName = ( LdapDN ) newParentName.clone();
+            newName.add( oriChildName.get( oriChildName.size() - 1 ) );
+            List<Modification> mods = getModsOnEntryRdnChange( oriChildName, newName, entry );
+
+            if ( mods.size() > 0 )
+            {
+                nexus.modify( new ModifyOperationContext( registries, newName, mods ) );
+            }
+        }
+    }
+
+
+    // -----------------------------------------------------------------------
+    // Methods dealing subentry modification
+    // -----------------------------------------------------------------------
+
+    private int getSubentryTypes( ServerEntry entry, List<Modification> mods ) throws NamingException
+    {
+        ServerAttribute ocFinalState = ( ServerAttribute ) entry.get( SchemaConstants.OBJECT_CLASS_AT ).clone();
+
+        for ( Modification mod : mods )
+        {
+            if ( mod.getAttribute().getId().equalsIgnoreCase( SchemaConstants.OBJECT_CLASS_AT ) )
+            {
+                switch ( mod.getOperation() )
+                {
+                    case ADD_ATTRIBUTE:
+                        for ( Value<?> value : ( ServerAttribute ) mod.getAttribute() )
+                        {
+                            ocFinalState.add( ( String ) value.get() );
+                        }
+
+                        break;
+
+                    case REMOVE_ATTRIBUTE:
+                        for ( Value<?> value : ( ServerAttribute ) mod.getAttribute() )
+                        {
+                            ocFinalState.remove( ( String ) value.get() );
+                        }
+
+                        break;
+
+                    case REPLACE_ATTRIBUTE:
+                        ocFinalState = ( ServerAttribute ) mod.getAttribute();
+                        break;
+                }
+            }
+        }
+
+        ServerEntry attrs = new DefaultServerEntry( registries, LdapDN.EMPTY_LDAPDN );
+        attrs.put( ocFinalState );
+        return getSubentryTypes( attrs );
+    }
+
+
+    public void modify( NextInterceptor next, ModifyOperationContext opContext ) throws NamingException
+    {
+        LdapDN name = opContext.getDn();
+        List<Modification> mods = opContext.getModItems();
+
+        ServerEntry entry = nexus.lookup( new LookupOperationContext( registries, name ) );
+
+        ServerEntry oldEntry = ( ServerEntry ) entry.clone();
+        EntryAttribute objectClasses = entry.get( objectClassType );
+        boolean isSubtreeSpecificationModification = false;
+        Modification subtreeMod = null;
+
+        for ( Modification mod : mods )
+        {
+            if ( SchemaConstants.SUBTREE_SPECIFICATION_AT.equalsIgnoreCase( mod.getAttribute().getId() ) )
+            {
+                isSubtreeSpecificationModification = true;
+                subtreeMod = mod;
+            }
+        }
+
+        if ( objectClasses.contains( SchemaConstants.SUBENTRY_OC ) && isSubtreeSpecificationModification )
+        {
+            SubtreeSpecification ssOld = subentryCache.removeSubentry( name.toString() ).getSubtreeSpecification();
+            SubtreeSpecification ssNew;
+
+            try
+            {
+                ssNew = ssParser.parse( ( ( ServerAttribute ) subtreeMod.getAttribute() ).getString() );
+            }
+            catch ( Exception e )
+            {
+                String msg = "failed to parse the new subtreeSpecification";
+                LOG.error( msg, e );
+                throw new LdapInvalidAttributeValueException( msg, ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX );
+            }
+
+            subentryCache.setSubentry( name.toNormName(), ssNew, getSubentryTypes( entry, mods ) );
+            next.modify( opContext );
+
+            // search for all entries selected by the old SS and remove references to subentry
+            LdapDN apName = ( LdapDN ) name.clone();
+            apName.remove( apName.size() - 1 );
+            LdapDN oldBaseDn = ( LdapDN ) apName.clone();
+            oldBaseDn.addAll( ssOld.getBase() );
+            ExprNode filter = new PresenceNode( oidRegistry.getOid( SchemaConstants.OBJECT_CLASS_AT ) );
+            SearchControls controls = new SearchControls();
+            controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+            controls.setReturningAttributes( new String[]
+                { SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES, SchemaConstants.ALL_USER_ATTRIBUTES } );
+            NamingEnumeration<ServerSearchResult> subentries = nexus.search( new SearchOperationContext( registries,
+                oldBaseDn, AliasDerefMode.NEVER_DEREF_ALIASES, filter, controls ) );
+
+            while ( subentries.hasMore() )
+            {
+                ServerSearchResult result = subentries.next();
+                LdapDN dn = result.getDn();
+                dn.normalize( atRegistry.getNormalizerMapping() );
+                ServerEntry candidate = result.getServerEntry();
+
+                if ( evaluator.evaluate( ssOld, apName, dn, candidate ) )
+                {
+                    nexus.modify( new ModifyOperationContext( registries, dn, getOperationalModsForRemove( name,
+                        candidate ) ) );
+                }
+            }
+
+            // search for all selected entries by the new SS and add references to subentry
+            Subentry subentry = subentryCache.getSubentry( name.toNormName() );
+            ServerEntry operational = getSubentryOperatationalAttributes( name, subentry );
+            LdapDN newBaseDn = ( LdapDN ) apName.clone();
+            newBaseDn.addAll( ssNew.getBase() );
+            subentries = nexus.search( new SearchOperationContext( registries, newBaseDn,
+                AliasDerefMode.NEVER_DEREF_ALIASES, filter, controls ) );
+            while ( subentries.hasMore() )
+            {
+                ServerSearchResult result = subentries.next();
+                LdapDN dn = result.getDn();
+                dn.normalize( atRegistry.getNormalizerMapping() );
+                ServerEntry candidate = result.getServerEntry();
+
+                if ( evaluator.evaluate( ssNew, apName, dn, candidate ) )
+                {
+                    nexus.modify( new ModifyOperationContext( registries, dn, getOperationalModsForAdd( candidate,
+                        operational ) ) );
+                }
+            }
+        }
+        else
+        {
+            next.modify( opContext );
+
+            if ( !objectClasses.contains( SchemaConstants.SUBENTRY_OC ) )
+            {
+                ServerEntry newEntry = nexus.lookup( new LookupOperationContext( registries, name ) );
+
+                List<Modification> subentriesOpAttrMods = getModsOnEntryModification( name, oldEntry, newEntry );
+
+                if ( subentriesOpAttrMods.size() > 0 )
+                {
+                    nexus.modify( new ModifyOperationContext( registries, name, subentriesOpAttrMods ) );
+                }
+            }
+        }
+    }
+
+
+    // -----------------------------------------------------------------------
+    // Utility Methods
+    // -----------------------------------------------------------------------
+
+    private List<Modification> getOperationalModsForReplace( Name oldName, Name newName, Subentry subentry,
+        ServerEntry entry ) throws NamingException
+    {
+        List<Modification> modList = new ArrayList<Modification>();
+
+        ServerAttribute operational;
+
+        if ( subentry.isAccessControlSubentry() )
+        {
+            operational = ( ServerAttribute ) entry.get( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT ).clone();
+
+            if ( operational == null )
+            {
+                operational = new DefaultServerAttribute( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT, atRegistry
+                    .lookup( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT ) );
+                operational.add( newName.toString() );
+            }
+            else
+            {
+                operational.remove( oldName.toString() );
+                operational.add( newName.toString() );
+            }
+
+            modList.add( new ServerModification( ModificationOperation.REPLACE_ATTRIBUTE, operational ) );
+        }
+
+        if ( subentry.isSchemaSubentry() )
+        {
+            operational = ( ServerAttribute ) entry.get( SchemaConstants.SUBSCHEMA_SUBENTRY_AT ).clone();
+
+            if ( operational == null )
+            {
+                operational = new DefaultServerAttribute( SchemaConstants.SUBSCHEMA_SUBENTRY_AT, atRegistry
+                    .lookup( SchemaConstants.SUBSCHEMA_SUBENTRY_AT ) );
+                operational.add( newName.toString() );
+            }
+            else
+            {
+                operational.remove( oldName.toString() );
+                operational.add( newName.toString() );
+            }
+
+            modList.add( new ServerModification( ModificationOperation.REPLACE_ATTRIBUTE, operational ) );
+        }
+
+        if ( subentry.isCollectiveSubentry() )
+        {
+            operational = ( ServerAttribute ) entry.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ).clone();
+
+            if ( operational == null )
+            {
+                operational = new DefaultServerAttribute( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT,
+                    atRegistry.lookup( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
+                operational.add( newName.toString() );
+            }
+            else
+            {
+                operational.remove( oldName.toString() );
+                operational.add( newName.toString() );
+            }
+
+            modList.add( new ServerModification( ModificationOperation.REPLACE_ATTRIBUTE, operational ) );
+        }
+
+        if ( subentry.isTriggerSubentry() )
+        {
+            operational = ( ServerAttribute ) entry.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ).clone();
+
+            if ( operational == null )
+            {
+                operational = new DefaultServerAttribute( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT, atRegistry
+                    .lookup( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ) );
+                operational.add( newName.toString() );
+            }
+            else
+            {
+                operational.remove( oldName.toString() );
+                operational.add( newName.toString() );
+            }
+
+            modList.add( new ServerModification( ModificationOperation.REPLACE_ATTRIBUTE, operational ) );
+        }
+
+        return modList;
+    }
+
+
+    /**
+     * Gets the subschema operational attributes to be added to or removed from
+     * an entry selected by a subentry's subtreeSpecification.
+     *
+     * @param name the normalized distinguished name of the subentry (the value of op attrs)
+     * @param subentry the subentry to get attributes from
+     * @return the set of attributes to be added or removed from entries
+     */
+    private ServerEntry getSubentryOperatationalAttributes( LdapDN name, Subentry subentry ) throws NamingException
+    {
+        ServerEntry operational = new DefaultServerEntry( registries, name );
+
+        if ( subentry.isAccessControlSubentry() )
+        {
+            if ( operational.get( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT ) == null )
+            {
+                operational.put( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT, name.toString() );
+            }
+            else
+            {
+                operational.get( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT ).add( name.toString() );
+            }
+        }
+        if ( subentry.isSchemaSubentry() )
+        {
+            if ( operational.get( SchemaConstants.SUBSCHEMA_SUBENTRY_AT ) == null )
+            {
+                operational.put( SchemaConstants.SUBSCHEMA_SUBENTRY_AT, name.toString() );
+            }
+            else
+            {
+                operational.get( SchemaConstants.SUBSCHEMA_SUBENTRY_AT ).add( name.toString() );
+            }
+        }
+        if ( subentry.isCollectiveSubentry() )
+        {
+            if ( operational.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) == null )
+            {
+                operational.put( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT, name.toString() );
+            }
+            else
+            {
+                operational.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ).add( name.toString() );
+            }
+        }
+        if ( subentry.isTriggerSubentry() )
+        {
+            if ( operational.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ) == null )
+            {
+                operational.put( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT, name.toString() );
+            }
+            else
+            {
+                operational.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ).add( name.toString() );
+            }
+        }
+
+        return operational;
+    }
+
+
+    /**
+     * Calculates the subentry operational attributes to remove from a candidate
+     * entry selected by a subtreeSpecification.  When we remove a subentry we
+     * must remove the operational attributes in the entries that were once selected
+     * by the subtree specification of that subentry.  To do so we must perform
+     * a modify operation with the set of modifications to perform.  This method
+     * calculates those modifications.
+     *
+     * @param subentryDn the distinguished name of the subentry
+     * @param candidate the candidate entry to removed from the
+     * @return the set of modifications required to remove an entry's reference to
+     * a subentry
+     */
+    private List<Modification> getOperationalModsForRemove( LdapDN subentryDn, ServerEntry candidate )
+        throws NamingException
+    {
+        List<Modification> modList = new ArrayList<Modification>();
+        String dn = subentryDn.toNormName();
+
+        for ( String opAttrId : SUBENTRY_OPATTRS )
+        {
+            EntryAttribute opAttr = candidate.get( opAttrId );
+
+            if ( ( opAttr != null ) && opAttr.contains( dn ) )
+            {
+                AttributeType attributeType = atRegistry.lookup( opAttrId );
+                ServerAttribute attr = new DefaultServerAttribute( opAttrId, attributeType, dn );
+                modList.add( new ServerModification( ModificationOperation.REMOVE_ATTRIBUTE, attr ) );
+            }
+        }
+
+        return modList;
+    }
+
+
+    /**
+     * Calculates the subentry operational attributes to add or replace from
+     * a candidate entry selected by a subtree specification.  When a subentry
+     * is added or it's specification is modified some entries must have new
+     * operational attributes added to it to point back to the associated
+     * subentry.  To do so a modify operation must be performed on entries
+     * selected by the subtree specification.  This method calculates the
+     * modify operation to be performed on the entry.
+     *
+     * @param entry the entry being modified
+     * @param operational the set of operational attributes supported by the AP
+     * of the subentry
+     * @return the set of modifications needed to update the entry
+     * @throws NamingException if there are probelms accessing modification items
+     */
+    public List<Modification> getOperationalModsForAdd( ServerEntry entry, ServerEntry operational )
+        throws NamingException
+    {
+        List<Modification> modList = new ArrayList<Modification>();
+
+        for ( AttributeType attributeType : operational.getAttributeTypes() )
+        {
+            ModificationOperation op = ModificationOperation.REPLACE_ATTRIBUTE;
+            EntryAttribute result = new DefaultServerAttribute( attributeType );
+            EntryAttribute opAttrAdditions = operational.get( attributeType );
+            EntryAttribute opAttrInEntry = entry.get( attributeType );
+
+            for ( Value<?> value : opAttrAdditions )
+            {
+                result.add( value );
+            }
+
+            if ( opAttrInEntry != null && opAttrInEntry.size() > 0 )
+            {
+                for ( Value<?> value : opAttrInEntry )
+                {
+                    result.add( value );
+                }
+            }
+            else
+            {
+                op = ModificationOperation.ADD_ATTRIBUTE;
+            }
+
+            modList.add( new ServerModification( op, result ) );
+        }
+
+        return modList;
+    }
+
+    /**
+     * SearchResultFilter used to filter out subentries based on objectClass values.
+     */
+    public class HideSubentriesFilter implements SearchResultFilter
+    {
+        public boolean accept( Invocation invocation, ServerSearchResult result, SearchControls controls )
+            throws NamingException
+        {
+            String dn = result.getDn().getNormName();
+
+            // see if we can get a match without normalization
+            if ( subentryCache.hasSubentry( dn ) )
+            {
+                return false;
+            }
+
+            // see if we can use objectclass if present
+            EntryAttribute objectClasses = result.getServerEntry().get( SchemaConstants.OBJECT_CLASS_AT );
+
+            if ( objectClasses != null )
+            {
+                return !objectClasses.contains( SchemaConstants.SUBENTRY_OC );
+            }
+
+            if ( !result.isRelative() )
+            {
+                LdapDN ndn = new LdapDN( dn );
+                ndn.normalize( atRegistry.getNormalizerMapping() );
+                String normalizedDn = ndn.toString();
+                return !subentryCache.hasSubentry( normalizedDn );
+            }
+
+            LdapDN name = new LdapDN( invocation.getCaller().getNameInNamespace() );
+            name.normalize( atRegistry.getNormalizerMapping() );
+
+            LdapDN rest = result.getDn();
+            rest.normalize( atRegistry.getNormalizerMapping() );
+            name.addAll( rest );
+            return !subentryCache.hasSubentry( name.toString() );
+        }
+    }
+
+    /**
+     * SearchResultFilter used to filter out normal entries but shows subentries based on 
+     * objectClass values.
+     */
+    public class HideEntriesFilter implements SearchResultFilter
+    {
+        public boolean accept( Invocation invocation, ServerSearchResult result, SearchControls controls )
+            throws NamingException
+        {
+            String dn = result.getDn().getNormName();
+
+            // see if we can get a match without normalization
+            if ( subentryCache.hasSubentry( dn ) )
+            {
+                return true;
+            }
+
+            // see if we can use objectclass if present
+            EntryAttribute objectClasses = result.getServerEntry().get( SchemaConstants.OBJECT_CLASS_AT );
+
+            if ( objectClasses != null )
+            {
+                return objectClasses.contains( SchemaConstants.SUBENTRY_OC );
+            }
+
+            if ( !result.isRelative() )
+            {
+                LdapDN ndn = new LdapDN( dn );
+                ndn.normalize( atRegistry.getNormalizerMapping() );
+                return subentryCache.hasSubentry( ndn.toNormName() );
+            }
+
+            LdapDN name = new LdapDN( invocation.getCaller().getNameInNamespace() );
+            name.normalize( atRegistry.getNormalizerMapping() );
+
+            LdapDN rest = result.getDn();
+            rest.normalize( atRegistry.getNormalizerMapping() );
+            name.addAll( rest );
+            return subentryCache.hasSubentry( name.toNormName() );
+        }
+    }
+
+
+    private List<Modification> getModsOnEntryModification( LdapDN name, ServerEntry oldEntry, ServerEntry newEntry )
+        throws NamingException
+    {
+        List<Modification> modList = new ArrayList<Modification>();
+
+        Iterator<String> subentries = subentryCache.nameIterator();
+
+        while ( subentries.hasNext() )
+        {
+            String subentryDn = subentries.next();
+            Name apDn = new LdapDN( subentryDn );
+            apDn.remove( apDn.size() - 1 );
+            SubtreeSpecification ss = subentryCache.getSubentry( subentryDn ).getSubtreeSpecification();
+            boolean isOldEntrySelected = evaluator.evaluate( ss, apDn, name, oldEntry );
+            boolean isNewEntrySelected = evaluator.evaluate( ss, apDn, name, newEntry );
+
+            if ( isOldEntrySelected == isNewEntrySelected )
+            {
+                continue;
+            }
+
+            // need to remove references to the subentry
+            if ( isOldEntrySelected && !isNewEntrySelected )
+            {
+                for ( String aSUBENTRY_OPATTRS : SUBENTRY_OPATTRS )
+                {
+                    ModificationOperation op = ModificationOperation.REPLACE_ATTRIBUTE;
+                    EntryAttribute opAttr = oldEntry.get( aSUBENTRY_OPATTRS );
+
+                    if ( opAttr != null )
+                    {
+                        opAttr = ( ServerAttribute ) opAttr.clone();
+                        opAttr.remove( subentryDn );
+
+                        if ( opAttr.size() < 1 )
+                        {
+                            op = ModificationOperation.REMOVE_ATTRIBUTE;
+                        }
+
+                        modList.add( new ServerModification( op, opAttr ) );
+                    }
+                }
+            }
+            // need to add references to the subentry
+            else if ( isNewEntrySelected && !isOldEntrySelected )
+            {
+                for ( String attribute : SUBENTRY_OPATTRS )
+                {
+                    ModificationOperation op = ModificationOperation.ADD_ATTRIBUTE;
+                    AttributeType type = atRegistry.lookup( attribute );
+                    ServerAttribute opAttr = new DefaultServerAttribute( attribute, type );
+                    opAttr.add( subentryDn );
+                    modList.add( new ServerModification( op, opAttr ) );
+                }
+            }
+        }
+
+        return modList;
+    }
+
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/subtree/SubtreeEvaluator.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/subtree/SubtreeEvaluator.java
new file mode 100644
index 0000000..5d29773
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/subtree/SubtreeEvaluator.java
@@ -0,0 +1,208 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.subtree;
+
+
+import java.util.Iterator;
+
+import javax.naming.Name;
+import javax.naming.NamingException;
+
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.event.Evaluator;
+import org.apache.directory.server.core.event.ExpressionEvaluator;
+import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
+import org.apache.directory.server.schema.registries.OidRegistry;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.subtree.SubtreeSpecification;
+import org.apache.directory.shared.ldap.util.NamespaceTools;
+
+
+/**
+ * An evaluator used to determine if an entry is included in the collection
+ * represented by a subtree specification.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class SubtreeEvaluator
+{
+    /** A refinement filter evaluator */
+    private final Evaluator evaluator;
+
+
+    /**
+     * Creates a subtreeSpecification evaluatior which can be used to determine
+     * if an entry is included within the collection of a subtree.
+     *
+     * @param oidRegistry a registry used to lookup objectClass names for OIDs
+     * @param attrRegistry registry to be looked up
+     * @throws NamingException 
+     */
+    public SubtreeEvaluator( OidRegistry oidRegistry, AttributeTypeRegistry attrRegistry )
+    {
+        evaluator = new ExpressionEvaluator(oidRegistry, attrRegistry );
+    }
+
+
+    /**
+     * Determines if an entry is selected by a subtree specification.
+     *
+     * @param subtree the subtree specification
+     * @param apDn the distinguished name of the administrative point containing the subentry
+     * @param entryDn the distinguished name of the candidate entry
+     * @return true if the entry is selected by the specification, false if it is not
+     * @throws javax.naming.NamingException if errors are encountered while evaluating selection
+     */
+    public boolean evaluate( SubtreeSpecification subtree, Name apDn, Name entryDn, ServerEntry entry )
+        throws NamingException
+    {
+        // TODO: Try to make this cast unnecessary.
+        LdapDN entryLdapDn = (LdapDN) entryDn;
+        
+        /* =====================================================================
+         * NOTE: Regarding the overall approach, we try to narrow down the
+         * possibilities by slowly pruning relative names off of the entryDn.
+         * For example we check first if the entry is a descendant of the AP.
+         * If so we use the relative name thereafter to calculate if it is
+         * a descendant of the base.  This means shorter names to compare and
+         * less work to do while we continue to deduce inclusion by the subtree
+         * specification.
+         * =====================================================================
+         */
+
+        /*
+         * First we simply check if the candidate entry is a descendant of the
+         * administrative point.  In the process we calculate the relative
+         * distinguished name relative to the administrative point.
+         */
+        Name apRelativeRdn;
+        
+        if ( !NamespaceTools.isDescendant( apDn, entryDn ) )
+        {
+            return false;
+        }
+        else if ( apDn.equals( entryDn ) )
+        {
+            apRelativeRdn = new LdapDN();
+        }
+        else
+        {
+            apRelativeRdn = NamespaceTools.getRelativeName( apDn, entryDn );
+        }
+
+        /*
+         * We do the same thing with the base as we did with the administrative
+         * point: check if the entry is a descendant of the base and find the
+         * relative name of the entry with respect to the base rdn.  With the
+         * baseRelativeRdn we can later make comparisons with specific exclusions.
+         */
+        Name baseRelativeRdn;
+        
+        if ( subtree.getBase() != null && subtree.getBase().size() == 0 )
+        {
+            baseRelativeRdn = apRelativeRdn;
+        }
+        else if ( apRelativeRdn.equals( subtree.getBase() ) )
+        {
+            baseRelativeRdn = new LdapDN();
+        }
+        else if ( !NamespaceTools.isDescendant( subtree.getBase(), apRelativeRdn ) )
+        {
+            return false;
+        }
+        else
+        {
+            baseRelativeRdn = NamespaceTools.getRelativeName( subtree.getBase(), apRelativeRdn );
+        }
+
+        /*
+         * Evaluate based on minimum and maximum chop values.  Here we simply
+         * need to compare the distances respectively with the size of the
+         * baseRelativeRdn.  For the max distance entries with a baseRelativeRdn
+         * size greater than the max distance are rejected.  For the min distance
+         * entries with a baseRelativeRdn size less than the minimum distance
+         * are rejected.
+         */
+        if ( subtree.getMaxBaseDistance() != SubtreeSpecification.UNBOUNDED_MAX )
+        {
+            if ( subtree.getMaxBaseDistance() < baseRelativeRdn.size() )
+            {
+                return false;
+            }
+        }
+
+        if ( subtree.getMinBaseDistance() > 0 )
+        {
+            if ( baseRelativeRdn.size() < subtree.getMinBaseDistance() )
+            {
+                return false;
+            }
+        }
+
+        /*
+         * For specific exclusions we must iterate through the set and check
+         * if the baseRelativeRdn is a descendant of the exclusion.  The
+         * isDescendant() function will return true if the compared names
+         * are equal so for chopAfter exclusions we must check for equality
+         * as well and reject if the relative names are equal.
+         */
+        Iterator list = subtree.getChopBeforeExclusions().iterator();
+        
+        while ( list.hasNext() )
+        {
+            Name chopBefore = ( Name ) list.next();
+            
+            if ( NamespaceTools.isDescendant( chopBefore, baseRelativeRdn ) )
+            {
+                return false;
+            }
+        }
+
+        list = subtree.getChopAfterExclusions().iterator();
+        
+        while ( list.hasNext() )
+        {
+            Name chopAfter = ( Name ) list.next();
+            
+            if ( NamespaceTools.isDescendant( chopAfter, baseRelativeRdn ) && !chopAfter.equals( baseRelativeRdn ) )
+            {
+                return false;
+            }
+        }
+
+        /*
+         * The last remaining step is to check and see if the refinement filter
+         * selects the entry candidate based on objectClass attribute values.
+         * To do this we invoke the refinement evaluator members evaluate() method.
+         */
+        if ( subtree.getRefinement() != null )
+        {
+            return evaluator.evaluate( subtree.getRefinement(), entryLdapDn.toNormName(), entry );
+        }
+
+        /*
+         * If nothing has rejected the candidate entry and there is no refinement
+         * filter then the entry is included in the collection represented by the
+         * subtree specification so we return true.
+         */
+        return true;
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/trigger/AbstractStoredProcedureParameterInjector.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/trigger/AbstractStoredProcedureParameterInjector.java
new file mode 100644
index 0000000..ca1ae54
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/trigger/AbstractStoredProcedureParameterInjector.java
@@ -0,0 +1,110 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.core.trigger;
+
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.naming.Name;
+import javax.naming.NamingException;
+
+import org.apache.directory.server.core.invocation.Invocation;
+import org.apache.directory.server.core.jndi.ServerContext;
+import org.apache.directory.server.core.jndi.ServerLdapContext;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.trigger.StoredProcedureParameter;
+import org.apache.directory.shared.ldap.trigger.StoredProcedureParameter.Generic_LDAP_CONTEXT;
+
+public abstract class AbstractStoredProcedureParameterInjector implements StoredProcedureParameterInjector
+{
+    private Invocation invocation;
+    private Map<Class<?>, MicroInjector> injectors;
+    
+    public AbstractStoredProcedureParameterInjector( Invocation invocation )
+    {
+        this.invocation = invocation;
+        injectors = new HashMap<Class<?>, MicroInjector>();
+        injectors.put( StoredProcedureParameter.Generic_OPERATION_PRINCIPAL.class, $operationPrincipalInjector );
+        injectors.put( StoredProcedureParameter.Generic_LDAP_CONTEXT.class, $ldapContextInjector );
+    }
+    
+    protected Name getOperationPrincipal() throws NamingException
+    {
+        Principal principal = ( ( ServerContext ) invocation.getCaller() ).getPrincipal();
+        Name userName = new LdapDN( principal.getName() );
+        return userName;
+    }
+    
+    protected Map<Class<?>, MicroInjector> getInjectors()
+    {
+        return injectors;
+    }
+    
+    public Invocation getInvocation()
+    {
+        return invocation;
+    }
+    
+    public void setInvocation( Invocation invocation )
+    {
+        this.invocation = invocation;
+    }
+    
+    public final List<Object> getArgumentsToInject( Registries registries, List<StoredProcedureParameter> parameterList ) throws NamingException
+    {
+        List<Object> arguments = new ArrayList<Object>();
+        
+        Iterator<StoredProcedureParameter> it = parameterList.iterator();
+        
+        while ( it.hasNext() )
+        {
+            StoredProcedureParameter spParameter = it.next();
+            MicroInjector injector = injectors.get( spParameter.getClass() );
+            arguments.add( injector.inject( registries, spParameter ) );
+        }
+        
+        return arguments;
+    }
+    
+    MicroInjector $operationPrincipalInjector = new MicroInjector()
+    {
+        public Object inject( Registries registries, StoredProcedureParameter param ) throws NamingException
+        {
+            return getOperationPrincipal();
+        }
+    };
+    
+    MicroInjector $ldapContextInjector = new MicroInjector()
+    {
+        public Object inject(  Registries registries, StoredProcedureParameter param ) throws NamingException
+        {
+            Generic_LDAP_CONTEXT ldapCtxParam = ( Generic_LDAP_CONTEXT ) param;
+            LdapDN ldapCtxName = ldapCtxParam.getCtxName();
+            return ( ( ServerLdapContext ) ( ( ServerLdapContext ) invocation.getCaller() ).getRootContext()).lookup( ldapCtxName );
+        }
+    };
+
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/trigger/AddStoredProcedureParameterInjector.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/trigger/AddStoredProcedureParameterInjector.java
new file mode 100644
index 0000000..4104bef
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/trigger/AddStoredProcedureParameterInjector.java
@@ -0,0 +1,65 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.core.trigger;
+
+import java.util.Map;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.invocation.Invocation;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.trigger.StoredProcedureParameter;
+
+public class AddStoredProcedureParameterInjector extends AbstractStoredProcedureParameterInjector
+{
+    private LdapDN addedEntryName;
+    private ServerEntry addedEntry;
+    
+    public AddStoredProcedureParameterInjector( Invocation invocation, LdapDN addedEntryName, ServerEntry addedEntry )
+    {
+        super( invocation );
+        this.addedEntryName = addedEntryName;
+        this.addedEntry = addedEntry;
+        Map<Class<?>, MicroInjector> injectors = super.getInjectors();
+        injectors.put( StoredProcedureParameter.Add_ENTRY.class, $entryInjector );
+        injectors.put( StoredProcedureParameter.Add_ATTRIBUTES.class, $attributesInjector );
+    }
+    
+    MicroInjector $entryInjector = new MicroInjector()
+    {
+        public Object inject( Registries registries, StoredProcedureParameter param ) throws NamingException
+        {
+            // Return a safe copy constructed with user provided name.
+            return new LdapDN( addedEntryName.getUpName() );
+        }
+    };
+    
+    MicroInjector $attributesInjector = new MicroInjector()
+    {
+        public Object inject( Registries registries, StoredProcedureParameter param ) throws NamingException
+        {
+            return addedEntry.clone();
+        }
+    };
+
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/trigger/DeleteStoredProcedureParameterInjector.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/trigger/DeleteStoredProcedureParameterInjector.java
new file mode 100644
index 0000000..054707a
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/trigger/DeleteStoredProcedureParameterInjector.java
@@ -0,0 +1,80 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.core.trigger;
+
+
+import java.util.Map;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.interceptor.context.LookupOperationContext;
+import org.apache.directory.server.core.invocation.Invocation;
+import org.apache.directory.server.core.partition.PartitionNexusProxy;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.trigger.StoredProcedureParameter;
+
+
+public class DeleteStoredProcedureParameterInjector extends AbstractStoredProcedureParameterInjector
+{
+    private LdapDN deletedEntryName;
+    private ServerEntry deletedEntry;
+    
+    public DeleteStoredProcedureParameterInjector( Registries registries, Invocation invocation, LdapDN deletedEntryName ) throws NamingException
+    {
+        super( invocation );
+        this.deletedEntryName = deletedEntryName;
+        this.deletedEntry = getDeletedEntry( registries );
+        Map<Class<?>, MicroInjector> injectors = super.getInjectors();
+        injectors.put( StoredProcedureParameter.Delete_NAME.class, $nameInjector );
+        injectors.put( StoredProcedureParameter.Delete_DELETED_ENTRY.class, $deletedEntryInjector );
+    }
+    
+    MicroInjector $nameInjector = new MicroInjector()
+    {
+        public Object inject( Registries registries, StoredProcedureParameter param ) throws NamingException
+        {
+            // Return a safe copy constructed with user provided name.
+            return new LdapDN( deletedEntryName.getUpName() );
+        }
+    };
+    
+    MicroInjector $deletedEntryInjector = new MicroInjector()
+    {
+        public Object inject( Registries registries, StoredProcedureParameter param ) throws NamingException
+        {
+            return deletedEntry;
+        }
+    };
+    
+    private ServerEntry getDeletedEntry( Registries registries ) throws NamingException
+    {
+        PartitionNexusProxy proxy = getInvocation().getProxy();
+        /**
+         * Using LOOKUP_EXCLUDING_OPR_ATTRS_BYPASS here to exclude operational attributes
+         * especially subentry related ones like "triggerExecutionSubentries".
+         */
+        ServerEntry deletedEntry = proxy.lookup( new LookupOperationContext( registries, deletedEntryName ), PartitionNexusProxy.LOOKUP_EXCLUDING_OPR_ATTRS_BYPASS );
+        
+        return deletedEntry;
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/trigger/ModifyDNStoredProcedureParameterInjector.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/trigger/ModifyDNStoredProcedureParameterInjector.java
new file mode 100644
index 0000000..ed08d0f
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/trigger/ModifyDNStoredProcedureParameterInjector.java
@@ -0,0 +1,149 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.core.trigger;
+
+import org.apache.directory.server.core.invocation.Invocation;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.name.Rdn;
+import org.apache.directory.shared.ldap.trigger.StoredProcedureParameter;
+
+import javax.naming.NamingException;
+import java.util.Map;
+
+
+public class ModifyDNStoredProcedureParameterInjector extends AbstractStoredProcedureParameterInjector
+{
+    private boolean deleteOldRn;
+    private LdapDN oldRDN;
+    private Rdn newRDN;
+    private LdapDN oldSuperiorDN;
+    private LdapDN newSuperiorDN;
+    private LdapDN oldDN;
+    private LdapDN newDN;
+
+    public ModifyDNStoredProcedureParameterInjector( Invocation invocation, boolean deleteOldRn,
+        LdapDN oldRDN, Rdn newRDN, LdapDN oldSuperiorDN, LdapDN newSuperiorDN, LdapDN oldDN, LdapDN newDN)
+    {
+        super( invocation );
+        this.deleteOldRn = deleteOldRn;
+        this.oldRDN = oldRDN;
+        this.newRDN = newRDN;
+        this.oldSuperiorDN = oldSuperiorDN;
+        this.newSuperiorDN = newSuperiorDN;
+        this.oldDN = oldDN;
+        this.newDN = newDN;
+        
+        Map<Class<?>, MicroInjector> injectors = super.getInjectors();
+        injectors.put( StoredProcedureParameter.ModifyDN_ENTRY.class, $entryInjector );
+        injectors.put( StoredProcedureParameter.ModifyDN_NEW_RDN.class, $newrdnInjector );
+        injectors.put( StoredProcedureParameter.ModifyDN_DELETE_OLD_RDN.class, $deleteoldrdnInjector );
+        injectors.put( StoredProcedureParameter.ModifyDN_NEW_SUPERIOR.class, $newSuperiorInjector );
+        injectors.put( StoredProcedureParameter.ModifyDN_OLD_RDN.class, $oldRDNInjector );
+        injectors.put( StoredProcedureParameter.ModifyDN_OLD_SUPERIOR_DN.class, $oldSuperiorDNInjector );
+        injectors.put( StoredProcedureParameter.ModifyDN_NEW_DN.class, $newDNInjector );
+        
+    }
+    /**
+     * Injector for 'entry' parameter of ModifyDNRequest as in RFC4511.
+     */
+    MicroInjector $entryInjector = new MicroInjector()
+    {
+        public Object inject( Registries registries, StoredProcedureParameter param ) throws NamingException
+        {
+            // Return a safe copy constructed with user provided name.
+            return new LdapDN( oldDN.getUpName() );
+        }
+    };
+
+    /**
+     * Injector for 'newrdn' parameter of ModifyDNRequest as in RFC4511.
+     */
+    MicroInjector $newrdnInjector = new MicroInjector()
+    {
+        public Object inject( Registries registries, StoredProcedureParameter param ) throws NamingException
+        {
+            // Return a safe copy constructed with user provided name.
+            return new LdapDN( newRDN.getUpName() );
+        }
+    };
+
+    /**
+     * Injector for 'newrdn' parameter of ModifyDNRequest as in RFC4511.
+     */
+    MicroInjector $deleteoldrdnInjector = new MicroInjector()
+    {
+        public Object inject( Registries registries, StoredProcedureParameter param ) throws NamingException
+        {
+            // Return a safe copy constructed with user provided name.
+            return deleteOldRn;
+        }
+    };
+
+    /**
+     * Injector for 'newSuperior' parameter of ModifyDNRequest as in RFC4511.
+     */
+    MicroInjector $newSuperiorInjector = new MicroInjector()
+    {
+        public Object inject( Registries registries, StoredProcedureParameter param ) throws NamingException
+        {
+            // Return a safe copy constructed with user provided name.
+            return new LdapDN( newSuperiorDN.getUpName() );
+        }
+    };
+    
+    /**
+     * Extra injector for 'oldRDN' which can be derived from parameters specified for ModifyDNRequest as in RFC4511.
+     */
+    MicroInjector $oldRDNInjector = new MicroInjector()
+    {
+        public Object inject( Registries registries, StoredProcedureParameter param ) throws NamingException
+        {
+            // Return a safe copy constructed with user provided name.
+            return new LdapDN( oldRDN.getUpName() );
+        }
+    };
+    
+    /**
+     * Extra injector for 'oldRDN' which can be derived from parameters specified for ModifyDNRequest as in RFC4511.
+     */
+    MicroInjector $oldSuperiorDNInjector = new MicroInjector()
+    {
+        public Object inject( Registries registries, StoredProcedureParameter param ) throws NamingException
+        {
+            // Return a safe copy constructed with user provided name.
+            return new LdapDN( oldSuperiorDN.getUpName() );
+        }
+    };
+    
+    /**
+     * Extra injector for 'newDN' which can be derived from parameters specified for ModifyDNRequest as in RFC4511.
+     */
+    MicroInjector $newDNInjector = new MicroInjector()
+    {
+        public Object inject( Registries registries, StoredProcedureParameter param ) throws NamingException
+        {
+            // Return a safe copy constructed with user provided name.
+            return new LdapDN( newDN.getUpName() );
+        }
+    };
+    
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/trigger/ModifyStoredProcedureParameterInjector.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/trigger/ModifyStoredProcedureParameterInjector.java
new file mode 100644
index 0000000..76b0925
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/trigger/ModifyStoredProcedureParameterInjector.java
@@ -0,0 +1,109 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.core.trigger;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.interceptor.context.LookupOperationContext;
+import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
+import org.apache.directory.server.core.invocation.Invocation;
+import org.apache.directory.server.core.partition.PartitionNexusProxy;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.entry.Modification;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.trigger.StoredProcedureParameter;
+
+public class ModifyStoredProcedureParameterInjector extends AbstractStoredProcedureParameterInjector
+{
+    private LdapDN modifiedEntryName;
+    private List<Modification> modifications;
+    private ServerEntry oldEntry;
+    
+    
+    public ModifyStoredProcedureParameterInjector( Invocation invocation, ModifyOperationContext opContext ) throws NamingException
+    {
+        super( invocation );
+        modifiedEntryName = opContext.getDn();
+        modifications = opContext.getModItems();
+        this.oldEntry = getEntry( opContext.getRegistries() );
+        Map<Class<?>, MicroInjector> injectors = super.getInjectors();
+        injectors.put( StoredProcedureParameter.Modify_OBJECT.class, $objectInjector );
+        injectors.put( StoredProcedureParameter.Modify_MODIFICATION.class, $modificationInjector );
+        injectors.put( StoredProcedureParameter.Modify_OLD_ENTRY.class, $oldEntryInjector );
+        injectors.put( StoredProcedureParameter.Modify_NEW_ENTRY.class, $newEntryInjector );
+    }
+    
+    MicroInjector $objectInjector = new MicroInjector()
+    {
+        public Object inject( Registries registries, StoredProcedureParameter param ) throws NamingException
+        {
+            // Return a safe copy constructed with user provided name.
+            return new LdapDN( modifiedEntryName.getUpName() );
+        }
+    };
+    
+    MicroInjector $modificationInjector = new MicroInjector()
+    {
+        public Object inject( Registries registries, StoredProcedureParameter param ) throws NamingException
+        {
+            List<Modification> newMods = new ArrayList<Modification>();
+            
+            for ( Modification mod:modifications )
+            {
+                newMods.add( mod.clone() );
+            }
+            
+            return newMods;
+        }
+    };
+    
+    MicroInjector $oldEntryInjector = new MicroInjector()
+    {
+        public Object inject( Registries registries, StoredProcedureParameter param ) throws NamingException
+        {
+            return oldEntry;
+        }
+    };
+    
+    MicroInjector $newEntryInjector = new MicroInjector()
+    {
+        public Object inject( Registries registries, StoredProcedureParameter param ) throws NamingException
+        {
+            return getEntry( registries );
+        }
+    };
+    
+    private ServerEntry getEntry( Registries registries ) throws NamingException
+    {
+        PartitionNexusProxy proxy = getInvocation().getProxy();
+        /**
+         * Using LOOKUP_EXCLUDING_OPR_ATTRS_BYPASS here to exclude operational attributes
+         * especially subentry related ones like "triggerExecutionSubentries".
+         */
+        return proxy.lookup( new LookupOperationContext( registries, modifiedEntryName ), PartitionNexusProxy.LOOKUP_EXCLUDING_OPR_ATTRS_BYPASS );
+    }
+
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/trigger/SimpleTriggerExecutionAuthorizer.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/trigger/SimpleTriggerExecutionAuthorizer.java
new file mode 100644
index 0000000..afdadb5
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/trigger/SimpleTriggerExecutionAuthorizer.java
@@ -0,0 +1,59 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.trigger;
+
+import java.security.Principal;
+
+import javax.naming.InvalidNameException;
+import javax.naming.NamingException;
+
+import org.apache.directory.server.constants.ServerDNConstants;
+import org.apache.directory.server.core.invocation.Invocation;
+import org.apache.directory.server.core.invocation.InvocationStack;
+import org.apache.directory.server.core.jndi.ServerContext;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+public class SimpleTriggerExecutionAuthorizer implements TriggerExecutionAuthorizer
+{
+    private static LdapDN adminName;
+    
+    static
+    {
+        try
+        {
+            adminName = new LdapDN( ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED );
+        }
+        catch ( InvalidNameException e )
+        {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        }
+    }
+    
+    public boolean hasPermission() throws NamingException
+    {
+        Invocation invocation = InvocationStack.getInstance().peek();
+        Principal principal = ( ( ServerContext ) invocation.getCaller() ).getPrincipal();
+        LdapDN principalName = new LdapDN( principal.getName() );
+        
+        return principalName.equals( adminName );
+    }
+
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/trigger/StoredProcedureParameterInjector.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/trigger/StoredProcedureParameterInjector.java
new file mode 100644
index 0000000..bc98c50
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/trigger/StoredProcedureParameterInjector.java
@@ -0,0 +1,38 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.core.trigger;
+
+import java.util.List;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.trigger.StoredProcedureParameter;
+
+public interface StoredProcedureParameterInjector
+{
+    List<Object> getArgumentsToInject( Registries registries, List<StoredProcedureParameter> parameterList ) throws NamingException;
+    
+    public interface MicroInjector
+    {
+        Object inject( Registries registries, StoredProcedureParameter param ) throws NamingException;
+    }
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/trigger/TriggerExecutionAuthorizer.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/trigger/TriggerExecutionAuthorizer.java
new file mode 100644
index 0000000..2ebceef
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/trigger/TriggerExecutionAuthorizer.java
@@ -0,0 +1,27 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.trigger;
+
+import javax.naming.NamingException;
+
+public interface TriggerExecutionAuthorizer
+{
+    boolean hasPermission() throws NamingException;
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/trigger/TriggerInterceptor.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/trigger/TriggerInterceptor.java
new file mode 100644
index 0000000..11f25ed
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/trigger/TriggerInterceptor.java
@@ -0,0 +1,627 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.core.trigger;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.interceptor.BaseInterceptor;
+import org.apache.directory.server.core.interceptor.InterceptorChain;
+import org.apache.directory.server.core.interceptor.NextInterceptor;
+import org.apache.directory.server.core.interceptor.context.AddOperationContext;
+import org.apache.directory.server.core.interceptor.context.DeleteOperationContext;
+import org.apache.directory.server.core.interceptor.context.LookupOperationContext;
+import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
+import org.apache.directory.server.core.interceptor.context.MoveAndRenameOperationContext;
+import org.apache.directory.server.core.interceptor.context.MoveOperationContext;
+import org.apache.directory.server.core.interceptor.context.RenameOperationContext;
+import org.apache.directory.server.core.invocation.Invocation;
+import org.apache.directory.server.core.invocation.InvocationStack;
+import org.apache.directory.server.core.jndi.ServerLdapContext;
+import org.apache.directory.server.core.partition.PartitionNexusProxy;
+import org.apache.directory.server.core.sp.StoredProcEngine;
+import org.apache.directory.server.core.sp.StoredProcEngineConfig;
+import org.apache.directory.server.core.sp.StoredProcExecutionManager;
+import org.apache.directory.server.core.sp.java.JavaStoredProcEngineConfig;
+import org.apache.directory.server.core.subtree.SubentryInterceptor;
+import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.entry.Value;
+import org.apache.directory.shared.ldap.exception.LdapNamingException;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.name.Rdn;
+import org.apache.directory.shared.ldap.schema.NormalizerMappingResolver;
+import org.apache.directory.shared.ldap.schema.OidNormalizer;
+import org.apache.directory.shared.ldap.trigger.ActionTime;
+import org.apache.directory.shared.ldap.trigger.LdapOperation;
+import org.apache.directory.shared.ldap.trigger.TriggerSpecification;
+import org.apache.directory.shared.ldap.trigger.TriggerSpecification.SPSpec;
+import org.apache.directory.shared.ldap.trigger.TriggerSpecificationParser;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.naming.NamingException;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * The Trigger Service based on the Trigger Specification.
+ *
+ * @org.apache.xbean.XBean
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev:$
+ */
+public class TriggerInterceptor extends BaseInterceptor
+{
+    /** the logger for this class */
+    private static final Logger LOG = LoggerFactory.getLogger( TriggerInterceptor.class );
+    
+    /** the entry trigger attribute string: entryTrigger */
+    private static final String ENTRY_TRIGGER_ATTR = "entryTriggerSpecification";
+
+    /** a triggerSpecCache that responds to add, delete, and modify attempts */
+    private TriggerSpecCache triggerSpecCache;
+    
+    /** a normalizing Trigger Specification parser */
+    private TriggerSpecificationParser triggerParser;
+    
+    /** the global registries */
+    private Registries registries;
+    
+    /** */
+    private InterceptorChain chain;
+    
+    /** whether or not this interceptor is activated */
+    private boolean enabled = true;
+
+    /** a Trigger Execution Authorizer */
+    private TriggerExecutionAuthorizer triggerExecutionAuthorizer = new SimpleTriggerExecutionAuthorizer();
+    
+    private StoredProcExecutionManager manager;
+
+    /**
+     * Adds prescriptiveTrigger TriggerSpecificaitons to a collection of
+     * TriggerSpeficaitions by accessing the triggerSpecCache.  The trigger
+     * specification cache is accessed for each trigger subentry associated
+     * with the entry.
+     * Note that subentries are handled differently: their parent, the administrative
+     * entry is accessed to determine the perscriptiveTriggers effecting the AP
+     * and hence the subentry which is considered to be in the same context.
+     *
+     * @param triggerSpecs the collection of trigger specifications to add to
+     * @param dn the normalized distinguished name of the entry
+     * @param entry the target entry that is considered as the trigger source
+     * @throws NamingException if there are problems accessing attribute values
+     * @param proxy the partition nexus proxy 
+     */
+    private void addPrescriptiveTriggerSpecs( List<TriggerSpecification> triggerSpecs, PartitionNexusProxy proxy,
+        LdapDN dn, ServerEntry entry ) throws NamingException
+    {
+        
+        /*
+         * If the protected entry is a subentry, then the entry being evaluated
+         * for perscriptiveTriggerss is in fact the administrative entry.  By
+         * substituting the administrative entry for the actual subentry the
+         * code below this "if" statement correctly evaluates the effects of
+         * perscriptiveTrigger on the subentry.  Basically subentries are considered
+         * to be in the same naming context as their access point so the subentries
+         * effecting their parent entry applies to them as well.
+         */
+        if ( entry.contains( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.SUBENTRY_OC ) )
+        {
+            LdapDN parentDn = ( LdapDN ) dn.clone();
+            parentDn.remove( dn.size() - 1 );
+            entry = proxy.lookup( new LookupOperationContext( registries, parentDn ), PartitionNexusProxy.LOOKUP_BYPASS );
+        }
+
+        EntryAttribute subentries = entry.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT );
+        
+        if ( subentries == null )
+        {
+            return;
+        }
+        
+        for ( Value<?> value:subentries )
+        {
+            String subentryDn = ( String ) value.get();
+            triggerSpecs.addAll( triggerSpecCache.getSubentryTriggerSpecs( subentryDn ) );
+        }
+    }
+
+    /**
+     * Adds the set of entryTriggers to a collection of trigger specifications.
+     * The entryTrigger is parsed and tuples are generated on they fly then
+     * added to the collection.
+     *
+     * @param triggerSpecs the collection of trigger specifications to add to
+     * @param entry the target entry that is considered as the trigger source
+     * @throws NamingException if there are problems accessing attribute values
+     */
+    private void addEntryTriggerSpecs( List<TriggerSpecification> triggerSpecs, ServerEntry entry ) throws NamingException
+    {
+        EntryAttribute entryTrigger = entry.get( ENTRY_TRIGGER_ATTR );
+        
+        if ( entryTrigger == null )
+        {
+            return;
+        }
+
+        for ( Value<?> value:entryTrigger )
+        {
+            String triggerString = ( String ) value.get();
+            TriggerSpecification item;
+
+            try
+            {
+                item = triggerParser.parse( triggerString );
+            }
+            catch ( ParseException e )
+            {
+                String msg = "failed to parse entryTrigger: " + triggerString;
+                LOG.error( msg, e );
+                throw new LdapNamingException( msg, ResultCodeEnum.OPERATIONS_ERROR );
+            }
+
+            triggerSpecs.add( item );
+        }
+    }
+    
+    /**
+     * Return a selection of trigger specifications for a certain type of trigger action time.
+     * 
+     * @note This method serves as an extion point for new Action Time types.
+     * 
+     * @param triggerSpecs the trigger specifications
+     * @param ldapOperation the ldap operation being performed
+     * @return the set of trigger specs for a trigger action 
+     */
+    public Map<ActionTime, List<TriggerSpecification>> getActionTimeMappedTriggerSpecsForOperation( List<TriggerSpecification> triggerSpecs, LdapOperation ldapOperation )
+    {
+        List<TriggerSpecification> afterTriggerSpecs = new ArrayList<TriggerSpecification>();
+        Map<ActionTime, List<TriggerSpecification>> triggerSpecMap = new HashMap<ActionTime, List<TriggerSpecification>>();
+
+        for ( TriggerSpecification triggerSpec : triggerSpecs )
+        {
+            if ( triggerSpec.getLdapOperation().equals( ldapOperation ) )
+            {
+                if ( triggerSpec.getActionTime().equals( ActionTime.AFTER ) )
+                {
+                    afterTriggerSpecs.add( triggerSpec );
+                }
+                else
+                {
+
+                }
+            }
+        }
+        
+        triggerSpecMap.put( ActionTime.AFTER, afterTriggerSpecs );
+        
+        return triggerSpecMap;
+    }
+    
+    ////////////////////////////////////////////////////////////////////////////
+    // Interceptor Overrides
+    ////////////////////////////////////////////////////////////////////////////
+    
+    public void init( DirectoryService directoryService ) throws NamingException
+    {
+        super.init( directoryService );
+        registries = directoryService.getRegistries();
+        
+        triggerSpecCache = new TriggerSpecCache( directoryService );
+        final AttributeTypeRegistry attrRegistry = directoryService.getRegistries().getAttributeTypeRegistry();
+        triggerParser = new TriggerSpecificationParser
+            ( new NormalizerMappingResolver()
+                {
+                    public Map<String, OidNormalizer> getNormalizerMapping() throws NamingException
+                    {
+                        return attrRegistry.getNormalizerMapping();
+                    }
+                }
+            );
+        chain = directoryService.getInterceptorChain();
+        
+        //StoredProcEngineConfig javaxScriptSPEngineConfig = new JavaxStoredProcEngineConfig();
+        StoredProcEngineConfig javaSPEngineConfig = new JavaStoredProcEngineConfig();
+        List<StoredProcEngineConfig> spEngineConfigs = new ArrayList<StoredProcEngineConfig>();
+        //spEngineConfigs.add( javaxScriptSPEngineConfig );
+        spEngineConfigs.add( javaSPEngineConfig );
+        String spContainer = "ou=Stored Procedures,ou=system";
+        manager = new StoredProcExecutionManager( spContainer, spEngineConfigs );
+        
+        this.enabled = true; // TODO: Get this from the configuration if needed.
+    }
+
+    public void add( NextInterceptor next, AddOperationContext addContext ) throws NamingException
+    {
+        LdapDN name = addContext.getDn();
+        ServerEntry entry = addContext.getEntry();
+        
+        // Bypass trigger handling if the service is disabled.
+        if ( !enabled )
+        {
+            next.add( addContext );
+            return;
+        }
+        
+        // Gather supplementary data.
+        Invocation invocation = InvocationStack.getInstance().peek();
+        PartitionNexusProxy proxy = invocation.getProxy();
+        ServerLdapContext callerRootCtx = ( ServerLdapContext ) ( ( ServerLdapContext ) invocation.getCaller() ).getRootContext();
+        StoredProcedureParameterInjector injector = new AddStoredProcedureParameterInjector( invocation, name, entry );
+
+        // Gather Trigger Specifications which apply to the entry being added.
+        List<TriggerSpecification> triggerSpecs = new ArrayList<TriggerSpecification>();
+        addPrescriptiveTriggerSpecs( triggerSpecs, proxy, name, entry );
+
+        /**
+         *  NOTE: We do not handle entryTriggerSpecs for ADD operation.
+         */
+        
+        Map<ActionTime, List<TriggerSpecification>> triggerMap = getActionTimeMappedTriggerSpecsForOperation( triggerSpecs, LdapOperation.ADD );
+        
+        next.add( addContext );
+        triggerSpecCache.subentryAdded( name, entry );
+        
+        // Fire AFTER Triggers.
+        List<TriggerSpecification> afterTriggerSpecs = triggerMap.get( ActionTime.AFTER );
+        executeTriggers( afterTriggerSpecs, injector, callerRootCtx );
+    }
+
+    public void delete( NextInterceptor next, DeleteOperationContext deleteContext ) throws NamingException
+    {
+        LdapDN name = deleteContext.getDn();
+        
+        // Bypass trigger handling if the service is disabled.
+        if ( !enabled )
+        {
+            next.delete( deleteContext );
+            return;
+        }
+        
+        // Gather supplementary data.
+        Invocation invocation = InvocationStack.getInstance().peek();
+        PartitionNexusProxy proxy = invocation.getProxy();
+        ServerEntry deletedEntry = proxy.lookup( new LookupOperationContext( registries, name ), PartitionNexusProxy.LOOKUP_BYPASS );
+        
+        ServerLdapContext callerRootCtx = ( ServerLdapContext ) ( ( ServerLdapContext ) invocation.getCaller() ).getRootContext();
+        StoredProcedureParameterInjector injector = new DeleteStoredProcedureParameterInjector( registries, invocation, name );
+
+        // Gather Trigger Specifications which apply to the entry being deleted.
+        List<TriggerSpecification> triggerSpecs = new ArrayList<TriggerSpecification>();
+        addPrescriptiveTriggerSpecs( triggerSpecs, proxy, name, deletedEntry );
+        addEntryTriggerSpecs( triggerSpecs, deletedEntry );
+        
+        Map<ActionTime, List<TriggerSpecification>> triggerMap = getActionTimeMappedTriggerSpecsForOperation( triggerSpecs, LdapOperation.DELETE );
+        
+        next.delete( deleteContext );
+        triggerSpecCache.subentryDeleted( name, deletedEntry );
+        
+        // Fire AFTER Triggers.
+        List<TriggerSpecification> afterTriggerSpecs = triggerMap.get( ActionTime.AFTER );
+        executeTriggers( afterTriggerSpecs, injector, callerRootCtx );
+    }
+    
+    public void modify( NextInterceptor next, ModifyOperationContext opContext ) throws NamingException
+    {
+        // Bypass trigger handling if the service is disabled.
+        if ( !enabled )
+        {
+            next.modify( opContext );
+            return;
+        }
+        
+        LdapDN normName = opContext.getDn();
+        
+        // Gather supplementary data.
+        Invocation invocation = InvocationStack.getInstance().peek();
+        PartitionNexusProxy proxy = invocation.getProxy();
+        ServerEntry modifiedEntry = proxy.lookup( new LookupOperationContext( registries, normName ), PartitionNexusProxy.LOOKUP_BYPASS );
+        
+        ServerLdapContext callerRootCtx = ( ServerLdapContext ) ( ( ServerLdapContext ) invocation.getCaller() ).getRootContext();
+        StoredProcedureParameterInjector injector = new ModifyStoredProcedureParameterInjector( invocation, opContext );
+
+        // Gather Trigger Specifications which apply to the entry being modified.
+        List<TriggerSpecification> triggerSpecs = new ArrayList<TriggerSpecification>();
+        addPrescriptiveTriggerSpecs( triggerSpecs, proxy, normName, modifiedEntry );
+        addEntryTriggerSpecs( triggerSpecs, modifiedEntry );
+        
+        Map<ActionTime, List<TriggerSpecification>> triggerMap = getActionTimeMappedTriggerSpecsForOperation( triggerSpecs, LdapOperation.MODIFY );
+        
+        next.modify( opContext );
+        triggerSpecCache.subentryModified( opContext, modifiedEntry );
+        
+        // Fire AFTER Triggers.
+        List<TriggerSpecification> afterTriggerSpecs = triggerMap.get( ActionTime.AFTER );
+        executeTriggers( afterTriggerSpecs, injector, callerRootCtx );
+    }
+    
+
+    public void rename( NextInterceptor next, RenameOperationContext renameContext ) throws NamingException
+    {
+        LdapDN name = renameContext.getDn();
+        Rdn newRdn = renameContext.getNewRdn();
+        boolean deleteOldRn = renameContext.getDelOldDn();
+        
+        // Bypass trigger handling if the service is disabled.
+        if ( !enabled )
+        {
+            next.rename( renameContext );
+            return;
+        }
+        
+        // Gather supplementary data.        
+        Invocation invocation = InvocationStack.getInstance().peek();
+        PartitionNexusProxy proxy = invocation.getProxy();
+        ServerEntry renamedEntry = proxy.lookup( new LookupOperationContext( registries, name ), PartitionNexusProxy.LOOKUP_BYPASS );
+        
+        ServerLdapContext callerRootCtx = ( ServerLdapContext ) ( ( ServerLdapContext ) invocation.getCaller() ).getRootContext();
+        
+        LdapDN oldRDN = new LdapDN( name.getRdn().getUpName() );
+        LdapDN oldSuperiorDN = ( LdapDN ) name.clone();
+        oldSuperiorDN.remove( oldSuperiorDN.size() - 1 );
+        LdapDN newSuperiorDN = ( LdapDN ) oldSuperiorDN.clone();
+        LdapDN oldDN = ( LdapDN ) name.clone();
+        LdapDN newDN = ( LdapDN ) name.clone();
+        newDN.add( newRdn );
+        
+        StoredProcedureParameterInjector injector = new ModifyDNStoredProcedureParameterInjector(
+            invocation, deleteOldRn, oldRDN, newRdn, oldSuperiorDN, newSuperiorDN, oldDN, newDN );
+        
+        // Gather Trigger Specifications which apply to the entry being renamed.
+        List<TriggerSpecification> triggerSpecs = new ArrayList<TriggerSpecification>();
+        addPrescriptiveTriggerSpecs( triggerSpecs, proxy, name, renamedEntry );
+        addEntryTriggerSpecs( triggerSpecs, renamedEntry );
+        
+        Map<ActionTime, List<TriggerSpecification>> triggerMap = getActionTimeMappedTriggerSpecsForOperation( triggerSpecs, LdapOperation.MODIFYDN_RENAME );
+        
+        next.rename( renameContext );
+        triggerSpecCache.subentryRenamed( name, newDN );
+        
+        // Fire AFTER Triggers.
+        List<TriggerSpecification> afterTriggerSpecs = triggerMap.get( ActionTime.AFTER );
+        executeTriggers( afterTriggerSpecs, injector, callerRootCtx );
+    }
+    
+    public void moveAndRename( NextInterceptor next, MoveAndRenameOperationContext moveAndRenameContext ) throws NamingException
+    {
+        LdapDN oriChildName = moveAndRenameContext.getDn();
+        LdapDN parent = moveAndRenameContext.getParent();
+        Rdn newRdn = moveAndRenameContext.getNewRdn();
+        boolean deleteOldRn = moveAndRenameContext.getDelOldDn();
+
+        // Bypass trigger handling if the service is disabled.
+        if ( !enabled )
+        {
+            next.moveAndRename( moveAndRenameContext );
+            return;
+        }
+        
+        // Gather supplementary data.        
+        Invocation invocation = InvocationStack.getInstance().peek();
+        PartitionNexusProxy proxy = invocation.getProxy();
+        ServerEntry movedEntry = proxy.lookup( new LookupOperationContext( registries, oriChildName ), PartitionNexusProxy.LOOKUP_BYPASS );
+        
+        ServerLdapContext callerRootCtx = ( ServerLdapContext ) ( ( ServerLdapContext ) invocation.getCaller() ).getRootContext();
+        
+        LdapDN oldRDN = new LdapDN( oriChildName.getRdn().getUpName() );
+        LdapDN oldSuperiorDN = ( LdapDN ) oriChildName.clone();
+        oldSuperiorDN.remove( oldSuperiorDN.size() - 1 );
+        LdapDN newSuperiorDN = ( LdapDN ) parent.clone();
+        LdapDN oldDN = ( LdapDN ) oriChildName.clone();
+        LdapDN newDN = ( LdapDN ) parent.clone();
+        newDN.add( newRdn.getUpName() );
+
+        StoredProcedureParameterInjector injector = new ModifyDNStoredProcedureParameterInjector(
+            invocation, deleteOldRn, oldRDN, newRdn, oldSuperiorDN, newSuperiorDN, oldDN, newDN );
+
+        // Gather Trigger Specifications which apply to the entry being exported.
+        List<TriggerSpecification> exportTriggerSpecs = new ArrayList<TriggerSpecification>();
+        addPrescriptiveTriggerSpecs( exportTriggerSpecs, proxy, oriChildName, movedEntry );
+        addEntryTriggerSpecs( exportTriggerSpecs, movedEntry );
+        
+        // Get the entry again without operational attributes
+        // because access control subentry operational attributes
+        // will not be valid at the new location.
+        // This will certainly be fixed by the SubentryInterceptor,
+        // but after this service.
+        ServerEntry importedEntry = proxy.lookup( new LookupOperationContext( registries, oriChildName ), PartitionNexusProxy.LOOKUP_EXCLUDING_OPR_ATTRS_BYPASS );
+        
+        // As the target entry does not exist yet and so
+        // its subentry operational attributes are not there,
+        // we need to construct an entry to represent it
+        // at least with minimal requirements which are object class
+        // and access control subentry operational attributes.
+        SubentryInterceptor subentryInterceptor = ( SubentryInterceptor ) chain.get( SubentryInterceptor.class.getName() );
+        ServerEntry fakeImportedEntry = subentryInterceptor.getSubentryAttributes( newDN, importedEntry );
+        
+        for ( EntryAttribute attribute:importedEntry )
+        {
+            fakeImportedEntry.put( attribute );
+        }
+        
+        // Gather Trigger Specifications which apply to the entry being imported.
+        // Note: Entry Trigger Specifications are not valid for Import.
+        List<TriggerSpecification> importTriggerSpecs = new ArrayList<TriggerSpecification>();
+        addPrescriptiveTriggerSpecs( importTriggerSpecs, proxy, newDN, fakeImportedEntry );
+        
+        Map<ActionTime, List<TriggerSpecification>> exportTriggerMap = getActionTimeMappedTriggerSpecsForOperation( exportTriggerSpecs, LdapOperation.MODIFYDN_EXPORT );
+        
+        Map<ActionTime, List<TriggerSpecification>> importTriggerMap = getActionTimeMappedTriggerSpecsForOperation( importTriggerSpecs, LdapOperation.MODIFYDN_IMPORT );
+        
+        next.moveAndRename( moveAndRenameContext );
+        triggerSpecCache.subentryRenamed( oldDN, newDN );
+        
+        // Fire AFTER Triggers.
+        List<TriggerSpecification> afterExportTriggerSpecs = exportTriggerMap.get( ActionTime.AFTER );
+        List<TriggerSpecification> afterImportTriggerSpecs = importTriggerMap.get( ActionTime.AFTER );
+        executeTriggers( afterExportTriggerSpecs, injector, callerRootCtx );
+        executeTriggers( afterImportTriggerSpecs, injector, callerRootCtx );
+    }
+    
+    
+    public void move( NextInterceptor next, MoveOperationContext moveContext ) throws NamingException
+    {
+        // Bypass trigger handling if the service is disabled.
+        if ( !enabled )
+        {
+            next.move( moveContext );
+            return;
+        }
+        
+        LdapDN oriChildName = moveContext.getDn();
+        LdapDN newParentName = moveContext.getParent();
+        
+        // Gather supplementary data.        
+        Invocation invocation = InvocationStack.getInstance().peek();
+        PartitionNexusProxy proxy = invocation.getProxy();
+        ServerEntry movedEntry = proxy.lookup( new LookupOperationContext( registries, oriChildName ), PartitionNexusProxy.LOOKUP_BYPASS );
+        
+        ServerLdapContext callerRootCtx = ( ServerLdapContext ) ( ( ServerLdapContext ) invocation.getCaller() ).getRootContext();
+        
+        LdapDN oldRDN = new LdapDN( oriChildName.getRdn().getUpName() );
+        Rdn newRDN = new Rdn( oriChildName.getRdn().getUpName() );
+        LdapDN oldSuperiorDN = ( LdapDN ) oriChildName.clone();
+        oldSuperiorDN.remove( oldSuperiorDN.size() - 1 );
+        LdapDN newSuperiorDN = ( LdapDN ) newParentName.clone();
+        LdapDN oldDN = ( LdapDN ) oriChildName.clone();
+        LdapDN newDN = ( LdapDN ) newParentName.clone();
+        newDN.add( newRDN.getUpName() );
+
+        StoredProcedureParameterInjector injector = new ModifyDNStoredProcedureParameterInjector(
+            invocation, false, oldRDN, newRDN, oldSuperiorDN, newSuperiorDN, oldDN, newDN );
+
+        // Gather Trigger Specifications which apply to the entry being exported.
+        List<TriggerSpecification> exportTriggerSpecs = new ArrayList<TriggerSpecification>();
+        addPrescriptiveTriggerSpecs( exportTriggerSpecs, proxy, oriChildName, movedEntry );
+        addEntryTriggerSpecs( exportTriggerSpecs, movedEntry );
+        
+        // Get the entry again without operational attributes
+        // because access control subentry operational attributes
+        // will not be valid at the new location.
+        // This will certainly be fixed by the SubentryInterceptor,
+        // but after this service.
+        ServerEntry importedEntry = proxy.lookup( new LookupOperationContext( registries, oriChildName ), PartitionNexusProxy.LOOKUP_EXCLUDING_OPR_ATTRS_BYPASS );
+
+        // As the target entry does not exist yet and so
+        // its subentry operational attributes are not there,
+        // we need to construct an entry to represent it
+        // at least with minimal requirements which are object class
+        // and access control subentry operational attributes.
+        SubentryInterceptor subentryInterceptor = ( SubentryInterceptor ) chain.get( SubentryInterceptor.class.getName() );
+        ServerEntry fakeImportedEntry = subentryInterceptor.getSubentryAttributes( newDN, importedEntry );
+        
+        for ( EntryAttribute attribute:importedEntry )
+        {
+            fakeImportedEntry.put( attribute );
+        }
+        
+        // Gather Trigger Specifications which apply to the entry being imported.
+        // Note: Entry Trigger Specifications are not valid for Import.
+        List<TriggerSpecification> importTriggerSpecs = new ArrayList<TriggerSpecification>();
+        addPrescriptiveTriggerSpecs( importTriggerSpecs, proxy, newDN, fakeImportedEntry );
+        
+        Map<ActionTime, List<TriggerSpecification>> exportTriggerMap = getActionTimeMappedTriggerSpecsForOperation( exportTriggerSpecs, LdapOperation.MODIFYDN_EXPORT );
+        
+        Map<ActionTime, List<TriggerSpecification>> importTriggerMap = getActionTimeMappedTriggerSpecsForOperation( importTriggerSpecs, LdapOperation.MODIFYDN_IMPORT );
+        
+        next.move( moveContext );
+        triggerSpecCache.subentryRenamed( oldDN, newDN );
+        
+        // Fire AFTER Triggers.
+        List<TriggerSpecification> afterExportTriggerSpecs = exportTriggerMap.get( ActionTime.AFTER );
+        List<TriggerSpecification> afterImportTriggerSpecs = importTriggerMap.get( ActionTime.AFTER );
+        executeTriggers( afterExportTriggerSpecs, injector, callerRootCtx );
+        executeTriggers( afterImportTriggerSpecs, injector, callerRootCtx );
+    }
+    
+    ////////////////////////////////////////////////////////////////////////////
+    // Utility Methods
+    ////////////////////////////////////////////////////////////////////////////
+    
+    private Object executeTriggers( List<TriggerSpecification> triggerSpecs, StoredProcedureParameterInjector injector, ServerLdapContext callerRootCtx ) throws NamingException
+    {
+        Object result = null;
+
+        for ( TriggerSpecification triggerSpec : triggerSpecs )
+        {
+            // TODO: Replace the Authorization Code with a REAL one.
+            if ( triggerExecutionAuthorizer.hasPermission() )
+            {
+                /**
+                 * If there is only one Trigger to be executed, this assignment
+                 * will make sense (as in INSTEADOF search Triggers).
+                 */
+                result = executeTrigger( triggerSpec, injector, callerRootCtx );
+            }
+        }
+        
+        /**
+         * If only one Trigger has been executed, returning its result
+         * can make sense (as in INSTEADOF Search Triggers).
+         */
+        return result;
+    }
+
+    private Object executeTrigger( TriggerSpecification tsec, StoredProcedureParameterInjector injector, ServerLdapContext callerRootCtx ) throws NamingException
+    {
+        List<Object> returnValues = new ArrayList<Object>();
+        List<SPSpec> spSpecs = tsec.getSPSpecs();
+        for ( SPSpec spSpec : spSpecs )
+        {
+            List<Object> arguments = new ArrayList<Object>();
+            arguments.addAll( injector.getArgumentsToInject( registries, spSpec.getParameters() ) );
+            Object[] values = arguments.toArray();
+            Object returnValue = executeProcedure( callerRootCtx, spSpec.getName(), values );
+            returnValues.add( returnValue );
+        }
+        
+        return returnValues; 
+    }
+
+    
+    private Object executeProcedure( ServerLdapContext ctx, String procedure, Object[] values ) throws NamingException
+    {
+        
+        try
+        {
+            ServerEntry spUnit = manager.findStoredProcUnit( ctx, procedure, registries );
+            StoredProcEngine engine = manager.getStoredProcEngineInstance( spUnit );
+            return engine.invokeProcedure( ctx, procedure, values );
+        }
+        catch ( NamingException e )
+        {
+            LdapNamingException lne = new LdapNamingException( ResultCodeEnum.OTHER );
+            lne.setRootCause( e );
+            throw lne;
+        }
+    }
+
+}
diff --git a/old_trunk/core/src/main/java/org/apache/directory/server/core/trigger/TriggerSpecCache.java b/old_trunk/core/src/main/java/org/apache/directory/server/core/trigger/TriggerSpecCache.java
new file mode 100644
index 0000000..6c3cece
--- /dev/null
+++ b/old_trunk/core/src/main/java/org/apache/directory/server/core/trigger/TriggerSpecCache.java
@@ -0,0 +1,238 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.core.trigger;
+
+import org.apache.directory.server.constants.ApacheSchemaConstants;
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerSearchResult;
+import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
+import org.apache.directory.server.core.interceptor.context.SearchOperationContext;
+import org.apache.directory.server.core.partition.PartitionNexus;
+import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.entry.Modification;
+import org.apache.directory.shared.ldap.entry.Value;
+import org.apache.directory.shared.ldap.filter.EqualityNode;
+import org.apache.directory.shared.ldap.filter.ExprNode;
+import org.apache.directory.shared.ldap.message.AliasDerefMode;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.schema.NormalizerMappingResolver;
+import org.apache.directory.shared.ldap.schema.OidNormalizer;
+import org.apache.directory.shared.ldap.trigger.TriggerSpecification;
+import org.apache.directory.shared.ldap.trigger.TriggerSpecificationParser;
+import org.apache.directory.shared.ldap.entry.client.ClientStringValue;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.SearchControls;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * A cache for Trigger Specifications which responds to specific events to
+ * perform cache house keeping as trigger subentries are added, deleted
+ * and modified.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev:$
+ */
+public class TriggerSpecCache
+{
+    /** the attribute id for prescriptive trigger: prescriptiveTrigger */
+    private static final String PRESCRIPTIVE_TRIGGER_ATTR = "prescriptiveTriggerSpecification";
+
+    /** the logger for this class */
+    private static final Logger LOG = LoggerFactory.getLogger( TriggerSpecCache.class );
+
+    /** a map of strings to TriggerSpecification collections */
+    private final Map<String, List<TriggerSpecification>> triggerSpecs = new HashMap<String, List<TriggerSpecification>>();
+    /** a handle on the partition nexus */
+    private final PartitionNexus nexus;
+    /** a normalizing TriggerSpecification parser */
+    private final TriggerSpecificationParser triggerSpecParser;
+
+
+    /**
+     * Creates a TriggerSpecification cache.
+     *
+     * @param directoryService the directory service core
+     * @throws NamingException with problems initializing cache
+     */
+    public TriggerSpecCache( DirectoryService directoryService ) throws NamingException
+    {
+        this.nexus = directoryService.getPartitionNexus();
+        final AttributeTypeRegistry registry = directoryService.getRegistries().getAttributeTypeRegistry();
+        triggerSpecParser = new TriggerSpecificationParser( new NormalizerMappingResolver()
+            {
+                public Map<String, OidNormalizer> getNormalizerMapping() throws NamingException
+                {
+                    return registry.getNormalizerMapping();
+                }
+            });
+        initialize( directoryService.getRegistries() );
+    }
+
+
+    private void initialize( Registries registries ) throws NamingException
+    {
+        // search all naming contexts for trigger subentenries
+        // generate TriggerSpecification arrays for each subentry
+        // add that subentry to the hash
+        Iterator<String> suffixes = nexus.listSuffixes( null );
+        
+        while ( suffixes.hasNext() )
+        {
+            String suffix = suffixes.next();
+            LdapDN baseDn = new LdapDN( suffix );
+            ExprNode filter = new EqualityNode( SchemaConstants.OBJECT_CLASS_AT, 
+                    new ClientStringValue( ApacheSchemaConstants.TRIGGER_EXECUTION_SUBENTRY_OC ) );
+            SearchControls ctls = new SearchControls();
+            ctls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+            NamingEnumeration<ServerSearchResult> results = 
+                nexus.search( new SearchOperationContext( registries, baseDn, AliasDerefMode.DEREF_ALWAYS, filter, ctls ) );
+            
+            while ( results.hasMore() )
+            {
+                ServerSearchResult result = results.next();
+                LdapDN subentryDn = result.getDn();
+                ServerEntry resultEntry = result.getServerEntry();
+                EntryAttribute triggerSpec = resultEntry.get( PRESCRIPTIVE_TRIGGER_ATTR );
+                
+                if ( triggerSpec == null )
+                {
+                    LOG.warn( "Found triggerExecutionSubentry '" + subentryDn + "' without any " + PRESCRIPTIVE_TRIGGER_ATTR );
+                    continue;
+                }
+
+                LdapDN normSubentryName = subentryDn.normalize( registries.getAttributeTypeRegistry().getNormalizerMapping() );
+                subentryAdded( normSubentryName, resultEntry );
+            }
+            
+            results.close();
+        }
+    }
+
+
+    private boolean hasPrescriptiveTrigger( ServerEntry entry ) throws NamingException
+    {
+        // only do something if the entry contains prescriptiveTrigger
+        EntryAttribute triggerSpec = entry.get( PRESCRIPTIVE_TRIGGER_ATTR );
+
+        return triggerSpec != null;
+    }
+
+
+    public void subentryAdded( LdapDN normName, ServerEntry entry ) throws NamingException
+    {
+        // only do something if the entry contains prescriptiveTrigger
+        EntryAttribute triggerSpec = entry.get( PRESCRIPTIVE_TRIGGER_ATTR );
+        
+        if ( triggerSpec == null )
+        {
+            return;
+        }
+        
+        List<TriggerSpecification> subentryTriggerSpecs = new ArrayList<TriggerSpecification>();
+        
+        for ( Value<?> value:triggerSpec )
+        {
+            TriggerSpecification item = null;
+
+            try
+            {
+                item = triggerSpecParser.parse( ( String ) value.get() );
+                subentryTriggerSpecs.add( item );
+            }
+            catch ( ParseException e )
+            {
+                String msg = "TriggerSpecification parser failure on '" + item + "'. Cannnot add Trigger Specificaitons to TriggerSpecCache.";
+                LOG.error( msg, e );
+            }
+            
+        }
+        
+        triggerSpecs.put( normName.toString(), subentryTriggerSpecs );
+    }
+
+
+    public void subentryDeleted( LdapDN normName, ServerEntry entry ) throws NamingException
+    {
+        if ( !hasPrescriptiveTrigger( entry ) )
+        {
+            return;
+        }
+
+        triggerSpecs.remove( normName.toString() );
+    }
+
+
+    public void subentryModified( ModifyOperationContext opContext, ServerEntry entry ) throws NamingException
+    {
+        if ( !hasPrescriptiveTrigger( entry ) )
+        {
+            return;
+        }
+
+        LdapDN normName = opContext.getDn();
+        List<Modification> mods = opContext.getModItems();
+
+        boolean isTriggerSpecModified = false;
+
+        for ( Modification mod : mods )
+        {
+            isTriggerSpecModified |= mod.getAttribute().contains( PRESCRIPTIVE_TRIGGER_ATTR );
+        }
+        
+        if ( isTriggerSpecModified )
+        {
+            subentryDeleted( normName, entry );
+            subentryAdded( normName, entry );
+        }
+    }
+
+
+    public List<TriggerSpecification> getSubentryTriggerSpecs( String subentryDn )
+    {
+        List<TriggerSpecification> subentryTriggerSpecs = triggerSpecs.get( subentryDn );
+        if ( subentryTriggerSpecs == null )
+        {
+            return Collections.emptyList();
+        }
+        return Collections.unmodifiableList( subentryTriggerSpecs );
+    }
+
+
+    public void subentryRenamed( LdapDN oldName, LdapDN newName )
+    {
+        triggerSpecs.put( newName.toString(), triggerSpecs.remove( oldName.toString() ) );
+    }
+}
diff --git a/old_trunk/core/src/main/resources/org/apache/directory/server/core/partition/impl/btree/gui/server.gif b/old_trunk/core/src/main/resources/org/apache/directory/server/core/partition/impl/btree/gui/server.gif
new file mode 100644
index 0000000..401de45
--- /dev/null
+++ b/old_trunk/core/src/main/resources/org/apache/directory/server/core/partition/impl/btree/gui/server.gif
Binary files differ
diff --git a/old_trunk/core/src/main/resources/org/apache/directory/server/core/partition/version.properties b/old_trunk/core/src/main/resources/org/apache/directory/server/core/partition/version.properties
new file mode 100644
index 0000000..ef46322
--- /dev/null
+++ b/old_trunk/core/src/main/resources/org/apache/directory/server/core/partition/version.properties
@@ -0,0 +1 @@
+apacheds.version=${pom.version}
diff --git a/old_trunk/core/src/site/site.xml b/old_trunk/core/src/site/site.xml
new file mode 100644
index 0000000..9ec7c16
--- /dev/null
+++ b/old_trunk/core/src/site/site.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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.
+-->
+<!--
+  @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+-->
+<project name="${project.name}">
+    <bannerLeft>
+        <name>${project.name}</name>
+    </bannerLeft>
+    <publishDate position="navigation-bottom" format="dd-MM-yyyy HH:mm" />
+    <body>
+        <menu name="Parent">
+            <item name="Apache Directory ApacheDS" href="../index.html" />
+        </menu>
+        <menu ref="reports" />
+    </body>
+</project>
diff --git a/old_trunk/core/src/test/java/org/apache/directory/server/core/authn/SimpleAuthenticatorOneWayEncryptedTest.java b/old_trunk/core/src/test/java/org/apache/directory/server/core/authn/SimpleAuthenticatorOneWayEncryptedTest.java
new file mode 100644
index 0000000..6d96d00
--- /dev/null
+++ b/old_trunk/core/src/test/java/org/apache/directory/server/core/authn/SimpleAuthenticatorOneWayEncryptedTest.java
@@ -0,0 +1,71 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.core.authn;
+
+
+import org.apache.directory.server.core.authn.SimpleAuthenticator;
+import org.apache.directory.shared.ldap.util.StringTools;
+
+import junit.framework.TestCase;
+
+
+/**
+ * Test case for helper methods within SimpleAuthenticator.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class SimpleAuthenticatorOneWayEncryptedTest extends TestCase
+{
+    private SimpleAuthenticator auth = null;
+
+
+    protected void setUp() throws Exception
+    {
+        super.setUp();
+        this.auth = new SimpleAuthenticator();
+    }
+
+
+    public void testGetAlgorithmForHashedPassword()
+    {
+        String digestetValue = "{SHA}LhkDrSoM6qr0fW6hzlfOJQW61tc=";
+        assertEquals( "SHA", auth.getAlgorithmForHashedPassword( StringTools.getBytesUtf8( digestetValue ) ) );
+        assertEquals( "SHA", auth.getAlgorithmForHashedPassword( digestetValue.getBytes() ) );
+
+        String noAlgorithm = "Secret1!";
+        assertEquals( null, auth.getAlgorithmForHashedPassword( StringTools.getBytesUtf8( noAlgorithm ) ) );
+        assertEquals( null, auth.getAlgorithmForHashedPassword( noAlgorithm.getBytes() ) );
+
+        String unknownAlgorithm = "{XYZ}LhkDrSoM6qr0fW6hzlfOJQW61tc=";
+        assertEquals( null, auth.getAlgorithmForHashedPassword( StringTools.getBytesUtf8( unknownAlgorithm ) ) );
+        assertEquals( null, auth.getAlgorithmForHashedPassword( unknownAlgorithm.getBytes() ) );
+    }
+
+
+    public void testCreateDigestedPassword() throws IllegalArgumentException
+    {
+        String pwd = "Secret1!";
+        String expected = "{SHA}znbJr3+tymFoQD4+Njh4ITtI7Cc=";
+        String digested = auth.createDigestedPassword( "SHA", StringTools.getBytesUtf8( pwd ) );
+
+        assertEquals( expected, digested );
+    }
+}
\ No newline at end of file
diff --git a/old_trunk/core/src/test/java/org/apache/directory/server/core/authz/support/DummyAttributeTypeRegistry.java b/old_trunk/core/src/test/java/org/apache/directory/server/core/authz/support/DummyAttributeTypeRegistry.java
new file mode 100644
index 0000000..11e37ed
--- /dev/null
+++ b/old_trunk/core/src/test/java/org/apache/directory/server/core/authz/support/DummyAttributeTypeRegistry.java
@@ -0,0 +1,579 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.authz.support;
+
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.core.authz.support.ACITupleFilter;
+import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.schema.MatchingRule;
+import org.apache.directory.shared.ldap.schema.Normalizer;
+import org.apache.directory.shared.ldap.schema.OidNormalizer;
+import org.apache.directory.shared.ldap.schema.Syntax;
+import org.apache.directory.shared.ldap.schema.UsageEnum;
+import org.apache.directory.shared.ldap.schema.syntax.SyntaxChecker;
+import org.apache.directory.shared.ldap.util.StringTools;
+
+
+/**
+ * A mock {@link AttributeTypeRegistry} to test {@link ACITupleFilter} implementations.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ *
+ */
+public class DummyAttributeTypeRegistry implements AttributeTypeRegistry
+{
+    private final boolean returnOperational;
+
+
+    public DummyAttributeTypeRegistry(boolean returnOperational)
+    {
+        this.returnOperational = returnOperational;
+    }
+
+
+    public AttributeType lookup( final String id ) throws NamingException
+    {
+        if ( returnOperational )
+        {
+            return new AttributeType()
+            {
+                private static final long serialVersionUID = 1L;
+
+
+                public boolean isSingleValue()
+                {
+                    return false;
+                }
+
+
+                public boolean isCanUserModify()
+                {
+                    return false;
+                }
+
+
+                public boolean isCollective()
+                {
+                    return false;
+                }
+
+
+                public UsageEnum getUsage()
+                {
+                    return null;
+                }
+
+
+                public AttributeType getSuperior() throws NamingException
+                {
+                    return null;
+                }
+
+
+                public Syntax getSyntax() throws NamingException
+                {
+                    return null;
+                }
+
+
+                public int getLength()
+                {
+                    return 0;
+                }
+
+
+                public MatchingRule getEquality() throws NamingException
+                {
+                    return new MatchingRule()
+                    {
+                        private static final long serialVersionUID = 1L;
+
+                        public Syntax getSyntax() throws NamingException
+                        {
+                            return null;
+                        }
+
+                        public Comparator getComparator() throws NamingException
+                        {
+                            return null;
+                        }
+
+                        public Normalizer getNormalizer() throws NamingException
+                        {
+                            return new Normalizer()
+                            {
+                                private static final long serialVersionUID = 1L;
+
+                                public Object normalize( Object value ) throws NamingException
+                                {
+                                    return StringTools.deepTrimToLower( value.toString() );
+                                }
+                            };
+                        }
+
+                        public boolean isObsolete()
+                        {
+                            return false;
+                        }
+
+                        public String getOid()
+                        {
+                            return null;
+                        }
+
+                        public String[] getNamesRef()
+                        {
+                            return null;
+                        }
+
+                        public String getName()
+                        {
+                            return null;
+                        }
+
+                        public String getDescription()
+                        {
+                            return null;
+                        }
+
+                        public String getSchema()
+                        {
+                            return null;
+                        }
+
+                        public void setSchema( String schemaName )
+                        {
+                        }
+                    };
+                }
+
+
+                public MatchingRule getOrdering() throws NamingException
+                {
+                    return null;
+                }
+
+
+                public MatchingRule getSubstr() throws NamingException
+                {
+                    return null;
+                }
+
+
+                public boolean isAncestorOf( AttributeType descendant ) throws NamingException
+                {
+                    return false;
+                }
+
+
+                public boolean isDescentantOf( AttributeType ancestor ) throws NamingException
+                {
+                    return false;
+                }
+
+
+                public boolean isObsolete()
+                {
+                    return false;
+                }
+
+
+                public String getOid()
+                {
+                    return String.valueOf( id.hashCode() );
+                }
+
+
+                public String[] getNamesRef()
+                {
+                    return new String[]
+                        { id };
+                }
+
+
+                public String getName()
+                {
+                    return id;
+                }
+
+
+                public String getDescription()
+                {
+                    return id;
+                }
+
+
+                public String getSchema()
+                {
+                    return null;
+                }
+
+
+                public void setSchema( String schemaName )
+                {
+                }
+            };
+        }
+        else
+        {
+            return new AttributeType()
+            {
+                private static final long serialVersionUID = 1L;
+
+                public boolean isSingleValue()
+                {
+                    return false;
+                }
+
+
+                public boolean isCanUserModify()
+                {
+                    return true;
+                }
+
+
+                public boolean isCollective()
+                {
+                    return false;
+                }
+
+
+                public UsageEnum getUsage()
+                {
+                    return null;
+                }
+
+
+                public AttributeType getSuperior() throws NamingException
+                {
+                    return null;
+                }
+
+
+                public Syntax getSyntax() throws NamingException
+                {
+                    return new Syntax()
+                    {
+
+                        private static final long serialVersionUID = 1L;
+
+                        public boolean isHumanReadable()
+                        {
+                            return true;
+                        }
+
+                        public SyntaxChecker getSyntaxChecker() throws NamingException
+                        {
+                            return null;
+                        }
+
+                        public boolean isObsolete()
+                        {
+                            return false;
+                        }
+
+                        public String getOid()
+                        {
+                            return null;
+                        }
+
+                        public String[] getNamesRef()
+                        {
+                            return null;
+                        }
+
+                        public String getName()
+                        {
+                            return null;
+                        }
+
+                        public String getDescription()
+                        {
+                            return null;
+                        }
+
+                        public String getSchema()
+                        {
+                            return null;
+                        }
+
+                        public void setSchema( String schemaName )
+                        {
+                        }
+                    };
+                }
+
+
+                public int getLength()
+                {
+                    return 0;
+                }
+
+
+                public MatchingRule getEquality() throws NamingException
+                {
+                    return new MatchingRule()
+                    {
+                        private static final long serialVersionUID = 1L;
+
+                        public Syntax getSyntax() throws NamingException
+                        {
+                            return new Syntax()
+                            {
+                                private static final long serialVersionUID = 1L;
+
+
+                                public boolean isHumanReadable()
+                                {
+                                    return true;
+                                }
+
+                                public SyntaxChecker getSyntaxChecker() throws NamingException
+                                {
+                                    return null;
+                                }
+
+                                public boolean isObsolete()
+                                {
+                                    return false;
+                                }
+
+                                public String getOid()
+                                {
+                                    return null;
+                                }
+
+                                public String[] getNamesRef()
+                                {
+                                    return null;
+                                }
+
+                                public String getName()
+                                {
+                                    return null;
+                                }
+
+                                public String getDescription()
+                                {
+                                    return null;
+                                }
+
+                                public String getSchema()
+                                {
+                                    return null;
+                                }
+
+                                public void setSchema( String schemaName )
+                                {
+                                }
+                            };
+                        }
+
+                        public Comparator getComparator() throws NamingException
+                        {
+                            return null;
+                        }
+
+                        public Normalizer getNormalizer() throws NamingException
+                        {
+                            return new Normalizer()
+                            {
+                                private static final long serialVersionUID = 1L;
+
+                                public Object normalize( Object value ) throws NamingException
+                                {
+                                    return StringTools.deepTrimToLower( value.toString() );
+                                }
+                            };
+                        }
+
+                        public boolean isObsolete()
+                        {
+                            return false;
+                        }
+
+                        public String getOid()
+                        {
+                            return null;
+                        }
+
+                        public String[] getNamesRef()
+                        {
+                            return null;
+                        }
+
+                        public String getName()
+                        {
+                            return null;
+                        }
+
+                        public String getDescription()
+                        {
+                            return null;
+                        }
+
+                        public String getSchema()
+                        {
+                            return null;
+                        }
+
+                        public void setSchema( String schemaName )
+                        {
+                        }
+                    };
+                }
+
+
+                public MatchingRule getOrdering() throws NamingException
+                {
+                    return null;
+                }
+
+
+                public MatchingRule getSubstr() throws NamingException
+                {
+                    return null;
+                }
+
+
+                public boolean isAncestorOf( AttributeType descendant ) throws NamingException
+                {
+                    return false;
+                }
+
+
+                public boolean isDescentantOf( AttributeType ancestor ) throws NamingException
+                {
+                    return false;
+                }
+
+
+                public boolean isObsolete()
+                {
+                    return false;
+                }
+
+
+                public String getOid()
+                {
+                    return String.valueOf( id.hashCode() );
+                }
+
+
+                public String[] getNamesRef()
+                {
+                    return new String[]
+                        { id };
+                }
+
+
+                public String getName()
+                {
+                    return id;
+                }
+
+
+                public String getDescription()
+                {
+                    return id;
+                }
+
+
+                public String getSchema()
+                {
+                    return null;
+                }
+
+
+                public void setSchema( String schemaName )
+                {
+                }
+            };
+        }
+    }
+
+
+    public String getSchemaName( String id ) throws NamingException
+    {
+        return "dummy";
+    }
+
+
+    public boolean hasAttributeType( String id )
+    {
+        return true;
+    }
+
+
+    public Iterator list()
+    {
+        return new ArrayList().iterator();
+    }
+
+
+    public Map<String,OidNormalizer> getNormalizerMapping() throws NamingException
+    {
+        return null;
+    }
+
+
+    public Iterator<AttributeType> descendants( String ancestorId ) throws NamingException
+    {
+        return null;
+    }
+
+
+    public boolean hasDescendants( String ancestorId ) throws NamingException
+    {
+        return false;
+    }
+
+
+    public Iterator<AttributeType> iterator()
+    {
+        return null;
+    }
+
+
+    public void unregister( String numericOid ) throws NamingException
+    {
+    }
+
+
+    public void register( AttributeType attributeType ) throws NamingException
+    {
+    }
+
+
+    public Set<String> getBinaryAttributes() throws NamingException
+    {
+        return null;
+    }
+}
diff --git a/old_trunk/core/src/test/java/org/apache/directory/server/core/authz/support/DummyOidRegistry.java b/old_trunk/core/src/test/java/org/apache/directory/server/core/authz/support/DummyOidRegistry.java
new file mode 100644
index 0000000..66a00dd
--- /dev/null
+++ b/old_trunk/core/src/test/java/org/apache/directory/server/core/authz/support/DummyOidRegistry.java
@@ -0,0 +1,105 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.authz.support;
+
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.core.authz.support.ACITupleFilter;
+import org.apache.directory.server.schema.registries.OidRegistry;
+
+
+/**
+ * A mock {@link OidRegistry} to test {@link ACITupleFilter} implementations.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ *
+ */
+class DummyOidRegistry implements OidRegistry
+{
+    public String getOid( String name ) throws NamingException
+    {
+        return name.toLowerCase();
+    }
+
+
+    public boolean hasOid( String id )
+    {
+        return true;
+    }
+
+
+    public String getPrimaryName( String oid ) throws NamingException
+    {
+        return oid;
+    }
+
+
+    public List<String> getNameSet( String oid ) throws NamingException
+    {
+        List<String> list = new ArrayList<String>();
+        list.add( oid );
+        return list;
+    }
+
+
+    public Iterator list()
+    {
+        // Not used
+        return new ArrayList().iterator();
+    }
+
+
+    public void register( String name, String oid )
+    {
+        // Not used
+    }
+
+
+    /**
+     * Get the map of all the oids by their name
+     * @return The Map that contains all the oids
+     */
+    public Map getOidByName()
+    {
+        return null;
+    }
+
+
+    /**
+     * Get the map of all the oids by their name
+     * @return The Map that contains all the oids
+     */
+    public Map getNameByOid()
+    {
+        return null;
+    }
+
+
+    public void unregister( String numericOid ) throws NamingException
+    {
+    }
+}
\ No newline at end of file
diff --git a/old_trunk/core/src/test/java/org/apache/directory/server/core/authz/support/HighestPrecedenceFilterTest.java b/old_trunk/core/src/test/java/org/apache/directory/server/core/authz/support/HighestPrecedenceFilterTest.java
new file mode 100644
index 0000000..c709f86
--- /dev/null
+++ b/old_trunk/core/src/test/java/org/apache/directory/server/core/authz/support/HighestPrecedenceFilterTest.java
@@ -0,0 +1,98 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.authz.support;
+
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import org.apache.directory.server.core.authz.support.HighestPrecedenceFilter;
+import org.apache.directory.shared.ldap.aci.ACITuple;
+import org.apache.directory.shared.ldap.aci.MicroOperation;
+import org.apache.directory.shared.ldap.aci.ProtectedItem;
+import org.apache.directory.shared.ldap.aci.UserClass;
+import org.apache.directory.shared.ldap.constants.AuthenticationLevel;
+
+
+/**
+ * Tests {@link HighestPrecedenceFilter}.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ *
+ */
+public class HighestPrecedenceFilterTest extends TestCase
+{
+    private static final Collection<ProtectedItem> PI_EMPTY_COLLECTION = Collections.unmodifiableCollection( new ArrayList<ProtectedItem>() );
+    private static final Collection<UserClass> UC_EMPTY_COLLECTION = Collections.unmodifiableCollection( new ArrayList<UserClass>() );
+    private static final Collection<ACITuple> AT_EMPTY_COLLECTION = Collections.unmodifiableCollection( new ArrayList<ACITuple>() );
+    private static final Set<MicroOperation> MO_EMPTY_SET = Collections.unmodifiableSet( new HashSet<MicroOperation>() );
+
+
+    public void testZeroTuple() throws Exception
+    {
+        HighestPrecedenceFilter filter = new HighestPrecedenceFilter();
+        Assert.assertEquals( 0, filter.filter( null, AT_EMPTY_COLLECTION, null, null, null, null, null, null, null, null, null,
+            null, null, null ).size() );
+    }
+
+
+    public void testOneTuple() throws Exception
+    {
+        HighestPrecedenceFilter filter = new HighestPrecedenceFilter();
+        Collection<ACITuple> tuples = new ArrayList<ACITuple>();
+        
+        tuples.add( new ACITuple( UC_EMPTY_COLLECTION, AuthenticationLevel.NONE, PI_EMPTY_COLLECTION, MO_EMPTY_SET, true, 10 ) );
+        tuples = Collections.unmodifiableCollection( tuples );
+        
+        Assert.assertEquals( tuples, filter.filter( null, tuples, null, null, null, null, null, null, null, null, null, null,
+            null, null ) );
+    }
+
+
+    public void testMoreThanOneTuples() throws Exception
+    {
+        final int MAX_PRECEDENCE = 10;
+        HighestPrecedenceFilter filter = new HighestPrecedenceFilter();
+        Collection<ACITuple> tuples = new ArrayList<ACITuple>();
+        
+        tuples.add( new ACITuple( UC_EMPTY_COLLECTION, AuthenticationLevel.NONE, PI_EMPTY_COLLECTION, MO_EMPTY_SET, true,
+            MAX_PRECEDENCE ) );
+        tuples.add( new ACITuple( UC_EMPTY_COLLECTION, AuthenticationLevel.NONE, PI_EMPTY_COLLECTION, MO_EMPTY_SET, true,
+            MAX_PRECEDENCE / 2 ) );
+        tuples.add( new ACITuple( UC_EMPTY_COLLECTION, AuthenticationLevel.NONE, PI_EMPTY_COLLECTION, MO_EMPTY_SET, true,
+            MAX_PRECEDENCE ) );
+        tuples.add( new ACITuple( UC_EMPTY_COLLECTION, AuthenticationLevel.NONE, PI_EMPTY_COLLECTION, MO_EMPTY_SET, true,
+            MAX_PRECEDENCE / 3 ) );
+
+        tuples = filter.filter( null, tuples, null, null, null, null, null, null, null, null, null, null, null, null );
+
+        for ( ACITuple tuple:tuples )
+        {
+            Assert.assertEquals( MAX_PRECEDENCE, tuple.getPrecedence() );
+        }
+    }
+}
diff --git a/old_trunk/core/src/test/java/org/apache/directory/server/core/authz/support/MaxImmSubFilterTest.java b/old_trunk/core/src/test/java/org/apache/directory/server/core/authz/support/MaxImmSubFilterTest.java
new file mode 100644
index 0000000..a0c91e4
--- /dev/null
+++ b/old_trunk/core/src/test/java/org/apache/directory/server/core/authz/support/MaxImmSubFilterTest.java
@@ -0,0 +1,567 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.authz.support;
+
+
+import org.apache.directory.server.core.DefaultDirectoryService;
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.authn.LdapPrincipal;
+import org.apache.directory.server.core.changelog.ChangeLog;
+import org.apache.directory.server.core.entry.DefaultServerEntry;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerSearchResult;
+import org.apache.directory.server.core.interceptor.Interceptor;
+import org.apache.directory.server.core.interceptor.InterceptorChain;
+import org.apache.directory.server.core.interceptor.context.SearchOperationContext;
+import org.apache.directory.server.core.jndi.DeadContext;
+import org.apache.directory.server.core.partition.Partition;
+import org.apache.directory.server.core.partition.PartitionNexus;
+import org.apache.directory.server.core.partition.PartitionNexusProxy;
+import org.apache.directory.server.core.schema.SchemaOperationControl;
+import org.apache.directory.server.core.schema.SchemaService;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.aci.ACITuple;
+import org.apache.directory.shared.ldap.aci.MicroOperation;
+import org.apache.directory.shared.ldap.aci.ProtectedItem;
+import org.apache.directory.shared.ldap.aci.UserClass;
+import org.apache.directory.shared.ldap.constants.AuthenticationLevel;
+import org.apache.directory.shared.ldap.ldif.LdifEntry;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.ldap.LdapContext;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+import org.junit.Test;
+import org.junit.BeforeClass;
+import static org.junit.Assert.assertEquals;
+
+
+/**
+ * Tests {@link MaxImmSubFilter}.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class MaxImmSubFilterTest
+{
+    private static final Collection<ACITuple> EMPTY_ACI_TUPLE_COLLECTION = Collections.unmodifiableCollection( new ArrayList<ACITuple>() );
+    private static final Collection<UserClass> EMPTY_USER_CLASS_COLLECTION = Collections.unmodifiableCollection( new ArrayList<UserClass>() );
+    private static final Collection<ProtectedItem> EMPTY_PROTECTED_ITEM_COLLECTION = Collections.unmodifiableCollection( new ArrayList<ProtectedItem>() );
+
+    private static final Set<MicroOperation> EMPTY_MICRO_OPERATION_SET = Collections.unmodifiableSet( new HashSet<MicroOperation>() );
+
+    private static final LdapDN ROOTDSE_NAME = new LdapDN();
+    private static LdapDN ENTRY_NAME;
+    private static Collection<ProtectedItem> PROTECTED_ITEMS = new ArrayList<ProtectedItem>();
+    private static ServerEntry ENTRY;
+    
+    /** A reference to the directory service */
+    private static DirectoryService service;
+
+    
+    @BeforeClass public static void setup() throws NamingException
+    {
+        service = new DefaultDirectoryService();
+
+        ENTRY_NAME = new LdapDN( "ou=test, ou=system" );
+        PROTECTED_ITEMS.add( new ProtectedItem.MaxImmSub( 2 ) );
+        ENTRY = new DefaultServerEntry( service.getRegistries(), ENTRY_NAME );
+    }
+
+
+    @Test public void testWrongScope() throws Exception
+    {
+        MaxImmSubFilter filter = new MaxImmSubFilter();
+        Collection<ACITuple> tuples = new ArrayList<ACITuple>();
+        tuples.add( new ACITuple( EMPTY_USER_CLASS_COLLECTION, AuthenticationLevel.NONE, 
+            EMPTY_PROTECTED_ITEM_COLLECTION, EMPTY_MICRO_OPERATION_SET, true, 0 ) );
+
+        tuples = Collections.unmodifiableCollection( tuples );
+
+        assertEquals( tuples, filter.filter( null, tuples, OperationScope.ATTRIBUTE_TYPE, null, null, null, null,
+            null, ENTRY_NAME, null, null, ENTRY, null, null ) );
+
+        assertEquals( tuples, filter.filter( null, tuples, OperationScope.ATTRIBUTE_TYPE_AND_VALUE, null, null, null,
+            null, null, ENTRY_NAME, null, null, ENTRY, null, null ) );
+    }
+
+
+    @Test public void testRootDSE() throws Exception
+    {
+        MaxImmSubFilter filter = new MaxImmSubFilter();
+
+        Collection<ACITuple> tuples = new ArrayList<ACITuple>();
+        tuples.add( new ACITuple( EMPTY_USER_CLASS_COLLECTION, AuthenticationLevel.NONE, 
+            EMPTY_PROTECTED_ITEM_COLLECTION, EMPTY_MICRO_OPERATION_SET, true, 0 ) );
+
+        tuples = Collections.unmodifiableCollection( tuples );
+
+        assertEquals( tuples, filter.filter( null, tuples, OperationScope.ENTRY, null, null, null, null, null,
+            ROOTDSE_NAME, null, null, ENTRY, null, null ) );
+    }
+
+
+    @Test public void testZeroTuple() throws Exception
+    {
+        MaxImmSubFilter filter = new MaxImmSubFilter();
+
+        assertEquals( 0, filter.filter( null, EMPTY_ACI_TUPLE_COLLECTION, OperationScope.ENTRY, null, null, null, null, null,
+            ENTRY_NAME, null, null, ENTRY, null, null ).size() );
+    }
+
+
+    @Test public void testDenialTuple() throws Exception
+    {
+        MaxImmSubFilter filter = new MaxImmSubFilter();
+        Collection<ACITuple> tuples = new ArrayList<ACITuple>();
+        tuples.add( new ACITuple( EMPTY_USER_CLASS_COLLECTION, AuthenticationLevel.NONE, 
+            PROTECTED_ITEMS, EMPTY_MICRO_OPERATION_SET, false, 0 ) );
+
+        tuples = Collections.unmodifiableCollection( tuples );
+
+        assertEquals( tuples, filter.filter( null, tuples, OperationScope.ENTRY, null, null, null, null, null,
+            ENTRY_NAME, null, null, ENTRY, null, null ) );
+    }
+
+
+    @Test public void testGrantTuple() throws Exception
+    {
+        MaxImmSubFilter filter = new MaxImmSubFilter();
+        Collection<ACITuple> tuples = new ArrayList<ACITuple>();
+        tuples.add( new ACITuple( EMPTY_USER_CLASS_COLLECTION, AuthenticationLevel.NONE, 
+            PROTECTED_ITEMS, EMPTY_MICRO_OPERATION_SET, true, 0 ) );
+
+        assertEquals( 1, filter.filter( null, tuples, OperationScope.ENTRY, new MockProxy( 1 ), null, null, null,
+            null, ENTRY_NAME, null, null, ENTRY, null, null ).size() );
+
+        assertEquals( 0, filter.filter( null, tuples, OperationScope.ENTRY, new MockProxy( 3 ), null, null, null,
+            null, ENTRY_NAME, null, null, ENTRY, null, null ).size() );
+    }
+
+    class MockProxy extends PartitionNexusProxy
+    {
+        final int count;
+
+
+        public MockProxy(int count) throws NamingException 
+        {
+            super( new DeadContext(), new MockDirectoryService() );
+            this.count = count;
+        }
+
+
+        public NamingEnumeration<ServerSearchResult> search( SearchOperationContext opContext )
+            throws NamingException
+        {
+            //noinspection unchecked
+            return new BogusEnumeration( count );
+        }
+
+
+        public NamingEnumeration<ServerSearchResult> search( SearchOperationContext opContext, Collection bypass ) throws NamingException
+        {
+            //noinspection unchecked
+            return new BogusEnumeration( count );
+        }
+    }
+
+    class MockDirectoryService implements DirectoryService
+    {
+        public Hashtable<String, Object> getEnvironment()
+        {
+            return null;
+        }
+
+
+        public void setEnvironment( Hashtable<String, Object> environment )
+        {
+        }
+
+
+        public long revert( long revision ) throws NamingException
+        {
+            return 0;
+        }
+
+
+        public long revert() throws NamingException
+        {
+            return 0;
+        }
+
+
+        public PartitionNexus getPartitionNexus()
+        {
+            return null;
+        }
+
+
+        public InterceptorChain getInterceptorChain()
+        {
+            return null;
+        }
+
+
+        public void addPartition( Partition partition ) throws NamingException
+        {
+        }
+
+
+        public void removePartition( Partition partition ) throws NamingException
+        {
+        }
+
+
+        public Registries getRegistries()
+        {
+            return null;
+        }
+
+
+        public void setRegistries( Registries registries )
+        {
+        }
+
+
+        public SchemaService getSchemaService()
+        {
+            return null;
+        }
+
+
+        public void setSchemaService( SchemaService schemaService )
+        {
+
+        }
+
+
+        public SchemaOperationControl getSchemaManager()
+        {
+            return null;
+        }
+
+
+        public void setSchemaManager( SchemaOperationControl schemaManager )
+        {
+        }
+
+
+        public void startup() throws NamingException
+        {
+        }
+
+
+        public void shutdown() throws NamingException
+        {
+        }
+
+
+        public void sync() throws NamingException
+        {
+        }
+
+
+        public boolean isStarted()
+        {
+            return true;
+        }
+
+
+        public LdapContext getJndiContext() throws NamingException
+        {
+            return null;
+        }
+
+
+        public DirectoryService getDirectoryService()
+        {
+            return null;
+        }
+
+
+        public LdapContext getJndiContext( String baseName ) throws NamingException
+        {
+            return null;
+        }
+
+
+        public LdapContext getJndiContext( LdapPrincipal principal ) throws NamingException
+        {
+            return null;
+        }
+
+
+        public LdapContext getJndiContext( LdapPrincipal principal, String dn ) throws NamingException
+        {
+            return null;
+        }
+
+
+        public LdapContext getJndiContext( LdapDN principalDn, String principal, byte[] credential, 
+            String authentication, String baseName ) throws NamingException
+        {
+            return null;
+        }
+
+
+        public void setInstanceId( String instanceId )
+        {
+
+        }
+
+
+        public String getInstanceId()
+        {
+            return null;
+        }
+
+
+        public Set<? extends Partition> getPartitions()
+        {
+            return null;
+        }
+
+
+        public void setPartitions( Set<? extends Partition> partitions )
+        {
+        }
+
+
+        public boolean isAccessControlEnabled()
+        {
+            return false;
+        }
+
+
+        public void setAccessControlEnabled( boolean accessControlEnabled )
+        {
+        }
+
+
+        public boolean isAllowAnonymousAccess()
+        {
+            return false;
+        }
+
+
+        public void setAllowAnonymousAccess( boolean enableAnonymousAccess )
+        {
+
+        }
+
+
+        public List<Interceptor> getInterceptors()
+        {
+            return null;
+        }
+
+
+        public void setInterceptors( List<Interceptor> interceptors )
+        {
+
+        }
+
+
+        public List<LdifEntry> getTestEntries()
+        {
+            return null;
+        }
+
+
+        public void setTestEntries( List<? extends LdifEntry> testEntries )
+        {
+        }
+
+
+        public File getWorkingDirectory()
+        {
+            return null;
+        }
+
+
+        public void setWorkingDirectory( File workingDirectory )
+        {
+        }
+
+
+        public void validate()
+        {
+        }
+
+
+        public void setShutdownHookEnabled( boolean shutdownHookEnabled )
+        {
+
+        }
+
+
+        public boolean isShutdownHookEnabled()
+        {
+            return false;
+        }
+
+
+        public void setExitVmOnShutdown( boolean exitVmOnShutdown )
+        {
+
+        }
+
+
+        public boolean isExitVmOnShutdown()
+        {
+            return false;
+        }
+
+
+        public void setMaxSizeLimit( int maxSizeLimit )
+        {
+
+        }
+
+
+        public int getMaxSizeLimit()
+        {
+            return 0;
+        }
+
+
+        public void setMaxTimeLimit( int maxTimeLimit )
+        {
+
+        }
+
+
+        public int getMaxTimeLimit()
+        {
+            return 0;
+        }
+
+
+        public void setSystemPartition( Partition systemPartition )
+        {
+
+        }
+
+
+        public Partition getSystemPartition()
+        {
+            return null;
+        }
+
+
+        public boolean isDenormalizeOpAttrsEnabled()
+        {
+            return false;
+        }
+
+
+        public void setDenormalizeOpAttrsEnabled( boolean denormalizeOpAttrsEnabled )
+        {
+
+        }
+        
+        public void setChangeLog( ChangeLog changeLog )
+        {
+            
+        }
+        
+        public ChangeLog getChangeLog()
+        {
+            return null;
+        }
+
+
+        public ServerEntry newEntry( LdapDN dn ) throws NamingException
+        {
+            return null;
+        }
+        
+        public ServerEntry newEntry( String ldif, String dn )
+        {
+            return null;
+        }
+    }
+
+    class BogusEnumeration implements NamingEnumeration
+    {
+        final int count;
+        int ii;
+
+
+        public BogusEnumeration(int count)
+        {
+            this.count = count;
+        }
+
+
+        public Object next() throws NamingException
+        {
+            if ( ii >= count )
+            {
+                throw new NoSuchElementException();
+            }
+
+            ii++;
+            
+            return new Object();
+        }
+
+
+        public boolean hasMore() throws NamingException
+        {
+            return ii < count;
+        }
+
+
+        public void close() throws NamingException
+        {
+            ii = count;
+        }
+
+
+        public boolean hasMoreElements()
+        {
+            return ii < count;
+        }
+
+
+        public Object nextElement()
+        {
+            if ( ii >= count )
+            {
+                throw new NoSuchElementException();
+            }
+
+            ii++;
+            
+            return new Object();
+        }
+    }
+}
diff --git a/old_trunk/core/src/test/java/org/apache/directory/server/core/authz/support/MaxValueCountFilterTest.java b/old_trunk/core/src/test/java/org/apache/directory/server/core/authz/support/MaxValueCountFilterTest.java
new file mode 100644
index 0000000..b01f715
--- /dev/null
+++ b/old_trunk/core/src/test/java/org/apache/directory/server/core/authz/support/MaxValueCountFilterTest.java
@@ -0,0 +1,161 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.authz.support;
+
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.naming.NamingException;
+
+
+import org.apache.directory.server.core.DefaultDirectoryService;
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.authz.support.MaxValueCountFilter;
+import org.apache.directory.server.core.authz.support.OperationScope;
+import org.apache.directory.server.core.entry.DefaultServerEntry;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.shared.ldap.aci.ACITuple;
+import org.apache.directory.shared.ldap.aci.MicroOperation;
+import org.apache.directory.shared.ldap.aci.ProtectedItem;
+import org.apache.directory.shared.ldap.aci.UserClass;
+import org.apache.directory.shared.ldap.aci.ProtectedItem.MaxValueCountItem;
+import org.apache.directory.shared.ldap.constants.AuthenticationLevel;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+
+/**
+ * Tests {@link MaxValueCountFilter}.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class MaxValueCountFilterTest
+{
+    private static final Collection<ACITuple> EMPTY_ACI_TUPLE_COLLECTION = Collections.unmodifiableCollection( new ArrayList<ACITuple>() );
+    private static final Collection<UserClass> EMPTY_USER_CLASS_COLLECTION = Collections.unmodifiableCollection( new ArrayList<UserClass>() );
+    private static final Collection<ProtectedItem> EMPTY_PROTECTED_ITEM_COLLECTION = Collections.unmodifiableCollection( new ArrayList<ProtectedItem>() );
+
+    private static final Set<MicroOperation> EMPTY_MICRO_OPERATION_SET = Collections.unmodifiableSet( new HashSet<MicroOperation>() );
+
+    private static final Collection<ProtectedItem> PROTECTED_ITEMS = new ArrayList<ProtectedItem>();
+    private static ServerEntry ENTRY;
+    private static ServerEntry FULL_ENTRY;
+
+    static
+    {
+        Collection<MaxValueCountItem> mvcItems = new ArrayList<MaxValueCountItem>();
+        mvcItems.add( new MaxValueCountItem( "cn", 2 ) );
+        PROTECTED_ITEMS.add( new ProtectedItem.MaxValueCount( mvcItems ) );
+    }
+
+
+    /** A reference to the directory service */
+    private static DirectoryService service;
+
+    
+    @BeforeClass public static void init() throws NamingException
+    {
+        service = new DefaultDirectoryService();
+    }
+    
+    @Before public void setup() throws NamingException
+    {
+        LdapDN entryName = new LdapDN( "ou=test, ou=system" );
+        ENTRY = new DefaultServerEntry( service.getRegistries(), entryName );
+        FULL_ENTRY = new DefaultServerEntry( service.getRegistries(), entryName );
+        
+        ENTRY.put( "cn", "1" );
+        FULL_ENTRY.put( "cn", "1", "2", "3" );
+    }
+
+
+    @Test public void testWrongScope() throws Exception
+    {
+        MaxValueCountFilter filter = new MaxValueCountFilter();
+        Collection<ACITuple> tuples = new ArrayList<ACITuple>();
+        tuples.add( new ACITuple( EMPTY_USER_CLASS_COLLECTION, AuthenticationLevel.NONE, EMPTY_PROTECTED_ITEM_COLLECTION, 
+            EMPTY_MICRO_OPERATION_SET, true, 0 ) );
+
+        tuples = Collections.unmodifiableCollection( tuples );
+
+        assertEquals( tuples, filter.filter( null, tuples, OperationScope.ATTRIBUTE_TYPE, null, null, null, null,
+            null, null, null, null, null, null, null ) );
+
+        assertEquals( tuples, filter.filter( null, tuples, OperationScope.ENTRY, null, null, null, null, null, null,
+            null, null, null, null, null ) );
+    }
+
+
+    @Test public void testZeroTuple() throws Exception
+    {
+        MaxValueCountFilter filter = new MaxValueCountFilter();
+
+        assertEquals( 0, filter.filter( null, EMPTY_ACI_TUPLE_COLLECTION, OperationScope.ATTRIBUTE_TYPE_AND_VALUE, 
+            null, null, null, null, null, null, null, null, null, null, null ).size() );
+    }
+
+
+    @Test public void testDenialTuple() throws Exception
+    {
+        MaxValueCountFilter filter = new MaxValueCountFilter();
+        Collection<ACITuple> tuples = new ArrayList<ACITuple>();
+        tuples.add( new ACITuple( EMPTY_USER_CLASS_COLLECTION, AuthenticationLevel.NONE, PROTECTED_ITEMS, 
+            EMPTY_MICRO_OPERATION_SET, false, 0 ) );
+
+        tuples = Collections.unmodifiableCollection( tuples );
+
+        assertEquals( tuples, filter.filter( null, tuples, OperationScope.ATTRIBUTE_TYPE_AND_VALUE, null, null, null,
+            null, null, null, "cn", null, ENTRY, null, null ) );
+        assertEquals( tuples, filter.filter( null, tuples, OperationScope.ATTRIBUTE_TYPE_AND_VALUE, null, null, null,
+            null, null, null, "cn", null, FULL_ENTRY, null, null ) );
+    }
+
+
+    @Test public void testGrantTuple() throws Exception
+    {
+        MaxValueCountFilter filter = new MaxValueCountFilter();
+        Collection<ACITuple> tuples = new ArrayList<ACITuple>();
+        
+        // Test with this ACI :
+        // 
+        tuples.add( new ACITuple( 
+            EMPTY_USER_CLASS_COLLECTION, 
+            AuthenticationLevel.NONE, 
+            PROTECTED_ITEMS, 
+            EMPTY_MICRO_OPERATION_SET, 
+            true, 
+            0 ) );
+
+        assertEquals( 1, filter.filter( null, tuples, OperationScope.ATTRIBUTE_TYPE_AND_VALUE, null, null, null, null,
+            null, null, "cn", null, ENTRY, null, ENTRY ).size() );
+
+        assertEquals( 0, filter.filter( null, tuples, OperationScope.ATTRIBUTE_TYPE_AND_VALUE, null, null, null, null,
+            null, null, "cn", null, FULL_ENTRY, null, FULL_ENTRY ).size() );
+    }
+}
diff --git a/old_trunk/core/src/test/java/org/apache/directory/server/core/authz/support/MicroOperationFilterTest.java b/old_trunk/core/src/test/java/org/apache/directory/server/core/authz/support/MicroOperationFilterTest.java
new file mode 100644
index 0000000..af8361b
--- /dev/null
+++ b/old_trunk/core/src/test/java/org/apache/directory/server/core/authz/support/MicroOperationFilterTest.java
@@ -0,0 +1,91 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.authz.support;
+
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import org.apache.directory.server.core.authz.support.MicroOperationFilter;
+import org.apache.directory.server.core.authz.support.OperationScope;
+import org.apache.directory.shared.ldap.aci.ACITuple;
+import org.apache.directory.shared.ldap.aci.MicroOperation;
+import org.apache.directory.shared.ldap.aci.ProtectedItem;
+import org.apache.directory.shared.ldap.aci.UserClass;
+import org.apache.directory.shared.ldap.constants.AuthenticationLevel;
+
+
+/**
+ * Tests {@link MicroOperationFilter}.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class MicroOperationFilterTest extends TestCase
+{
+    private static final Collection<ACITuple> EMPTY_ACI_TUPLE_COLLECTION = Collections.unmodifiableCollection( new ArrayList<ACITuple>() );
+    private static final Collection<UserClass> EMPTY_USER_CLASS_COLLECTION = Collections.unmodifiableCollection( new ArrayList<UserClass>() );
+    private static final Collection<ProtectedItem> EMPTY_PROTECTED_ITEM_COLLECTION = Collections.unmodifiableCollection( new ArrayList<ProtectedItem>() );
+
+    private static final Set<MicroOperation> USER_OPERATIONS_A = new HashSet<MicroOperation>();
+    private static final Set<MicroOperation> USER_OPERATIONS_B = new HashSet<MicroOperation>();
+    private static final Set<MicroOperation> TUPLE_OPERATIONS = new HashSet<MicroOperation>();
+
+    static
+    {
+        USER_OPERATIONS_A.add( MicroOperation.ADD );
+        USER_OPERATIONS_A.add( MicroOperation.BROWSE );
+        USER_OPERATIONS_B.add( MicroOperation.COMPARE );
+        USER_OPERATIONS_B.add( MicroOperation.DISCLOSE_ON_ERROR );
+        TUPLE_OPERATIONS.add( MicroOperation.ADD );
+        TUPLE_OPERATIONS.add( MicroOperation.BROWSE );
+        TUPLE_OPERATIONS.add( MicroOperation.EXPORT );
+    }
+
+
+    public void testZeroTuple() throws Exception
+    {
+        MicroOperationFilter filter = new MicroOperationFilter();
+
+        Assert.assertEquals( 0, filter.filter( null, EMPTY_ACI_TUPLE_COLLECTION, OperationScope.ATTRIBUTE_TYPE_AND_VALUE, 
+            null, null, null, null, null, null, null, null, null, null, null ).size() );
+    }
+
+
+    public void testOneTuple() throws Exception
+    {
+        MicroOperationFilter filter = new MicroOperationFilter();
+        Collection<ACITuple> tuples = new ArrayList<ACITuple>();
+        
+        tuples.add( new ACITuple( EMPTY_USER_CLASS_COLLECTION, AuthenticationLevel.NONE, EMPTY_PROTECTED_ITEM_COLLECTION, 
+            TUPLE_OPERATIONS, true, 0 ) );
+
+        Assert.assertEquals( 1, filter.filter( null, tuples, OperationScope.ENTRY, null, null, null, null, null, null, null,
+            null, null, USER_OPERATIONS_A, null ).size() );
+        Assert.assertEquals( 0, filter.filter( null, tuples, OperationScope.ENTRY, null, null, null, null, null, null, null,
+            null, null, USER_OPERATIONS_B, null ).size() );
+    }
+}
diff --git a/old_trunk/core/src/test/java/org/apache/directory/server/core/authz/support/MostSpecificProtectedItemFilterTest.java b/old_trunk/core/src/test/java/org/apache/directory/server/core/authz/support/MostSpecificProtectedItemFilterTest.java
new file mode 100644
index 0000000..c937e92
--- /dev/null
+++ b/old_trunk/core/src/test/java/org/apache/directory/server/core/authz/support/MostSpecificProtectedItemFilterTest.java
@@ -0,0 +1,231 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.authz.support;
+
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.naming.directory.Attribute;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import org.apache.directory.server.core.authz.support.MostSpecificProtectedItemFilter;
+import org.apache.directory.server.core.authz.support.OperationScope;
+import org.apache.directory.shared.ldap.aci.ACITuple;
+import org.apache.directory.shared.ldap.aci.MicroOperation;
+import org.apache.directory.shared.ldap.aci.ProtectedItem;
+import org.apache.directory.shared.ldap.aci.UserClass;
+import org.apache.directory.shared.ldap.constants.AuthenticationLevel;
+import org.apache.directory.shared.ldap.filter.PresenceNode;
+
+
+/**
+ * Tests {@link MostSpecificProtectedItemFilter}.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class MostSpecificProtectedItemFilterTest extends TestCase
+{
+    private static final Collection<String> EMPTY_STRING_COLLECTION = Collections.unmodifiableCollection( new ArrayList<String>() );
+    
+    private static final Collection<Attribute> EMPTY_ATTRIBUTE_COLLECTION =
+        Collections.unmodifiableCollection( new ArrayList<Attribute>() );
+    
+    private static final Collection<UserClass> EMPTY_USER_CLASS_COLLECTION =
+        Collections.unmodifiableCollection( new ArrayList<UserClass>() );
+    
+    private static final Collection<ACITuple> EMPTY_ACI_TUPLE_COLLECTION =
+        Collections.unmodifiableCollection( new ArrayList<ACITuple>() );
+    
+    private static final Collection<ProtectedItem> EMPTY_PROTECTED_ITEM_COLLECTION =
+        Collections.unmodifiableCollection( new ArrayList<ProtectedItem>() );
+    
+    private static final Set<MicroOperation> EMPTY_MICRO_OPERATION_SET =
+         Collections.unmodifiableSet( new HashSet<MicroOperation>() );
+    
+    private static final List<ACITuple> TUPLES_A = new ArrayList<ACITuple>();
+    private static final List<ACITuple> TUPLES_B = new ArrayList<ACITuple>();
+    private static final List<ACITuple> TUPLES_C = new ArrayList<ACITuple>();
+    private static final List<ACITuple> TUPLES_D = new ArrayList<ACITuple>();
+    private static final List<ACITuple> TUPLES_E = new ArrayList<ACITuple>();
+
+    static
+    {
+        Collection<ProtectedItem> attributeType = new ArrayList<ProtectedItem>();
+        Collection<ProtectedItem> allAttributeValues = new ArrayList<ProtectedItem>();
+        Collection<ProtectedItem> selfValue = new ArrayList<ProtectedItem>();
+        Collection<ProtectedItem> attributeValue = new ArrayList<ProtectedItem>();
+        Collection<ProtectedItem> rangeOfValues = new ArrayList<ProtectedItem>();
+        Collection<ProtectedItem> allUserAttributeTypes = new ArrayList<ProtectedItem>();
+        Collection<ProtectedItem> allUserAttributeTypesAndValues = new ArrayList<ProtectedItem>();
+
+        attributeType.add( new ProtectedItem.AttributeType( EMPTY_STRING_COLLECTION ) );
+        allAttributeValues.add( new ProtectedItem.AllAttributeValues( EMPTY_STRING_COLLECTION ) );
+        selfValue.add( new ProtectedItem.SelfValue( EMPTY_STRING_COLLECTION ) );
+        attributeValue.add( new ProtectedItem.AttributeValue( EMPTY_ATTRIBUTE_COLLECTION ) );
+        rangeOfValues.add( new ProtectedItem.RangeOfValues( new PresenceNode( "objectClass" ) ) );
+        allUserAttributeTypes.add( ProtectedItem.ALL_USER_ATTRIBUTE_TYPES );
+        allUserAttributeTypesAndValues.add( ProtectedItem.ALL_USER_ATTRIBUTE_TYPES_AND_VALUES );
+
+        ACITuple attributeTypeTuple = new ACITuple( EMPTY_USER_CLASS_COLLECTION, AuthenticationLevel.NONE, attributeType,
+            EMPTY_MICRO_OPERATION_SET, true, 0 );
+        
+        ACITuple allAttributeValuesTuple = new ACITuple( EMPTY_USER_CLASS_COLLECTION, AuthenticationLevel.NONE,
+            allAttributeValues, EMPTY_MICRO_OPERATION_SET, true, 0 );
+        
+        ACITuple selfValueTuple = new ACITuple( EMPTY_USER_CLASS_COLLECTION, AuthenticationLevel.NONE, selfValue, 
+                EMPTY_MICRO_OPERATION_SET, true, 0 );
+        
+        ACITuple attributeValueTuple = new ACITuple( EMPTY_USER_CLASS_COLLECTION, AuthenticationLevel.NONE, attributeValue,
+                EMPTY_MICRO_OPERATION_SET, true, 0 );
+        
+        ACITuple rangeOfValuesTuple = new ACITuple( EMPTY_USER_CLASS_COLLECTION, AuthenticationLevel.NONE, rangeOfValues,
+                EMPTY_MICRO_OPERATION_SET, true, 0 );
+        
+        ACITuple allUserAttributeTypesTuple = new ACITuple( EMPTY_USER_CLASS_COLLECTION, AuthenticationLevel.NONE,
+            allUserAttributeTypes, EMPTY_MICRO_OPERATION_SET, true, 0 );
+        
+        ACITuple allUserAttributeTypesAndValuesTuple = new ACITuple( EMPTY_USER_CLASS_COLLECTION, AuthenticationLevel.NONE,
+            allUserAttributeTypesAndValues, EMPTY_MICRO_OPERATION_SET, true, 0 );
+
+        TUPLES_A.add( attributeTypeTuple );
+        TUPLES_A.add( allAttributeValuesTuple );
+        TUPLES_A.add( selfValueTuple );
+        TUPLES_A.add( attributeValueTuple );
+        TUPLES_A.add( rangeOfValuesTuple );
+        TUPLES_A.add( allUserAttributeTypesTuple );
+        TUPLES_A.add( allUserAttributeTypesAndValuesTuple );
+
+        TUPLES_B.add( allAttributeValuesTuple );
+        TUPLES_B.add( selfValueTuple );
+        TUPLES_B.add( attributeValueTuple );
+        TUPLES_B.add( rangeOfValuesTuple );
+        TUPLES_B.add( allUserAttributeTypesTuple );
+        TUPLES_B.add( allUserAttributeTypesAndValuesTuple );
+
+        TUPLES_C.add( selfValueTuple );
+        TUPLES_C.add( attributeValueTuple );
+        TUPLES_C.add( rangeOfValuesTuple );
+        TUPLES_C.add( allUserAttributeTypesTuple );
+        TUPLES_C.add( allUserAttributeTypesAndValuesTuple );
+
+        TUPLES_D.add( attributeValueTuple );
+        TUPLES_D.add( rangeOfValuesTuple );
+        TUPLES_D.add( allUserAttributeTypesTuple );
+        TUPLES_D.add( allUserAttributeTypesAndValuesTuple );
+
+        TUPLES_E.add( allUserAttributeTypesTuple );
+        TUPLES_E.add( allUserAttributeTypesAndValuesTuple );
+    }
+
+
+    public void testZeroOrOneTuple() throws Exception
+    {
+        MostSpecificProtectedItemFilter filter = new MostSpecificProtectedItemFilter();
+
+        Assert.assertEquals( 0, filter.filter( null, EMPTY_ACI_TUPLE_COLLECTION, OperationScope.ATTRIBUTE_TYPE_AND_VALUE, null, null,
+            null, null, null, null, null, null, null, null, null ).size() );
+
+        Collection<ACITuple> tuples = new ArrayList<ACITuple>();
+        tuples.add( new ACITuple( EMPTY_USER_CLASS_COLLECTION, AuthenticationLevel.NONE, EMPTY_PROTECTED_ITEM_COLLECTION, EMPTY_MICRO_OPERATION_SET, false, 0 ) );
+
+        Assert.assertEquals( 1, filter.filter( null, tuples, OperationScope.ATTRIBUTE_TYPE_AND_VALUE, null, null, null, null,
+            null, null, null, null, null, null, null ).size() );
+    }
+
+
+    public void testTuplesA() throws Exception
+    {
+        MostSpecificProtectedItemFilter filter = new MostSpecificProtectedItemFilter();
+
+        List<ACITuple> tuples = new ArrayList<ACITuple>( TUPLES_A );
+        tuples = ( List<ACITuple> ) filter.filter( null, tuples, OperationScope.ENTRY, null, null, null, null, null, null, null, null,
+            null, null, null );
+
+        Assert.assertEquals( 4, tuples.size() );
+        Assert.assertSame( TUPLES_A.get( 0 ), tuples.get( 0 ) );
+        Assert.assertSame( TUPLES_A.get( 1 ), tuples.get( 1 ) );
+        Assert.assertSame( TUPLES_A.get( 2 ), tuples.get( 2 ) );
+        Assert.assertSame( TUPLES_A.get( 3 ), tuples.get( 3 ) );
+    }
+
+
+    public void testTuplesB() throws Exception
+    {
+        MostSpecificProtectedItemFilter filter = new MostSpecificProtectedItemFilter();
+
+        List<ACITuple> tuples = new ArrayList<ACITuple>( TUPLES_B );
+        tuples = ( List<ACITuple> ) filter.filter( null, tuples, OperationScope.ENTRY, null, null, null, null, null, null, null, null,
+            null, null, null );
+
+        Assert.assertEquals( 3, tuples.size() );
+        Assert.assertSame( TUPLES_B.get( 0 ), tuples.get( 0 ) );
+        Assert.assertSame( TUPLES_B.get( 1 ), tuples.get( 1 ) );
+        Assert.assertSame( TUPLES_B.get( 2 ), tuples.get( 2 ) );
+    }
+
+
+    public void testTuplesC() throws Exception
+    {
+        MostSpecificProtectedItemFilter filter = new MostSpecificProtectedItemFilter();
+
+        List<ACITuple> tuples = new ArrayList<ACITuple>( TUPLES_C );
+        tuples = ( List<ACITuple> ) filter.filter( null, tuples, OperationScope.ENTRY, null, null, null, null, null, null, null, null,
+            null, null, null );
+
+        Assert.assertEquals( 2, tuples.size() );
+        Assert.assertSame( TUPLES_C.get( 0 ), tuples.get( 0 ) );
+        Assert.assertSame( TUPLES_C.get( 1 ), tuples.get( 1 ) );
+    }
+
+
+    public void testTuplesD() throws Exception
+    {
+        MostSpecificProtectedItemFilter filter = new MostSpecificProtectedItemFilter();
+
+        List<ACITuple> tuples = new ArrayList<ACITuple>( TUPLES_D );
+        tuples = ( List<ACITuple> ) filter.filter( null, tuples, OperationScope.ENTRY, null, null, null, null, null, null, null, null,
+            null, null, null );
+
+        Assert.assertEquals( 1, tuples.size() );
+        Assert.assertSame( TUPLES_D.get( 0 ), tuples.get( 0 ) );
+    }
+
+
+    public void testTuplesE() throws Exception
+    {
+        MostSpecificProtectedItemFilter filter = new MostSpecificProtectedItemFilter();
+
+        List<ACITuple> tuples = new ArrayList<ACITuple>( TUPLES_E );
+        tuples = ( List<ACITuple> ) filter.filter( null, tuples, OperationScope.ENTRY, null, null, null, null, null, null, null, null,
+            null, null, null );
+
+        Assert.assertEquals( 2, tuples.size() );
+        Assert.assertSame( TUPLES_E.get( 0 ), tuples.get( 0 ) );
+        Assert.assertSame( TUPLES_E.get( 1 ), tuples.get( 1 ) );
+    }
+}
diff --git a/old_trunk/core/src/test/java/org/apache/directory/server/core/authz/support/MostSpecificUserClassFilterTest.java b/old_trunk/core/src/test/java/org/apache/directory/server/core/authz/support/MostSpecificUserClassFilterTest.java
new file mode 100644
index 0000000..77424af
--- /dev/null
+++ b/old_trunk/core/src/test/java/org/apache/directory/server/core/authz/support/MostSpecificUserClassFilterTest.java
@@ -0,0 +1,188 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.authz.support;
+
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import org.apache.directory.server.core.authz.support.MostSpecificUserClassFilter;
+import org.apache.directory.server.core.authz.support.OperationScope;
+import org.apache.directory.shared.ldap.aci.ACITuple;
+import org.apache.directory.shared.ldap.aci.MicroOperation;
+import org.apache.directory.shared.ldap.aci.ProtectedItem;
+import org.apache.directory.shared.ldap.aci.UserClass;
+import org.apache.directory.shared.ldap.constants.AuthenticationLevel;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.subtree.SubtreeSpecification;
+
+
+/**
+ * Tests {@link MostSpecificUserClassFilter}.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class MostSpecificUserClassFilterTest extends TestCase
+{
+    private static final Set<LdapDN> EMPTY_NAME_SET = Collections.unmodifiableSet( new HashSet<LdapDN>() );
+    private static final Set<MicroOperation> EMPTY_MICRO_OPERATION_SET = Collections.unmodifiableSet( new HashSet<MicroOperation>() );
+    private static final Collection<UserClass> EMPTY_USER_CLASS_COLLECTION = Collections.unmodifiableCollection( new ArrayList<UserClass>() );
+    private static final Collection<SubtreeSpecification> EMPTY_SUBTREE_SPECIFICATION_COLLECTION = Collections.unmodifiableCollection( new ArrayList<SubtreeSpecification>() );
+    private static final Collection<ProtectedItem> EMPTY_PROTECTED_ITEM_COLLECTION = Collections.unmodifiableCollection( new ArrayList<ProtectedItem>() );
+    private static final Collection<ACITuple> EMPTY_ACI_TUPLE_COLLECTION = Collections.unmodifiableCollection( new ArrayList<ACITuple>() );
+
+    private static final List<ACITuple> TUPLES_A = new ArrayList<ACITuple>();
+    private static final List<ACITuple> TUPLES_B = new ArrayList<ACITuple>();
+    private static final List<ACITuple> TUPLES_C = new ArrayList<ACITuple>();
+    private static final List<ACITuple> TUPLES_D = new ArrayList<ACITuple>();
+    private static final List<ACITuple> TUPLES_E = new ArrayList<ACITuple>();
+
+    static
+    {
+        Collection<UserClass> name = new ArrayList<UserClass>();
+        Collection<UserClass> thisEntry = new ArrayList<UserClass>();
+        Collection<UserClass> userGroup = new ArrayList<UserClass>();
+        Collection<UserClass> subtree = new ArrayList<UserClass>();
+        Collection<UserClass> allUsers = new ArrayList<UserClass>();
+
+        name.add( new UserClass.Name( EMPTY_NAME_SET ) );
+        thisEntry.add( UserClass.THIS_ENTRY );
+        userGroup.add( new UserClass.UserGroup( EMPTY_NAME_SET ) );
+        subtree.add( new UserClass.Subtree( EMPTY_SUBTREE_SPECIFICATION_COLLECTION ) );
+        allUsers.add( UserClass.ALL_USERS );
+
+        ACITuple nameTuple = new ACITuple( name, AuthenticationLevel.NONE, EMPTY_PROTECTED_ITEM_COLLECTION, EMPTY_MICRO_OPERATION_SET, true, 0 );
+        ACITuple thisEntryTuple = new ACITuple( thisEntry, AuthenticationLevel.NONE, EMPTY_PROTECTED_ITEM_COLLECTION, EMPTY_MICRO_OPERATION_SET, true, 0 );
+        ACITuple userGroupTuple = new ACITuple( userGroup, AuthenticationLevel.NONE, EMPTY_PROTECTED_ITEM_COLLECTION, EMPTY_MICRO_OPERATION_SET, true, 0 );
+        ACITuple subtreeTuple = new ACITuple( subtree, AuthenticationLevel.NONE, EMPTY_PROTECTED_ITEM_COLLECTION, EMPTY_MICRO_OPERATION_SET, true, 0 );
+        ACITuple allUsersTuple = new ACITuple( allUsers, AuthenticationLevel.NONE, EMPTY_PROTECTED_ITEM_COLLECTION, EMPTY_MICRO_OPERATION_SET, true, 0 );
+
+        TUPLES_A.add( nameTuple );
+        TUPLES_A.add( thisEntryTuple );
+        TUPLES_A.add( userGroupTuple );
+        TUPLES_A.add( subtreeTuple );
+        TUPLES_A.add( allUsersTuple );
+
+        TUPLES_B.add( thisEntryTuple );
+        TUPLES_B.add( userGroupTuple );
+        TUPLES_B.add( subtreeTuple );
+        TUPLES_B.add( allUsersTuple );
+
+        TUPLES_C.add( userGroupTuple );
+        TUPLES_C.add( subtreeTuple );
+        TUPLES_C.add( allUsersTuple );
+
+        TUPLES_D.add( subtreeTuple );
+        TUPLES_D.add( allUsersTuple );
+
+        TUPLES_E.add( allUsersTuple );
+        TUPLES_E.add( allUsersTuple );
+    }
+
+
+    public void testZeroOrOneTuple() throws Exception
+    {
+        MostSpecificUserClassFilter filter = new MostSpecificUserClassFilter();
+
+        Assert.assertEquals( 0, filter.filter( null, EMPTY_ACI_TUPLE_COLLECTION, OperationScope.ATTRIBUTE_TYPE_AND_VALUE, null, null,
+            null, null, null, null, null, null, null, null, null ).size() );
+
+        Collection<ACITuple> tuples = new ArrayList<ACITuple>();
+        tuples.add( new ACITuple( EMPTY_USER_CLASS_COLLECTION, AuthenticationLevel.NONE, EMPTY_PROTECTED_ITEM_COLLECTION, EMPTY_MICRO_OPERATION_SET, false, 0 ) );
+
+        Assert.assertEquals( 1, filter.filter( null, tuples, OperationScope.ATTRIBUTE_TYPE_AND_VALUE, null, null, null, null,
+            null, null, null, null, null, null, null ).size() );
+    }
+
+
+    public void testNameAndThisEntry() throws Exception
+    {
+        MostSpecificUserClassFilter filter = new MostSpecificUserClassFilter();
+
+        List<ACITuple> tuples = new ArrayList<ACITuple>( TUPLES_A );
+        tuples = ( List<ACITuple> ) filter.filter( null, tuples, OperationScope.ENTRY, null, null, null, null, null, null, null, null,
+            null, null, null );
+
+        Assert.assertEquals( 2, tuples.size() );
+        Assert.assertSame( TUPLES_A.get( 0 ), tuples.get( 0 ) );
+        Assert.assertSame( TUPLES_A.get( 1 ), tuples.get( 1 ) );
+    }
+
+
+    public void testThisEntry() throws Exception
+    {
+        MostSpecificUserClassFilter filter = new MostSpecificUserClassFilter();
+
+        List<ACITuple> tuples = new ArrayList<ACITuple>( TUPLES_B );
+        tuples = ( List<ACITuple> ) filter.filter( null, tuples, OperationScope.ENTRY, null, null, null, null, null, null, null, null,
+            null, null, null );
+
+        Assert.assertEquals( 1, tuples.size() );
+        Assert.assertSame( TUPLES_B.get( 0 ), tuples.get( 0 ) );
+    }
+
+
+    public void testUserGroup() throws Exception
+    {
+        MostSpecificUserClassFilter filter = new MostSpecificUserClassFilter();
+
+        List<ACITuple> tuples = new ArrayList<ACITuple>( TUPLES_C );
+        tuples = ( List<ACITuple> ) filter.filter( null, tuples, OperationScope.ENTRY, null, null, null, null, null, null, null, null,
+            null, null, null );
+
+        Assert.assertEquals( 1, tuples.size() );
+        Assert.assertSame( TUPLES_C.get( 0 ), tuples.get( 0 ) );
+    }
+
+
+    public void testSubtree() throws Exception
+    {
+        MostSpecificUserClassFilter filter = new MostSpecificUserClassFilter();
+
+        List<ACITuple> tuples = new ArrayList<ACITuple>( TUPLES_D );
+        tuples = ( List<ACITuple> ) filter.filter( null, tuples, OperationScope.ENTRY, null, null, null, null, null, null, null, null,
+            null, null, null );
+
+        Assert.assertEquals( 1, tuples.size() );
+        Assert.assertSame( TUPLES_D.get( 0 ), tuples.get( 0 ) );
+    }
+
+
+    public void testOthers() throws Exception
+    {
+        MostSpecificUserClassFilter filter = new MostSpecificUserClassFilter();
+
+        List<ACITuple> tuples = new ArrayList<ACITuple>( TUPLES_E );
+        tuples = (List<ACITuple>)filter.filter( null, tuples, OperationScope.ENTRY, null, null, null, null, null, null, null, null,
+            null, null, null );
+
+        Assert.assertEquals( 2, tuples.size() );
+        Assert.assertSame( TUPLES_E.get( 0 ), tuples.get( 0 ) );
+        Assert.assertSame( TUPLES_E.get( 1 ), tuples.get( 1 ) );
+    }
+}
diff --git a/old_trunk/core/src/test/java/org/apache/directory/server/core/authz/support/OperationScopeTest.java b/old_trunk/core/src/test/java/org/apache/directory/server/core/authz/support/OperationScopeTest.java
new file mode 100644
index 0000000..a05c84d
--- /dev/null
+++ b/old_trunk/core/src/test/java/org/apache/directory/server/core/authz/support/OperationScopeTest.java
@@ -0,0 +1,53 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.authz.support;
+
+
+import org.apache.directory.server.core.authz.support.OperationScope;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+
+/**
+ * Tests {@link OperationScope}.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ *
+ */
+public class OperationScopeTest extends TestCase
+{
+    public void testGetName() throws Exception
+    {
+        Assert.assertEquals( "Entry", OperationScope.ENTRY.getName() );
+        Assert.assertEquals( "Attribute Type", OperationScope.ATTRIBUTE_TYPE.getName() );
+        Assert.assertEquals( "Attribute Type & Value", OperationScope.ATTRIBUTE_TYPE_AND_VALUE.getName() );
+    }
+
+
+    public void testGetNameAndToStringEquality()
+    {
+        Assert.assertEquals( OperationScope.ENTRY.getName(), OperationScope.ENTRY.toString() );
+        Assert.assertEquals( OperationScope.ATTRIBUTE_TYPE.getName(), OperationScope.ATTRIBUTE_TYPE.toString() );
+        Assert.assertEquals( OperationScope.ATTRIBUTE_TYPE_AND_VALUE.getName(), OperationScope.ATTRIBUTE_TYPE_AND_VALUE
+            .toString() );
+    }
+}
diff --git a/old_trunk/core/src/test/java/org/apache/directory/server/core/authz/support/RelatedProtectedItemFilterTest.java b/old_trunk/core/src/test/java/org/apache/directory/server/core/authz/support/RelatedProtectedItemFilterTest.java
new file mode 100644
index 0000000..2c674cd
--- /dev/null
+++ b/old_trunk/core/src/test/java/org/apache/directory/server/core/authz/support/RelatedProtectedItemFilterTest.java
@@ -0,0 +1,365 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.authz.support;
+
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+
+import org.apache.directory.server.core.DefaultDirectoryService;
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.authz.support.OperationScope;
+import org.apache.directory.server.core.authz.support.RelatedProtectedItemFilter;
+import org.apache.directory.server.core.authz.support.RelatedUserClassFilter;
+import org.apache.directory.server.core.entry.DefaultServerAttribute;
+import org.apache.directory.server.core.entry.DefaultServerEntry;
+import org.apache.directory.server.core.entry.ServerAttribute;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerEntryUtils;
+import org.apache.directory.server.core.event.ExpressionEvaluator;
+import org.apache.directory.server.core.subtree.RefinementEvaluator;
+import org.apache.directory.server.core.subtree.RefinementLeafEvaluator;
+import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
+import org.apache.directory.server.schema.registries.OidRegistry;
+import org.apache.directory.shared.ldap.aci.ACITuple;
+import org.apache.directory.shared.ldap.aci.MicroOperation;
+import org.apache.directory.shared.ldap.aci.ProtectedItem;
+import org.apache.directory.shared.ldap.aci.UserClass;
+import org.apache.directory.shared.ldap.aci.ProtectedItem.MaxValueCountItem;
+import org.apache.directory.shared.ldap.aci.ProtectedItem.RestrictedByItem;
+import org.apache.directory.shared.ldap.constants.AuthenticationLevel;
+import org.apache.directory.shared.ldap.entry.client.ClientStringValue;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests {@link RelatedUserClassFilter}.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class RelatedProtectedItemFilterTest
+{
+    private static final Collection<UserClass> EMPTY_USER_CLASS_COLLECTION = Collections.unmodifiableCollection( new ArrayList<UserClass>() );
+    private static final Collection<ACITuple> EMPTY_ACI_TUPLE_COLLECTION = Collections.unmodifiableCollection( new ArrayList<ACITuple>() );
+    private static final Set<MicroOperation> EMPTY_MICRO_OPERATION_SET = Collections.unmodifiableSet( new HashSet<MicroOperation>() );
+
+    private static LdapDN GROUP_NAME;
+    private static LdapDN USER_NAME;
+    private static Set<LdapDN> USER_NAMES = new HashSet<LdapDN>();
+    private static Set<LdapDN> GROUP_NAMES = new HashSet<LdapDN>();
+
+    private static final AttributeTypeRegistry ATTR_TYPE_REGISTRY_A = new DummyAttributeTypeRegistry( false );
+    private static final AttributeTypeRegistry ATTR_TYPE_REGISTRY_B = new DummyAttributeTypeRegistry( true );
+    private static OidRegistry OID_REGISTRY;
+
+    private static RelatedProtectedItemFilter filterA;
+    private static RelatedProtectedItemFilter filterB;
+
+    /** A reference to the directory service */
+    private static DirectoryService service;
+    
+    /** The CN attribute Type */
+    private static AttributeType CN_AT;
+
+    /** The SN attribute Type */
+    private static AttributeType SN_AT;
+
+    
+    @BeforeClass public static void setup() throws NamingException
+    {
+        service = new DefaultDirectoryService();
+        OID_REGISTRY = service.getRegistries().getOidRegistry();
+
+        GROUP_NAME = new LdapDN( "ou=test,ou=groups,ou=system" );
+        USER_NAME = new LdapDN( "ou=test, ou=users, ou=system" );
+
+        filterA = new RelatedProtectedItemFilter( new RefinementEvaluator( new RefinementLeafEvaluator(
+            OID_REGISTRY ) ), new ExpressionEvaluator( OID_REGISTRY, ATTR_TYPE_REGISTRY_A ), OID_REGISTRY, ATTR_TYPE_REGISTRY_A );
+
+        filterB = new RelatedProtectedItemFilter( new RefinementEvaluator( new RefinementLeafEvaluator(
+            OID_REGISTRY ) ), new ExpressionEvaluator( OID_REGISTRY, ATTR_TYPE_REGISTRY_B ), OID_REGISTRY, ATTR_TYPE_REGISTRY_B );
+
+        USER_NAMES.add( USER_NAME );
+        GROUP_NAMES.add( GROUP_NAME );
+        CN_AT = service.getRegistries().getAttributeTypeRegistry().lookup( "cn" );
+        SN_AT = service.getRegistries().getAttributeTypeRegistry().lookup( "sn" );
+    }
+
+    
+    private Collection<Attribute> convert( Collection<ServerAttribute> attributes )
+    {
+        Set<Attribute> jndiAttributes = new HashSet<Attribute>();
+        
+        for ( ServerAttribute attribute:attributes )
+        {
+            jndiAttributes.add( ServerEntryUtils.toAttributeImpl( attribute ) );
+        }
+        
+        return jndiAttributes;
+    }
+
+    @Test public void testZeroTuple() throws Exception
+    {
+        assertEquals( 0, filterA.filter( null, EMPTY_ACI_TUPLE_COLLECTION, OperationScope.ATTRIBUTE_TYPE_AND_VALUE, null, null,
+            null, null, null, null, null, null, null, null, null ).size() );
+    }
+
+
+    @Test public void testEntry() throws Exception
+    {
+        Collection<ACITuple> tuples = getTuples( ProtectedItem.ENTRY );
+
+        assertEquals( 1, filterA.filter( null, tuples, OperationScope.ENTRY, null, null, null, null,
+            AuthenticationLevel.NONE, null, "ou", null, null, null, null ).size() );
+    }
+
+
+    @Test public void testAllUserAttributeTypes() throws Exception
+    {
+        Collection<ACITuple> tuples = getTuples( ProtectedItem.ALL_USER_ATTRIBUTE_TYPES );
+
+        // Test wrong scope
+        assertEquals( 0, filterA.filter( null, tuples, OperationScope.ENTRY, null, null, USER_NAME, null, null, null,
+            "cn", null, null, null, null ).size() );
+
+        tuples = getTuples( ProtectedItem.ALL_USER_ATTRIBUTE_TYPES );
+
+        assertEquals( 1, filterA.filter( null, tuples, OperationScope.ATTRIBUTE_TYPE, null, null, USER_NAME, null,
+            null, null, "cn", null, null, null, null ).size() );
+    }
+
+
+    @Test public void testAllUserAttributeTypesAndValues() throws Exception
+    {
+        Collection<ACITuple> tuples = getTuples( ProtectedItem.ALL_USER_ATTRIBUTE_TYPES_AND_VALUES );
+
+        // Test wrong scope
+        assertEquals( 0, filterA.filter( null, tuples, OperationScope.ENTRY, null, null, USER_NAME, null, null, null,
+            "cn", null, null, null, null ).size() );
+
+        tuples = getTuples( ProtectedItem.ALL_USER_ATTRIBUTE_TYPES_AND_VALUES );
+
+        assertEquals( 1, filterA.filter( null, tuples, OperationScope.ATTRIBUTE_TYPE, null, null, USER_NAME, null,
+            null, null, "cn", null, null, null, null ).size() );
+    }
+
+
+    @Test public void testAllAttributeValues() throws Exception
+    {
+        Collection<String> attrTypes = new ArrayList<String>();
+        attrTypes.add( "cn" );
+        Collection<ACITuple> tuples = getTuples( new ProtectedItem.AllAttributeValues( attrTypes ) );
+
+        // Test wrong scope
+        assertEquals( 0, filterA.filter( null, tuples, OperationScope.ENTRY, null, null, USER_NAME, null, null, null,
+            "cn", null, null, null, null ).size() );
+
+        tuples = getTuples( new ProtectedItem.AllAttributeValues( attrTypes ) );
+
+        assertEquals( 1, filterA.filter( null, tuples, OperationScope.ATTRIBUTE_TYPE_AND_VALUE, null, null, USER_NAME, null,
+            null, null, "cn", null, null, null, null ).size() );
+
+        assertEquals( 0, filterB.filter( null, tuples, OperationScope.ATTRIBUTE_TYPE_AND_VALUE, null, null, USER_NAME, null,
+            null, null, "sn", null, null, null, null ).size() );
+    }
+
+
+    @Test public void testAttributeType() throws Exception
+    {
+        Collection<String> attrTypes = new ArrayList<String>();
+        attrTypes.add( "cn" );
+        Collection<ACITuple> tuples = getTuples( new ProtectedItem.AttributeType( attrTypes ) );
+
+        // Test wrong scope
+        assertEquals( 0, filterA.filter( null, tuples, OperationScope.ENTRY, null, null, USER_NAME, null, null, null,
+            "cn", null, null, null, null ).size() );
+
+        tuples = getTuples( new ProtectedItem.AttributeType( attrTypes ) );
+
+        assertEquals( 1, filterA.filter( null, tuples, OperationScope.ATTRIBUTE_TYPE, null, null, USER_NAME, null,
+            null, null, "cn", null, null, null, null ).size() );
+
+        assertEquals( 0, filterA.filter( null, tuples, OperationScope.ATTRIBUTE_TYPE, null, null, USER_NAME, null,
+            null, null, "sn", null, null, null, null ).size() );
+    }
+
+
+    @Test public void testAttributeValue() throws Exception
+    {
+        Collection<ServerAttribute> attributes = new ArrayList<ServerAttribute>();
+        attributes.add( new DefaultServerAttribute( "cn", CN_AT, "valueA" ) );
+        Collection<ACITuple> tuples = getTuples( new ProtectedItem.AttributeValue( convert( attributes ) ) );
+
+        // Test wrong scope
+        assertEquals( 0, filterA.filter( null, tuples, OperationScope.ENTRY, null, null, USER_NAME, null, null, null,
+            "cn", null, null, null, null ).size() );
+        tuples = getTuples( new ProtectedItem.AttributeValue( convert( attributes )  ) );
+        assertEquals( 0, filterA.filter( null, tuples, OperationScope.ATTRIBUTE_TYPE, null, null, USER_NAME, null,
+            null, null, "cn", null, null, null, null ).size() );
+
+        tuples = getTuples( new ProtectedItem.AttributeValue( convert( attributes )  ) );
+
+        assertEquals( 1, filterA.filter( null, tuples, OperationScope.ATTRIBUTE_TYPE_AND_VALUE, null, null, USER_NAME,
+            null, null, null, "cn", new ClientStringValue( "valueA" ), null, null, null ).size() );
+
+        assertEquals( 0, filterA.filter( null, tuples, OperationScope.ATTRIBUTE_TYPE_AND_VALUE, null, null, USER_NAME,
+            null, null, null, "cn", new ClientStringValue( "valueB" ), null, null, null ).size() );
+
+        tuples = getTuples( new ProtectedItem.AttributeValue( convert( attributes )  ) );
+
+        assertEquals( 0, filterA.filter( null, tuples, OperationScope.ATTRIBUTE_TYPE_AND_VALUE, null, null, USER_NAME,
+            null, null, null, "sn", new ClientStringValue( "valueA" ), null, null, null ).size() );
+    }
+
+
+    public void testClasses() throws Exception
+    {
+        // TODO I don't know how to test with Refinement yet.
+    }
+
+
+    @Test public void testMaxImmSub() throws Exception
+    {
+        Collection<ACITuple> tuples = getTuples( new ProtectedItem.MaxImmSub( 2 ) );
+
+        // Should always retain tuples.
+        assertEquals( 1, filterA.filter( null, tuples, OperationScope.ENTRY, null, null, USER_NAME, null, null, null,
+            "cn", null, null, null, null ).size() );
+    }
+
+
+    @Test public void testMaxValueCount() throws Exception
+    {
+        Collection<MaxValueCountItem> mvcItems = new ArrayList<MaxValueCountItem>();
+        mvcItems.add( new MaxValueCountItem( "cn", 3 ) );
+        Collection<ACITuple> tuples = getTuples( new ProtectedItem.MaxValueCount( mvcItems ) );
+
+        // Test wrong scope
+        assertEquals( 0, filterA.filter( null, tuples, OperationScope.ENTRY, null, null, USER_NAME, null, null, null,
+            "cn", null, null, null, null ).size() );
+        tuples = getTuples( new ProtectedItem.MaxValueCount( mvcItems ) );
+        assertEquals( 0, filterA.filter( null, tuples, OperationScope.ATTRIBUTE_TYPE, null, null, USER_NAME, null,
+            null, null, "cn", null, null, null, null ).size() );
+
+        tuples = getTuples( new ProtectedItem.MaxValueCount( mvcItems ) );
+
+        assertEquals( 1, filterA.filter( null, tuples, OperationScope.ATTRIBUTE_TYPE_AND_VALUE, null, null, USER_NAME,
+            null, null, null, "cn", null, null, null, null ).size() );
+
+        assertEquals( 0, filterA.filter( null, tuples, OperationScope.ATTRIBUTE_TYPE_AND_VALUE, null, null, USER_NAME,
+            null, null, null, "sn", null, null, null, null ).size() );
+    }
+
+
+    /* this test requires a real registry with real values or the dummy registry
+     * needs to be altered to contain some usable mock data.  This is a result of
+     * using the registry now in this operation.    
+     *
+    public void testRangeOfValues() throws Exception
+    {
+        ServerEntry entry = new DefaultServerEntry( service.getRegistries(), USER_NAME );
+        entry.put( "cn", "valueA" );
+        Collection<ACITuple> tuples = getTuples( new ProtectedItem.RangeOfValues( new PresenceNode( "cn" ) ) );
+
+        Assert.assertEquals( 1, filterA.filter( tuples, OperationScope.ENTRY, null, null, USER_NAME, null, null,
+            new LdapDN( "ou=testEntry" ), null, null, entry, null ).size() );
+
+        entry.remove( "cn" );
+        Assert.assertEquals( 0, filterA.filter( service.getRegistries(), tuples, OperationScope.ATTRIBUTE_TYPE_AND_VALUE, null, null, USER_NAME,
+            null, null, new LdapDN( "ou=testEntry" ), null, null, entry, null ).size() );
+    }
+    */
+
+
+    @Test public void testRestrictedBy() throws Exception
+    {
+        Collection<RestrictedByItem> rbItems = new ArrayList<RestrictedByItem>();
+        rbItems.add( new RestrictedByItem( "cn", "sn" ) );
+        Collection<ACITuple> tuples = getTuples( new ProtectedItem.RestrictedBy( rbItems ) );
+
+        // Test wrong scope
+        assertEquals( 0, filterA.filter( null, tuples, OperationScope.ENTRY, null, null, USER_NAME, null, null, null,
+            "cn", null, null, null, null ).size() );
+        tuples = getTuples( new ProtectedItem.RestrictedBy( rbItems ) );
+        assertEquals( 0, filterA.filter( null, tuples, OperationScope.ATTRIBUTE_TYPE, null, null, USER_NAME, null,
+            null, null, "cn", null, null, null, null ).size() );
+
+        tuples = getTuples( new ProtectedItem.RestrictedBy( rbItems ) );
+
+        assertEquals( 1, filterA.filter( null, tuples, OperationScope.ATTRIBUTE_TYPE_AND_VALUE, null, null, USER_NAME,
+            null, null, null, "cn", null, null, null, null ).size() );
+
+        assertEquals( 0, filterA.filter( null, tuples, OperationScope.ATTRIBUTE_TYPE_AND_VALUE, null, null, USER_NAME,
+            null, null, null, "sn", null, null, null, null ).size() );
+    }
+
+
+    @Test public void testSelfValue() throws Exception
+    {
+        Collection<String> attrTypes = new ArrayList<String>();
+        attrTypes.add( "cn" );
+        Collection<ACITuple> tuples = getTuples( new ProtectedItem.SelfValue( attrTypes ) );
+
+        ServerEntry entry = new DefaultServerEntry( service.getRegistries(), USER_NAME );
+        entry.put( "cn", USER_NAME.toNormName() );
+
+        // Test wrong scope
+        assertEquals( 0, filterA.filter( null, tuples, OperationScope.ENTRY, null, null, USER_NAME, null, null, null,
+            "cn", null, entry, null, null ).size() );
+
+        tuples = getTuples( new ProtectedItem.SelfValue( attrTypes ) );
+
+        assertEquals( 1, filterA.filter( null, tuples, OperationScope.ATTRIBUTE_TYPE_AND_VALUE, null, null, USER_NAME,
+            null, null, null, "cn", null, entry, null, null ).size() );
+
+        entry.removeAttributes( "cn" );
+        assertEquals( 0, filterA.filter( null, tuples, OperationScope.ATTRIBUTE_TYPE_AND_VALUE, null, null, USER_NAME,
+            null, null, null, "cn", null, entry, null, null ).size() );
+
+        tuples = getTuples( new ProtectedItem.SelfValue( attrTypes ) );
+        assertEquals( 0, filterA.filter( null, tuples, OperationScope.ATTRIBUTE_TYPE_AND_VALUE, null, null, USER_NAME,
+            null, null, null, "sn", null, entry, null, null ).size() );
+    }
+
+
+    private static Collection<ACITuple> getTuples( ProtectedItem protectedItem )
+    {
+        Collection<ProtectedItem> protectedItems = new ArrayList<ProtectedItem>();
+        protectedItems.add( protectedItem );
+
+        Collection<ACITuple> tuples = new ArrayList<ACITuple>();
+        tuples.add( new ACITuple( EMPTY_USER_CLASS_COLLECTION, AuthenticationLevel.NONE, protectedItems, EMPTY_MICRO_OPERATION_SET, true, 0 ) );
+
+        return tuples;
+    }
+}
diff --git a/old_trunk/core/src/test/java/org/apache/directory/server/core/authz/support/RelatedUserClassFilterTest.java b/old_trunk/core/src/test/java/org/apache/directory/server/core/authz/support/RelatedUserClassFilterTest.java
new file mode 100644
index 0000000..85e7d48
--- /dev/null
+++ b/old_trunk/core/src/test/java/org/apache/directory/server/core/authz/support/RelatedUserClassFilterTest.java
@@ -0,0 +1,213 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.authz.support;
+
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.naming.NamingException;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import org.apache.directory.server.core.authz.support.OperationScope;
+import org.apache.directory.server.core.authz.support.RelatedUserClassFilter;
+import org.apache.directory.server.core.subtree.SubtreeEvaluator;
+import org.apache.directory.shared.ldap.aci.ACITuple;
+import org.apache.directory.shared.ldap.aci.MicroOperation;
+import org.apache.directory.shared.ldap.aci.ProtectedItem;
+import org.apache.directory.shared.ldap.aci.UserClass;
+import org.apache.directory.shared.ldap.constants.AuthenticationLevel;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+
+/**
+ * Tests {@link RelatedUserClassFilter}.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class RelatedUserClassFilterTest extends TestCase
+{
+    private static final Collection<ACITuple> EMPTY_ACI_TUPLE_COLLECTION = Collections.unmodifiableCollection( new ArrayList<ACITuple>() );
+    private static final Collection<ProtectedItem> EMPTY_PROTECTED_ITEM_COLLECTION = Collections.unmodifiableCollection( new ArrayList<ProtectedItem>() );
+
+    private static final Set<MicroOperation> EMPTY_MICRO_OPERATION_SET = Collections.unmodifiableSet( new HashSet<MicroOperation>() );
+
+    private static final LdapDN GROUP_NAME;
+    private static final LdapDN USER_NAME;
+    private static final Set<LdapDN> USER_NAMES = new HashSet<LdapDN>();
+    private static final Set<LdapDN> GROUP_NAMES = new HashSet<LdapDN>();
+
+    private static final SubtreeEvaluator SUBTREE_EVALUATOR;
+
+    private static final RelatedUserClassFilter filter;
+
+    static
+    {
+        SUBTREE_EVALUATOR = new SubtreeEvaluator( new DummyOidRegistry(), new DummyAttributeTypeRegistry(true) );
+        filter = new RelatedUserClassFilter( SUBTREE_EVALUATOR );
+        
+        try
+        {
+            GROUP_NAME = new LdapDN( "ou=test,ou=groups,ou=system" );
+            USER_NAME = new LdapDN( "ou=test, ou=users, ou=system" );
+        }
+        catch ( NamingException e )
+        {
+            throw new Error();
+        }
+
+        USER_NAMES.add( USER_NAME );
+        GROUP_NAMES.add( GROUP_NAME );
+    }
+
+
+    public void testZeroTuple() throws Exception
+    {
+        Assert.assertEquals( 0, filter.filter( null, EMPTY_ACI_TUPLE_COLLECTION, OperationScope.ATTRIBUTE_TYPE_AND_VALUE, null, null,
+            null, null, null, null, null, null, null, null, null ).size() );
+    }
+
+
+    public void testAllUsers() throws Exception
+    {
+        Collection<ACITuple> tuples = getTuples( UserClass.ALL_USERS );
+
+        Assert.assertEquals( 1, filter.filter( null, tuples, OperationScope.ENTRY, null, null, null, null,
+            AuthenticationLevel.NONE, null, null, null, null, null, null ).size() );
+    }
+
+
+    public void testThisEntry() throws Exception
+    {
+        Collection<ACITuple> tuples = getTuples( UserClass.THIS_ENTRY );
+
+        Assert.assertEquals( 1, filter.filter( null, tuples, OperationScope.ENTRY, null, null, USER_NAME, null,
+            AuthenticationLevel.NONE, USER_NAME, null, null, null, null, null ).size() );
+        Assert.assertEquals( 0, filter.filter( null, tuples, OperationScope.ENTRY, null, null, USER_NAME, null,
+            AuthenticationLevel.NONE, new LdapDN( "ou=unrelated" ), null, null, null, null, null ).size() );
+    }
+
+
+    public void testName() throws Exception
+    {
+        Collection<ACITuple> tuples = getTuples( new UserClass.Name( USER_NAMES ) );
+        Assert.assertEquals( 1, filter.filter( null, tuples, OperationScope.ENTRY, null, null, USER_NAME, null,
+            AuthenticationLevel.NONE, null, null, null, null, null, null ).size() );
+        Assert.assertEquals( 0, filter.filter( null, tuples, OperationScope.ENTRY, null, null,
+            new LdapDN( "ou=unrelateduser, ou=users" ), null, AuthenticationLevel.NONE, USER_NAME, null, null, null,
+            null, null ).size() );
+    }
+
+
+    public void testUserGroup() throws Exception
+    {
+        Collection<ACITuple> tuples = getTuples( new UserClass.UserGroup( GROUP_NAMES ) );
+        Assert.assertEquals( 1, filter.filter( null, tuples, OperationScope.ENTRY, null, GROUP_NAMES, USER_NAME, null,
+            AuthenticationLevel.NONE, null, null, null, null, null, null ).size() );
+
+        Set<LdapDN> wrongGroupNames = new HashSet<LdapDN>();
+        wrongGroupNames.add( new LdapDN( "ou=unrelatedgroup" ) );
+
+        Assert.assertEquals( 0, filter.filter( null, tuples, OperationScope.ENTRY, null, wrongGroupNames, USER_NAME, null,
+            AuthenticationLevel.NONE, USER_NAME, null, null, null, null, null ).size() );
+    }
+
+
+    public void testSubtree() throws Exception
+    {
+        // TODO Don't know how to test yet.
+    }
+
+
+    public void testAuthenticationLevel() throws Exception
+    {
+        Collection<ACITuple> tuples = getTuples( AuthenticationLevel.SIMPLE, true );
+
+        Assert.assertEquals( 1, filter.filter( null, tuples, OperationScope.ENTRY, null, null, null, null,
+            AuthenticationLevel.STRONG, null, null, null, null, null, null ).size() );
+        Assert.assertEquals( 1, filter.filter( null, tuples, OperationScope.ENTRY, null, null, null, null,
+            AuthenticationLevel.SIMPLE, null, null, null, null, null, null ).size() );
+        Assert.assertEquals( 0, filter.filter( null, tuples, OperationScope.ENTRY, null, null, null, null,
+            AuthenticationLevel.NONE, null, null, null, null, null, null ).size() );
+
+        tuples = getTuples( AuthenticationLevel.SIMPLE, false );
+
+        Assert.assertEquals( 1, filter.filter( null, tuples, OperationScope.ENTRY, null, null, null, null,
+            AuthenticationLevel.NONE, null, null, null, null, null, null ).size() );
+
+        Assert.assertEquals( 0, filter.filter( null, tuples, OperationScope.ENTRY, null, null, null, null,
+            AuthenticationLevel.STRONG, null, null, null, null, null, null ).size() );
+
+        tuples = getTuples( AuthenticationLevel.SIMPLE, false );
+
+        Assert.assertEquals( 0, filter.filter( null, tuples, OperationScope.ENTRY, null, null, null, null,
+            AuthenticationLevel.SIMPLE, null, null, null, null, null, null ).size() );
+    }
+
+
+    private static Collection<ACITuple> getTuples( UserClass userClass )
+    {
+        Collection<UserClass> classes = new ArrayList<UserClass>();
+        classes.add( userClass );
+
+        Collection<ACITuple> tuples = new ArrayList<ACITuple>();
+        tuples.add( new ACITuple( classes, AuthenticationLevel.NONE, EMPTY_PROTECTED_ITEM_COLLECTION, 
+            EMPTY_MICRO_OPERATION_SET, true, 0 ) );
+
+        return tuples;
+    }
+
+
+    private static Collection<ACITuple> getTuples( AuthenticationLevel level, boolean grant )
+    {
+        Collection<UserClass> classes = new ArrayList<UserClass>();
+        
+        if ( grant )
+        {
+            classes.add( UserClass.ALL_USERS );
+        }
+        else
+        {
+            Set<LdapDN> names = new HashSet<LdapDN>();
+            
+            try
+            {
+                names.add( new LdapDN( "dummy=dummy" ) );
+            }
+            catch ( NamingException e )
+            {
+                throw new Error();
+            }
+
+            classes.add( new UserClass.Name( names ) );
+        }
+
+        Collection<ACITuple> tuples = new ArrayList<ACITuple>();
+        tuples.add( new ACITuple( classes, level, EMPTY_PROTECTED_ITEM_COLLECTION, EMPTY_MICRO_OPERATION_SET, grant, 0 ) );
+
+        return tuples;
+    }
+}
diff --git a/old_trunk/core/src/test/java/org/apache/directory/server/core/authz/support/RestrictedByFilterTest.java b/old_trunk/core/src/test/java/org/apache/directory/server/core/authz/support/RestrictedByFilterTest.java
new file mode 100644
index 0000000..5e5c7a0
--- /dev/null
+++ b/old_trunk/core/src/test/java/org/apache/directory/server/core/authz/support/RestrictedByFilterTest.java
@@ -0,0 +1,145 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.authz.support;
+
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.core.DefaultDirectoryService;
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.authz.support.OperationScope;
+import org.apache.directory.server.core.authz.support.RestrictedByFilter;
+import org.apache.directory.server.core.entry.DefaultServerEntry;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.shared.ldap.aci.ACITuple;
+import org.apache.directory.shared.ldap.aci.MicroOperation;
+import org.apache.directory.shared.ldap.aci.ProtectedItem;
+import org.apache.directory.shared.ldap.aci.UserClass;
+import org.apache.directory.shared.ldap.aci.ProtectedItem.RestrictedByItem;
+import org.apache.directory.shared.ldap.constants.AuthenticationLevel;
+import org.apache.directory.shared.ldap.entry.client.ClientStringValue;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+
+/**
+ * Tests {@link RestrictedByFilter}.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class RestrictedByFilterTest
+{
+    private static final Collection<UserClass> UC_EMPTY_COLLECTION = Collections.unmodifiableCollection( new ArrayList<UserClass>() );
+    private static final Collection<ACITuple> AT_EMPTY_COLLECTION = Collections.unmodifiableCollection( new ArrayList<ACITuple>() );
+    private static final Collection<ProtectedItem> PI_EMPTY_COLLECTION = Collections.unmodifiableCollection( new ArrayList<ProtectedItem>() );
+    private static final Set<MicroOperation> MO_EMPTY_SET = Collections.unmodifiableSet( new HashSet<MicroOperation>() );
+
+    private static final Collection<ProtectedItem> PROTECTED_ITEMS = new ArrayList<ProtectedItem>();
+    private static ServerEntry ENTRY;
+
+    static
+    {
+        Collection<RestrictedByItem> mvcItems = new ArrayList<RestrictedByItem>();
+        mvcItems.add( new RestrictedByItem( "sn", "cn" ) );
+        PROTECTED_ITEMS.add( new ProtectedItem.RestrictedBy( mvcItems ) );
+    }
+
+
+    /** A reference to the directory service */
+    private static DirectoryService service;
+
+    
+    @BeforeClass public static void setup() throws NamingException
+    {
+        service = new DefaultDirectoryService();
+
+        LdapDN entryName = new LdapDN( "ou=test, ou=system" );
+        PROTECTED_ITEMS.add( new ProtectedItem.MaxImmSub( 2 ) );
+        ENTRY = new DefaultServerEntry( service.getRegistries(), entryName );
+
+        ENTRY.put( "cn", "1", "2" );
+    }
+
+
+    @Test public void testWrongScope() throws Exception
+    {
+        RestrictedByFilter filter = new RestrictedByFilter();
+        Collection<ACITuple> tuples = new ArrayList<ACITuple>();
+        tuples.add( new ACITuple( UC_EMPTY_COLLECTION, AuthenticationLevel.NONE, PI_EMPTY_COLLECTION, MO_EMPTY_SET, true, 0 ) );
+
+        tuples = Collections.unmodifiableCollection( tuples );
+
+        assertEquals( tuples, filter.filter( null, tuples, OperationScope.ATTRIBUTE_TYPE, null, null, null, null,
+            null, null, null, null, null, null, null ) );
+
+        assertEquals( tuples, filter.filter( null, tuples, OperationScope.ENTRY, null, null, null, null, null, null,
+            null, null, null, null, null ) );
+    }
+
+
+    @Test public void testZeroTuple() throws Exception
+    {
+        RestrictedByFilter filter = new RestrictedByFilter();
+
+        assertEquals( 0, filter.filter( null, AT_EMPTY_COLLECTION, OperationScope.ATTRIBUTE_TYPE_AND_VALUE, null, null,
+            null, null, null, null, null, null, null, null, null ).size() );
+    }
+
+
+    @Test public void testDenialTuple() throws Exception
+    {
+        RestrictedByFilter filter = new RestrictedByFilter();
+        Collection<ACITuple> tuples = new ArrayList<ACITuple>();
+        tuples.add( new ACITuple( UC_EMPTY_COLLECTION, AuthenticationLevel.NONE, PROTECTED_ITEMS, MO_EMPTY_SET, false, 0 ) );
+
+        tuples = Collections.unmodifiableCollection( tuples );
+
+        assertEquals( tuples, filter.filter( null, tuples, OperationScope.ATTRIBUTE_TYPE_AND_VALUE, null, null, null,
+            null, null, null, "testAttr", null, ENTRY, null, null ) );
+    }
+
+
+    @Test public void testGrantTuple() throws Exception
+    {
+        RestrictedByFilter filter = new RestrictedByFilter();
+        Collection<ACITuple> tuples = new ArrayList<ACITuple>();
+        tuples.add( new ACITuple( UC_EMPTY_COLLECTION, AuthenticationLevel.NONE, PROTECTED_ITEMS, MO_EMPTY_SET, true, 0 ) );
+
+        assertEquals( 1, filter.filter( null, tuples, OperationScope.ATTRIBUTE_TYPE_AND_VALUE, null, null, null, null,
+            null, null, "sn", new ClientStringValue( "1" ), ENTRY, null, null ).size() );
+
+        assertEquals( 1, filter.filter( null, tuples, OperationScope.ATTRIBUTE_TYPE_AND_VALUE, null, null, null, null,
+            null, null, "sn", new ClientStringValue( "2" ), ENTRY, null, null ).size() );
+
+        assertEquals( 0, filter.filter( null, tuples, OperationScope.ATTRIBUTE_TYPE_AND_VALUE, null, null, null, null,
+            null, null, "sn", new ClientStringValue( "3" ), ENTRY, null, null ).size() );
+    }
+}
diff --git a/old_trunk/core/src/test/java/org/apache/directory/server/core/changelog/MemoryChangeLogStoreTest.java b/old_trunk/core/src/test/java/org/apache/directory/server/core/changelog/MemoryChangeLogStoreTest.java
new file mode 100644
index 0000000..c431121
--- /dev/null
+++ b/old_trunk/core/src/test/java/org/apache/directory/server/core/changelog/MemoryChangeLogStoreTest.java
@@ -0,0 +1,71 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements.  See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership.  The ASF licenses this file

+ * to you 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.

+ */

+package org.apache.directory.server.core.changelog;

+

+

+import junit.framework.TestCase;

+import org.apache.directory.server.core.authn.LdapPrincipal;

+import org.apache.directory.shared.ldap.ldif.ChangeType;

+import org.apache.directory.shared.ldap.ldif.LdifEntry;

+import org.apache.directory.shared.ldap.ldif.LdifUtils;

+import org.apache.directory.shared.ldap.name.LdapDN;

+

+import javax.naming.NamingException;

+

+

+/**

+ * Tests the MemoryChangeLogStore.

+ *

+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>

+ * @version $Rev$, $Date$

+ */

+public class MemoryChangeLogStoreTest extends TestCase

+{

+    MemoryChangeLogStore store;

+

+

+    public void setUp() throws Exception

+    {

+        super.setUp();

+        store = new MemoryChangeLogStore();

+    }

+

+

+    public void tearDown() throws Exception

+    {

+        super.tearDown();

+        store = null;

+    }

+

+

+    public void testLogCheckRevision() throws NamingException

+    {

+        assertEquals( "first revision is always 0", 0, store.getCurrentRevision() );

+

+        LdifEntry forward = new LdifEntry();

+        forward.setDn( "ou=system" );

+        forward.setChangeType( ChangeType.Add );

+        forward.putAttribute( "objectClass", "organizationalUnit" );

+        forward.putAttribute( "ou", "system" );

+

+        LdifEntry reverse = LdifUtils.reverseAdd( new LdapDN( forward.getDn() ) );

+        assertEquals( 1, store.log( new LdapPrincipal(), forward, reverse ) );

+        assertEquals( 1, store.getCurrentRevision() );

+    }

+}

diff --git a/old_trunk/core/src/test/java/org/apache/directory/server/core/interceptor/InterceptorChainTest.java b/old_trunk/core/src/test/java/org/apache/directory/server/core/interceptor/InterceptorChainTest.java
new file mode 100644
index 0000000..908f020
--- /dev/null
+++ b/old_trunk/core/src/test/java/org/apache/directory/server/core/interceptor/InterceptorChainTest.java
@@ -0,0 +1,555 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.interceptor;
+
+
+import junit.framework.TestCase;
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.authn.LdapPrincipal;
+import org.apache.directory.server.core.changelog.ChangeLog;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.interceptor.context.LookupOperationContext;
+import org.apache.directory.server.core.invocation.Invocation;
+import org.apache.directory.server.core.invocation.InvocationStack;
+import org.apache.directory.server.core.jndi.DeadContext;
+import org.apache.directory.server.core.partition.Partition;
+import org.apache.directory.server.core.partition.PartitionNexus;
+import org.apache.directory.server.core.partition.PartitionNexusProxy;
+import org.apache.directory.server.core.schema.SchemaOperationControl;
+import org.apache.directory.server.core.schema.SchemaService;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.ldif.LdifEntry;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+import javax.naming.Context;
+import javax.naming.NamingException;
+import javax.naming.ldap.LdapContext;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Set;
+
+
+/**
+ * Unit test cases for InterceptorChain methods which test bypass 
+ * instructions in the chain.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class InterceptorChainTest extends TestCase
+{
+    private static final int INTERCEPTOR_COUNT = 5;
+    private InterceptorChain chain;
+    List<MockInterceptor> interceptors = new ArrayList<MockInterceptor>( INTERCEPTOR_COUNT );
+
+    
+    public InterceptorChainTest()
+    {
+    }
+    
+    
+    protected void setUp() throws Exception
+    {
+        chain = new InterceptorChain();
+
+        for ( int ii = 0; ii < INTERCEPTOR_COUNT; ii++ )
+        {
+            MockInterceptor interceptor = new MockInterceptor();
+            interceptor.setTest( this );
+            interceptor.setName( Integer.toString( ii ) );
+            chain.addLast(interceptor);
+        }
+        
+    }
+
+
+    protected void tearDown() throws Exception
+    {
+        chain = null;
+        interceptors.clear();
+    }
+
+
+    public void testNoBypass() throws NamingException
+    {
+        LdapDN dn = new LdapDN( "ou=system" );
+        Context ctx = new DeadContext();
+        DirectoryService ds = new MockDirectoryService();
+        PartitionNexusProxy proxy = new PartitionNexusProxy( ctx, ds );
+        Invocation i = new Invocation( proxy, ctx, "lookup" );
+        InvocationStack.getInstance().push( i );
+
+        try
+        {
+            chain.lookup( new LookupOperationContext( ds.getRegistries(), dn ) );
+        }
+        catch ( Exception e )
+        {
+        }
+
+        assertEquals( INTERCEPTOR_COUNT, interceptors.size() );
+        for ( int ii = 0; ii < INTERCEPTOR_COUNT; ii++ )
+        {
+            assertEquals( Integer.toString( ii ), interceptors.get( ii ).getName() );
+        }
+    }
+
+
+    public void testSingleBypass() throws NamingException
+    {
+        LdapDN dn = new LdapDN( "ou=system" );
+        Context ctx = new DeadContext();
+        DirectoryService ds = new MockDirectoryService();
+        PartitionNexusProxy proxy = new PartitionNexusProxy( ctx, ds );
+        Invocation i = new Invocation( proxy, ctx, "lookup", Collections.singleton( "0" ) );
+        InvocationStack.getInstance().push( i );
+
+        try
+        {
+            chain.lookup( new LookupOperationContext( ds.getRegistries(), dn ) );
+        }
+        catch ( Exception e )
+        {
+        }
+
+        assertEquals( INTERCEPTOR_COUNT - 1, interceptors.size() );
+        for ( int ii = 1; ii < INTERCEPTOR_COUNT; ii++ )
+        {
+            assertEquals( Integer.toString( ii ), interceptors.get( ii - 1 ).getName() );
+        }
+    }
+
+
+    public void testAdjacentDoubleBypass() throws NamingException
+    {
+        LdapDN dn = new LdapDN( "ou=system" );
+        Context ctx = new DeadContext();
+        DirectoryService ds = new MockDirectoryService();
+        PartitionNexusProxy proxy = new PartitionNexusProxy( ctx, ds );
+        Set<String> bypass = new HashSet<String>();
+        bypass.add( "0" );
+        bypass.add( "1" );
+        Invocation i = new Invocation( proxy, ctx, "lookup", bypass );
+        InvocationStack.getInstance().push( i );
+
+        try
+        {
+            chain.lookup( new LookupOperationContext( ds.getRegistries(), dn ) );
+        }
+        catch ( Exception e )
+        {
+        }
+
+        assertEquals( INTERCEPTOR_COUNT - 2, interceptors.size() );
+        for ( int ii = 2; ii < INTERCEPTOR_COUNT; ii++ )
+        {
+            assertEquals( Integer.toString( ii ), interceptors.get( ii - 2 ).getName() );
+        }
+    }
+
+
+    public void testFrontAndBackDoubleBypass() throws NamingException
+    {
+        LdapDN dn = new LdapDN( "ou=system" );
+        Context ctx = new DeadContext();
+        DirectoryService ds = new MockDirectoryService();
+        PartitionNexusProxy proxy = new PartitionNexusProxy( ctx, ds );
+        Set<String> bypass = new HashSet<String>();
+        bypass.add( "0" );
+        bypass.add( "4" );
+        Invocation i = new Invocation( proxy, ctx, "lookup", bypass );
+        InvocationStack.getInstance().push( i );
+
+        try
+        {
+            chain.lookup( new LookupOperationContext( ds.getRegistries(), dn ) );
+        }
+        catch ( Exception e )
+        {
+        }
+
+        assertEquals( INTERCEPTOR_COUNT - 2, interceptors.size() );
+        assertEquals( "1", interceptors.get( 0 ).getName() );
+        assertEquals( "2", interceptors.get( 1 ).getName() );
+        assertEquals( "3", interceptors.get( 2 ).getName() );
+    }
+
+
+    public void testDoubleBypass() throws NamingException
+    {
+        LdapDN dn = new LdapDN( "ou=system" );
+        Context ctx = new DeadContext();
+        DirectoryService ds = new MockDirectoryService();
+        PartitionNexusProxy proxy = new PartitionNexusProxy( ctx, ds );
+        Set<String> bypass = new HashSet<String>();
+        bypass.add( "1" );
+        bypass.add( "3" );
+        Invocation i = new Invocation( proxy, ctx, "lookup", bypass );
+        InvocationStack.getInstance().push( i );
+
+        try
+        {
+            chain.lookup( new LookupOperationContext( ds.getRegistries(), dn ) );
+        }
+        catch ( Exception e )
+        {
+        }
+
+        assertEquals( INTERCEPTOR_COUNT - 2, interceptors.size() );
+        assertEquals( "0", interceptors.get( 0 ).getName() );
+        assertEquals( "2", interceptors.get( 1 ).getName() );
+        assertEquals( "4", interceptors.get( 2 ).getName() );
+    }
+
+
+    public void testCompleteBypass() throws NamingException
+    {
+        LdapDN dn = new LdapDN( "ou=system" );
+        Context ctx = new DeadContext();
+        DirectoryService ds = new MockDirectoryService();
+        PartitionNexusProxy proxy = new PartitionNexusProxy( ctx, ds );
+        Invocation i = new Invocation( proxy, ctx, "lookup", PartitionNexusProxy.BYPASS_ALL_COLLECTION );
+        InvocationStack.getInstance().push( i );
+
+        try
+        {
+            chain.lookup( new LookupOperationContext(ds.getRegistries(),  dn ) );
+        }
+        catch ( Exception e )
+        {
+        }
+
+        assertEquals( 0, interceptors.size() );
+    }
+
+    
+    class MockDirectoryService implements DirectoryService
+    {
+        public Hashtable<String, Object> getEnvironment()
+        {
+            return null;
+        }
+
+
+        public void setEnvironment( Hashtable<String, Object> environment )
+        {
+        }
+
+
+        public long revert( long revision ) throws NamingException
+        {
+            return 0;
+        }
+
+
+        public long revert() throws NamingException
+        {
+            return 0;
+        }
+
+
+        public PartitionNexus getPartitionNexus()
+        {
+            return null;
+        }
+
+
+        public InterceptorChain getInterceptorChain()
+        {
+            return null;
+        }
+
+
+        public void addPartition( Partition partition ) throws NamingException
+        {
+        }
+
+
+        public void removePartition( Partition partition ) throws NamingException
+        {
+        }
+
+
+        public Registries getRegistries()
+        {
+            return null;
+        }
+
+
+        public void setRegistries( Registries registries )
+        {
+        }
+
+
+        public SchemaService getSchemaService()
+        {
+            return null;
+        }
+
+
+        public void setSchemaService( SchemaService schemaService )
+        {
+
+        }
+
+
+        public SchemaOperationControl getSchemaManager()
+        {
+            return null;
+        }
+
+
+        public void setSchemaManager( SchemaOperationControl schemaManager )
+        {
+        }
+
+
+        public void startup() throws NamingException
+        {
+        }
+
+
+        public void shutdown() throws NamingException
+        {
+        }
+
+
+        public void sync() throws NamingException
+        {
+        }
+
+
+        public boolean isStarted()
+        {
+            return false;
+        }
+
+
+        public LdapContext getJndiContext() throws NamingException
+        {
+            return null;
+        }
+
+
+        public DirectoryService getDirectoryService()
+        {
+            return null;
+        }
+
+
+        public LdapContext getJndiContext( String baseName ) throws NamingException
+        {
+            return null;
+        }
+
+
+        public LdapContext getJndiContext( LdapPrincipal principal ) throws NamingException
+        {
+            return null;
+        }
+
+
+        public LdapContext getJndiContext( LdapPrincipal principal, String dn ) throws NamingException
+        {
+            return null;
+        }
+
+
+        public LdapContext getJndiContext( LdapDN principalDn, String principal, byte[] credential,
+            String authentication, String baseName ) throws NamingException
+        {
+            return null;
+        }
+
+
+        public void setInstanceId( String instanceId )
+        {
+        }
+
+
+        public String getInstanceId()
+        {
+            return null;
+        }
+
+
+        public Set<? extends Partition> getPartitions()
+        {
+            return null;
+        }
+
+
+        public void setPartitions( Set<? extends Partition> partitions )
+        {
+        }
+
+
+        public boolean isAccessControlEnabled()
+        {
+            return false;
+        }
+
+
+        public void setAccessControlEnabled( boolean accessControlEnabled )
+        {
+        }
+
+
+        public boolean isAllowAnonymousAccess()
+        {
+            return false;
+        }
+
+
+        public void setAllowAnonymousAccess( boolean enableAnonymousAccess )
+        {
+        }
+
+
+        public List<Interceptor> getInterceptors()
+        {
+            return null;
+        }
+
+
+        public void setInterceptors( List<Interceptor> interceptors )
+        {
+        }
+
+
+        public List<LdifEntry> getTestEntries()
+        {
+            return null;
+        }
+
+
+        public void setTestEntries( List<? extends LdifEntry> testEntries )
+        {
+        }
+
+
+        public File getWorkingDirectory()
+        {
+            return null;
+        }
+
+
+        public void setWorkingDirectory( File workingDirectory )
+        {
+        }
+
+
+        public void validate()
+        {
+        }
+
+
+        public void setShutdownHookEnabled( boolean shutdownHookEnabled )
+        {
+        }
+
+
+        public boolean isShutdownHookEnabled()
+        {
+            return false;
+        }
+
+
+        public void setExitVmOnShutdown( boolean exitVmOnShutdown )
+        {
+        }
+
+
+        public boolean isExitVmOnShutdown()
+        {
+            return false;
+        }
+
+
+        public void setMaxSizeLimit( int maxSizeLimit )
+        {
+        }
+
+
+        public int getMaxSizeLimit()
+        {
+            return 0;
+        }
+
+
+        public void setMaxTimeLimit( int maxTimeLimit )
+        {
+        }
+
+
+        public int getMaxTimeLimit()
+        {
+            return 0;
+        }
+
+
+        public void setSystemPartition( Partition systemPartition )
+        {
+        }
+
+
+        public Partition getSystemPartition()
+        {
+            return null;
+        }
+
+
+        public boolean isDenormalizeOpAttrsEnabled()
+        {
+            return false;
+        }
+
+
+        public void setDenormalizeOpAttrsEnabled( boolean denormalizeOpAttrsEnabled )
+        {
+        }
+        
+        public void setChangeLog( ChangeLog changeLog )
+        {
+            
+        }
+        
+        public ChangeLog getChangeLog()
+        {
+            return null;
+        }
+
+
+        public ServerEntry newEntry( LdapDN dn ) throws NamingException
+        {
+            return null;
+        }
+
+        
+        public ServerEntry newEntry( String ldif, String dn )
+        {
+            return null;
+        }
+    }
+}
diff --git a/old_trunk/core/src/test/java/org/apache/directory/server/core/interceptor/MockInterceptor.java b/old_trunk/core/src/test/java/org/apache/directory/server/core/interceptor/MockInterceptor.java
new file mode 100755
index 0000000..02dfa6e
--- /dev/null
+++ b/old_trunk/core/src/test/java/org/apache/directory/server/core/interceptor/MockInterceptor.java
@@ -0,0 +1,229 @@
+/*
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *   or more contributor license agreements.  See the NOTICE file
+ *   distributed with this work for additional information
+ *   regarding copyright ownership.  The ASF licenses this file
+ *   to you 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.
+ *
+ */
+package org.apache.directory.server.core.interceptor;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerSearchResult;
+import org.apache.directory.server.core.interceptor.context.AddContextPartitionOperationContext;
+import org.apache.directory.server.core.interceptor.context.AddOperationContext;
+import org.apache.directory.server.core.interceptor.context.BindOperationContext;
+import org.apache.directory.server.core.interceptor.context.CompareOperationContext;
+import org.apache.directory.server.core.interceptor.context.DeleteOperationContext;
+import org.apache.directory.server.core.interceptor.context.EntryOperationContext;
+import org.apache.directory.server.core.interceptor.context.GetMatchedNameOperationContext;
+import org.apache.directory.server.core.interceptor.context.GetRootDSEOperationContext;
+import org.apache.directory.server.core.interceptor.context.GetSuffixOperationContext;
+import org.apache.directory.server.core.interceptor.context.ListOperationContext;
+import org.apache.directory.server.core.interceptor.context.ListSuffixOperationContext;
+import org.apache.directory.server.core.interceptor.context.LookupOperationContext;
+import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
+import org.apache.directory.server.core.interceptor.context.MoveAndRenameOperationContext;
+import org.apache.directory.server.core.interceptor.context.MoveOperationContext;
+import org.apache.directory.server.core.interceptor.context.RemoveContextPartitionOperationContext;
+import org.apache.directory.server.core.interceptor.context.RenameOperationContext;
+import org.apache.directory.server.core.interceptor.context.SearchOperationContext;
+import org.apache.directory.server.core.interceptor.context.UnbindOperationContext;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import java.util.Iterator;
+
+
+public class MockInterceptor implements Interceptor
+{
+    InterceptorChainTest test;
+    String name;
+
+
+    public void setName( String name )
+    {
+        this.name = name;
+    }
+    
+    
+    public void setTest( InterceptorChainTest test )
+    {
+        this.test = test;
+    }
+    
+
+    public String getName()
+    {
+        return this.name;
+    }
+
+
+    public void init( DirectoryService directoryService )
+        throws NamingException
+    {
+    }
+
+
+    public void destroy()
+    {
+    }
+
+
+    public ServerEntry getRootDSE( NextInterceptor next, GetRootDSEOperationContext opContext ) throws NamingException
+    {
+        test.interceptors.add( this );
+        return next.getRootDSE( opContext );
+    }
+
+
+    public LdapDN getMatchedName ( NextInterceptor next, GetMatchedNameOperationContext opContext ) throws NamingException
+    {
+        test.interceptors.add( this );
+        return next.getMatchedName( opContext );
+    }
+
+
+    public LdapDN getSuffix ( NextInterceptor next, GetSuffixOperationContext opContext ) throws NamingException
+    {
+        test.interceptors.add( this );
+        return next.getSuffix( opContext );
+    }
+
+
+    public Iterator listSuffixes ( NextInterceptor next, ListSuffixOperationContext opContext ) throws NamingException
+    {
+        test.interceptors.add( this );
+        return next.listSuffixes( opContext );
+    }
+
+
+    public void addContextPartition( NextInterceptor next, AddContextPartitionOperationContext opContext )
+        throws NamingException
+    {
+        test.interceptors.add( this );
+        next.addContextPartition( opContext );
+    }
+
+
+    public void removeContextPartition( NextInterceptor next, RemoveContextPartitionOperationContext opContext ) throws NamingException
+    {
+        test.interceptors.add( this );
+        next.removeContextPartition( opContext );
+    }
+
+
+    public boolean compare( NextInterceptor next, CompareOperationContext opContext ) throws NamingException
+    {
+        test.interceptors.add( this );
+        return next.compare( opContext );
+    }
+
+
+    public void delete( NextInterceptor next, DeleteOperationContext opContext ) throws NamingException
+    {
+        test.interceptors.add( this );
+        next.delete( opContext );
+    }
+
+
+    public void add( NextInterceptor next, AddOperationContext opContext )
+        throws NamingException
+    {
+        test.interceptors.add( this );
+        next.add( opContext );
+    }
+
+
+    public void modify( NextInterceptor next, ModifyOperationContext opContext ) throws NamingException
+    {
+        test.interceptors.add( this );
+        next.modify( opContext );
+    }
+
+
+    public NamingEnumeration list( NextInterceptor next, ListOperationContext opContext ) throws NamingException
+    {
+        test.interceptors.add( this );
+        return next.list( opContext );
+    }
+
+
+    public NamingEnumeration<ServerSearchResult> search( NextInterceptor next, SearchOperationContext opContext ) throws NamingException
+    {
+        test.interceptors.add( this );
+        return next.search( opContext );
+    }
+
+
+    public ServerEntry lookup( NextInterceptor next, LookupOperationContext opContext ) throws NamingException
+    {
+        test.interceptors.add( this );
+        return next.lookup( opContext );
+    }
+
+
+    public boolean hasEntry( NextInterceptor next, EntryOperationContext opContext ) throws NamingException
+    {
+        test.interceptors.add( this );
+        return next.hasEntry( opContext );
+    }
+
+
+    public void rename( NextInterceptor next, RenameOperationContext opContext )
+        throws NamingException
+    {
+        test.interceptors.add( this );
+        next.rename( opContext );
+    }
+
+
+    public void move( NextInterceptor next, MoveOperationContext opContext ) throws NamingException
+    {
+        test.interceptors.add( this );
+        next.move( opContext );
+    }
+
+
+    public void moveAndRename( NextInterceptor next, MoveAndRenameOperationContext opContext )
+        throws NamingException
+    {
+        test.interceptors.add( this );
+        next.moveAndRename( opContext );
+    }
+
+
+    public void bind( NextInterceptor next, BindOperationContext opContext )
+    throws NamingException
+    {
+        test.interceptors.add( this );
+        next.bind( opContext );
+    }
+
+
+    public void unbind( NextInterceptor next, UnbindOperationContext opContext ) throws NamingException
+    {
+        test.interceptors.add( this );
+        next.unbind( opContext );
+    }
+
+
+    public String toString()
+    {
+        return name;
+    }
+}
\ No newline at end of file
diff --git a/old_trunk/core/src/test/java/org/apache/directory/server/core/jndi/LdapJndiPropertiesTest.java b/old_trunk/core/src/test/java/org/apache/directory/server/core/jndi/LdapJndiPropertiesTest.java
new file mode 100644
index 0000000..237185c
--- /dev/null
+++ b/old_trunk/core/src/test/java/org/apache/directory/server/core/jndi/LdapJndiPropertiesTest.java
@@ -0,0 +1,142 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.jndi;
+
+
+import java.util.Hashtable;
+
+import javax.naming.Context;
+
+import org.apache.commons.lang.ArrayUtils;
+import org.apache.directory.server.core.jndi.LdapJndiProperties;
+import org.apache.directory.shared.ldap.constants.AuthenticationLevel;
+import org.apache.directory.shared.ldap.exception.LdapConfigurationException;
+import org.apache.directory.shared.ldap.util.StringTools;
+
+import junit.framework.TestCase;
+
+
+/**
+ * Tests the LdapJndiProperties.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class LdapJndiPropertiesTest extends TestCase
+{
+    public void testEmptyEnv() throws Exception
+    {
+        try
+        {
+            LdapJndiProperties.getLdapJndiProperties( new Hashtable() );
+            fail( "should never get here" );
+        }
+        catch ( LdapConfigurationException e )
+        {
+        }
+    }
+
+
+    public void testNullEnv() throws Exception
+    {
+        try
+        {
+            LdapJndiProperties.getLdapJndiProperties( null );
+            fail( "should never get here" );
+        }
+        catch ( LdapConfigurationException e )
+        {
+        }
+    }
+
+
+    public void testNoAuthWithCredsEnv() throws Exception
+    {
+        Hashtable env = new Hashtable();
+        env.put( Context.SECURITY_PRINCIPAL, "uid=admin,ou=system" );
+        env.put( Context.SECURITY_CREDENTIALS, "asdf" );
+        env.put( Context.PROVIDER_URL, "" );
+        LdapJndiProperties props = LdapJndiProperties.getLdapJndiProperties( env );
+        assertEquals( AuthenticationLevel.SIMPLE, props.getAuthenticationLevel() );
+        assertEquals( 1, props.getAuthenticationMechanisms().size() );
+        assertEquals( "simple", props.getAuthenticationMechanisms().get( 0 ) );
+        assertTrue( ArrayUtils.isEquals( StringTools.getBytesUtf8( "asdf" ), props.getCredentials() ) );
+    }
+
+
+    public void testNoAuthWithNoCredsEnv() throws Exception
+    {
+        Hashtable env = new Hashtable();
+        env.put( Context.SECURITY_PRINCIPAL, "" );
+        env.put( Context.PROVIDER_URL, "" );
+        LdapJndiProperties props = LdapJndiProperties.getLdapJndiProperties( env );
+        assertEquals( AuthenticationLevel.NONE, props.getAuthenticationLevel() );
+        assertEquals( 1, props.getAuthenticationMechanisms().size() );
+        assertEquals( "none", props.getAuthenticationMechanisms().get( 0 ) );
+        assertTrue( props.getCredentials() == null );
+    }
+
+
+    public void testAuthWithNoCredsEnv() throws Exception
+    {
+        Hashtable env = new Hashtable();
+        env.put( Context.SECURITY_PRINCIPAL, "" );
+        env.put( Context.SECURITY_AUTHENTICATION, "simple" );
+        try
+        {
+            LdapJndiProperties.getLdapJndiProperties( env );
+            fail( "should never get here" );
+        }
+        catch ( LdapConfigurationException e )
+        {
+        }
+    }
+
+
+    public void testAuthWithNoCredsStrong() throws Exception
+    {
+        Hashtable env = new Hashtable();
+        env.put( Context.SECURITY_PRINCIPAL, "" );
+        env.put( Context.SECURITY_AUTHENTICATION, "DIGEST-MD5 CRAM-MD5" );
+        env.put( Context.PROVIDER_URL, "" );
+        LdapJndiProperties props = LdapJndiProperties.getLdapJndiProperties( env );
+        assertEquals( AuthenticationLevel.STRONG, props.getAuthenticationLevel() );
+        assertEquals( 2, props.getAuthenticationMechanisms().size() );
+        assertEquals( "DIGEST-MD5", props.getAuthenticationMechanisms().get( 0 ) );
+        assertEquals( "CRAM-MD5", props.getAuthenticationMechanisms().get( 1 ) );
+        assertTrue( props.getCredentials() == null );
+    }
+
+
+    public void testAuthWithCredsStrong() throws Exception
+    {
+        Hashtable env = new Hashtable();
+        env.put( Context.SECURITY_PRINCIPAL, "" );
+        env.put( Context.SECURITY_CREDENTIALS, "asdf" );
+        env.put( Context.SECURITY_AUTHENTICATION, "DIGEST-MD5 CRAM-MD5" );
+        env.put( Context.PROVIDER_URL, "" );
+        LdapJndiProperties props = LdapJndiProperties.getLdapJndiProperties( env );
+        assertEquals( AuthenticationLevel.STRONG, props.getAuthenticationLevel() );
+        assertEquals( 2, props.getAuthenticationMechanisms().size() );
+        assertEquals( "DIGEST-MD5", props.getAuthenticationMechanisms().get( 0 ) );
+        assertEquals( "CRAM-MD5", props.getAuthenticationMechanisms().get( 1 ) );
+        assertTrue( ArrayUtils.isEquals( StringTools.getBytesUtf8( "asdf" ), props.getCredentials() ) );
+    }
+}
diff --git a/old_trunk/core/src/test/java/org/apache/directory/server/core/partition/impl/btree/jdbm/AttributesSerializerTest.java b/old_trunk/core/src/test/java/org/apache/directory/server/core/partition/impl/btree/jdbm/AttributesSerializerTest.java
new file mode 100644
index 0000000..a6acdfe
--- /dev/null
+++ b/old_trunk/core/src/test/java/org/apache/directory/server/core/partition/impl/btree/jdbm/AttributesSerializerTest.java
@@ -0,0 +1,95 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree.jdbm;
+
+
+import java.io.IOException;
+
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.util.ArrayUtils;
+import org.apache.directory.shared.ldap.util.AttributesSerializerUtils;
+
+import junit.framework.TestCase;
+
+
+/**
+ * Tests the {@link EntryAttributeSerializer}.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class AttributesSerializerTest extends TestCase
+{
+    public void testFullCycle() throws IOException
+    {
+        AttributesImpl attrs = new AttributesImpl();
+        AttributeImpl attr0 = new AttributeImpl( "attr0" );
+        attr0.add( "value0" );
+        attr0.add( "val1" );
+        attr0.add( "anything over here!" );
+        
+        AttributeImpl attr1 = new AttributeImpl( "attr1" );
+        byte[] ba0 = new byte[2];
+        ba0[0] = 7;
+        ba0[1] = 23;
+        attr1.add( ba0 );
+        byte[] ba1 = new byte[3];
+        ba1[0] = 34;
+        ba1[1] = 111;
+        ba1[2] = 67;
+        attr1.add( ba1 );
+        
+        attrs.put( attr0 );
+        attrs.put( attr1 );
+        byte[] buf = AttributesSerializerUtils.serialize( attrs );
+        AttributesImpl deserialized = ( AttributesImpl ) AttributesSerializerUtils.deserialize( buf );
+
+        AttributeImpl attrDeserialized0 = ( AttributeImpl ) deserialized.get( "attr0" );
+        assertEquals( "value0", attrDeserialized0.get() );
+        assertEquals( "val1", attrDeserialized0.get( 1 ) );
+        assertEquals( "anything over here!", attrDeserialized0.get( 2 ) );
+        
+        AttributeImpl attrDeserialized1 = ( AttributeImpl ) deserialized.get( "attr1" );
+        ArrayUtils.isEquals( ba0, attrDeserialized1.get() );
+        ArrayUtils.isEquals( ba1, attrDeserialized1.get( 1 ) );
+    }
+    
+    
+//    public void doSerializerSpeedTest() throws IOException
+//    {
+//        final int limit = 1000000;
+//        long start = System.currentTimeMillis();
+//        for ( int ii = 0; ii < limit; ii++ )
+//        {
+//            AttributeImpl attr = new AttributeImpl( "testing" );
+//            AttributeSerializer serializer = new AttributeSerializer();
+//            attr.add( "value0" );
+//            attr.add( "val1" );
+//            attr.add( "anything over here!" );
+//            
+//            byte[] serialized = serializer.serialize( attr );
+//            serializer.deserialize( serialized );
+//        }
+//        
+//        System.out.println( limit + " attributes with 3 values each were serialized and deserialized in " 
+//            + ( System.currentTimeMillis() - start ) + " (ms)" );
+//    }
+}
diff --git a/old_trunk/core/src/test/java/org/apache/directory/server/core/partition/impl/btree/jdbm/BTreeEnumerationTest.java b/old_trunk/core/src/test/java/org/apache/directory/server/core/partition/impl/btree/jdbm/BTreeEnumerationTest.java
new file mode 100644
index 0000000..0a13ae3
--- /dev/null
+++ b/old_trunk/core/src/test/java/org/apache/directory/server/core/partition/impl/btree/jdbm/BTreeEnumerationTest.java
@@ -0,0 +1,124 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree.jdbm;
+
+
+import java.io.File;
+import java.io.IOException;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.shared.ldap.util.LongComparator;
+
+import jdbm.RecordManager;
+import jdbm.btree.BTree;
+import jdbm.recman.BaseRecordManager;
+
+//import org.apache.directory.shared.ldap.util.BigIntegerComparator;
+
+import junit.framework.TestCase;
+
+
+/**
+ * Tests that the BTreeEnumeration functions as expected.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class BTreeEnumerationTest extends TestCase
+{
+    private final static byte[] EMPTY_BYTES = new byte[0];
+    private File tempFile = null;
+    private BTree tree = null;
+    private RecordManager rm = null;
+    
+    
+    public void setUp() throws Exception 
+    {
+        tempFile = File.createTempFile( "jdbm", "test" );
+        rm = new BaseRecordManager( tempFile.getAbsolutePath() );
+        tree = BTree.createInstance( rm, new LongComparator() );
+    }
+    
+    protected void tearDown() throws Exception
+    {
+        String tmp = tempFile.getAbsolutePath();
+        new File( tmp ).delete();
+        new File( tmp + ".db" ).delete();
+        new File( tmp + ".lg" ).delete();
+    }
+
+    public void testEmptyBTree() throws NamingException
+    {
+        BTreeEnumeration bte = new BTreeEnumeration( tree );
+        assertFalse( "enumeration on empty btree should not have elements", bte.hasMore() );
+    }
+    
+    
+    public void testOneElement() throws IOException, NamingException
+    {
+        Long value = 1L;
+        tree.insert( value, EMPTY_BYTES, true );
+        BTreeEnumeration bte = new BTreeEnumeration( tree );
+        assertTrue( bte.hasMore() );
+        assertEquals( value, bte.next() );
+        assertFalse( "enumeration consumed should not have elements", bte.hasMore() );
+    }
+    
+    
+    public void testManyElements() throws IOException, NamingException
+    {
+        /*
+         * Adding the following values for this test
+         * 1, -
+         * 2, -
+         * 4, -
+         * 5, -
+         */
+        Long value = 1L;
+        tree.insert( value, EMPTY_BYTES, true );
+        
+        value += 1L;
+        tree.insert( value, EMPTY_BYTES, true );
+
+        value += 1L;
+        value += 1L;
+        tree.insert( value, EMPTY_BYTES, true );
+
+        value += 1L;
+        tree.insert( value, EMPTY_BYTES, true );
+        
+        BTreeEnumeration bte = new BTreeEnumeration( tree );
+
+        assertTrue( bte.hasMore() );
+        assertEquals( 1L, bte.next() );
+
+        assertTrue( bte.hasMore() );
+        assertEquals( 2L, bte.next() );
+
+        assertTrue( bte.hasMore() );
+        assertEquals( 4L, bte.next() );
+
+        assertTrue( bte.hasMore() );
+        assertEquals( 5L, bte.next() );
+
+        assertFalse( "enumeration consumed should not have elements", bte.hasMore() );
+    }
+}
diff --git a/old_trunk/core/src/test/java/org/apache/directory/server/core/partition/impl/btree/jdbm/BTreeIteratorTest.java b/old_trunk/core/src/test/java/org/apache/directory/server/core/partition/impl/btree/jdbm/BTreeIteratorTest.java
new file mode 100644
index 0000000..122186d
--- /dev/null
+++ b/old_trunk/core/src/test/java/org/apache/directory/server/core/partition/impl/btree/jdbm/BTreeIteratorTest.java
@@ -0,0 +1,164 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree.jdbm;
+
+
+import java.io.File;
+import java.io.IOException;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.shared.ldap.util.LongComparator;
+
+import jdbm.RecordManager;
+import jdbm.btree.BTree;
+import jdbm.recman.BaseRecordManager;
+
+//import org.apache.directory.shared.ldap.util.BigIntegerComparator;
+
+import junit.framework.TestCase;
+
+
+/**
+ * Tests that the BTreeEnumeration functions as expected.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class BTreeIteratorTest extends TestCase
+{
+    private final static byte[] EMPTY_BYTES = new byte[0];
+    private File tempFile = null;
+    private BTree tree = null;
+    private RecordManager rm = null;
+    
+    
+    public void setUp() throws Exception 
+    {
+        tempFile = File.createTempFile( "jdbm", "test" );
+        rm = new BaseRecordManager( tempFile.getAbsolutePath() );
+        tree = BTree.createInstance( rm, new LongComparator() );
+    }
+    
+    protected void tearDown() throws Exception
+    {
+        String tmp = tempFile.getAbsolutePath();
+        new File( tmp ).delete();
+        new File( tmp + ".db" ).delete();
+        new File( tmp + ".lg" ).delete();
+    }
+
+    public void testEmptyBTree() throws NamingException
+    {
+        BTreeIterator bte = new BTreeIterator( tree, true );
+        assertFalse( "iterator on empty btree should not have elements", bte.hasNext() );
+    }
+    
+    
+    public void testOneElement() throws IOException, NamingException
+    {
+        Long value = 1L;
+        tree.insert( value, EMPTY_BYTES, true );
+        BTreeIterator bte = new BTreeIterator( tree, true );
+        assertTrue( bte.hasNext() );
+        assertEquals( value, bte.next() );
+        assertFalse( "iterator consumed should not have elements", bte.hasNext() );
+    }
+    
+    
+    public void testManyElements() throws IOException, NamingException
+    {
+        /*
+         * Adding the following values for this test
+         * 1, -
+         * 2, -
+         * 4, -
+         * 5, -
+         */
+        Long value = 1L;
+        tree.insert( value, EMPTY_BYTES, true );
+        
+        value += 1L;
+        tree.insert( value, EMPTY_BYTES, true );
+
+        value += 1L;
+        value += 1L;
+        tree.insert( value, EMPTY_BYTES, true );
+
+        value += 1L;
+        tree.insert( value, EMPTY_BYTES, true );
+        
+        BTreeIterator bte = new BTreeIterator( tree, true );
+
+        assertTrue( bte.hasNext() );
+        assertEquals( 1L, bte.next() );
+
+        assertTrue( bte.hasNext() );
+        assertEquals( 2L, bte.next() );
+
+        assertTrue( bte.hasNext() );
+        assertEquals( 4L, bte.next() );
+
+        assertTrue( bte.hasNext() );
+        assertEquals( 5L, bte.next() );
+
+        assertFalse( "iterator consumed should not have elements", bte.hasNext() );
+    }
+
+
+    public void testManyElementsReversed() throws IOException, NamingException
+    {
+        /*
+         * Adding the following values for this test
+         * 1, -
+         * 2, -
+         * 4, -
+         * 5, -
+         */
+        Long value = 1L;
+        tree.insert( value, EMPTY_BYTES, true );
+        
+        value += 1L;
+        tree.insert( value, EMPTY_BYTES, true );
+
+        value += 1L;
+        value += 1L;
+        tree.insert( value, EMPTY_BYTES, true );
+
+        value += 1L;
+        tree.insert( value, EMPTY_BYTES, true );
+        
+        BTreeIterator bte = new BTreeIterator( tree, false );
+
+        assertTrue( bte.hasNext() );
+        assertEquals( 5L, bte.next() );
+
+        assertTrue( bte.hasNext() );
+        assertEquals( 4L, bte.next() );
+
+        assertTrue( bte.hasNext() );
+        assertEquals( 2L, bte.next() );
+
+        assertTrue( bte.hasNext() );
+        assertEquals( 1L, bte.next() );
+
+        assertFalse( "iterator consumed should not have elements", bte.hasNext() );
+    }
+}
diff --git a/old_trunk/core/src/test/java/org/apache/directory/server/core/partition/impl/btree/jdbm/BTreeTupleEnumerationTest.java b/old_trunk/core/src/test/java/org/apache/directory/server/core/partition/impl/btree/jdbm/BTreeTupleEnumerationTest.java
new file mode 100644
index 0000000..8971399
--- /dev/null
+++ b/old_trunk/core/src/test/java/org/apache/directory/server/core/partition/impl/btree/jdbm/BTreeTupleEnumerationTest.java
@@ -0,0 +1,414 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree.jdbm;
+
+
+import java.io.File;
+import java.io.IOException;
+
+import javax.naming.NamingException;
+
+import jdbm.RecordManager;
+import jdbm.btree.BTree;
+import jdbm.recman.BaseRecordManager;
+
+import org.apache.directory.server.core.partition.impl.btree.Tuple;
+import org.apache.directory.shared.ldap.util.LongComparator;
+//import org.apache.directory.shared.ldap.util.BigIntegerComparator;
+
+import junit.framework.TestCase;
+
+
+/**
+ * Tests that the BTreeTupleEnumeration functions as expected.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class BTreeTupleEnumerationTest extends TestCase
+{
+    private final static byte[] EMPTY_BYTES = new byte[0];
+    private File tempFile = null;
+    private BTree tree = null;
+    private RecordManager rm = null;
+    
+    
+    public void setUp() throws Exception 
+    {
+        tempFile = File.createTempFile( "jdbm", "test" );
+        rm = new BaseRecordManager( tempFile.getAbsolutePath() );
+        tree = BTree.createInstance( rm, new LongComparator() );
+    }
+    
+    protected void tearDown() throws Exception
+    {
+        String tmp = tempFile.getAbsolutePath();
+        new File( tmp ).delete();
+        new File( tmp + ".db" ).delete();
+        new File( tmp + ".lg" ).delete();
+    }
+
+    public void testEmptyBTree() throws NamingException
+    {
+        BTreeTupleEnumeration bte = new BTreeTupleEnumeration( tree, 1L );
+        assertFalse( "enumeration on empty btree should not have elements", bte.hasMore() );
+    }
+    
+    
+    public void testOneElement() throws IOException, NamingException
+    {
+        Long value = 1L;
+        tree.insert( value, EMPTY_BYTES, true );
+        BTreeTupleEnumeration bte = new BTreeTupleEnumeration( tree, 1L );
+        assertTrue( bte.hasMore() );
+        Tuple tuple = ( Tuple ) bte.next();
+        assertEquals( 1L, tuple.getKey() );
+        assertEquals( value, tuple.getValue() );
+        assertFalse( "enumeration consumed should not have elements", bte.hasMore() );
+    }
+    
+    
+    public void testManyElements() throws IOException, NamingException
+    {
+        /*
+         * Adding the following values for this test
+         * 1, -
+         * 2, -
+         * 4, -
+         * 5, -
+         */
+        Long value = 1L;
+        tree.insert( value, EMPTY_BYTES, true );
+        
+        value += 1L;
+        tree.insert( value, EMPTY_BYTES, true );
+
+        value += 1L;
+        value += 1L;
+        tree.insert( value, EMPTY_BYTES, true );
+
+        value += 1L;
+        tree.insert( value, EMPTY_BYTES, true );
+        
+        BTreeTupleEnumeration bte = new BTreeTupleEnumeration( tree, 1L );
+        
+        Tuple tuple = ( Tuple ) bte.next();
+        assertTrue( bte.hasMore() );
+        assertEquals( 1L, tuple.getKey() );
+        assertEquals( 1L, tuple.getValue() );
+
+        bte.next();
+        assertTrue( bte.hasMore() );
+        assertEquals( 1L, tuple.getKey() );
+        assertEquals( 2L, tuple.getValue() );
+
+        bte.next();
+        assertTrue( bte.hasMore() );
+        assertEquals( 1L, tuple.getKey() );
+        assertEquals( 4L, tuple.getValue() );
+
+        bte.next();
+        assertEquals( 1L, tuple.getKey() );
+        assertEquals( 5L, tuple.getValue() );
+
+        assertFalse( "enumeration consumed should not have elements", bte.hasMore() );
+    }
+    
+    
+    public void testManyElementsLessThanNonExistantValue() throws IOException, NamingException
+    {
+        /*
+         * Adding the following values for this test
+         * 1, -
+         * 2, -
+         * 4, -
+         * 5, -
+         */
+        Long value = 1L;
+        tree.insert( value, EMPTY_BYTES, true );
+        
+        value += 1L;
+        tree.insert( value, EMPTY_BYTES, true );
+
+        value += 1L;
+        value += 1L;
+        tree.insert( value, EMPTY_BYTES, true );
+
+        value += 1L;
+        tree.insert( value, EMPTY_BYTES, true );
+        
+        BTreeTupleEnumeration bte = new BTreeTupleEnumeration( tree, new LongComparator(), 
+            1L, 3L, false );
+        
+        Tuple tuple = ( Tuple ) bte.next();
+        assertTrue( bte.hasMore() );
+        assertEquals( 1L, tuple.getKey() );
+        assertEquals( 2L, tuple.getValue() );
+
+        bte.next();
+        assertEquals( 1L, tuple.getKey() );
+        assertEquals( 1L, tuple.getValue() );
+
+        assertFalse( "enumeration consumed should not have elements", bte.hasMore() );
+    }
+    
+    
+    public void testManyElementsLessThanLowestValue() throws IOException, NamingException
+    {
+        /*
+         * Adding the following values for this test
+         * 1, -
+         * 2, -
+         * 4, -
+         * 5, -
+         */
+        Long value = 1L;
+        tree.insert( value, EMPTY_BYTES, true );
+        
+        value += 1L;
+        tree.insert( value, EMPTY_BYTES, true );
+
+        value += 1L;
+        value += 1L;
+        tree.insert( value, EMPTY_BYTES, true );
+
+        value += 1L;
+        tree.insert( value, EMPTY_BYTES, true );
+        
+        BTreeTupleEnumeration bte = new BTreeTupleEnumeration( tree, new LongComparator(), 
+            1L, 0L, false );
+        
+        assertFalse( "enumeration consumed should not have elements", bte.hasMore() );
+    }
+    
+    
+    public void testManyElementsLessThanAtLowestValue() throws IOException, NamingException
+    {
+        /*
+         * Adding the following values for this test
+         * 1, -
+         * 2, -
+         * 4, -
+         * 5, -
+         */
+        Long value = 1L;
+        tree.insert( value, EMPTY_BYTES, true );
+        
+        value += 1L;
+        tree.insert( value, EMPTY_BYTES, true );
+
+        value += 1L;
+        value += 1L;
+        tree.insert( value, EMPTY_BYTES, true );
+
+        value += 1L;
+        tree.insert( value, EMPTY_BYTES, true );
+        
+        BTreeTupleEnumeration bte = new BTreeTupleEnumeration( tree, new LongComparator(), 
+            1L, 1L, false );
+        
+        Tuple tuple = ( Tuple ) bte.next();
+        assertEquals( 1L, tuple.getKey() );
+        assertEquals( 1L, tuple.getValue() );
+
+        assertFalse( "enumeration consumed should not have elements", bte.hasMore() );
+    }
+    
+    
+    public void testManyElementsGreaterThanNonExistantValue() throws IOException, NamingException
+    {
+        /*
+         * Adding the following values for this test
+         * 1, -
+         * 2, -
+         * 4, -
+         * 5, -
+         */
+        Long value = 1L;
+        tree.insert( value, EMPTY_BYTES, true );
+        
+        value += 1L;
+        tree.insert( value, EMPTY_BYTES, true );
+
+        value += 1L;
+        value += 1L;
+        tree.insert( value, EMPTY_BYTES, true );
+
+        value += 1L;
+        tree.insert( value, EMPTY_BYTES, true );
+        
+        BTreeTupleEnumeration bte = new BTreeTupleEnumeration( tree, new LongComparator(), 
+            1L, 3L, true );
+        
+        Tuple tuple = ( Tuple ) bte.next();
+        assertTrue( bte.hasMore() );
+        assertEquals( 1L, tuple.getKey() );
+        assertEquals( 4L, tuple.getValue() );
+
+        bte.next();
+        assertEquals( 1L, tuple.getKey() );
+        assertEquals( 5L, tuple.getValue() );
+
+        assertFalse( "enumeration consumed should not have elements", bte.hasMore() );
+    }
+    
+    
+    public void testManyElementsGreaterThanLastValue() throws IOException, NamingException
+    {
+        /*
+         * Adding the following values for this test
+         * 1, -
+         * 2, -
+         * 4, -
+         * 5, -
+         */
+        Long value = 1L;
+        tree.insert( value, EMPTY_BYTES, true );
+        
+        value += 1L;
+        tree.insert( value, EMPTY_BYTES, true );
+
+        value += 1L;
+        value += 1L;
+        tree.insert( value, EMPTY_BYTES, true );
+
+        value += 1L;
+        tree.insert( value, EMPTY_BYTES, true );
+        
+        BTreeTupleEnumeration bte = new BTreeTupleEnumeration( tree, new LongComparator(), 
+            1L, 6L, true );
+        
+        assertFalse( "enumeration consumed should not have elements", bte.hasMore() );
+    }
+    
+    
+    public void testManyElementsGreaterThanAtLastValue() throws IOException, NamingException
+    {
+        /*
+         * Adding the following values for this test
+         * 1, -
+         * 2, -
+         * 4, -
+         * 5, -
+         */
+        Long value = 1L;
+        tree.insert( value, EMPTY_BYTES, true );
+        
+        value += 1L;
+        tree.insert( value, EMPTY_BYTES, true );
+
+        value += 1L;
+        value += 1L;
+        tree.insert( value, EMPTY_BYTES, true );
+
+        value += 1L;
+        tree.insert( value, EMPTY_BYTES, true );
+        
+        BTreeTupleEnumeration bte = new BTreeTupleEnumeration( tree, new LongComparator(), 
+            1L, 5L, true );
+        
+        Tuple tuple = ( Tuple ) bte.next();
+        assertEquals( 1L, tuple.getKey() );
+        assertEquals( 5L, tuple.getValue() );
+
+        assertFalse( "enumeration consumed should not have elements", bte.hasMore() );
+    }
+    
+    
+    public void testManyElementsLessThanExistantValue() throws IOException, NamingException
+    {
+        /*
+         * Adding the following values for this test
+         * 1, -
+         * 2, -
+         * 4, -
+         * 5, -
+         */
+        Long value = 1L;
+        tree.insert( value, EMPTY_BYTES, true );
+        
+        value += 1L;
+        tree.insert( value, EMPTY_BYTES, true );
+
+        value += 1L;
+        value += 1L;
+        tree.insert( value, EMPTY_BYTES, true );
+
+        value += 1L;
+        tree.insert( value, EMPTY_BYTES, true );
+        
+        BTreeTupleEnumeration bte = new BTreeTupleEnumeration( tree, new LongComparator(), 
+            1L, 4L, false );
+        
+        Tuple tuple = ( Tuple ) bte.next();
+        assertTrue( bte.hasMore() );
+        assertEquals( 1L, tuple.getKey() );
+        assertEquals( 4L, tuple.getValue() );
+
+        bte.next();
+        assertTrue( bte.hasMore() );
+        assertEquals( 1L, tuple.getKey() );
+        assertEquals( 2L, tuple.getValue() );
+
+        bte.next();
+        assertEquals( 1L, tuple.getKey() );
+        assertEquals( 1L, tuple.getValue() );
+
+        assertFalse( "enumeration consumed should not have elements", bte.hasMore() );
+    }
+    
+    
+    public void testManyElementsGreaterThanExistantValue() throws IOException, NamingException
+    {
+        /*
+         * Adding the following values for this test
+         * 1, -
+         * 2, -
+         * 4, -
+         * 5, -
+         */
+        Long value = 1L;
+        tree.insert( value, EMPTY_BYTES, true );
+        
+        value += 1L;
+        tree.insert( value, EMPTY_BYTES, true );
+
+        value += 1L;
+        value += 1L;
+        tree.insert( value, EMPTY_BYTES, true );
+
+        value += 1L;
+        tree.insert( value, EMPTY_BYTES, true );
+        
+        BTreeTupleEnumeration bte = new BTreeTupleEnumeration( tree, new LongComparator(), 
+            1L, 4L, true );
+        
+        Tuple tuple = ( Tuple ) bte.next();
+        assertTrue( bte.hasMore() );
+        assertEquals( 1L, tuple.getKey() );
+        assertEquals( 4L, tuple.getValue() );
+
+        bte.next();
+        assertEquals( 1L, tuple.getKey() );
+        assertEquals( 5L, tuple.getValue() );
+
+        assertFalse( "enumeration consumed should not have elements", bte.hasMore() );
+    }
+}
diff --git a/old_trunk/core/src/test/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmTableDupsBTreeTest.java b/old_trunk/core/src/test/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmTableDupsBTreeTest.java
new file mode 100644
index 0000000..87189d5
--- /dev/null
+++ b/old_trunk/core/src/test/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmTableDupsBTreeTest.java
@@ -0,0 +1,650 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree.jdbm;
+
+
+import java.io.File;
+import java.io.Serializable;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+
+import org.apache.directory.server.core.partition.impl.btree.Tuple;
+import org.apache.directory.server.core.partition.impl.btree.TupleComparator;
+import org.apache.directory.server.schema.SerializableComparator;
+import org.apache.directory.shared.ldap.util.ArrayEnumeration;
+import org.apache.directory.shared.ldap.util.LongComparator;
+
+import jdbm.RecordManager;
+import jdbm.recman.BaseRecordManager;
+
+import junit.framework.TestCase;
+
+
+/**
+ * Tests for JdbmTable.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class JdbmTableDupsBTreeTest extends TestCase implements Serializable
+{
+    private static final long serialVersionUID = 1L;
+    private transient File tempFile = null;
+    private transient RecordManager rm = null;
+    private final LongComparator biComparator = new LongComparator();
+    private final SerializableComparator serializableComparator = new SerializableComparator( "integerMatchingRule" )
+    {
+        private static final long serialVersionUID = 1L;
+
+        public int compare( Object o1, Object o2 )
+        {
+            return biComparator.compare( o1, o2 );
+        }
+    };
+    private TupleComparator comparator = new TupleComparator()
+    {
+        private static final long serialVersionUID = 1L;
+
+        public int compareKey( Object key1, Object key2 )
+        {
+            return biComparator.compare( key1, key2 );
+        }
+
+        public int compareValue( Object value1, Object value2 )
+        {
+            return biComparator.compare( value1, value2 );
+        }
+
+        public SerializableComparator getKeyComparator()
+        {
+            return serializableComparator;
+        }
+
+        public SerializableComparator getValueComparator()
+        {
+            return serializableComparator;
+        }
+    };
+
+
+    transient JdbmTable table;
+
+
+    /**
+     * Here's what the table looks like:
+     * <pre>
+     * .-.-.
+     * |1|0|
+     * |1|1|
+     * |1|2|
+     * |2|1|
+     * |4|1|
+     * |5|1|
+     * .-.-. 
+     * </pre>
+     */
+    public void setUp() throws Exception
+    {
+        tempFile = File.createTempFile( "jdbm", "test" );
+        rm = new BaseRecordManager( tempFile.getAbsolutePath() );
+
+        // make sure the table never uses a btree for duplicates
+        table = new JdbmTable( "test", true, 1, rm, comparator, null, null );
+
+        for ( Long ii = 0L; ii.intValue() < 3; ii++ )
+        {
+            table.put( 1L, ii );
+        }
+
+        table.put( 2L, 1L );
+        table.put( 4L, 1L );
+        table.put( 5L, 1L );
+    }
+
+    protected void tearDown() throws Exception
+    {
+        String tmp = tempFile.getAbsolutePath();
+        new File( tmp ).delete();
+        new File( tmp + ".db" ).delete();
+        new File( tmp + ".lg" ).delete();
+    }
+
+    /**
+     * Tests the has() methods for correct behavoir:
+     * <ul>
+     *   <li>has(Object)</li>
+     *   <li>has(Object, boolean)</li>
+     *   <li>has(Object, Object)</li>
+     *   <li>has(Object, Object, boolean)</li>
+     * </ul>
+     *
+     * @throws NamingException
+     */
+    public void testHas() throws Exception
+    {
+        // test the has( Object ) method
+        assertTrue( table.has( 1L ) );
+        assertTrue( table.has( 2L ) );
+        assertTrue( table.has( 4L ) );
+        assertTrue( table.has( 5L ) );
+        assertFalse( table.has( 3L ) );
+        assertFalse( table.has( 0L ) );
+        assertFalse( table.has( 999L ) );
+
+        // test the has( Object, Object ) method
+        assertTrue( table.has( 1L, 1L ) );
+        assertTrue( table.has( 2L, 1L ) );
+        assertTrue( table.has( 4L, 1L ) );
+        assertTrue( table.has( 5L, 1L ) );
+        assertFalse( table.has( 5L, 0L ) );
+        assertFalse( table.has( 3L, 1L ) );
+        assertFalse( table.has( 1L, 999L ) );
+        assertFalse( table.has( 999L, 1L ) );
+
+        // test the has( Object, boolean ) method
+        assertFalse( table.has( Long.valueOf( 0L ), false ) ); // we do not have a key less than or equal to 0
+        assertTrue( table.has( Long.valueOf( 1L ), false ) ); // we do have a key less than or equal to 1
+        assertTrue( table.has( Long.valueOf( 0L ), true ) ); // we do have a key greater than or equal to 0
+        assertTrue( table.has( Long.valueOf( 1L ), true ) ); // we do have a key greater than or equal to 1
+        assertTrue( table.has( Long.valueOf( 5L ), true ) ); // we do have a key greater than or equal to 5
+        assertFalse( table.has( Long.valueOf( 6L ), true ) ); // we do NOT have a key greater than or equal to 11
+        assertFalse( table.has( Long.valueOf( 999L ), true ) ); // we do NOT have a key greater than or equal to 12
+
+        // test the has( Object, Object, boolean ) method
+        assertTrue( table.has( 1L, 0L, true ) );
+        assertTrue( table.has( 1L, 1L, true ) );
+        assertTrue( table.has( 1L, 2L, true ) );
+        assertFalse( table.has( 1L, 3L, true ) );
+        assertTrue( table.has( 1L, 0L, false ) );
+        assertFalse( table.has( 1L, -1L, false ) );
+    }
+    
+    
+    /**
+     * Tests the count() methods for correct behavoir:
+     * <ul>
+     *   <li>count()</li>
+     *   <li>count(Object)</li>
+     *   <li>count(Object, boolean)</li>
+     * </ul>
+     * 
+     * @throws Exception
+     */
+    public void testCount() throws Exception
+    {
+        // test the count() method
+        assertEquals( 6, table.count() );
+        
+        // test the count(Object) method
+        assertEquals( 3, table.count( 1L ) );
+        assertEquals( 0, table.count( 0L ) );
+        assertEquals( 1, table.count( 2L ) );
+        
+        // test the count( Object, boolean ) method 
+        // note for speed this count method returns the same as count()
+        assertEquals( table.count(), table.count( 1L, true ) );
+    }
+    
+    
+    /**
+     * Tests the get() method for correct behavoir.
+     * 
+     * @throws Exception
+     */
+    public void testGet() throws Exception
+    {
+        assertEquals( 0L, table.get( 1L ) );
+        assertEquals( 1L, table.get( 2L ) );
+        assertEquals( null, table.get( 3L ) );
+        assertEquals( 1L, table.get( 4L ) );
+        assertEquals( 1L, table.get( 5L ) );
+    }
+    
+    
+    /**
+     * Tests the listTuples() methods for correct behavoir:
+     * <ul>
+     *   <li>listTuples()</li>
+     *   <li>listTuples(Object)</li>
+     *   <li>listTuples(Object,boolean)</li>
+     *   <li>listTuples(Object,Object,boolean)</li>
+     * </ul>
+     * 
+     * @throws Exception
+     */
+    public void testListTuples() throws Exception
+    {
+        Tuple tuple;
+
+        // -------------------------------------------------------------------
+        // test the listTuples() method
+        // -------------------------------------------------------------------
+
+        NamingEnumeration<Tuple> tuples = table.listTuples();
+        
+        assertTrue( tuples.hasMore() ) ;
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 1L, tuple.getKey() );
+        assertEquals( 0L, tuple.getValue() );
+        
+        assertTrue( tuples.hasMore() ) ;
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 1L, tuple.getKey() );
+        assertEquals( 1L, tuple.getValue() );
+        
+        assertTrue( tuples.hasMore() ) ;
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 1L, tuple.getKey() );
+        assertEquals( 2L, tuple.getValue() );
+        
+        assertTrue( tuples.hasMore() ) ;
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 2L, tuple.getKey() );
+        assertEquals( 1L, tuple.getValue() );
+        
+        assertTrue( tuples.hasMore() ) ;
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 4L, tuple.getKey() );
+        assertEquals( 1L, tuple.getValue() );
+        
+        assertTrue( tuples.hasMore() ) ;
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 5L, tuple.getKey() );
+        assertEquals( 1L, tuple.getValue() );
+        
+        assertFalse( tuples.hasMore() );
+
+        // -------------------------------------------------------------------
+        // test the listTuples(Object) method
+        // -------------------------------------------------------------------
+
+        tuples = table.listTuples( 0L );
+        assertFalse( tuples.hasMore() );
+
+        tuples = table.listTuples( 2L );
+        assertTrue( tuples.hasMore() );
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 2L, tuple.getKey() );
+        assertEquals( 1L, tuple.getValue() );
+        assertFalse( tuples.hasMore() );
+        
+        tuples = table.listTuples( 1L );
+        assertTrue( tuples.hasMore() ) ;
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 1L, tuple.getKey() );
+        assertEquals( 0L, tuple.getValue() );
+        
+        assertTrue( tuples.hasMore() ) ;
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 1L, tuple.getKey() );
+        assertEquals( 1L, tuple.getValue() );
+        
+        assertTrue( tuples.hasMore() ) ;
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 1L, tuple.getKey() );
+        assertEquals( 2L, tuple.getValue() );
+        
+        assertFalse( tuples.hasMore() );
+        
+        // -------------------------------------------------------------------
+        // test the listTuples(Object, boolean) method
+        // -------------------------------------------------------------------
+
+        tuples = table.listTuples( 0L, false );
+        assertFalse( tuples.hasMore() );
+
+
+        tuples = table.listTuples( 1L, false );
+        assertTrue( tuples.hasMore() ) ;
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 1L, tuple.getKey() );
+        assertEquals( 2L, tuple.getValue() );
+        
+        assertTrue( tuples.hasMore() ) ;
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 1L, tuple.getKey() );
+        assertEquals( 1L, tuple.getValue() );
+        
+        assertTrue( tuples.hasMore() ) ;
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 1L, tuple.getKey() );
+        assertEquals( 0L, tuple.getValue() );
+        assertFalse( tuples.hasMore() );
+
+
+        tuples = table.listTuples( 2L, false );
+
+        assertTrue( tuples.hasMore() ) ;
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 2L, tuple.getKey() );
+        assertEquals( 1L, tuple.getValue() );
+
+        assertTrue( tuples.hasMore() ) ;
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 1L, tuple.getKey() );
+        assertEquals( 2L, tuple.getValue() );
+        
+        assertTrue( tuples.hasMore() ) ;
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 1L, tuple.getKey() );
+        assertEquals( 1L, tuple.getValue() );
+        
+        assertTrue( tuples.hasMore() ) ;
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 1L, tuple.getKey() );
+        assertEquals( 0L, tuple.getValue() );
+        assertFalse( tuples.hasMore() );
+
+        
+        tuples = table.listTuples( 6L, true );
+        assertFalse( tuples.hasMore() );
+
+        
+        tuples = table.listTuples( 5L, true );
+        assertTrue( tuples.hasMore() ) ;
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 5L, tuple.getKey() );
+        assertEquals( 1L, tuple.getValue() );
+        assertFalse( tuples.hasMore() );
+
+        
+        tuples = table.listTuples( 4L, true );
+        assertTrue( tuples.hasMore() ) ;
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 4L, tuple.getKey() );
+        assertEquals( 1L, tuple.getValue() );
+
+        assertTrue( tuples.hasMore() ) ;
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 5L, tuple.getKey() );
+        assertEquals( 1L, tuple.getValue() );
+        assertFalse( tuples.hasMore() );
+
+        // -------------------------------------------------------------------
+        // test the listTuples(Object,Object,boolean) method
+        // -------------------------------------------------------------------
+        
+        tuples = table.listTuples( 0L, 0L, true );
+        assertFalse( tuples.hasMore() );
+
+        tuples = table.listTuples( 0L, 0L, false );
+        assertFalse( tuples.hasMore() );
+        
+        tuples = table.listTuples( 2L, 0L, false );
+        assertFalse( tuples.hasMore() );
+        
+        tuples = table.listTuples( 2L, 99L, true );
+        assertFalse( tuples.hasMore() );
+        
+        tuples = table.listTuples( 2L, 1L, false );
+        assertTrue( tuples.hasMore() ) ;
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 2L, tuple.getKey() );
+        assertEquals( 1L, tuple.getValue() );
+        assertFalse( tuples.hasMore() );
+
+        tuples = table.listTuples( 2L, 99L, false );
+        assertTrue( tuples.hasMore() ) ;
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 2L, tuple.getKey() );
+        assertEquals( 1L, tuple.getValue() );
+        assertFalse( tuples.hasMore() );
+
+        
+        tuples = table.listTuples( 1L, 3L, true );
+        assertFalse( tuples.hasMore() );
+
+
+        tuples = table.listTuples( 1L, -1L, false );
+        assertFalse( tuples.hasMore() );
+
+
+        tuples = table.listTuples( 1L, 1L, true );
+
+        assertTrue( tuples.hasMore() ) ;
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 1L, tuple.getKey() );
+        assertEquals( 1L, tuple.getValue() );
+
+        assertTrue( tuples.hasMore() ) ;
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 1L, tuple.getKey() );
+        assertEquals( 2L, tuple.getValue() );
+
+        assertFalse( tuples.hasMore() );
+
+        
+        tuples = table.listTuples( 1L, 1L, false );
+
+        assertTrue( tuples.hasMore() ) ;
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 1L, tuple.getKey() );
+        assertEquals( 1L, tuple.getValue() );
+
+        assertTrue( tuples.hasMore() ) ;
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 1L, tuple.getKey() );
+        assertEquals( 0L, tuple.getValue() );
+
+        assertFalse( tuples.hasMore() );
+
+    }
+
+    
+    /**
+     * Tests the listValues() method for correct behavoir.
+     */
+    public void testListValues() throws Exception
+    {
+        // -------------------------------------------------------------------
+        // test the listValues(Object) method
+        // -------------------------------------------------------------------
+
+        NamingEnumeration<Object> values = table.listValues( 0L );
+        assertFalse( values.hasMore() );
+
+        values = table.listValues( 2L );
+        assertTrue( values.hasMore() );
+        Object value = values.next();
+        assertEquals( 1L, value );
+        assertFalse( values.hasMore() );
+        
+        values = table.listValues( 1L );
+        assertTrue( values.hasMore() ) ;
+        value = values.next();
+        assertEquals( 0L, value );
+        
+        assertTrue( values.hasMore() ) ;
+        value = values.next();
+        assertEquals( 1L, value );
+        
+        assertTrue( values.hasMore() ) ;
+        value = values.next();
+        assertEquals( 2L, value );
+        
+        assertFalse( values.hasMore() );
+    }
+    
+    
+    /**
+     * Tests the put() methods for correct behavior:
+     * <ul>
+     *   <li>put(Object, Object)</li>
+     *   <li>put(Object, NamingEnumeration)</li>
+     * </ul>
+     */
+    public void testPut() throws Exception
+    {
+        // put(Object,Object) already tested in setUp() tests the 
+        // this instead tests the NamingEnumeration overload
+        
+        NamingEnumeration<Object> values = new ArrayNE( new Object[] {
+            3L,
+            4L,
+            5L,
+            6L,
+        } );
+        
+        table.put( 1L, values );
+        assertFalse( values.hasMore() );
+        
+        values = table.listValues( 1L );
+        
+        assertTrue( values.hasMore() );
+        assertEquals( 0L, values.next() );
+        
+        assertTrue( values.hasMore() );
+        assertEquals( 1L, values.next() );
+        
+        assertTrue( values.hasMore() );
+        assertEquals( 2L, values.next() );
+        
+        assertTrue( values.hasMore() );
+        assertEquals( 3L, values.next() );
+        
+        assertTrue( values.hasMore() );
+        assertEquals( 4L, values.next() );
+        
+        assertTrue( values.hasMore() );
+        assertEquals( 5L, values.next() );
+        
+        assertTrue( values.hasMore() );
+        assertEquals( 6L, values.next() );
+        assertFalse( values.hasMore() );
+
+    
+        values = new ArrayNE( new Object[] {
+            3L,
+            4L,
+            5L,
+            6L,
+        } );
+        
+        table.put( 0L, values );
+        assertFalse( values.hasMore() );
+        
+        values = table.listValues( 0L );
+        
+        assertTrue( values.hasMore() );
+        assertEquals( 3L, values.next() );
+        
+        assertTrue( values.hasMore() );
+        assertEquals( 4L, values.next() );
+        
+        assertTrue( values.hasMore() );
+        assertEquals( 5L, values.next() );
+        
+        assertTrue( values.hasMore() );
+        assertEquals( 6L, values.next() );
+        assertFalse( values.hasMore() );
+    }
+    
+    
+    /**
+     * Tests the remove(Object) for correct behavoir:
+     */
+    public void testRemoveObject() throws Exception
+    {
+        // -------------------------------------------------------------------
+        // tests the remove(Object) method
+        // -------------------------------------------------------------------
+
+        try
+        {
+            table.remove( 0L );
+            fail( "should not get here trying to remove non-existent key" );
+        }
+        catch ( IllegalArgumentException e )
+        {
+        }
+        
+        Object value = table.remove( 2L );
+        assertEquals( 1L, value );
+        assertEquals( 5, table.count() );
+        
+        value = table.remove( 1L );
+        assertEquals( 0L, value ); // return first value of dups
+        assertEquals( 2, table.count() );
+    }
+    
+    
+    /**
+     * Tests the remove(Object,Object) for correct behavoir:
+     */
+    public void testRemoveObjectObject() throws Exception
+    {
+        // -------------------------------------------------------------------
+        // tests the remove(Object) method
+        // -------------------------------------------------------------------
+
+        Object value = table.remove( 0L, 0L );
+        assertNull( value );
+        
+        value = table.remove( 2L, 1L );
+        assertEquals( 1L, value );
+        assertEquals( 5, table.count() );
+        
+        value = table.remove( 1L, 2L );
+        assertEquals( 2L, value ); 
+        assertEquals( 4, table.count() );
+    }
+    
+    
+    /**
+     * Tests the remove(Object,NamingEnumeration) for correct behavoir:
+     */
+    public void testRemoveObjectNamingEnumeration() throws Exception
+    {
+        NamingEnumeration values = new ArrayNE( new Object[] {
+            1L,
+            2L
+        } );
+        
+        Object value = table.remove( 1L, values );
+        assertEquals( 1L, value );
+        assertEquals( 4, table.count() );
+    }
+    
+    
+    class ArrayNE extends ArrayEnumeration implements NamingEnumeration
+    {
+        public ArrayNE( Object[] array )
+        {
+            super( array );
+        }
+
+        public void close() throws NamingException
+        {
+        }
+
+        public boolean hasMore() throws NamingException
+        {
+            return hasMoreElements();
+        }
+
+        public Object next() throws NamingException
+        {
+            return nextElement();
+        }
+    }
+}
+
+    
\ No newline at end of file
diff --git a/old_trunk/core/src/test/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmTableDupsTreeSetTest.java b/old_trunk/core/src/test/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmTableDupsTreeSetTest.java
new file mode 100644
index 0000000..106d7d5
--- /dev/null
+++ b/old_trunk/core/src/test/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmTableDupsTreeSetTest.java
@@ -0,0 +1,652 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree.jdbm;
+
+
+import java.io.File;
+import java.io.Serializable;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+
+import org.apache.directory.server.core.partition.impl.btree.Tuple;
+import org.apache.directory.server.core.partition.impl.btree.TupleComparator;
+import org.apache.directory.server.schema.SerializableComparator;
+import org.apache.directory.shared.ldap.util.ArrayEnumeration;
+import org.apache.directory.shared.ldap.util.LongComparator;
+
+import jdbm.RecordManager;
+import jdbm.recman.BaseRecordManager;
+
+import junit.framework.TestCase;
+
+
+/**
+ * Tests for JdbmTable.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class JdbmTableDupsTreeSetTest extends TestCase implements Serializable
+{
+    private static final long serialVersionUID = 1L;
+    private transient File tempFile = null;
+    private transient RecordManager rm = null;
+    private final LongComparator biComparator = new LongComparator();
+    private final SerializableComparator serializableComparator = new SerializableComparator( "integerMatchingRule" )
+    {
+        private static final long serialVersionUID = 1L;
+
+        public int compare( Object o1, Object o2 )
+        {
+            return biComparator.compare( o1, o2 );
+        }
+    };
+    private TupleComparator comparator = new TupleComparator()
+    {
+        private static final long serialVersionUID = 1L;
+
+        public int compareKey( Object key1, Object key2 )
+        {
+            return biComparator.compare( key1, key2 );
+        }
+
+        public int compareValue( Object value1, Object value2 )
+        {
+            return biComparator.compare( value1, value2 );
+        }
+
+        public SerializableComparator getKeyComparator()
+        {
+            return serializableComparator;
+        }
+
+        public SerializableComparator getValueComparator()
+        {
+            return serializableComparator;
+        }
+    };
+
+
+    transient JdbmTable table;
+
+
+    /**
+     * Here's what the table looks like:
+     * <pre>
+     * .-.-.
+     * |1|0|
+     * |1|1|
+     * |1|2|
+     * |2|1|
+     * |4|1|
+     * |5|1|
+     * .-.-. 
+     * </pre>
+     */
+    public void setUp() throws Exception
+    {
+        tempFile = File.createTempFile( "jdbm", "test" );
+        rm = new BaseRecordManager( tempFile.getAbsolutePath() );
+
+        // make sure the table never uses a btree for duplicates
+        table = new JdbmTable( "test", true, Integer.MAX_VALUE, rm, 
+            comparator, null, null );
+
+        for ( Long ii = 0L; ii.intValue() < 3; ii++ )
+        {
+            table.put( 1L, ii );
+        }
+
+        table.put( 2L, 1L );
+        table.put( 4L, 1L );
+        table.put( 5L, 1L );
+    }
+
+    protected void tearDown() throws Exception
+    {
+        String tmp = tempFile.getAbsolutePath();
+        new File( tmp ).delete();
+        new File( tmp + ".db" ).delete();
+        new File( tmp + ".lg" ).delete();
+    }
+
+
+    /**
+     * Tests the has() methods for correct behavoir:
+     * <ul>
+     *   <li>has(Object)</li>
+     *   <li>has(Object, boolean)</li>
+     *   <li>has(Object, Object)</li>
+     *   <li>has(Object, Object, boolean)</li>
+     * </ul>
+     *
+     * @throws NamingException
+     */
+    public void testHas() throws Exception
+    {
+        // test the has( Object ) method
+        assertTrue( table.has( 1L ) );
+        assertTrue( table.has( 2L ) );
+        assertTrue( table.has( 4L ) );
+        assertTrue( table.has( 5L ) );
+        assertFalse( table.has( 3L ) );
+        assertFalse( table.has( 0L ) );
+        assertFalse( table.has( 999L ) );
+
+        // test the has( Object, Object ) method
+        assertTrue( table.has( 1L, 1L ) );
+        assertTrue( table.has( 2L, 1L ) );
+        assertTrue( table.has( 4L, 1L ) );
+        assertTrue( table.has( 5L, 1L ) );
+        assertFalse( table.has( 5L, 0L ) );
+        assertFalse( table.has( 3L, 1L ) );
+        assertFalse( table.has( 1L, 999L ) );
+        assertFalse( table.has( 999L, 1L ) );
+
+        // test the has( Object, boolean ) method
+        assertFalse( table.has( Long.valueOf( 0 ), false ) ); // we do not have a key less than or equal to 0
+        assertTrue( table.has( Long.valueOf( 1 ), false ) ); // we do have a key less than or equal to 1
+        assertTrue( table.has( Long.valueOf( 0 ), true ) ); // we do have a key greater than or equal to 0
+        assertTrue( table.has( Long.valueOf( 1 ), true ) ); // we do have a key greater than or equal to 1
+        assertTrue( table.has( Long.valueOf( 5 ), true ) ); // we do have a key greater than or equal to 5
+        assertFalse( table.has( Long.valueOf( 6 ), true ) ); // we do NOT have a key greater than or equal to 11
+        assertFalse( table.has( Long.valueOf( 999 ), true ) ); // we do NOT have a key greater than or equal to 12
+
+        // test the has( Object, Object, boolean ) method
+        assertTrue( table.has( 1L, 0L, true ) );
+        assertTrue( table.has( 1L, 1L, true ) );
+        assertTrue( table.has( 1L, 2L, true ) );
+        assertFalse( table.has( 1L, 3L, true ) );
+        assertTrue( table.has( 1L, 0L, false ) );
+        assertFalse( table.has( 1L, -1L, false ) );
+    }
+    
+    
+    /**
+     * Tests the count() methods for correct behavoir:
+     * <ul>
+     *   <li>count()</li>
+     *   <li>count(Object)</li>
+     *   <li>count(Object, boolean)</li>
+     * </ul>
+     * 
+     * @throws Exception
+     */
+    public void testCount() throws Exception
+    {
+        // test the count() method
+        assertEquals( 6, table.count() );
+        
+        // test the count(Object) method
+        assertEquals( 3, table.count( 1L ) );
+        assertEquals( 0, table.count( 0L ) );
+        assertEquals( 1, table.count( 2L ) );
+        
+        // test the count( Object, boolean ) method 
+        // note for speed this count method returns the same as count()
+        assertEquals( table.count(), table.count( 1L, true ) );
+    }
+    
+    
+    /**
+     * Tests the get() method for correct behavoir.
+     * 
+     * @throws Exception
+     */
+    public void testGet() throws Exception
+    {
+        assertEquals( 0L, table.get( 1L ) );
+        assertEquals( 1L, table.get( 2L ) );
+        assertEquals( null, table.get( 3L ) );
+        assertEquals( 1L, table.get( 4L ) );
+        assertEquals( 1L, table.get( 5L ) );
+    }
+    
+    
+    /**
+     * Tests the listTuples() methods for correct behavoir:
+     * <ul>
+     *   <li>listTuples()</li>
+     *   <li>listTuples(Object)</li>
+     *   <li>listTuples(Object,boolean)</li>
+     *   <li>listTuples(Object,Object,boolean)</li>
+     * </ul>
+     * 
+     * @throws Exception
+     */
+    public void testListTuples() throws Exception
+    {
+        Tuple tuple;
+
+        // -------------------------------------------------------------------
+        // test the listTuples() method
+        // -------------------------------------------------------------------
+
+        NamingEnumeration tuples = table.listTuples();
+        
+        assertTrue( tuples.hasMore() ) ;
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 1L, tuple.getKey() );
+        assertEquals( 0L, tuple.getValue() );
+        
+        assertTrue( tuples.hasMore() ) ;
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 1L, tuple.getKey() );
+        assertEquals( 1L, tuple.getValue() );
+        
+        assertTrue( tuples.hasMore() ) ;
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 1L, tuple.getKey() );
+        assertEquals( 2L, tuple.getValue() );
+        
+        assertTrue( tuples.hasMore() ) ;
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 2L, tuple.getKey() );
+        assertEquals( 1L, tuple.getValue() );
+        
+        assertTrue( tuples.hasMore() ) ;
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 4L, tuple.getKey() );
+        assertEquals( 1L, tuple.getValue() );
+        
+        assertTrue( tuples.hasMore() ) ;
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 5L, tuple.getKey() );
+        assertEquals( 1L, tuple.getValue() );
+        
+        assertFalse( tuples.hasMore() );
+
+        // -------------------------------------------------------------------
+        // test the listTuples(Object) method
+        // -------------------------------------------------------------------
+
+        tuples = table.listTuples( 0L );
+        assertFalse( tuples.hasMore() );
+
+        tuples = table.listTuples( 2L );
+        assertTrue( tuples.hasMore() );
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 2L, tuple.getKey() );
+        assertEquals( 1L, tuple.getValue() );
+        assertFalse( tuples.hasMore() );
+        
+        tuples = table.listTuples( 1L );
+        assertTrue( tuples.hasMore() ) ;
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 1L, tuple.getKey() );
+        assertEquals( 0L, tuple.getValue() );
+        
+        assertTrue( tuples.hasMore() ) ;
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 1L, tuple.getKey() );
+        assertEquals( 1L, tuple.getValue() );
+        
+        assertTrue( tuples.hasMore() ) ;
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 1L, tuple.getKey() );
+        assertEquals( 2L, tuple.getValue() );
+        
+        assertFalse( tuples.hasMore() );
+        
+        // -------------------------------------------------------------------
+        // test the listTuples(Object, boolean) method
+        // -------------------------------------------------------------------
+
+        tuples = table.listTuples( 0L, false );
+        assertFalse( tuples.hasMore() );
+
+
+        tuples = table.listTuples( 1L, false );
+        assertTrue( tuples.hasMore() ) ;
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 1L, tuple.getKey() );
+        assertEquals( 2L, tuple.getValue() );
+        
+        assertTrue( tuples.hasMore() ) ;
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 1L, tuple.getKey() );
+        assertEquals( 1L, tuple.getValue() );
+        
+        assertTrue( tuples.hasMore() ) ;
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 1L, tuple.getKey() );
+        assertEquals( 0L, tuple.getValue() );
+        assertFalse( tuples.hasMore() );
+
+
+        tuples = table.listTuples( 2L, false );
+
+        assertTrue( tuples.hasMore() ) ;
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 2L, tuple.getKey() );
+        assertEquals( 1L, tuple.getValue() );
+
+        assertTrue( tuples.hasMore() ) ;
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 1L, tuple.getKey() );
+        assertEquals( 2L, tuple.getValue() );
+        
+        assertTrue( tuples.hasMore() ) ;
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 1L, tuple.getKey() );
+        assertEquals( 1L, tuple.getValue() );
+        
+        assertTrue( tuples.hasMore() ) ;
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 1L, tuple.getKey() );
+        assertEquals( 0L, tuple.getValue() );
+        assertFalse( tuples.hasMore() );
+
+        
+        tuples = table.listTuples( 6L, true );
+        assertFalse( tuples.hasMore() );
+
+        
+        tuples = table.listTuples( 5L, true );
+        assertTrue( tuples.hasMore() ) ;
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 5L, tuple.getKey() );
+        assertEquals( 1L, tuple.getValue() );
+        assertFalse( tuples.hasMore() );
+
+        
+        tuples = table.listTuples( 4L, true );
+        assertTrue( tuples.hasMore() ) ;
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 4L, tuple.getKey() );
+        assertEquals( 1L, tuple.getValue() );
+
+        assertTrue( tuples.hasMore() ) ;
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 5L, tuple.getKey() );
+        assertEquals( 1L, tuple.getValue() );
+        assertFalse( tuples.hasMore() );
+
+        // -------------------------------------------------------------------
+        // test the listTuples(Object,Object,boolean) method
+        // -------------------------------------------------------------------
+        
+        tuples = table.listTuples( 0L, 0L, true );
+        assertFalse( tuples.hasMore() );
+
+        tuples = table.listTuples( 0L, 0L, false );
+        assertFalse( tuples.hasMore() );
+        
+        tuples = table.listTuples( 2L, 0L, false );
+        assertFalse( tuples.hasMore() );
+        
+        tuples = table.listTuples( 2L, 99L, true );
+        assertFalse( tuples.hasMore() );
+        
+        tuples = table.listTuples( 2L, 1L, false );
+        assertTrue( tuples.hasMore() ) ;
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 2L, tuple.getKey() );
+        assertEquals( 1L, tuple.getValue() );
+        assertFalse( tuples.hasMore() );
+
+        tuples = table.listTuples( 2L, 99L, false );
+        assertTrue( tuples.hasMore() ) ;
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 2L, tuple.getKey() );
+        assertEquals( 1L, tuple.getValue() );
+        assertFalse( tuples.hasMore() );
+
+        
+        tuples = table.listTuples( 1L, 3L, true );
+        assertFalse( tuples.hasMore() );
+
+
+        tuples = table.listTuples( 1L, -1L, false );
+        assertFalse( tuples.hasMore() );
+
+
+        tuples = table.listTuples( 1L, 1L, true );
+
+        assertTrue( tuples.hasMore() ) ;
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 1L, tuple.getKey() );
+        assertEquals( 1L, tuple.getValue() );
+
+        assertTrue( tuples.hasMore() ) ;
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 1L, tuple.getKey() );
+        assertEquals( 2L, tuple.getValue() );
+
+        assertFalse( tuples.hasMore() );
+
+        
+        tuples = table.listTuples( 1L, 1L, false );
+
+        assertTrue( tuples.hasMore() ) ;
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 1L, tuple.getKey() );
+        assertEquals( 1L, tuple.getValue() );
+
+        assertTrue( tuples.hasMore() ) ;
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 1L, tuple.getKey() );
+        assertEquals( 0L, tuple.getValue() );
+
+        assertFalse( tuples.hasMore() );
+
+    }
+
+    
+    /**
+     * Tests the listValues() method for correct behavoir.
+     */
+    public void testListValues() throws Exception
+    {
+        // -------------------------------------------------------------------
+        // test the listValues(Object) method
+        // -------------------------------------------------------------------
+
+        NamingEnumeration<Object> values = table.listValues( 0L );
+        assertFalse( values.hasMore() );
+
+        values = table.listValues( 2L );
+        assertTrue( values.hasMore() );
+        Object value = values.next();
+        assertEquals( 1L, value );
+        assertFalse( values.hasMore() );
+        
+        values = table.listValues( 1L );
+        assertTrue( values.hasMore() ) ;
+        value = values.next();
+        assertEquals( 0L, value );
+        
+        assertTrue( values.hasMore() ) ;
+        value = values.next();
+        assertEquals( 1L, value );
+        
+        assertTrue( values.hasMore() ) ;
+        value = values.next();
+        assertEquals( 2L, value );
+        
+        assertFalse( values.hasMore() );
+    }
+    
+    
+    /**
+     * Tests the put() methods for correct behavior:
+     * <ul>
+     *   <li>put(Object, Object)</li>
+     *   <li>put(Object, NamingEnumeration)</li>
+     * </ul>
+     */
+    public void testPut() throws Exception
+    {
+        // put(Object,Object) already tested in setUp() tests the 
+        // this instead tests the NamingEnumeration overload
+        
+        NamingEnumeration values = new ArrayNE( new Object[] {
+            3L,
+            4L,
+            5L,
+            6L,
+        } );
+        
+        table.put( 1L, values );
+        assertFalse( values.hasMore() );
+        
+        values = table.listValues( 1L );
+        
+        assertTrue( values.hasMore() );
+        assertEquals( 0L, values.next() );
+        
+        assertTrue( values.hasMore() );
+        assertEquals( 1L, values.next() );
+        
+        assertTrue( values.hasMore() );
+        assertEquals( 2L, values.next() );
+        
+        assertTrue( values.hasMore() );
+        assertEquals( 3L, values.next() );
+        
+        assertTrue( values.hasMore() );
+        assertEquals( 4L, values.next() );
+        
+        assertTrue( values.hasMore() );
+        assertEquals( 5L, values.next() );
+        
+        assertTrue( values.hasMore() );
+        assertEquals( 6L, values.next() );
+        assertFalse( values.hasMore() );
+
+    
+        values = new ArrayNE( new Object[] {
+            3L,
+            4L,
+            5L,
+            6L,
+        } );
+        
+        table.put( 0L, values );
+        assertFalse( values.hasMore() );
+        
+        values = table.listValues( 0L );
+        
+        assertTrue( values.hasMore() );
+        assertEquals( 3L, values.next() );
+        
+        assertTrue( values.hasMore() );
+        assertEquals( 4L, values.next() );
+        
+        assertTrue( values.hasMore() );
+        assertEquals( 5L, values.next() );
+        
+        assertTrue( values.hasMore() );
+        assertEquals( 6L, values.next() );
+        assertFalse( values.hasMore() );
+    }
+    
+    
+    /**
+     * Tests the remove(Object) for correct behavoir:
+     */
+    public void testRemoveObject() throws Exception
+    {
+        // -------------------------------------------------------------------
+        // tests the remove(Object) method
+        // -------------------------------------------------------------------
+
+        try
+        {
+            table.remove( 0L );
+            fail( "should not get here trying to remove non-existent key" );
+        }
+        catch ( IllegalArgumentException e )
+        {
+        }
+        
+        Object value = table.remove( 2L );
+        assertEquals( 1L, value );
+        assertEquals( 5, table.count() );
+        
+        value = table.remove( 1L );
+        assertEquals( 0L, value ); // return first value of dups
+        assertEquals( 2, table.count() );
+    }
+    
+    
+    /**
+     * Tests the remove(Object,Object) for correct behavoir:
+     */
+    public void testRemoveObjectObject() throws Exception
+    {
+        // -------------------------------------------------------------------
+        // tests the remove(Object) method
+        // -------------------------------------------------------------------
+
+        Object value = table.remove( 0L, 0L );
+        assertNull( value );
+        
+        value = table.remove( 2L, 1L );
+        assertEquals( 1L, value );
+        assertEquals( 5, table.count() );
+        
+        value = table.remove( 1L, 2L );
+        assertEquals( 2L, value ); 
+        assertEquals( 4, table.count() );
+    }
+    
+    
+    /**
+     * Tests the remove(Object,NamingEnumeration) for correct behavoir:
+     */
+    public void testRemoveObjectNamingEnumeration() throws Exception
+    {
+        NamingEnumeration values = new ArrayNE( new Object[] {
+            1L,
+            2L
+        } );
+        
+        Object value = table.remove( 1L, values );
+        assertEquals( 1L, value );
+        assertEquals( 4, table.count() );
+    }
+    
+    
+    class ArrayNE extends ArrayEnumeration implements NamingEnumeration
+    {
+        public ArrayNE( Object[] array )
+        {
+            super( array );
+        }
+
+        public void close() throws NamingException
+        {
+        }
+
+        public boolean hasMore() throws NamingException
+        {
+            return hasMoreElements();
+        }
+
+        public Object next() throws NamingException
+        {
+            return nextElement();
+        }
+    }
+}
+
+    
\ No newline at end of file
diff --git a/old_trunk/core/src/test/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmTableNoDupsTest.java b/old_trunk/core/src/test/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmTableNoDupsTest.java
new file mode 100644
index 0000000..86ccd3c
--- /dev/null
+++ b/old_trunk/core/src/test/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmTableNoDupsTest.java
@@ -0,0 +1,498 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree.jdbm;
+
+
+import java.io.File;
+import java.io.Serializable;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+
+import org.apache.directory.server.core.partition.impl.btree.Tuple;
+import org.apache.directory.server.core.partition.impl.btree.TupleRenderer;
+import org.apache.directory.server.schema.SerializableComparator;
+import org.apache.directory.shared.ldap.util.ArrayEnumeration;
+import org.apache.directory.shared.ldap.util.LongComparator;
+
+import jdbm.RecordManager;
+import jdbm.helper.LongSerializer;
+import jdbm.recman.BaseRecordManager;
+
+import junit.framework.TestCase;
+
+
+/**
+ * Tests for JdbmTable.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class JdbmTableNoDupsTest extends TestCase implements Serializable
+{
+    private static final long serialVersionUID = 1L;
+    private transient File tempFile = null;
+    private transient RecordManager rm = null;
+    private final LongComparator biComparator = new LongComparator();
+    private final SerializableComparator serializableComparator = new SerializableComparator( "integerMatchingRule" )
+    {
+        private static final long serialVersionUID = 1L;
+
+        public int compare( Object o1, Object o2 )
+        {
+            return biComparator.compare( o1, o2 );
+        }
+    };
+
+
+    transient JdbmTable table;
+
+
+    /**
+     * Here's what the table looks like:
+     * <pre>
+     * .-.-.
+     * |1|1|
+     * |2|1|
+     * |4|1|
+     * |5|1|
+     * .-.-. 
+     * </pre>
+     */
+    public void setUp() throws Exception
+    {
+        tempFile = File.createTempFile( "jdbm", "test" );
+        rm = new BaseRecordManager( tempFile.getAbsolutePath() );
+
+        // make sure the table does not use duplicates
+        table = new JdbmTable( "test", rm, serializableComparator, LongSerializer.INSTANCE, LongSerializer.INSTANCE );
+
+        table.put( 1L, 1L );
+        table.put( 2L, 1L );
+        table.put( 4L, 1L );
+        table.put( 5L, 1L );
+    }
+
+    protected void tearDown() throws Exception
+    {
+        String tmp = tempFile.getAbsolutePath();
+        new File( tmp ).delete();
+        new File( tmp + ".db" ).delete();
+        new File( tmp + ".lg" ).delete();
+    }
+
+    
+    public void testCatchAll() throws Exception
+    {
+        assertFalse( table.isDupsEnabled() );
+        assertFalse( table.isSortedDupsEnabled() );
+        assertEquals( "test", table.getName() );
+        assertNotNull( table.getComparator() );
+        assertNull( table.getRenderer() );
+        table.setRenderer( new TupleRenderer() {
+            public String getKeyString( Object key )
+            {
+                return null;
+            }
+            public String getValueString( Object value )
+            {
+                return null;
+            }} );
+        assertNotNull( table.getRenderer() );
+        table.sync();
+        table.close();
+
+        table = new JdbmTable( "test", rm, serializableComparator, LongSerializer.INSTANCE, LongSerializer.INSTANCE );
+    }
+    
+
+    /**
+     * Tests the has() methods for correct behavoir:
+     * <ul>
+     *   <li>has(Object)</li>
+     *   <li>has(Object, boolean)</li>
+     *   <li>has(Object, Object)</li>
+     *   <li>has(Object, Object, boolean)</li>
+     * </ul>
+     *
+     * @throws NamingException
+     */
+    public void testHas() throws Exception
+    {
+        // test the has( Object ) method
+        assertTrue( table.has( 1L ) );
+        assertTrue( table.has( 2L ) );
+        assertTrue( table.has( 4L ) );
+        assertTrue( table.has( 5L ) );
+        assertFalse( table.has( 3L ) );
+        assertFalse( table.has( 0L ) );
+        assertFalse( table.has( 999L ) );
+
+        // test the has( Object, Object ) method
+        assertTrue( table.has( 1L, 1L ) );
+        assertTrue( table.has( 2L, 1L ) );
+        assertTrue( table.has( 4L, 1L ) );
+        assertTrue( table.has( 5L, 1L ) );
+        assertFalse( table.has( 5L, 0L ) );
+        assertFalse( table.has( 3L, 1L ) );
+        assertFalse( table.has( 1L, 999L ) );
+        assertFalse( table.has( 999L, 1L ) );
+
+        // test the has( Object, boolean ) method
+        assertFalse( table.has( Long.valueOf(0), false ) ); // we do not have a key less than or equal to 0
+        assertTrue( table.has( Long.valueOf(1), false ) ); // we do have a key less than or equal to 1
+        assertTrue( table.has( Long.valueOf(0), true ) ); // we do have a key greater than or equal to 0
+        assertTrue( table.has( Long.valueOf(1), true ) ); // we do have a key greater than or equal to 1
+        assertTrue( table.has( Long.valueOf(5), true ) ); // we do have a key greater than or equal to 5
+        assertFalse( table.has( Long.valueOf(6), true ) ); // we do NOT have a key greater than or equal to 11
+        assertFalse( table.has( Long.valueOf(999), true ) ); // we do NOT have a key greater than or equal to 12
+
+        // test the has( Object, Object, boolean ) method
+        try
+        {
+            table.has( 1L, 0L, true );
+        }
+        catch ( UnsupportedOperationException usoe )
+        {
+            
+        }
+    }
+    
+    
+    /**
+     * Tests the count() methods for correct behavoir:
+     * <ul>
+     *   <li>count()</li>
+     *   <li>count(Object)</li>
+     *   <li>count(Object, boolean)</li>
+     * </ul>
+     * 
+     * @throws Exception
+     */
+    public void testCount() throws Exception
+    {
+        // test the count() method
+        assertEquals( 4, table.count() );
+        
+        // test the count(Object) method
+        assertEquals( 1, table.count( 1L ) );
+        assertEquals( 0, table.count( 0L ) );
+        assertEquals( 1, table.count( 2L ) );
+        
+        // test the count( Object, boolean ) method 
+        // note for speed this count method returns the same as count()
+        assertEquals( table.count(), table.count( 1L, true ) );
+    }
+    
+    
+    /**
+     * Tests the get() method for correct behavoir.
+     * 
+     * @throws Exception
+     */
+    public void testGet() throws Exception
+    {
+        assertEquals( 1L, table.get( 1L ) );
+        assertEquals( 1L, table.get( 2L ) );
+        assertEquals( null, table.get( 3L ) );
+        assertEquals( 1L, table.get( 4L ) );
+        assertEquals( 1L, table.get( 5L ) );
+    }
+    
+    
+    /**
+     * Tests the listTuples() methods for correct behavoir:
+     * <ul>
+     *   <li>listTuples()</li>
+     *   <li>listTuples(Object)</li>
+     *   <li>listTuples(Object,boolean)</li>
+     *   <li>listTuples(Object,Object,boolean)</li>
+     * </ul>
+     * 
+     * @throws Exception
+     */
+    public void testListTuples() throws Exception
+    {
+        Tuple tuple;
+
+        // -------------------------------------------------------------------
+        // test the listTuples() method
+        // -------------------------------------------------------------------
+
+        NamingEnumeration tuples = table.listTuples();
+        
+        assertTrue( tuples.hasMore() ) ;
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 1L, tuple.getKey() );
+        assertEquals( 1L, tuple.getValue() );
+        
+        assertTrue( tuples.hasMore() ) ;
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 2L, tuple.getKey() );
+        assertEquals( 1L, tuple.getValue() );
+        
+        assertTrue( tuples.hasMore() ) ;
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 4L, tuple.getKey() );
+        assertEquals( 1L, tuple.getValue() );
+        
+        assertTrue( tuples.hasMore() ) ;
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 5L, tuple.getKey() );
+        assertEquals( 1L, tuple.getValue() );
+        
+        assertFalse( tuples.hasMore() );
+
+        // -------------------------------------------------------------------
+        // test the listTuples(Object) method
+        // -------------------------------------------------------------------
+
+        tuples = table.listTuples( 0L );
+        assertFalse( tuples.hasMore() );
+
+
+        tuples = table.listTuples( 2L );
+        assertTrue( tuples.hasMore() );
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 2L, tuple.getKey() );
+        assertEquals( 1L, tuple.getValue() );
+        assertFalse( tuples.hasMore() );
+        
+        // -------------------------------------------------------------------
+        // test the listTuples(Object, boolean) method
+        // -------------------------------------------------------------------
+
+        tuples = table.listTuples( 0L, false );
+        assertFalse( tuples.hasMore() );
+
+
+        tuples = table.listTuples( 1L, false );
+        assertTrue( tuples.hasMore() ) ;
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 1L, tuple.getKey() );
+        assertEquals( 1L, tuple.getValue() );
+        assertFalse( tuples.hasMore() );
+
+
+        tuples = table.listTuples( 2L, false );
+
+        assertTrue( tuples.hasMore() ) ;
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 2L, tuple.getKey() );
+        assertEquals( 1L, tuple.getValue() );
+
+        assertTrue( tuples.hasMore() ) ;
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 1L, tuple.getKey() );
+        assertEquals( 1L, tuple.getValue() );
+        assertFalse( tuples.hasMore() );
+
+        
+        tuples = table.listTuples( 6L, true );
+        assertFalse( tuples.hasMore() );
+
+        
+        tuples = table.listTuples( 5L, true );
+        assertTrue( tuples.hasMore() ) ;
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 5L, tuple.getKey() );
+        assertEquals( 1L, tuple.getValue() );
+        assertFalse( tuples.hasMore() );
+
+        
+        tuples = table.listTuples( 4L, true );
+        assertTrue( tuples.hasMore() ) ;
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 4L, tuple.getKey() );
+        assertEquals( 1L, tuple.getValue() );
+
+        assertTrue( tuples.hasMore() ) ;
+        tuple = ( Tuple ) tuples.next();
+        assertEquals( 5L, tuple.getKey() );
+        assertEquals( 1L, tuple.getValue() );
+        assertFalse( tuples.hasMore() );
+
+        // -------------------------------------------------------------------
+        // test the listTuples(Object,Object,boolean) method
+        // -------------------------------------------------------------------
+
+        try
+        {
+            tuples = table.listTuples( 0L, 0L, true );
+        }
+        catch( UnsupportedOperationException e )
+        {
+            
+        }
+    }
+
+    
+    /**
+     * Tests the listValues() method for correct behavoir.
+     */
+    public void testListValues() throws Exception
+    {
+        // -------------------------------------------------------------------
+        // test the listValues(Object) method
+        // -------------------------------------------------------------------
+
+        NamingEnumeration<Object> values = table.listValues( 0L );
+        assertFalse( values.hasMore() );
+
+        values = table.listValues( 2L );
+        assertTrue( values.hasMore() );
+        Object value = values.next();
+        assertEquals( 1L, value );
+        assertFalse( values.hasMore() );
+        
+        values = table.listValues( 1L );
+        assertTrue( values.hasMore() ) ;
+        value = values.next();
+        assertEquals( 1L, value );
+        assertFalse( values.hasMore() );
+    }
+    
+    
+    /**
+     * Tests the put() methods for correct behavior:
+     * <ul>
+     *   <li>put(Object, Object)</li>
+     *   <li>put(Object, NamingEnumeration)</li>
+     * </ul>
+     */
+    public void testPut() throws Exception
+    {
+        // put(Object,Object) already tested in setUp() tests the 
+        // this instead tests the NamingEnumeration overload
+        
+        NamingEnumeration values = new ArrayNE( new Object[] {
+            3L,
+            4L,
+            5L,
+            6L,
+        } );
+
+        try
+        {
+            table.put( 1L, values );
+        }
+        catch( UnsupportedOperationException e )
+        {
+        }
+    }
+    
+    
+    /**
+     * Tests the remove(Object) for correct behavoir:
+     */
+    public void testRemoveObject() throws Exception
+    {
+        // -------------------------------------------------------------------
+        // tests the remove(Object) method
+        // -------------------------------------------------------------------
+
+        try
+        {
+            table.remove( 0L );
+            fail( "should not get here trying to remove non-existent key" );
+        }
+        catch ( IllegalArgumentException e )
+        {
+        }
+        
+        Object value = table.remove( 2L );
+        assertEquals( 1L, value );
+        assertEquals( 3, table.count() );
+        
+        value = table.remove( 1L );
+        assertEquals( 1L, value );
+        assertEquals( 2, table.count() );
+    }
+    
+    
+    /**
+     * Tests the remove(Object,Object) for correct behavoir:
+     */
+    public void testRemoveObjectObject() throws Exception
+    {
+        // -------------------------------------------------------------------
+        // tests the remove(Object) method
+        // -------------------------------------------------------------------
+
+        Object value = table.remove( 0L, 0L );
+        assertNull( value );
+        
+        value = table.remove( 2L, 1L );
+        assertEquals( 1L, value );
+        assertEquals( 3, table.count() );
+        
+        value = table.remove( 1L, 2L );
+        assertEquals( null, value ); 
+        assertEquals( 3, table.count() );
+    }
+    
+    
+    /**
+     * Tests the remove(Object,NamingEnumeration) for correct behavoir:
+     */
+    public void testRemoveObjectNamingEnumeration() throws Exception
+    {
+        NamingEnumeration values = new ArrayNE( new Object[] {
+            1L,
+            2L
+        } );
+        
+        try
+        {
+            table.remove( 1L, values );
+        }
+        catch( UnsupportedOperationException e )
+        {
+            
+        }
+
+        values.close();
+    }
+    
+    
+    class ArrayNE extends ArrayEnumeration implements NamingEnumeration
+    {
+        public ArrayNE( Object[] array )
+        {
+            super( array );
+        }
+
+        public void close() throws NamingException
+        {
+        }
+
+        public boolean hasMore() throws NamingException
+        {
+            return hasMoreElements();
+        }
+
+        public Object next() throws NamingException
+        {
+            return nextElement();
+        }
+    }
+}
+
+    
\ No newline at end of file
diff --git a/old_trunk/core/src/test/java/org/apache/directory/server/core/partition/impl/btree/jdbm/StringSerializerTest.java b/old_trunk/core/src/test/java/org/apache/directory/server/core/partition/impl/btree/jdbm/StringSerializerTest.java
new file mode 100644
index 0000000..b4f1bea
--- /dev/null
+++ b/old_trunk/core/src/test/java/org/apache/directory/server/core/partition/impl/btree/jdbm/StringSerializerTest.java
@@ -0,0 +1,74 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree.jdbm;
+
+import java.io.IOException;
+
+import org.apache.commons.lang.RandomStringUtils;
+
+import junit.framework.TestCase;
+
+/**
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class StringSerializerTest extends TestCase
+{
+    public void testRandom() throws IOException
+    {
+        StringSerializer serializer = new StringSerializer();
+        for ( int ii = 0; ii < 100; ii++ )
+        {
+            String str = RandomStringUtils.random( ii );
+            byte [] serialized = serializer.serialize( str );
+            String deserialized = ( String ) serializer.deserialize( serialized );
+            assertEquals( str, deserialized );
+        }
+    }
+    
+    
+    char getChar( byte[] bites )
+    {
+        int ch = bites[0] << 8 & 0x0000FF00;
+        ch |= bites[1] & 0x000000FF;
+        return ( char ) ch;
+    }
+    
+    
+    byte[] getBytes( char ch )
+    {
+        byte[] bites = new byte[2];
+        bites[0] = ( byte ) ( ch >> 8 & 0x00FF );
+        bites[1] = ( byte ) ( ch & 0x00FF );
+        return bites;
+    }
+
+    
+    public void testConversion()
+    {
+        for ( char ch = 0; ch < 16383; ch++ )
+        {
+            byte[] bites = getBytes( ch );
+            char deserialized = getChar( bites );
+            assertEquals( ch, deserialized );
+        }
+    }
+}
diff --git a/old_trunk/core/src/test/java/org/apache/directory/server/core/partition/tree/PartitionTreeTest.java b/old_trunk/core/src/test/java/org/apache/directory/server/core/partition/tree/PartitionTreeTest.java
new file mode 100644
index 0000000..ea4d115
--- /dev/null
+++ b/old_trunk/core/src/test/java/org/apache/directory/server/core/partition/tree/PartitionTreeTest.java
@@ -0,0 +1,181 @@
+/*
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *   or more contributor license agreements.  See the NOTICE file
+ *   distributed with this work for additional information
+ *   regarding copyright ownership.  The ASF licenses this file
+ *   to you 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.
+ *
+ */
+
+package org.apache.directory.server.core.partition.tree;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.core.partition.Partition;
+import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmPartition;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.junit.Test;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+
+/**
+ * Test the partition tree manipulations.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class PartitionTreeTest
+{
+    /**
+     * Test the addition of a single partition
+     */
+    @Test public void testNewPartitionTree() throws NamingException
+    {
+        /** A structure to hold all the partitions */
+        BranchNode partitionLookupTree = new BranchNode();
+        
+        LdapDN suffix = new LdapDN( "dc=example, dc=com" );
+        Partition partition = new JdbmPartition();
+        partition.setSuffix( suffix.getUpName() );
+        
+        Node node = partitionLookupTree.recursivelyAddPartition( partitionLookupTree, suffix, 0, partition );
+        
+        assertNotNull( node );
+        assertTrue( node instanceof BranchNode );
+        assertTrue( ((BranchNode)node).contains( "dc=com" ) );
+        
+        Node child = ((BranchNode)node).getChild( "dc=com" );
+        assertTrue( child instanceof BranchNode );
+        assertTrue( ((BranchNode)child).contains( "dc=example" ) );
+
+        child = ((BranchNode)child).getChild( "dc=example" );
+        assertEquals( "dc=example, dc=com", ((LeafNode)child).getPartition().getSuffix() );
+    }
+
+
+    /**
+     * Test the addition of a two disjointed partition
+     */
+    @Test public void testNewPartitionTree2Nodes() throws NamingException
+    {
+        /** A structure to hold all the partitions */
+        BranchNode partitionLookupTree = new BranchNode();
+        
+        LdapDN suffix1 = new LdapDN( "dc=example, dc=com" );
+        Partition partition1 = new JdbmPartition();
+        partition1.setSuffix( suffix1.getUpName() );
+        
+        Node node = partitionLookupTree.recursivelyAddPartition( partitionLookupTree, suffix1, 0, partition1 );
+        
+        LdapDN suffix2 = new LdapDN( "ou=system" );
+        Partition partition2 = new JdbmPartition();
+        partition2.setSuffix( suffix2.getUpName() );
+        
+        node = partitionLookupTree.recursivelyAddPartition( partitionLookupTree, suffix2, 0, partition2 );
+
+        assertNotNull( node );
+        assertTrue( node instanceof BranchNode );
+        assertTrue( ((BranchNode)node).contains( "ou=system" ) );
+        assertTrue( ((BranchNode)node).contains( "dc=com" ) );
+        
+        Node child = ((BranchNode)node).getChild( "ou=system" );
+        assertTrue( child instanceof LeafNode );
+        assertEquals( "ou=system", ((LeafNode)child).getPartition().getSuffix() );
+
+        child = ((BranchNode)node).getChild( "dc=com" );
+        assertTrue( child instanceof BranchNode );
+        assertTrue( ((BranchNode)child).contains( "dc=example" ) );
+        
+        child = ((BranchNode)child).getChild( "dc=example" );
+        assertTrue( child instanceof LeafNode );
+        assertEquals( "dc=example, dc=com", ((LeafNode)child).getPartition().getSuffix() );
+    }
+
+
+    /**
+     * Test the addition of a two overlapping partitions
+     */
+    @Test public void testNewPartitionTree2OverlapingNodes() throws NamingException
+    {
+        /** A structure to hold all the partitions */
+        BranchNode partitionLookupTree = new BranchNode();
+        
+        LdapDN suffix1 = new LdapDN( "dc=com" );
+        Partition partition1 = new JdbmPartition();
+        partition1.setSuffix( suffix1.getUpName() );
+        
+        partitionLookupTree.recursivelyAddPartition( partitionLookupTree, suffix1, 0, partition1 );
+        
+        LdapDN suffix2 = new LdapDN( "dc=example, dc=com" );
+        Partition partition2 = new JdbmPartition();
+        partition2.setSuffix( suffix2.getUpName() );
+        
+        try
+        {
+            partitionLookupTree.recursivelyAddPartition( partitionLookupTree, suffix2, 0, partition2 );
+            fail();
+        }
+        catch ( NamingException ne )
+        {
+            assertTrue( true );
+        }
+    }
+
+
+    /**
+     * Test the addition of a two partitions with the same root
+     */
+    @Test public void testNewPartitionTree2NodesWithSameRoot() throws NamingException
+    {
+        /** A structure to hold all the partitions */
+        BranchNode partitionLookupTree = new BranchNode();
+        
+        LdapDN suffix1 = new LdapDN( "dc=example1, dc=com" );
+        Partition partition1 = new JdbmPartition();
+        partition1.setSuffix( suffix1.getUpName() );
+        
+        partitionLookupTree.recursivelyAddPartition( partitionLookupTree, suffix1, 0, partition1 );
+        
+        LdapDN suffix2 = new LdapDN( "dc=example2, dc=com" );
+        Partition partition2 = new JdbmPartition();
+        partition2.setSuffix( suffix2.getUpName() );
+        
+        Node node = partitionLookupTree.recursivelyAddPartition( partitionLookupTree, suffix2, 0, partition2 );
+
+        assertNotNull( node );
+        
+        assertTrue( node instanceof BranchNode );
+        assertTrue( ((BranchNode)node).contains( "dc=com" ) );
+        
+        Node child = ((BranchNode)node).getChild( "dc=com" );
+        assertTrue( child instanceof BranchNode );
+
+        child = ((BranchNode)node).getChild( "dc=com" );
+        assertTrue( child instanceof BranchNode );
+        assertTrue( ((BranchNode)child).contains( "dc=example1" ) );
+        assertTrue( ((BranchNode)child).contains( "dc=example2" ) );
+        
+        Node child1 = ((BranchNode)child).getChild( "dc=example1" );
+        assertTrue( child1 instanceof LeafNode );
+        assertEquals( "dc=example1, dc=com", ((LeafNode)child1).getPartition().getSuffix() );
+
+        Node child2 = ((BranchNode)child).getChild( "dc=example1" );
+        assertTrue( child2 instanceof LeafNode );
+        assertEquals( "dc=example1, dc=com", ((LeafNode)child2).getPartition().getSuffix() );
+    }
+}
diff --git a/old_trunk/core/src/test/java/org/apache/directory/server/core/prefs/PreferencesUtilsTest.java b/old_trunk/core/src/test/java/org/apache/directory/server/core/prefs/PreferencesUtilsTest.java
new file mode 100644
index 0000000..012f948
--- /dev/null
+++ b/old_trunk/core/src/test/java/org/apache/directory/server/core/prefs/PreferencesUtilsTest.java
@@ -0,0 +1,83 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.prefs;
+
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.constants.ServerDNConstants;
+import org.apache.directory.server.core.prefs.PreferencesUtils;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+import junit.framework.TestCase;
+
+
+/**
+ * Test caseses for preference utility methods.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class PreferencesUtilsTest extends TestCase
+{
+    /**
+     * Tests to confirm the toSysDn() method can translate an absolute
+     * preference node path into an LDAP distinguished name.
+     *
+     * @throws NamingException if there are problems transforming the name
+     */
+    public void testToSysDn() throws NamingException
+    {
+        // simple test
+        String expectedDN = "prefNodeName=kerberos,prefNodeName=apache,prefNodeName=org," +
+                ServerDNConstants.SYSPREFROOT_SYSTEM_DN;
+        
+        String test = "/org/apache/kerberos/";
+
+        LdapDN dn = ( LdapDN ) PreferencesUtils.toSysDn( test );
+
+        assertEquals( expectedDN, dn.getUpName() );
+
+        // simple test without trailing '/'
+
+        test = "/org/apache/kerberos";
+
+        dn = ( LdapDN ) PreferencesUtils.toSysDn( test );
+
+        assertEquals( expectedDN, dn.getUpName() );
+
+        // basis condition tests
+
+        test = "/";
+
+        dn = ( LdapDN ) PreferencesUtils.toSysDn( test );
+
+        assertEquals( ServerDNConstants.SYSPREFROOT_SYSTEM_DN, dn.getUpName() );
+
+        // endpoint tests
+
+        test = "//////";
+
+        dn = ( LdapDN ) PreferencesUtils.toSysDn( test );
+
+        assertEquals( ServerDNConstants.SYSPREFROOT_SYSTEM_DN, dn.getUpName() );
+
+    }
+}
diff --git a/old_trunk/core/src/test/java/org/apache/directory/server/core/referral/ReferralLutTest.java b/old_trunk/core/src/test/java/org/apache/directory/server/core/referral/ReferralLutTest.java
new file mode 100644
index 0000000..e27b39a
--- /dev/null
+++ b/old_trunk/core/src/test/java/org/apache/directory/server/core/referral/ReferralLutTest.java
@@ -0,0 +1,247 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.referral;
+
+
+import javax.naming.NamingException;
+
+import junit.framework.TestCase;
+
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+
+/**
+ * Unit tests for ReferralLut.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class ReferralLutTest extends TestCase
+{
+    public void testNullLimits()
+    {
+        ReferralLut lut = new ReferralLut();
+        try
+        {
+            lut.isReferral( ( String ) null );
+            fail( "can't get here" );
+        }
+        catch ( IllegalArgumentException e )
+        {
+        }
+        try
+        {
+            lut.isReferral( ( LdapDN ) null );
+            fail( "can't get here" );
+        }
+        catch ( IllegalArgumentException e )
+        {
+        }
+        try
+        {
+            lut.getFarthestReferralAncestor( ( LdapDN ) null );
+            fail( "can't get here" );
+        }
+        catch ( IllegalArgumentException e )
+        {
+        }
+        try
+        {
+            lut.getNearestReferralAncestor( ( LdapDN ) null );
+            fail( "can't get here" );
+        }
+        catch ( IllegalArgumentException e )
+        {
+        }
+        try
+        {
+            lut.referralAdded( ( LdapDN ) null );
+            fail( "can't get here" );
+        }
+        catch ( IllegalArgumentException e )
+        {
+        }
+        try
+        {
+            lut.referralAdded( ( String ) null );
+            fail( "can't get here" );
+        }
+        catch ( IllegalArgumentException e )
+        {
+        }
+        try
+        {
+            lut.referralDeleted( ( LdapDN ) null );
+            fail( "can't get here" );
+        }
+        catch ( IllegalArgumentException e )
+        {
+        }
+        try
+        {
+            lut.referralDeleted( ( String ) null );
+            fail( "can't get here" );
+        }
+        catch ( IllegalArgumentException e )
+        {
+        }
+        try
+        {
+            lut.referralChanged( ( LdapDN ) null, ( LdapDN ) null );
+            fail( "can't get here" );
+        }
+        catch ( IllegalArgumentException e )
+        {
+        }
+        try
+        {
+            lut.referralChanged( ( String ) null, ( String ) null );
+            fail( "can't get here" );
+        }
+        catch ( IllegalArgumentException e )
+        {
+        }
+        try
+        {
+            lut.referralChanged( ( LdapDN ) null, ( String ) null );
+            fail( "can't get here" );
+        }
+        catch ( IllegalArgumentException e )
+        {
+        }
+        try
+        {
+            lut.referralChanged( ( String ) null, ( LdapDN ) null );
+            fail( "can't get here" );
+        }
+        catch ( IllegalArgumentException e )
+        {
+        }
+    }
+
+
+    public void testUpdateOperations() throws NamingException
+    {
+        String dn = "ou=users,ou=system";
+        LdapDN name = new LdapDN( dn );
+        ReferralLut lut = new ReferralLut();
+
+        // some add delete tests
+        assertFalse( lut.isReferral( dn ) );
+        lut.referralAdded( dn );
+        assertTrue( lut.isReferral( dn ) );
+        lut.referralDeleted( dn );
+        assertFalse( lut.isReferral( dn ) );
+
+        assertFalse( lut.isReferral( name ) );
+        lut.referralAdded( name );
+        assertTrue( lut.isReferral( name ) );
+        lut.referralDeleted( name );
+        assertFalse( lut.isReferral( name ) );
+
+        assertFalse( lut.isReferral( name ) );
+        lut.referralAdded( dn );
+        assertTrue( lut.isReferral( name ) );
+        lut.referralDeleted( name );
+        assertFalse( lut.isReferral( name ) );
+
+        assertFalse( lut.isReferral( dn ) );
+        lut.referralAdded( name );
+        assertTrue( lut.isReferral( dn ) );
+        lut.referralDeleted( dn );
+        assertFalse( lut.isReferral( dn ) );
+
+        assertFalse( lut.isReferral( name ) );
+        lut.referralAdded( dn );
+        assertTrue( lut.isReferral( name ) );
+        lut.referralDeleted( dn );
+        assertFalse( lut.isReferral( name ) );
+
+        assertFalse( lut.isReferral( dn ) );
+        lut.referralAdded( name );
+        assertTrue( lut.isReferral( dn ) );
+        lut.referralDeleted( name );
+        assertFalse( lut.isReferral( dn ) );
+
+        // change (rename and move) tests
+        String newDn = "ou=people,ou=system";
+        LdapDN newName = new LdapDN( newDn );
+
+        assertFalse( lut.isReferral( dn ) );
+        lut.referralAdded( dn );
+        assertTrue( lut.isReferral( dn ) );
+        lut.referralChanged( dn, newDn );
+        assertFalse( lut.isReferral( dn ) );
+        assertTrue( lut.isReferral( newDn ) );
+        lut.referralDeleted( dn );
+
+        assertFalse( lut.isReferral( name ) );
+        lut.referralAdded( name );
+        assertTrue( lut.isReferral( name ) );
+        lut.referralChanged( name, newName );
+        assertFalse( lut.isReferral( name ) );
+        assertTrue( lut.isReferral( newName ) );
+        lut.referralDeleted( name );
+
+        assertFalse( lut.isReferral( dn ) );
+        lut.referralAdded( dn );
+        assertTrue( lut.isReferral( dn ) );
+        lut.referralChanged( dn, newName );
+        assertFalse( lut.isReferral( dn ) );
+        assertTrue( lut.isReferral( newDn ) );
+        lut.referralDeleted( dn );
+
+        assertFalse( lut.isReferral( dn ) );
+        lut.referralAdded( dn );
+        assertTrue( lut.isReferral( dn ) );
+        lut.referralChanged( name, newDn );
+        assertFalse( lut.isReferral( dn ) );
+        assertTrue( lut.isReferral( newDn ) );
+        lut.referralDeleted( dn );
+    }
+
+
+    public void testReferralAncestors() throws NamingException
+    {
+        LdapDN ancestor = new LdapDN( "ou=users,ou=system" );
+        LdapDN farthest = new LdapDN( "ou=system" );
+        LdapDN nearest = new LdapDN( "ou=apache,ou=users,ou=system" );
+        LdapDN testDn = new LdapDN( "cn=Alex Karasulu,ou=apache,ou=users,ou=system" );
+        ReferralLut lut = new ReferralLut();
+        assertNull( lut.getNearestReferralAncestor( testDn ) );
+        assertNull( lut.getFarthestReferralAncestor( testDn ) );
+        lut.referralAdded( testDn );
+        assertNull( lut.getNearestReferralAncestor( testDn ) );
+        assertNull( lut.getFarthestReferralAncestor( testDn ) );
+        lut.referralDeleted( testDn );
+        lut.referralAdded( ancestor );
+        assertEquals( ancestor, lut.getNearestReferralAncestor( testDn ) );
+        assertEquals( ancestor, lut.getFarthestReferralAncestor( testDn ) );
+        lut.referralAdded( testDn );
+        assertEquals( ancestor, lut.getNearestReferralAncestor( testDn ) );
+        assertEquals( ancestor, lut.getFarthestReferralAncestor( testDn ) );
+        lut.referralAdded( nearest );
+        assertEquals( nearest, lut.getNearestReferralAncestor( testDn ) );
+        assertEquals( ancestor, lut.getFarthestReferralAncestor( testDn ) );
+        lut.referralAdded( farthest );
+        assertEquals( nearest, lut.getNearestReferralAncestor( testDn ) );
+        assertEquals( farthest, lut.getFarthestReferralAncestor( testDn ) );
+    }
+}
diff --git a/old_trunk/core/src/test/java/org/apache/directory/server/core/schema/PartitionSchemaLoaderTest.java b/old_trunk/core/src/test/java/org/apache/directory/server/core/schema/PartitionSchemaLoaderTest.java
new file mode 100644
index 0000000..51379ea
--- /dev/null
+++ b/old_trunk/core/src/test/java/org/apache/directory/server/core/schema/PartitionSchemaLoaderTest.java
@@ -0,0 +1,296 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.schema;
+
+
+import org.apache.directory.server.core.DefaultDirectoryService;
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.entry.DefaultServerEntry;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.partition.impl.btree.Index;
+import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmIndex;
+import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmPartition;
+import org.apache.directory.server.schema.SerializableComparator;
+import org.apache.directory.server.schema.bootstrap.ApacheSchema;
+import org.apache.directory.server.schema.bootstrap.ApachemetaSchema;
+import org.apache.directory.server.schema.bootstrap.BootstrapSchemaLoader;
+import org.apache.directory.server.schema.bootstrap.CoreSchema;
+import org.apache.directory.server.schema.bootstrap.Schema;
+import org.apache.directory.server.schema.bootstrap.SystemSchema;
+import org.apache.directory.server.schema.bootstrap.partition.SchemaPartitionExtractor;
+import org.apache.directory.server.schema.registries.DefaultOidRegistry;
+import org.apache.directory.server.schema.registries.DefaultRegistries;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+
+import javax.naming.NamingException;
+import java.io.File;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+
+/**
+ * Tests the partition schema loader.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class PartitionSchemaLoaderTest
+{
+    private static Registries registries;
+    private static DirectoryService directoryService;
+    private static JdbmPartition schemaPartition;
+
+
+    @BeforeClass public static void setUp() throws Exception
+    {
+        // setup working directory
+        directoryService = new DefaultDirectoryService();
+        File workingDirectory = new File( System.getProperty( "workingDirectory", System.getProperty( "user.dir" ) ) );
+        
+        if ( ! workingDirectory.exists() )
+        {
+            workingDirectory.mkdirs();
+        }
+        
+        directoryService.setWorkingDirectory( workingDirectory );
+        
+        // --------------------------------------------------------------------
+        // Load the bootstrap schemas to start up the schema partition
+        // --------------------------------------------------------------------
+
+        // setup temporary loader and temp registry 
+        BootstrapSchemaLoader loader = new BootstrapSchemaLoader();
+        registries = new DefaultRegistries( "bootstrap", loader, new DefaultOidRegistry() );
+        directoryService.setRegistries( registries );
+        
+        // load essential bootstrap schemas 
+        Set<Schema> bootstrapSchemas = new HashSet<Schema>();
+        bootstrapSchemas.add( new ApachemetaSchema() );
+        bootstrapSchemas.add( new ApacheSchema() );
+        bootstrapSchemas.add( new CoreSchema() );
+        bootstrapSchemas.add( new SystemSchema() );
+        loader.loadWithDependencies( bootstrapSchemas, registries );
+        
+        // run referential integrity tests
+        List<Throwable> errors = registries.checkRefInteg();
+        
+        if ( !errors.isEmpty() )
+        {
+            NamingException e = new NamingException();
+            e.setRootCause( errors.get( 0 ) );
+            throw e;
+        }
+
+        SerializableComparator.setRegistry( registries.getComparatorRegistry() );
+
+        // --------------------------------------------------------------------
+        // If not present extract schema partition from jar
+        // --------------------------------------------------------------------
+
+        SchemaPartitionExtractor extractor = null; 
+        try
+        {
+            extractor = new SchemaPartitionExtractor( directoryService.getWorkingDirectory() );
+            extractor.extract();
+        }
+        catch ( IOException e )
+        {
+            NamingException ne = new NamingException( "Failed to extract pre-loaded schema partition." );
+            ne.setRootCause( e );
+            throw ne;
+        }
+        
+        // --------------------------------------------------------------------
+        // Initialize schema partition
+        // --------------------------------------------------------------------
+
+        schemaPartition = new JdbmPartition();
+        schemaPartition.setId( "schema" );
+        schemaPartition.setCacheSize( 1000 );
+
+        Set<Index> indexedAttributes = new HashSet<Index>();
+        for ( String attributeId : extractor.getDbFileListing().getIndexedAttributes() )
+        {
+            indexedAttributes.add( new JdbmIndex( attributeId ) );
+        }
+
+        schemaPartition.setIndexedAttributes( indexedAttributes );
+        schemaPartition.setSuffix( "ou=schema" );
+        
+        ServerEntry entry = new DefaultServerEntry( registries, new LdapDN( "ou=schema" ) );
+        entry.put( "objectClass", "top", "organizationalUnit" );
+        entry.put( "ou", "schema" );
+        schemaPartition.setContextEntry( entry );
+        schemaPartition.init( directoryService );
+    }
+    
+    
+    @Test public void testGetSchemas() throws NamingException
+    {
+        PartitionSchemaLoader loader = new PartitionSchemaLoader( schemaPartition, registries );
+        Map<String,Schema> schemas = loader.getSchemas();
+        
+        Schema schema = schemas.get( "mozilla" );
+        assertNotNull( schema );
+        assertEquals( schema.getSchemaName(), "mozilla" );
+        //assertTrue( schema.isDisabled() );
+        assertEquals( schema.getOwner(), "uid=admin,ou=system" );
+        schema = null;
+        
+        schema = schemas.get( "core" );
+        assertNotNull( schema );
+        assertEquals( schema.getSchemaName(), "core" );
+        assertFalse( schema.isDisabled() );
+        assertEquals( schema.getOwner(), "uid=admin,ou=system" );
+        schema = null;
+        
+        schema = schemas.get( "apachedns" );
+        assertNotNull( schema );
+        assertEquals( schema.getSchemaName(), "apachedns" );
+        //assertTrue( schema.isDisabled() );
+        assertEquals( schema.getOwner(), "uid=admin,ou=system" );
+        schema = null;
+        
+        schema = schemas.get( "autofs" );
+        assertNotNull( schema );
+        assertEquals( schema.getSchemaName(), "autofs" );
+        //assertTrue( schema.isDisabled() );
+        assertEquals( schema.getOwner(), "uid=admin,ou=system" );
+        schema = null;
+        
+        schema = schemas.get( "apache" );
+        assertNotNull( schema );
+        assertEquals( schema.getSchemaName(), "apache" );
+        assertFalse( schema.isDisabled() );
+        assertEquals( schema.getOwner(), "uid=admin,ou=system" );
+        schema = null;
+
+        schema = schemas.get( "cosine" );
+        assertNotNull( schema );
+        assertEquals( schema.getSchemaName(), "cosine" );
+        assertFalse( schema.isDisabled() );
+        assertEquals( schema.getOwner(), "uid=admin,ou=system" );
+        schema = null;
+        
+        schema = schemas.get( "krb5kdc" );
+        assertNotNull( schema );
+        assertEquals( schema.getSchemaName(), "krb5kdc" );
+        //assertTrue( schema.isDisabled() );
+        assertEquals( schema.getOwner(), "uid=admin,ou=system" );
+        schema = null;
+        
+        schema = schemas.get( "samba" );
+        assertNotNull( schema );
+        assertEquals( schema.getSchemaName(), "samba" );
+        //assertTrue( schema.isDisabled() );
+        assertEquals( schema.getOwner(), "uid=admin,ou=system" );
+        schema = null;
+        
+        schema = schemas.get( "collective" );
+        assertNotNull( schema );
+        assertEquals( schema.getSchemaName(), "collective" );
+        assertFalse( schema.isDisabled() );
+        assertEquals( schema.getOwner(), "uid=admin,ou=system" );
+        schema = null;
+        
+        schema = schemas.get( "java" );
+        assertNotNull( schema );
+        assertEquals( schema.getSchemaName(), "java" );
+        assertFalse( schema.isDisabled() );
+        assertEquals( schema.getOwner(), "uid=admin,ou=system" );
+        schema = null;
+        
+        schema = schemas.get( "dhcp" );
+        assertNotNull( schema );
+        assertEquals( schema.getSchemaName(), "dhcp" );
+        //assertTrue( schema.isDisabled() );
+        assertEquals( schema.getOwner(), "uid=admin,ou=system" );
+        schema = null;
+        
+        schema = schemas.get( "corba" );
+        assertNotNull( schema );
+        assertEquals( schema.getSchemaName(), "corba" );
+        //assertTrue( schema.isDisabled() );
+        assertEquals( schema.getOwner(), "uid=admin,ou=system" );
+        schema = null;
+        
+        schema = schemas.get( "nis" );
+        assertNotNull( schema );
+        assertEquals( schema.getSchemaName(), "nis" );
+        //assertTrue( schema.isDisabled() );
+        assertEquals( schema.getOwner(), "uid=admin,ou=system" );
+        schema = null;
+        
+        schema = schemas.get( "inetorgperson" );
+        assertNotNull( schema );
+        assertEquals( schema.getSchemaName(), "inetorgperson" );
+        assertFalse( schema.isDisabled() );
+        assertEquals( schema.getOwner(), "uid=admin,ou=system" );
+        schema = null;
+        
+        schema = schemas.get( "system" );
+        assertNotNull( schema );
+        assertEquals( schema.getSchemaName(), "system" );
+        assertFalse( schema.isDisabled() );
+        assertEquals( schema.getOwner(), "uid=admin,ou=system" );
+        schema = null;
+        
+        schema = schemas.get( "apachemeta" );
+        assertNotNull( schema );
+        assertEquals( schema.getSchemaName(), "apachemeta" );
+        assertFalse( schema.isDisabled() );
+        assertEquals( schema.getOwner(), "uid=admin,ou=system" );
+        schema = null;
+    }
+    
+    
+    @Test public void testGetSchemaNames() throws NamingException
+    {
+        PartitionSchemaLoader loader = new PartitionSchemaLoader( schemaPartition, registries );
+        Set<String> schemaNames = loader.getSchemaNames();
+        assertTrue( schemaNames.contains( "mozilla" ) );
+        assertTrue( schemaNames.contains( "core" ) );
+        assertTrue( schemaNames.contains( "apachedns" ) );
+        assertTrue( schemaNames.contains( "autofs" ) );
+        assertTrue( schemaNames.contains( "apache" ) );
+        assertTrue( schemaNames.contains( "cosine" ) );
+        assertTrue( schemaNames.contains( "krb5kdc" ) );
+        assertTrue( schemaNames.contains( "samba" ) );
+        assertTrue( schemaNames.contains( "collective" ) );
+        assertTrue( schemaNames.contains( "java" ) );
+        assertTrue( schemaNames.contains( "dhcp" ) );
+        assertTrue( schemaNames.contains( "corba" ) );
+        assertTrue( schemaNames.contains( "nis" ) );
+        assertTrue( schemaNames.contains( "inetorgperson" ) );
+        assertTrue( schemaNames.contains( "system" ) );
+        assertTrue( schemaNames.contains( "apachemeta" ) );
+    }
+}
diff --git a/old_trunk/core/src/test/java/org/apache/directory/server/core/schema/SchemaCheckerTest.java b/old_trunk/core/src/test/java/org/apache/directory/server/core/schema/SchemaCheckerTest.java
new file mode 100644
index 0000000..2b3cd6d
--- /dev/null
+++ b/old_trunk/core/src/test/java/org/apache/directory/server/core/schema/SchemaCheckerTest.java
@@ -0,0 +1,646 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.schema;
+
+
+import junit.framework.TestCase;
+
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+
+import org.apache.directory.server.core.entry.DefaultServerAttribute;
+import org.apache.directory.server.core.entry.DefaultServerEntry;
+import org.apache.directory.server.core.entry.ServerAttribute;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.schema.SchemaChecker;
+import org.apache.directory.server.schema.bootstrap.ApacheSchema;
+import org.apache.directory.server.schema.bootstrap.BootstrapSchemaLoader;
+import org.apache.directory.server.schema.bootstrap.CoreSchema;
+import org.apache.directory.server.schema.bootstrap.Schema;
+import org.apache.directory.server.schema.bootstrap.SystemSchema;
+import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
+import org.apache.directory.server.schema.registries.DefaultOidRegistry;
+import org.apache.directory.server.schema.registries.DefaultRegistries;
+import org.apache.directory.server.schema.registries.ObjectClassRegistry;
+import org.apache.directory.server.schema.registries.OidRegistry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.entry.ModificationOperation;
+import org.apache.directory.shared.ldap.exception.LdapSchemaViolationException;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.util.StringTools;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.HashSet;
+
+
+/**
+ * Tests to make sure the schema checker is operating correctly.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class SchemaCheckerTest extends TestCase
+{
+    Registries registries = null;
+
+
+    public SchemaCheckerTest() throws NamingException
+    {
+        this( "SchemaCheckerTest" );
+    }
+
+
+    public SchemaCheckerTest(String name) throws NamingException
+    {
+        super( name );
+
+        BootstrapSchemaLoader loader = new BootstrapSchemaLoader();
+        registries = new DefaultRegistries( "bootstrap", loader, new DefaultOidRegistry() );
+
+        Set<Schema> schemas = new HashSet<Schema>();
+        schemas.add( new SystemSchema() );
+        schemas.add( new ApacheSchema() );
+        schemas.add( new CoreSchema() );
+        loader.loadWithDependencies( schemas,registries );
+
+        List<Throwable> errors = registries.checkRefInteg();
+        
+        if ( !errors.isEmpty() )
+        {
+            NamingException e = new NamingException();
+            e.setRootCause( ( Throwable ) errors.get( 0 ) );
+            throw e;
+        }
+    }
+
+
+    /**
+     * Test case to check the schema checker operates correctly when modify
+     * operations replace objectClasses.
+     */
+    public void testPreventStructuralClassRemovalOnModifyReplace() throws Exception
+    {
+        LdapDN name = new LdapDN( "uid=akarasulu,ou=users,dc=example,dc=com" );
+        int mod = DirContext.REPLACE_ATTRIBUTE;
+        Attributes modifyAttributes = new AttributesImpl( true );
+        modifyAttributes.put( new AttributeImpl( "cn" ) );
+
+        ObjectClassRegistry ocRegistry = registries.getObjectClassRegistry();
+
+        // this should pass
+        SchemaChecker.preventStructuralClassRemovalOnModifyReplace( ocRegistry, name, mod, modifyAttributes );
+
+        // this should succeed since person is still in replaced set and is structural
+        modifyAttributes.remove( "cn" );
+        Attribute objectClassesReplaced = new AttributeImpl( "objectClass" );
+        objectClassesReplaced.add( "top" );
+        objectClassesReplaced.add( "person" );
+        modifyAttributes.put( objectClassesReplaced );
+        SchemaChecker.preventStructuralClassRemovalOnModifyReplace( ocRegistry, name, mod, modifyAttributes );
+
+        // this should fail since only top is left
+        objectClassesReplaced = new AttributeImpl( "objectClass" );
+        objectClassesReplaced.add( "top" );
+        modifyAttributes.put( objectClassesReplaced );
+        try
+        {
+            SchemaChecker.preventStructuralClassRemovalOnModifyReplace( ocRegistry, name, mod, modifyAttributes );
+            fail( "should never get here due to an LdapSchemaViolationException" );
+        }
+        catch ( LdapSchemaViolationException e )
+        {
+            assertEquals( e.getResultCode(), ResultCodeEnum.OBJECT_CLASS_MODS_PROHIBITED );
+        }
+
+        // this should fail since the modify operation tries to delete all
+        // objectClass attribute values
+        modifyAttributes.remove( "cn" );
+        objectClassesReplaced = new AttributeImpl( "objectClass" );
+        modifyAttributes.put( objectClassesReplaced );
+        try
+        {
+            SchemaChecker.preventStructuralClassRemovalOnModifyReplace( ocRegistry, name, mod, modifyAttributes );
+            fail( "should never get here due to an LdapSchemaViolationException" );
+        }
+        catch ( LdapSchemaViolationException e )
+        {
+            assertEquals( e.getResultCode(), ResultCodeEnum.OBJECT_CLASS_MODS_PROHIBITED );
+        }
+    }
+
+
+    /**
+     * Test case to check the schema checker operates correctly when modify
+     * operations remove objectClasses.
+     *
+    public void testPreventStructuralClassRemovalOnModifyRemove() throws Exception
+    {
+        LdapDN name = new LdapDN( "uid=akarasulu,ou=users,dc=example,dc=com" );
+        int mod = DirContext.REMOVE_ATTRIBUTE;
+        Attributes modifyAttributes = new AttributesImpl( true );
+        Attribute entryObjectClasses = new AttributeImpl( "objectClass" );
+        entryObjectClasses.add( "top" );
+        entryObjectClasses.add( "person" );
+        entryObjectClasses.add( "organizationalPerson" );
+        modifyAttributes.put( new AttributeImpl( "cn" ) );
+
+        ObjectClassRegistry ocRegistry = registries.getObjectClassRegistry();
+
+        // this should pass
+        SchemaChecker.preventStructuralClassRemovalOnModifyRemove( ocRegistry, name, mod, modifyAttributes,
+            entryObjectClasses );
+
+        // this should succeed since person is left and is structural
+        modifyAttributes.remove( "cn" );
+        Attribute objectClassesRemoved = new AttributeImpl( "objectClass" );
+        objectClassesRemoved.add( "person" );
+        modifyAttributes.put( objectClassesRemoved );
+        SchemaChecker.preventStructuralClassRemovalOnModifyRemove( ocRegistry, name, mod, modifyAttributes,
+            entryObjectClasses );
+
+        // this should fail since only top is left
+        modifyAttributes.remove( "cn" );
+        objectClassesRemoved = new AttributeImpl( "objectClass" );
+        objectClassesRemoved.add( "person" );
+        objectClassesRemoved.add( "organizationalPerson" );
+        modifyAttributes.put( objectClassesRemoved );
+        try
+        {
+            SchemaChecker.preventStructuralClassRemovalOnModifyRemove( ocRegistry, name, mod, modifyAttributes,
+                entryObjectClasses );
+            fail( "should never get here due to an LdapSchemaViolationException" );
+        }
+        catch ( LdapSchemaViolationException e )
+        {
+            assertEquals( e.getResultCode(), ResultCodeEnum.OBJECT_CLASS_MODS_PROHIBITED );
+        }
+
+        // this should fail since the modify operation tries to delete all
+        // objectClass attribute values
+        modifyAttributes.remove( "cn" );
+        objectClassesRemoved = new AttributeImpl( "objectClass" );
+        modifyAttributes.put( objectClassesRemoved );
+        try
+        {
+            SchemaChecker.preventStructuralClassRemovalOnModifyRemove( ocRegistry, name, mod, modifyAttributes,
+                entryObjectClasses );
+            fail( "should never get here due to an LdapSchemaViolationException" );
+        }
+        catch ( LdapSchemaViolationException e )
+        {
+            assertEquals( e.getResultCode(), ResultCodeEnum.OBJECT_CLASS_MODS_PROHIBITED );
+        }
+    }
+
+
+    /**
+     * Test case to check the schema checker operates correctly when modify
+     * operations remove RDN attributes.
+     */
+    public void testPreventRdnChangeOnModifyRemove() throws Exception
+    {
+        int mod = DirContext.REMOVE_ATTRIBUTE;
+        LdapDN name = new LdapDN( "ou=user,dc=example,dc=com" );
+        ServerEntry attributes = new DefaultServerEntry( registries, name );
+        attributes.put( "cn", "does not matter" );
+
+        // postive test which should pass
+        SchemaChecker.preventRdnChangeOnModifyRemove( name, mod, attributes, registries.getOidRegistry() );
+
+        // test should fail since we are removing the ou attribute
+        AttributeType OU_AT = registries.getAttributeTypeRegistry().lookup( "ou" );
+        attributes.put( new DefaultServerAttribute( "ou", OU_AT ) );
+
+        try
+        {
+            SchemaChecker.preventRdnChangeOnModifyRemove( name, mod, attributes, registries.getOidRegistry() );
+            fail( "should never get here due to a LdapSchemaViolationException being thrown" );
+        }
+        catch ( LdapSchemaViolationException e )
+        {
+            assertEquals( ResultCodeEnum.NOT_ALLOWED_ON_RDN, e.getResultCode() );
+        }
+
+        // test success using more than one attribute for the Rdn but not modifying rdn attribute
+        name = new LdapDN( "ou=users+cn=system users,dc=example,dc=com" );
+        attributes = new DefaultServerEntry( registries, name );
+        attributes.put( "sn", "does not matter" );
+        SchemaChecker.preventRdnChangeOnModifyRemove( name, mod, attributes, registries.getOidRegistry() );
+
+        // test for failure when modifying Rdn attribute in multi attribute Rdn
+        AttributeType CN_AT = registries.getAttributeTypeRegistry().lookup( "cn" );
+        attributes.put( new DefaultServerAttribute( "cn", CN_AT ) );
+        
+        try
+        {
+            SchemaChecker.preventRdnChangeOnModifyRemove( name, mod, attributes, registries.getOidRegistry() );
+            fail( "should never get here due to a LdapSchemaViolationException being thrown" );
+        }
+        catch ( LdapSchemaViolationException e )
+        {
+            assertEquals( ResultCodeEnum.NOT_ALLOWED_ON_RDN, e.getResultCode() );
+        }
+
+        // should succeed since the value being deleted from the rdn attribute is
+        // is not used when composing the Rdn
+        attributes = new DefaultServerEntry( registries, name );
+        attributes.put( "ou", "container" );
+        SchemaChecker.preventRdnChangeOnModifyRemove( name, mod, attributes, registries.getOidRegistry() );
+
+        // now let's make it fail again just by providing the right value for ou (users)
+        attributes = new DefaultServerEntry( registries, name );
+        attributes.put( "ou", "users" );
+        try
+        {
+            SchemaChecker.preventRdnChangeOnModifyRemove( name, mod, attributes, registries.getOidRegistry() );
+            fail( "should never get here due to a LdapSchemaViolationException being thrown" );
+        }
+        catch ( LdapSchemaViolationException e )
+        {
+            assertEquals( ResultCodeEnum.NOT_ALLOWED_ON_RDN, e.getResultCode() );
+        }
+    }
+
+
+    /**
+     * Test case to check the schema checker operates correctly when modify
+     * operations replace RDN attributes.
+     */
+    public void testPreventRdnChangeOnModifyReplace() throws Exception
+    {
+        int mod = DirContext.REPLACE_ATTRIBUTE;
+        LdapDN name = new LdapDN( "ou=user,dc=example,dc=com" );
+        ServerEntry attributes = new DefaultServerEntry( registries, name );
+        attributes.put( "cn", "does not matter" );
+
+        // postive test which should pass
+        SchemaChecker.preventRdnChangeOnModifyReplace( name, mod, attributes, registries.getOidRegistry() );
+
+        // test should fail since we are removing the ou attribute
+        attributes.put( "ou", (String)null );
+        
+        try
+        {
+            SchemaChecker.preventRdnChangeOnModifyReplace( name, mod, attributes, registries.getOidRegistry() );
+            fail( "should never get here due to a LdapSchemaViolationException being thrown" );
+        }
+        catch ( LdapSchemaViolationException e )
+        {
+            assertEquals( ResultCodeEnum.NOT_ALLOWED_ON_RDN, e.getResultCode() );
+        }
+
+        // test success using more than one attribute for the Rdn but not modifying rdn attribute
+        name = new LdapDN( "ou=users+cn=system users,dc=example,dc=com" );
+        attributes = new DefaultServerEntry( registries, name );
+        attributes.put( "sn", "does not matter" );
+        SchemaChecker.preventRdnChangeOnModifyReplace( name, mod, attributes, registries.getOidRegistry() );
+
+        // test for failure when modifying Rdn attribute in multi attribute Rdn
+        attributes.put("cn", (String)null );
+        
+        try
+        {
+            SchemaChecker.preventRdnChangeOnModifyReplace( name, mod, attributes, registries.getOidRegistry() );
+            fail( "should never get here due to a LdapSchemaViolationException being thrown" );
+        }
+        catch ( LdapSchemaViolationException e )
+        {
+            assertEquals( ResultCodeEnum.NOT_ALLOWED_ON_RDN, e.getResultCode() );
+        }
+
+        // should succeed since the values being replaced from the rdn attribute is
+        // is includes the old Rdn attribute value
+        attributes = new DefaultServerEntry( registries, name );
+        attributes.put( "ou", "container" );
+        attributes.put( "ou", "users" );
+        SchemaChecker.preventRdnChangeOnModifyReplace( name, mod, attributes, registries.getOidRegistry() );
+
+        // now let's make it fail by not including the old value for ou (users)
+        attributes = new DefaultServerEntry( registries, name );
+        attributes.put( "ou", "container" );
+        try
+        {
+            SchemaChecker.preventRdnChangeOnModifyReplace( name, mod, attributes, registries.getOidRegistry() );
+            fail( "should never get here due to a LdapSchemaViolationException being thrown" );
+        }
+        catch ( LdapSchemaViolationException e )
+        {
+            assertEquals( ResultCodeEnum.NOT_ALLOWED_ON_RDN, e.getResultCode() );
+        }
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Single Attribute Test Cases
+    // ------------------------------------------------------------------------
+
+    /**
+     * Test case to check the schema checker operates correctly when modify
+     * operations replace objectClasses.
+     */
+    public void testPreventStructuralClassRemovalOnModifyReplaceAttribute() throws Exception
+    {
+        ObjectClassRegistry ocRegistry = registries.getObjectClassRegistry();
+        AttributeType OBJECT_CLASS = registries.getAttributeTypeRegistry().lookup( "objectClass" );
+        AttributeType CN_AT = registries.getAttributeTypeRegistry().lookup( "cn" );
+
+        // this should pass
+        LdapDN name = new LdapDN( "uid=akarasulu,ou=users,dc=example,dc=com" );
+        ModificationOperation mod = ModificationOperation.REPLACE_ATTRIBUTE;
+        SchemaChecker.preventStructuralClassRemovalOnModifyReplace( ocRegistry, name, mod, new DefaultServerAttribute( "cn", CN_AT ) );
+
+        // this should succeed since person is still in replaced set and is structural
+        ServerAttribute objectClassesReplaced = new DefaultServerAttribute( "objectClass", OBJECT_CLASS );
+        objectClassesReplaced.add( "top" );
+        objectClassesReplaced.add( "person" );
+        SchemaChecker.preventStructuralClassRemovalOnModifyReplace( ocRegistry, name, mod, objectClassesReplaced );
+
+        // this should fail since only top is left
+        objectClassesReplaced = new DefaultServerAttribute( "objectClass", OBJECT_CLASS );
+        objectClassesReplaced.add( "top" );
+        try
+        {
+            SchemaChecker.preventStructuralClassRemovalOnModifyReplace( ocRegistry, name, mod, objectClassesReplaced );
+            fail( "should never get here due to an LdapSchemaViolationException" );
+        }
+        catch ( LdapSchemaViolationException e )
+        {
+            assertEquals( e.getResultCode(), ResultCodeEnum.OBJECT_CLASS_MODS_PROHIBITED );
+        }
+
+        // this should fail since the modify operation tries to delete all
+        // objectClass attribute values
+        objectClassesReplaced = new DefaultServerAttribute( "objectClass", OBJECT_CLASS );
+        try
+        {
+            SchemaChecker.preventStructuralClassRemovalOnModifyReplace( ocRegistry, name, mod, objectClassesReplaced );
+            fail( "should never get here due to an LdapSchemaViolationException" );
+        }
+        catch ( LdapSchemaViolationException e )
+        {
+            assertEquals( e.getResultCode(), ResultCodeEnum.OBJECT_CLASS_MODS_PROHIBITED );
+        }
+    }
+
+
+    /**
+     * Test case to check the schema checker operates correctly when modify
+     * operations remove objectClasses.
+     */
+    public void testPreventStructuralClassRemovalOnModifyRemoveAttribute() throws Exception
+    {
+        AttributeTypeRegistry atReg = registries.getAttributeTypeRegistry();
+        LdapDN name = new LdapDN( "uid=akarasulu,ou=users,dc=example,dc=com" );
+        ModificationOperation mod = ModificationOperation.REMOVE_ATTRIBUTE;
+        AttributeType ocAt = atReg.lookup( "objectClass" );
+        
+        ServerAttribute entryObjectClasses = new DefaultServerAttribute( "objectClass", ocAt );
+        entryObjectClasses.add( "top", "person", "organizationalPerson" );
+
+        ObjectClassRegistry ocRegistry = registries.getObjectClassRegistry();
+
+        // this should pass
+        SchemaChecker.preventStructuralClassRemovalOnModifyRemove( 
+            ocRegistry, 
+            name, 
+            mod, 
+            new DefaultServerAttribute( "cn", atReg.lookup( "cn" ) ),
+            entryObjectClasses );
+
+        // this should succeed since person is left and is structural
+        ServerAttribute objectClassesRemoved = new DefaultServerAttribute( 
+            "objectClass", ocAt );
+        objectClassesRemoved.add( "person" );
+        SchemaChecker.preventStructuralClassRemovalOnModifyRemove( ocRegistry, name, mod, objectClassesRemoved,
+            entryObjectClasses );
+
+        // this should fail since only top is left
+        objectClassesRemoved = new DefaultServerAttribute( "objectClass", ocAt );
+        objectClassesRemoved.add( "person", "organizationalPerson" );
+        
+        try
+        {
+            SchemaChecker.preventStructuralClassRemovalOnModifyRemove( ocRegistry, name, mod, objectClassesRemoved,
+                entryObjectClasses );
+            fail( "should never get here due to an LdapSchemaViolationException" );
+        }
+        catch ( LdapSchemaViolationException e )
+        {
+            assertEquals( e.getResultCode(), ResultCodeEnum.OBJECT_CLASS_MODS_PROHIBITED );
+        }
+
+        // this should fail since the modify operation tries to delete all
+        // objectClass attribute values
+        objectClassesRemoved = new DefaultServerAttribute( "objectClass", ocAt );
+
+        try
+        {
+            SchemaChecker.preventStructuralClassRemovalOnModifyRemove( ocRegistry, name, mod, objectClassesRemoved,
+                entryObjectClasses );
+            fail( "should never get here due to an LdapSchemaViolationException" );
+        }
+        catch ( LdapSchemaViolationException e )
+        {
+            assertEquals( e.getResultCode(), ResultCodeEnum.OBJECT_CLASS_MODS_PROHIBITED );
+        }
+    }
+
+
+    /**
+     * Test case to check the schema checker operates correctly when modify
+     * operations remove RDN attributes.
+     */
+    public void testPreventRdnChangeOnModifyRemoveAttribute() throws Exception
+    {
+        OidRegistry registry = new MockOidRegistry();
+        ModificationOperation mod = ModificationOperation.REMOVE_ATTRIBUTE;
+        LdapDN name = new LdapDN( "ou=user,dc=example,dc=com" );
+        AttributeType cnAt = registries.getAttributeTypeRegistry().lookup( "cn" );
+        AttributeType ouAt = registries.getAttributeTypeRegistry().lookup( "ou" );
+        AttributeType snAt = registries.getAttributeTypeRegistry().lookup( "sn" );
+
+        // postive test which should pass
+        SchemaChecker.preventRdnChangeOnModifyRemove( name, mod, 
+            new DefaultServerAttribute( "cn", cnAt, "does not matter" ), registry );
+
+        // test should fail since we are removing the ou attribute
+        try
+        {
+            SchemaChecker.preventRdnChangeOnModifyRemove( name, mod, 
+                new DefaultServerAttribute( "ou", ouAt ), registry );
+            fail( "should never get here due to a LdapSchemaViolationException being thrown" );
+        }
+        catch ( LdapSchemaViolationException e )
+        {
+            assertEquals( ResultCodeEnum.NOT_ALLOWED_ON_RDN, e.getResultCode() );
+        }
+
+        // test success using more than one attribute for the Rdn but not modifying rdn attribute
+        name = new LdapDN( "ou=users+cn=system users,dc=example,dc=com" );
+        SchemaChecker.preventRdnChangeOnModifyRemove( name, mod, 
+            new DefaultServerAttribute( "sn", snAt, "does not matter" ), registry );
+
+        // test for failure when modifying Rdn attribute in multi attribute Rdn
+        try
+        {
+            SchemaChecker.preventRdnChangeOnModifyRemove( name, mod, 
+                new DefaultServerAttribute( "cn", cnAt ), registry );
+            fail( "should never get here due to a LdapSchemaViolationException being thrown" );
+        }
+        catch ( LdapSchemaViolationException e )
+        {
+            assertEquals( ResultCodeEnum.NOT_ALLOWED_ON_RDN, e.getResultCode() );
+        }
+
+        // should succeed since the value being deleted from the rdn attribute is
+        // is not used when composing the Rdn
+        SchemaChecker.preventRdnChangeOnModifyRemove( name, mod, 
+            new DefaultServerAttribute( "ou", ouAt, "container" ), registry );
+
+        // now let's make it fail again just by providing the right value for ou (users)
+        try
+        {
+            SchemaChecker.preventRdnChangeOnModifyRemove( name, mod, 
+                new DefaultServerAttribute( "ou", ouAt, "users" ), registry );
+            fail( "should never get here due to a LdapSchemaViolationException being thrown" );
+        }
+        catch ( LdapSchemaViolationException e )
+        {
+            assertEquals( ResultCodeEnum.NOT_ALLOWED_ON_RDN, e.getResultCode() );
+        }
+    }
+
+
+//    /**
+//     * Test case to check the schema checker operates correctly when modify
+//     * operations replace RDN attributes.
+//     */
+//    public void testPreventRdnChangeOnModifyReplaceAttribute() throws Exception
+//    {
+//        int mod = DirContext.REPLACE_ATTRIBUTE;
+//        LdapDN name = new LdapDN( "ou=user,dc=example,dc=com" );
+//
+//        // postive test which should pass
+//        SchemaChecker.preventRdnChangeOnModifyReplace( name, mod, new AttributeImpl( "cn", "does not matter" ), registries.getOidRegistry() );
+//
+//        // test should fail since we are removing the ou attribute
+//        try
+//        {
+//            SchemaChecker.preventRdnChangeOnModifyReplace( name, mod, new AttributeImpl( "ou" ), registries.getOidRegistry() );
+//            fail( "should never get here due to a LdapSchemaViolationException being thrown" );
+//        }
+//        catch ( LdapSchemaViolationException e )
+//        {
+//            assertEquals( ResultCodeEnum.NOT_ALLOWED_ON_RDN, e.getResultCode() );
+//        }
+//
+//        // test success using more than one attribute for the Rdn but not modifying rdn attribute
+//        name = new LdapDN( "ou=users+cn=system users,dc=example,dc=com" );
+//        SchemaChecker.preventRdnChangeOnModifyReplace( name, mod, new AttributeImpl( "sn", "does not matter" ), registries.getOidRegistry() );
+//
+//        // test for failure when modifying Rdn attribute in multi attribute Rdn
+//        try
+//        {
+//            SchemaChecker.preventRdnChangeOnModifyReplace( name, mod, new AttributeImpl( "cn" ), registries.getOidRegistry() );
+//            fail( "should never get here due to a LdapSchemaViolationException being thrown" );
+//        }
+//        catch ( LdapSchemaViolationException e )
+//        {
+//            assertEquals( ResultCodeEnum.NOT_ALLOWED_ON_RDN, e.getResultCode() );
+//        }
+//
+//        // should succeed since the values being replaced from the rdn attribute is
+//        // is includes the old Rdn attribute value
+//        Attribute attribute = new AttributeImpl( "ou" );
+//        attribute.add( "container" );
+//        attribute.add( "users" );
+//        SchemaChecker.preventRdnChangeOnModifyReplace( name, mod, attribute, registries.getOidRegistry() );
+//
+//        // now let's make it fail by not including the old value for ou (users)
+//        attribute = new AttributeImpl( "ou" );
+//        attribute.add( "container" );
+//        try
+//        {
+//            SchemaChecker.preventRdnChangeOnModifyReplace( name, mod, attribute, registries.getOidRegistry() );
+//            fail( "should never get here due to a LdapSchemaViolationException being thrown" );
+//        }
+//        catch ( LdapSchemaViolationException e )
+//        {
+//            assertEquals( ResultCodeEnum.NOT_ALLOWED_ON_RDN, e.getResultCode() );
+//        }
+//    }
+
+
+    class MockOidRegistry implements OidRegistry
+    {
+        public String getOid( String name ) throws NamingException
+        {
+            return StringTools.deepTrimToLower( name );
+        }
+
+        public boolean hasOid( String id )
+        {
+            return true;
+        }
+
+        public String getPrimaryName( String oid ) throws NamingException
+        {
+            return oid;
+        }
+
+        public List getNameSet( String oid ) throws NamingException
+        {
+            return Collections.singletonList( oid );
+        }
+
+        public Iterator list()
+        {
+            return Collections.EMPTY_LIST.iterator();
+        }
+
+        public void register( String name, String oid )
+        {
+        }
+
+        public Map getOidByName()
+        {
+            return null;
+        }
+
+        public Map getNameByOid()
+        {
+            return null;
+        }
+
+        public void unregister( String numericOid ) throws NamingException
+        {
+        }
+    }
+}
diff --git a/old_trunk/core/src/test/java/org/apache/directory/server/core/schema/SchemaServiceTest.java b/old_trunk/core/src/test/java/org/apache/directory/server/core/schema/SchemaServiceTest.java
new file mode 100644
index 0000000..a72cde6
--- /dev/null
+++ b/old_trunk/core/src/test/java/org/apache/directory/server/core/schema/SchemaServiceTest.java
@@ -0,0 +1,155 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.schema;
+
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import org.apache.directory.server.schema.bootstrap.ApacheSchema;
+import org.apache.directory.server.schema.bootstrap.BootstrapSchemaLoader;
+import org.apache.directory.server.schema.bootstrap.CoreSchema;
+import org.apache.directory.server.schema.bootstrap.SystemSchema;
+import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
+import org.apache.directory.server.schema.registries.DefaultOidRegistry;
+import org.apache.directory.server.schema.registries.DefaultRegistries;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+
+import junit.framework.TestCase;
+
+
+/**
+ * Tests methods in SchemaInterceptor.
+ */
+public class SchemaServiceTest extends TestCase
+{
+    DefaultRegistries registries;
+
+
+    public void setUp() throws Exception
+    {
+        BootstrapSchemaLoader loader = new BootstrapSchemaLoader();
+        registries = new DefaultRegistries( "bootstrap", loader, new DefaultOidRegistry() );
+        loader.load( new SystemSchema(), registries, false );
+        loader.load( new ApacheSchema(), registries, false );
+        loader.load( new CoreSchema(), registries, false );
+    }
+
+    
+    public void testDescendants() throws Exception
+    {
+        AttributeTypeRegistry attrRegistry = registries.getAttributeTypeRegistry();
+        Iterator list = attrRegistry.descendants( "name" );
+        Set<String> nameAttrs = new HashSet<String>();
+        while ( list.hasNext() )
+        {
+            AttributeType type = ( AttributeType ) list.next();
+            nameAttrs.add( type.getName() );
+        }
+        assertEquals( "size of attributes extending name", 13, nameAttrs.size() );
+        assertTrue( nameAttrs.contains( "dmdName" ) );
+        assertTrue( nameAttrs.contains( "o" ) );
+        assertTrue( nameAttrs.contains( "c" ) );
+        assertTrue( nameAttrs.contains( "initials" ) );
+        assertTrue( nameAttrs.contains( "ou" ) );
+        assertTrue( nameAttrs.contains( "sn" ) );
+        assertTrue( nameAttrs.contains( "title" ) );
+        assertTrue( nameAttrs.contains( "l" ) );
+        assertTrue( nameAttrs.contains( "apacheExistance" ) );
+        assertTrue( nameAttrs.contains( "cn" ) );
+        assertTrue( nameAttrs.contains( "st" ) );
+        assertTrue( nameAttrs.contains( "givenName" ) );
+    }
+    
+/*
+    public void testAlterObjectClassesBogusAttr() throws NamingException
+    {
+        Attribute attr = new AttributeImpl( "blah", "blah" );
+
+        try
+        {
+            SchemaInterceptor.alterObjectClasses( attr, registries.getObjectClassRegistry() );
+            fail( "should not get here" );
+        }
+        catch ( LdapNamingException e )
+        {
+            assertEquals( ResultCodeEnum.OPERATIONS_ERROR, e.getResultCode() );
+        }
+
+        attr = new AttributeImpl( "objectClass" );
+        SchemaInterceptor.alterObjectClasses( attr );
+        assertEquals( 0, attr.size() );
+    }
+
+
+    public void testAlterObjectClassesNoAttrValue() throws NamingException
+    {
+        Attribute attr = new AttributeImpl( "objectClass" );
+        SchemaInterceptor.alterObjectClasses( attr );
+        assertEquals( 0, attr.size() );
+    }
+
+
+    public void testAlterObjectClassesTopAttrValue() throws NamingException
+    {
+        Attribute attr = new AttributeImpl( "objectClass", "top" );
+        SchemaInterceptor.alterObjectClasses( attr, registries.getObjectClassRegistry() );
+        assertEquals( 0, attr.size() );
+    }
+
+
+    public void testAlterObjectClassesInetOrgPersonAttrValue() throws NamingException
+    {
+        Attribute attr = new AttributeImpl( "objectClass", "organizationalPerson" );
+        SchemaInterceptor.alterObjectClasses( attr, registries.getObjectClassRegistry() );
+        assertEquals( 2, attr.size() );
+        assertTrue( attr.contains( "person" ) );
+        assertTrue( attr.contains( "organizationalPerson" ) );
+    }
+
+
+    public void testAlterObjectClassesOverlapping() throws NamingException
+    {
+        Attribute attr = new AttributeImpl( "objectClass", "organizationalPerson" );
+        attr.add( "residentialPerson" );
+        SchemaInterceptor.alterObjectClasses( attr, registries.getObjectClassRegistry() );
+        assertEquals( 3, attr.size() );
+        assertTrue( attr.contains( "person" ) );
+        assertTrue( attr.contains( "organizationalPerson" ) );
+        assertTrue( attr.contains( "residentialPerson" ) );
+    }
+
+
+    public void testAlterObjectClassesOverlappingAndDsa() throws NamingException
+    {
+        Attribute attr = new AttributeImpl( "objectClass", "organizationalPerson" );
+        attr.add( "residentialPerson" );
+        attr.add( "dSA" );
+        SchemaInterceptor.alterObjectClasses( attr, registries.getObjectClassRegistry() );
+        assertEquals( 5, attr.size() );
+        assertTrue( attr.contains( "person" ) );
+        assertTrue( attr.contains( "organizationalPerson" ) );
+        assertTrue( attr.contains( "residentialPerson" ) );
+        assertTrue( attr.contains( "dSA" ) );
+        assertTrue( attr.contains( "applicationEntity" ) );
+    }
+    */
+}
diff --git a/old_trunk/core/src/test/java/org/apache/directory/server/core/schema/bootstrap/BootstrapSchemaLoaderTest.java b/old_trunk/core/src/test/java/org/apache/directory/server/core/schema/bootstrap/BootstrapSchemaLoaderTest.java
new file mode 100644
index 0000000..4f845a5
--- /dev/null
+++ b/old_trunk/core/src/test/java/org/apache/directory/server/core/schema/bootstrap/BootstrapSchemaLoaderTest.java
@@ -0,0 +1,220 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.schema.bootstrap;
+
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.naming.NamingException;
+
+import junit.framework.TestCase;
+
+import org.apache.directory.server.schema.bootstrap.ApacheSchema;
+import org.apache.directory.server.schema.bootstrap.BootstrapSchemaLoader;
+import org.apache.directory.server.schema.bootstrap.CoreSchema;
+import org.apache.directory.server.schema.bootstrap.Schema;
+import org.apache.directory.server.schema.bootstrap.SystemSchema;
+import org.apache.directory.server.schema.registries.DefaultOidRegistry;
+import org.apache.directory.server.schema.registries.DefaultRegistries;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+
+
+/**
+ * A unit test case for the BootstrapSchemaLoader class.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class BootstrapSchemaLoaderTest extends TestCase
+{
+    BootstrapSchemaLoader loader = new BootstrapSchemaLoader();
+    DefaultRegistries registries;
+
+
+    protected void setUp() throws Exception
+    {
+        super.setUp();
+        registries = new DefaultRegistries( "bootstrap", loader, new DefaultOidRegistry() );
+    }
+
+
+    protected void tearDown() throws Exception
+    {
+        super.tearDown();
+        registries = null;
+    }
+
+
+    public void testLoadAll() throws NamingException
+    {
+        Set<Schema> schemas = new HashSet<Schema>();
+        schemas.add( new CoreSchema() );
+        schemas.add( new ApacheSchema() );
+        schemas.add( new SystemSchema() );
+
+        loader.loadWithDependencies( schemas, registries );
+        AttributeType type;
+
+        // from core.schema
+        type = registries.getAttributeTypeRegistry().lookup( "knowledgeInformation" );
+        assertNotNull( type );
+
+        // from apache.schema
+        type = registries.getAttributeTypeRegistry().lookup( "apacheAlias" );
+        assertNotNull( type );
+
+        // from system.schema
+        type = registries.getAttributeTypeRegistry().lookup( "distinguishedName" );
+        assertNotNull( type );
+    }
+
+
+    public void testSystemSchemaLoad() throws NamingException
+    {
+        SystemSchema systemSchema = new SystemSchema();
+        loader.load( systemSchema, registries, false );
+
+        AttributeType type;
+        type = registries.getAttributeTypeRegistry().lookup( "distinguishedName" );
+        assertNotNull( type );
+
+        type = registries.getAttributeTypeRegistry().lookup( "objectClass" );
+        assertNotNull( type );
+
+        type = registries.getAttributeTypeRegistry().lookup( "modifyTimestamp" );
+        assertNotNull( type );
+    }
+
+
+    public void testApacheSchemaLoad() throws NamingException
+    {
+        testSystemSchemaLoad();
+        ApacheSchema apacheSchema = new ApacheSchema();
+        loader.load( apacheSchema, registries, false );
+
+        AttributeType type;
+        type = registries.getAttributeTypeRegistry().lookup( "apacheNdn" );
+        assertNotNull( type );
+
+        type = registries.getAttributeTypeRegistry().lookup( "apacheAlias" );
+        assertNotNull( type );
+
+        type = registries.getAttributeTypeRegistry().lookup( "apacheUpdn" );
+        assertNotNull( type );
+    }
+
+
+    public void testEveDepsSchemaLoad() throws NamingException
+    {
+        Set<Schema> schemas = new HashSet<Schema>();
+        schemas.add( new ApacheSchema() );
+        schemas.add( new SystemSchema() );
+
+        loader.loadWithDependencies( schemas, registries );
+        AttributeType type;
+        type = registries.getAttributeTypeRegistry().lookup( "apacheNdn" );
+        assertNotNull( type );
+
+        type = registries.getAttributeTypeRegistry().lookup( "apacheAlias" );
+        assertNotNull( type );
+
+        type = registries.getAttributeTypeRegistry().lookup( "apacheUpdn" );
+        assertNotNull( type );
+    }
+
+
+    public void testCoreSchemaLoad() throws NamingException
+    {
+        testSystemSchemaLoad();
+        CoreSchema coreSchema = new CoreSchema();
+        loader.load( coreSchema, registries, false );
+
+        AttributeType type;
+        type = registries.getAttributeTypeRegistry().lookup( "knowledgeInformation" );
+        assertNotNull( type );
+
+        type = registries.getAttributeTypeRegistry().lookup( "countryName" );
+        assertNotNull( type );
+
+        type = registries.getAttributeTypeRegistry().lookup( "serialNumber" );
+        assertNotNull( type );
+    }
+
+
+    public void testCoreDepsSchemaLoad() throws NamingException
+    {
+        Set<Schema> schemas = new HashSet<Schema>();
+        schemas.add( new CoreSchema() );
+        schemas.add( new SystemSchema() );
+
+        loader.loadWithDependencies( schemas, registries );
+        AttributeType type;
+        type = registries.getAttributeTypeRegistry().lookup( "knowledgeInformation" );
+        assertNotNull( type );
+
+        type = registries.getAttributeTypeRegistry().lookup( "countryName" );
+        assertNotNull( type );
+
+        type = registries.getAttributeTypeRegistry().lookup( "serialNumber" );
+        assertNotNull( type );
+    }
+
+
+    /**
+     * Attempts to resolve the dependent schema objects of all entities that
+     * refer to other objects within the registries.
+     *
+     * @throws NamingException if there are problems.
+     */
+    public void testReferentialIntegrity() throws NamingException
+    {
+        if ( System.getProperties().containsKey( "ignore.ref.integ.test" ) )
+        {
+            System.err.println( "REFERENTIAL INTEGRITY TESTS BYPASSED!!!" );
+            return;
+        }
+
+        testLoadAll();
+        List errors = registries.checkRefInteg();
+        assertNotNull( errors );
+
+        StringBuffer buf = new StringBuffer();
+
+        if ( !errors.isEmpty() )
+        {
+            buf.append( "expected empty erorrs but got " ).append( errors.size() ).append( " errors:\n" );
+            for ( int ii = 0; ii < errors.size(); ii++ )
+            {
+                buf.append( '\t' ).append( errors.get( ii ).toString() ).append( '\n' );
+            }
+
+            StringWriter out = new StringWriter();
+            Exception e = ( Exception ) errors.get( 0 );
+            e.printStackTrace( new PrintWriter( out ) );
+            buf.append( "\nfirst exception trace:\n" + out.getBuffer().toString() );
+        }
+
+        assertTrue( buf.toString(), errors.isEmpty() );
+    }
+}
diff --git a/old_trunk/core/src/test/java/org/apache/directory/server/core/security/TlsKeyGeneratorTest.java b/old_trunk/core/src/test/java/org/apache/directory/server/core/security/TlsKeyGeneratorTest.java
new file mode 100644
index 0000000..65bec3b
--- /dev/null
+++ b/old_trunk/core/src/test/java/org/apache/directory/server/core/security/TlsKeyGeneratorTest.java
@@ -0,0 +1,111 @@
+/*
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *   or more contributor license agreements.  See the NOTICE file
+ *   distributed with this work for additional information
+ *   regarding copyright ownership.  The ASF licenses this file
+ *   to you 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.
+ *
+ */
+
+package org.apache.directory.server.core.security;
+
+
+import static org.junit.Assert.*;
+
+import java.io.FileInputStream;
+import java.security.KeyPair;
+import java.security.KeyStore;
+import java.security.cert.X509Certificate;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.core.entry.DefaultServerEntry;
+import org.apache.directory.server.schema.bootstrap.ApacheSchema;
+import org.apache.directory.server.schema.bootstrap.ApachemetaSchema;
+import org.apache.directory.server.schema.bootstrap.BootstrapSchemaLoader;
+import org.apache.directory.server.schema.bootstrap.CoreSchema;
+import org.apache.directory.server.schema.bootstrap.CosineSchema;
+import org.apache.directory.server.schema.bootstrap.InetorgpersonSchema;
+import org.apache.directory.server.schema.bootstrap.Schema;
+import org.apache.directory.server.schema.bootstrap.SystemSchema;
+import org.apache.directory.server.schema.registries.DefaultOidRegistry;
+import org.apache.directory.server.schema.registries.DefaultRegistries;
+import org.apache.directory.server.schema.registries.OidRegistry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * Test for the TlsKeyGenerator class.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class TlsKeyGeneratorTest
+{
+    private static final Logger LOG = LoggerFactory.getLogger( TlsKeyGeneratorTest.class );
+    private static BootstrapSchemaLoader loader;
+    private static Registries registries;
+    private static OidRegistry oidRegistry;
+    
+
+    /**
+     * Initialize the registries once for the whole test suite
+     */
+    @BeforeClass
+    public static void setup() throws NamingException
+    {
+        loader = new BootstrapSchemaLoader();
+        oidRegistry = new DefaultOidRegistry();
+        registries = new DefaultRegistries( "bootstrap", loader, oidRegistry );
+        
+        // load essential bootstrap schemas 
+        Set<Schema> bootstrapSchemas = new HashSet<Schema>();
+        bootstrapSchemas.add( new ApachemetaSchema() );
+        bootstrapSchemas.add( new ApacheSchema() );
+        bootstrapSchemas.add( new CoreSchema() );
+        bootstrapSchemas.add( new SystemSchema() );
+        bootstrapSchemas.add( new InetorgpersonSchema() );
+        bootstrapSchemas.add( new CosineSchema() );
+        loader.loadWithDependencies( bootstrapSchemas, registries );
+        
+    }
+    
+    
+    /**
+     * Test method for all methods in one.
+     */
+    @Test
+    public void testAll() throws NamingException
+    {
+        DefaultServerEntry entry = new DefaultServerEntry( registries, new LdapDN() );
+        TlsKeyGenerator.addKeyPair( entry );
+        LOG.debug( "Entry: {}", entry );
+        assertTrue( entry.contains( SchemaConstants.OBJECT_CLASS_AT, TlsKeyGenerator.TLS_KEY_INFO_OC ) );
+        
+        KeyPair keyPair = TlsKeyGenerator.getKeyPair( entry );
+        assertNotNull( keyPair );
+        
+        X509Certificate cert = TlsKeyGenerator.getCertificate( entry );
+        assertNotNull( cert );
+    }
+}
diff --git a/old_trunk/core/src/test/java/org/apache/directory/server/core/sp/StoredProcUtilsTest.java b/old_trunk/core/src/test/java/org/apache/directory/server/core/sp/StoredProcUtilsTest.java
new file mode 100644
index 0000000..0ce7f61
--- /dev/null
+++ b/old_trunk/core/src/test/java/org/apache/directory/server/core/sp/StoredProcUtilsTest.java
@@ -0,0 +1,42 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+
+package org.apache.directory.server.core.sp;
+
+import junit.framework.TestCase;
+
+public class StoredProcUtilsTest extends TestCase
+{
+
+    public void testSPNameTokenization()
+    {
+        String fullSPName = "Greeter:seyHello";
+        String expectedSPUnitName = "Greeter";
+        String expectedSPName = "seyHello";
+        
+        String actualSPUnitName = StoredProcUtils.extractStoredProcUnitName( fullSPName );
+        String actualSPName = StoredProcUtils.extractStoredProcName( fullSPName );
+        
+        assertEquals( expectedSPUnitName, actualSPUnitName );
+        assertEquals( expectedSPName, actualSPName );
+    }
+
+}
diff --git a/old_trunk/core/src/test/java/org/apache/directory/server/core/subtree/RefinementEvaluatorTest.java b/old_trunk/core/src/test/java/org/apache/directory/server/core/subtree/RefinementEvaluatorTest.java
new file mode 100644
index 0000000..5450375
--- /dev/null
+++ b/old_trunk/core/src/test/java/org/apache/directory/server/core/subtree/RefinementEvaluatorTest.java
@@ -0,0 +1,247 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.subtree;
+
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.core.DefaultDirectoryService;
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.entry.DefaultServerAttribute;
+import org.apache.directory.server.core.entry.ServerAttribute;
+import org.apache.directory.server.core.subtree.RefinementEvaluator;
+import org.apache.directory.server.core.subtree.RefinementLeafEvaluator;
+import org.apache.directory.server.schema.registries.OidRegistry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.entry.client.ClientStringValue;
+import org.apache.directory.shared.ldap.filter.EqualityNode;
+import org.apache.directory.shared.ldap.filter.ExprNode;
+import org.apache.directory.shared.ldap.filter.FilterParser;
+import org.apache.directory.shared.ldap.filter.NotNode;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import static org.junit.Assert.fail;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * Unit test cases for testing the evaluator for refinements.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class RefinementEvaluatorTest
+{
+    /** the registries */
+    private static Registries registries;
+    
+    /** the refinement evaluator to test */
+    private static RefinementEvaluator evaluator;
+
+    /** The ObjectClass AttributeType */
+    private static AttributeType OBJECT_CLASS;
+
+    /** The CN AttributeType */
+    private static AttributeType CN;
+
+    /** A reference to the directory service */
+    private static DirectoryService service;
+
+    
+    /**
+     * Initializes the global registries.
+     * @throws javax.naming.NamingException if there is a failure loading the schema
+     */
+    @BeforeClass public static void init() throws NamingException
+    {
+        service = new DefaultDirectoryService();
+        registries = service.getRegistries();
+    }
+
+
+    /**
+     * Initializes registries and creates the leaf evalutator
+     * @throws Exception if there are schema initialization problems
+     */
+    @Before public void setUp() throws Exception
+    {
+        OidRegistry registry = registries.getOidRegistry();
+        RefinementLeafEvaluator leafEvaluator = new RefinementLeafEvaluator( registry );
+        evaluator = new RefinementEvaluator( leafEvaluator );
+        
+        OBJECT_CLASS = registries.getAttributeTypeRegistry().lookup( "objectClass" );
+        CN = registries.getAttributeTypeRegistry().lookup( "cn" );
+    }
+
+
+    /**
+     * Sets evaluator and registries to null.
+     */
+    @After public void tearDown()
+    {
+        evaluator = null;
+    }
+
+
+    /**
+     * Test cases for various bad combinations of arguments
+     * @throws Exception if something goes wrong
+     */
+    @Test public void testForBadArguments() throws Exception
+    {
+        try
+        {
+            assertFalse( evaluator.evaluate( null, new DefaultServerAttribute( "objectClass", OBJECT_CLASS ) ) );
+            fail( "should never get here due to an IAE" );
+        }
+        catch ( IllegalArgumentException iae )
+        {
+        }
+
+        try
+        {
+            assertFalse( evaluator.evaluate( new EqualityNode( "", new ClientStringValue( "" ) ), null ) );
+            fail( "should never get here due to an IAE" );
+        }
+        catch ( IllegalArgumentException iae )
+        {
+        }
+
+        try
+        {
+            assertFalse( evaluator.evaluate( new EqualityNode( "", new ClientStringValue( "" ) ), new DefaultServerAttribute( "cn", CN ) ) );
+            fail( "should never get here due to an IAE" );
+        }
+        catch ( IllegalArgumentException iae )
+        {
+        }
+    }
+
+
+    @Test public void testMatchByName() throws Exception
+    {
+        ServerAttribute objectClasses = null;
+
+        // positive test
+        objectClasses = new DefaultServerAttribute( "objectClass", OBJECT_CLASS, "person" );
+        assertTrue( evaluator.evaluate( new EqualityNode( "objectClass", new ClientStringValue( "person" ) ), objectClasses ) );
+
+        objectClasses = new DefaultServerAttribute( "objectClass", OBJECT_CLASS, "person", "blah" );
+        assertTrue( evaluator.evaluate( new EqualityNode( "objectClass", new ClientStringValue( "person" ) ), objectClasses ) );
+
+        // negative tests
+        objectClasses = new DefaultServerAttribute( "objectClass", OBJECT_CLASS, "person" );
+        assertFalse( evaluator.evaluate( new EqualityNode( "objectClass", new ClientStringValue( "blah" ) ), objectClasses ) );
+
+        objectClasses = new DefaultServerAttribute( "objectClass", OBJECT_CLASS, "blah" );
+        assertFalse( evaluator.evaluate( new EqualityNode( "objectClass", new ClientStringValue( "person" ) ), objectClasses ) );
+    }
+
+
+    @Test public void testMatchByOID() throws Exception
+    {
+        ServerAttribute objectClasses = new DefaultServerAttribute( "objectClass", OBJECT_CLASS, "person" );
+        
+        // positive test
+        assertTrue( evaluator.evaluate( new EqualityNode( "objectClass", new ClientStringValue( "2.5.6.6" ) ), objectClasses ) );
+
+        objectClasses = new DefaultServerAttribute( "objectClass", OBJECT_CLASS, "person", "blah" );
+        assertTrue( evaluator.evaluate( new EqualityNode( "objectClass", new ClientStringValue( "2.5.6.6" ) ), objectClasses ) );
+
+        // negative tests
+        objectClasses = new DefaultServerAttribute( "objectClass", OBJECT_CLASS, "person" );
+        assertFalse( evaluator.evaluate( new EqualityNode( "objectClass", new ClientStringValue( "2.5.6.5" ) ), objectClasses ) );
+
+        objectClasses = new DefaultServerAttribute( "objectClass", OBJECT_CLASS, "blah" );
+        assertFalse( evaluator.evaluate( new EqualityNode( "objectClass", new ClientStringValue( "2.5.6.5" ) ), objectClasses ) );
+    }
+
+
+    @Test public void testComplexOrRefinement() throws Exception
+    {
+        ExprNode refinement = null;
+        ServerAttribute objectClasses = new DefaultServerAttribute( "objectClass", OBJECT_CLASS, "person" );
+        String refStr = "(|(objectClass=person)(objectClass=organizationalUnit))";
+        
+        refinement = FilterParser.parse( refStr );
+
+        assertTrue( evaluator.evaluate( refinement, objectClasses ) );
+        
+        objectClasses = new DefaultServerAttribute( "objectClass", OBJECT_CLASS, "organizationalUnit" );
+        assertTrue( evaluator.evaluate( refinement, objectClasses ) );
+        
+        objectClasses = new DefaultServerAttribute( "objectClass", OBJECT_CLASS, "domain" );
+        assertFalse( evaluator.evaluate( refinement, objectClasses ) );
+    }
+
+
+    @Test public void testComplexAndRefinement() throws Exception
+    {
+        ExprNode refinement = null;
+        ServerAttribute objectClasses = new DefaultServerAttribute( "objectClass", OBJECT_CLASS, "person" );
+        objectClasses.add( "organizationalUnit" );
+        String refStr = "(&(objectClass=person)(objectClass=organizationalUnit))";
+        
+        refinement = FilterParser.parse( refStr );
+
+        assertTrue( evaluator.evaluate( refinement, objectClasses ) );
+        
+        objectClasses = new DefaultServerAttribute( "objectClass", OBJECT_CLASS, "organizationalUnit" );
+        assertFalse( evaluator.evaluate( refinement, objectClasses ) );
+        
+        objectClasses = new DefaultServerAttribute( "objectClass", OBJECT_CLASS, "person" );
+        assertFalse( evaluator.evaluate( refinement, objectClasses ) );
+        
+        objectClasses = new DefaultServerAttribute( "objectClass", OBJECT_CLASS, "domain" );
+        assertFalse( evaluator.evaluate( refinement, objectClasses ) );
+    }
+
+
+    @Test public void testComplexNotRefinement() throws Exception
+    {
+        ExprNode refinement = null;
+        ServerAttribute objectClasses = new DefaultServerAttribute( "objectClass", OBJECT_CLASS, "person" );
+        String refStr = "(!(objectClass=person))";
+
+        refinement = FilterParser.parse( refStr );
+
+        assertFalse( evaluator.evaluate( refinement, objectClasses ) );
+        
+        objectClasses = new DefaultServerAttribute( "objectClass", OBJECT_CLASS, "organizationalUnit" );
+        assertTrue( evaluator.evaluate( refinement, objectClasses ) );
+        
+        objectClasses = new DefaultServerAttribute( "objectClass", OBJECT_CLASS, "domain" );
+        assertTrue( evaluator.evaluate( refinement, objectClasses ) );
+
+        try
+        {
+            assertFalse( evaluator.evaluate( new NotNode(), new DefaultServerAttribute( "objectClass", OBJECT_CLASS ) ) );
+            fail( "should never get here due to an IAE" );
+        }
+        catch ( IllegalArgumentException iae )
+        {
+        }
+    }
+}
diff --git a/old_trunk/core/src/test/java/org/apache/directory/server/core/subtree/RefinementLeafEvaluatorTest.java b/old_trunk/core/src/test/java/org/apache/directory/server/core/subtree/RefinementLeafEvaluatorTest.java
new file mode 100644
index 0000000..22e1634
--- /dev/null
+++ b/old_trunk/core/src/test/java/org/apache/directory/server/core/subtree/RefinementLeafEvaluatorTest.java
@@ -0,0 +1,194 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.subtree;
+
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.core.DefaultDirectoryService;
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.entry.DefaultServerAttribute;
+import org.apache.directory.server.core.entry.ServerAttribute;
+import org.apache.directory.server.core.subtree.RefinementLeafEvaluator;
+import org.apache.directory.server.schema.registries.OidRegistry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.entry.client.ClientStringValue;
+import org.apache.directory.shared.ldap.filter.EqualityNode;
+import org.apache.directory.shared.ldap.filter.GreaterEqNode;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+
+/**
+ * Unit test cases for testing the evaluator for refinement leaf nodes.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class RefinementLeafEvaluatorTest
+{
+    /** The ObjectClass AttributeType */
+    private static AttributeType OBJECT_CLASS;
+
+    /** A reference to the directory service */
+    private static DirectoryService service;
+    
+    /** the registries */
+    private static Registries registries;
+
+    /** the refinement leaf evaluator to test */
+    private RefinementLeafEvaluator evaluator;
+
+
+    /**
+     * Initializes the global registries.
+     * @throws javax.naming.NamingException if there is a failure loading the schema
+     */
+    @BeforeClass public static void init() throws NamingException
+    {
+        service = new DefaultDirectoryService();
+        registries = service.getRegistries();
+        OBJECT_CLASS = registries.getAttributeTypeRegistry().lookup( "objectClass" );
+    }
+    
+
+    /**
+     * Initializes registries and creates the leaf evalutator
+     * @throws Exception if there are schema initialization problems
+     */
+    @Before public void setUp() throws Exception
+    {
+        OidRegistry registry = registries.getOidRegistry();
+        evaluator = new RefinementLeafEvaluator( registry );
+    }
+
+
+    /**
+     * Sets evaluator and registries to null.
+     */
+    @After public void tearDown()
+    {
+        evaluator = null;
+    }
+
+
+    /**
+     * Test cases for various bad combinations of arguments
+     * @throws Exception if something goes wrongg
+     */
+    @Test public void testForBadArguments() throws Exception
+    {
+        ServerAttribute objectClasses = null;
+
+        try
+        {
+            assertFalse( evaluator.evaluate( null, null ) );
+            fail( "should never get here due to an IAE" );
+        }
+        catch ( IllegalArgumentException iae )
+        {
+        }
+
+        try
+        {
+            assertFalse( evaluator.evaluate( new GreaterEqNode( "", new ClientStringValue( "" ) ), objectClasses ) );
+            fail( "should never get here due to an NE" );
+        }
+        catch ( NamingException ne )
+        {
+        }
+
+        try
+        {
+            assertFalse( evaluator.evaluate( new EqualityNode( "", new ClientStringValue( "" ) ), objectClasses ) );
+            fail( "should never get here due to an NE" );
+        }
+        catch ( NamingException ne )
+        {
+        }
+
+        try
+        {
+            assertFalse( evaluator.evaluate( new EqualityNode( "objectClass", new ClientStringValue( "" ) ), objectClasses ) );
+            fail( "should never get here due to an IAE" );
+        }
+        catch ( IllegalArgumentException iae )
+        {
+        }
+
+        try
+        {
+            objectClasses = new DefaultServerAttribute( "cn", OBJECT_CLASS );
+            assertFalse( evaluator.evaluate( new EqualityNode( "cn", new ClientStringValue( "" ) ), objectClasses ) );
+            fail( "should never get here due to an IAE" );
+        }
+        catch ( NamingException ne )
+        {
+            assertTrue( true );
+        }
+    }
+
+
+    @Test public void testMatchByName() throws Exception
+    {
+        // positive test
+        ServerAttribute objectClasses = new DefaultServerAttribute( "objectClass", OBJECT_CLASS, "person" );
+        assertTrue( evaluator.evaluate( new EqualityNode( "objectClass", new ClientStringValue( "person" ) ), objectClasses ) );
+
+        objectClasses = new DefaultServerAttribute( "objectClass", OBJECT_CLASS );
+        objectClasses.add( "person" );
+        objectClasses.add( "blah" );
+        assertTrue( evaluator.evaluate( new EqualityNode( "objectClass", new ClientStringValue( "person" ) ), objectClasses ) );
+
+        // negative tests
+        objectClasses = new DefaultServerAttribute( "objectClass", OBJECT_CLASS, "person" );
+        assertFalse( evaluator.evaluate( new EqualityNode( "objectClass", new ClientStringValue( "blah" ) ), objectClasses ) );
+
+        objectClasses = new DefaultServerAttribute( "objectClass", OBJECT_CLASS, "blah" );
+        assertFalse( evaluator.evaluate( new EqualityNode( "objectClass", new ClientStringValue( "person" ) ), objectClasses ) );
+    }
+
+
+    @Test public void testMatchByOID() throws Exception
+    {
+        ServerAttribute objectClasses = new DefaultServerAttribute( "objectClass", OBJECT_CLASS, "person" );
+
+        // positive test
+        assertTrue( evaluator.evaluate( new EqualityNode( "objectClass", new ClientStringValue( "2.5.6.6" ) ), objectClasses ) );
+
+        objectClasses = new DefaultServerAttribute( "objectClass", OBJECT_CLASS );
+        objectClasses.add( "person" );
+        objectClasses.add( "blah" );
+        assertTrue( evaluator.evaluate( new EqualityNode( "objectClass", new ClientStringValue( "2.5.6.6" ) ), objectClasses ) );
+
+        // negative tests
+        objectClasses = new DefaultServerAttribute( "objectClass", OBJECT_CLASS, "person" );
+        assertFalse( evaluator.evaluate( new EqualityNode( "objectClass", new ClientStringValue( "2.5.6.5" ) ), objectClasses ) );
+
+        objectClasses = new DefaultServerAttribute( "objectClass", OBJECT_CLASS, "blah" );
+        assertFalse( evaluator.evaluate( new EqualityNode( "objectClass", new ClientStringValue( "2.5.6.5" ) ), objectClasses ) );
+    }
+}
diff --git a/old_trunk/core/src/test/java/org/apache/directory/server/core/subtree/SubtreeEvaluatorTest.java b/old_trunk/core/src/test/java/org/apache/directory/server/core/subtree/SubtreeEvaluatorTest.java
new file mode 100644
index 0000000..7232e73
--- /dev/null
+++ b/old_trunk/core/src/test/java/org/apache/directory/server/core/subtree/SubtreeEvaluatorTest.java
@@ -0,0 +1,310 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.subtree;
+
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.naming.NamingException;
+
+import junit.framework.TestCase;
+
+import org.apache.directory.server.core.entry.DefaultServerEntry;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.schema.bootstrap.ApacheSchema;
+import org.apache.directory.server.schema.bootstrap.BootstrapSchemaLoader;
+import org.apache.directory.server.schema.bootstrap.CoreSchema;
+import org.apache.directory.server.schema.bootstrap.Schema;
+import org.apache.directory.server.schema.bootstrap.SystemSchema;
+import org.apache.directory.server.schema.registries.DefaultOidRegistry;
+import org.apache.directory.server.schema.registries.DefaultRegistries;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.filter.ExprNode;
+import org.apache.directory.shared.ldap.filter.FilterParser;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.subtree.SubtreeSpecification;
+import org.apache.directory.shared.ldap.subtree.SubtreeSpecificationModifier;
+
+
+/**
+ * Unit test cases for the SubtreeEvaluator.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class SubtreeEvaluatorTest extends TestCase
+{
+    private Registries registries;
+    private SubtreeEvaluator evaluator;
+
+
+    private void init() throws NamingException
+    {
+        BootstrapSchemaLoader loader = new BootstrapSchemaLoader();
+        DefaultRegistries bsRegistries = new DefaultRegistries( "bootstrap", loader, new DefaultOidRegistry() );
+        registries = bsRegistries;
+        Set<Schema> schemas = new HashSet<Schema>();
+        schemas.add( new SystemSchema() );
+        schemas.add( new ApacheSchema() );
+        schemas.add( new CoreSchema() );
+        loader.loadWithDependencies( schemas, bsRegistries );
+    }
+
+
+    protected void setUp() throws Exception
+    {
+        init();
+        evaluator = new SubtreeEvaluator( registries.getOidRegistry(), registries.getAttributeTypeRegistry() );
+    }
+
+
+    protected void tearDown() throws Exception
+    {
+        evaluator = null;
+        registries = null;
+    }
+
+
+    public void testDefaults() throws Exception
+    {
+        SubtreeSpecificationModifier modifier = new SubtreeSpecificationModifier();
+        SubtreeSpecification ss = modifier.getSubtreeSpecification();
+        LdapDN apDn = new LdapDN( "ou=system" );
+        LdapDN entryDn = new LdapDN( "ou=users,ou=system" );
+        ServerEntry entry = new DefaultServerEntry( registries, entryDn, "objectClass" );
+
+        assertTrue( evaluator.evaluate( ss, apDn, entryDn, entry ) );
+
+        entryDn = new LdapDN( "ou=system" );
+        assertTrue( evaluator.evaluate( ss, apDn, entryDn, entry ) );
+
+        entryDn = new LdapDN( "ou=abc" );
+        assertFalse( evaluator.evaluate( ss, apDn, entryDn, entry ) );
+    }
+
+
+    public void testWithBase() throws Exception
+    {
+        SubtreeSpecificationModifier modifier = new SubtreeSpecificationModifier();
+        modifier.setBase( new LdapDN( "ou=users" ) );
+        SubtreeSpecification ss = modifier.getSubtreeSpecification();
+        LdapDN apDn = new LdapDN( "ou=system" );
+        LdapDN entryDn = new LdapDN( "ou=users,ou=system" );
+        ServerEntry entry = new DefaultServerEntry( registries, entryDn, "objectClass" );
+
+        assertTrue( evaluator.evaluate( ss, apDn, entryDn, entry ) );
+
+        entryDn = new LdapDN( "uid=akarasulu,ou=users,ou=system" );
+        assertTrue( evaluator.evaluate( ss, apDn, entryDn, entry ) );
+
+        entryDn = new LdapDN( "ou=system" );
+        assertFalse( evaluator.evaluate( ss, apDn, entryDn, entry ) );
+    }
+
+
+    public void testWithMinMax() throws Exception
+    {
+        SubtreeSpecificationModifier modifier = new SubtreeSpecificationModifier();
+        modifier.setMinBaseDistance( 1 );
+        modifier.setMaxBaseDistance( 3 );
+        modifier.setBase( new LdapDN( "ou=users" ) );
+        SubtreeSpecification ss = modifier.getSubtreeSpecification();
+        LdapDN apDn = new LdapDN( "ou=system" );
+        LdapDN entryDn = new LdapDN( "ou=users,ou=system" );
+        ServerEntry entry = new DefaultServerEntry( registries, entryDn, "objectClass" );
+
+        assertFalse( evaluator.evaluate( ss, apDn, entryDn, entry ) );
+
+        entryDn = new LdapDN( "uid=akarasulu,ou=users,ou=system" );
+        assertTrue( evaluator.evaluate( ss, apDn, entryDn, entry ) );
+
+        entryDn = new LdapDN( "ou=system" );
+        assertFalse( evaluator.evaluate( ss, apDn, entryDn, entry ) );
+
+        entryDn = new LdapDN( "ou=twolevels,uid=akarasulu,ou=users,ou=system" );
+        assertTrue( evaluator.evaluate( ss, apDn, entryDn, entry ) );
+
+        entryDn = new LdapDN( "ou=threelevels,ou=twolevels,uid=akarasulu,ou=users,ou=system" );
+        assertTrue( evaluator.evaluate( ss, apDn, entryDn, entry ) );
+
+        entryDn = new LdapDN( "ou=fourlevels,ou=threelevels,ou=twolevels,uid=akarasulu,ou=users,ou=system" );
+        assertFalse( evaluator.evaluate( ss, apDn, entryDn, entry ) );
+    }
+
+
+    public void testWithMinMaxAndChopAfter() throws Exception
+    {
+        SubtreeSpecificationModifier modifier = new SubtreeSpecificationModifier();
+        Set<LdapDN> chopAfter = new HashSet<LdapDN>();
+        chopAfter.add( new LdapDN( "uid=Tori Amos" ) );
+        chopAfter.add( new LdapDN( "ou=twolevels,uid=akarasulu" ) );
+        modifier.setChopAfterExclusions( chopAfter );
+        modifier.setMinBaseDistance( 1 );
+        modifier.setMaxBaseDistance( 3 );
+        modifier.setBase( new LdapDN( "ou=users" ) );
+        SubtreeSpecification ss = modifier.getSubtreeSpecification();
+        LdapDN apDn = new LdapDN( "ou=system" );
+        LdapDN entryDn = new LdapDN( "ou=users,ou=system" );
+        ServerEntry entry = new DefaultServerEntry( registries, entryDn, "objectClass" );
+
+        assertFalse( evaluator.evaluate( ss, apDn, entryDn, entry ) );
+
+        entryDn = new LdapDN( "uid=akarasulu,ou=users,ou=system" );
+        assertTrue( evaluator.evaluate( ss, apDn, entryDn, entry ) );
+
+        entryDn = new LdapDN( "ou=system" );
+        assertFalse( evaluator.evaluate( ss, apDn, entryDn, entry ) );
+
+        entryDn = new LdapDN( "ou=twolevels,uid=akarasulu,ou=users,ou=system" );
+        assertTrue( evaluator.evaluate( ss, apDn, entryDn, entry ) );
+
+        entryDn = new LdapDN( "ou=threelevels,ou=twolevels,uid=akarasulu,ou=users,ou=system" );
+        assertFalse( evaluator.evaluate( ss, apDn, entryDn, entry ) );
+
+        entryDn = new LdapDN( "ou=fourlevels,ou=threelevels,ou=twolevels,uid=akarasulu,ou=users,ou=system" );
+        assertFalse( evaluator.evaluate( ss, apDn, entryDn, entry ) );
+    }
+
+
+    public void testWithMinMaxAndChopBefore() throws Exception
+    {
+        SubtreeSpecificationModifier modifier = new SubtreeSpecificationModifier();
+        Set<LdapDN> chopBefore = new HashSet<LdapDN>();
+        chopBefore.add( new LdapDN( "uid=Tori Amos" ) );
+        chopBefore.add( new LdapDN( "ou=threelevels,ou=twolevels,uid=akarasulu" ) );
+        modifier.setChopBeforeExclusions( chopBefore );
+        modifier.setMinBaseDistance( 1 );
+        modifier.setMaxBaseDistance( 3 );
+        modifier.setBase( new LdapDN( "ou=users" ) );
+        SubtreeSpecification ss = modifier.getSubtreeSpecification();
+        LdapDN apDn = new LdapDN( "ou=system" );
+        LdapDN entryDn = new LdapDN( "ou=users,ou=system" );
+        ServerEntry entry = new DefaultServerEntry( registries, entryDn, "objectClass" );
+
+        assertFalse( evaluator.evaluate( ss, apDn, entryDn, entry ) );
+
+        entryDn = new LdapDN( "uid=akarasulu,ou=users,ou=system" );
+        assertTrue( evaluator.evaluate( ss, apDn, entryDn, entry ) );
+
+        entryDn = new LdapDN( "ou=system" );
+        assertFalse( evaluator.evaluate( ss, apDn, entryDn, entry ) );
+
+        entryDn = new LdapDN( "ou=twolevels,uid=akarasulu,ou=users,ou=system" );
+        assertTrue( evaluator.evaluate( ss, apDn, entryDn, entry ) );
+
+        entryDn = new LdapDN( "ou=threelevels,ou=twolevels,uid=akarasulu,ou=users,ou=system" );
+        assertFalse( evaluator.evaluate( ss, apDn, entryDn, entry ) );
+
+        entryDn = new LdapDN( "ou=fourlevels,ou=threelevels,ou=twolevels,uid=akarasulu,ou=users,ou=system" );
+        assertFalse( evaluator.evaluate( ss, apDn, entryDn, entry ) );
+    }
+
+
+    public void testWithMinMaxAndSimpleRefinement() throws Exception
+    {
+        ExprNode refinement = FilterParser.parse( "(objectClass=person)" );
+
+        SubtreeSpecificationModifier modifier = new SubtreeSpecificationModifier();
+        modifier.setRefinement( refinement );
+        modifier.setMinBaseDistance( 1 );
+        modifier.setMaxBaseDistance( 3 );
+        modifier.setBase( new LdapDN( "ou=users" ) );
+        SubtreeSpecification ss = modifier.getSubtreeSpecification();
+        LdapDN apDn = new LdapDN( "ou=system" );
+        LdapDN entryDn = new LdapDN( "ou=users,ou=system" );
+        ServerEntry entry = new DefaultServerEntry( registries, entryDn );
+        entry.put( "objectClass", "person" );
+
+        assertFalse( evaluator.evaluate( ss, apDn, entryDn, entry ) );
+
+        entryDn = new LdapDN( "uid=akarasulu,ou=users,ou=system" );
+        assertTrue( evaluator.evaluate( ss, apDn, entryDn, entry ) );
+
+        entryDn = new LdapDN( "ou=system" );
+        assertFalse( evaluator.evaluate( ss, apDn, entryDn, entry ) );
+
+        entryDn = new LdapDN( "ou=twolevels,uid=akarasulu,ou=users,ou=system" );
+        assertTrue( evaluator.evaluate( ss, apDn, entryDn, entry ) );
+
+        entryDn = new LdapDN( "ou=threelevels,ou=twolevels,uid=akarasulu,ou=users,ou=system" );
+        assertTrue( evaluator.evaluate( ss, apDn, entryDn, entry ) );
+
+        entryDn = new LdapDN( "ou=fourlevels,ou=threelevels,ou=twolevels,uid=akarasulu,ou=users,ou=system" );
+        assertFalse( evaluator.evaluate( ss, apDn, entryDn, entry ) );
+
+        // now change the refinement so the entry is rejected
+        entry = new DefaultServerEntry( registries, entryDn );
+        entry.put( "objectClass", "organizationalUnit" );
+        
+
+        assertFalse( evaluator.evaluate( ss, apDn, entryDn, entry ) );
+
+        entryDn = new LdapDN( "uid=akarasulu,ou=users,ou=system" );
+        assertFalse( evaluator.evaluate( ss, apDn, entryDn, entry ) );
+
+        entryDn = new LdapDN( "ou=system" );
+        assertFalse( evaluator.evaluate( ss, apDn, entryDn, entry ) );
+
+        entryDn = new LdapDN( "ou=twolevels,uid=akarasulu,ou=users,ou=system" );
+        assertFalse( evaluator.evaluate( ss, apDn, entryDn, entry ) );
+
+        entryDn = new LdapDN( "ou=threelevels,ou=twolevels,uid=akarasulu,ou=users,ou=system" );
+        assertFalse( evaluator.evaluate( ss, apDn, entryDn, entry ) );
+
+        entryDn = new LdapDN( "ou=fourlevels,ou=threelevels,ou=twolevels,uid=akarasulu,ou=users,ou=system" );
+        assertFalse( evaluator.evaluate( ss, apDn, entryDn, entry ) );
+
+    }
+    
+    
+    public void testWithFilter() throws Exception
+    {
+        ExprNode filter = FilterParser.parse( "(&(cn=Ersin)(objectClass=person))" );
+
+        SubtreeSpecificationModifier modifier = new SubtreeSpecificationModifier();
+        modifier.setRefinement( filter );
+        modifier.setMinBaseDistance( 1 );
+        modifier.setMaxBaseDistance( 3 );
+        modifier.setBase( new LdapDN( "ou=users" ) );
+        SubtreeSpecification ss = modifier.getSubtreeSpecification();
+        LdapDN apDn = new LdapDN( "ou=system" );
+        LdapDN entryDn = new LdapDN( "ou=users,ou=system" );
+
+        ServerEntry entry = new DefaultServerEntry( registries, entryDn );;
+        entry.put( "objectClass", "person" );
+        entry.put( "cn", "Ersin" );
+
+        assertFalse( evaluator.evaluate( ss, apDn, entryDn, entry ) );
+
+        entryDn = new LdapDN( "cn=Ersin,ou=users,ou=system" );
+        assertTrue( evaluator.evaluate( ss, apDn, entryDn, entry ) );
+
+        // now change the filter so the entry is rejected
+        entry = new DefaultServerEntry( registries, entryDn );;
+        entry.put( "objectClass", "person" );
+        entry.put( "cn", "Alex" );
+
+        assertFalse( evaluator.evaluate( ss, apDn, entryDn, entry ) );
+
+        entryDn = new LdapDN( "cn=Alex,ou=users,ou=system" );
+        assertFalse( evaluator.evaluate( ss, apDn, entryDn, entry ) );
+    }
+}
diff --git a/old_trunk/core/src/test/resources/log4j.properties b/old_trunk/core/src/test/resources/log4j.properties
new file mode 100644
index 0000000..b05742f
--- /dev/null
+++ b/old_trunk/core/src/test/resources/log4j.properties
@@ -0,0 +1,22 @@
+#############################################################################
+#    Licensed to the Apache Software Foundation (ASF) under one or more
+#    contributor license agreements.  See the NOTICE file distributed with
+#    this work for additional information regarding copyright ownership.
+#    The ASF licenses this file to You 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.
+#############################################################################
+log4j.rootCategory=ERROR, stdout
+
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=[%d{HH:mm:ss}] %p [%c] - %m%n
+
diff --git a/old_trunk/doap_apacheds.rdf b/old_trunk/doap_apacheds.rdf
new file mode 100644
index 0000000..6248dad
--- /dev/null
+++ b/old_trunk/doap_apacheds.rdf
@@ -0,0 +1,94 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you 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.
+-->
+
+<rdf:RDF xml:lang="en"
+         xmlns="http://usefulinc.com/ns/doap#" 
+         xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" 
+         xmlns:asfext="http://projects.apache.org/ns/asfext#"
+         xmlns:foaf="http://xmlns.com/foaf/0.1/">
+<!--
+  =======================================================================
+
+   Copyright (c) 2006 The Apache Software Foundation.  
+   All rights reserved.
+
+  =======================================================================
+-->
+  <Project rdf:about="http://directory.apache.org">
+    <created>2007-04-17</created>
+    <license rdf:resource="http://usefulinc.com/doap/licenses/asl20" />
+    <name>Apache Directory Server</name>
+    <homepage rdf:resource="http://directory.apache.org" />
+    <asfext:pmc rdf:resource="http://directory.apache.org" />
+    <shortdesc>ApacheDS is an an embeddable directory server entirely written in Java.</shortdesc>
+    <description>ApacheDS is an an embeddable directory server entirely written in Java, which has been certified LDAPv3 compatible by the Open Group. Besides LDAP it supports Kerberos 5 and the Change Password Protocol.</description>
+    <bug-database rdf:resource="http://issues.apache.org/jira/browse/DIRSERVER" />
+    <mailing-list rdf:resource="http://directory.apache.org/community%26resources/mailing-lists-and-irc.html" />
+    <download-page rdf:resource="http://www.apache.org/dyn/closer.cgi/directory/apacheds/" />
+    <programming-language>Java</programming-language>
+    <category rdf:resource="http://projects.apache.org/category/network-server" />
+
+    <release>
+      <Version>
+        <name>Feature release</name>
+        <created>2008-13-07</created>
+        <revision>1.5.3</revision>
+      </Version>
+    </release>
+
+    <release>
+      <Version>
+        <name>Stable release</name>
+        <created>2007-05-26</created>
+        <revision>1.0.2</revision>
+      </Version>
+    </release>
+
+    <repository>
+      <SVNRepository>
+        <location rdf:resource="http://svn.apache.org/repos/asf/directory/apacheds/"/>
+        <browse rdf:resource="http://svn.apache.org/viewvc/directory/apacheds/"/>
+      </SVNRepository>
+    </repository>
+
+    <maintainer>
+      <foaf:Person>
+        <foaf:name>Stefan Zoerner</foaf:name>
+          <foaf:mbox rdf:resource="mailto:szoerner@apache.org"/>
+      </foaf:Person>
+    </maintainer>
+
+    <asfext:implements><asfext:Standard>
+      <asfext:title>Lightweight Directory Access Protocol (LDAP): The Protocol</asfext:title>
+      <asfext:body>IETF</asfext:body>
+      <asfext:id>RFC 4511</asfext:id>
+      <asfext:url rdf:resource="http://www.ietf.org/rfc/rfc4511.txt"/>
+    </asfext:Standard></asfext:implements>
+
+    <asfext:implements><asfext:Standard>
+      <asfext:title>The Kerberos Network Authentication Service (V5)</asfext:title>
+      <asfext:body>IETF</asfext:body>
+      <asfext:id>RFC 4120</asfext:id>
+      <asfext:url rdf:resource="http://www.ietf.org/rfc/rfc4120.txt"/>
+    </asfext:Standard></asfext:implements>
+
+  </Project>
+</rdf:RDF>
diff --git a/old_trunk/interceptor-kerberos/pom.xml b/old_trunk/interceptor-kerberos/pom.xml
new file mode 100644
index 0000000..ebe8e56
--- /dev/null
+++ b/old_trunk/interceptor-kerberos/pom.xml
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you 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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.directory.server</groupId>
+    <artifactId>apacheds-parent</artifactId>
+    <version>1.5.4-SNAPSHOT</version>
+  </parent>
+  <artifactId>apacheds-interceptor-kerberos</artifactId>
+  <name>ApacheDS Interceptors for Kerberos</name>
+  <packaging>jar</packaging>
+
+  <description>
+    Interceptors used by the ApacheDS kerberos service.
+  </description>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-io</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-core</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-bootstrap-partition</artifactId>
+      <version>${pom.version}</version>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-kerberos-shared</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <configuration> 
+          <systemProperties>
+            <property>
+              <name>workingDirectory</name>
+              <value>${basedir}/target/server-work</value>
+            </property>
+          </systemProperties>
+        </configuration>
+      </plugin>
+    </plugins>
+
+    <resources>
+      <resource>
+        <directory>src/main/resources</directory>
+        <filtering>true</filtering>
+        <excludes>
+          <exclude>**/*.gif</exclude>
+        </excludes>
+      </resource>
+    </resources>
+  </build>
+</project>
+
diff --git a/old_trunk/interceptor-kerberos/src/main/java/org/apache/directory/server/core/kerberos/KeyDerivationInterceptor.java b/old_trunk/interceptor-kerberos/src/main/java/org/apache/directory/server/core/kerberos/KeyDerivationInterceptor.java
new file mode 100644
index 0000000..4f04e8b
--- /dev/null
+++ b/old_trunk/interceptor-kerberos/src/main/java/org/apache/directory/server/core/kerberos/KeyDerivationInterceptor.java
@@ -0,0 +1,513 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.kerberos;
+
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.core.interceptor.BaseInterceptor;
+import org.apache.directory.server.core.interceptor.Interceptor;
+import org.apache.directory.server.core.interceptor.NextInterceptor;
+import org.apache.directory.server.core.interceptor.context.AddOperationContext;
+import org.apache.directory.server.core.interceptor.context.LookupOperationContext;
+import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
+import org.apache.directory.server.core.invocation.Invocation;
+import org.apache.directory.server.core.invocation.InvocationStack;
+import org.apache.directory.server.core.partition.PartitionNexusProxy;
+import org.apache.directory.server.core.normalization.NormalizationInterceptor;
+import org.apache.directory.server.core.authn.AuthenticationInterceptor;
+import org.apache.directory.server.core.referral.ReferralInterceptor;
+import org.apache.directory.server.core.authz.AciAuthorizationInterceptor;
+import org.apache.directory.server.core.authz.DefaultAuthorizationInterceptor;
+import org.apache.directory.server.core.exception.ExceptionInterceptor;
+import org.apache.directory.server.core.operational.OperationalAttributeInterceptor;
+import org.apache.directory.server.core.schema.SchemaInterceptor;
+import org.apache.directory.server.core.subtree.SubentryInterceptor;
+import org.apache.directory.server.core.collective.CollectiveAttributeInterceptor;
+import org.apache.directory.server.core.entry.DefaultServerAttribute;
+import org.apache.directory.server.core.entry.ServerAttribute;
+import org.apache.directory.server.core.entry.ServerBinaryValue;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerModification;
+import org.apache.directory.server.core.entry.ServerStringValue;
+import org.apache.directory.server.core.event.EventInterceptor;
+import org.apache.directory.server.core.trigger.TriggerInterceptor;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.KerberosKeyFactory;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.RandomKeyFactory;
+import org.apache.directory.server.kerberos.shared.exceptions.KerberosException;
+import org.apache.directory.server.kerberos.shared.io.encoder.EncryptionKeyEncoder;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+import org.apache.directory.server.kerberos.shared.store.KerberosAttribute;
+import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.entry.Modification;
+import org.apache.directory.shared.ldap.entry.ModificationOperation;
+import org.apache.directory.shared.ldap.entry.Value;
+import org.apache.directory.shared.ldap.exception.LdapAuthenticationException;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.util.StringTools;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * An {@link Interceptor} that creates symmetric Kerberos keys for users.  When a
+ * 'userPassword' is added or modified, the 'userPassword' and 'krb5PrincipalName'
+ * are used to derive Kerberos keys.  If the 'userPassword' is the special keyword
+ * 'randomKey', a random key is generated and used as the Kerberos key.
+ * 
+ * @org.apache.xbean.XBean
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class KeyDerivationInterceptor extends BaseInterceptor
+{
+    /** The log for this class. */
+    private static final Logger log = LoggerFactory.getLogger( KeyDerivationInterceptor.class );
+
+    /** The service name. */
+    public static final String NAME = "keyDerivationService";
+
+    /**
+     * Define the interceptors to bypass upon user lookup.
+     */
+    private static final Collection<String> USERLOOKUP_BYPASS;
+    static
+    {
+        Set<String> c = new HashSet<String>();
+        c.add( NormalizationInterceptor.class.getName() );
+        c.add( AuthenticationInterceptor.class.getName() );
+        c.add( ReferralInterceptor.class.getName() );
+        c.add( AciAuthorizationInterceptor.class.getName() );
+        c.add( DefaultAuthorizationInterceptor.class.getName() );
+        c.add( ExceptionInterceptor.class.getName() );
+        c.add( OperationalAttributeInterceptor.class.getName() );
+        c.add( SchemaInterceptor.class.getName() );
+        c.add( SubentryInterceptor.class.getName() );
+        c.add( CollectiveAttributeInterceptor.class.getName() );
+        c.add( EventInterceptor.class.getName() );
+        c.add( TriggerInterceptor.class.getName() );
+        USERLOOKUP_BYPASS = Collections.unmodifiableCollection( c );
+    }
+
+
+    /**
+     * Intercept the addition of the 'userPassword' and 'krb5PrincipalName' attributes.  Use the 'userPassword'
+     * and 'krb5PrincipalName' attributes to derive Kerberos keys for the principal.  If the 'userPassword' is
+     * the special keyword 'randomKey', set random keys for the principal.  Set the key version number (kvno)
+     * to '0'.
+     */
+    public void add( NextInterceptor next, AddOperationContext addContext ) throws NamingException
+    {
+        LdapDN normName = addContext.getDn();
+
+        ServerEntry entry = addContext.getEntry();
+
+        if ( ( entry.get( SchemaConstants.USER_PASSWORD_AT ) != null ) && 
+            ( entry.get( KerberosAttribute.KRB5_PRINCIPAL_NAME_AT ) != null ) )
+        {
+            log.debug( "Adding the entry '{}' for DN '{}'.", entry, normName.getUpName() );
+
+            ServerBinaryValue userPassword = (ServerBinaryValue)entry.get( SchemaConstants.USER_PASSWORD_AT ).get();
+            String strUserPassword = StringTools.utf8ToString( userPassword.get() );
+
+            if ( log.isDebugEnabled() )
+            {
+                StringBuffer sb = new StringBuffer();
+                sb.append( "'" + strUserPassword + "' ( " );
+                sb.append( userPassword );
+                sb.append( " )" );
+                log.debug( "Adding Attribute id : 'userPassword',  Values : [ {} ]", sb.toString() );
+            }
+
+            Value<?> principalNameValue = entry.get( KerberosAttribute.KRB5_PRINCIPAL_NAME_AT ).get();
+            
+            String principalName = (String)principalNameValue.get();
+
+            log.debug( "Got principal '{}' with userPassword '{}'.", principalName, strUserPassword );
+
+            Map<EncryptionType, EncryptionKey> keys = generateKeys( principalName, strUserPassword );
+
+            entry.put( KerberosAttribute.KRB5_PRINCIPAL_NAME_AT, principalName );
+            entry.put( KerberosAttribute.KRB5_KEY_VERSION_NUMBER_AT, "0" );
+
+            entry.put( getKeyAttribute( addContext.getRegistries(), keys ) );
+
+            log.debug( "Adding modified entry '{}' for DN '{}'.", entry, normName
+                .getUpName() );
+        }
+
+        next.add( addContext );
+    }
+
+
+    /**
+     * Intercept the modification of the 'userPassword' attribute.  Perform a lookup to check for an
+     * existing principal name and key version number (kvno).  If a 'krb5PrincipalName' is not in
+     * the modify request, attempt to use an existing 'krb5PrincipalName' attribute.  If a kvno
+     * exists, increment the kvno; otherwise, set the kvno to '0'.
+     * 
+     * If both a 'userPassword' and 'krb5PrincipalName' can be found, use the 'userPassword' and
+     * 'krb5PrincipalName' attributes to derive Kerberos keys for the principal.
+     * 
+     * If the 'userPassword' is the special keyword 'randomKey', set random keys for the principal.
+     */
+    public void modify( NextInterceptor next, ModifyOperationContext modContext ) throws NamingException
+    {
+        ModifySubContext subContext = new ModifySubContext();
+
+        detectPasswordModification( modContext, subContext );
+
+        if ( subContext.getUserPassword() != null )
+        {
+            lookupPrincipalAttributes( modContext, subContext );
+        }
+
+        if ( subContext.isPrincipal() && subContext.hasValues() )
+        {
+            deriveKeys( modContext, subContext );
+        }
+
+        next.modify( modContext );
+    }
+
+
+    /**
+     * Detect password modification by checking the modify request for the 'userPassword'.  Additionally,
+     * check to see if a 'krb5PrincipalName' was provided.
+     *
+     * @param modContext
+     * @param subContext
+     * @throws NamingException
+     */
+    void detectPasswordModification( ModifyOperationContext modContext, ModifySubContext subContext )
+        throws NamingException
+    {
+        List<Modification> mods = modContext.getModItems();
+
+        String operation = null;
+
+        // Loop over attributes being modified to pick out 'userPassword' and 'krb5PrincipalName'.
+        for ( Modification mod:mods )
+        {
+            if ( log.isDebugEnabled() )
+            {
+                switch ( mod.getOperation() )
+                {
+                    case ADD_ATTRIBUTE:
+                        operation = "Adding";
+                        break;
+                        
+                    case REMOVE_ATTRIBUTE:
+                        operation = "Removing";
+                        break;
+                        
+                    case REPLACE_ATTRIBUTE:
+                        operation = "Replacing";
+                        break;
+                }
+            }
+
+            ServerAttribute attr = (ServerAttribute)mod.getAttribute();
+
+            if ( attr.instanceOf( SchemaConstants.USER_PASSWORD_AT ) )
+            {
+                Object firstValue = attr.get();
+                String password = null;
+
+                if ( firstValue instanceof ServerStringValue )
+                {
+                    password = ((ServerStringValue)firstValue).get();
+                    log.debug( "{} Attribute id : 'userPassword',  Values : [ '{}' ]", operation, password );
+                }
+                else if ( firstValue instanceof ServerBinaryValue )
+                {
+                    password = StringTools.utf8ToString( ((ServerBinaryValue)firstValue).get() );
+
+                    if ( log.isDebugEnabled() )
+                    {
+                        StringBuffer sb = new StringBuffer();
+                        sb.append( "'" + password + "' ( " );
+                        sb.append( StringTools.dumpBytes( ((ServerBinaryValue)firstValue).get() ).trim() );
+                        sb.append( " )" );
+                        log.debug( "{} Attribute id : 'userPassword',  Values : [ {} ]", operation, sb.toString() );
+                    }
+                }
+
+                subContext.setUserPassword( password );
+                log.debug( "Got userPassword '{}'.", subContext.getUserPassword() );
+            }
+
+            if ( attr.instanceOf( KerberosAttribute.KRB5_PRINCIPAL_NAME_AT ) )
+            {
+                subContext.setPrincipalName( attr.getString() );
+                log.debug( "Got principal '{}'.", subContext.getPrincipalName() );
+            }
+        }
+    }
+
+
+    /**
+     * Lookup the principal's attributes that are relevant to executing key derivation.
+     *
+     * @param modContext
+     * @param subContext
+     * @throws NamingException
+     */
+    void lookupPrincipalAttributes( ModifyOperationContext modContext, ModifySubContext subContext )
+        throws NamingException
+    {
+        LdapDN principalDn = modContext.getDn();
+
+        Invocation invocation = InvocationStack.getInstance().peek();
+        PartitionNexusProxy proxy = invocation.getProxy();
+        ServerEntry userEntry;
+
+        LookupOperationContext lookupContext = 
+            new LookupOperationContext( modContext.getRegistries(),
+                new String[]
+                           { 
+                            SchemaConstants.OBJECT_CLASS_AT, 
+                            KerberosAttribute.KRB5_PRINCIPAL_NAME_AT, 
+                            KerberosAttribute.KRB5_KEY_VERSION_NUMBER_AT 
+                           } );
+        
+        lookupContext.setDn( principalDn );
+
+        userEntry = proxy.lookup( lookupContext, USERLOOKUP_BYPASS );
+
+        if ( userEntry == null )
+        {
+            throw new LdapAuthenticationException( "Failed to authenticate user '" + principalDn + "'." );
+        }
+
+        EntryAttribute objectClass = userEntry.get( SchemaConstants.OBJECT_CLASS_AT );
+        
+        if ( !objectClass.contains( SchemaConstants.KRB5_PRINCIPAL_OC ) )
+        {
+            return;
+        }
+        else
+        {
+            subContext.isPrincipal( true );
+            log.debug( "DN {} is a Kerberos principal.  Will attempt key derivation.", principalDn.getUpName() );
+        }
+
+        if ( subContext.getPrincipalName() == null )
+        {
+            EntryAttribute principalAttribute = userEntry.get( KerberosAttribute.KRB5_PRINCIPAL_NAME_AT );
+            String principalName = principalAttribute.getString();
+            subContext.setPrincipalName( principalName );
+            log.debug( "Found principal '{}' from lookup.", principalName );
+        }
+
+        EntryAttribute keyVersionNumberAttr = userEntry.get( KerberosAttribute.KRB5_KEY_VERSION_NUMBER_AT );
+
+        if ( keyVersionNumberAttr == null )
+        {
+            subContext.setNewKeyVersionNumber( 0 );
+            log.debug( "Key version number was null, setting to 0." );
+        }
+        else
+        {
+            int oldKeyVersionNumber = Integer.valueOf( keyVersionNumberAttr.getString() );
+            int newKeyVersionNumber = oldKeyVersionNumber + 1;
+            subContext.setNewKeyVersionNumber( newKeyVersionNumber );
+            log.debug( "Found key version number '{}', setting to '{}'.", oldKeyVersionNumber, newKeyVersionNumber );
+        }
+    }
+
+
+    /**
+     * Use the 'userPassword' and 'krb5PrincipalName' attributes to derive Kerberos keys for the principal.
+     * 
+     * If the 'userPassword' is the special keyword 'randomKey', set random keys for the principal.
+     *
+     * @param modContext
+     * @param subContext
+     */
+    void deriveKeys( ModifyOperationContext modContext, ModifySubContext subContext ) throws NamingException
+    {
+        List<Modification> mods = modContext.getModItems();
+
+        String principalName = subContext.getPrincipalName();
+        String userPassword = subContext.getUserPassword();
+        int kvno = subContext.getNewKeyVersionNumber();
+
+        log.debug( "Got principal '{}' with userPassword '{}'.", principalName, userPassword );
+
+        Map<EncryptionType, EncryptionKey> keys = generateKeys( principalName, userPassword );
+
+        List<Modification> newModsList = new ArrayList<Modification>();
+
+        // Make sure we preserve any other modification items.
+        for ( Modification mod:mods )
+        {
+            newModsList.add( mod );
+        }
+        
+        AttributeTypeRegistry atRegistry = modContext.getRegistries().getAttributeTypeRegistry();
+
+        // Add our modification items.
+        newModsList.add( 
+            new ServerModification( 
+                ModificationOperation.REPLACE_ATTRIBUTE, 
+                new DefaultServerAttribute(
+                    KerberosAttribute.KRB5_PRINCIPAL_NAME_AT, 
+                    atRegistry.lookup( KerberosAttribute.KRB5_PRINCIPAL_NAME_AT ),
+                    principalName ) ) );
+        newModsList.add( 
+            new ServerModification( 
+                ModificationOperation.REPLACE_ATTRIBUTE, 
+                new DefaultServerAttribute(
+                    KerberosAttribute.KRB5_KEY_VERSION_NUMBER_AT, 
+                    atRegistry.lookup( KerberosAttribute.KRB5_KEY_VERSION_NUMBER_AT ),
+                    Integer.toString( kvno ) ) ) );
+        
+        ServerAttribute attribute = getKeyAttribute( modContext.getRegistries(), keys );
+        newModsList.add( 
+            new ServerModification( ModificationOperation.REPLACE_ATTRIBUTE, attribute ) );
+
+        modContext.setModItems( newModsList );
+    }
+
+
+    private ServerAttribute getKeyAttribute( Registries registries, Map<EncryptionType, EncryptionKey> keys ) throws NamingException
+    {
+        ServerAttribute keyAttribute = 
+            new DefaultServerAttribute( KerberosAttribute.KRB5_KEY_AT, 
+                registries.getAttributeTypeRegistry().lookup( KerberosAttribute.KRB5_KEY_AT ) );
+
+        Iterator<EncryptionKey> it = keys.values().iterator();
+
+        while ( it.hasNext() )
+        {
+            try
+            {
+                keyAttribute.add( EncryptionKeyEncoder.encode( it.next() ) );
+            }
+            catch ( IOException ioe )
+            {
+                log.error( "Error encoding EncryptionKey.", ioe );
+            }
+        }
+
+        return keyAttribute;
+    }
+
+
+    private Map<EncryptionType, EncryptionKey> generateKeys( String principalName, String userPassword )
+    {
+        if ( userPassword.equalsIgnoreCase( "randomKey" ) )
+        {
+            // Generate random key.
+            try
+            {
+                return RandomKeyFactory.getRandomKeys();
+            }
+            catch ( KerberosException ke )
+            {
+                log.debug( ke.getMessage(), ke );
+                return null;
+            }
+        }
+        else
+        {
+            // Derive key based on password and principal name.
+            return KerberosKeyFactory.getKerberosKeys( principalName, userPassword );
+        }
+    }
+
+    class ModifySubContext
+    {
+        private boolean isPrincipal = false;
+        private String principalName;
+        private String userPassword;
+        private int newKeyVersionNumber = -1;
+
+
+        boolean isPrincipal()
+        {
+            return isPrincipal;
+        }
+
+
+        void isPrincipal( boolean isPrincipal )
+        {
+            this.isPrincipal = isPrincipal;
+        }
+
+
+        String getPrincipalName()
+        {
+            return principalName;
+        }
+
+
+        void setPrincipalName( String principalName )
+        {
+            this.principalName = principalName;
+        }
+
+
+        String getUserPassword()
+        {
+            return userPassword;
+        }
+
+
+        void setUserPassword( String userPassword )
+        {
+            this.userPassword = userPassword;
+        }
+
+
+        int getNewKeyVersionNumber()
+        {
+            return newKeyVersionNumber;
+        }
+
+
+        void setNewKeyVersionNumber( int newKeyVersionNumber )
+        {
+            this.newKeyVersionNumber = newKeyVersionNumber;
+        }
+
+
+        boolean hasValues()
+        {
+            return userPassword != null && principalName != null && newKeyVersionNumber > -1;
+        }
+    }
+}
diff --git a/old_trunk/interceptor-kerberos/src/main/java/org/apache/directory/server/core/kerberos/PasswordPolicyInterceptor.java b/old_trunk/interceptor-kerberos/src/main/java/org/apache/directory/server/core/kerberos/PasswordPolicyInterceptor.java
new file mode 100644
index 0000000..870a7ab
--- /dev/null
+++ b/old_trunk/interceptor-kerberos/src/main/java/org/apache/directory/server/core/kerberos/PasswordPolicyInterceptor.java
@@ -0,0 +1,351 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.kerberos;
+
+
+import org.apache.directory.server.core.entry.ServerAttribute;
+import org.apache.directory.server.core.entry.ServerBinaryValue;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerStringValue;
+import org.apache.directory.server.core.interceptor.BaseInterceptor;
+import org.apache.directory.server.core.interceptor.Interceptor;
+import org.apache.directory.server.core.interceptor.NextInterceptor;
+import org.apache.directory.server.core.interceptor.context.AddOperationContext;
+import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.entry.Modification;
+import org.apache.directory.shared.ldap.entry.Value;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.util.StringTools;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.naming.NamingException;
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * An {@link Interceptor} that enforces password policy for users.  Add or modify operations
+ * on the 'userPassword' attribute are checked against a password policy.  The password is
+ * rejected if it does not pass the password policy checks.  The password MUST be passed to
+ * the core as plaintext.
+ * 
+ * @org.apache.xbean.XBean
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class PasswordPolicyInterceptor extends BaseInterceptor
+{
+    /** The log for this class. */
+    private static final Logger log = LoggerFactory.getLogger( PasswordPolicyInterceptor.class );
+
+    /** The service name. */
+    public static final String NAME = "passwordPolicyService";
+
+
+    /**
+     * Check added attributes for a 'userPassword'.  If a 'userPassword' is found, apply any
+     * password policy checks.
+     */
+    public void add( NextInterceptor next, AddOperationContext addContext ) throws NamingException
+    {
+        LdapDN normName = addContext.getDn();
+
+        ServerEntry entry = addContext.getEntry();
+
+        log.debug( "Adding the entry '{}' for DN '{}'.", entry, normName.getUpName() );
+
+        if ( entry.get( SchemaConstants.USER_PASSWORD_AT ) != null )
+        {
+            String username = null;
+
+            ServerBinaryValue userPassword = (ServerBinaryValue)entry.get( SchemaConstants.USER_PASSWORD_AT ).get();
+
+            // The password is stored in a non H/R attribute, but it's a String
+            String strUserPassword = StringTools.utf8ToString( userPassword.get() );
+
+            if ( log.isDebugEnabled() )
+            {
+                StringBuffer sb = new StringBuffer();
+                sb.append( "'" + strUserPassword + "' ( " );
+                sb.append( userPassword );
+                sb.append( " )" );
+                log.debug( "Adding Attribute id : 'userPassword',  Values : [ {} ]", sb.toString() );
+            }
+
+            if ( entry.get( SchemaConstants.CN_AT ) != null )
+            {
+                ServerStringValue attr = (ServerStringValue)entry.get( SchemaConstants.CN_AT ).get();
+                username = attr.get();
+            }
+
+            // If userPassword fails checks, throw new NamingException.
+            check( username, strUserPassword );
+        }
+
+        next.add( addContext );
+    }
+
+
+    /**
+     * Check modification items for a 'userPassword'.  If a 'userPassword' is found, apply any
+     * password policy checks.
+     */
+    public void modify( NextInterceptor next, ModifyOperationContext modContext ) throws NamingException
+    {
+        LdapDN name = modContext.getDn();
+
+        List<Modification> mods = modContext.getModItems();
+
+        String operation = null;
+
+        for ( Modification mod:mods )
+        {
+            if ( log.isDebugEnabled() )
+            {
+                switch ( mod.getOperation() )
+                {
+                    case ADD_ATTRIBUTE:
+                        operation = "Adding";
+                        break;
+                        
+                    case REMOVE_ATTRIBUTE:
+                        operation = "Removing";
+                        break;
+                        
+                    case REPLACE_ATTRIBUTE:
+                        operation = "Replacing";
+                        break;
+                }
+            }
+
+            ServerAttribute attr = (ServerAttribute)mod.getAttribute();
+
+            if ( attr.instanceOf( SchemaConstants.USER_PASSWORD_AT ) )
+            {
+                Value<?> userPassword = attr.get();
+                String pwd = "";
+
+                if ( userPassword != null )
+                {
+                    if ( userPassword instanceof ServerStringValue )
+                    {
+                        log.debug( "{} Attribute id : 'userPassword',  Values : [ '{}' ]", operation, attr );
+                        pwd = ((ServerStringValue)userPassword).get();
+                    }
+                    else if ( userPassword instanceof ServerBinaryValue )
+                    {
+                        ServerBinaryValue password = (ServerBinaryValue)userPassword.get();
+                        
+                        String string = "";
+                        
+                        if ( password != null )
+                        {
+                            string = StringTools.utf8ToString( password.get() );
+                        }
+
+                        if ( log.isDebugEnabled() )
+                        {
+                            StringBuffer sb = new StringBuffer();
+                            sb.append( "'" + string + "' ( " );
+                            sb.append( StringTools.dumpBytes( password.get() ).trim() );
+                            sb.append( " )" );
+                            log.debug( "{} Attribute id : 'userPassword',  Values : [ {} ]", operation, sb.toString() );
+                        }
+
+                        pwd = string;
+                    }
+
+                    // if userPassword fails checks, throw new NamingException.
+                    check( name.getUpName(), pwd );
+                }
+            }
+
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( operation + " for entry '" + name.getUpName() + "' the attribute " + mod.getAttribute() );
+            }
+        }
+
+        next.modify( modContext );
+    }
+
+
+    void check( String username, String password ) throws NamingException
+    {
+        int passwordLength = 6;
+        int categoryCount = 2;
+        int tokenSize = 3;
+
+        if ( !isValid( username, password, passwordLength, categoryCount, tokenSize ) )
+        {
+            String explanation = buildErrorMessage( username, password, passwordLength, categoryCount, tokenSize );
+            log.error( explanation );
+
+            throw new NamingException( explanation );
+        }
+    }
+
+
+    /**
+     * Tests that:
+     * The password is at least six characters long.
+     * The password contains a mix of characters.
+     * The password does not contain three letter (or more) tokens from the user's account name.
+     */
+    boolean isValid( String username, String password, int passwordLength, int categoryCount, int tokenSize )
+    {
+        return isValidPasswordLength( password, passwordLength ) && isValidCategoryCount( password, categoryCount )
+            && isValidUsernameSubstring( username, password, tokenSize );
+    }
+
+
+    /**
+     * The password is at least six characters long.
+     */
+    boolean isValidPasswordLength( String password, int passwordLength )
+    {
+        return password.length() >= passwordLength;
+    }
+
+
+    /**
+     * The password contains characters from at least three of the following four categories:
+     * English uppercase characters (A - Z)
+     * English lowercase characters (a - z)
+     * Base 10 digits (0 - 9)
+     * Any non-alphanumeric character (for example: !, $, #, or %)
+     */
+    boolean isValidCategoryCount( String password, int categoryCount )
+    {
+        int uppercase = 0;
+        int lowercase = 0;
+        int digit = 0;
+        int nonAlphaNumeric = 0;
+
+        char[] characters = password.toCharArray();
+
+        for ( char character:characters )
+        {
+            if ( Character.isLowerCase( character ) )
+            {
+                lowercase = 1;
+            }
+            else
+            {
+                if ( Character.isUpperCase( character ) )
+                {
+                    uppercase = 1;
+                }
+                else
+                {
+                    if ( Character.isDigit( character ) )
+                    {
+                        digit = 1;
+                    }
+                    else
+                    {
+                        if ( !Character.isLetterOrDigit( character ) )
+                        {
+                            nonAlphaNumeric = 1;
+                        }
+                    }
+                }
+            }
+        }
+
+        return ( uppercase + lowercase + digit + nonAlphaNumeric ) >= categoryCount;
+    }
+
+
+    /**
+     * The password does not contain three letter (or more) tokens from the user's account name.
+     * 
+     * If the account name is less than three characters long, this check is not performed
+     * because the rate at which passwords would be rejected is too high. For each token that is
+     * three or more characters long, that token is searched for in the password; if it is present,
+     * the password change is rejected. For example, the name "First M. Last" would be split into
+     * three tokens: "First", "M", and "Last". Because the second token is only one character long,
+     * it would be ignored. Therefore, this user could not have a password that included either
+     * "first" or "last" as a substring anywhere in the password. All of these checks are
+     * case-insensitive.
+     */
+    boolean isValidUsernameSubstring( String username, String password, int tokenSize )
+    {
+        String[] tokens = username.split( "[^a-zA-Z]" );
+
+        for ( int ii = 0; ii < tokens.length; ii++ )
+        {
+            if ( tokens[ii].length() >= tokenSize )
+            {
+                if ( password.matches( "(?i).*" + tokens[ii] + ".*" ) )
+                {
+                    return false;
+                }
+            }
+        }
+
+        return true;
+    }
+
+
+    private String buildErrorMessage( String username, String password, int passwordLength, int categoryCount,
+        int tokenSize )
+    {
+        List<String> violations = new ArrayList<String>();
+
+        if ( !isValidPasswordLength( password, passwordLength ) )
+        {
+            violations.add( "length too short" );
+        }
+
+        if ( !isValidCategoryCount( password, categoryCount ) )
+        {
+            violations.add( "insufficient character mix" );
+        }
+
+        if ( !isValidUsernameSubstring( username, password, tokenSize ) )
+        {
+            violations.add( "contains portions of username" );
+        }
+
+        StringBuffer sb = new StringBuffer( "Password violates policy:  " );
+
+        boolean isFirst = true;
+
+        for ( String violation : violations )
+        {
+            if ( isFirst )
+            {
+                isFirst = false;
+            }
+            else
+            {
+                sb.append( ", " );
+            }
+
+            sb.append( violation );
+        }
+
+        return sb.toString();
+    }
+}
diff --git a/old_trunk/interceptor-kerberos/src/site/site.xml b/old_trunk/interceptor-kerberos/src/site/site.xml
new file mode 100644
index 0000000..9ec7c16
--- /dev/null
+++ b/old_trunk/interceptor-kerberos/src/site/site.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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.
+-->
+<!--
+  @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+-->
+<project name="${project.name}">
+    <bannerLeft>
+        <name>${project.name}</name>
+    </bannerLeft>
+    <publishDate position="navigation-bottom" format="dd-MM-yyyy HH:mm" />
+    <body>
+        <menu name="Parent">
+            <item name="Apache Directory ApacheDS" href="../index.html" />
+        </menu>
+        <menu ref="reports" />
+    </body>
+</project>
diff --git a/old_trunk/jdbm-store/pom.xml b/old_trunk/jdbm-store/pom.xml
new file mode 100644
index 0000000..a460508
--- /dev/null
+++ b/old_trunk/jdbm-store/pom.xml
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you 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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.directory.server</groupId>
+    <artifactId>apacheds-parent</artifactId>
+    <version>1.5.4-SNAPSHOT</version>
+  </parent>
+  <artifactId>apacheds-jdbm-store</artifactId>
+  <name>ApacheDS JDBM Store</name>
+  <packaging>jar</packaging>
+
+  <description>
+    A JDBM entry store which does not have any dependency on core interfaces.
+    The JDBM partition will use this store and build on it to adapt this to 
+    server specific partition interfaces.
+    Having this separate module without dependencies on core interfaces makes
+    it easier to avoid cyclic dependencies between modules.  This is especially
+    important for use within the bootstrap plugin which needs to build the
+    schema partition used for bootstrapping the server.
+  </description>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-io</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>${pom.groupId}</groupId>
+      <version>${pom.version}</version>
+      <artifactId>apacheds-jdbm</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>${pom.groupId}</groupId>
+      <artifactId>apacheds-core-constants</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>${pom.groupId}</groupId>
+      <artifactId>apacheds-core-entry</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>${pom.groupId}</groupId>
+      <artifactId>apacheds-btree-base</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>${pom.groupId}</groupId>
+      <artifactId>apacheds-schema-registries</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-source-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>attach-sources</id>
+            <phase>verify</phase>
+            <goals>
+              <goal>jar</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+
+</project>
+
diff --git a/old_trunk/jdbm-store/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/BTreeEnumeration.java b/old_trunk/jdbm-store/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/BTreeEnumeration.java
new file mode 100644
index 0000000..9c48de3
--- /dev/null
+++ b/old_trunk/jdbm-store/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/BTreeEnumeration.java
@@ -0,0 +1,127 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree.jdbm;
+
+
+import java.io.IOException;
+import java.util.NoSuchElementException;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+
+import org.apache.directory.shared.ldap.exception.LdapNamingException;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+
+import jdbm.btree.BTree;
+import jdbm.helper.TupleBrowser;
+
+
+/**
+ * A NamingEnumeration that returns keys in a BTree.  This is used specifically 
+ * for situations when tables support duplicate keys and a BTree is used for 
+ * storing the values for that key.  This enumeration thus advances a browser forwards 
+ * returning keys from a BTree as values.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class BTreeEnumeration implements NamingEnumeration
+{
+    private final jdbm.helper.Tuple jdbmTuple = new jdbm.helper.Tuple();
+    private TupleBrowser browser;
+    private boolean success = false;
+    
+    
+    BTreeEnumeration( BTree tree ) throws NamingException 
+    {
+        try
+        {
+            browser = tree.browse();
+            prefetch();
+        }
+        catch ( IOException e )
+        {
+            LdapNamingException lne = new LdapNamingException( "Failure on btree: " + e.getMessage(), 
+                ResultCodeEnum.OTHER );
+            lne.setRootCause( e );
+            throw lne;
+        }
+    }
+
+    
+    private void prefetch() throws IOException
+    {
+        success = browser.getNext( jdbmTuple );
+    }
+    
+    
+    public void close() throws NamingException
+    {
+        success = false;
+    }
+
+
+    public boolean hasMore() throws NamingException
+    {
+        return success;
+    }
+
+
+    public Object next() throws NamingException
+    {
+        if ( ! success )
+        {
+            throw new NoSuchElementException();
+        }
+        
+        Object next = jdbmTuple.getKey();
+        try
+        {
+            prefetch();
+        }
+        catch ( IOException e )
+        {
+            LdapNamingException lne = new LdapNamingException( "Failure on btree: " + e.getMessage(), 
+                ResultCodeEnum.OTHER );
+            lne.setRootCause( e );
+            throw lne;
+        }
+        return next;
+    }
+
+
+    public boolean hasMoreElements()
+    {
+        return success;
+    }
+
+
+    public Object nextElement()
+    {
+        try
+        {
+            return next();
+        }
+        catch ( NamingException e )
+        {
+            throw new NoSuchElementException( "Got IO Failure on btree: " + e.getCause().getMessage() );
+        }
+    }
+}
diff --git a/old_trunk/jdbm-store/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/BTreeIterator.java b/old_trunk/jdbm-store/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/BTreeIterator.java
new file mode 100644
index 0000000..5a6b420
--- /dev/null
+++ b/old_trunk/jdbm-store/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/BTreeIterator.java
@@ -0,0 +1,123 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree.jdbm;
+
+
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.shared.ldap.exception.LdapNamingException;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+
+import jdbm.btree.BTree;
+import jdbm.helper.TupleBrowser;
+
+
+/**
+ * A NamingEnumeration that returns keys in a BTree.  This is used specifically 
+ * for situations when tables support duplicate keys and a BTree is used for 
+ * storing the values for that key.  This enumeration thus advances a browser forwards 
+ * returning keys from a BTree as values.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class BTreeIterator implements Iterator
+{
+    private final jdbm.helper.Tuple jdbmTuple = new jdbm.helper.Tuple();
+    private TupleBrowser browser;
+    private boolean success = false;
+    private boolean doAscending = true;
+    
+    
+    BTreeIterator( BTree tree, boolean doAscending ) throws NamingException 
+    {
+        this.doAscending = doAscending;
+        
+        try
+        {
+            if ( doAscending )
+            {
+                browser = tree.browse();
+            }
+            else
+            {
+                browser = tree.browse( null );
+            }
+            
+            prefetch();
+        }
+        catch ( IOException e )
+        {
+            LdapNamingException lne = new LdapNamingException( "Failure on btree: " + e.getMessage(), 
+                ResultCodeEnum.OTHER );
+            lne.setRootCause( e );
+            throw lne;
+        }
+    }
+
+    
+    private void prefetch() throws IOException
+    {
+        if ( doAscending )
+        {
+            success = browser.getNext( jdbmTuple );
+        }
+        else
+        {
+            success = browser.getPrevious( jdbmTuple );
+        }
+    }
+    
+    
+    public boolean hasNext()
+    {
+        return success;
+    }
+
+
+    public Object next()
+    {
+        if ( ! success )
+        {
+            throw new NoSuchElementException();
+        }
+        
+        Object next = jdbmTuple.getKey();
+        try
+        {
+            prefetch();
+        }
+        catch ( IOException e )
+        {
+            throw new NoSuchElementException( "Failure on btree: " + e.getMessage() );
+        }
+        return next;
+    }
+
+
+    public void remove()
+    {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/old_trunk/jdbm-store/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/BTreeRedirect.java b/old_trunk/jdbm-store/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/BTreeRedirect.java
new file mode 100644
index 0000000..bcec010
--- /dev/null
+++ b/old_trunk/jdbm-store/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/BTreeRedirect.java
@@ -0,0 +1,53 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree.jdbm;
+
+import java.io.Serializable;
+
+
+/**
+ * A redirection pointer to another BTree.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class BTreeRedirect implements Serializable
+{
+    private static final long serialVersionUID = -4289810071005184834L;
+
+    private Long recId;
+
+    
+    public BTreeRedirect()
+    {
+    }
+
+    
+    public BTreeRedirect( long recId )
+    {
+        this.recId = new Long( recId );
+    }
+    
+    
+    public Long getRecId()
+    {
+        return recId;
+    }
+}
diff --git a/old_trunk/jdbm-store/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/BTreeTupleEnumeration.java b/old_trunk/jdbm-store/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/BTreeTupleEnumeration.java
new file mode 100644
index 0000000..602b483
--- /dev/null
+++ b/old_trunk/jdbm-store/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/BTreeTupleEnumeration.java
@@ -0,0 +1,186 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree.jdbm;
+
+
+import java.io.IOException;
+import java.util.Comparator;
+import java.util.NoSuchElementException;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+
+import org.apache.directory.server.core.partition.impl.btree.Tuple;
+import org.apache.directory.shared.ldap.exception.LdapNamingException;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+
+import jdbm.btree.BTree;
+import jdbm.helper.TupleBrowser;
+
+
+/**
+ * A NamingEnumeration that returns underlying keys in a BTree as values for a 
+ * single key within a Tuple.  This is used specifically for situations when tables
+ * support duplicate keys and a BTree is used for storing the values for that
+ * key.  This enumeration thus advances a browser forwards or reverse returning
+ * keys from a BTree as Tuple values.  The key provided in the constructor is always
+ * returned as the key of the Tuple.
+ * 
+ * <p>
+ * WARNING: The tuple returned is reused every time for efficiency and populated
+ * a over and over again with the new value.  The key never changes.
+ * </p>
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class BTreeTupleEnumeration implements NamingEnumeration
+{
+    private final Object key;
+    private final Tuple tuple = new Tuple();
+    private final jdbm.helper.Tuple jdbmTuple = new jdbm.helper.Tuple();
+    private final boolean stepForward;
+    private TupleBrowser browser;
+    private boolean success = false;
+    
+    
+    BTreeTupleEnumeration( BTree tree, Comparator<Object> comparator, Object key, Object val, boolean isGreaterThan ) 
+        throws LdapNamingException 
+    {
+        this.key = key;
+        stepForward = isGreaterThan;
+        
+        try
+        {
+            browser = tree.browse( val );
+            
+            if ( ! isGreaterThan )
+            {
+                boolean gotNextOk = browser.getNext( jdbmTuple );
+                if ( gotNextOk )
+                {
+                    Object nextVal = jdbmTuple.getKey();
+                    int compared = comparator.compare( val, nextVal );
+                    if ( compared != 0 )
+                    {
+                        browser.getPrevious( jdbmTuple );
+                    }
+                }
+            }
+            
+            prefetch();
+        }
+        catch ( IOException e )
+        {
+            LdapNamingException lne = new LdapNamingException( "Failure on btree: " + e.getMessage(), 
+                ResultCodeEnum.OTHER );
+            lne.setRootCause( e );
+            throw lne;
+        }
+    }
+
+    
+    BTreeTupleEnumeration( BTree tree, Object key ) throws NamingException 
+    {
+        this.key = key;
+        stepForward = true;
+        
+        try
+        {
+            browser = tree.browse();
+            prefetch();
+        }
+        catch ( IOException e )
+        {
+            LdapNamingException lne = new LdapNamingException( "Failure on btree: " + e.getMessage(), 
+                ResultCodeEnum.OTHER );
+            lne.setRootCause( e );
+            throw lne;
+        }
+    }
+
+    
+    private void prefetch() throws IOException
+    {
+        if ( stepForward )
+        {
+            success = browser.getNext( jdbmTuple );
+        }
+        else
+        {
+            success = browser.getPrevious( jdbmTuple );
+        }
+    }
+    
+    
+    public void close() throws NamingException
+    {
+        success = false;
+    }
+
+
+    public boolean hasMore() throws NamingException
+    {
+        return success;
+    }
+
+
+    public Object next() throws NamingException
+    {
+        if ( ! success )
+        {
+            throw new NoSuchElementException();
+        }
+        
+        tuple.setKey( key );
+        tuple.setValue( jdbmTuple.getKey() );
+        try
+        {
+            prefetch();
+        }
+        catch ( IOException e )
+        {
+            LdapNamingException lne = new LdapNamingException( "Failure on btree: " + e.getMessage(), 
+                ResultCodeEnum.OTHER );
+            lne.setRootCause( e );
+            throw lne;
+        }
+        return tuple;
+    }
+
+
+    public boolean hasMoreElements()
+    {
+        return success;
+    }
+
+
+    public Object nextElement()
+    {
+        try
+        {
+            return next();
+        }
+        catch ( NamingException e )
+        {
+            throw new NoSuchElementException( "Got IO Failure on btree: " + e.getCause().getMessage() );
+        }
+    }
+}
diff --git a/old_trunk/jdbm-store/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/DupsEnumeration.java b/old_trunk/jdbm-store/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/DupsEnumeration.java
new file mode 100644
index 0000000..2f5ab2c
--- /dev/null
+++ b/old_trunk/jdbm-store/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/DupsEnumeration.java
@@ -0,0 +1,256 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree.jdbm;
+
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.TreeSet;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+
+import jdbm.btree.BTree;
+
+import org.apache.directory.server.core.partition.impl.btree.NoDupsEnumeration;
+import org.apache.directory.server.core.partition.impl.btree.Tuple;
+
+
+/**
+ * NamingEnumeration that enumerates over duplicate values nested into a value 
+ * using a TreeSet.
+ *
+ * @warning The Tuple returned by this listing is always the same instance 
+ * object returned every time. It is reused to for the sake of efficency rather 
+ * than creating a new tuple for each next() call.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class DupsEnumeration implements NamingEnumeration
+{
+    /** Marker for whether or not next() will return successfully */
+    private boolean hasMore = true;
+    /** The Tuple to return */
+    private final Tuple returned = new Tuple();
+    /** The Tuple to store prefetched values with */
+    private final Tuple prefetched = new Tuple();
+    /** The underlying no duplicates enumeration this enum expands out */
+    private final NoDupsEnumeration underlying;
+
+    /** 
+     * The current Tuple returned from the underlying NoDupsEnumeration which
+     * contains TreeSets for Tuple values.  A NoDupsEnumeration on a Table that
+     * allows duplicates essentially returns Strings for keys and TreeSets for 
+     * their values.
+     */
+    private Tuple duplicates;
+    /** 
+     * The iterator over a set of Tuple values with the same key.  Basically
+     * iterates over the TreeSet values in the duplicates Tuple. 
+     */
+    private Iterator dupIterator;
+
+    private JdbmTable table;
+
+    
+    // ------------------------------------------------------------------------
+    // Constructor
+    // ------------------------------------------------------------------------
+
+    /**
+     * Creates a DupsEnumeration over a enumeration of Tuples holding TreeSets
+     * for values that have the same key.
+     *
+     * @param list the underlying enumeration
+     * @throws NamingException if there is a problem
+     */
+    public DupsEnumeration( JdbmTable table, NoDupsEnumeration list ) throws NamingException
+    {
+        this.table = table;
+        underlying = list;
+
+        // Protect against closed cursors
+        if ( !underlying.hasMore() )
+        {
+            close();
+            return;
+        }
+
+        prefetch();
+    }
+
+
+    // ------------------------------------------------------------------------
+    // NamingEnumeration Interface Method Implementations
+    // ------------------------------------------------------------------------
+
+    /**
+     * Returns the same Tuple every time but with different key/value pairs.
+     * 
+     * @see javax.naming.NamingEnumeration#next()
+     */
+    public Object next() throws NamingException
+    {
+        returned.setKey( prefetched.getKey() );
+        returned.setValue( prefetched.getValue() );
+
+        prefetch();
+
+        return returned;
+    }
+
+
+    /**
+     * Returns the same Tuple every time but with different key/value pairs.
+     * 
+     * @see java.util.Enumeration#nextElement()
+     */
+    public Object nextElement()
+    {
+        try
+        {
+            return next();
+        }
+        catch ( NamingException ne )
+        {
+            throw new NoSuchElementException();
+        }
+    }
+
+
+    /**
+     * @see javax.naming.NamingEnumeration#hasMore()
+     */
+    public boolean hasMore()
+    {
+        return hasMore;
+    }
+
+
+    /**
+     * Calls hasMore.
+     *
+     * @see java.util.Enumeration#hasMoreElements()
+     */
+    public boolean hasMoreElements()
+    {
+        return hasMore;
+    }
+
+
+    /**
+     * Closes the underlying NamingEnumeration
+     *
+     * @see javax.naming.NamingEnumeration#close()
+     */
+    public void close()
+    {
+        hasMore = false;
+        underlying.close();
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Private/Package Friendly Methods
+    // ------------------------------------------------------------------------
+
+    /**
+     * Prefetches values into the prefetched Tuple taking into account that 
+     * the returned Tuple values of the underlying enumeration list are really
+     * TreeSets that hold multiple sorted values for the same key.  
+     * 
+     * <p> The values prefetched into the prefetched Tuple are actual values 
+     * taken from the TreeSet.  So this NamingEnumeration simply expands out 
+     * duplicate keyed Tuples which it returns.  iterator is an iteration over
+     * the values held in the TreeSet returned by the underlying enumeration.  
+     * The values pulled off of this iterator are put into prefetched. 
+     * </p>
+     */
+    @SuppressWarnings("unchecked")
+    private void prefetch() throws NamingException
+    {
+        /*
+         * If the iterator over the values of the current key is null or is 
+         * extinguished then we need to advance to the next key.
+         */
+        while ( null == dupIterator || !dupIterator.hasNext() )
+        {
+            /*
+             * If the underlying enumeration has more elements we get the next
+             * key/TreeSet Tuple to work with and get an iterator over it. 
+             */
+            if ( underlying.hasMore() )
+            {
+                duplicates = underlying.next();
+                
+                Object values = duplicates.getValue();
+                
+                if ( values instanceof TreeSet )
+                {
+                    TreeSet set = ( TreeSet ) duplicates.getValue();
+    
+                    if ( underlying.doAscendingScan() )
+                    {
+                        dupIterator = set.iterator();
+                    }
+                    else
+                    {
+                        /*
+                         * Need to reverse the list and iterate over the reversed
+                         * list.  
+                         * 
+                         * TODO This can be optimized by using a ReverseIterator 
+                         * over the array list.  I don't think there is a way to 
+                         * do this on the TreeSet.
+                         */
+                        List list = new ArrayList( set.size() );
+                        list.addAll( set );
+                        Collections.reverse( list );
+                        dupIterator = list.iterator();
+                    }
+                }
+                else if ( values instanceof BTreeRedirect )
+                {
+                    BTree tree = table.getBTree( ( BTreeRedirect ) values );
+                    dupIterator = new BTreeIterator( tree, underlying.doAscendingScan() );
+                }
+            }
+            else
+            {
+                close();
+                return;
+            }
+        }
+
+        /*
+         * If we get to this point then iterator has more elements and 
+         * duplicates holds the Tuple containing the key and TreeSet of 
+         * values for that key which the iterator iterates over.  All we
+         * need to do is populate the prefetched Tuple with the key and the
+         * next value in the iterator.
+         */
+        prefetched.setKey( duplicates.getKey() );
+        prefetched.setValue( dupIterator.next() );
+    }
+}
diff --git a/old_trunk/jdbm-store/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmIndex.java b/old_trunk/jdbm-store/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmIndex.java
new file mode 100644
index 0000000..fde7012
--- /dev/null
+++ b/old_trunk/jdbm-store/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmIndex.java
@@ -0,0 +1,657 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree.jdbm;
+
+
+import jdbm.RecordManager;
+import jdbm.helper.MRU;
+import jdbm.recman.BaseRecordManager;
+import jdbm.recman.CacheRecordManager;
+import org.apache.directory.server.core.partition.impl.btree.Index;
+import org.apache.directory.server.core.partition.impl.btree.IndexComparator;
+import org.apache.directory.server.core.partition.impl.btree.IndexEnumeration;
+import org.apache.directory.server.core.partition.impl.btree.Tuple;
+import org.apache.directory.server.schema.SerializableComparator;
+import org.apache.directory.shared.ldap.entry.Value;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.util.AttributeUtils;
+import org.apache.directory.shared.ldap.util.SynchronizedLRUMap;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import java.io.File;
+import java.io.IOException;
+import java.util.regex.Pattern;
+
+
+/** 
+ * A Jdbm based index implementation.
+ *
+ * @org.apache.xbean.XBean
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class JdbmIndex implements Index
+{
+    /**
+     * default duplicate limit before duplicate keys switch to using a btree for values
+     */
+    public static final int DEFAULT_DUPLICATE_LIMIT = 512;
+
+    /**  the key used for the forward btree name */
+    public static final String FORWARD_BTREE = "_forward";
+    /**  the key used for the reverse btree name */
+    public static final String REVERSE_BTREE = "_reverse";
+
+    /** the attribute type resolved for this JdbmIndex */
+    private AttributeType attribute;
+    /**
+     * the forward btree where the btree key is the value of the indexed attribute and
+     * the value of the btree is the entry id of the entry containing an attribute with
+     * that value
+     */
+    private JdbmTable forward;
+    /**
+     * the reverse btree where the btree key is the entry id of the entry containing a
+     * value for the indexed attribute, and the btree value is the value of the indexed
+     * attribute
+     */
+    private JdbmTable reverse;
+    /**
+     * the JDBM record manager for the file containing this index
+     */
+    private RecordManager recMan;
+    /**
+     * the normalized value cache for this index
+     * @todo I don't think the keyCache is required anymore since the normalizer
+     * will cache values for us.
+     */
+    private SynchronizedLRUMap keyCache;
+    /** the size (number of index entries) for the cache */
+    private int cacheSize = DEFAULT_INDEX_CACHE_SIZE;
+    /**
+     * duplicate limit before duplicate keys switch to using a btree for values
+     */
+    private int numDupLimit = DEFAULT_DUPLICATE_LIMIT;
+    /**
+     * the attribute identifier set at configuration time for this index which may not
+     * be the OID but an alias name for the attributeType associated with this Index
+     */
+    private String attributeId;
+    /** whether or not this index has been initialized */
+    private boolean initialized;
+    /** a customm working directory path when specified in configuration */
+    private File wkDirPath;
+
+
+    /*
+     * NOTE: Duplicate Key Limit
+     *
+     * Jdbm cannot store duplicate keys: meaning it cannot have more than one value
+     * for the same key in the btree.  Thus as a workaround we stuff values for the
+     * same key into a TreeSet.  This is only effective up to some threshold after
+     * which we run into problems with serialization on and off disk.  A threshold
+     * is used to determine when to switch from using a TreeSet to start using another
+     * btree in the same index file just for the values.  This value only btree just
+     * has keys populated without a value for it's btree entries. When the switch
+     * occurs the value for the key in the index btree contains a pointer to the
+     * btree containing it's values.
+     *
+     * This numDupLimit is the threshold at which we switch from using in memory
+     * containers for values of the same key to using a btree for those values
+     * instead with indirection.
+     */
+
+    // ------------------------------------------------------------------------
+    // C O N S T R U C T O R S
+    // ------------------------------------------------------------------------
+
+    public JdbmIndex()
+    {
+        initialized = false;
+    }
+
+
+    public JdbmIndex( String attributeId )
+    {
+        initialized = false;
+        setAttributeId( attributeId );
+    }
+
+
+    public void init( AttributeType attributeType, File wkDirPath ) throws NamingException
+    {
+        this.keyCache = new SynchronizedLRUMap( cacheSize );
+        this.attribute = attributeType;
+        if ( this.wkDirPath == null )
+        {
+            this.wkDirPath = wkDirPath;
+        }
+
+        File file = new File( this.wkDirPath.getPath() + File.separator + attribute.getName() );
+
+        try
+        {
+            String path = file.getAbsolutePath();
+            BaseRecordManager base = new BaseRecordManager( path );
+            base.disableTransactions();
+            this.recMan = new CacheRecordManager( base, new MRU( cacheSize ) );
+        }
+        catch ( IOException e )
+        {
+            NamingException ne = new NamingException( "Could not initialize the record manager" );
+            ne.setRootCause( e );
+            throw ne;
+        }
+
+        initTables();
+        initialized = true;
+    }
+
+
+    /**
+     * Initializes the forward and reverse tables used by this Index.
+     * 
+     * @throws NamingException if we cannot initialize the forward and reverse 
+     * tables
+     */
+    private void initTables() throws NamingException
+    {
+        SerializableComparator comp;
+        comp = new SerializableComparator( attribute.getEquality().getOid() );
+
+        /*
+         * The forward key/value map stores attribute values to master table 
+         * primary keys.  A value for an attribute can occur several times in
+         * different entries so the forward map can have more than one value.
+         */
+        forward = new JdbmTable( attribute.getName() + FORWARD_BTREE, true, numDupLimit, recMan, new IndexComparator(
+            comp, true ), null, null );
+        //LongSerializer.INSTANCE );
+
+        /*
+         * Now the reverse map stores the primary key into the master table as
+         * the key and the values of attributes as the value.  If an attribute
+         * is single valued according to its specification based on a schema 
+         * then duplicate keys should not be allowed within the reverse table.
+         */
+        reverse = new JdbmTable( attribute.getName() + REVERSE_BTREE, !attribute.isSingleValue(), numDupLimit, recMan,
+            new IndexComparator( comp, false ), null, //LongSerializer.INSTANCE,
+            null );
+    }
+
+
+    /**
+     * @see org.apache.directory.server.core.partition.impl.btree.Index#getAttribute()
+     */
+    public AttributeType getAttribute()
+    {
+        return attribute;
+    }
+
+
+    // ------------------------------------------------------------------------
+    // C O N F I G U R A T I O N   M E T H O D S
+    // ------------------------------------------------------------------------
+
+    /**
+     * Protects configuration properties from being set after initialization.
+     *
+     * @param property the property to protect
+     */
+    private void protect( String property )
+    {
+        if ( initialized )
+        {
+            throw new IllegalStateException( "The " + property
+                + " property for an index cannot be set after it has been initialized." );
+        }
+    }
+
+
+    /**
+     * Gets the attribute identifier set at configuration time for this index which may not
+     * be the OID but an alias name for the attributeType associated with this Index
+     *
+     * @return configured attribute oid or alias name
+     */
+    public String getAttributeId()
+    {
+        return attributeId;
+    }
+
+
+    /**
+     * Sets the attribute identifier set at configuration time for this index which may not
+     * be the OID but an alias name for the attributeType associated with this Index
+     *
+     * @param attributeId configured attribute oid or alias name
+     */
+    public void setAttributeId( String attributeId )
+    {
+        protect( "attributeId" );
+        this.attributeId = attributeId;
+    }
+
+
+    /**
+     * Gets the threshold at which point duplicate keys use btree indirection to store
+     * their values.
+     *
+     * @return the threshold for storing a keys values in another btree
+     */
+    public int getNumDupLimit()
+    {
+        return numDupLimit;
+    }
+
+
+    /**
+     * Sets the threshold at which point duplicate keys use btree indirection to store
+     * their values.
+     *
+     * @param numDupLimit the threshold for storing a keys values in another btree
+     */
+    public void setNumDupLimit( int numDupLimit )
+    {
+        protect( "numDupLimit" );
+        this.numDupLimit = numDupLimit;
+    }
+
+
+    /**
+     * Gets the size of the index cache in terms of the number of index entries to be cached.
+     *
+     * @return the size of the index cache
+     */
+    public int getCacheSize()
+    {
+        return cacheSize;
+    }
+
+
+    /**
+     * Sets the size of the index cache in terms of the number of index entries to be cached.
+     *
+     * @param cacheSize the size of the index cache
+     */
+    public void setCacheSize( int cacheSize )
+    {
+        protect( "cacheSize" );
+        this.cacheSize = cacheSize;
+    }
+
+
+    /**
+     * Sets the working directory path to something other than the default. Sometimes more
+     * performance is gained by locating indices on separate disk spindles.
+     *
+     * @param wkDirPath optional working directory path
+     */
+    public void setWkDirPath( File wkDirPath )
+    {
+        protect( "wkDirPath" );
+        this.wkDirPath = wkDirPath;
+    }
+
+
+    /**
+     * Gets the working directory path to something other than the default. Sometimes more
+     * performance is gained by locating indices on separate disk spindles.
+     *
+     * @return optional working directory path 
+     */
+    public File getWkDirPath()
+    {
+        return wkDirPath;
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Scan Count Methods
+    // ------------------------------------------------------------------------
+
+    /**
+     * @see Index#count()
+     */
+    public int count() throws NamingException
+    {
+        return forward.count();
+    }
+
+
+    /**
+     * @see Index#count(java.lang.Object)
+     */
+    public int count( Object attrVal ) throws NamingException
+    {
+        return forward.count( getNormalized( attrVal ) );
+    }
+
+
+    /**
+     * @see org.apache.directory.server.core.partition.impl.btree.Index#count(java.lang.Object, boolean)
+     */
+    public int count( Object attrVal, boolean isGreaterThan ) throws NamingException
+    {
+        return forward.count( getNormalized( attrVal ), isGreaterThan );
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Forward and Reverse Lookups
+    // ------------------------------------------------------------------------
+
+    /**
+     * @see Index#forwardLookup(java.lang.Object)
+     */
+    public Long forwardLookup( Object attrVal ) throws NamingException
+    {
+        return ( Long ) forward.get( getNormalized( attrVal ) );
+    }
+
+
+    /**
+     * @see Index#reverseLookup(Object)
+     */
+    public Object reverseLookup( Object id ) throws NamingException
+    {
+        return reverse.get( id );
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Add/Drop Methods
+    // ------------------------------------------------------------------------
+
+    /**
+     * @see Index#add(Object,Object)
+     */
+    public synchronized void add( Object attrVal, Object id ) throws NamingException
+    {
+        forward.put( getNormalized( attrVal ), id );
+        reverse.put( id, getNormalized( attrVal ) );
+    }
+
+
+    /**
+     * @see Index#add(Attribute, Object)
+     */
+    public synchronized void add( Attribute attr, Object id ) throws NamingException
+    {
+        // Can efficiently batch add to the reverse table 
+        NamingEnumeration<?> values = attr.getAll();
+        reverse.put( id, values );
+
+        // Have no choice but to add each value individually to forward table
+        values = attr.getAll();
+        while ( values.hasMore() )
+        {
+            forward.put( values.next(), id );
+        }
+    }
+
+
+    /**
+     * @see Index#add(Attributes, Object)
+     */
+    public synchronized void add( Attributes attrs, Object id ) throws NamingException
+    {
+        add( AttributeUtils.getAttribute( attrs, attribute ), id );
+    }
+
+
+    /**
+     * @see Index#drop(Object,Object)
+     */
+    public synchronized void drop( Object attrVal, Object id ) throws NamingException
+    {
+        forward.remove( getNormalized( attrVal ), id );
+        reverse.remove( id, getNormalized( attrVal ) );
+    }
+
+
+    /**
+     * @see Index#drop(Object)
+     */
+    public void drop( Object entryId ) throws NamingException
+    {
+        NamingEnumeration<Object> values = reverse.listValues( entryId );
+
+        while ( values.hasMore() )
+        {
+            forward.remove( values.next(), entryId );
+        }
+
+        reverse.remove( entryId );
+    }
+
+
+    /**
+     * @see Index#drop(Attribute, Object)
+     */
+    public void drop( Attribute attr, Object id ) throws NamingException
+    {
+        // Can efficiently batch remove from the reverse table 
+        NamingEnumeration<?> values = attr.getAll();
+
+        // If their are no values in attr this is a request to drop all
+        if ( !values.hasMore() )
+        {
+            drop( id );
+            return;
+        }
+
+        reverse.remove( id, values );
+
+        // Have no choice but to remove values individually from forward table
+        values = attr.getAll();
+        while ( values.hasMore() )
+        {
+            forward.remove( values.next(), id );
+        }
+    }
+
+
+    /**
+     * @see Index#drop(Attributes, Object)
+     */
+    public void drop( Attributes attrs, Object id ) throws NamingException
+    {
+        drop( AttributeUtils.getAttribute( attrs, attribute ), id );
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Index Listing Operations
+    // ------------------------------------------------------------------------
+
+    /**
+     * @see Index#listReverseIndices(Object)
+     */
+    public IndexEnumeration listReverseIndices( Object id ) throws NamingException
+    {
+        return new IndexEnumeration<Tuple>( reverse.listTuples( id ), true );
+    }
+
+
+    /**
+     * @see Index#listIndices()
+     */
+    public IndexEnumeration listIndices() throws NamingException
+    {
+        return new IndexEnumeration<Tuple>( forward.listTuples() );
+    }
+
+
+    /**
+     * @see Index#listIndices(Object)
+     */
+    public IndexEnumeration listIndices( Object attrVal ) throws NamingException
+    {
+        return new IndexEnumeration<Tuple>( forward.listTuples( getNormalized( attrVal ) ) );
+    }
+
+
+    /**
+     * @see Index#listIndices(Object,boolean)
+     */
+    public IndexEnumeration<Tuple> listIndices( Object attrVal, boolean isGreaterThan ) throws NamingException
+    {
+        return new IndexEnumeration<Tuple>( forward.listTuples( getNormalized( attrVal ), isGreaterThan ) );
+    }
+
+
+    /**
+     * @see Index#listIndices(Pattern)
+     */
+    public IndexEnumeration<Tuple> listIndices( Pattern regex ) throws NamingException
+    {
+        return new IndexEnumeration<Tuple>( forward.listTuples(), false, regex );
+    }
+
+
+    /**
+     * @see Index#listIndices(Pattern,String)
+     */
+    public IndexEnumeration<Tuple> listIndices( Pattern regex, String prefix ) throws NamingException
+    {
+        return new IndexEnumeration<Tuple>( forward.listTuples( getNormalized( prefix ), true ), false, regex );
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Value Assertion (a.k.a Index Lookup) Methods //
+    // ------------------------------------------------------------------------
+
+    /**
+     * @see Index#hasValue(java.lang.Object,
+     * Object)
+     */
+    public boolean hasValue( Object attrVal, Object id ) throws NamingException
+    {
+        return forward.has( getNormalized( attrVal ), id );
+    }
+
+
+    /**
+     * @see Index#hasValue(java.lang.Object,
+     * Object, boolean)
+     */
+    public boolean hasValue( Object attrVal, Object id, boolean isGreaterThan ) throws NamingException
+    {
+        return forward.has( getNormalized( attrVal ), id, isGreaterThan );
+    }
+
+
+    /**
+     * @see Index#hasValue(Pattern,Object)
+     */
+    public boolean hasValue( Pattern regex, Object id ) throws NamingException
+    {
+        IndexEnumeration<Tuple> list = new IndexEnumeration<Tuple>( reverse.listTuples( id ), true, regex );
+        boolean hasValue = list.hasMore();
+        list.close();
+        return hasValue;
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Maintenance Methods 
+    // ------------------------------------------------------------------------
+
+    /**
+     * @see Index#close()
+     */
+    public synchronized void close() throws NamingException
+    {
+        try
+        {
+            forward.close();
+            reverse.close();
+            recMan.commit();
+            recMan.close();
+        }
+        catch ( IOException e )
+        {
+            NamingException ne = new NamingException( "Exception while closing backend index file for attribute "
+                + attribute.getName() );
+            ne.setRootCause( e );
+            throw ne;
+        }
+    }
+
+
+    /**
+     * @see Index#sync()
+     */
+    public synchronized void sync() throws NamingException
+    {
+        try
+        {
+            recMan.commit();
+        }
+        catch ( IOException e )
+        {
+            NamingException ne = new NamingException( "Exception while syncing backend index file for attribute "
+                + attribute.getName() );
+            ne.setRootCause( e );
+            throw ne;
+        }
+    }
+
+
+    /**
+     * TODO I don't think the keyCache is required anymore since the normalizer
+     * will cache values for us.
+     */
+    public Object getNormalized( Object attrVal ) throws NamingException
+    {
+        if ( attrVal instanceof Long )
+        {
+            return attrVal;
+        }
+
+        Object normalized = keyCache.get( attrVal );
+
+        if ( null == normalized )
+        {
+            if ( attrVal instanceof Value<?> )
+            {
+                normalized = attribute.getEquality().getNormalizer().normalize( ( ( Value<?> ) attrVal ).get() );
+            }
+            else
+            {
+                normalized = attribute.getEquality().getNormalizer().normalize( attrVal );
+            }
+
+            // Double map it so if we use an already normalized
+            // value we can get back the same normalized value.
+            // and not have to regenerate a second time.
+            keyCache.put( attrVal, normalized );
+            keyCache.put( normalized, normalized );
+        }
+
+        return normalized;
+    }
+}
diff --git a/old_trunk/jdbm-store/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmMasterTable.java b/old_trunk/jdbm-store/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmMasterTable.java
new file mode 100644
index 0000000..58cf0db
--- /dev/null
+++ b/old_trunk/jdbm-store/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmMasterTable.java
@@ -0,0 +1,265 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.
+ *
+ */
+package org.apache.directory.server.core.partition.impl.btree.jdbm;
+
+
+import javax.naming.NamingException;
+
+import jdbm.RecordManager;
+import jdbm.helper.LongSerializer;
+import jdbm.helper.StringComparator;
+
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerEntrySerializer;
+import org.apache.directory.server.core.partition.impl.btree.MasterTable;
+import org.apache.directory.server.schema.SerializableComparator;
+import org.apache.directory.server.schema.registries.Registries;
+
+
+/**
+ * The master table used to store the Attributes of entries.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class JdbmMasterTable extends JdbmTable implements MasterTable
+{
+    private static final StringComparator STRCOMP = new StringComparator();
+
+    private static final SerializableComparator LONG_COMPARATOR = new SerializableComparator( "1.3.6.1.4.1.18060.0.4.1.1.2" )
+    {
+        private static final long serialVersionUID = 4048791282048841016L;
+
+
+        public int compare( Object o1, Object o2 )
+        {
+            if ( o1 == null )
+            {
+                throw new IllegalArgumentException( "Argument 'obj1' is null" );
+            } 
+            else if ( o2 == null )
+            {
+                throw new IllegalArgumentException( "Argument 'obj2' is null" );
+            }
+
+            long thisVal = ( Long ) o1;
+            long anotherVal = ( Long ) o2;
+            
+            if ( thisVal == anotherVal )
+            {
+                return 0;
+            }
+            
+            if ( thisVal == anotherVal )
+            {
+                return 0;
+            }
+            
+            if ( thisVal >= 0 )
+            {
+                if ( anotherVal >= 0 )
+                {
+                    return ( thisVal > anotherVal ) ? 1 : -1;
+                }
+                else
+                {
+                    return -1;
+                }
+            }
+            else if ( anotherVal >= 0 )
+            {
+                return 1;
+            }
+            else
+            {
+                return ( thisVal < anotherVal ) ? -1 : 1;
+            }
+        }
+    };
+
+    private static final SerializableComparator STRING_COMPARATOR = new SerializableComparator( "1.3.6.1.4.1.18060.0.4.1.1.3" )
+    {
+        private static final long serialVersionUID = 3258689922792961845L;
+
+
+        public int compare( Object o1, Object o2 )
+        {
+            return STRCOMP.compare( o1, o2 );
+        }
+    };
+
+    private final JdbmTable adminTbl;
+
+
+    /**
+     * Creates the master entry table using a Berkeley Db for the backing store.
+     *
+     * @param recMan the jdbm record manager
+     * @throws NamingException if there is an error opening the Db file.
+     */
+    public JdbmMasterTable( RecordManager recMan, Registries registries ) throws NamingException
+    {
+        super( DBF, recMan, LONG_COMPARATOR, LongSerializer.INSTANCE, new ServerEntrySerializer( registries ) );
+        adminTbl = new JdbmTable( "admin", recMan, STRING_COMPARATOR, null, null );
+        String seqValue = ( String ) adminTbl.get( SEQPROP_KEY );
+
+        if ( null == seqValue )
+        {
+            adminTbl.put( SEQPROP_KEY, "0" );
+        }
+    }
+
+
+    /**
+     * Gets the ServerEntry from this MasterTable.
+     *
+     * @param id the Long id of the entry to retrieve.
+     * @return the ServerEntry with operational attributes and all.
+     * @throws NamingException if there is a read error on the underlying Db.
+     */
+    public ServerEntry get( Object id ) throws NamingException
+    {
+        return ( ServerEntry ) super.get( id );
+    }
+
+
+    /**
+     * Puts the ServerEntry into this master table at an index
+     * specified by id.  Used both to create new entries and update existing
+     * ones.
+     *
+     * @param entry the ServerEntry w/ operational attributes
+     * @param id    the Long id of the entry to put
+     * @return the ServerEntry put
+     * @throws NamingException if there is a write error on the underlying Db.
+     */
+    public ServerEntry put( ServerEntry entry, Object id ) throws NamingException
+    {
+        return ( ServerEntry ) super.put( id, entry );
+    }
+
+
+    /**
+     * Deletes a ServerEntry from the master table at an index specified by id.
+     *
+     * @param id the Long id of the entry to delete
+     * @return the Attributes of the deleted entry
+     * @throws NamingException if there is a write error on the underlying Db
+     */
+    public ServerEntry delete( Object id ) throws NamingException
+    {
+        return ( ServerEntry ) super.remove( id );
+    }
+
+
+    /**
+     * Get's the current id value from this master database's sequence without
+     * affecting the seq.
+     *
+     * @return the current value.
+     * @throws NamingException if the admin table storing sequences cannot be
+     *                         read.
+     */
+    public Long getCurrentId() throws NamingException
+    {
+        Long id;
+
+        synchronized ( adminTbl )
+        {
+            id = new Long( ( String ) adminTbl.get( SEQPROP_KEY ) );
+
+            //noinspection ConstantConditions
+            if ( null == id )
+            {
+                adminTbl.put( SEQPROP_KEY, "0" );
+                id = 0L;
+            }
+        }
+
+        return id;
+    }
+
+
+    /**
+     * Get's the next value from this SequenceBDb.  This has the side-effect of
+     * changing the current sequence values permanently in memory and on disk.
+     * Master table sequence begins at BigInteger.ONE.  The BigInteger.ZERO is
+     * used for the fictitious parent of the suffix root entry.
+     *
+     * @return the current value incremented by one.
+     * @throws NamingException if the admin table storing sequences cannot be
+     *                         read and written to.
+     */
+    public Long getNextId() throws NamingException
+    {
+        Long lastVal;
+        Long nextVal;
+
+        synchronized ( adminTbl )
+        {
+            lastVal = new Long( ( String ) adminTbl.get( SEQPROP_KEY ) );
+
+            //noinspection ConstantConditions
+            if ( null == lastVal )
+            {
+                adminTbl.put( SEQPROP_KEY, "1" );
+                return 1L;
+            } else
+            {
+                nextVal = lastVal + 1L;
+                adminTbl.put( SEQPROP_KEY, nextVal.toString() );
+            }
+        }
+
+        return nextVal;
+    }
+
+
+    /**
+     * Gets a persistent property stored in the admin table of this MasterTable.
+     *
+     * @param property the key of the property to get the value of
+     * @return the value of the property
+     * @throws NamingException when the underlying admin table cannot be read
+     */
+    public String getProperty( String property ) throws NamingException
+    {
+        synchronized ( adminTbl )
+        {
+            return ( String ) adminTbl.get( property );
+        }
+    }
+
+
+    /**
+     * Sets a persistent property stored in the admin table of this MasterTable.
+     *
+     * @param property the key of the property to set the value of
+     * @param value    the value of the property
+     * @throws NamingException when the underlying admin table cannot be writen
+     */
+    public void setProperty( String property, String value ) throws NamingException
+    {
+        synchronized ( adminTbl )
+        {
+            adminTbl.put( property, value );
+        }
+    }
+}
diff --git a/old_trunk/jdbm-store/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmStore.java b/old_trunk/jdbm-store/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmStore.java
new file mode 100644
index 0000000..4d75e75
--- /dev/null
+++ b/old_trunk/jdbm-store/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmStore.java
@@ -0,0 +1,1969 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree.jdbm;
+
+
+import jdbm.RecordManager;
+import jdbm.helper.MRU;
+import jdbm.recman.BaseRecordManager;
+import jdbm.recman.CacheRecordManager;
+
+import org.apache.directory.server.core.entry.DefaultServerAttribute;
+import org.apache.directory.server.core.entry.DefaultServerEntry;
+import org.apache.directory.server.core.entry.ServerAttribute;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerEntryUtils;
+import org.apache.directory.server.core.entry.ServerStringValue;
+import org.apache.directory.server.core.partition.Oid;
+import org.apache.directory.server.core.partition.impl.btree.Index;
+import org.apache.directory.server.core.partition.impl.btree.IndexAssertion;
+import org.apache.directory.server.core.partition.impl.btree.IndexAssertionEnumeration;
+import org.apache.directory.server.core.partition.impl.btree.IndexNotFoundException;
+import org.apache.directory.server.core.partition.impl.btree.IndexRecord;
+import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
+import org.apache.directory.server.schema.registries.OidRegistry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.entry.Modification;
+import org.apache.directory.shared.ldap.entry.ModificationOperation;
+import org.apache.directory.shared.ldap.entry.Value;
+import org.apache.directory.shared.ldap.exception.LdapNameNotFoundException;
+import org.apache.directory.shared.ldap.exception.LdapSchemaViolationException;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.name.AttributeTypeAndValue;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.name.Rdn;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.util.NamespaceTools;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+
+public class JdbmStore
+{
+    /** static logger */
+    private static final Logger LOG = LoggerFactory.getLogger( JdbmStore.class );
+    /** The default cache size is set to 10 000 objects */
+    private static final int DEFAULT_CACHE_SIZE = 10000;
+
+    /** the JDBM record manager used by this database */
+    private RecordManager recMan;
+    /** the normalized suffix DN of this backend database */
+    private LdapDN normSuffix;
+    /** the user provided suffix DN of this backend database */
+    private LdapDN upSuffix;
+    /** the working directory to use for files */
+    private File workingDirectory;
+    /** the master table storing entries by primary key */
+    private JdbmMasterTable master;
+    /** a map of attributeType numeric ids to user userIndices */
+    private Map<String, JdbmIndex> userIndices = new HashMap<String, JdbmIndex>();
+    /** a map of attributeType numeric ids to system userIndices */
+    private Map<String, JdbmIndex> systemIndices = new HashMap<String, JdbmIndex>();
+    /** true if initialized */
+    private boolean initialized;
+    /** true if we sync disks on every write operation */
+    private boolean isSyncOnWrite = true;
+
+    /** the normalized distinguished name index */
+    private JdbmIndex ndnIdx;
+    /** the user provided distinguished name index */
+    private JdbmIndex updnIdx;
+    /** the attribute existance index */
+    private JdbmIndex existanceIdx;
+    /** the parent child relationship index */
+    private JdbmIndex hierarchyIdx;
+    /** the one level scope alias index */
+    private JdbmIndex oneAliasIdx;
+    /** the subtree scope alias index */
+    private JdbmIndex subAliasIdx;
+    /** a system index on aliasedObjectName attribute */
+    private JdbmIndex aliasIdx;
+
+    /** Two static declaration to avoid lookup all over the code */
+    private static AttributeType OBJECT_CLASS_AT;
+    private static AttributeType ALIASED_OBJECT_NAME_AT;
+
+    /** A pointer on the global registries */
+    private Registries registries;
+
+    /** A pointer on the AT registry */
+    private AttributeTypeRegistry attributeTypeRegistry;
+
+    /** A pointer on the OID registry */
+    private OidRegistry oidRegistry;
+
+
+    // ------------------------------------------------------------------------
+    // C O N S T R U C T O R S
+    // ------------------------------------------------------------------------
+
+    /**
+     * Creates a store based on JDBM B+Trees.
+     */
+    public JdbmStore()
+    {
+    }
+
+    // -----------------------------------------------------------------------
+    // C O N F I G U R A T I O N   M E T H O D S
+    // -----------------------------------------------------------------------
+
+    private ServerEntry contextEntry;
+    private String suffixDn;
+    private boolean enableOptimizer;
+    private int cacheSize = DEFAULT_CACHE_SIZE;
+    private String name;
+
+
+    private void protect( String property )
+    {
+        if ( initialized )
+        {
+            throw new IllegalStateException( "Cannot set jdbm store property " + property + " after initialization." );
+        }
+    }
+
+
+    public void setWorkingDirectory( File workingDirectory )
+    {
+        protect( "workingDirectory" );
+        this.workingDirectory = workingDirectory;
+    }
+
+
+    public File getWorkingDirectory()
+    {
+        return workingDirectory;
+    }
+
+
+    public void setUserIndices( Set<JdbmIndex> userIndices )
+    {
+        protect( "userIndices" );
+        for ( JdbmIndex index : userIndices )
+        {
+            this.userIndices.put( index.getAttributeId(), index );
+        }
+    }
+
+
+    public Set<JdbmIndex> getUserIndices()
+    {
+        return new HashSet<JdbmIndex>( userIndices.values() );
+    }
+
+
+    public void setContextEntry( ServerEntry contextEntry )
+    {
+        protect( "contextEntry" );
+        this.contextEntry = contextEntry;
+    }
+
+
+    public ServerEntry getContextEntry()
+    {
+        return contextEntry;
+    }
+
+
+    public void setSuffixDn( String suffixDn )
+    {
+        protect( "suffixDn" );
+        this.suffixDn = suffixDn;
+    }
+
+
+    public String getSuffixDn()
+    {
+        return suffixDn;
+    }
+
+
+    public void setSyncOnWrite( boolean isSyncOnWrite )
+    {
+        protect( "syncOnWrite" );
+        this.isSyncOnWrite = isSyncOnWrite;
+    }
+
+
+    public boolean isSyncOnWrite()
+    {
+        return isSyncOnWrite;
+    }
+
+
+    public void setEnableOptimizer( boolean enableOptimizer )
+    {
+        protect( "enableOptimizer" );
+        this.enableOptimizer = enableOptimizer;
+    }
+
+
+    public boolean isEnableOptimizer()
+    {
+        return enableOptimizer;
+    }
+
+
+    public void setCacheSize( int cacheSize )
+    {
+        protect( "cacheSize" );
+        this.cacheSize = cacheSize;
+    }
+
+
+    public int getCacheSize()
+    {
+        return cacheSize;
+    }
+
+
+    public void setName( String name )
+    {
+        protect( "name" );
+        this.name = name;
+    }
+
+
+    public String getName()
+    {
+        return name;
+    }
+
+
+    // -----------------------------------------------------------------------
+    // E N D   C O N F I G U R A T I O N   M E T H O D S
+    // -----------------------------------------------------------------------
+
+    /**
+     * Initialize the JDBM storage system.
+     *
+     * @param oidRegistry an OID registry to resolve numeric identifiers from names
+     * @param attributeTypeRegistry an attributeType specification registry to lookup type specs
+     * @throws NamingException on failure to create proper database files
+     */
+    public synchronized void init( Registries registries ) throws NamingException
+    {
+        this.registries = registries;
+        this.oidRegistry = registries.getOidRegistry();
+        this.attributeTypeRegistry = registries.getAttributeTypeRegistry();
+
+        OBJECT_CLASS_AT = attributeTypeRegistry.lookup( SchemaConstants.OBJECT_CLASS_AT );
+        ALIASED_OBJECT_NAME_AT = attributeTypeRegistry.lookup( SchemaConstants.ALIASED_OBJECT_NAME_AT );
+
+        this.upSuffix = new LdapDN( suffixDn );
+        this.normSuffix = LdapDN.normalize( upSuffix, attributeTypeRegistry.getNormalizerMapping() );
+        workingDirectory.mkdirs();
+
+        try
+        {
+            // First, check if the file storing the data exists
+            String path = workingDirectory.getPath() + File.separator + "master";
+            BaseRecordManager base = new BaseRecordManager( path );
+            base.disableTransactions();
+
+            if ( cacheSize < 0 )
+            {
+                cacheSize = DEFAULT_CACHE_SIZE;
+
+                if ( LOG.isDebugEnabled() )
+                {
+                    LOG.debug( "Using the default entry cache size of {} for {} partition", cacheSize, name );
+                }
+            }
+            else
+            {
+                if ( LOG.isDebugEnabled() )
+                {
+                    LOG.debug( "Using the custom configured cache size of {} for {} partition", cacheSize, name );
+                }
+            }
+
+            // Now, create the entry cache for this partition
+            recMan = new CacheRecordManager( base, new MRU( cacheSize ) );
+        }
+        catch ( IOException e )
+        {
+            NamingException ne = new NamingException( "Could not initialize RecordManager" );
+            ne.setRootCause( e );
+            throw ne;
+        }
+
+        // Create the master table (the table wcontaining all the entries)
+        master = new JdbmMasterTable( recMan, registries );
+
+        // -------------------------------------------------------------------
+        // Initializes the user and system indices
+        // -------------------------------------------------------------------
+
+        setupSystemIndices();
+        setupUserIndices();
+
+        contextEntry.getDn().normalize( attributeTypeRegistry.getNormalizerMapping() );
+
+        initSuffixEntry3( suffixDn, contextEntry );
+
+        // We are done !
+        initialized = true;
+    }
+
+
+    private void setupSystemIndices() throws NamingException
+    {
+        if ( systemIndices.size() > 0 )
+        {
+            HashMap<String, JdbmIndex> tmp = new HashMap<String, JdbmIndex>();
+            for ( JdbmIndex index : systemIndices.values() )
+            {
+                String oid = oidRegistry.getOid( index.getAttributeId() );
+                tmp.put( oid, index );
+                index.init( attributeTypeRegistry.lookup( oid ), workingDirectory );
+            }
+            systemIndices = tmp;
+        }
+
+        if ( ndnIdx == null )
+        {
+            ndnIdx = new JdbmIndex();
+            ndnIdx.setAttributeId( Oid.NDN );
+            systemIndices.put( Oid.NDN, ndnIdx );
+            ndnIdx.init( attributeTypeRegistry.lookup( Oid.NDN ), workingDirectory );
+        }
+
+        if ( updnIdx == null )
+        {
+            updnIdx = new JdbmIndex();
+            updnIdx.setAttributeId( Oid.UPDN );
+            systemIndices.put( Oid.UPDN, updnIdx );
+            updnIdx.init( attributeTypeRegistry.lookup( Oid.UPDN ), workingDirectory );
+        }
+
+        if ( existanceIdx == null )
+        {
+            existanceIdx = new JdbmIndex();
+            existanceIdx.setAttributeId( Oid.EXISTANCE );
+            systemIndices.put( Oid.EXISTANCE, existanceIdx );
+            existanceIdx.init( attributeTypeRegistry.lookup( Oid.EXISTANCE ), workingDirectory );
+        }
+
+        if ( hierarchyIdx == null )
+        {
+            hierarchyIdx = new JdbmIndex();
+            hierarchyIdx.setAttributeId( Oid.HIERARCHY );
+            systemIndices.put( Oid.HIERARCHY, hierarchyIdx );
+            hierarchyIdx.init( attributeTypeRegistry.lookup( Oid.HIERARCHY ), workingDirectory );
+        }
+
+        if ( oneAliasIdx == null )
+        {
+            oneAliasIdx = new JdbmIndex();
+            oneAliasIdx.setAttributeId( Oid.ONEALIAS );
+            systemIndices.put( Oid.ONEALIAS, oneAliasIdx );
+            oneAliasIdx.init( attributeTypeRegistry.lookup( Oid.ONEALIAS ), workingDirectory );
+        }
+
+        if ( subAliasIdx == null )
+        {
+            subAliasIdx = new JdbmIndex();
+            subAliasIdx.setAttributeId( Oid.SUBALIAS );
+            systemIndices.put( Oid.SUBALIAS, subAliasIdx );
+            subAliasIdx.init( attributeTypeRegistry.lookup( Oid.SUBALIAS ), workingDirectory );
+        }
+
+        if ( aliasIdx == null )
+        {
+            aliasIdx = new JdbmIndex();
+            aliasIdx.setAttributeId( Oid.ALIAS );
+            systemIndices.put( Oid.ALIAS, aliasIdx );
+            aliasIdx.init( attributeTypeRegistry.lookup( Oid.ALIAS ), workingDirectory );
+        }
+    }
+
+
+    private void setupUserIndices() throws NamingException
+    {
+        if ( userIndices != null && userIndices.size() > 0 )
+        {
+            HashMap<String, JdbmIndex> tmp = new HashMap<String, JdbmIndex>();
+            for ( JdbmIndex index : userIndices.values() )
+            {
+                String oid = oidRegistry.getOid( index.getAttributeId() );
+                tmp.put( oid, index );
+                index.init( attributeTypeRegistry.lookup( oid ), workingDirectory );
+            }
+            userIndices = tmp;
+        }
+        else
+        {
+            userIndices = new HashMap<String, JdbmIndex>();
+        }
+    }
+
+
+    /**
+     * Called last (4th) to check if the suffix entry has been created on disk,
+     * and if not it is created.
+     *  
+     * @param suffix the suffix for the store
+     * @param entry the root entry of the store
+     * @throws NamingException on failure to add the root entry
+     */
+    protected void initSuffixEntry3( String suffix, ServerEntry entry ) throws NamingException
+    {
+        // add entry for context, if it does not exist
+        ServerEntry suffixOnDisk = getSuffixEntry();
+
+        if ( suffixOnDisk == null )
+        {
+            LdapDN dn = new LdapDN( suffix );
+            LdapDN normalizedSuffix = LdapDN.normalize( dn, attributeTypeRegistry.getNormalizerMapping() );
+
+            add( normalizedSuffix, entry );
+        }
+    }
+
+
+    /**
+     * Close the parttion : we have to close all the userIndices and the master table.
+     */
+    public synchronized void destroy()
+    {
+        LOG.debug( "destroy() called on store for {}", this.suffixDn );
+
+        if ( !initialized )
+        {
+            return;
+        }
+
+        List<JdbmIndex> array = new ArrayList<JdbmIndex>();
+        array.addAll( userIndices.values() );
+
+        if ( null != ndnIdx )
+        {
+            array.add( ndnIdx );
+        }
+
+        if ( null != updnIdx )
+        {
+            array.add( updnIdx );
+        }
+
+        if ( null != aliasIdx )
+        {
+            array.add( aliasIdx );
+        }
+
+        if ( null != oneAliasIdx )
+        {
+            array.add( oneAliasIdx );
+        }
+
+        if ( null != subAliasIdx )
+        {
+            array.add( subAliasIdx );
+        }
+
+        if ( null != hierarchyIdx )
+        {
+            array.add( hierarchyIdx );
+        }
+
+        if ( null != existanceIdx )
+        {
+            array.add( existanceIdx );
+        }
+
+        for ( JdbmIndex index : array )
+        {
+            try
+            {
+                index.close();
+                LOG.debug( "Closed {} index for {} partition.", index.getAttributeId(), suffixDn );
+            }
+            catch ( Throwable t )
+            {
+                LOG.error( "Failed to close an index.", t );
+            }
+        }
+
+        try
+        {
+            master.close();
+            LOG.debug( "Closed master table for {} partition.", suffixDn );
+        }
+        catch ( Throwable t )
+        {
+            LOG.error( "Failed to close the master.", t );
+        }
+
+        try
+        {
+            recMan.close();
+            LOG.debug( "Closed record manager for {} partition.", suffixDn );
+        }
+        catch ( Throwable t )
+        {
+            LOG.error( "Failed to close the record manager", t );
+        }
+
+        initialized = false;
+    }
+
+
+    /**
+     * Gets whether the store is initialized.
+     *
+     * @return true if the partition store is initialized
+     */
+    public boolean isInitialized()
+    {
+        return initialized;
+    }
+
+
+    /**
+     * This method is called when the synch thread is waking up, to write
+     * the modified data.
+     * 
+     * @throws NamingException on failures to sync to disk
+     */
+    public synchronized void sync() throws NamingException
+    {
+        if ( !initialized )
+        {
+            return;
+        }
+
+        List<Index> array = new ArrayList<Index>();
+        array.addAll( userIndices.values() );
+        array.add( ndnIdx );
+        array.add( updnIdx );
+        array.add( aliasIdx );
+        array.add( oneAliasIdx );
+        array.add( subAliasIdx );
+        array.add( hierarchyIdx );
+        array.add( existanceIdx );
+
+        // Sync all user defined userIndices
+        for ( Index idx : array )
+        {
+            idx.sync();
+        }
+
+        master.sync();
+
+        try
+        {
+            recMan.commit();
+        }
+        catch ( Throwable t )
+        {
+            throw ( NamingException ) new NamingException( "Failed to commit changes to the record manager." )
+                .initCause( t );
+        }
+    }
+
+
+    // ------------------------------------------------------------------------
+    // I N D E X   M E T H O D S
+    // ------------------------------------------------------------------------
+
+    public void addIndex( JdbmIndex index ) throws NamingException
+    {
+        userIndices.put( index.getAttributeId(), index );
+    }
+
+
+    public JdbmIndex getExistanceIndex()
+    {
+        return existanceIdx;
+    }
+
+
+    public void setExistanceIndex( JdbmIndex index ) throws NamingException
+    {
+        protect( "existanceIndex" );
+        existanceIdx = index;
+        systemIndices.put( index.getAttributeId(), existanceIdx );
+    }
+
+
+    public JdbmIndex getHierarchyIndex()
+    {
+        return hierarchyIdx;
+    }
+
+
+    public void setHierarchyIndex( JdbmIndex index ) throws NamingException
+    {
+        protect( "hierarchyIndex" );
+        hierarchyIdx = index;
+        systemIndices.put( index.getAttributeId(), hierarchyIdx );
+    }
+
+
+    public JdbmIndex getAliasIndex()
+    {
+        return aliasIdx;
+    }
+
+
+    public void setAliasIndex( JdbmIndex index ) throws NamingException
+    {
+        protect( "aliasIndex" );
+        aliasIdx = index;
+        systemIndices.put( index.getAttributeId(), aliasIdx );
+    }
+
+
+    public JdbmIndex getOneAliasIndex()
+    {
+        return oneAliasIdx;
+    }
+
+
+    public void setOneAliasIndex( JdbmIndex index ) throws NamingException
+    {
+        protect( "oneAliasIndex" );
+        oneAliasIdx = index;
+        systemIndices.put( index.getAttributeId(), oneAliasIdx );
+    }
+
+
+    public JdbmIndex getSubAliasIndex()
+    {
+        return subAliasIdx;
+    }
+
+
+    public void setSubAliasIndex( JdbmIndex index ) throws NamingException
+    {
+        protect( "subAliasIndex" );
+        subAliasIdx = index;
+        systemIndices.put( index.getAttributeId(), subAliasIdx );
+    }
+
+
+    public JdbmIndex getUpdnIndex()
+    {
+        return updnIdx;
+    }
+
+
+    public void setUpdnIndex( JdbmIndex index ) throws NamingException
+    {
+        protect( "updnIndex" );
+        updnIdx = index;
+        systemIndices.put( index.getAttributeId(), updnIdx );
+    }
+
+
+    public JdbmIndex getNdnIndex()
+    {
+        return ndnIdx;
+    }
+
+
+    public void setNdnIndex( JdbmIndex index ) throws NamingException
+    {
+        protect( "ndnIndex" );
+        ndnIdx = index;
+        systemIndices.put( index.getAttributeId(), ndnIdx );
+    }
+
+
+    public Iterator<String> userIndices()
+    {
+        return userIndices.keySet().iterator();
+    }
+
+
+    public Iterator<String> systemIndices()
+    {
+        return systemIndices.keySet().iterator();
+    }
+
+
+    public boolean hasUserIndexOn( String id ) throws NamingException
+    {
+        return userIndices.containsKey( oidRegistry.getOid( id ) );
+    }
+
+
+    public boolean hasSystemIndexOn( String id ) throws NamingException
+    {
+        return systemIndices.containsKey( oidRegistry.getOid( id ) );
+    }
+
+
+    public JdbmIndex getUserIndex( String id ) throws IndexNotFoundException
+    {
+        try
+        {
+            id = oidRegistry.getOid( id );
+        }
+        catch ( NamingException e )
+        {
+            LOG.error( "Failed to identify OID for: " + id, e );
+            throw new IndexNotFoundException( "Failed to identify OID for: " + id, id, e );
+        }
+
+        if ( userIndices.containsKey( id ) )
+        {
+            return userIndices.get( id );
+        }
+        else
+        {
+            String name;
+
+            try
+            {
+                name = oidRegistry.getPrimaryName( id );
+            }
+            catch ( NamingException e )
+            {
+                String msg = "Failed to resolve primary name for " + id + " in user index lookup";
+                LOG.error( msg, e );
+                throw new IndexNotFoundException( msg, id, e );
+            }
+
+            throw new IndexNotFoundException( "A user index on attribute " + id + " (" + name + ") does not exist!" );
+        }
+    }
+
+
+    public JdbmIndex getSystemIndex( String id ) throws IndexNotFoundException
+    {
+        try
+        {
+            id = oidRegistry.getOid( id );
+        }
+        catch ( NamingException e )
+        {
+            LOG.error( "Failed to identify OID for: " + id, e );
+            throw new IndexNotFoundException( "Failed to identify OID for: " + id, id, e );
+        }
+
+        if ( systemIndices.containsKey( id ) )
+        {
+            return systemIndices.get( id );
+        }
+        else
+        {
+            String name;
+
+            try
+            {
+                name = oidRegistry.getPrimaryName( id );
+            }
+            catch ( NamingException e )
+            {
+                String msg = "Failed to resolve primary name for " + id + " in user index lookup";
+                LOG.error( msg, e );
+                throw new IndexNotFoundException( msg, id, e );
+            }
+
+            throw new IndexNotFoundException( "A system index on attribute " + id + " (" + name + ") does not exist!" );
+        }
+    }
+
+
+    public Long getEntryId( String dn ) throws NamingException
+    {
+        return ndnIdx.forwardLookup( dn );
+    }
+
+
+    public String getEntryDn( Long id ) throws NamingException
+    {
+        return ( String ) ndnIdx.reverseLookup( id );
+    }
+
+
+    public Long getParentId( String dn ) throws NamingException
+    {
+        Long childId = ndnIdx.forwardLookup( dn );
+        return ( Long ) hierarchyIdx.reverseLookup( childId );
+    }
+
+
+    public Long getParentId( Long childId ) throws NamingException
+    {
+        return ( Long ) hierarchyIdx.reverseLookup( childId );
+    }
+
+
+    public String getEntryUpdn( Long id ) throws NamingException
+    {
+        return ( String ) updnIdx.reverseLookup( id );
+    }
+
+
+    public String getEntryUpdn( String dn ) throws NamingException
+    {
+        Long id = ndnIdx.forwardLookup( dn );
+        return ( String ) updnIdx.reverseLookup( id );
+    }
+
+
+    public int count() throws NamingException
+    {
+        return master.count();
+    }
+
+
+    /**
+     * Removes the index entries for an alias before the entry is deleted from
+     * the master table.
+     * 
+     * @todo Optimize this by walking the hierarchy index instead of the name 
+     * @param aliasId the id of the alias entry in the master table
+     * @throws NamingException if we cannot delete the userIndices
+     */
+    private void dropAliasIndices( Long aliasId ) throws NamingException
+    {
+        String targetDn = ( String ) aliasIdx.reverseLookup( aliasId );
+        Long targetId = getEntryId( targetDn );
+        String aliasDn = getEntryDn( aliasId );
+        LdapDN ancestorDn = ( LdapDN ) new LdapDN( aliasDn ).getPrefix( 1 );
+        Long ancestorId = getEntryId( ancestorDn.toString() );
+
+        /*
+         * We cannot just drop all tuples in the one level and subtree userIndices
+         * linking baseIds to the targetId.  If more than one alias refers to
+         * the target then droping all tuples with a value of targetId would
+         * make all other aliases to the target inconsistent.
+         * 
+         * We need to walk up the path of alias ancestors until we reach the 
+         * upSuffix, deleting each ( ancestorId, targetId ) tuple in the
+         * subtree scope alias.  We only need to do this for the direct parent
+         * of the alias on the one level subtree.
+         */
+        oneAliasIdx.drop( ancestorId, targetId );
+        subAliasIdx.drop( ancestorId, targetId );
+
+        while ( !ancestorDn.equals( normSuffix ) )
+        {
+            ancestorDn = ( LdapDN ) ancestorDn.getPrefix( 1 );
+            ancestorId = getEntryId( ancestorDn.toString() );
+
+            subAliasIdx.drop( ancestorId, targetId );
+        }
+
+        // Drops all alias tuples pointing to the id of the alias to be deleted
+        aliasIdx.drop( aliasId );
+    }
+
+
+    /**
+     * Adds userIndices for an aliasEntry to be added to the database while checking
+     * for constrained alias constructs like alias cycles and chaining.
+     * 
+     * @param aliasDn normalized distinguished name for the alias entry
+     * @param aliasTarget the user provided aliased entry dn as a string
+     * @param aliasId the id of alias entry to add
+     * @throws NamingException if index addition fails, of the alias is not 
+     * allowed due to chaining or cycle formation.
+     */
+    private void addAliasIndices( Long aliasId, LdapDN aliasDn, String aliasTarget ) throws NamingException
+    {
+        LdapDN normalizedAliasTargetDn; // Name value of aliasedObjectName
+        Long targetId; // Id of the aliasedObjectName
+        LdapDN ancestorDn; // Name of an alias entry relative
+        Long ancestorId; // Id of an alias entry relative
+
+        // Access aliasedObjectName, normalize it and generate the Name 
+        normalizedAliasTargetDn = new LdapDN( aliasTarget );
+        normalizedAliasTargetDn.normalize( attributeTypeRegistry.getNormalizerMapping() );
+
+        /*
+         * Check For Cycles
+         * 
+         * Before wasting time to lookup more values we check using the target
+         * dn to see if we have the possible formation of an alias cycle.  This
+         * happens when the alias refers back to a target that is also a 
+         * relative of the alias entry.  For detection we test if the aliased
+         * entry Dn starts with the target Dn.  If it does then we know the 
+         * aliased target is a relative and we have a perspecitive cycle.
+         */
+        if ( aliasDn.startsWith( normalizedAliasTargetDn ) )
+        {
+            if ( aliasDn.equals( normalizedAliasTargetDn ) )
+            {
+                throw new NamingException( "[36] aliasDereferencingProblem - " + "attempt to create alias to itself." );
+            }
+
+            throw new NamingException( "[36] aliasDereferencingProblem - "
+                + "attempt to create alias with cycle to relative " + aliasTarget
+                + " not allowed from descendent alias " + aliasDn );
+        }
+
+        /*
+         * Check For Aliases External To Naming Context
+         * 
+         * id may be null but the alias may be to a valid entry in 
+         * another namingContext.  Such aliases are not allowed and we
+         * need to point it out to the user instead of saying the target
+         * does not exist when it potentially could outside of this upSuffix.
+         */
+        if ( !normalizedAliasTargetDn.startsWith( normSuffix ) )
+        {
+            // Complain specifically about aliases to outside naming contexts
+            throw new NamingException( "[36] aliasDereferencingProblem -"
+                + " the alias points to an entry outside of the " + upSuffix.getUpName()
+                + " namingContext to an object whose existance cannot be" + " determined." );
+        }
+
+        // L O O K U P   T A R G E T   I D
+        targetId = ndnIdx.forwardLookup( normalizedAliasTargetDn.toNormName() );
+
+        /*
+         * Check For Target Existance
+         * 
+         * We do not allow the creation of inconsistant aliases.  Aliases should
+         * not be broken links.  If the target does not exist we start screaming
+         */
+        if ( null == targetId )
+        {
+            // Complain about target not existing
+            throw new NamingException( "[33] aliasProblem - "
+                + "the alias when dereferenced would not name a known object "
+                + "the aliasedObjectName must be set to a valid existing " + "entry." );
+        }
+
+        /*
+         * Detect Direct Alias Chain Creation
+         * 
+         * Rather than resusitate the target to test if it is an alias and fail
+         * due to chaing creation we use the alias index to determine if the
+         * target is an alias.  Hence if the alias we are about to create points
+         * to another alias as its target in the aliasedObjectName attribute, 
+         * then we have a situation where an alias chain is being created.  
+         * Alias chaining is not allowed so we throw and exception. 
+         */
+        if ( null != aliasIdx.reverseLookup( targetId ) )
+        {
+            // Complain about illegal alias chain
+            throw new NamingException( "[36] aliasDereferencingProblem -"
+                + " the alias points to another alias.  Alias chaining is" + " not supported by this backend." );
+        }
+
+        // Add the alias to the simple alias index
+        aliasIdx.add( normalizedAliasTargetDn.getNormName(), aliasId );
+
+        /*
+         * Handle One Level Scope Alias Index
+         * 
+         * The first relative is special with respect to the one level alias
+         * index.  If the target is not a sibling of the alias then we add the
+         * index entry maping the parent's id to the aliased target id.
+         */
+        ancestorDn = ( LdapDN ) aliasDn.clone();
+        ancestorDn.remove( aliasDn.size() - 1 );
+        ancestorId = getEntryId( ancestorDn.toNormName() );
+
+        if ( !NamespaceTools.isSibling( normalizedAliasTargetDn, aliasDn ) )
+        {
+            oneAliasIdx.add( ancestorId, targetId );
+        }
+
+        /*
+         * Handle Sub Level Scope Alias Index
+         * 
+         * Walk the list of relatives from the parents up to the upSuffix, testing
+         * to see if the alias' target is a descendant of the relative.  If the
+         * alias target is not a descentant of the relative it extends the scope
+         * and is added to the sub tree scope alias index.  The upSuffix node is
+         * ignored since everything is under its scope.  The first loop 
+         * iteration shall handle the parents.
+         */
+        while ( !ancestorDn.equals( normSuffix ) && null != ancestorId )
+        {
+            if ( !NamespaceTools.isDescendant( ancestorDn, normalizedAliasTargetDn ) )
+            {
+                subAliasIdx.add( ancestorId, targetId );
+            }
+
+            ancestorDn.remove( ancestorDn.size() - 1 );
+            ancestorId = getEntryId( ancestorDn.toNormName() );
+        }
+    }
+
+
+    public void add( LdapDN normName, ServerEntry entry ) throws NamingException
+    {
+        Long id;
+        Long parentId;
+
+        id = master.getNextId();
+
+        //
+        // Suffix entry cannot have a parent since it is the root so it is 
+        // capped off using the zero value which no entry can have since 
+        // entry sequences start at 1.
+        //
+
+        LdapDN parentDn = null;
+
+        if ( normName.equals( normSuffix ) )
+        {
+            parentId = 0L;
+        }
+        else
+        {
+            parentDn = ( LdapDN ) normName.clone();
+            parentDn.remove( parentDn.size() - 1 );
+            parentId = getEntryId( parentDn.toString() );
+        }
+
+        // don't keep going if we cannot find the parent Id
+        if ( parentId == null )
+        {
+            throw new LdapNameNotFoundException( "Id for parent '" + parentDn + "' not found!" );
+        }
+
+        EntryAttribute objectClass = entry.get( OBJECT_CLASS_AT );
+
+        if ( objectClass == null )
+        {
+            String msg = "Entry " + normName.getUpName() + " contains no objectClass attribute: " + entry;
+            throw new LdapSchemaViolationException( msg, ResultCodeEnum.OBJECT_CLASS_VIOLATION );
+        }
+
+        // Start adding the system userIndices
+        // Why bother doing a lookup if this is not an alias.
+
+        if ( objectClass.contains( SchemaConstants.ALIAS_OC ) )
+        {
+            EntryAttribute aliasAttr = entry.get( ALIASED_OBJECT_NAME_AT );
+            addAliasIndices( id, normName, aliasAttr.getString() );
+        }
+
+        if ( !Character.isDigit( normName.toNormName().charAt( 0 ) ) )
+        {
+            throw new IllegalStateException( "Not a normalized name: " + normName.toNormName() );
+        }
+
+        ndnIdx.add( normName.toNormName(), id );
+        updnIdx.add( normName.getUpName(), id );
+        hierarchyIdx.add( parentId, id );
+
+        // Now work on the user defined userIndices
+        for ( EntryAttribute attribute : entry )
+        {
+            String attributeOid = ( ( ServerAttribute ) attribute ).getAttributeType().getOid();
+
+            if ( hasUserIndexOn( attributeOid ) )
+            {
+                Index idx = getUserIndex( attributeOid );
+
+                // here lookup by attributeId is ok since we got attributeId from 
+                // the entry via the enumeration - it's in there as is for sure
+
+                for ( Value<?> value : attribute )
+                {
+                    idx.add( value.get(), id );
+                }
+
+                // Adds only those attributes that are indexed
+                existanceIdx.add( attributeOid, id );
+            }
+        }
+
+        master.put( entry, id );
+
+        if ( isSyncOnWrite )
+        {
+            sync();
+        }
+    }
+
+
+    public ServerEntry lookup( Long id ) throws NamingException
+    {
+        return master.get( id );
+    }
+
+
+    public void delete( Long id ) throws NamingException
+    {
+        ServerEntry entry = lookup( id );
+        Long parentId = getParentId( id );
+
+        EntryAttribute objectClass = entry.get( OBJECT_CLASS_AT );
+
+        if ( objectClass.contains( SchemaConstants.ALIAS_OC ) )
+        {
+            dropAliasIndices( id );
+        }
+
+        ndnIdx.drop( id );
+        updnIdx.drop( id );
+        hierarchyIdx.drop( id );
+
+        // Remove parent's reference to entry only if entry is not the upSuffix
+        if ( !parentId.equals( 0L ) )
+        {
+            hierarchyIdx.drop( parentId, id );
+        }
+
+        for ( EntryAttribute attribute : entry )
+        {
+            String attributeOid = ( ( ServerAttribute ) attribute ).getAttributeType().getOid();
+
+            if ( hasUserIndexOn( attributeOid ) )
+            {
+                Index index = getUserIndex( attributeOid );
+
+                // here lookup by attributeId is ok since we got attributeId from 
+                // the entry via the enumeration - it's in there as is for sure
+                for ( Value<?> value : attribute )
+                {
+                    index.drop( value, id );
+                }
+
+                existanceIdx.drop( attributeOid, id );
+            }
+        }
+
+        master.delete( id );
+
+        if ( isSyncOnWrite )
+        {
+            sync();
+        }
+    }
+
+
+    public NamingEnumeration<?> list( Long id ) throws NamingException
+    {
+        return hierarchyIdx.listIndices( id );
+    }
+
+
+    public int getChildCount( Long id ) throws NamingException
+    {
+        return hierarchyIdx.count( id );
+    }
+
+
+    public LdapDN getSuffix()
+    {
+        return normSuffix;
+    }
+
+
+    public LdapDN getUpSuffix()
+    {
+        return upSuffix;
+    }
+
+
+    public ServerEntry getSuffixEntry() throws NamingException
+    {
+        Long id = getEntryId( normSuffix.toNormName() );
+
+        if ( null == id )
+        {
+            return null;
+        }
+
+        return lookup( id );
+    }
+
+
+    public void setProperty( String propertyName, String propertyValue ) throws NamingException
+    {
+        master.setProperty( propertyName, propertyValue );
+    }
+
+
+    public String getProperty( String propertyName ) throws NamingException
+    {
+        return master.getProperty( propertyName );
+    }
+
+
+    public ServerEntry getIndices( Long id ) throws NamingException
+    {
+        ServerEntry attributes = new DefaultServerEntry( registries );
+
+        // Get the distinguishedName to id mapping
+        attributes.put( "_nDn", getEntryDn( id ) );
+        attributes.put( "_upDn", getEntryUpdn( id ) );
+        attributes.put( "_parent", getParentId( id ).toString() );
+
+        // Get all standard index attribute to value mappings
+        for ( Index index : this.userIndices.values() )
+        {
+            NamingEnumeration<IndexRecord> list = index.listReverseIndices( id );
+
+            while ( list.hasMore() )
+            {
+                IndexRecord rec = list.next();
+                Object val = rec.getIndexKey();
+                String attrId = index.getAttribute().getName();
+                EntryAttribute attr = attributes.get( attrId );
+
+                if ( attr == null )
+                {
+                    attr = new DefaultServerAttribute( attributeTypeRegistry.lookup( attrId ) );
+                }
+
+                if ( val instanceof String )
+                {
+                    attr.add( ( String ) val );
+                }
+                else
+                {
+                    attr.add( ( byte[] ) val );
+                }
+
+                attributes.put( attr );
+            }
+        }
+
+        // Get all existance mappings for this id creating a special key
+        // that looks like so 'existance[attribute]' and the value is set to id
+        NamingEnumeration<IndexRecord> list = existanceIdx.listReverseIndices( id );
+        StringBuffer val = new StringBuffer();
+
+        while ( list.hasMore() )
+        {
+            IndexRecord rec = list.next();
+            val.append( "_existance[" );
+            val.append( rec.getIndexKey() );
+            val.append( "]" );
+
+            String valStr = val.toString();
+            EntryAttribute attr = attributes.get( valStr );
+
+            if ( attr == null )
+            {
+                attr = new DefaultServerAttribute( attributeTypeRegistry.lookup( valStr ) );
+            }
+
+            Object idRec = rec.getEntryId();
+
+            if ( idRec instanceof String )
+            {
+                attr.add( ( String ) idRec );
+            }
+            else
+            {
+                attr.add( ( byte[] ) idRec );
+            }
+
+            attributes.put( attr );
+            val.setLength( 0 );
+        }
+
+        // Get all parent child mappings for this entry as the parent using the
+        // key 'child' with many entries following it.
+        list = hierarchyIdx.listIndices( id );
+        EntryAttribute childAttr = new DefaultServerAttribute( attributeTypeRegistry.lookup( "_child" ) );
+
+        attributes.put( childAttr );
+
+        while ( list.hasMore() )
+        {
+            IndexRecord rec = ( IndexRecord ) list.next();
+
+            Object idRec = rec.getEntryId();
+
+            if ( idRec instanceof String )
+            {
+                childAttr.add( ( String ) idRec );
+            }
+            else
+            {
+                childAttr.add( ( byte[] ) idRec );
+            }
+        }
+
+        return attributes;
+    }
+
+
+    /**
+     * Adds a set of attribute values while affecting the appropriate userIndices.
+     * The entry is not persisted: it is only changed in anticipation for a put 
+     * into the master table.
+     *
+     * @param id the primary key of the entry
+     * @param entry the entry to alter
+     * @param mods the attribute and values to add 
+     * @throws NamingException if index alteration or attribute addition
+     * fails.
+     */
+    private void add( Long id, ServerEntry entry, EntryAttribute mods ) throws NamingException
+    {
+        String modsOid = oidRegistry.getOid( mods.getId() );
+
+        if ( hasUserIndexOn( modsOid ) )
+        {
+            Index idx = getUserIndex( modsOid );
+            idx.add( ServerEntryUtils.toAttributeImpl( mods ), id );
+
+            // If the attr didn't exist for this id add it to existance index
+            if ( !existanceIdx.hasValue( modsOid, id ) )
+            {
+                existanceIdx.add( modsOid, id );
+            }
+        }
+
+        // add all the values in mods to the same attribute in the entry
+        AttributeType type = attributeTypeRegistry.lookup( modsOid );
+
+        for ( Value<?> value : mods )
+        {
+            entry.add( type, value );
+        }
+
+        if ( modsOid.equals( oidRegistry.getOid( SchemaConstants.ALIASED_OBJECT_NAME_AT ) ) )
+        {
+            String ndnStr = ( String ) ndnIdx.reverseLookup( id );
+            addAliasIndices( id, new LdapDN( ndnStr ), mods.getString() );
+        }
+    }
+
+
+    /**
+     * Completely removes the set of values for an attribute having the values 
+     * supplied while affecting the appropriate userIndices.  The entry is not
+     * persisted: it is only changed in anticipation for a put into the master 
+     * table.  Note that an empty attribute w/o values will remove all the 
+     * values within the entry where as an attribute w/ values will remove those
+     * attribute values it contains.
+     *
+     * @param id the primary key of the entry
+     * @param entry the entry to alter
+     * @param mods the attribute and its values to delete
+     * @throws NamingException if index alteration or attribute modification 
+     * fails.
+     */
+    private void remove( Long id, ServerEntry entry, EntryAttribute mods ) throws NamingException
+    {
+        String modsOid = oidRegistry.getOid( mods.getId() );
+
+        if ( hasUserIndexOn( modsOid ) )
+        {
+            Index idx = getUserIndex( modsOid );
+            idx.drop( ServerEntryUtils.toAttributeImpl( mods ), id );
+
+            /* 
+             * If no attribute values exist for this entryId in the index then
+             * we remove the existance index entry for the removed attribute.
+             */
+            if ( null == idx.reverseLookup( id ) )
+            {
+                existanceIdx.drop( modsOid, id );
+            }
+        }
+
+        AttributeType attrType = attributeTypeRegistry.lookup( modsOid );
+        /*
+         * If there are no attribute values in the modifications then this 
+         * implies the compelete removal of the attribute from the entry. Else
+         * we remove individual attribute values from the entry in mods one 
+         * at a time.
+         */
+        if ( mods.size() == 0 )
+        {
+            entry.removeAttributes( attrType );
+        }
+        else
+        {
+            EntryAttribute entryAttr = entry.get( attrType );
+
+            for ( Value<?> value : mods )
+            {
+                if ( value instanceof ServerStringValue )
+                {
+                    entryAttr.remove( ( String ) value.get() );
+                }
+                else
+                {
+                    entryAttr.remove( ( byte[] ) value.get() );
+                }
+            }
+
+            // if nothing is left just remove empty attribute
+            if ( entryAttr.size() == 0 )
+            {
+                entry.removeAttributes( entryAttr.getId() );
+            }
+        }
+
+        // Aliases->single valued comp/partial attr removal is not relevant here
+        if ( modsOid.equals( oidRegistry.getOid( SchemaConstants.ALIASED_OBJECT_NAME_AT ) ) )
+        {
+            dropAliasIndices( id );
+        }
+    }
+
+
+    /**
+     * Completely replaces the existing set of values for an attribute with the
+     * modified values supplied affecting the appropriate userIndices.  The entry
+     * is not persisted: it is only changed in anticipation for a put into the
+     * master table.
+     *
+     * @param id the primary key of the entry
+     * @param entry the entry to alter
+     * @param mods the replacement attribute and values
+     * @throws NamingException if index alteration or attribute modification 
+     * fails.
+     */
+    private void replace( Long id, ServerEntry entry, EntryAttribute mods ) throws NamingException
+    {
+        String modsOid = oidRegistry.getOid( mods.getId() );
+
+        if ( hasUserIndexOn( modsOid ) )
+        {
+            Index idx = getUserIndex( modsOid );
+
+            // Drop all existing attribute value index entries and add new ones
+            idx.drop( id );
+            idx.add( ServerEntryUtils.toAttributeImpl( mods ), id );
+
+            /* 
+             * If no attribute values exist for this entryId in the index then
+             * we remove the existance index entry for the removed attribute.
+             */
+            if ( null == idx.reverseLookup( id ) )
+            {
+                existanceIdx.drop( modsOid, id );
+            }
+        }
+
+        String aliasAttributeOid = oidRegistry.getOid( SchemaConstants.ALIASED_OBJECT_NAME_AT );
+
+        if ( modsOid.equals( aliasAttributeOid ) )
+        {
+            dropAliasIndices( id );
+        }
+
+        // replaces old attributes with new modified ones if they exist
+        if ( mods.size() > 0 )
+        {
+            entry.put( mods );
+        }
+        else
+        // removes old attributes if new replacements do not exist
+        {
+            entry.remove( mods );
+        }
+
+        if ( modsOid.equals( aliasAttributeOid ) && mods.size() > 0 )
+        {
+            String ndnStr = ( String ) ndnIdx.reverseLookup( id );
+            addAliasIndices( id, new LdapDN( ndnStr ), mods.getString() );
+        }
+    }
+
+
+    public void modify( LdapDN dn, ModificationOperation modOp, ServerEntry mods ) throws NamingException
+    {
+        NamingEnumeration<String> attrs;
+        Long id = getEntryId( dn.toString() );
+        ServerEntry entry = master.get( id );
+
+        for ( AttributeType attributeType : mods.getAttributeTypes() )
+        {
+            EntryAttribute attr = mods.get( attributeType );
+
+            switch ( modOp )
+            {
+                case ADD_ATTRIBUTE:
+                    add( id, entry, attr );
+                    break;
+
+                case REMOVE_ATTRIBUTE:
+                    remove( id, entry, attr );
+                    break;
+
+                case REPLACE_ATTRIBUTE:
+                    replace( id, entry, attr );
+
+                    break;
+
+                default:
+                    throw new NamingException( "Unidentified modification operation" );
+            }
+        }
+
+        master.put( entry, id );
+
+        if ( isSyncOnWrite )
+        {
+            sync();
+        }
+    }
+
+
+    public void modify( LdapDN dn, List<Modification> mods ) throws NamingException
+    {
+        Long id = getEntryId( dn.toString() );
+        ServerEntry entry = master.get( id );
+
+        for ( Modification mod : mods )
+        {
+            ServerAttribute attrMods = ( ServerAttribute ) mod.getAttribute();
+
+            switch ( mod.getOperation() )
+            {
+                case ADD_ATTRIBUTE:
+                    add( id, entry, attrMods );
+                    break;
+
+                case REMOVE_ATTRIBUTE:
+                    remove( id, entry, attrMods );
+                    break;
+
+                case REPLACE_ATTRIBUTE:
+                    replace( id, entry, attrMods );
+                    break;
+
+                default:
+                    throw new NamingException( "Unidentified modification operation" );
+            }
+        }
+
+        master.put( entry, id );
+
+        if ( isSyncOnWrite )
+        {
+            sync();
+        }
+    }
+
+
+    /**
+     * Changes the relative distinguished name of an entry specified by a 
+     * distinguished name with the optional removal of the old Rdn attribute
+     * value from the entry.  Name changes propagate down as dn changes to the 
+     * descendants of the entry where the Rdn changed. 
+     * 
+     * An Rdn change operation does not change parent child relationships.  It 
+     * merely propagates a name change at a point in the DIT where the Rdn is 
+     * changed. The change propagates down the subtree rooted at the 
+     * distinguished name specified.
+     *
+     * @param dn the normalized distinguished name of the entry to alter
+     * @param newRdn the new Rdn to set
+     * @param deleteOldRdn whether or not to remove the old Rdn attr/val
+     * @throws NamingException if there are any errors propagating the name
+     *        changes.
+     */
+    public void rename( LdapDN dn, Rdn newRdn, boolean deleteOldRdn ) throws NamingException
+    {
+        Long id = getEntryId( dn.getNormName() );
+        ServerEntry entry = lookup( id );
+        LdapDN updn = entry.getDn();
+
+        /* 
+         * H A N D L E   N E W   R D N
+         * ====================================================================
+         * Add the new Rdn attribute to the entry.  If an index exists on the 
+         * new Rdn attribute we add the index for this attribute value pair.
+         * Also we make sure that the existance index shows the existance of the
+         * new Rdn attribute within this entry.
+         */
+
+        for ( AttributeTypeAndValue newAtav : newRdn )
+        {
+            String newNormType = newAtav.getNormType();
+            String newNormValue = ( String ) newAtav.getNormValue();
+            AttributeType newRdnAttrType = attributeTypeRegistry.lookup( newNormType );
+            entry.add( newRdnAttrType, ( String ) newAtav.getUpValue() );
+
+            if ( hasUserIndexOn( newNormType ) )
+            {
+                Index idx = getUserIndex( newNormType );
+                idx.add( newNormValue, id );
+
+                // Make sure the altered entry shows the existance of the new attrib
+                if ( !existanceIdx.hasValue( newNormType, id ) )
+                {
+                    existanceIdx.add( newNormType, id );
+                }
+            }
+        }
+
+        /*
+         * H A N D L E   O L D   R D N
+         * ====================================================================
+         * If the old Rdn is to be removed we need to get the attribute and 
+         * value for it.  Keep in mind the old Rdn need not be based on the 
+         * same attr as the new one.  We remove the Rdn value from the entry
+         * and remove the value/id tuple from the index on the old Rdn attr
+         * if any.  We also test if the delete of the old Rdn index tuple 
+         * removed all the attribute values of the old Rdn using a reverse
+         * lookup.  If so that means we blew away the last value of the old 
+         * Rdn attribute.  In this case we need to remove the attrName/id 
+         * tuple from the existance index.
+         * 
+         * We only remove an ATAV of the old Rdn if it is not included in the
+         * new Rdn.
+         */
+
+        if ( deleteOldRdn )
+        {
+            Rdn oldRdn = updn.getRdn();
+            for ( AttributeTypeAndValue oldAtav : oldRdn )
+            {
+                // check if the new ATAV is part of the old Rdn
+                // if that is the case we do not remove the ATAV
+                boolean mustRemove = true;
+                for ( AttributeTypeAndValue newAtav : newRdn )
+                {
+                    if ( oldAtav.equals( newAtav ) )
+                    {
+                        mustRemove = false;
+                        break;
+                    }
+                }
+
+                if ( mustRemove )
+                {
+                    String oldNormType = oldAtav.getNormType();
+                    String oldNormValue = ( String ) oldAtav.getNormValue();
+                    AttributeType oldRdnAttrType = attributeTypeRegistry.lookup( oldNormType );
+                    entry.remove( oldRdnAttrType, oldNormValue );
+
+                    if ( hasUserIndexOn( oldNormType ) )
+                    {
+                        Index idx = getUserIndex( oldNormType );
+                        idx.drop( oldNormValue, id );
+
+                        /*
+                         * If there is no value for id in this index due to our
+                         * drop above we remove the oldRdnAttr from the existance idx
+                         */
+                        if ( null == idx.reverseLookup( id ) )
+                        {
+                            existanceIdx.drop( oldNormType, id );
+                        }
+                    }
+                }
+            }
+        }
+
+        /*
+         * H A N D L E   D N   C H A N G E
+         * ====================================================================
+         * 1) Build the new user defined distinguished name
+         *      - clone / copy old updn
+         *      - remove old upRdn from copy
+         *      - add the new upRdn to the copy
+         * 2) Make call to recursive modifyDn method to change the names of the
+         *    entry and its descendants
+         */
+
+        LdapDN newUpdn = ( LdapDN ) updn.clone(); // copy da old updn
+        newUpdn.remove( newUpdn.size() - 1 ); // remove old upRdn
+        newUpdn.add( newRdn.getUpName() ); // add da new upRdn
+
+        // gotta normalize cuz this thang is cloned and not normalized by default
+        newUpdn.normalize( attributeTypeRegistry.getNormalizerMapping() );
+
+        modifyDn( id, newUpdn, false ); // propagate dn changes
+
+        // Update the current entry
+        entry.setDn( newUpdn );
+        master.put( entry, id );
+
+        if ( isSyncOnWrite )
+        {
+            sync();
+        }
+    }
+
+
+    /*
+     * The move operation severs a child from a parent creating a new parent
+     * child relationship.  As a consequence the relationships between the 
+     * old ancestors of the child and its descendants change.  A descendant is
+     *   
+     */
+
+    /**
+     * Recursively modifies the distinguished name of an entry and the names of
+     * its descendants calling itself in the recursion.
+     *
+     * @param id the primary key of the entry
+     * @param updn User provided distinguished name to set as the new DN
+     * @param isMove whether or not the name change is due to a move operation
+     * which affects alias userIndices.
+     * @throws NamingException if something goes wrong
+     */
+    private void modifyDn( Long id, LdapDN updn, boolean isMove ) throws NamingException
+    {
+        String aliasTarget;
+
+        // Now we can handle the appropriate name userIndices for all cases
+        ndnIdx.drop( id );
+
+        if ( !updn.isNormalized() )
+        {
+            updn.normalize( attributeTypeRegistry.getNormalizerMapping() );
+        }
+
+        ndnIdx.add( ndnIdx.getNormalized( updn.toNormName() ), id );
+
+        updnIdx.drop( id );
+        updnIdx.add( updn.getUpName(), id );
+
+        /* 
+         * Read Alias Index Tuples
+         * 
+         * If this is a name change due to a move operation then the one and
+         * subtree userIndices for aliases were purged before the aliases were
+         * moved.  Now we must add them for each alias entry we have moved.  
+         * 
+         * aliasTarget is used as a marker to tell us if we're moving an 
+         * alias.  If it is null then the moved entry is not an alias.
+         */
+        if ( isMove )
+        {
+            aliasTarget = ( String ) aliasIdx.reverseLookup( id );
+
+            if ( null != aliasTarget )
+            {
+                addAliasIndices( id, new LdapDN( getEntryDn( id ) ), aliasTarget );
+            }
+        }
+
+        NamingEnumeration children = list( id );
+        while ( children.hasMore() )
+        {
+            // Get the child and its id
+            IndexRecord rec = ( IndexRecord ) children.next();
+            Long childId = ( Long ) rec.getEntryId();
+
+            /* 
+             * Calculate the Dn for the child's new name by copying the parents
+             * new name and adding the child's old upRdn to new name as its Rdn
+             */
+            LdapDN childUpdn = ( LdapDN ) updn.clone();
+            LdapDN oldUpdn = new LdapDN( getEntryUpdn( childId ) );
+
+            String rdn = oldUpdn.get( oldUpdn.size() - 1 );
+            LdapDN rdnDN = new LdapDN( rdn );
+            rdnDN.normalize( attributeTypeRegistry.getNormalizerMapping() );
+            childUpdn.add( rdnDN.getRdn() );
+
+            // Modify the child
+            ServerEntry entry = lookup( childId );
+            entry.setDn( childUpdn );
+            master.put( childId, entry );
+
+            // Recursively change the names of the children below
+            modifyDn( childId, childUpdn, isMove );
+        }
+    }
+
+
+    public void move( LdapDN oldChildDn, LdapDN newParentDn, Rdn newRdn, boolean deleteOldRdn ) throws NamingException
+    {
+        Long childId = getEntryId( oldChildDn.toString() );
+        rename( oldChildDn, newRdn, deleteOldRdn );
+        move( oldChildDn, childId, newParentDn );
+
+        if ( isSyncOnWrite )
+        {
+            sync();
+        }
+    }
+
+
+    public void move( LdapDN oldChildDn, LdapDN newParentDn ) throws NamingException
+    {
+        Long childId = getEntryId( oldChildDn.toString() );
+        move( oldChildDn, childId, newParentDn );
+
+        if ( isSyncOnWrite )
+        {
+            sync();
+        }
+    }
+
+
+    /**
+     * Moves an entry under a new parent.  The operation causes a shift in the
+     * parent child relationships between the old parent, new parent and the 
+     * child moved.  All other descendant entries under the child never change
+     * their direct parent child relationships.  Hence after the parent child
+     * relationship changes are broken at the old parent and set at the new
+     * parent a modifyDn operation is conducted to handle name changes 
+     * propagating down through the moved child and its descendants.
+     * 
+     * @param oldChildDn the normalized dn of the child to be moved
+     * @param childId the id of the child being moved
+     * @param newParentDn the normalized dn of the new parent for the child
+     * @throws NamingException if something goes wrong
+     */
+    private void move( LdapDN oldChildDn, Long childId, LdapDN newParentDn ) throws NamingException
+    {
+        // Get the child and the new parent to be entries and Ids
+        Long newParentId = getEntryId( newParentDn.toString() );
+        Long oldParentId = getParentId( childId );
+
+        /*
+         * All aliases including and below oldChildDn, will be affected by
+         * the move operation with respect to one and subtree userIndices since
+         * their relationship to ancestors above oldChildDn will be 
+         * destroyed.  For each alias below and including oldChildDn we will
+         * drop the index tuples mapping ancestor ids above oldChildDn to the
+         * respective target ids of the aliases.
+         */
+        dropMovedAliasIndices( oldChildDn );
+
+        /*
+         * Drop the old parent child relationship and add the new one
+         * Set the new parent id for the child replacing the old parent id
+         */
+        hierarchyIdx.drop( oldParentId, childId );
+        hierarchyIdx.add( newParentId, childId );
+
+        /*
+         * Build the new user provided DN (updn) for the child using the child's
+         * user provided RDN & the new parent's UPDN.  Basically add the child's
+         * UpRdn String to the tail of the new parent's Updn Name.
+         */
+        LdapDN childUpdn = new LdapDN( getEntryUpdn( childId ) );
+        String childRdn = childUpdn.get( childUpdn.size() - 1 );
+        LdapDN newUpdn = new LdapDN( getEntryUpdn( newParentId ) );
+        newUpdn.add( newUpdn.size(), childRdn );
+
+        // Call the modifyDn operation with the new updn
+        modifyDn( childId, newUpdn, true );
+    }
+
+
+    /**
+     * For all aliases including and under the moved base, this method removes
+     * one and subtree alias index tuples for old ancestors above the moved base
+     * that will no longer be ancestors after the move.
+     * 
+     * @param movedBase the base at which the move occured - the moved node
+     * @throws NamingException if system userIndices fail
+     */
+    private void dropMovedAliasIndices( final LdapDN movedBase ) throws NamingException
+    {
+        // Find all the aliases from movedBase down
+        IndexAssertion isBaseDescendant = new IndexAssertion()
+        {
+            public boolean assertCandidate( IndexRecord rec ) throws NamingException
+            {
+                String dn = getEntryDn( ( Long ) rec.getEntryId() );
+                return dn.endsWith( movedBase.toString() );
+            }
+        };
+
+        Long movedBaseId = getEntryId( movedBase.toString() );
+
+        if ( aliasIdx.reverseLookup( movedBaseId ) != null )
+        {
+            dropAliasIndices( movedBaseId, movedBase );
+        }
+
+        NamingEnumeration<IndexRecord> aliases = new IndexAssertionEnumeration( aliasIdx.listIndices( movedBase
+            .toString(), true ), isBaseDescendant );
+
+        while ( aliases.hasMore() )
+        {
+            IndexRecord entry = aliases.next();
+            dropAliasIndices( ( Long ) entry.getEntryId(), movedBase );
+        }
+    }
+
+
+    /**
+     * For the alias id all ancestor one and subtree alias tuples are moved 
+     * above the moved base.
+     * 
+     * @param aliasId the id of the alias 
+     * @param movedBase the base where the move occured
+     * @throws NamingException if userIndices fail
+     */
+    private void dropAliasIndices( Long aliasId, LdapDN movedBase ) throws NamingException
+    {
+        String targetDn = ( String ) aliasIdx.reverseLookup( aliasId );
+        Long targetId = getEntryId( targetDn );
+        String aliasDn = getEntryDn( aliasId );
+
+        /*
+         * Start droping index tuples with the first ancestor right above the 
+         * moved base.  This is the first ancestor effected by the move.
+         */
+        LdapDN ancestorDn = ( LdapDN ) movedBase.getPrefix( 1 );
+        Long ancestorId = getEntryId( ancestorDn.toString() );
+
+        /*
+         * We cannot just drop all tuples in the one level and subtree userIndices
+         * linking baseIds to the targetId.  If more than one alias refers to
+         * the target then droping all tuples with a value of targetId would
+         * make all other aliases to the target inconsistent.
+         * 
+         * We need to walk up the path of alias ancestors right above the moved 
+         * base until we reach the upSuffix, deleting each ( ancestorId,
+         * targetId ) tuple in the subtree scope alias.  We only need to do 
+         * this for the direct parent of the alias on the one level subtree if
+         * the moved base is the alias.
+         */
+        if ( aliasDn.equals( movedBase.toString() ) )
+        {
+            oneAliasIdx.drop( ancestorId, targetId );
+        }
+
+        subAliasIdx.drop( ancestorId, targetId );
+
+        while ( !ancestorDn.equals( upSuffix ) )
+        {
+            ancestorDn = ( LdapDN ) ancestorDn.getPrefix( 1 );
+            ancestorId = getEntryId( ancestorDn.toString() );
+
+            subAliasIdx.drop( ancestorId, targetId );
+        }
+    }
+
+
+    public void initRegistries( Registries registries )
+    {
+        this.attributeTypeRegistry = registries.getAttributeTypeRegistry();
+        this.oidRegistry = registries.getOidRegistry();
+    }
+}
diff --git a/old_trunk/jdbm-store/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmTable.java b/old_trunk/jdbm-store/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmTable.java
new file mode 100644
index 0000000..8047876
--- /dev/null
+++ b/old_trunk/jdbm-store/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmTable.java
@@ -0,0 +1,1661 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree.jdbm;
+
+
+import jdbm.RecordManager;
+import jdbm.btree.BTree;
+import jdbm.helper.Serializer;
+import jdbm.helper.TupleBrowser;
+import org.apache.commons.collections.iterators.ArrayIterator;
+import org.apache.directory.server.core.partition.impl.btree.KeyOnlyComparator;
+import org.apache.directory.server.core.partition.impl.btree.NoDupsEnumeration;
+import org.apache.directory.server.core.partition.impl.btree.Table;
+import org.apache.directory.server.core.partition.impl.btree.Tuple;
+import org.apache.directory.server.core.partition.impl.btree.TupleComparator;
+import org.apache.directory.server.core.partition.impl.btree.TupleEnumeration;
+import org.apache.directory.server.core.partition.impl.btree.TupleRenderer;
+import org.apache.directory.server.schema.SerializableComparator;
+import org.apache.directory.shared.ldap.exception.LdapNamingException;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.util.EmptyEnumeration;
+import org.apache.directory.shared.ldap.util.SingletonEnumeration;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+
+/**
+ * A jdbm Btree wrapper that enables duplicate sorted keys using collections.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class JdbmTable implements Table
+{
+    /**  */
+    private static final String SZSUFFIX = "_btree_sz";
+
+    /** */
+    private final String name;
+    /** */
+    private final RecordManager recMan;
+    /** */
+    private final boolean allowsDuplicates;
+    /** */
+    private final TupleComparator comparator;
+
+    /** */
+    private int count = 0;
+    /** */
+    private BTree bt;
+    /** */
+    private TupleRenderer renderer;
+
+    private int numDupLimit = JdbmIndex.DEFAULT_DUPLICATE_LIMIT;
+
+    private Map<Long, BTree> duplicateBtrees = new HashMap<Long, BTree>();
+    
+    
+    // ------------------------------------------------------------------------
+    // C O N S T R U C T O R
+    // ------------------------------------------------------------------------
+
+    /**
+     * Creates a Jdbm BTree based tuple Table abstraction that enables 
+     * duplicates.
+     *
+     * @param name the name of the table
+     * @param allowsDuplicates whether or not duplicates are enabled 
+     * @param manager the record manager to be used for this table
+     * @param comparator a tuple comparator
+     * @throws NamingException if the table's file cannot be created
+     */
+    public JdbmTable( String name, boolean allowsDuplicates, int numDupLimit, 
+        RecordManager manager, TupleComparator comparator, Serializer keySerializer, 
+        Serializer valueSerializer )
+        throws NamingException
+    {
+        this.numDupLimit = numDupLimit;
+        this.name = name;
+        this.recMan = manager;
+        this.comparator = comparator;
+        this.allowsDuplicates = allowsDuplicates;
+
+        long recId;
+
+        try
+        {
+            recId = recMan.getNamedObject( name );
+        }
+        catch ( IOException e )
+        {
+            NamingException ne = new NamingException();
+            ne.setRootCause( e );
+            throw ne;
+        }
+
+        try
+        {
+
+            //            
+            // Load existing BTree
+            //
+
+            if ( recId != 0 )
+            {
+                bt = BTree.load( recMan, recId );
+                bt.setValueSerializer( valueSerializer );
+                recId = recMan.getNamedObject( name + SZSUFFIX );
+                count = ( ( Integer ) recMan.fetch( recId ) ).intValue();
+            }
+            else
+            {
+                bt = BTree.createInstance( recMan, comparator.getKeyComparator(), keySerializer, valueSerializer );
+                recId = bt.getRecid();
+                recMan.setNamedObject( name, recId );
+                recId = recMan.insert( new Integer( 0 ) );
+                recMan.setNamedObject( name + SZSUFFIX, recId );
+            }
+        }
+        catch ( IOException e )
+        {
+            NamingException ne = new NamingException();
+            ne.setRootCause( e );
+            throw ne;
+        }
+    }
+
+
+    /**
+     * Creates a Jdbm BTree based tuple Table abstraction without duplicates 
+     * enabled using a simple key comparator.
+     *
+     * @param name the name of the table
+     * @param manager the record manager to be used for this table
+     * @param keyComparator a tuple comparator
+     * @throws NamingException if the table's file cannot be created
+     */
+    public JdbmTable( String name, RecordManager manager, SerializableComparator keyComparator, Serializer keySerializer, Serializer valueSerializer ) 
+        throws NamingException
+    {
+        this( name, false, Integer.MAX_VALUE, manager, new KeyOnlyComparator( keyComparator ), keySerializer, valueSerializer );
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Simple Table Properties
+    // ------------------------------------------------------------------------
+
+    /**
+     * @see org.apache.directory.server.core.partition.impl.btree.Table#getComparator()
+     */
+    public TupleComparator getComparator()
+    {
+        return comparator;
+    }
+
+
+    /**
+     * @see org.apache.directory.server.core.partition.impl.btree.Table#isDupsEnabled()
+     */
+    public boolean isDupsEnabled()
+    {
+        return allowsDuplicates;
+    }
+
+
+    /**
+     * @see org.apache.directory.server.core.partition.impl.btree.Table#getName()
+     */
+    public String getName()
+    {
+        return name;
+    }
+
+
+    /**
+     * @see org.apache.directory.server.core.partition.impl.btree.Table#getRenderer()
+     */
+    public TupleRenderer getRenderer()
+    {
+        return renderer;
+    }
+
+
+    /**
+     * @see Table#setRenderer(
+     * TupleRenderer)
+     */
+    public void setRenderer( TupleRenderer renderer )
+    {
+        this.renderer = renderer;
+    }
+
+
+    /**
+     * @see Table#isSortedDupsEnabled()
+     */
+    public boolean isSortedDupsEnabled()
+    {
+        // If duplicates are enabled than duplicates will be maintained in
+        // sorted order.
+        return allowsDuplicates;
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Count Overloads
+    // ------------------------------------------------------------------------
+
+    /**
+     * @see Table#count(java.lang.Object, boolean)
+     */
+    public int count( Object key, boolean isGreaterThan ) throws NamingException
+    {
+        // take a best guess
+        return count;
+    }
+
+
+    /**
+     * @see Table#count(java.lang.Object)
+     */
+    public int count( Object key ) throws NamingException
+    {
+        if ( !allowsDuplicates )
+        {
+            if ( null == getRaw( key ) )
+            {
+                return 0;
+            }
+            else
+            {
+                return 1;
+            }
+        }
+
+        Object values = getRaw( key );
+        
+        if ( values == null )
+        {
+            return 0;
+        }
+        
+        // -------------------------------------------------------------------
+        // Handle the use of a TreeSet for storing duplicates
+        // -------------------------------------------------------------------
+
+        if ( values instanceof TreeSet )
+        {
+            return ( ( TreeSet ) values ).size();
+        }
+
+        // -------------------------------------------------------------------
+        // Handle the use of a BTree for storing duplicates
+        // -------------------------------------------------------------------
+
+        if ( values instanceof BTreeRedirect )
+        {
+            return getBTree( ( BTreeRedirect ) values ).size();
+        }
+        
+        throw new IllegalStateException( "When using duplicate keys either a TreeSet or BTree is used for values." );
+    }
+
+
+    /**
+     * @see org.apache.directory.server.core.partition.impl.btree.Table#count()
+     */
+    public int count() throws NamingException
+    {
+        return count;
+    }
+
+    
+    // ------------------------------------------------------------------------
+    // get/has/put/remove Methods and Overloads
+    // ------------------------------------------------------------------------
+
+    /**
+     * @see Table#get(java.lang.Object)
+     */
+    public Object get( Object key ) throws NamingException
+    {
+        if ( ! allowsDuplicates )
+        {
+            return getRaw( key );
+        }
+
+        Object values = getRaw( key );
+        
+        if ( values == null )
+        {
+            return null;
+        }
+        
+        if ( values instanceof TreeSet )
+        {
+            TreeSet set = ( TreeSet ) values;
+            
+            if ( set.size() == 0 )
+            {
+                return null;
+            }
+            else
+            {
+                return set.first();
+            }
+        }
+
+        if ( values instanceof BTreeRedirect )
+        {
+            BTree tree = getBTree( ( BTreeRedirect ) values );
+            
+            if ( tree.size() == 0 )
+            {
+                return null;
+            }
+            else
+            {
+                return firstKey( tree );
+            }
+        }
+        
+        throw new IllegalStateException( "When using duplicate keys either a TreeSet or BTree is used for values." );
+    }
+
+    
+    /**
+     * @see Table#has(java.lang.Object,
+     * java.lang.Object, boolean)
+     */
+    @SuppressWarnings("unchecked")
+    public boolean has( Object key, Object val, boolean isGreaterThan ) throws NamingException
+    {
+        if ( !allowsDuplicates )
+        {
+            Object rval = getRaw( key );
+
+            // key does not exist so return nothing
+            if ( null == rval )
+            {
+                return false;
+            }
+            // val == val return true
+            else if ( val.equals( rval ) )
+            {
+                return true;
+            }
+            // val >= val and test is for greater then return true
+            else if ( comparator.compareValue( rval, val ) >= 1 && isGreaterThan )
+            {
+                return true;
+            }
+            // val <= val and test is for lesser then return true
+            else if ( comparator.compareValue( rval, val ) <= 1 && !isGreaterThan )
+            {
+                return true;
+            }
+
+            return false;
+        }
+
+        Object values = getRaw( key );
+        
+        if ( values == null )
+        {
+            return false;
+        }
+        
+        if ( values instanceof TreeSet )
+        {
+            TreeSet set = ( TreeSet ) values;
+            SortedSet subset = null;
+    
+            if ( set.size() == 0 )
+            {
+                return false;
+            }
+    
+            if ( isGreaterThan )
+            {
+                subset = set.tailSet( val );
+            }
+            else
+            {
+                subset = set.headSet( val );
+            }
+    
+            if ( subset.size() > 0 || set.contains( val ) )
+            {
+                return true;
+            }
+    
+            return false;
+        }
+        
+        if ( values instanceof BTreeRedirect )
+        {
+            BTree tree = getBTree( ( BTreeRedirect ) values );
+            if ( tree.size() == 0 )
+            {
+                return false;
+            }
+
+            return btreeHas( tree, val, isGreaterThan );
+        }
+        
+        throw new IllegalStateException( "When using duplicate keys either a TreeSet or BTree is used for values." );
+    }
+    
+
+    /**
+     * @see Table#has(java.lang.Object, boolean)
+     */
+    public boolean has( Object key, boolean isGreaterThan ) throws NamingException
+    {
+        try
+        {
+            // See if we can find the border between keys greater than and less
+            // than in the set of keys.  This will be the spot we search from.
+            jdbm.helper.Tuple tuple = bt.findGreaterOrEqual( key );
+
+            // Test for equality first since it satisfies both greater/less than
+            if ( null != tuple && comparator.compareKey( tuple.getKey(), key ) == 0 )
+            {
+                return true;
+            }
+
+            // Greater searches are easy and quick thanks to findGreaterOrEqual
+            if ( isGreaterThan )
+            {
+                // A null return above means there were no equal or greater keys
+                if ( null == tuple )
+                {
+                    return false;
+                }
+
+                // Not Null! - we found a tuple with equal or greater key value
+                return true;
+            }
+
+            // Less than searches occur below and are not as efficient or easy.
+            // We need to scan up from the begining if findGreaterOrEqual failed
+            // or scan down if findGreaterOrEqual succeed.
+            TupleBrowser browser = null;
+            if ( null == tuple )
+            {
+                // findGreaterOrEqual failed so we create a tuple and scan from
+                // the lowest values up via getNext comparing each key to key
+                tuple = new jdbm.helper.Tuple();
+                browser = bt.browse();
+
+                // We should at most have to read one key.  If 1st key is not
+                // less than or equal to key then all keys are > key
+                // since the keys are assorted in ascending order based on the
+                // comparator.
+                while ( browser.getNext( tuple ) )
+                {
+                    if ( comparator.compareKey( tuple.getKey(), key ) <= 0 )
+                    {
+                        return true;
+                    }
+
+                    return false;
+                }
+            }
+            else
+            {
+                // findGreaterOrEqual succeeded so use the existing tuple and
+                // scan the down from the highest key less than key via
+                // getPrevious while comparing each key to key.
+                browser = bt.browse( tuple.getKey() );
+
+                // The above call positions the browser just before the given
+                // key so we need to step forward once then back.  Remember this
+                // key represents a key greater than or equal to key.
+                if ( comparator.compareKey( tuple.getKey(), key ) <= 0 )
+                {
+                    return true;
+                }
+
+                browser.getNext( tuple );
+
+                // We should at most have to read one key, but we don't short
+                // the search as in the search above first because the chance of
+                // unneccessarily looping is nil since values get smaller.
+                while ( browser.getPrevious( tuple ) )
+                {
+                    if ( comparator.compareKey( tuple.getKey(), key ) <= 0 )
+                    {
+                        return true;
+                    }
+                }
+            }
+        }
+        catch ( IOException e )
+        {
+            NamingException ne = new NamingException();
+            ne.setRootCause( e );
+            throw ne;
+        }
+
+        return false;
+    }
+
+
+    /**
+     * @see org.apache.directory.server.core.partition.impl.btree.Table#has(java.lang.Object,
+     * java.lang.Object)
+     */
+    public boolean has( Object key, Object value ) throws NamingException
+    {
+        if ( ! allowsDuplicates )
+        {
+            Object obj = getRaw( key );
+
+            if ( null == obj )
+            {
+                return false;
+            }
+
+            return obj.equals( value );
+        }
+        
+        Object values = getRaw( key );
+        
+        if ( values == null )
+        {
+            return false;
+        }
+        
+        if ( values instanceof TreeSet )
+        {
+            return ( ( TreeSet ) values ).contains( value );
+        }
+        
+        if ( values instanceof BTreeRedirect )
+        {
+            return btreeHas( getBTree( ( BTreeRedirect ) values ), value );
+        }
+        
+        throw new IllegalStateException( "When using duplicate keys either a TreeSet or BTree is used for values." );
+    }
+    
+
+    /**
+     * @see Table#has(java.lang.Object)
+     */
+    public boolean has( Object key ) throws NamingException
+    {
+        return getRaw( key ) != null;
+    }
+
+
+    /**
+     * @see org.apache.directory.server.core.partition.impl.btree.Table#put(java.lang.Object,
+     * java.lang.Object)
+     */
+    @SuppressWarnings("unchecked")
+    public Object put( Object key, Object value ) throws NamingException
+    {
+        Object replaced = null;
+
+        if ( ! allowsDuplicates )
+        {
+            replaced = putRaw( key, value, true );
+
+            if ( null == replaced )
+            {
+                count++;
+            }
+
+            return replaced;
+        }
+        
+        Object values = getRaw( key );
+        
+        if ( values == null )
+        {
+            values = new TreeSet( comparator.getValueComparator() );
+        }
+        
+        if ( values instanceof TreeSet )
+        {
+            TreeSet set = ( TreeSet ) values;
+            
+            if ( set.contains( value ) )
+            {
+                return value;
+            }
+            
+            boolean addSuccessful = set.add( value );
+            
+            if ( set.size() > numDupLimit )
+            {
+                BTree tree = convertToBTree( set );
+                BTreeRedirect redirect = new BTreeRedirect( tree.getRecid() );
+                replaced = putRaw( key, redirect, true );
+            }
+            else
+            {
+                replaced = putRaw( key, set, true );
+            }
+            
+            if ( addSuccessful )
+            {
+                count++;
+                return replaced;
+            }
+            return null;
+        }
+        
+        if ( values instanceof BTreeRedirect )
+        {
+            BTree tree = getBTree( ( BTreeRedirect ) values );
+            if ( insertDupIntoBTree( tree, value ) )
+            {
+                count++;
+                return values;
+            }
+            return null;
+        }
+        
+        throw new IllegalStateException( "When using duplicate keys either a TreeSet or BTree is used for values." );
+    }
+    
+
+    /**
+     * @see Table#put(java.lang.Object,
+     * javax.naming.NamingEnumeration)
+     */
+    @SuppressWarnings("unchecked")
+    public Object put( Object key, NamingEnumeration values ) throws NamingException
+    {
+        /*
+         * If we do not allow duplicates call the single add put using the
+         * first value in the enumeration if it exists.  If it does not we
+         * just return null without doing anything.  If more than one value
+         * is in the enumeration than we blow a UnsupportedOperationException.
+         */
+        if ( !allowsDuplicates )
+        {
+            if ( values.hasMore() )
+            {
+                Object value = values.next();
+
+                if ( values.hasMore() )
+                {
+                    throw new UnsupportedOperationException( "Attempting to put duplicate keys into table " + name
+                        + " which does not support duplicates" );
+                }
+
+                return put( key, value );
+            }
+
+            return null;
+        }
+
+        Object storedValues = getRaw( key );
+        
+        if ( storedValues == null )
+        {
+            storedValues = new TreeSet( comparator.getValueComparator() );
+        }
+        
+        if ( storedValues instanceof TreeSet )
+        {
+            /*
+             * Here the table allows duplicates so we get the TreeSet from the 
+             * Table holding all the duplicate key values or create one if it
+             * does not exist for key.  We check if the value is present and
+             * if it is we add it and increment the table entry counter.
+             */
+            TreeSet set = ( TreeSet ) storedValues;
+            while ( values.hasMore() )
+            {
+                Object val = values.next();
+    
+                if ( !set.contains( val ) )
+                {
+                    boolean isAddSuccessful = set.add( val );
+                    if ( isAddSuccessful )
+                    {
+                        count++;
+                    }
+                }
+            }
+    
+            if ( set.size() > numDupLimit )
+            {
+                BTree tree = convertToBTree( set );
+                BTreeRedirect redirect = new BTreeRedirect( tree.getRecid() );
+                return putRaw( key, redirect, true );
+            }
+            else
+            {
+                return putRaw( key, set, true );
+            }
+        }
+        
+        if ( storedValues instanceof BTreeRedirect )
+        {
+            BTree tree = getBTree( ( BTreeRedirect ) storedValues );
+            while ( values.hasMore() )
+            {
+                Object val = values.next();
+                
+                if ( insertDupIntoBTree( tree, val ) )
+                {
+                    count++;
+                }
+            }
+
+            return storedValues;
+        }
+        
+        throw new IllegalStateException( "When using duplicate keys either a TreeSet or BTree is used for values." );
+    }
+
+
+    /**
+     * @see Table#remove(java.lang.Object,
+     * java.lang.Object)
+     */
+    public Object remove( Object key, Object value ) throws NamingException
+    {
+        if ( ! allowsDuplicates )
+        {
+            Object oldValue = getRaw( key );
+        
+            // Remove the value only if it is the same as value.
+            if ( oldValue != null && oldValue.equals( value ) )
+            {
+                removeRaw( key );
+                count--;
+                return oldValue;
+            }
+
+            return null;
+        }
+
+        Object values = getRaw( key );
+        
+        if ( values == null )
+        {
+            return null;
+        }
+        
+        if ( values instanceof TreeSet )
+        {
+            TreeSet set = ( TreeSet ) values;
+
+            // If removal succeeds then remove if set is empty else replace it
+            if ( set.remove( value ) )
+            {
+                if ( set.isEmpty() )
+                {
+                    removeRaw( key );
+                }
+                else
+                {
+                    putRaw( key, set, true );
+                }
+
+                // Decrement counter if removal occurs.
+                count--;
+                return value;
+            }
+
+            return null;
+        }
+
+        // TODO might be nice to add code here that reverts to a TreeSet
+        // if the number of duplicates falls below the numDupLimit value
+        if ( values instanceof BTreeRedirect )
+        {
+            BTree tree = getBTree( ( BTreeRedirect ) values );
+            
+            if ( removeDupFromBTree( tree, value ) )
+            {
+                if ( tree.size() == 0 )
+                {
+                    removeRaw( key );
+                }
+                
+                count--;
+                return value;
+            }
+            
+            return null;
+        }
+        
+        throw new IllegalStateException( "When using duplicate keys either a TreeSet or BTree is used for values." );
+    }
+
+
+    /**
+     * @see Table#remove(java.lang.Object,
+     * javax.naming.NamingEnumeration)
+     */
+    public Object remove( Object key, NamingEnumeration values ) throws NamingException
+    {
+        /*
+         * If we do not allow dupliicates call the single remove using the
+         * first value in the enumeration if it exists.  If it does not we
+         * just return null without doing anything.  If more than one value
+         * is in the enumeration than we blow a UnsupportedOperationException.
+         */
+        if ( !allowsDuplicates )
+        {
+            if ( values.hasMore() )
+            {
+                Object value = values.next();
+
+                if ( values.hasMore() )
+                {
+                    throw new UnsupportedOperationException( "Attempting to remove duplicate keys from table " + name
+                        + " which does not support duplicates" );
+                }
+
+                return remove( key, value );
+            }
+
+            return null;
+        }
+
+        Object storedValues = getRaw( key );
+        
+        if ( storedValues == null )
+        {
+            return null;
+        }
+        
+        if ( storedValues instanceof TreeSet )
+        {
+            /*
+             * Here the table allows duplicates so we get the TreeSet from the 
+             * Table holding all the duplicate key values or return null if it
+             * does not exist for key - nothing to do here.
+             */
+            TreeSet set = ( TreeSet ) storedValues;
+    
+            /*
+             * So we have a valid TreeSet with values in it.  We check if each value
+             * is in the set and remove it if it is present.  We decrement the 
+             * counter while doing so.
+             */
+            Object firstValue = null;
+            while ( values.hasMore() )
+            {
+                Object val = values.next();
+    
+                // get the first value
+                if ( firstValue == null )
+                {
+                    firstValue = val;
+                }
+
+                if ( set.contains( val ) )
+                {
+                    set.remove( val );
+                    count--;
+                }
+            }
+    
+            // Return the raw TreeSet and put the changed one back.
+            putRaw( key, set, true );
+            return firstValue;
+        }
+        
+        // TODO might be nice to add code here that reverts to a TreeSet
+        // if the number of duplicates falls below the numDupLimit value
+        if ( storedValues instanceof BTreeRedirect )
+        {
+            BTree tree = getBTree( ( BTreeRedirect ) storedValues );
+            Object first = null;
+            while ( values.hasMore() )
+            {
+                Object val = values.next();
+                
+                if ( removeDupFromBTree( tree, val ) )
+                {
+                    count--;
+                    
+                    if ( first == null )
+                    {
+                        first = val;
+                    }
+                }
+            }
+            
+            return first;
+        }
+        
+        throw new IllegalStateException( "When using duplicate keys either a TreeSet or BTree is used for values." );
+    }
+
+
+    /**
+     * @see Table#remove(java.lang.Object)
+     */
+    public Object remove( Object key ) throws NamingException
+    {
+        Object returned = removeRaw( key );
+
+        if ( null == returned )
+        {
+            return null;
+        }
+
+        if ( ! allowsDuplicates )
+        {
+            this.count--;
+            return returned;
+        }
+
+        if ( returned instanceof TreeSet )
+        {
+            TreeSet set = ( TreeSet ) returned;
+            this.count -= set.size();
+            return set.first();
+        }
+        
+        if ( returned instanceof BTreeRedirect )
+        {
+            BTree tree = getBTree( ( BTreeRedirect ) returned );
+            this.count -= tree.size();
+            return removeAll( tree );
+        }
+        
+        throw new IllegalStateException( "When using duplicate keys either a TreeSet or BTree is used for values." );
+    }
+
+
+    /**
+     * @see Table#listValues(java.lang.Object)
+     */
+    public NamingEnumeration<Object> listValues( Object key ) throws NamingException
+    {
+        if ( !allowsDuplicates )
+        {
+            Object value = get( key );
+
+            if ( null == value )
+            {
+                return new EmptyEnumeration();
+            }
+            else
+            {
+                return new SingletonEnumeration( value );
+            }
+        }
+
+        Object values = getRaw( key );
+        
+        if ( values == null )
+        {
+            return new EmptyEnumeration();
+        }
+        
+        if ( values instanceof TreeSet )
+        {
+            TreeSet set = ( TreeSet ) values;
+            final Iterator list = set.iterator();
+            return new NamingEnumeration()
+            {
+                public void close()
+                {
+                }
+    
+    
+                public Object nextElement()
+                {
+                    return list.next();
+                }
+    
+    
+                public Object next()
+                {
+                    return list.next();
+                }
+    
+    
+                public boolean hasMore()
+                {
+                    return list.hasNext();
+                }
+    
+    
+                public boolean hasMoreElements()
+                {
+                    return list.hasNext();
+                }
+            };
+        }
+        
+        if ( values instanceof BTreeRedirect )
+        {
+            BTree tree = getBTree( ( BTreeRedirect ) values );
+            return new BTreeEnumeration( tree );
+        }
+        
+        throw new IllegalStateException( "When using duplicate keys either a TreeSet or BTree is used for values." );
+    }
+
+
+    // ------------------------------------------------------------------------
+    // listTuple Overloads 
+    // ------------------------------------------------------------------------
+
+    /**
+     * @see org.apache.directory.server.core.partition.impl.btree.Table#listTuples()
+     */
+    public NamingEnumeration<Tuple> listTuples() throws NamingException
+    {
+        NamingEnumeration<Tuple> list = null;
+
+        try
+        {
+            JdbmTupleBrowser browser = new JdbmTupleBrowser( bt.browse() );
+            list = new NoDupsEnumeration( browser, true );
+        }
+        catch ( IOException e )
+        {
+            NamingException ne = new NamingException();
+            ne.setRootCause( e );
+            throw ne;
+        }
+
+        if ( allowsDuplicates )
+        {
+            return new DupsEnumeration( this, ( NoDupsEnumeration ) list );
+        }
+
+        return list;
+    }
+
+
+    /**
+     * @see org.apache.directory.server.core.partition.impl.btree.Table#listTuples(java.lang.Object)
+     */
+    @SuppressWarnings("unchecked")
+    public NamingEnumeration<Tuple> listTuples( Object key ) throws NamingException
+    {
+        // Handle single and zero value returns without duplicates enabled
+        if ( !allowsDuplicates )
+        {
+            Object val = getRaw( key );
+
+            if ( null == val )
+            {
+                return new EmptyEnumeration();
+            }
+            else
+            {
+                return new SingletonEnumeration( new Tuple( key, getRaw( key ) ) );
+            }
+        }
+
+        Object values = getRaw( key );
+
+        if ( values == null )
+        {
+            return new EmptyEnumeration();
+        }
+        
+        if ( values instanceof TreeSet )
+        {
+            TreeSet set = ( TreeSet ) values;
+            Object[] objs = new Object[set.size()];
+            objs = set.toArray( objs );
+            ArrayIterator iterator = new ArrayIterator( objs );
+            return new TupleEnumeration( key, iterator );
+        }
+        
+        if ( values instanceof BTreeRedirect )
+        {
+            return new BTreeTupleEnumeration( getBTree( ( BTreeRedirect ) values ), key );
+        }
+
+        throw new IllegalStateException( "When using duplicate keys either a TreeSet or BTree is used for values." );
+    }
+
+
+    /**
+     * @see Table#listTuples(java.lang.Object,
+     * boolean)
+     */
+    public NamingEnumeration<Tuple> listTuples( Object key, boolean isGreaterThan ) throws NamingException
+    {
+        NamingEnumeration<Tuple> list = null;
+
+        try
+        {
+            if ( isGreaterThan )
+            {
+                JdbmTupleBrowser browser = new JdbmTupleBrowser( bt.browse( key ) );
+                list = new NoDupsEnumeration( browser, isGreaterThan );
+            }
+            else
+            {
+                /* According to the jdbm docs a browser is positioned right
+                 * before a key greater than or equal to key.  getNext() will
+                 * return the next tuple with a key greater than or equal to
+                 * key.  getPrevious() used in descending scans for less than
+                 * for equal to comparisions will not.  We need to advance
+                 * forward once and check if the returned Tuple key equals
+                 * key.  If it does then we do nothing feeding in the browser
+                 * to the NoDupsCursor.  If it does not we call getPrevious and
+                 * pass it into the NoDupsCursor constructor.
+                 */
+                jdbm.helper.Tuple tuple = new jdbm.helper.Tuple();
+                TupleBrowser browser = bt.browse( key );
+
+                if ( browser.getNext( tuple ) )
+                {
+                    Object greaterKey = tuple.getKey();
+
+                    if ( 0 != comparator.compareKey( key, greaterKey ) )
+                    {
+                        // Make sure we don't return greaterKey in cursor
+                        browser.getPrevious( tuple );
+                    }
+                }
+
+                // If greaterKey != key above then it will not be returned.
+                list = new NoDupsEnumeration( new JdbmTupleBrowser( browser ), isGreaterThan );
+            }
+        }
+        catch ( IOException e )
+        {
+            NamingException ne = new NamingException( "Failed to get TupleBrowser on table " + name + " using key "
+                + renderKey( key ) );
+            ne.setRootCause( e );
+            throw ne;
+        }
+
+        if ( allowsDuplicates )
+        {
+            list = new DupsEnumeration( this, ( NoDupsEnumeration ) list );
+        }
+
+        return list;
+    }
+
+
+    /**
+     * @see org.apache.directory.server.core.partition.impl.btree.Table#listTuples(java.lang.Object,
+     * java.lang.Object, boolean)
+     */
+    @SuppressWarnings("unchecked")
+    public NamingEnumeration listTuples( Object key, Object val, boolean isGreaterThan ) throws NamingException
+    {
+        if ( !allowsDuplicates )
+        {
+            throw new UnsupportedOperationException( "Cannot list tuples over duplicates on table that " +
+                    "does not support duplicates." );
+//            Object rval = getRaw( key );
+//
+//            if ( null == rval ) // key does not exist so return nothing
+//            {
+//                return new EmptyEnumeration();
+//            }
+//            else if ( val.equals( rval ) ) // val == rval return tuple
+//            {
+//                return new SingletonEnumeration( new Tuple( key, val ) );
+//            }
+//            // val >= val and test is for greater then return tuple
+//            else if ( comparator.compareValue( val, rval ) >= 1 && isGreaterThan )
+//            {
+//                return new SingletonEnumeration( new Tuple( key, val ) );
+//            }
+//            // val <= val and test is for lesser then return tuple
+//            else if ( comparator.compareValue( val, rval ) <= 1 && !isGreaterThan )
+//            {
+//                return new SingletonEnumeration( new Tuple( key, val ) );
+//            }
+//
+//            return new EmptyEnumeration();
+        }
+
+        Object values = getRaw( key );
+        
+        if ( values == null )
+        {
+            return new EmptyEnumeration();
+        }
+
+        if ( values instanceof TreeSet )
+        {
+            TreeSet set = ( TreeSet ) values;
+    
+            if ( isGreaterThan )
+            {
+                Set tailSet = set.tailSet( val );
+                
+                if ( tailSet.isEmpty() )
+                {
+                    return new EmptyEnumeration();
+                }
+                
+                Object[] objs = new Object[tailSet.size()];
+                objs = tailSet.toArray( objs );
+                ArrayIterator iterator = new ArrayIterator( objs );
+                return new TupleEnumeration( key, iterator );
+            }
+            else
+            {
+                // Get all values from the smallest upto val and put them into
+                // a list.  They will be in ascending order so we need to reverse
+                // the list after adding val which is not included in headSet.
+                SortedSet headset = set.headSet( val );
+                List list = new ArrayList( headset.size() + 1 );
+                list.addAll( headset );
+    
+                // Add largest value (val) if it is in the set.  TreeSet.headSet
+                // does not get val if val is in the set.  So we add it now to
+                // the end of the list.  List is now ascending from smallest to
+                // val
+                if ( set.contains( val ) )
+                {
+                    list.add( val );
+                }
+    
+                // Reverse the list now we have descending values from val to the
+                // smallest value that key has.  Return tuple cursor over list.
+                Collections.reverse( list );
+                return new TupleEnumeration( key, list.iterator() );
+            }
+        }
+        
+        if ( values instanceof BTreeRedirect )
+        {
+            return new BTreeTupleEnumeration( getBTree( ( BTreeRedirect ) values ), 
+                comparator.getValueComparator(), key, val, isGreaterThan );
+        }
+
+        throw new IllegalStateException( "When using duplicate keys either a TreeSet or BTree is used for values." );
+    }
+    
+
+    // ------------------------------------------------------------------------
+    // Maintenance Operations 
+    // ------------------------------------------------------------------------
+
+    /**
+     * @see Table#close()
+     */
+    public synchronized void close() throws NamingException
+    {
+        sync();
+    }
+
+
+    /**
+     * Synchronizes the buffers with disk.
+     *
+     * @throws NamingException if errors are encountered on the flush
+     */
+    public void sync() throws NamingException
+    {
+        try
+        {
+            long recId = recMan.getNamedObject( name + SZSUFFIX );
+
+            if ( 0 == recId )
+            {
+                recId = recMan.insert( new Integer( count ) );
+            }
+            else
+            {
+                recMan.update( recId, new Integer( count ) );
+            }
+        }
+        catch ( IOException e )
+        {
+            NamingException ne = new NamingException();
+            ne.setRootCause( e );
+            throw ne;
+        }
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Private Utility Methods 
+    // ------------------------------------------------------------------------
+
+    /**
+     * Renders a key using the renderer associated with this table.
+     *
+     * @param obj the key to render.
+     * @return the rendered String representation of obj
+     */
+    private String renderKey( Object obj )
+    {
+        StringBuffer buf = new StringBuffer();
+
+        buf.append( "\'" );
+        if ( null == renderer )
+        {
+            buf.append( obj.toString() );
+        }
+        else
+        {
+            buf.append( renderer.getKeyString( obj ) );
+        }
+
+        buf.append( "\'" );
+        return buf.toString();
+    }
+
+
+    /**
+     * Gets a Tuple value from the btree while wrapping any IOExceptions with a 
+     * NamingException.
+     *
+     * @param key the key of the Tuple to get the value of 
+     * @return the raw value object from the btree
+     * @throws NamingException if there are any problems accessing the btree.
+     */
+    private Object getRaw( Object key ) throws NamingException
+    {
+        Object val = null;
+
+        if ( null == key )
+        {
+            return null;
+        }
+
+        try
+        {
+            if ( !allowsDuplicates )
+            {
+                val = bt.find( key );
+            }
+            else
+            {
+                val = bt.find( key );
+            }
+        }
+        catch ( IOException e )
+        {
+            NamingException ne = new NamingException();
+            ne.setRootCause( e );
+            throw ne;
+        }
+
+        return val;
+    }
+
+
+    /**
+     * Puts a Tuple into the btree while wrapping any IOExceptions with a 
+     * NamingException.
+     *
+     * @param key the key of the Tuple to put
+     * @param value the value of the Tuple to put
+     * @param doReplace whether or not to replace the object if it exists
+     * @return the raw value object removed from the btree on replacement
+     * @throws NamingException if there are any problems accessing the btree.
+     */
+    private Object putRaw( Object key, Object value, boolean doReplace ) throws NamingException
+    {
+        Object val = null;
+
+        try
+        {
+            val = bt.insert( key, value, doReplace );
+        }
+        catch ( IOException e )
+        {
+            NamingException ne = new NamingException();
+            ne.setRootCause( e );
+            throw ne;
+        }
+
+        return val;
+    }
+
+
+    /**
+     * Removes a entry from the btree while wrapping any IOExceptions with a 
+     * NamingException.
+     *
+     * @param key the key of the Tuple to remove
+     * @return the raw value object removed from the btree
+     * @throws NamingException if there are any problems accessing the btree.
+     */
+    private Object removeRaw( Object key ) throws NamingException
+    {
+        Object val = null;
+
+        try
+        {
+            val = bt.remove( key );
+        }
+        catch ( IOException e )
+        {
+            NamingException ne = new NamingException();
+            ne.setRootCause( e );
+            throw ne;
+        }
+
+        return val;
+    }
+
+
+    BTree getBTree( BTreeRedirect redirect ) throws NamingException
+    {
+        if ( duplicateBtrees.containsKey( redirect.getRecId() ) )
+        {
+            return duplicateBtrees.get( redirect.getRecId() );
+        }
+        
+        try
+        {
+            BTree tree = BTree.load( recMan, redirect.getRecId().longValue() );
+            duplicateBtrees.put( redirect.getRecId(), tree );
+            return tree;
+        }
+        catch ( IOException e )
+        {
+            LdapNamingException lne = new LdapNamingException( "Failed to load btree", 
+                ResultCodeEnum.OTHER );
+            lne.setRootCause( e );
+            throw lne;
+        }
+    }
+
+
+    private Object firstKey ( BTree tree ) throws NamingException
+    {
+        jdbm.helper.Tuple tuple = new jdbm.helper.Tuple();
+        boolean success = false;
+        
+        try
+        {
+            success = tree.browse().getNext( tuple );
+            
+            if ( success )
+            {
+                return tuple.getKey();
+            }
+            else 
+            {
+                return null;
+            }
+        }
+        catch ( IOException e )
+        {
+            LdapNamingException lne = new LdapNamingException( "IO failure while acessing btree: "
+                + e.getMessage(), ResultCodeEnum.OTHER );
+            lne.setRootCause( e );
+            throw lne;
+        }
+    }
+
+    
+    private boolean btreeHas( BTree tree, Object key, boolean isGreaterThan ) throws NamingException
+    {
+        jdbm.helper.Tuple tuple = new jdbm.helper.Tuple();
+        
+        try
+        {
+            TupleBrowser browser = tree.browse( key );
+            if ( isGreaterThan )
+            {
+                boolean success = browser.getNext( tuple );
+                if ( success )
+                {
+                    return true;
+                }
+                else
+                {
+                    return false;
+                }
+            }
+            else
+            {
+                boolean success = browser.getPrevious( tuple );
+                if ( success )
+                {
+                    return true;
+                }
+                else
+                {
+                    /*
+                     * Calls to getPrevious() will return a lower key even
+                     * if there exists a key equal to the one searched
+                     * for.  Since isGreaterThan when false really means
+                     * 'less than or equal to' we must check to see if 
+                     * the key in front is equal to the key argument provided.
+                     */
+                    success = browser.getNext( tuple );
+                    if ( success )
+                    {
+                        Object biggerKey = tuple.getKey();
+                        if ( comparator.compareValue( key, biggerKey ) == 0 )
+                        {
+                            return true;
+                        }
+                    }
+                    return false;
+                }
+            }
+        }
+        catch ( IOException e )
+        {
+            LdapNamingException lne = new LdapNamingException( "IO failure while acessing btree: "
+                + e.getMessage(), ResultCodeEnum.OTHER );
+            lne.setRootCause( e );
+            throw lne;
+        }
+    }
+
+
+    private boolean btreeHas( BTree tree, Object key ) throws NamingException
+    {
+        jdbm.helper.Tuple tuple = new jdbm.helper.Tuple();
+        
+        try
+        {
+            TupleBrowser browser = tree.browse( key );
+            boolean success = browser.getNext( tuple );
+            if ( success )
+            {
+                if ( comparator.compareValue( key, tuple.getKey() ) == 0 )
+                {
+                    return true;
+                }
+            }
+
+            return false;
+        }
+        catch ( IOException e )
+        {
+            LdapNamingException lne = new LdapNamingException( "IO failure while acessing btree: "
+                + e.getMessage(), ResultCodeEnum.OTHER );
+            lne.setRootCause( e );
+            throw lne;
+        }
+    }
+
+    
+    private boolean insertDupIntoBTree( BTree tree, Object value ) throws LdapNamingException
+    {
+        try
+        {
+            Object replaced = tree.insert( value, EMPTY_BYTES, true );
+            return null == replaced;
+        }
+        catch ( IOException e )
+        {
+            LdapNamingException lne = new LdapNamingException( "Failed to insert dup into BTree", 
+                ResultCodeEnum.OTHER );
+            lne.setRootCause( e );
+            throw lne;
+        }
+    }
+    
+
+    private boolean removeDupFromBTree( BTree tree, Object value ) throws LdapNamingException
+    {
+        try
+        {
+            Object removed = null;
+            if ( tree.find( value ) != null )
+            {
+                removed = tree.remove( value );
+            }
+            return null != removed;
+        }
+        catch ( IOException e )
+        {
+            LdapNamingException lne = new LdapNamingException( "Failed to remove dup from BTree", 
+                ResultCodeEnum.OTHER );
+            lne.setRootCause( e );
+            throw lne;
+        }
+    }
+    
+
+    private static final byte[] EMPTY_BYTES = new byte[0];
+    private BTree convertToBTree( TreeSet set ) throws NamingException
+    {
+        try
+        {
+            BTree tree = BTree.createInstance( recMan, comparator.getValueComparator() );
+            for ( Iterator ii = set.iterator(); ii.hasNext(); /**/ )
+            {
+                tree.insert( ii.next(), EMPTY_BYTES, true );
+            }
+            return tree;
+        }
+        catch ( IOException e )
+        {
+            LdapNamingException lne = new LdapNamingException( "Failed to convert TreeSet values to BTree", 
+                ResultCodeEnum.OTHER );
+            lne.setRootCause( e );
+            throw lne;
+        }
+    }
+    
+    
+    private Object removeAll( BTree tree ) throws NamingException
+    {
+        Object first = null;
+        jdbm.helper.Tuple jdbmTuple = new jdbm.helper.Tuple();
+        TupleBrowser browser;
+        try
+        {
+            browser = tree.browse();
+            while( browser.getNext( jdbmTuple ) )
+            {
+                tree.remove( jdbmTuple.getKey() );
+                if ( first == null )
+                {
+                    first = jdbmTuple.getKey();
+                }
+            }
+        }
+        catch ( IOException e )
+        {
+            LdapNamingException lne = new LdapNamingException( "Failed to remove all keys in BTree",
+                ResultCodeEnum.OTHER );
+            lne.setRootCause( e );
+            throw lne;
+        }
+        
+        return first;
+    }
+}
+
diff --git a/old_trunk/jdbm-store/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmTupleBrowser.java b/old_trunk/jdbm-store/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmTupleBrowser.java
new file mode 100644
index 0000000..f9e3f91
--- /dev/null
+++ b/old_trunk/jdbm-store/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmTupleBrowser.java
@@ -0,0 +1,116 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.core.partition.impl.btree.jdbm;
+
+
+import java.io.IOException;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.core.partition.impl.btree.Tuple;
+import org.apache.directory.server.core.partition.impl.btree.TupleBrowser;
+
+
+/**
+ * TupleBrowser wrapper for Jdbm based TupleBrowsers.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class JdbmTupleBrowser implements TupleBrowser
+{
+    /** underlying wrapped jdbm.helper.TupleBrowser */
+    private jdbm.helper.TupleBrowser jdbmBrowser;
+    /** safe temp jdbm.helper.Tuple used to store next/previous tuples */
+    private jdbm.helper.Tuple jdbmTuple = new jdbm.helper.Tuple();
+
+
+    /**
+     * Creates a JdbmTupleBrowser.
+     *
+     * @param jdbmBrowser JDBM browser to wrap.
+     */
+    public JdbmTupleBrowser(jdbm.helper.TupleBrowser jdbmBrowser)
+    {
+        this.jdbmBrowser = jdbmBrowser;
+    }
+
+
+    /**
+     * @see TupleBrowser#getNext(org.apache.directory.server.core.partition.impl.btree.Tuple)
+     */
+    public boolean getNext( Tuple tuple ) throws NamingException
+    {
+        boolean isSuccess = false;
+
+        synchronized ( jdbmTuple )
+        {
+            try
+            {
+                isSuccess = jdbmBrowser.getNext( jdbmTuple );
+            }
+            catch ( IOException ioe )
+            {
+                NamingException ne = new NamingException( "Failed on call to jdbm TupleBrowser.getNext()" );
+                ne.setRootCause( ioe );
+                throw ne;
+            }
+
+            if ( isSuccess )
+            {
+                tuple.setKey( jdbmTuple.getKey() );
+                tuple.setValue( jdbmTuple.getValue() );
+            }
+        }
+
+        return isSuccess;
+    }
+
+
+    /**
+     * @see TupleBrowser#getPrevious(Tuple)
+     */
+    public boolean getPrevious( Tuple tuple ) throws NamingException
+    {
+        boolean isSuccess = false;
+
+        synchronized ( jdbmTuple )
+        {
+            try
+            {
+                isSuccess = jdbmBrowser.getPrevious( jdbmTuple );
+            }
+            catch ( IOException ioe )
+            {
+                NamingException ne = new NamingException( "Failed on call to jdbm TupleBrowser.getPrevious()" );
+                ne.setRootCause( ioe );
+                throw ne;
+            }
+
+            if ( isSuccess )
+            {
+                tuple.setKey( jdbmTuple.getKey() );
+                tuple.setValue( jdbmTuple.getValue() );
+            }
+        }
+
+        return isSuccess;
+    }
+}
diff --git a/old_trunk/jdbm-store/src/site/site.xml b/old_trunk/jdbm-store/src/site/site.xml
new file mode 100644
index 0000000..9ec7c16
--- /dev/null
+++ b/old_trunk/jdbm-store/src/site/site.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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.
+-->
+<!--
+  @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+-->
+<project name="${project.name}">
+    <bannerLeft>
+        <name>${project.name}</name>
+    </bannerLeft>
+    <publishDate position="navigation-bottom" format="dd-MM-yyyy HH:mm" />
+    <body>
+        <menu name="Parent">
+            <item name="Apache Directory ApacheDS" href="../index.html" />
+        </menu>
+        <menu ref="reports" />
+    </body>
+</project>
diff --git a/old_trunk/kerberos-shared/pom.xml b/old_trunk/kerberos-shared/pom.xml
new file mode 100644
index 0000000..385a1c8
--- /dev/null
+++ b/old_trunk/kerberos-shared/pom.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you 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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.directory.server</groupId>
+    <artifactId>apacheds-parent</artifactId>
+    <version>1.5.4-SNAPSHOT</version>
+  </parent>
+  <artifactId>apacheds-kerberos-shared</artifactId>
+  <name>ApacheDS Protocol Kerberos Shared</name>
+
+  <description>
+    The Kerberos protocol provider for ApacheDS
+  </description>
+
+  <packaging>jar</packaging>  
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.mina</groupId>
+      <artifactId>mina-core</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-core</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-protocol-shared</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
+  </dependencies>
+</project>
+
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/KerberosConstants.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/KerberosConstants.java
new file mode 100644
index 0000000..6d8fd0d
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/KerberosConstants.java
@@ -0,0 +1,32 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared;
+
+/**
+ * An cass to define Kerberos constants
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class KerberosConstants
+{
+    /** The Kerbersion version 5 */
+    public static final int KERBEROS_V5 = 5;
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/KerberosMessageType.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/KerberosMessageType.java
new file mode 100644
index 0000000..3f9ded2
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/KerberosMessageType.java
@@ -0,0 +1,117 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared;
+
+/**
+ * An enum listing all the Kerberos V5 messages :
+ * 
+ *   AS-REQ    (10) : Authentication Serveur Request
+ *   AS-REP    (11) : Authentication Serveur Response
+ *   TGS-REQ   (12) : Ticket Granting Server Request
+ *   TGS-REP   (13) : Ticket Granting Server Response
+ *   AP-REQ    (14) : Application Request
+ *   AP-REP    (15) : Application Response
+ *   KRB-SAFE  (20) : Safe (checksummed) application message
+ *   KRB-PRIV  (21) : Private (encrypted) application message
+ *   KRB-CRED  (22) : Private (encrypted) message to forward credentials
+ *   ENC_AP_REP_PART (27) : Encrypted application reply part
+ *   ENC_PRIV_PART (28) : Encrypted private message part
+ *   KRB-ERROR (30) : A kerberos error response
+ *   
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public enum KerberosMessageType
+{
+    AS_REQ( 10, "initial authentication request" ),
+    AS_REP( 11, "initial authentication response"),
+    TGS_REQ( 12, "request for authentication based on TGT" ),
+    TGS_REP( 13, "response to authentication based on TGT" ),
+    AP_REQ( 14, "application request" ), 
+    AP_REP( 15, "application response" ), 
+    KRB_SAFE( 20, "safe (checksummed) application message" ), 
+    KRB_PRIV( 21,  "private (encrypted) application message" ), 
+    KRB_CRED( 22, "private (encrypted) message to forward credentials" ),
+    ENC_AP_REP_PART( 27, "encrypted application reply part" ),
+    ENC_PRIV_PART( 28, "encrypted private message part" ),
+    KRB_ERROR( 30, "error response" );
+    
+    private int value;
+    private String message;
+    
+    /**
+     * Creates a new instance of KerberosMessageType.
+     */
+    private KerberosMessageType( int value, String message )
+    {
+        this.value = value;
+        this.message = message;
+    }
+
+    
+    /**
+     * Get the int value for this element
+     *
+     * @return The int value of this element
+     */
+    public int getOrdinal()
+    {
+        return value;
+    }
+    
+    
+    /**
+     * Get the message associated with this element
+     *
+     * @return The message associated with this element
+     */
+    public String getMessage()
+    {
+        return message;
+    }
+    
+    
+    /**
+     * Get the instance of a KerberosMessageType from an int value
+     *
+     * @param value The int value 
+     * @return A KerberosMessageType associated with this value
+     */
+    public static KerberosMessageType getTypeByOrdinal( int value )
+    {
+        switch ( value )
+        {
+            case 10 : return AS_REQ;
+            case 11 : return AS_REP;
+            case 12 : return TGS_REQ;
+            case 13 : return TGS_REP;
+            case 14 : return AP_REQ; 
+            case 15 : return AP_REP; 
+            case 20 : return KRB_SAFE; 
+            case 21 : return KRB_PRIV; 
+            case 22 : return KRB_CRED;
+            case 27 : return ENC_AP_REP_PART;
+            case 28 : return ENC_PRIV_PART;
+            case 30 : return KRB_ERROR;
+            default : return null;
+        }
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/KerberosUtils.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/KerberosUtils.java
new file mode 100644
index 0000000..0864336
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/KerberosUtils.java
@@ -0,0 +1,460 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared;
+
+import java.net.InetAddress;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.apache.directory.server.kerberos.shared.crypto.encryption.CipherTextHandler;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.KeyUsage;
+import org.apache.directory.server.kerberos.shared.exceptions.ErrorType;
+import org.apache.directory.server.kerberos.shared.exceptions.KerberosException;
+import org.apache.directory.server.kerberos.shared.messages.ApplicationRequest;
+import org.apache.directory.server.kerberos.shared.messages.components.Authenticator;
+import org.apache.directory.server.kerberos.shared.messages.components.EncTicketPart;
+import org.apache.directory.server.kerberos.shared.messages.components.Ticket;
+import org.apache.directory.server.kerberos.shared.messages.value.ApOptions;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+import org.apache.directory.server.kerberos.shared.messages.value.HostAddress;
+import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
+import org.apache.directory.server.kerberos.shared.messages.value.PrincipalName;
+import org.apache.directory.server.kerberos.shared.replay.ReplayCache;
+import org.apache.directory.server.kerberos.shared.store.PrincipalStore;
+import org.apache.directory.server.kerberos.shared.store.PrincipalStoreEntry;
+import org.apache.directory.shared.ldap.util.StringTools;
+
+/**
+ * An utility class for Kerberos.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class KerberosUtils
+{
+    /** A constant for integer optional values */
+    public static final int NULL = -1;
+
+    /** An empty list of principal names */
+    public static final List<String> EMPTY_PRINCIPAL_NAME = new ArrayList<String>();
+    
+    /**
+     * Parse a KerberosPrincipal instance and return the names. The Principal name
+     * is described in RFC 1964 : <br/>
+     * <br/>
+     * This name type corresponds to the single-string representation of a<br/>
+     * Kerberos name.  (Within the MIT Kerberos V5 implementation, such<br/>
+     * names are parseable with the krb5_parse_name() function.)  The<br/>
+     * elements included within this name representation are as follows,<br/>
+     * proceeding from the beginning of the string:<br/>
+     * <br/>
+     *  (1) One or more principal name components; if more than one<br/>
+     *  principal name component is included, the components are<br/>
+     *  separated by `/`.  Arbitrary octets may be included within<br/>
+     *  principal name components, with the following constraints and<br/>
+     *  special considerations:<br/>
+     * <br/>
+     *     (1a) Any occurrence of the characters `@` or `/` within a<br/>
+     *     name component must be immediately preceded by the `\`<br/>
+     *     quoting character, to prevent interpretation as a component<br/>
+     *     or realm separator.<br/>
+     * <br/>
+     *     (1b) The ASCII newline, tab, backspace, and null characters<br/>
+     *     may occur directly within the component or may be<br/>
+     *     represented, respectively, by `\n`, `\t`, `\b`, or `\0`.<br/>
+     * <br/>
+     *     (1c) If the `\` quoting character occurs outside the contexts<br/>
+     *     described in (1a) and (1b) above, the following character is<br/>
+     *     interpreted literally.  As a special case, this allows the<br/>
+     *     doubled representation `\\` to represent a single occurrence<br/>
+     *     of the quoting character.<br/>
+     * <br/>
+     *     (1d) An occurrence of the `\` quoting character as the last<br/>
+     *     character of a component is illegal.<br/>
+     * <br/>
+     *  (2) Optionally, a `@` character, signifying that a realm name<br/>
+     *  immediately follows. If no realm name element is included, the<br/>
+     *  local realm name is assumed.  The `/` , `:`, and null characters<br/>
+     *  may not occur within a realm name; the `@`, newline, tab, and<br/>
+     *  backspace characters may be included using the quoting<br/>
+     *  conventions described in (1a), (1b), and (1c) above.<br/>
+     * 
+     * @param principal The principal to be parsed
+     * @return The names as a List of nameComponent
+     * 
+     * @throws ParseException if the name is not valid
+     */
+    public static List<String> getNames( KerberosPrincipal principal ) throws ParseException
+    {
+        if ( principal == null )
+        {
+            return EMPTY_PRINCIPAL_NAME;
+        }
+        
+        String names = principal.getName();
+        
+        if ( StringTools.isEmpty( names ) )
+        {
+            // Empty name...
+            return EMPTY_PRINCIPAL_NAME;
+        }
+        
+        return getNames( names );
+    }
+
+    /**
+     * Parse a PrincipalName and return the names.
+     */
+    public static List<String> getNames( String principalNames ) throws ParseException
+    {
+        if ( principalNames == null )
+        {
+            return EMPTY_PRINCIPAL_NAME;
+        }
+        
+        List<String> nameComponents = new ArrayList<String>();
+        
+        // Start the parsing. Another State Machine :)
+        char[] chars = principalNames.toCharArray();
+        
+        boolean escaped = false;
+        boolean done = false;
+        int start = 0;
+        int pos = 0;
+        
+        for ( int i = 0; i < chars.length; i++ )
+        {
+            pos = i;
+            
+            switch ( chars[i] )
+            {
+                case '\\' :
+                    escaped = !escaped;
+                    break;
+                    
+                case '/'  :
+                    if ( escaped )
+                    {
+                        escaped = false;
+                    }
+                    else 
+                    {
+                        // We have a new name component
+                        if ( i - start > 0 )
+                        {
+                            String nameComponent = new String( chars, start, i - start );
+                            nameComponents.add( nameComponent );
+                            start = i + 1;
+                        }
+                        else
+                        {
+                            throw new ParseException( "An empty name is not valid in a kerberos name", i );
+                        }
+                    }
+                    
+                    break;
+                    
+                case '@'  :
+                    if ( escaped )
+                    {
+                        escaped = false;
+                    }
+                    else
+                    {
+                        // We have reached the realm : let's get out
+                        done = true;
+                        // We have a new name component
+
+                        if ( i - start > 0 )
+                        {
+                            String nameComponent = new String( chars, start, i - start );
+                            nameComponents.add( nameComponent );
+                            start = i + 1;
+                        }
+                        else
+                        {
+                            throw new ParseException( "An empty name is not valid in a kerberos name", i );
+                        }
+                    }
+                    
+                    break;
+                    
+                default :
+            }
+            
+            if ( done )
+            {
+                break;
+            }
+        } 
+        
+        if ( escaped )
+        {
+            throw new ParseException( "A '/' at the end of a Kerberos Name is not valid.", pos );
+        }
+        
+        return nameComponents;
+    }
+    
+    
+    /**
+     * Constructs a KerberosPrincipal from a PrincipalName and an 
+     * optional realm
+     *
+     * @param principal The principal name and type
+     * @param realm The optional realm
+     * 
+     * @return A KerberosPrincipal
+     */
+    public static KerberosPrincipal getKerberosPrincipal( PrincipalName principal, String realm )
+    {
+        String name = principal.getNameString(); 
+        
+        if ( !StringTools.isEmpty( realm ) )
+        {
+            name += '@' + realm;
+        }
+        
+        return new KerberosPrincipal( name, principal.getNameType().getOrdinal() );
+    }
+
+
+    /**
+     * Get the matching encryption type from the configured types, searching
+     * into the requested types. We returns the first we find.
+     *
+     * @param requestedTypes The client encryption types
+     * @param configuredTypes The configured encryption types
+     * @return The first matching encryption type.
+     */
+    public static EncryptionType getBestEncryptionType( Set<EncryptionType> requestedTypes, Set<EncryptionType> configuredTypes )
+    {
+        for ( EncryptionType encryptionType:requestedTypes )
+        {
+            if ( configuredTypes.contains( encryptionType ) )
+            {
+                return encryptionType;
+            }
+        }
+
+        return null;
+    }
+    
+    
+    /**
+     * Build a list of encryptionTypes
+     *
+     * @param encryptionTypes The encryptionTypes
+     * @return A list comma separated of the encryptionTypes
+     */
+    public static String getEncryptionTypesString( Set<EncryptionType> encryptionTypes )
+    {
+        StringBuilder sb = new StringBuilder();
+        boolean isFirst = true;
+
+        for ( EncryptionType etype:encryptionTypes )
+        {
+            if ( isFirst )
+            {
+                isFirst = false;
+            }
+            else
+            {
+                sb.append( ", " );
+            }
+            
+            sb.append( etype );
+        }
+
+        return sb.toString();
+    }
+
+
+    /**
+     * Get a PrincipalStoreEntry given a principal.  The ErrorType is used to indicate
+     * whether any resulting error pertains to a server or client.
+     *
+     * @param principal
+     * @param store
+     * @param errorType
+     * @return The PrincipalStoreEntry
+     * @throws Exception
+     */
+    public static PrincipalStoreEntry getEntry( KerberosPrincipal principal, PrincipalStore store, ErrorType errorType )
+        throws KerberosException
+    {
+        PrincipalStoreEntry entry = null;
+
+        try
+        {
+            entry = store.getPrincipal( principal );
+        }
+        catch ( Exception e )
+        {
+            throw new KerberosException( errorType, e );
+        }
+
+        if ( entry == null )
+        {
+            throw new KerberosException( errorType );
+        }
+
+        if ( entry.getKeyMap() == null || entry.getKeyMap().isEmpty() )
+        {
+            throw new KerberosException( ErrorType.KDC_ERR_NULL_KEY );
+        }
+
+        return entry;
+    }
+
+
+    /**
+     * Verifies an AuthHeader using guidelines from RFC 1510 section A.10., "KRB_AP_REQ verification."
+     *
+     * @param authHeader
+     * @param ticket
+     * @param serverKey
+     * @param clockSkew
+     * @param replayCache
+     * @param emptyAddressesAllowed
+     * @param clientAddress
+     * @param lockBox
+     * @param authenticatorKeyUsage
+     * @param isValidate
+     * @return The authenticator.
+     * @throws KerberosException
+     */
+    public static Authenticator verifyAuthHeader( ApplicationRequest authHeader, Ticket ticket, EncryptionKey serverKey,
+        long clockSkew, ReplayCache replayCache, boolean emptyAddressesAllowed, InetAddress clientAddress,
+        CipherTextHandler lockBox, KeyUsage authenticatorKeyUsage, boolean isValidate ) throws KerberosException
+    {
+        if ( authHeader.getProtocolVersionNumber() != KerberosConstants.KERBEROS_V5 )
+        {
+            throw new KerberosException( ErrorType.KRB_AP_ERR_BADVERSION );
+        }
+
+        if ( authHeader.getMessageType() != KerberosMessageType.AP_REQ )
+        {
+            throw new KerberosException( ErrorType.KRB_AP_ERR_MSG_TYPE );
+        }
+
+        if ( authHeader.getTicket().getTktVno() != KerberosConstants.KERBEROS_V5 )
+        {
+            throw new KerberosException( ErrorType.KRB_AP_ERR_BADVERSION );
+        }
+
+        EncryptionKey ticketKey = null;
+
+        if ( authHeader.getOption( ApOptions.USE_SESSION_KEY ) )
+        {
+            ticketKey = authHeader.getTicket().getEncTicketPart().getSessionKey();
+        }
+        else
+        {
+            ticketKey = serverKey;
+        }
+
+        if ( ticketKey == null )
+        {
+            // TODO - check server key version number, skvno; requires store
+            if ( false )
+            {
+                throw new KerberosException( ErrorType.KRB_AP_ERR_BADKEYVER );
+            }
+
+            throw new KerberosException( ErrorType.KRB_AP_ERR_NOKEY );
+        }
+
+        EncTicketPart encPart = ( EncTicketPart ) lockBox.unseal( EncTicketPart.class, ticketKey, ticket.getEncPart(),
+            KeyUsage.NUMBER2 );
+        ticket.setEncTicketPart( encPart );
+
+        Authenticator authenticator = ( Authenticator ) lockBox.unseal( Authenticator.class, ticket.getEncTicketPart().getSessionKey(),
+            authHeader.getEncPart(), authenticatorKeyUsage );
+
+        if ( !authenticator.getClientPrincipal().getName().equals( ticket.getEncTicketPart().getClientPrincipal().getName() ) )
+        {
+            throw new KerberosException( ErrorType.KRB_AP_ERR_BADMATCH );
+        }
+
+        if ( ticket.getEncTicketPart().getClientAddresses() != null )
+        {
+            if ( !ticket.getEncTicketPart().getClientAddresses().contains( new HostAddress( clientAddress ) ) )
+            {
+                throw new KerberosException( ErrorType.KRB_AP_ERR_BADADDR );
+            }
+        }
+        else
+        {
+            if ( !emptyAddressesAllowed )
+            {
+                throw new KerberosException( ErrorType.KRB_AP_ERR_BADADDR );
+            }
+        }
+
+        KerberosPrincipal serverPrincipal = ticket.getServerPrincipal();
+        KerberosPrincipal clientPrincipal = authenticator.getClientPrincipal();
+        KerberosTime clientTime = authenticator.getClientTime();
+        int clientMicroSeconds = authenticator.getClientMicroSecond();
+
+        if ( replayCache.isReplay( serverPrincipal, clientPrincipal, clientTime, clientMicroSeconds ) )
+        {
+            throw new KerberosException( ErrorType.KRB_AP_ERR_REPEAT );
+        }
+
+        replayCache.save( serverPrincipal, clientPrincipal, clientTime, clientMicroSeconds );
+
+        if ( !authenticator.getClientTime().isInClockSkew( clockSkew ) )
+        {
+            throw new KerberosException( ErrorType.KRB_AP_ERR_SKEW );
+        }
+
+        /*
+         * "The server computes the age of the ticket: local (server) time minus
+         * the starttime inside the Ticket.  If the starttime is later than the
+         * current time by more than the allowable clock skew, or if the INVALID
+         * flag is set in the ticket, the KRB_AP_ERR_TKT_NYV error is returned."
+         */
+        KerberosTime startTime = ( ticket.getEncTicketPart().getStartTime() != null ) ? ticket.getEncTicketPart().getStartTime() : ticket.getEncTicketPart().getAuthTime();
+
+        KerberosTime now = new KerberosTime();
+        boolean isValidStartTime = startTime.lessThan( now );
+
+        if ( !isValidStartTime || ( ticket.getEncTicketPart().getFlags().isInvalid() && !isValidate ) )
+        {
+            // it hasn't yet become valid
+            throw new KerberosException( ErrorType.KRB_AP_ERR_TKT_NYV );
+        }
+
+        // TODO - doesn't take into account skew
+        if ( !ticket.getEncTicketPart().getEndTime().greaterThan( now ) )
+        {
+            throw new KerberosException( ErrorType.KRB_AP_ERR_TKT_EXPIRED );
+        }
+
+        authHeader.setOption( ApOptions.MUTUAL_REQUIRED );
+
+        return authenticator;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/checksum/ChecksumEngine.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/checksum/ChecksumEngine.java
new file mode 100644
index 0000000..088e46f
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/checksum/ChecksumEngine.java
@@ -0,0 +1,49 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.crypto.checksum;
+
+
+import org.apache.directory.server.kerberos.shared.crypto.encryption.KeyUsage;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public interface ChecksumEngine
+{
+    /**
+     * Returns the checksum type of this checksum engine.
+     *
+     * @return The checksum type.
+     */
+    public ChecksumType checksumType();
+
+
+    /**
+     * Calculate a checksum given raw bytes and an (optional) key.
+     *
+     * @param data
+     * @param key
+     * @param usage 
+     * @return The checksum value.
+     */
+    public byte[] calculateChecksum( byte[] data, byte[] key, KeyUsage usage );
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/checksum/ChecksumHandler.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/checksum/ChecksumHandler.java
new file mode 100644
index 0000000..e77ced2
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/checksum/ChecksumHandler.java
@@ -0,0 +1,138 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.crypto.checksum;
+
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.directory.server.kerberos.shared.crypto.encryption.Aes128CtsSha1Encryption;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.Aes256CtsSha1Encryption;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.Des3CbcSha1KdEncryption;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.KeyUsage;
+import org.apache.directory.server.kerberos.shared.exceptions.ErrorType;
+import org.apache.directory.server.kerberos.shared.exceptions.KerberosException;
+import org.apache.directory.server.kerberos.shared.messages.value.Checksum;
+
+
+/**
+ * A Hashed Adapter encapsulating checksum engines for performing integrity checks.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ChecksumHandler
+{
+    /** A map of the default encodable class names to the encoder class names. */
+    private static final Map DEFAULT_CHECKSUMS;
+
+    static
+    {
+        Map<ChecksumType, Class> map = new HashMap<ChecksumType, Class>();
+
+        map.put( ChecksumType.HMAC_MD5, HmacMd5Checksum.class );
+        map.put( ChecksumType.HMAC_SHA1_96_AES128, Aes128CtsSha1Encryption.class );
+        map.put( ChecksumType.HMAC_SHA1_96_AES256, Aes256CtsSha1Encryption.class );
+        map.put( ChecksumType.HMAC_SHA1_DES3_KD, Des3CbcSha1KdEncryption.class );
+        map.put( ChecksumType.RSA_MD5, RsaMd5Checksum.class );
+
+        DEFAULT_CHECKSUMS = Collections.unmodifiableMap( map );
+    }
+
+
+    /**
+     * Calculate a checksum based on raw bytes and an (optional) key for keyed checksums.
+     *
+     * @param checksumType
+     * @param bytes
+     * @param key
+     * @param usage
+     * @return The {@link Checksum}.
+     * @throws KerberosException
+     */
+    public Checksum calculateChecksum( ChecksumType checksumType, byte[] bytes, byte[] key, KeyUsage usage )
+        throws KerberosException
+    {
+        if ( !DEFAULT_CHECKSUMS.containsKey( checksumType ) )
+        {
+            throw new KerberosException( ErrorType.KDC_ERR_SUMTYPE_NOSUPP );
+        }
+
+        ChecksumEngine digester = getEngine( checksumType );
+        return new Checksum( checksumType, digester.calculateChecksum( bytes, key, usage ) );
+    }
+
+
+    /**
+     * Verify a checksum by providing the raw bytes and an (optional) key for keyed checksums.
+     *
+     * @param checksum
+     * @param bytes
+     * @param key
+     * @param usage 
+     * @throws KerberosException
+     */
+    public void verifyChecksum( Checksum checksum, byte[] bytes, byte[] key, KeyUsage usage ) throws KerberosException
+    {
+        if ( checksum == null )
+        {
+            throw new KerberosException( ErrorType.KRB_AP_ERR_INAPP_CKSUM );
+        }
+
+        if ( !DEFAULT_CHECKSUMS.containsKey( checksum.getChecksumType() ) )
+        {
+            throw new KerberosException( ErrorType.KDC_ERR_SUMTYPE_NOSUPP );
+        }
+
+        ChecksumType checksumType = checksum.getChecksumType();
+        ChecksumEngine digester = getEngine( checksumType );
+        Checksum newChecksum = new Checksum( checksumType, digester.calculateChecksum( bytes, key, usage ) );
+
+        if ( !newChecksum.equals( checksum ) )
+        {
+            throw new KerberosException( ErrorType.KRB_AP_ERR_MODIFIED );
+        }
+    }
+
+
+    private ChecksumEngine getEngine( ChecksumType checksumType ) throws KerberosException
+    {
+        Class clazz = ( Class ) DEFAULT_CHECKSUMS.get( checksumType );
+
+        if ( clazz == null )
+        {
+            throw new KerberosException( ErrorType.KDC_ERR_SUMTYPE_NOSUPP );
+        }
+
+        try
+        {
+            return ( ChecksumEngine ) clazz.newInstance();
+        }
+        catch ( IllegalAccessException iae )
+        {
+            throw new KerberosException( ErrorType.KDC_ERR_SUMTYPE_NOSUPP, iae );
+        }
+        catch ( InstantiationException ie )
+        {
+            throw new KerberosException( ErrorType.KDC_ERR_SUMTYPE_NOSUPP, ie );
+        }
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/checksum/ChecksumType.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/checksum/ChecksumType.java
new file mode 100644
index 0000000..71b3be6
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/checksum/ChecksumType.java
@@ -0,0 +1,179 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.crypto.checksum;
+
+
+/**
+ * A type-safe enumeration of Kerberos checksum types.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public enum ChecksumType implements Comparable<ChecksumType>
+{
+    /**
+     * The "unknown" checksum type.
+     */
+    UNKNOWN( -1 ),
+
+    /**
+     * The "null" checksum type.
+     */
+    NULL( 0 ),
+
+    /**
+     * The CRC32 checksum type.
+     */
+    CRC32( 1 ),
+
+    /**
+     * The rsa-md4 checksum type.
+     */
+    RSA_MD4( 2 ),
+
+    /**
+     * The rsa-md4-des checksum type.
+     */
+    RSA_MD4_DES( 3 ),
+
+    /**
+     * The des-mac checksum type.
+     */
+    DES_MAC( 4 ),
+
+    /**
+     * The des-mac-k checksum type.
+     */
+    DES_MAC_K( 5 ),
+
+    /**
+     * The rsa-md4-des-k checksum type.
+     */
+    RSA_MD4_DES_K( 6 ),
+
+    /**
+     * The rsa-md5 checksum type.
+     */
+    RSA_MD5( 7 ),
+
+    /**
+     * The rsa-md5-des checksum type.
+     */
+    RSA_MD5_DES( 8 ),
+
+    /**
+     * The rsa-md5-des3 checksum type.
+     */
+    RSA_MD5_DES3( 9 ),
+
+    /**
+     * The sha1 (unkeyed) checksum type.
+     */
+    SHA1( 10 ),
+
+    /**
+     * The hmac-sha1-des3-kd checksum type.
+     */
+    HMAC_SHA1_DES3_KD( 12 ),
+
+    /**
+     * The hmac-sha1-des3 checksum type.
+     */
+    HMAC_SHA1_DES3( 13 ),
+
+    /**
+     * The sha1 (unkeyed) checksum type.
+     */
+    SHA1_2 ( 14 ),
+
+    /**
+     * The hmac-sha1-96-aes128 checksum type.
+     */
+    HMAC_SHA1_96_AES128( 15 ),
+
+    /**
+     * The hmac-sha1-96-aes256 checksum type.
+     */
+    HMAC_SHA1_96_AES256( 16 ),
+
+    /**
+     * The hmac-md5 checksum type.
+     */
+    HMAC_MD5( -138 );
+
+
+    /**
+     * The value/code for the checksum type.
+     */
+    private final int ordinal;
+
+
+    /**
+     * Private constructor prevents construction outside of this class.
+     */
+    private ChecksumType( int ordinal )
+    {
+        this.ordinal = ordinal;
+    }
+
+
+    /**
+     * Returns the checksum type when specified by its ordinal.
+     *
+     * @param type
+     * @return The checksum type.
+     */
+    public static ChecksumType getTypeByOrdinal( int type )
+    {
+        switch ( type )
+        {
+            case -1 : return UNKNOWN;
+            case 0  : return NULL;
+            case 1  : return CRC32;
+            case 2  : return RSA_MD4;
+            case 3  : return RSA_MD4_DES;
+            case 4  : return DES_MAC;
+            case 5  : return DES_MAC_K;
+            case 6  : return RSA_MD4_DES_K;
+            case 7  : return RSA_MD5;
+            case 8  : return RSA_MD5_DES;
+            case 9  : return RSA_MD5_DES3;
+            case 10 : return SHA1;
+            case 12 : return HMAC_SHA1_DES3_KD;
+            case 13 : return HMAC_SHA1_DES3;
+            case 14 : return SHA1_2;
+            case 15 : return HMAC_SHA1_96_AES128;
+            case 16 : return HMAC_SHA1_96_AES256;
+            case -138 : return HMAC_MD5;
+            default : return UNKNOWN;
+        }
+    }
+
+
+    /**
+     * Returns the number associated with this checksum type.
+     *
+     * @return The checksum type ordinal.
+     */
+    public int getOrdinal()
+    {
+        return ordinal;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/checksum/Crc32Checksum.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/checksum/Crc32Checksum.java
new file mode 100644
index 0000000..0e35f31
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/checksum/Crc32Checksum.java
@@ -0,0 +1,62 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.kerberos.shared.crypto.checksum;
+
+
+import java.util.zip.CRC32;
+
+import org.apache.directory.server.kerberos.shared.crypto.encryption.KeyUsage;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+class Crc32Checksum implements ChecksumEngine
+{
+    public ChecksumType checksumType()
+    {
+        return ChecksumType.CRC32;
+    }
+
+
+    public byte[] calculateChecksum( byte[] data, byte[] key, KeyUsage usage )
+    {
+        CRC32 crc32 = new CRC32();
+        crc32.update( data );
+
+        return int2octet( ( int ) crc32.getValue() );
+    }
+
+
+    private byte[] int2octet( int value )
+    {
+        byte[] bytes = new byte[4];
+        int i, shift;
+
+        for ( i = 0, shift = 24; i < 4; i++, shift -= 8 )
+        {
+            bytes[i] = ( byte ) ( 0xFF & ( value >> shift ) );
+        }
+
+        return bytes;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/checksum/HmacMd5Checksum.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/checksum/HmacMd5Checksum.java
new file mode 100644
index 0000000..3600913
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/checksum/HmacMd5Checksum.java
@@ -0,0 +1,61 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.crypto.checksum;
+
+
+import java.security.GeneralSecurityException;
+
+import javax.crypto.Mac;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.apache.directory.server.kerberos.shared.crypto.encryption.KeyUsage;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+class HmacMd5Checksum implements ChecksumEngine
+{
+    public ChecksumType checksumType()
+    {
+        return ChecksumType.HMAC_MD5;
+    }
+
+
+    public byte[] calculateChecksum( byte[] data, byte[] key, KeyUsage usage )
+    {
+        try
+        {
+            SecretKey sk = new SecretKeySpec( key, "ARCFOUR" );
+
+            Mac mac = Mac.getInstance( "HmacMD5" );
+            mac.init( sk );
+
+            return mac.doFinal( data );
+        }
+        catch ( GeneralSecurityException nsae )
+        {
+            nsae.printStackTrace();
+            return null;
+        }
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/checksum/RsaMd5Checksum.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/checksum/RsaMd5Checksum.java
new file mode 100644
index 0000000..8bc1440
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/checksum/RsaMd5Checksum.java
@@ -0,0 +1,54 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.crypto.checksum;
+
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+import org.apache.directory.server.kerberos.shared.crypto.encryption.KeyUsage;
+import org.apache.directory.shared.ldap.constants.LdapSecurityConstants;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+class RsaMd5Checksum implements ChecksumEngine
+{
+    public ChecksumType checksumType()
+    {
+        return ChecksumType.RSA_MD5;
+    }
+
+
+    public byte[] calculateChecksum( byte[] data, byte[] key, KeyUsage usage )
+    {
+        try
+        {
+            MessageDigest digester = MessageDigest.getInstance( LdapSecurityConstants.HASH_METHOD_MD5.getName() );
+            return digester.digest( data );
+        }
+        catch ( NoSuchAlgorithmException nsae )
+        {
+            return null;
+        }
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/checksum/package-info.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/checksum/package-info.java
new file mode 100644
index 0000000..dbf2802
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/checksum/package-info.java
@@ -0,0 +1,28 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+/**
+ * Provides support for checksum calculations.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+
+package org.apache.directory.server.kerberos.shared.crypto.checksum;
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/encryption/Aes128CtsSha1Encryption.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/encryption/Aes128CtsSha1Encryption.java
new file mode 100644
index 0000000..e6ddbeb
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/encryption/Aes128CtsSha1Encryption.java
@@ -0,0 +1,48 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.crypto.encryption;
+
+
+import org.apache.directory.server.kerberos.shared.crypto.checksum.ChecksumType;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class Aes128CtsSha1Encryption extends AesCtsSha1Encryption
+{
+    public EncryptionType getEncryptionType()
+    {
+        return EncryptionType.AES128_CTS_HMAC_SHA1_96;
+    }
+
+
+    public ChecksumType checksumType()
+    {
+        return ChecksumType.HMAC_SHA1_96_AES128;
+    }
+
+
+    public int getKeyLength()
+    {
+        return 128;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/encryption/Aes256CtsSha1Encryption.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/encryption/Aes256CtsSha1Encryption.java
new file mode 100644
index 0000000..b54db50
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/encryption/Aes256CtsSha1Encryption.java
@@ -0,0 +1,48 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.crypto.encryption;
+
+
+import org.apache.directory.server.kerberos.shared.crypto.checksum.ChecksumType;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class Aes256CtsSha1Encryption extends AesCtsSha1Encryption
+{
+    public EncryptionType getEncryptionType()
+    {
+        return EncryptionType.AES256_CTS_HMAC_SHA1_96;
+    }
+
+
+    public ChecksumType checksumType()
+    {
+        return ChecksumType.HMAC_SHA1_96_AES256;
+    }
+
+
+    public int getKeyLength()
+    {
+        return 256;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/encryption/AesCtsSha1Encryption.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/encryption/AesCtsSha1Encryption.java
new file mode 100644
index 0000000..7544edd
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/encryption/AesCtsSha1Encryption.java
@@ -0,0 +1,198 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.crypto.encryption;
+
+
+import java.security.GeneralSecurityException;
+import java.security.spec.AlgorithmParameterSpec;
+import java.util.Arrays;
+
+import javax.crypto.Cipher;
+import javax.crypto.Mac;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.apache.directory.server.kerberos.shared.crypto.checksum.ChecksumEngine;
+import org.apache.directory.server.kerberos.shared.exceptions.ErrorType;
+import org.apache.directory.server.kerberos.shared.exceptions.KerberosException;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptedData;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+abstract class AesCtsSha1Encryption extends EncryptionEngine implements ChecksumEngine
+{
+    private static final byte[] iv = new byte[]
+        { ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00,
+            ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00,
+            ( byte ) 0x00, ( byte ) 0x00 };
+
+
+    public int getConfounderLength()
+    {
+        return 16;
+    }
+
+
+    public int getChecksumLength()
+    {
+        return 12;
+    }
+
+
+    protected abstract int getKeyLength();
+
+
+    public byte[] calculateChecksum( byte[] data, byte[] key, KeyUsage usage )
+    {
+        byte[] Kc = deriveKey( key, getUsageKc( usage ), 128, getKeyLength() );
+        byte[] checksum = processChecksum( data, Kc );
+
+        return removeTrailingBytes( checksum, 0, checksum.length - getChecksumLength() );
+    }
+
+
+    public byte[] calculateIntegrity( byte[] data, byte[] key, KeyUsage usage )
+    {
+        byte[] Ki = deriveKey( key, getUsageKi( usage ), 128, getKeyLength() );
+        byte[] checksum = processChecksum( data, Ki );
+
+        return removeTrailingBytes( checksum, 0, checksum.length - getChecksumLength() );
+    }
+
+
+    public byte[] getDecryptedData( EncryptionKey key, EncryptedData data, KeyUsage usage ) throws KerberosException
+    {
+        byte[] Ke = deriveKey( key.getKeyValue(), getUsageKe( usage ), 128, getKeyLength() );
+
+        byte[] encryptedData = data.getCipher();
+
+        // extract the old checksum
+        byte[] oldChecksum = new byte[getChecksumLength()];
+        System
+            .arraycopy( encryptedData, encryptedData.length - getChecksumLength(), oldChecksum, 0, oldChecksum.length );
+
+        // remove trailing checksum
+        encryptedData = removeTrailingBytes( encryptedData, 0, getChecksumLength() );
+
+        // decrypt the data
+        byte[] decryptedData = decrypt( encryptedData, Ke );
+
+        // remove leading confounder
+        byte[] withoutConfounder = removeLeadingBytes( decryptedData, getConfounderLength(), 0 );
+
+        // calculate a new checksum
+        byte[] newChecksum = calculateIntegrity( decryptedData, key.getKeyValue(), usage );
+
+        // compare checksums
+        if ( !Arrays.equals( oldChecksum, newChecksum ) )
+        {
+            throw new KerberosException( ErrorType.KRB_AP_ERR_BAD_INTEGRITY );
+        }
+
+        return withoutConfounder;
+    }
+
+
+    public EncryptedData getEncryptedData( EncryptionKey key, byte[] plainText, KeyUsage usage )
+    {
+        byte[] Ke = deriveKey( key.getKeyValue(), getUsageKe( usage ), 128, getKeyLength() );
+
+        // build the ciphertext structure
+        byte[] conFounder = getRandomBytes( getConfounderLength() );
+        byte[] dataBytes = concatenateBytes( conFounder, plainText );
+
+        byte[] checksumBytes = calculateIntegrity( dataBytes, key.getKeyValue(), usage );
+
+        byte[] encryptedData = encrypt( dataBytes, Ke );
+        byte[] cipherText = concatenateBytes( encryptedData, checksumBytes );
+
+        return new EncryptedData( getEncryptionType(), key.getKeyVersion(), cipherText );
+    }
+
+
+    public byte[] encrypt( byte[] plainText, byte[] keyBytes )
+    {
+        return processCipher( true, plainText, keyBytes );
+    }
+
+
+    public byte[] decrypt( byte[] cipherText, byte[] keyBytes )
+    {
+        return processCipher( false, cipherText, keyBytes );
+    }
+
+
+    protected byte[] deriveKey( byte[] baseKey, byte[] usage, int n, int k )
+    {
+        return deriveRandom( baseKey, usage, n, k );
+    }
+
+
+    private byte[] processChecksum( byte[] data, byte[] key )
+    {
+        try
+        {
+            SecretKey sk = new SecretKeySpec( key, "AES" );
+
+            Mac mac = Mac.getInstance( "HmacSHA1" );
+            mac.init( sk );
+
+            return mac.doFinal( data );
+        }
+        catch ( GeneralSecurityException nsae )
+        {
+            nsae.printStackTrace();
+            return null;
+        }
+    }
+
+
+    private byte[] processCipher( boolean isEncrypt, byte[] data, byte[] keyBytes )
+    {
+        try
+        {
+            Cipher cipher = Cipher.getInstance( "AES/CTS/NoPadding" );
+            SecretKey key = new SecretKeySpec( keyBytes, "AES" );
+
+            AlgorithmParameterSpec paramSpec = new IvParameterSpec( iv );
+
+            if ( isEncrypt )
+            {
+                cipher.init( Cipher.ENCRYPT_MODE, key, paramSpec );
+            }
+            else
+            {
+                cipher.init( Cipher.DECRYPT_MODE, key, paramSpec );
+            }
+
+            return cipher.doFinal( data );
+        }
+        catch ( GeneralSecurityException nsae )
+        {
+            nsae.printStackTrace();
+            return null;
+        }
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/encryption/ArcFourHmacMd5Encryption.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/encryption/ArcFourHmacMd5Encryption.java
new file mode 100644
index 0000000..f123e5b
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/encryption/ArcFourHmacMd5Encryption.java
@@ -0,0 +1,122 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.crypto.encryption;
+
+
+import java.security.GeneralSecurityException;
+import java.security.NoSuchAlgorithmException;
+
+import javax.crypto.Cipher;
+import javax.crypto.Mac;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.apache.directory.server.kerberos.shared.exceptions.KerberosException;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptedData;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+class ArcFourHmacMd5Encryption extends EncryptionEngine
+{
+    public EncryptionType getEncryptionType()
+    {
+        return EncryptionType.RC4_HMAC;
+    }
+
+
+    public int getChecksumLength()
+    {
+        return 16;
+    }
+
+
+    public int getConfounderLength()
+    {
+        return 8;
+    }
+
+
+    public byte[] getDecryptedData( EncryptionKey key, EncryptedData data, KeyUsage usage ) throws KerberosException
+    {
+        return data.getCipher();
+    }
+
+
+    public EncryptedData getEncryptedData( EncryptionKey key, byte[] plainText, KeyUsage usage )
+    {
+        return new EncryptedData( getEncryptionType(), key.getKeyVersion(), plainText );
+    }
+
+
+    public byte[] encrypt( byte[] plainText, byte[] keyBytes )
+    {
+        return processCipher( true, plainText, keyBytes );
+    }
+
+
+    public byte[] decrypt( byte[] cipherText, byte[] keyBytes )
+    {
+        return processCipher( false, cipherText, keyBytes );
+    }
+
+
+    public byte[] calculateIntegrity( byte[] data, byte[] key, KeyUsage usage )
+    {
+        try
+        {
+            Mac digester = Mac.getInstance( "HmacMD5" );
+            return digester.doFinal( data );
+        }
+        catch ( NoSuchAlgorithmException nsae )
+        {
+            return null;
+        }
+    }
+
+
+    private byte[] processCipher( boolean isEncrypt, byte[] data, byte[] keyBytes )
+    {
+        try
+        {
+            Cipher cipher = Cipher.getInstance( "ARCFOUR" );
+            SecretKey key = new SecretKeySpec( keyBytes, "ARCFOUR" );
+
+            if ( isEncrypt )
+            {
+                cipher.init( Cipher.ENCRYPT_MODE, key );
+            }
+            else
+            {
+                cipher.init( Cipher.DECRYPT_MODE, key );
+            }
+
+            return cipher.doFinal( data );
+        }
+        catch ( GeneralSecurityException nsae )
+        {
+            nsae.printStackTrace();
+            return null;
+        }
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/encryption/CipherTextHandler.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/encryption/CipherTextHandler.java
new file mode 100644
index 0000000..26844e6
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/encryption/CipherTextHandler.java
@@ -0,0 +1,280 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.kerberos.shared.crypto.encryption;
+
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.directory.server.kerberos.shared.exceptions.ErrorType;
+import org.apache.directory.server.kerberos.shared.exceptions.KerberosException;
+import org.apache.directory.server.kerberos.shared.io.decoder.AuthenticatorDecoder;
+import org.apache.directory.server.kerberos.shared.io.decoder.AuthorizationDataDecoder;
+import org.apache.directory.server.kerberos.shared.io.decoder.Decoder;
+import org.apache.directory.server.kerberos.shared.io.decoder.DecoderFactory;
+import org.apache.directory.server.kerberos.shared.io.decoder.EncApRepPartDecoder;
+import org.apache.directory.server.kerberos.shared.io.decoder.EncKdcRepPartDecoder;
+import org.apache.directory.server.kerberos.shared.io.decoder.EncKrbPrivPartDecoder;
+import org.apache.directory.server.kerberos.shared.io.decoder.EncTicketPartDecoder;
+import org.apache.directory.server.kerberos.shared.io.decoder.EncryptedTimestampDecoder;
+import org.apache.directory.server.kerberos.shared.io.encoder.AuthenticatorEncoder;
+import org.apache.directory.server.kerberos.shared.io.encoder.EncApRepPartEncoder;
+import org.apache.directory.server.kerberos.shared.io.encoder.EncAsRepPartEncoder;
+import org.apache.directory.server.kerberos.shared.io.encoder.EncKrbPrivPartEncoder;
+import org.apache.directory.server.kerberos.shared.io.encoder.EncTgsRepPartEncoder;
+import org.apache.directory.server.kerberos.shared.io.encoder.EncTicketPartEncoder;
+import org.apache.directory.server.kerberos.shared.io.encoder.Encoder;
+import org.apache.directory.server.kerberos.shared.io.encoder.EncoderFactory;
+import org.apache.directory.server.kerberos.shared.io.encoder.EncryptedTimestampEncoder;
+import org.apache.directory.server.kerberos.shared.messages.AuthenticationReply;
+import org.apache.directory.server.kerberos.shared.messages.Encodable;
+import org.apache.directory.server.kerberos.shared.messages.TicketGrantReply;
+import org.apache.directory.server.kerberos.shared.messages.components.Authenticator;
+import org.apache.directory.server.kerberos.shared.messages.components.EncApRepPart;
+import org.apache.directory.server.kerberos.shared.messages.components.EncKdcRepPart;
+import org.apache.directory.server.kerberos.shared.messages.components.EncKrbPrivPart;
+import org.apache.directory.server.kerberos.shared.messages.components.EncTicketPart;
+import org.apache.directory.server.kerberos.shared.messages.value.AuthorizationData;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptedData;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptedTimeStamp;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+
+
+/**
+ * A Hashed Adapter encapsulating ASN.1 encoders and decoders and cipher text engines to
+ * perform seal() and unseal() operations.  A seal() operation performs an encode and an
+ * encrypt, while an unseal() operation performs a decrypt and a decode.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class CipherTextHandler
+{
+    /** a map of the default encodable class names to the encoder class names */
+    private static final Map DEFAULT_ENCODERS;
+    /** a map of the default encodable class names to the decoder class names */
+    private static final Map DEFAULT_DECODERS;
+    /** a map of the default encryption types to the encryption engine class names */
+    private static final Map DEFAULT_CIPHERS;
+
+    static
+    {
+        Map<Class, Class> map = new HashMap<Class, Class>();
+
+        map.put( EncryptedTimeStamp.class, EncryptedTimestampEncoder.class );
+        map.put( EncTicketPart.class, EncTicketPartEncoder.class );
+        map.put( AuthenticationReply.class, EncAsRepPartEncoder.class );
+        map.put( TicketGrantReply.class, EncTgsRepPartEncoder.class );
+        map.put( EncKrbPrivPart.class, EncKrbPrivPartEncoder.class );
+        map.put( EncApRepPart.class, EncApRepPartEncoder.class );
+        map.put( Authenticator.class, AuthenticatorEncoder.class );
+
+        DEFAULT_ENCODERS = Collections.unmodifiableMap( map );
+    }
+
+    static
+    {
+        Map<Class, Class> map = new HashMap<Class, Class>();
+
+        map.put( EncTicketPart.class, EncTicketPartDecoder.class );
+        map.put( Authenticator.class, AuthenticatorDecoder.class );
+        map.put( EncryptedTimeStamp.class, EncryptedTimestampDecoder.class );
+        map.put( AuthorizationData.class, AuthorizationDataDecoder.class );
+        map.put( EncKrbPrivPart.class, EncKrbPrivPartDecoder.class );
+        map.put( EncApRepPart.class, EncApRepPartDecoder.class );
+        map.put( EncKdcRepPart.class, EncKdcRepPartDecoder.class );
+
+        DEFAULT_DECODERS = Collections.unmodifiableMap( map );
+    }
+
+    static
+    {
+        Map<EncryptionType, Class> map = new HashMap<EncryptionType, Class>();
+
+        map.put( EncryptionType.DES_CBC_MD5, DesCbcMd5Encryption.class );
+        map.put( EncryptionType.DES3_CBC_SHA1_KD, Des3CbcSha1KdEncryption.class );
+        map.put( EncryptionType.AES128_CTS_HMAC_SHA1_96, Aes128CtsSha1Encryption.class );
+        map.put( EncryptionType.AES256_CTS_HMAC_SHA1_96, Aes256CtsSha1Encryption.class );
+        map.put( EncryptionType.RC4_HMAC, ArcFourHmacMd5Encryption.class );
+
+        DEFAULT_CIPHERS = Collections.unmodifiableMap( map );
+    }
+
+
+    /**
+     * Performs an encode and an encrypt.
+     *
+     * @param key The key to use for encrypting.
+     * @param encodable The Kerberos object to encode.
+     * @param usage The key usage.
+     * @return The Kerberos EncryptedData.
+     * @throws KerberosException
+     */
+    public EncryptedData seal( EncryptionKey key, Encodable encodable, KeyUsage usage ) throws KerberosException
+    {
+        try
+        {
+            return encrypt( key, encode( encodable ), usage );
+        }
+        catch ( IOException ioe )
+        {
+            throw new KerberosException( ErrorType.KRB_AP_ERR_BAD_INTEGRITY, ioe );
+        }
+        catch ( ClassCastException cce )
+        {
+            throw new KerberosException( ErrorType.KRB_AP_ERR_BAD_INTEGRITY, cce );
+        }
+    }
+
+
+    /**
+     * Perform a decrypt and a decode.
+     *
+     * @param hint The class the encrypted data is expected to contain.
+     * @param key The key to use for decryption.
+     * @param data The data to decrypt.
+     * @param usage The key usage.
+     * @return The Kerberos object resulting from a successful decrypt and decode.
+     * @throws KerberosException
+     */
+    public Encodable unseal( Class hint, EncryptionKey key, EncryptedData data, KeyUsage usage )
+        throws KerberosException
+    {
+        try
+        {
+            return decode( hint, decrypt( key, data, usage ) );
+        }
+        catch ( IOException ioe )
+        {
+            throw new KerberosException( ErrorType.KRB_AP_ERR_BAD_INTEGRITY, ioe );
+        }
+        catch ( ClassCastException cce )
+        {
+            throw new KerberosException( ErrorType.KRB_AP_ERR_BAD_INTEGRITY, cce );
+        }
+    }
+
+
+    private EncryptedData encrypt( EncryptionKey key, byte[] plainText, KeyUsage usage ) throws KerberosException
+    {
+        EncryptionEngine engine = getEngine( key );
+
+        return engine.getEncryptedData( key, plainText, usage );
+    }
+
+
+    private byte[] decrypt( EncryptionKey key, EncryptedData data, KeyUsage usage ) throws KerberosException
+    {
+        EncryptionEngine engine = getEngine( key );
+
+        return engine.getDecryptedData( key, data, usage );
+    }
+
+
+    private byte[] encode( Encodable encodable ) throws IOException
+    {
+        Class encodableClass = encodable.getClass();
+
+        Class clazz = ( Class ) DEFAULT_ENCODERS.get( encodableClass );
+
+        if ( clazz == null )
+        {
+            throw new IOException( "Encoder unavailable for " + encodableClass );
+        }
+
+        EncoderFactory factory = null;
+
+        try
+        {
+            factory = ( EncoderFactory ) clazz.newInstance();
+        }
+        catch ( IllegalAccessException iae )
+        {
+            throw new IOException( "Error accessing encoder for " + encodableClass );
+        }
+        catch ( InstantiationException ie )
+        {
+            throw new IOException( "Error instantiating encoder for " + encodableClass );
+        }
+
+        Encoder encoder = factory.getEncoder();
+
+        return encoder.encode( encodable );
+    }
+
+
+    private Encodable decode( Class encodable, byte[] plainText ) throws IOException
+    {
+        Class clazz = ( Class ) DEFAULT_DECODERS.get( encodable );
+
+        if ( clazz == null )
+        {
+            throw new IOException( "Decoder unavailable for " + encodable );
+        }
+
+        DecoderFactory factory = null;
+
+        try
+        {
+            factory = ( DecoderFactory ) clazz.newInstance();
+        }
+        catch ( IllegalAccessException iae )
+        {
+            throw new IOException( "Error accessing decoder for " + encodable );
+        }
+        catch ( InstantiationException ie )
+        {
+            throw new IOException( "Error instantiating decoder for " + encodable );
+        }
+
+        Decoder decoder = factory.getDecoder();
+
+        return decoder.decode( plainText );
+    }
+
+
+    private EncryptionEngine getEngine( EncryptionKey key ) throws KerberosException
+    {
+        EncryptionType encryptionType = key.getKeyType();
+
+        Class clazz = ( Class ) DEFAULT_CIPHERS.get( encryptionType );
+
+        if ( clazz == null )
+        {
+            throw new KerberosException( ErrorType.KDC_ERR_ETYPE_NOSUPP );
+        }
+
+        try
+        {
+            return ( EncryptionEngine ) clazz.newInstance();
+        }
+        catch ( IllegalAccessException iae )
+        {
+            throw new KerberosException( ErrorType.KDC_ERR_ETYPE_NOSUPP, iae );
+        }
+        catch ( InstantiationException ie )
+        {
+            throw new KerberosException( ErrorType.KDC_ERR_ETYPE_NOSUPP, ie );
+        }
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/encryption/Des3CbcSha1KdEncryption.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/encryption/Des3CbcSha1KdEncryption.java
new file mode 100644
index 0000000..3064284
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/encryption/Des3CbcSha1KdEncryption.java
@@ -0,0 +1,307 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.crypto.encryption;
+
+
+import java.security.GeneralSecurityException;
+import java.security.spec.AlgorithmParameterSpec;
+import java.util.Arrays;
+
+import javax.crypto.Cipher;
+import javax.crypto.Mac;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.apache.directory.server.kerberos.shared.crypto.checksum.ChecksumEngine;
+import org.apache.directory.server.kerberos.shared.crypto.checksum.ChecksumType;
+import org.apache.directory.server.kerberos.shared.exceptions.ErrorType;
+import org.apache.directory.server.kerberos.shared.exceptions.KerberosException;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptedData;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class Des3CbcSha1KdEncryption extends EncryptionEngine implements ChecksumEngine
+{
+    private static final byte[] iv = new byte[]
+        { ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00,
+            ( byte ) 0x00 };
+
+
+    public EncryptionType getEncryptionType()
+    {
+        return EncryptionType.DES3_CBC_SHA1_KD;
+    }
+
+
+    public int getConfounderLength()
+    {
+        return 8;
+    }
+
+
+    public int getChecksumLength()
+    {
+        return 20;
+    }
+
+
+    public ChecksumType checksumType()
+    {
+        return ChecksumType.HMAC_SHA1_DES3_KD;
+    }
+
+
+    public byte[] calculateChecksum( byte[] data, byte[] key, KeyUsage usage )
+    {
+        byte[] Kc = deriveKey( key, getUsageKc( usage ), 64, 168 );
+
+        return processChecksum( data, Kc );
+    }
+
+
+    public byte[] calculateIntegrity( byte[] data, byte[] key, KeyUsage usage )
+    {
+        byte[] Ki = deriveKey( key, getUsageKi( usage ), 64, 168 );
+
+        return processChecksum( data, Ki );
+    }
+
+
+    public byte[] getDecryptedData( EncryptionKey key, EncryptedData data, KeyUsage usage ) throws KerberosException
+    {
+        byte[] Ke = deriveKey( key.getKeyValue(), getUsageKe( usage ), 64, 168 );
+
+        byte[] encryptedData = data.getCipher();
+
+        // extract the old checksum
+        byte[] oldChecksum = new byte[getChecksumLength()];
+        System
+            .arraycopy( encryptedData, encryptedData.length - getChecksumLength(), oldChecksum, 0, oldChecksum.length );
+
+        // remove trailing checksum
+        encryptedData = removeTrailingBytes( encryptedData, 0, getChecksumLength() );
+
+        // decrypt the data
+        byte[] decryptedData = decrypt( encryptedData, Ke );
+
+        // remove leading confounder
+        byte[] withoutConfounder = removeLeadingBytes( decryptedData, getConfounderLength(), 0 );
+
+        // calculate a new checksum
+        byte[] newChecksum = calculateIntegrity( decryptedData, key.getKeyValue(), usage );
+
+        // compare checksums
+        if ( !Arrays.equals( oldChecksum, newChecksum ) )
+        {
+            throw new KerberosException( ErrorType.KRB_AP_ERR_BAD_INTEGRITY );
+        }
+
+        return withoutConfounder;
+    }
+
+
+    public EncryptedData getEncryptedData( EncryptionKey key, byte[] plainText, KeyUsage usage )
+    {
+        byte[] Ke = deriveKey( key.getKeyValue(), getUsageKe( usage ), 64, 168 );
+
+        // build the ciphertext structure
+        byte[] conFounder = getRandomBytes( getConfounderLength() );
+        byte[] paddedPlainText = padString( plainText );
+        byte[] dataBytes = concatenateBytes( conFounder, paddedPlainText );
+        byte[] checksumBytes = calculateIntegrity( dataBytes, key.getKeyValue(), usage );
+
+        //byte[] encryptedData = encrypt( paddedDataBytes, key.getKeyValue() );
+        byte[] encryptedData = encrypt( dataBytes, Ke );
+
+        byte[] cipherText = concatenateBytes( encryptedData, checksumBytes );
+
+        return new EncryptedData( getEncryptionType(), key.getKeyVersion(), cipherText );
+    }
+
+
+    public byte[] encrypt( byte[] plainText, byte[] keyBytes )
+    {
+        return processCipher( true, plainText, keyBytes );
+    }
+
+
+    public byte[] decrypt( byte[] cipherText, byte[] keyBytes )
+    {
+        return processCipher( false, cipherText, keyBytes );
+    }
+
+
+    /**
+     * Derived Key = DK(Base Key, Well-Known Constant)
+     * DK(Key, Constant) = random-to-key(DR(Key, Constant))
+     * DR(Key, Constant) = k-truncate(E(Key, Constant, initial-cipher-state))
+     */
+    protected byte[] deriveKey( byte[] baseKey, byte[] usage, int n, int k )
+    {
+        byte[] result = deriveRandom( baseKey, usage, n, k );
+        result = randomToKey( result );
+
+        return result;
+    }
+
+
+    protected byte[] randomToKey( byte[] seed )
+    {
+        int kBytes = 24;
+        byte[] result = new byte[kBytes];
+
+        byte[] fillingKey = new byte[0];
+
+        int pos = 0;
+
+        for ( int i = 0; i < kBytes; i++ )
+        {
+            if ( pos < fillingKey.length )
+            {
+                result[i] = fillingKey[pos];
+                pos++;
+            }
+            else
+            {
+                fillingKey = getBitGroup( seed, i / 8 );
+                fillingKey = setParity( fillingKey );
+                pos = 0;
+                result[i] = fillingKey[pos];
+                pos++;
+            }
+        }
+
+        return result;
+    }
+
+
+    protected byte[] getBitGroup( byte[] seed, int group )
+    {
+        int srcPos = group * 7;
+
+        byte[] result = new byte[7];
+
+        System.arraycopy( seed, srcPos, result, 0, 7 );
+
+        return result;
+    }
+
+
+    protected byte[] setParity( byte[] in )
+    {
+        byte[] expandedIn = new byte[8];
+
+        System.arraycopy( in, 0, expandedIn, 0, in.length );
+
+        setBit( expandedIn, 62, getBit( in, 7 ) );
+        setBit( expandedIn, 61, getBit( in, 15 ) );
+        setBit( expandedIn, 60, getBit( in, 23 ) );
+        setBit( expandedIn, 59, getBit( in, 31 ) );
+        setBit( expandedIn, 58, getBit( in, 39 ) );
+        setBit( expandedIn, 57, getBit( in, 47 ) );
+        setBit( expandedIn, 56, getBit( in, 55 ) );
+
+        byte[] out = new byte[8];
+
+        int bitCount = 0;
+        int index = 0;
+
+        for ( int i = 0; i < 64; i++ )
+        {
+            if ( ( i + 1 ) % 8 == 0 )
+            {
+                if ( bitCount % 2 == 0 )
+                {
+                    setBit( out, i, 1 );
+                }
+
+                index++;
+                bitCount = 0;
+            }
+            else
+            {
+                int val = getBit( expandedIn, index );
+                boolean bit = val > 0;
+
+                if ( bit )
+                {
+                    setBit( out, i, val );
+                    bitCount++;
+                }
+
+                index++;
+            }
+        }
+
+        return out;
+    }
+
+
+    private byte[] processCipher( boolean isEncrypt, byte[] data, byte[] keyBytes )
+    {
+        try
+        {
+            Cipher cipher = Cipher.getInstance( "DESede/CBC/NoPadding" );
+            SecretKey key = new SecretKeySpec( keyBytes, "DESede" );
+
+            AlgorithmParameterSpec paramSpec = new IvParameterSpec( iv );
+
+            if ( isEncrypt )
+            {
+                cipher.init( Cipher.ENCRYPT_MODE, key, paramSpec );
+            }
+            else
+            {
+                cipher.init( Cipher.DECRYPT_MODE, key, paramSpec );
+            }
+
+            return cipher.doFinal( data );
+        }
+        catch ( GeneralSecurityException nsae )
+        {
+            nsae.printStackTrace();
+            return null;
+        }
+    }
+
+
+    private byte[] processChecksum( byte[] data, byte[] key )
+    {
+        try
+        {
+            SecretKey sk = new SecretKeySpec( key, "DESede" );
+
+            Mac mac = Mac.getInstance( "HmacSHA1" );
+            mac.init( sk );
+
+            return mac.doFinal( data );
+        }
+        catch ( GeneralSecurityException nsae )
+        {
+            nsae.printStackTrace();
+            return null;
+        }
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/encryption/DesCbcCrcEncryption.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/encryption/DesCbcCrcEncryption.java
new file mode 100644
index 0000000..1682eda
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/encryption/DesCbcCrcEncryption.java
@@ -0,0 +1,180 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.kerberos.shared.crypto.encryption;
+
+
+import java.security.GeneralSecurityException;
+import java.security.spec.AlgorithmParameterSpec;
+import java.util.Arrays;
+import java.util.zip.CRC32;
+
+import javax.crypto.Cipher;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.apache.directory.server.kerberos.shared.exceptions.ErrorType;
+import org.apache.directory.server.kerberos.shared.exceptions.KerberosException;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptedData;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class DesCbcCrcEncryption extends EncryptionEngine
+{
+    private static final byte[] iv = new byte[]
+        { ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00,
+            ( byte ) 0x00 };
+
+
+    public EncryptionType getEncryptionType()
+    {
+        return EncryptionType.DES_CBC_CRC;
+    }
+
+
+    public int getConfounderLength()
+    {
+        return 8;
+    }
+
+
+    public int getChecksumLength()
+    {
+        return 4;
+    }
+
+
+    public byte[] calculateIntegrity( byte[] data, byte[] key, KeyUsage usage )
+    {
+        CRC32 crc32 = new CRC32();
+        crc32.update( data );
+
+        return int2octet( ( int ) crc32.getValue() );
+    }
+
+
+    private byte[] int2octet( int value )
+    {
+        byte[] bytes = new byte[4];
+        int i, shift;
+
+        for ( i = 0, shift = 24; i < 4; i++, shift -= 8 )
+        {
+            bytes[i] = ( byte ) ( 0xFF & ( value >> shift ) );
+        }
+
+        return bytes;
+    }
+
+
+    public byte[] getDecryptedData( EncryptionKey key, EncryptedData data, KeyUsage usage ) throws KerberosException
+    {
+        // decrypt the data
+        byte[] decryptedData = decrypt( data.getCipher(), key.getKeyValue() );
+
+        // extract the old checksum
+        byte[] oldChecksum = new byte[getChecksumLength()];
+        System.arraycopy( decryptedData, getConfounderLength(), oldChecksum, 0, oldChecksum.length );
+
+        // zero out the old checksum in the cipher text
+        for ( int i = getConfounderLength(); i < getConfounderLength() + getChecksumLength(); i++ )
+        {
+            decryptedData[i] = 0;
+        }
+
+        // calculate a new checksum
+        byte[] newChecksum = calculateIntegrity( decryptedData, key.getKeyValue(), usage );
+
+        // compare checksums
+        if ( !Arrays.equals( oldChecksum, newChecksum ) )
+        {
+            throw new KerberosException( ErrorType.KRB_AP_ERR_BAD_INTEGRITY );
+        }
+
+        // remove leading confounder and checksum
+        return removeLeadingBytes( decryptedData, getConfounderLength(), getChecksumLength() );
+    }
+
+
+    public EncryptedData getEncryptedData( EncryptionKey key, byte[] plainText, KeyUsage usage )
+    {
+        // build the ciphertext structure
+        byte[] conFounder = getRandomBytes( getConfounderLength() );
+        byte[] zeroedChecksum = new byte[getChecksumLength()];
+        byte[] dataBytes = concatenateBytes( conFounder, concatenateBytes( zeroedChecksum, plainText ) );
+        byte[] paddedDataBytes = padString( dataBytes );
+        byte[] checksumBytes = calculateIntegrity( paddedDataBytes, null, usage );
+
+        // lay the checksum into the ciphertext
+        for ( int i = getConfounderLength(); i < getConfounderLength() + getChecksumLength(); i++ )
+        {
+            paddedDataBytes[i] = checksumBytes[i - getConfounderLength()];
+        }
+
+        byte[] encryptedData = encrypt( paddedDataBytes, key.getKeyValue() );
+
+        return new EncryptedData( getEncryptionType(), key.getKeyVersion(), encryptedData );
+    }
+
+
+    public byte[] encrypt( byte[] plainText, byte[] keyBytes )
+    {
+        return processCipher( true, plainText, keyBytes );
+    }
+
+
+    public byte[] decrypt( byte[] cipherText, byte[] keyBytes )
+    {
+        return processCipher( false, cipherText, keyBytes );
+    }
+
+
+    private byte[] processCipher( boolean isEncrypt, byte[] data, byte[] keyBytes )
+    {
+        try
+        {
+            Cipher cipher = Cipher.getInstance( "DES/CBC/NoPadding" );
+            SecretKey key = new SecretKeySpec( keyBytes, "DES" );
+
+            AlgorithmParameterSpec paramSpec = new IvParameterSpec( iv );
+
+            if ( isEncrypt )
+            {
+                cipher.init( Cipher.ENCRYPT_MODE, key, paramSpec );
+            }
+            else
+            {
+                cipher.init( Cipher.DECRYPT_MODE, key, paramSpec );
+            }
+
+            return cipher.doFinal( data );
+        }
+        catch ( GeneralSecurityException nsae )
+        {
+            nsae.printStackTrace();
+            return null;
+        }
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/encryption/DesCbcMd5Encryption.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/encryption/DesCbcMd5Encryption.java
new file mode 100644
index 0000000..52895f7
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/encryption/DesCbcMd5Encryption.java
@@ -0,0 +1,172 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.crypto.encryption;
+
+
+import java.security.GeneralSecurityException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.spec.AlgorithmParameterSpec;
+import java.util.Arrays;
+
+import javax.crypto.Cipher;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.apache.directory.server.kerberos.shared.exceptions.ErrorType;
+import org.apache.directory.server.kerberos.shared.exceptions.KerberosException;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptedData;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+import org.apache.directory.shared.ldap.constants.LdapSecurityConstants;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+class DesCbcMd5Encryption extends EncryptionEngine
+{
+    private static final byte[] iv = new byte[]
+        { ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00,
+            ( byte ) 0x00 };
+
+
+    public EncryptionType getEncryptionType()
+    {
+        return EncryptionType.DES_CBC_MD5;
+    }
+
+
+    public int getConfounderLength()
+    {
+        return 8;
+    }
+
+
+    public int getChecksumLength()
+    {
+        return 16;
+    }
+
+
+    public byte[] calculateIntegrity( byte[] data, byte[] key, KeyUsage usage )
+    {
+        try
+        {
+            MessageDigest digester = MessageDigest.getInstance( LdapSecurityConstants.HASH_METHOD_MD5.getName() );
+            return digester.digest( data );
+        }
+        catch ( NoSuchAlgorithmException nsae )
+        {
+            return null;
+        }
+    }
+
+
+    public byte[] getDecryptedData( EncryptionKey key, EncryptedData data, KeyUsage usage ) throws KerberosException
+    {
+        // decrypt the data
+        byte[] decryptedData = decrypt( data.getCipher(), key.getKeyValue() );
+
+        // extract the old checksum
+        byte[] oldChecksum = new byte[getChecksumLength()];
+        System.arraycopy( decryptedData, getConfounderLength(), oldChecksum, 0, oldChecksum.length );
+
+        // zero out the old checksum in the cipher text
+        for ( int i = getConfounderLength(); i < getConfounderLength() + getChecksumLength(); i++ )
+        {
+            decryptedData[i] = 0;
+        }
+
+        // calculate a new checksum
+        byte[] newChecksum = calculateIntegrity( decryptedData, key.getKeyValue(), usage );
+
+        // compare checksums
+        if ( !Arrays.equals( oldChecksum, newChecksum ) )
+        {
+            throw new KerberosException( ErrorType.KRB_AP_ERR_BAD_INTEGRITY );
+        }
+
+        // remove leading confounder and checksum
+        return removeLeadingBytes( decryptedData, getConfounderLength(), getChecksumLength() );
+    }
+
+
+    public EncryptedData getEncryptedData( EncryptionKey key, byte[] plainText, KeyUsage usage )
+    {
+        // build the ciphertext structure
+        byte[] conFounder = getRandomBytes( getConfounderLength() );
+        byte[] zeroedChecksum = new byte[getChecksumLength()];
+        byte[] dataBytes = concatenateBytes( conFounder, concatenateBytes( zeroedChecksum, plainText ) );
+        byte[] paddedDataBytes = padString( dataBytes );
+        byte[] checksumBytes = calculateIntegrity( paddedDataBytes, null, usage );
+
+        // lay the checksum into the ciphertext
+        for ( int i = getConfounderLength(); i < getConfounderLength() + getChecksumLength(); i++ )
+        {
+            paddedDataBytes[i] = checksumBytes[i - getConfounderLength()];
+        }
+
+        byte[] encryptedData = encrypt( paddedDataBytes, key.getKeyValue() );
+
+        return new EncryptedData( getEncryptionType(), key.getKeyVersion(), encryptedData );
+    }
+
+
+    public byte[] encrypt( byte[] plainText, byte[] keyBytes )
+    {
+        return processCipher( true, plainText, keyBytes );
+    }
+
+
+    public byte[] decrypt( byte[] cipherText, byte[] keyBytes )
+    {
+        return processCipher( false, cipherText, keyBytes );
+    }
+
+
+    private byte[] processCipher( boolean isEncrypt, byte[] data, byte[] keyBytes )
+    {
+        try
+        {
+            Cipher cipher = Cipher.getInstance( "DES/CBC/NoPadding" );
+            SecretKey key = new SecretKeySpec( keyBytes, "DES" );
+
+            AlgorithmParameterSpec paramSpec = new IvParameterSpec( iv );
+
+            if ( isEncrypt )
+            {
+                cipher.init( Cipher.ENCRYPT_MODE, key, paramSpec );
+            }
+            else
+            {
+                cipher.init( Cipher.DECRYPT_MODE, key, paramSpec );
+            }
+
+            return cipher.doFinal( data );
+        }
+        catch ( GeneralSecurityException nsae )
+        {
+            nsae.printStackTrace();
+            return null;
+        }
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/encryption/DesStringToKey.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/encryption/DesStringToKey.java
new file mode 100644
index 0000000..44b46bc
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/encryption/DesStringToKey.java
@@ -0,0 +1,372 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.crypto.encryption;
+
+
+import java.io.UnsupportedEncodingException;
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.security.spec.AlgorithmParameterSpec;
+
+import javax.crypto.Cipher;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.DESKeySpec;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+
+/**
+ * An implementation of the DES string-to-key function as originally described
+ * in RFC 1510, "The Kerberos Network Authentication Service (V5)," and clarified
+ * in RFC 3961, "Encryption and Checksum Specifications for Kerberos 5."
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 502338 $, $Date: 2007-02-01 11:59:43 -0800 (Thu, 01 Feb 2007) $
+ */
+public class DesStringToKey
+{
+    /**
+     * Returns a DES symmetric key for the given passphrase.
+     *
+     * @param passPhrase The passphrase to derive a symmetric DES key from.
+     * @return The derived symmetric DES key.
+     */
+    public byte[] getKey( String passPhrase )
+    {
+        return generateKey( passPhrase );
+    }
+
+
+    /**
+     * Returns a DES symmetric key for the given input String components,
+     * which will be concatenated in the order described in RFC's 1510 and 3961,
+     * namely password+realm+username.
+     *
+     * @param password The password.
+     * @param realmName The name of the realm.
+     * @param userName The username.
+     * @return The derived symmetric DES key.
+     */
+    public byte[] getKey( String password, String realmName, String userName )
+    {
+        return generateKey( password + realmName + userName );
+    }
+
+
+    /**
+     * Returns a DES symmetric key for the given input String.
+     *
+     * @param passPhrase The passphrase.
+     * @return The DES key.
+     * @throws Exception
+     */
+    protected byte[] generateKey( String passPhrase )
+    {
+        byte encodedByteArray[] = characterEncodeString( passPhrase );
+
+        byte paddedByteArray[] = padString( encodedByteArray );
+
+        byte[] secretKey = fanFold( paddedByteArray );
+
+        secretKey = setParity( secretKey );
+        secretKey = getStrongKey( secretKey );
+        secretKey = calculateChecksum( paddedByteArray, secretKey );
+        secretKey = setParity( secretKey );
+        secretKey = getStrongKey( secretKey );
+
+        return secretKey;
+    }
+
+
+    /**
+     * Set odd parity on an eight-byte array.
+     *
+     * @param in The byte array to set parity on.
+     * @return The parity-adjusted byte array.
+     */
+    protected byte[] setParity( byte[] in )
+    {
+        byte[] out = new byte[8];
+
+        int bitCount = 0;
+        int index = 0;
+
+        for ( int i = 0; i < 64; i++ )
+        {
+            if ( ( i + 1 ) % 8 == 0 )
+            {
+                if ( bitCount % 2 == 0 )
+                {
+                    setBit( out, i, 1 );
+                }
+
+                index++;
+                bitCount = 0;
+            }
+            else
+            {
+                int val = getBit( in, index );
+                boolean bit = val > 0;
+
+                if ( bit )
+                {
+                    setBit( out, i, val );
+                    bitCount++;
+                }
+
+                index++;
+            }
+        }
+
+        return out;
+    }
+
+
+    /**
+     * Gets a bit at a given position.
+     *
+     * @param data
+     * @param pos
+     * @return The value of the bit.
+     */
+    protected int getBit( byte[] data, int pos )
+    {
+        int posByte = pos / 8;
+        int posBit = pos % 8;
+
+        byte valByte = data[posByte];
+        int valInt = valByte >> ( 8 - ( posBit + 1 ) ) & 0x0001;
+        return valInt;
+    }
+
+
+    /**
+     * Sets a bit at a given position.
+     *
+     * @param data
+     * @param pos
+     * @param val
+     */
+    protected void setBit( byte[] data, int pos, int val )
+    {
+        int posByte = pos / 8;
+        int posBit = pos % 8;
+        byte oldByte = data[posByte];
+        oldByte = ( byte ) ( ( ( 0xFF7F >> posBit ) & oldByte ) & 0x00FF );
+        byte newByte = ( byte ) ( ( val << ( 8 - ( posBit + 1 ) ) ) | oldByte );
+        data[posByte] = newByte;
+    }
+
+
+    /**
+     * "The top bit of each octet (always zero if the password is plain
+     * ASCII, as was assumed when the original specification was written) is
+     * discarded, and the remaining seven bits of each octet form a
+     * bitstring.  This is then fan-folded and eXclusive-ORed with itself to
+     * produce a 56-bit string.  An eight-octet key is formed from this
+     * string, each octet using seven bits from the bitstring, leaving the
+     * least significant bit unassigned."
+     *
+     * @param paddedByteArray The padded byte array.
+     * @return The fan-folded intermediate DES key.
+     */
+    protected byte[] fanFold( byte[] paddedByteArray )
+    {
+        byte secretKey[] = new byte[8];
+
+        int div = paddedByteArray.length / 8;
+
+        for ( int ii = 0; ii < div; ii++ )
+        {
+            byte blockValue1[] = new byte[8];
+            System.arraycopy( paddedByteArray, ii * 8, blockValue1, 0, 8 );
+
+            if ( ii % 2 == 1 )
+            {
+                byte tempbyte1 = 0;
+                byte tempbyte2 = 0;
+                byte blockValue2[] = new byte[8];
+
+                for ( int jj = 0; jj < 8; jj++ )
+                {
+                    tempbyte2 = 0;
+
+                    for ( int kk = 0; kk < 4; kk++ )
+                    {
+                        tempbyte2 = ( byte ) ( ( 1 << ( 7 - kk ) ) & 0xff );
+                        tempbyte1 |= ( blockValue1[jj] & tempbyte2 ) >>> ( 7 - 2 * kk );
+                        tempbyte2 = 0;
+                    }
+
+                    for ( int kk = 4; kk < 8; kk++ )
+                    {
+                        tempbyte2 = ( byte ) ( ( 1 << ( 7 - kk ) ) & 0xff );
+                        tempbyte1 |= ( blockValue1[jj] & tempbyte2 ) << ( 2 * kk - 7 );
+                        tempbyte2 = 0;
+                    }
+
+                    blockValue2[7 - jj] = tempbyte1;
+                    tempbyte1 = 0;
+                }
+
+                for ( int jj = 0; jj < 8; jj++ )
+                {
+                    blockValue2[jj] = ( byte ) ( ( ( blockValue2[jj] & 0xff ) >>> 1 ) & 0xff );
+                }
+
+                System.arraycopy( blockValue2, 0, blockValue1, 0, blockValue2.length );
+            }
+
+            for ( int jj = 0; jj < 8; jj++ )
+            {
+                blockValue1[jj] = ( byte ) ( ( ( blockValue1[jj] & 0xff ) << 1 ) & 0xff );
+            }
+
+            // ... eXclusive-ORed with itself to form an 8-byte DES key
+            for ( int jj = 0; jj < 8; jj++ )
+            {
+                secretKey[jj] ^= blockValue1[jj];
+            }
+        }
+
+        return secretKey;
+    }
+
+
+    /**
+     * Calculates the checksum as described in "String or Random-Data to
+     * Key Transformation."  An intermediate key is used to generate a DES CBC
+     * "checksum" on the initial passphrase+salt.  The encryption key is also
+     * used as the IV.  The final eight-byte block is returned as the "checksum."
+     *
+     * @param data The data to encrypt.
+     * @param keyBytes The bytes of the intermediate key.
+     * @return The final eight-byte block as the checksum.
+     */
+    protected byte[] calculateChecksum( byte[] data, byte[] keyBytes )
+    {
+        try
+        {
+            Cipher cipher = Cipher.getInstance( "DES/CBC/NoPadding" );
+            SecretKey key = new SecretKeySpec( keyBytes, "DES" );
+
+            AlgorithmParameterSpec paramSpec = new IvParameterSpec( keyBytes );
+
+            cipher.init( Cipher.ENCRYPT_MODE, key, paramSpec );
+
+            byte[] result = cipher.doFinal( data );
+
+            byte[] checksum = new byte[8];
+            System.arraycopy( result, result.length - 8, checksum, 0, 8 );
+
+            return checksum;
+        }
+        catch ( GeneralSecurityException nsae )
+        {
+            nsae.printStackTrace();
+            return null;
+        }
+    }
+
+
+    /**
+     * If the secret key is weak, correct by exclusive OR'ing
+     * with the constant 0xF0.
+     * 
+     * @param secretKey The key to correct, if necessary.
+     * @return The corrected key.
+     */
+    protected byte[] getStrongKey( byte[] secretKey )
+    {
+        try
+        {
+            if ( DESKeySpec.isWeak( secretKey, 0 ) )
+            {
+                secretKey[7] ^= 0xf0;
+            }
+        }
+        catch ( InvalidKeyException ike )
+        {
+            return new byte[8];
+        }
+
+        return secretKey;
+    }
+
+
+    /**
+     * Encodes string with UTF-8 encoding.
+     *
+     * @param string The String to encode.
+     * @return The encoded String.
+     */
+    protected byte[] characterEncodeString( String string )
+    {
+        byte encodedByteArray[] = new byte[string.length()];
+
+        try
+        {
+            encodedByteArray = string.getBytes( "UTF-8" );
+        }
+        catch ( UnsupportedEncodingException ue )
+        {
+            // Shouldn't ever happen for UTF-8.
+        }
+
+        return encodedByteArray;
+    }
+
+
+    /**
+     * Add padding to make an exact multiple of 8 bytes.
+     *
+     * @param encodedString
+     * @return The padded byte array.
+     */
+    protected byte[] padString( byte encodedString[] )
+    {
+        int length;
+
+        if ( encodedString.length < 8 )
+        {
+            length = encodedString.length;
+        }
+        else
+        {
+            length = encodedString.length % 8;
+        }
+
+        if ( length == 0 )
+        {
+            return encodedString;
+        }
+
+        byte paddedByteArray[] = new byte[( 8 - length ) + encodedString.length];
+
+        for ( int ii = paddedByteArray.length - 1; ii > encodedString.length - 1; ii-- )
+        {
+            paddedByteArray[ii] = 0;
+        }
+
+        System.arraycopy( encodedString, 0, paddedByteArray, 0, encodedString.length );
+
+        return paddedByteArray;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/encryption/EncryptionEngine.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/encryption/EncryptionEngine.java
new file mode 100644
index 0000000..025436c
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/encryption/EncryptionEngine.java
@@ -0,0 +1,260 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.crypto.encryption;
+
+
+import java.security.SecureRandom;
+
+import org.apache.directory.server.kerberos.shared.exceptions.KerberosException;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptedData;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public abstract class EncryptionEngine
+{
+    private static final SecureRandom random = new SecureRandom();
+
+
+    protected abstract byte[] getDecryptedData( EncryptionKey key, EncryptedData data, KeyUsage usage )
+        throws KerberosException;
+
+
+    protected abstract EncryptedData getEncryptedData( EncryptionKey key, byte[] plainText, KeyUsage usage );
+
+
+    protected abstract EncryptionType getEncryptionType();
+
+
+    protected abstract int getConfounderLength();
+
+
+    protected abstract int getChecksumLength();
+
+
+    protected abstract byte[] encrypt( byte[] plainText, byte[] key );
+
+
+    protected abstract byte[] decrypt( byte[] cipherText, byte[] key );
+
+
+    protected abstract byte[] calculateIntegrity( byte[] plainText, byte[] key, KeyUsage usage );
+
+
+    protected byte[] deriveRandom( byte[] key, byte[] usage, int n, int k )
+    {
+        byte[] nFoldedUsage = NFold.nFold( n, usage );
+
+        int kBytes = k / 8;
+        byte[] result = new byte[kBytes];
+
+        byte[] fillingKey = encrypt( nFoldedUsage, key );
+
+        int pos = 0;
+
+        for ( int i = 0; i < kBytes; i++ )
+        {
+            if ( pos < fillingKey.length )
+            {
+                result[i] = fillingKey[pos];
+                pos++;
+            }
+            else
+            {
+                fillingKey = encrypt( fillingKey, key );
+                pos = 0;
+                result[i] = fillingKey[pos];
+                pos++;
+            }
+        }
+
+        return result;
+    }
+
+
+    // Encryption
+    protected byte[] getRandomBytes( int size )
+    {
+        byte[] bytes = new byte[size];
+
+        // SecureRandom.nextBytes is already synchronized
+        random.nextBytes( bytes );
+
+        return bytes;
+    }
+
+
+    // Encryption
+    protected byte[] padString( byte encodedString[] )
+    {
+        int x;
+        if ( encodedString.length < 8 )
+        {
+            x = encodedString.length;
+        }
+        else
+        {
+            x = encodedString.length % 8;
+        }
+
+        if ( x == 0 )
+        {
+            return encodedString;
+        }
+
+        byte paddedByteArray[] = new byte[( 8 - x ) + encodedString.length];
+
+        for ( int y = paddedByteArray.length - 1; y > encodedString.length - 1; y-- )
+        {
+            paddedByteArray[y] = 0;
+        }
+
+        System.arraycopy( encodedString, 0, paddedByteArray, 0, encodedString.length );
+
+        return paddedByteArray;
+    }
+
+
+    // Encryption
+    protected byte[] concatenateBytes( byte[] array1, byte[] array2 )
+    {
+        byte concatenatedBytes[] = new byte[array1.length + array2.length];
+
+        for ( int i = 0; i < array1.length; i++ )
+        {
+            concatenatedBytes[i] = array1[i];
+        }
+
+        for ( int j = array1.length; j < concatenatedBytes.length; j++ )
+        {
+            concatenatedBytes[j] = array2[j - array1.length];
+        }
+
+        return concatenatedBytes;
+    }
+
+
+    // Decryption
+    protected byte[] removeLeadingBytes( byte[] array, int confounder, int checksum )
+    {
+        byte lessBytes[] = new byte[array.length - confounder - checksum];
+
+        int j = 0;
+        for ( int i = confounder + checksum; i < array.length; i++ )
+        {
+            lessBytes[j] = array[i];
+            j++;
+        }
+
+        return lessBytes;
+    }
+
+
+    protected byte[] removeTrailingBytes( byte[] array, int confounder, int checksum )
+    {
+        byte lessBytes[] = new byte[array.length - confounder - checksum];
+
+        int j = 0;
+        for ( int i = 0; i < array.length - confounder - checksum; i++ )
+        {
+            lessBytes[j] = array[i];
+            j++;
+        }
+
+        return lessBytes;
+    }
+
+
+    protected int getBit( byte[] data, int pos )
+    {
+        int posByte = pos / 8;
+        int posBit = pos % 8;
+
+        byte valByte = data[posByte];
+        int valInt = valByte >> ( 8 - ( posBit + 1 ) ) & 0x0001;
+        return valInt;
+    }
+
+
+    protected void setBit( byte[] data, int pos, int val )
+    {
+        int posByte = pos / 8;
+        int posBit = pos % 8;
+        byte oldByte = data[posByte];
+        oldByte = ( byte ) ( ( ( 0xFF7F >> posBit ) & oldByte ) & 0x00FF );
+        byte newByte = ( byte ) ( ( val << ( 8 - ( posBit + 1 ) ) ) | oldByte );
+        data[posByte] = newByte;
+    }
+
+
+    /**
+     * The "well-known constant" used for the DK function is the key
+     * usage number, expressed as four octets in big-endian order,
+     * followed by one octet indicated below.
+     * 
+     *  Kc = DK(base-key, usage | 0x99);
+     */
+    protected byte[] getUsageKc( KeyUsage usage )
+    {
+        return getUsage( usage.getOrdinal(), ( byte ) 0x99 );
+    }
+
+
+    /**
+     * The "well-known constant" used for the DK function is the key
+     * usage number, expressed as four octets in big-endian order,
+     * followed by one octet indicated below.
+     * 
+     *  Ke = DK(base-key, usage | 0xAA);
+     */
+    protected byte[] getUsageKe( KeyUsage usage )
+    {
+        return getUsage( usage.getOrdinal(), ( byte ) 0xAA );
+    }
+
+
+    /**
+     * The "well-known constant" used for the DK function is the key
+     * usage number, expressed as four octets in big-endian order,
+     * followed by one octet indicated below.
+     * 
+     *  Ki = DK(base-key, usage | 0x55);
+     */
+    protected byte[] getUsageKi( KeyUsage usage )
+    {
+        return getUsage( usage.getOrdinal(), ( byte ) 0x55 );
+    }
+
+
+    private byte[] getUsage( int usage, byte constant )
+    {
+        byte[] bytes = new byte[5];
+        bytes[0] = ( byte ) ( ( usage >>> 24 ) & 0x000000FF );
+        bytes[1] = ( byte ) ( ( usage >> 16 ) & 0x000000FF );
+        bytes[2] = ( byte ) ( ( usage >> 8 ) & 0x000000FF );
+        bytes[3] = ( byte ) ( usage & 0x00FF );
+        bytes[4] = constant;
+
+        return bytes;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/encryption/EncryptionType.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/encryption/EncryptionType.java
new file mode 100644
index 0000000..b6fe192
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/encryption/EncryptionType.java
@@ -0,0 +1,337 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.crypto.encryption;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+
+/**
+ * A type-safe enumeration of Kerberos encryption types.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public enum EncryptionType
+{
+    /**
+     * The "unknown" encryption type.
+     */
+    UNKNOWN( -1 ),
+
+    /**
+     * The "null" encryption type.
+     */
+    NULL( 0 ),
+
+    /**
+     * The des-cbc-crc encryption type.
+     */
+    DES_CBC_CRC( 1 ),
+
+    /**
+     * The des-cbc-md4 encryption type.
+     */
+    DES_CBC_MD4( 2 ),
+
+    /**
+     * The des-cbc-md5 encryption type.
+     */
+    DES_CBC_MD5( 3 ),
+
+    /**
+     * The reserved (4) encryption type.
+     */
+    RESERVED4( 4 ),
+
+    /**
+     * The des3-cbc-md5 encryption type.
+     */
+    DES3_CBC_MD5( 5 ),
+
+    /**
+     * The reserved (6) encryption type.
+     */
+    RESERVED6( 6 ),
+
+    /**
+     * The des3-cbc-sha1 encryption type.
+     */
+    DES3_CBC_SHA1( 7 ),
+
+    /**
+     * The dsaWithSHA1-CmsOID encryption type.
+     */
+    DSAWITHSHA1_CMSOID( 9 ),
+
+    /**
+     * The md5WithRSAEncryption-CmsOID encryption type.
+     */
+    MD5WITHRSAENCRYPTION_CMSOID( 10 ),
+
+    /**
+     * The sha1WithRSAEncryption-CmsOID encryption type.
+     */
+    SHA1WITHRSAENCRYPTION_CMSOID( 11 ),
+
+    /**
+     * The rc2CBC-EnvOID encryption type.
+     */
+    RC2CBC_ENVOID( 12 ),
+
+    /**
+     * The rsaEncryption-EnvOID encryption type.
+     */
+    RSAENCRYPTION_ENVOID( 13 ),
+
+    /**
+     * The rsaES-OAEP-ENV-OID encryption type.
+     */
+    RSAES_OAEP_ENV_OID( 14 ),
+
+    /**
+     * The des-ede3-cbc-Env-OID encryption type.
+     */
+    DES_EDE3_CBC_ENV_OID( 15 ),
+
+    /**
+     * The des3-cbc-sha1-kd encryption type.
+     */
+    DES3_CBC_SHA1_KD( 16 ),
+
+    /**
+     * The aes128-cts-hmac-sha1-96 encryption type.
+     */
+    AES128_CTS_HMAC_SHA1_96( 17 ),
+
+    /**
+     * The aes256-cts-hmac-sha1-96 encryption type.
+     */
+    AES256_CTS_HMAC_SHA1_96( 18 ),
+
+    /**
+     * The rc4-hmac encryption type.
+     */
+    RC4_HMAC( 23 ),
+
+    /**
+     * The rc4-hmac-exp encryption type.
+     */
+    RC4_HMAC_EXP( 24 ),
+
+    /**
+     * The subkey-keymaterial encryption type.
+     */
+    SUBKEY_KEYMATERIAL( 65 ),
+
+    /**
+     * The rc4-md4 encryption type.
+     */
+    RC4_MD4( -128 ),
+
+    /**
+     * The c4-hmac-old encryption type.
+     */
+    RC4_HMAC_OLD( -133 ),
+
+    /**
+     * The rc4-hmac-old-exp encryption type.
+     */
+    RC4_HMAC_OLD_EXP( -135 );
+
+    /**
+     * The value/code for the encryption type.
+     */
+    private final int ordinal;
+
+    /** A map containing all the values */
+    private static Map<String, EncryptionType> encryptionTypes = new HashMap<String, EncryptionType>();
+    
+    /** Initialization of the previous map */
+    static
+    {
+        encryptionTypes.put( "null", NULL );
+        encryptionTypes.put( "des-cbc-crc", DES_CBC_CRC ); 
+        encryptionTypes.put( "des-cbc-md4", DES_CBC_MD4 );          
+        encryptionTypes.put( "des-cbc-md5", DES_CBC_MD5 );          
+        encryptionTypes.put( "[reserved]", RESERVED4 );         
+        encryptionTypes.put( "des3-cbc-md5", DES3_CBC_MD5 );            
+        encryptionTypes.put( "[reserved]", RESERVED6 );         
+        encryptionTypes.put( "des3-cbc-sha1", DES3_CBC_SHA1 );          
+        encryptionTypes.put( "dsaWithSHA1-CmsOID", DSAWITHSHA1_CMSOID );            
+        encryptionTypes.put( "md5WithRSAEncryption-CmsOID", MD5WITHRSAENCRYPTION_CMSOID );          
+        encryptionTypes.put( "sha1WithRSAEncryption-CmsOID", SHA1WITHRSAENCRYPTION_CMSOID );            
+        encryptionTypes.put( "rc2CBC-EnvOID", RC2CBC_ENVOID );          
+        encryptionTypes.put( "rsaEncryption-EnvOID", RSAENCRYPTION_ENVOID );            
+        encryptionTypes.put( "rsaES-OAEP-ENV-OID", RSAES_OAEP_ENV_OID );        
+        encryptionTypes.put( "des-ede3-cbc-Env-OID", DES_EDE3_CBC_ENV_OID );            
+        encryptionTypes.put( "des3-cbc-sha1-kd", DES3_CBC_SHA1_KD );        
+        encryptionTypes.put( "aes128-cts-hmac-sha1-96", AES128_CTS_HMAC_SHA1_96 );          
+        encryptionTypes.put( "aes256-cts-hmac-sha1-96", AES256_CTS_HMAC_SHA1_96 );          
+        encryptionTypes.put( "rc4-hmac", RC4_HMAC );            
+        encryptionTypes.put( "rc4-hmac-exp", RC4_HMAC_EXP );            
+        encryptionTypes.put( "subkey-keymaterial", SUBKEY_KEYMATERIAL );            
+        encryptionTypes.put( "rc4-md4", RC4_MD4 );      
+        encryptionTypes.put( "rc4-hmac-old", RC4_HMAC_OLD );            
+        encryptionTypes.put( "rc4-hmac-old-exp", RC4_HMAC_OLD_EXP );            
+        encryptionTypes.put( "UNKNOWN", UNKNOWN );
+    }
+
+    /**
+     * Private constructor prevents construction outside of this class.
+     */
+    private EncryptionType( int ordinal )
+    {
+        this.ordinal = ordinal;
+    }
+
+    
+    /**
+     * Get all the encryption types
+     *
+     * @return A set of encryption types.
+     */
+    public static Collection<EncryptionType> getEncryptionTypes()
+    {
+        return encryptionTypes.values();
+    }
+
+    /**
+     * Returns the encryption type when specified by its ordinal.
+     *
+     * @param type
+     * @return The encryption type.
+     */
+    public static EncryptionType getTypeByOrdinal( int type )
+    {
+        switch ( type )
+        {
+            case 0 : return NULL; 
+            case 1 : return DES_CBC_CRC; 
+            case 2 : return DES_CBC_MD4; 
+            case 3 : return DES_CBC_MD5; 
+            case 4 : return RESERVED4; 
+            case 5 : return DES3_CBC_MD5; 
+            case 6 : return RESERVED6; 
+            case 7 : return DES3_CBC_SHA1; 
+            case 9 : return DSAWITHSHA1_CMSOID; 
+            case 10 : return MD5WITHRSAENCRYPTION_CMSOID; 
+            case 11 : return SHA1WITHRSAENCRYPTION_CMSOID; 
+            case 12 : return RC2CBC_ENVOID; 
+            case 13 : return RSAENCRYPTION_ENVOID; 
+            case 14 : return RSAES_OAEP_ENV_OID; 
+            case 15 : return DES_EDE3_CBC_ENV_OID; 
+            case 16 : return DES3_CBC_SHA1_KD; 
+            case 17 : return AES128_CTS_HMAC_SHA1_96; 
+            case 18 : return AES256_CTS_HMAC_SHA1_96; 
+            case 23 : return RC4_HMAC; 
+            case 24 : return RC4_HMAC_EXP; 
+            case 65 : return SUBKEY_KEYMATERIAL; 
+            case -128 : return RC4_MD4; 
+            case -133 : return RC4_HMAC_OLD; 
+            case -135 : return RC4_HMAC_OLD_EXP; 
+            default : return UNKNOWN; 
+        }
+    }
+
+
+    /**
+     * Returns the number associated with this encryption type.
+     *
+     * @return The encryption type number.
+     */
+    public int getOrdinal()
+    {
+        return ordinal;
+    }
+
+
+    /**
+     * Returns the name associated with this encryption type.
+     *
+     * @return The name.
+     */
+    public String getName()
+    {
+        switch (this )
+        {
+            case NULL                           : return "NULL"; 
+            case DES_CBC_CRC                    : return "des-cbc-crc"; 
+            case DES_CBC_MD4                    : return "des-cbc-md4";          
+            case DES_CBC_MD5                    : return "des-cbc-md5";          
+            case RESERVED4                      : return "[reserved]";           
+            case DES3_CBC_MD5                   : return "des3-cbc-md5";         
+            case RESERVED6                      : return "[reserved]";           
+            case DES3_CBC_SHA1                  : return "des3-cbc-sha1";            
+            case DSAWITHSHA1_CMSOID             : return "dsaWithSHA1-CmsOID";           
+            case MD5WITHRSAENCRYPTION_CMSOID    : return "md5WithRSAEncryption-CmsOID";          
+            case SHA1WITHRSAENCRYPTION_CMSOID   : return "sha1WithRSAEncryption-CmsOID";         
+            case RC2CBC_ENVOID                  : return "rc2CBC-EnvOID";            
+            case RSAENCRYPTION_ENVOID           : return "rsaEncryption-EnvOID";         
+            case RSAES_OAEP_ENV_OID             : return "rsaES-OAEP-ENV-OID";       
+            case DES_EDE3_CBC_ENV_OID           : return "des-ede3-cbc-Env-OID";         
+            case DES3_CBC_SHA1_KD               : return "des3-cbc-sha1-kd";     
+            case AES128_CTS_HMAC_SHA1_96        : return "aes128-cts-hmac-sha1-96";          
+            case AES256_CTS_HMAC_SHA1_96        : return "aes256-cts-hmac-sha1-96";          
+            case RC4_HMAC                       : return "rc4-hmac";         
+            case RC4_HMAC_EXP                   : return "rc4-hmac-exp";         
+            case SUBKEY_KEYMATERIAL             : return "subkey-keymaterial";           
+            case RC4_MD4                        : return "rc4-md4";      
+            case RC4_HMAC_OLD                   : return "rc4-hmac-old";         
+            case RC4_HMAC_OLD_EXP               : return "rc4-hmac-old-exp";         
+            case UNKNOWN                        : return "UNKNOWN";
+            default                             : return "UNKNOWN";
+        }
+    }
+
+    /**
+     * Get the EncryptionType given a String.
+     * @param type The encryption string we want to find
+     * @return The found EncryptionType, or UNKNOWN
+     */
+    public static EncryptionType getByName( String type )
+    {
+        if ( type == null )
+        {
+            return UNKNOWN;
+        }
+        
+        String lcType = type.toLowerCase();
+        
+        if ( encryptionTypes.containsKey( lcType ) )
+        {
+            return encryptionTypes.get( lcType );
+        }
+        else
+        {
+            return UNKNOWN;
+        }
+    }
+    
+    
+    /**
+     * @see Object#toString()
+     */
+    public String toString()
+    {
+        return getName() + " (" + ordinal + ")";
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/encryption/KerberosKeyFactory.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/encryption/KerberosKeyFactory.java
new file mode 100644
index 0000000..b4b8c3b
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/encryption/KerberosKeyFactory.java
@@ -0,0 +1,116 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.kerberos.shared.crypto.encryption;
+
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import javax.security.auth.kerberos.KerberosKey;
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+
+
+/**
+ * A factory class for producing {@link KerberosKey}'s.  For a list of desired cipher
+ * types, Kerberos string-to-key functions are used to derive keys for DES-, DES3-, AES-,
+ * and RC4-based encryption types.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class KerberosKeyFactory
+{
+    /** A map of default encryption types mapped to cipher names. */
+    private static final Map<EncryptionType, String> DEFAULT_CIPHERS;
+
+    static
+    {
+        Map<EncryptionType, String> map = new HashMap<EncryptionType, String>();
+
+        map.put( EncryptionType.DES_CBC_MD5, "DES" );
+        map.put( EncryptionType.DES3_CBC_SHA1_KD, "DESede" );
+        map.put( EncryptionType.RC4_HMAC, "ArcFourHmac" );
+        map.put( EncryptionType.AES128_CTS_HMAC_SHA1_96, "AES128" );
+        map.put( EncryptionType.AES256_CTS_HMAC_SHA1_96, "AES256" );
+
+        DEFAULT_CIPHERS = Collections.unmodifiableMap( map );
+    }
+
+
+    /**
+     * Get a map of KerberosKey's for a given principal name and passphrase.  The default set
+     * of encryption types is used.
+     * 
+     * @param principalName The principal name to use for key derivation.
+     * @param passPhrase The passphrase to use for key derivation.
+     * @return The map of KerberosKey's.
+     */
+    public static Map<EncryptionType, EncryptionKey> getKerberosKeys( String principalName, String passPhrase )
+    {
+        return getKerberosKeys( principalName, passPhrase, DEFAULT_CIPHERS.keySet() );
+    }
+
+
+    /**
+     * Get a list of KerberosKey's for a given principal name and passphrase and list of cipher
+     * types to derive keys for.
+     *
+     * @param principalName The principal name to use for key derivation.
+     * @param passPhrase The passphrase to use for key derivation.
+     * @param ciphers The set of ciphers to derive keys for.
+     * @return The list of KerberosKey's.
+     */
+    public static Map<EncryptionType, EncryptionKey> getKerberosKeys( String principalName, String passPhrase,
+        Set<EncryptionType> ciphers )
+    {
+        KerberosPrincipal principal = new KerberosPrincipal( principalName );
+        Map<EncryptionType, EncryptionKey> kerberosKeys = new HashMap<EncryptionType, EncryptionKey>();
+
+        Iterator<EncryptionType> it = ciphers.iterator();
+        while ( it.hasNext() )
+        {
+            EncryptionType encryptionType = it.next();
+            String algorithm = DEFAULT_CIPHERS.get( encryptionType );
+
+            try
+            {
+                KerberosKey kerberosKey = new KerberosKey( principal, passPhrase.toCharArray(), algorithm );
+                EncryptionKey encryptionKey = new EncryptionKey( encryptionType, kerberosKey.getEncoded(), kerberosKey
+                    .getVersionNumber() );
+
+                kerberosKeys.put( encryptionType, encryptionKey );
+            }
+            catch ( IllegalArgumentException iae )
+            {
+                // Algorithm AES256 not enabled by policy.
+                // Algorithm ArcFourHmac not supported by IBM JREs.
+                // Algorithm DESede not supported by IBM JREs.
+            }
+        }
+
+        return kerberosKeys;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/encryption/KeyUsage.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/encryption/KeyUsage.java
new file mode 100644
index 0000000..0b105f1
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/encryption/KeyUsage.java
@@ -0,0 +1,191 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.crypto.encryption;
+
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+
+/**
+ * From RFC 4120, "The Kerberos Network Authentication Service (V5)":
+ * 
+ * 7.5.1.  Key Usage Numbers
+ * 
+ * The encryption and checksum specifications in [RFC3961] require as
+ * input a "key usage number", to alter the encryption key used in any
+ * specific message in order to make certain types of cryptographic
+ * attack more difficult.  These are the key usage values assigned in
+ * [RFC 4120]:
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public final class KeyUsage implements Comparable<KeyUsage>
+{
+    /**
+     * AS-REQ PA-ENC-TIMESTAMP padata timestamp, encrypted with the client key (Section 5.2.7.2)
+     */
+    public static final KeyUsage NUMBER1 = new KeyUsage( 1,
+        "AS-REQ PA-ENC-TIMESTAMP padata timestamp, encrypted with the client key" );
+
+    /**
+     * AS-REP Ticket and TGS-REP Ticket (includes TGS session key or application session key), encrypted with the service key (Section 5.3)
+     */
+    public static final KeyUsage NUMBER2 = new KeyUsage(
+        2,
+        "AS-REP Ticket and TGS-REP Ticket (includes TGS session key or application session key), encrypted with the service key" );
+
+    /**
+     * AS-REP encrypted part (includes TGS session key or application session key), encrypted with the client key (Section 5.4.2)
+     */
+    public static final KeyUsage NUMBER3 = new KeyUsage( 3,
+        "AS-REP encrypted part (includes TGS session key or application session key), encrypted with the client key" );
+
+    /**
+     * TGS-REQ KDC-REQ-BODY AuthorizationData, encrypted with the TGS session key (Section 5.4.1)
+     */
+    public static final KeyUsage NUMBER4 = new KeyUsage( 4,
+        "TGS-REQ KDC-REQ-BODY AuthorizationData, encrypted with the TGS session key" );
+
+    /**
+     * TGS-REQ KDC-REQ-BODY AuthorizationData, encrypted with the TGS authenticator subkey (Section 5.4.1)
+     */
+    public static final KeyUsage NUMBER5 = new KeyUsage( 5,
+        "TGS-REQ KDC-REQ-BODY AuthorizationData, encrypted with the TGS authenticator subkey" );
+
+    /**
+     * TGS-REQ PA-TGS-REQ padata AP-REQ Authenticator cksum, keyed with the TGS session key (Section 5.5.1)
+     */
+    public static final KeyUsage NUMBER6 = new KeyUsage( 6,
+        "TGS-REQ PA-TGS-REQ padata AP-REQ Authenticator cksum, keyed with the TGS session key" );
+
+    /**
+     * TGS-REQ PA-TGS-REQ padata AP-REQ Authenticator (includes TGS authenticator subkey), encrypted with the TGS session key (Section 5.5.1)
+     */
+    public static final KeyUsage NUMBER7 = new KeyUsage(
+        7,
+        "TGS-REQ PA-TGS-REQ padata AP-REQ Authenticator (includes TGS authenticator subkey), encrypted with the TGS session key" );
+
+    /**
+     * TGS-REP encrypted part (includes application session key), encrypted with the TGS session key (Section 5.4.2)
+     */
+    public static final KeyUsage NUMBER8 = new KeyUsage( 8,
+        "TGS-REP encrypted part (includes application session key), encrypted with the TGS session key" );
+
+    /**
+     * TGS-REP encrypted part (includes application session key), encrypted with the TGS authenticator subkey (Section 5.4.2)
+     */
+    public static final KeyUsage NUMBER9 = new KeyUsage( 9,
+        "TGS-REP encrypted part (includes application session key), encrypted with the TGS authenticator subkey" );
+
+    /**
+     * AP-REQ Authenticator cksum, keyed with the application session key (Section 5.5.1)
+     */
+    public static final KeyUsage NUMBER10 = new KeyUsage( 10,
+        "AP-REQ Authenticator cksum, keyed with the application session key" );
+
+    /**
+     * AP-REQ Authenticator (includes application authenticator subkey), encrypted with the application session key (Section 5.5.1)
+     */
+    public static final KeyUsage NUMBER11 = new KeyUsage( 11,
+        "AP-REQ Authenticator (includes application authenticator subkey), encrypted with the application session key" );
+
+    /**
+     * AP-REP encrypted part (includes application session subkey), encrypted with the application session key (Section 5.5.2)
+     */
+    public static final KeyUsage NUMBER12 = new KeyUsage( 12,
+        "AP-REP encrypted part (includes application session subkey), encrypted with the application session key" );
+
+    /**
+     * KRB-PRIV encrypted part, encrypted with a key chosen by the application (Section 5.7.1)
+     */
+    public static final KeyUsage NUMBER13 = new KeyUsage( 13,
+        "KRB-PRIV encrypted part, encrypted with a key chosen by the application" );
+
+    /**
+     * These two lines are all that's necessary to export a List of VALUES.
+     */
+    private static final KeyUsage[] values =
+        { NUMBER1, NUMBER2, NUMBER3, NUMBER4, NUMBER5, NUMBER6, NUMBER7, NUMBER8, NUMBER9, NUMBER10, NUMBER11,
+            NUMBER12, NUMBER13 };
+
+    /**
+     * VALUES needs to be located here, otherwise illegal forward reference.
+     */
+    public static final List<KeyUsage> VALUES = Collections.unmodifiableList( Arrays.asList( values ) );
+
+    private final int ordinal;
+    private final String name;
+
+
+    /**
+     * Private constructor prevents construction outside of this class.
+     */
+    private KeyUsage( int ordinal, String name )
+    {
+        this.ordinal = ordinal;
+        this.name = name;
+    }
+
+
+    /**
+     * Returns the key usage number type when specified by its ordinal.
+     *
+     * @param type
+     * @return The key usage number type.
+     */
+    public static KeyUsage getTypeByOrdinal( int type )
+    {
+        for ( int ii = 0; ii < values.length; ii++ )
+        {
+            if ( values[ii].ordinal == type )
+            {
+                return values[ii];
+            }
+        }
+
+        return NUMBER1;
+    }
+
+
+    /**
+     * Returns the number associated with this key usage number.
+     *
+     * @return The key usage number
+     */
+    public int getOrdinal()
+    {
+        return ordinal;
+    }
+
+
+    public int compareTo( KeyUsage that )
+    {
+        return ordinal - that.ordinal;
+    }
+
+
+    public String toString()
+    {
+        return name + " (" + ordinal + ")";
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/encryption/NFold.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/encryption/NFold.java
new file mode 100644
index 0000000..1c4d397
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/encryption/NFold.java
@@ -0,0 +1,219 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.crypto.encryption;
+
+
+/**
+ * An implementation of the n-fold algorithm, as required by RFC 3961,
+ * "Encryption and Checksum Specifications for Kerberos 5."
+ * 
+ * "To n-fold a number X, replicate the input value to a length that
+ * is the least common multiple of n and the length of X.  Before
+ * each repetition, the input is rotated to the right by 13 bit
+ * positions.  The successive n-bit chunks are added together using
+ * 1's-complement addition (that is, with end-around carry) to yield
+ * a n-bit result."
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class NFold
+{
+    /**
+     * N-fold the data n times.
+     * 
+     * @param n The number of times to n-fold the data.
+     * @param data The data to n-fold.
+     * @return The n-folded data.
+     */
+    public static byte[] nFold( int n, byte[] data )
+    {
+        int k = data.length * 8;
+        int lcm = getLcm( n, k );
+        int replicate = lcm / k;
+        byte[] sumBytes = new byte[lcm / 8];
+
+        for ( int i = 0; i < replicate; i++ )
+        {
+            int rotation = 13 * i;
+
+            byte[] temp = rotateRight( data, data.length * 8, rotation );
+
+            for ( int j = 0; j < temp.length; j++ )
+            {
+                sumBytes[j + i * temp.length] = temp[j];
+            }
+        }
+
+        byte[] sum = new byte[n / 8];
+        byte[] nfold = new byte[n / 8];
+
+        for ( int m = 0; m < lcm / n; m++ )
+        {
+            for ( int o = 0; o < n / 8; o++ )
+            {
+                sum[o] = sumBytes[o + ( m * n / 8 )];
+            }
+
+            nfold = sum( nfold, sum, nfold.length * 8 );
+
+        }
+
+        return nfold;
+    }
+
+
+    /**
+     * For 2 numbers, return the least-common multiple.
+     *
+     * @param n1 The first number.
+     * @param n2 The second number.
+     * @return The least-common multiple.
+     */
+    protected static int getLcm( int n1, int n2 )
+    {
+        int temp;
+        int product;
+
+        product = n1 * n2;
+
+        do
+        {
+            if ( n1 < n2 )
+            {
+                temp = n1;
+                n1 = n2;
+                n2 = temp;
+            }
+            n1 = n1 % n2;
+        }
+        while ( n1 != 0 );
+
+        return product / n2;
+    }
+
+
+    /**
+     * Right-rotate the given byte array.
+     *
+     * @param in The byte array to right-rotate.
+     * @param len The length of the byte array to rotate.
+     * @param step The number of positions to rotate the byte array.
+     * @return The right-rotated byte array.
+     */
+    private static byte[] rotateRight( byte[] in, int len, int step )
+    {
+        int numOfBytes = ( len - 1 ) / 8 + 1;
+        byte[] out = new byte[numOfBytes];
+
+        for ( int i = 0; i < len; i++ )
+        {
+            int val = getBit( in, i );
+            setBit( out, ( i + step ) % len, val );
+        }
+        return out;
+    }
+
+
+    /**
+     * Perform one's complement addition (addition with end-around carry).  Note
+     * that for purposes of n-folding, we do not actually complement the
+     * result of the addition.
+     * 
+     * @param n1 The first number.
+     * @param n2 The second number.
+     * @param len The length of the byte arrays to sum.
+     * @return The sum with end-around carry.
+     */
+    protected static byte[] sum( byte[] n1, byte[] n2, int len )
+    {
+        int numOfBytes = ( len - 1 ) / 8 + 1;
+        byte[] out = new byte[numOfBytes];
+        int carry = 0;
+
+        for ( int i = len - 1; i > -1; i-- )
+        {
+            int n1b = getBit( n1, i );
+            int n2b = getBit( n2, i );
+
+            int sum = n1b + n2b + carry;
+
+            if ( sum == 0 || sum == 1 )
+            {
+                setBit( out, i, sum );
+                carry = 0;
+            }
+            else if ( sum == 2 )
+            {
+                carry = 1;
+            }
+            else if ( sum == 3 )
+            {
+                setBit( out, i, 1 );
+                carry = 1;
+            }
+        }
+
+        if ( carry == 1 )
+        {
+            byte[] carryArray = new byte[n1.length];
+            carryArray[carryArray.length - 1] = 1;
+            out = sum( out, carryArray, n1.length * 8 );
+        }
+
+        return out;
+    }
+
+
+    /**
+     * Get a bit from a byte array at a given position.
+     *
+     * @param data The data to get the bit from.
+     * @param pos The position to get the bit at.
+     * @return The value of the bit.
+     */
+    private static int getBit( byte[] data, int pos )
+    {
+        int posByte = pos / 8;
+        int posBit = pos % 8;
+
+        byte valByte = data[posByte];
+        int valInt = valByte >> ( 8 - ( posBit + 1 ) ) & 0x0001;
+        return valInt;
+    }
+
+
+    /**
+     * Set a bit in a byte array at a given position.
+     *
+     * @param data The data to set the bit in.
+     * @param pos The position of the bit to set.
+     * @param The value to set the bit to.
+     */
+    private static void setBit( byte[] data, int pos, int val )
+    {
+        int posByte = pos / 8;
+        int posBit = pos % 8;
+        byte oldByte = data[posByte];
+        oldByte = ( byte ) ( ( ( 0xFF7F >> posBit ) & oldByte ) & 0x00FF );
+        byte newByte = ( byte ) ( ( val << ( 8 - ( posBit + 1 ) ) ) | oldByte );
+        data[posByte] = newByte;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/encryption/NullEncryption.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/encryption/NullEncryption.java
new file mode 100644
index 0000000..2c64563
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/encryption/NullEncryption.java
@@ -0,0 +1,80 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.crypto.encryption;
+
+
+import org.apache.directory.server.kerberos.shared.exceptions.KerberosException;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptedData;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+class NullEncryption extends EncryptionEngine
+{
+    public EncryptionType getEncryptionType()
+    {
+        return EncryptionType.NULL;
+    }
+
+
+    public int getChecksumLength()
+    {
+        return 0;
+    }
+
+
+    public int getConfounderLength()
+    {
+        return 0;
+    }
+
+
+    public byte[] getDecryptedData( EncryptionKey key, EncryptedData data, KeyUsage usage ) throws KerberosException
+    {
+        return data.getCipher();
+    }
+
+
+    public EncryptedData getEncryptedData( EncryptionKey key, byte[] plainText, KeyUsage usage )
+    {
+        return new EncryptedData( getEncryptionType(), key.getKeyVersion(), plainText );
+    }
+
+
+    public byte[] encrypt( byte[] plainText, byte[] keyBytes )
+    {
+        return plainText;
+    }
+
+
+    public byte[] decrypt( byte[] cipherText, byte[] keyBytes )
+    {
+        return cipherText;
+    }
+
+
+    public byte[] calculateIntegrity( byte[] plainText, byte[] key, KeyUsage usage )
+    {
+        return null;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/encryption/RandomKeyFactory.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/encryption/RandomKeyFactory.java
new file mode 100644
index 0000000..89c47f1
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/encryption/RandomKeyFactory.java
@@ -0,0 +1,143 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.crypto.encryption;
+
+
+import java.security.NoSuchAlgorithmException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
+
+import org.apache.directory.server.kerberos.shared.exceptions.ErrorType;
+import org.apache.directory.server.kerberos.shared.exceptions.KerberosException;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+
+
+/**
+ * A factory class for producing random keys, suitable for use as session keys.  For a
+ * list of desired cipher types, Kerberos random-to-key functions are used to derive
+ * keys for DES-, DES3-, AES-, and RC4-based encryption types.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class RandomKeyFactory
+{
+    /** A map of default encryption types mapped to cipher names. */
+    private static final Map<EncryptionType, String> DEFAULT_CIPHERS;
+
+    static
+    {
+        Map<EncryptionType, String> map = new HashMap<EncryptionType, String>();
+
+        map.put( EncryptionType.DES_CBC_MD5, "DES" );
+        map.put( EncryptionType.DES3_CBC_SHA1_KD, "DESede" );
+        map.put( EncryptionType.RC4_HMAC, "RC4" );
+        map.put( EncryptionType.AES128_CTS_HMAC_SHA1_96, "AES" );
+        map.put( EncryptionType.AES256_CTS_HMAC_SHA1_96, "AES" );
+
+        DEFAULT_CIPHERS = Collections.unmodifiableMap( map );
+    }
+
+
+    /**
+     * Get a map of random keys.  The default set of encryption types is used.
+     * 
+     * @return The map of random keys.
+     * @throws KerberosException 
+     */
+    public static Map<EncryptionType, EncryptionKey> getRandomKeys() throws KerberosException
+    {
+        return getRandomKeys( DEFAULT_CIPHERS.keySet() );
+    }
+
+
+    /**
+     * Get a map of random keys for a list of cipher types to derive keys for.
+     *
+     * @param ciphers The list of ciphers to derive keys for.
+     * @return The list of KerberosKey's.
+     * @throws KerberosException 
+     */
+    public static Map<EncryptionType, EncryptionKey> getRandomKeys( Set<EncryptionType> ciphers )
+        throws KerberosException
+    {
+        Map<EncryptionType, EncryptionKey> map = new HashMap<EncryptionType, EncryptionKey>();
+
+        Iterator<EncryptionType> it = ciphers.iterator();
+        while ( it.hasNext() )
+        {
+            EncryptionType type = it.next();
+            map.put( type, getRandomKey( type ) );
+        }
+
+        return map;
+    }
+
+
+    /**
+     * Get a new random key for a given {@link EncryptionType}.
+     * 
+     * @param encryptionType 
+     * 
+     * @return The new random key.
+     * @throws KerberosException 
+     */
+    public static EncryptionKey getRandomKey( EncryptionType encryptionType ) throws KerberosException
+    {
+        String algorithm = DEFAULT_CIPHERS.get( encryptionType );
+
+        if ( algorithm == null )
+        {
+            throw new KerberosException( ErrorType.KDC_ERR_ETYPE_NOSUPP, encryptionType.getName()
+                + " is not a supported encryption type." );
+        }
+
+        try
+        {
+            KeyGenerator keyGenerator = KeyGenerator.getInstance( algorithm );
+
+            if ( encryptionType.equals( EncryptionType.AES128_CTS_HMAC_SHA1_96 ) )
+            {
+                keyGenerator.init( 128 );
+            }
+
+            if ( encryptionType.equals( EncryptionType.AES256_CTS_HMAC_SHA1_96 ) )
+            {
+                keyGenerator.init( 256 );
+            }
+
+            SecretKey key = keyGenerator.generateKey();
+
+            byte[] keyBytes = key.getEncoded();
+
+            return new EncryptionKey( encryptionType, keyBytes );
+        }
+        catch ( NoSuchAlgorithmException nsae )
+        {
+            throw new KerberosException( ErrorType.KDC_ERR_ETYPE_NOSUPP, nsae );
+        }
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/encryption/package-info.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/encryption/package-info.java
new file mode 100644
index 0000000..bff8f8d
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/encryption/package-info.java
@@ -0,0 +1,28 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+/**
+ * Provides support for Kerberos CipherText operations.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+
+package org.apache.directory.server.kerberos.shared.crypto.encryption;
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/exceptions/ErrorType.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/exceptions/ErrorType.java
new file mode 100644
index 0000000..f8a90bc
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/exceptions/ErrorType.java
@@ -0,0 +1,538 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.exceptions;
+
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+
+/**
+ * A type-safe enumeration of Kerberos error types.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public final class ErrorType implements Comparable<ErrorType>
+{
+    /**
+     * No error.
+     */
+    public static final ErrorType KDC_ERR_NONE = new ErrorType( 0, "No error" );
+
+    /**
+     * Client's entry in database has expired.
+     */
+    public static final ErrorType KDC_ERR_NAME_EXP = new ErrorType( 1, "Client's entry in database has expired" );
+
+    /**
+     * Server's entry in database has expired.
+     */
+    public static final ErrorType KDC_ERR_SERVICE_EXP = new ErrorType( 2, "Server's entry in database has expired" );
+
+    /**
+     * Requested protocol version number not supported.
+     */
+    public static final ErrorType KDC_ERR_BAD_PVNO = new ErrorType( 3,
+        "Requested protocol version number not supported" );
+
+    /**
+     * Client's key encrypted in old master key.
+     */
+    public static final ErrorType KDC_ERR_C_OLD_MAST_KVNO = new ErrorType( 4,
+        "Client's key encrypted in old master key" );
+
+    /**
+     * Server's key encrypted in old master key.
+     */
+    public static final ErrorType KDC_ERR_S_OLD_MAST_KVNO = new ErrorType( 5,
+        "Server's key encrypted in old master key" );
+
+    /**
+     * Client not found in Kerberos database.
+     */
+    public static final ErrorType KDC_ERR_C_PRINCIPAL_UNKNOWN = new ErrorType( 6,
+        "Client not found in Kerberos database" );
+
+    /**
+     * Server not found in Kerberos database.
+     */
+    public static final ErrorType KDC_ERR_S_PRINCIPAL_UNKNOWN = new ErrorType( 7,
+        "Server not found in Kerberos database" );
+
+    /**
+     * Multiple principal entries in database.
+     */
+    public static final ErrorType KDC_ERR_PRINCIPAL_NOT_UNIQUE = new ErrorType( 8,
+        "Multiple principal entries in database" );
+
+    /**
+     * The client or server has a null key.
+     */
+    public static final ErrorType KDC_ERR_NULL_KEY = new ErrorType( 9, "The client or server has a null key" );
+
+    /**
+     * Ticket not eligible for postdating.
+     */
+    public static final ErrorType KDC_ERR_CANNOT_POSTDATE = new ErrorType( 10, "Ticket not eligible for postdating" );
+
+    /**
+     * Requested start time is later than end time.
+     */
+    public static final ErrorType KDC_ERR_NEVER_VALID = new ErrorType( 11,
+        "Requested start time is later than end time" );
+
+    /**
+     * KDC policy rejects request.
+     */
+    public static final ErrorType KDC_ERR_POLICY = new ErrorType( 12, "KDC policy rejects request" );
+
+    /**
+     * KDC cannot accommodate requested option.
+     */
+    public static final ErrorType KDC_ERR_BADOPTION = new ErrorType( 13, "KDC cannot accommodate requested option" );
+
+    /**
+     * KDC has no support for encryption type.
+     */
+    public static final ErrorType KDC_ERR_ETYPE_NOSUPP = new ErrorType( 14, "KDC has no support for encryption type" );
+
+    /**
+     * KDC has no support for checksum type.
+     */
+    public static final ErrorType KDC_ERR_SUMTYPE_NOSUPP = new ErrorType( 15, "KDC has no support for checksum type" );
+
+    /**
+     * KDC has no support for padata type.
+     */
+    public static final ErrorType KDC_ERR_PADATA_TYPE_NOSUPP = new ErrorType( 16, "KDC has no support for padata type" );
+
+    /**
+     * KDC has no support for transited type.
+     */
+    public static final ErrorType KDC_ERR_TRTYPE_NOSUPP = new ErrorType( 17, "KDC has no support for transited type" );
+
+    /**
+     * Clients credentials have been revoked.
+     */
+    public static final ErrorType KDC_ERR_CLIENT_REVOKED = new ErrorType( 18, "Clients credentials have been revoked" );
+
+    /**
+     * Credentials for server have been revoked.
+     */
+    public static final ErrorType KDC_ERR_SERVICE_REVOKED = new ErrorType( 19,
+        "Credentials for server have been revoked" );
+
+    /**
+     * TGT has been revoked.
+     */
+    public static final ErrorType KDC_ERR_TGT_REVOKED = new ErrorType( 20, "TGT has been revoked" );
+
+    /**
+     * Client not yet valid; try again later.
+     */
+    public static final ErrorType KDC_ERR_CLIENT_NOTYET = new ErrorType( 21, "Client not yet valid; try again later" );
+
+    /**
+     * Server not yet valid; try again later.
+     */
+    public static final ErrorType KDC_ERR_SERVICE_NOTYET = new ErrorType( 22, "Server not yet valid; try again later" );
+
+    /**
+     * Password has expired; change password to reset.
+     */
+    public static final ErrorType KDC_ERR_KEY_EXPIRED = new ErrorType( 23,
+        "Password has expired; change password to reset" );
+
+    /**
+     * Pre-authentication information was invalid.
+     */
+    public static final ErrorType KDC_ERR_PREAUTH_FAILED = new ErrorType( 24,
+        "Pre-authentication information was invalid" );
+
+    /**
+     * Additional pre-authentication required.
+     */
+    public static final ErrorType KDC_ERR_PREAUTH_REQUIRED = new ErrorType( 25,
+        "Additional pre-authentication required" );
+
+    /**
+     * Requested server and ticket don't match.
+     */
+    public static final ErrorType KDC_ERR_SERVER_NOMATCH = new ErrorType( 26, "Requested server and ticket don't match" );
+
+    /**
+     * Server valid for user2user only.
+     */
+    public static final ErrorType KDC_ERR_MUST_USE_USER2USER = new ErrorType( 27, "Server valid for user2user only" );
+
+    /**
+     * KDC Policy rejects transited path.
+     */
+    public static final ErrorType KDC_ERR_PATH_NOT_ACCEPTED = new ErrorType( 28, "KDC Policy rejects transited path" );
+
+    /**
+     * A service is not available.
+     */
+    public static final ErrorType KDC_ERR_SVC_UNAVAILABLE = new ErrorType( 29, "A service is not available" );
+
+    /**
+     * Integrity check on decrypted field failed.
+     */
+    public static final ErrorType KRB_AP_ERR_BAD_INTEGRITY = new ErrorType( 31,
+        "Integrity check on decrypted field failed" );
+
+    /**
+     * Ticket expired.
+     */
+    public static final ErrorType KRB_AP_ERR_TKT_EXPIRED = new ErrorType( 32, "Ticket expired" );
+
+    /**
+     * Ticket not yet valid.
+     */
+    public static final ErrorType KRB_AP_ERR_TKT_NYV = new ErrorType( 33, "Ticket not yet valid" );
+
+    /**
+     * Request is a replay.
+     */
+    public static final ErrorType KRB_AP_ERR_REPEAT = new ErrorType( 34, "Request is a replay" );
+
+    /**
+     * The ticket isn't for us.
+     */
+    public static final ErrorType KRB_AP_ERR_NOT_US = new ErrorType( 35, "The ticket isn't for us" );
+
+    /**
+     * Ticket and authenticator don't match.
+     */
+    public static final ErrorType KRB_AP_ERR_BADMATCH = new ErrorType( 36, "Ticket and authenticator don't match" );
+
+    /**
+     * Clock skew too great.
+     */
+    public static final ErrorType KRB_AP_ERR_SKEW = new ErrorType( 37, "Clock skew too great" );
+
+    /**
+     * Incorrect net address.
+     */
+    public static final ErrorType KRB_AP_ERR_BADADDR = new ErrorType( 38, "Incorrect net address" );
+
+    /**
+     * Protocol version mismatch.
+     */
+    public static final ErrorType KRB_AP_ERR_BADVERSION = new ErrorType( 39, "Protocol version mismatch" );
+
+    /**
+     * Invalid msg type.
+     */
+    public static final ErrorType KRB_AP_ERR_MSG_TYPE = new ErrorType( 40, "Invalid msg type" );
+
+    /**
+     * Message stream modified.
+     */
+    public static final ErrorType KRB_AP_ERR_MODIFIED = new ErrorType( 41, "Message stream modified" );
+
+    /**
+     * Message out of order.
+     */
+    public static final ErrorType KRB_AP_ERR_BADORDER = new ErrorType( 42, "Message out of order" );
+
+    /**
+     * Specified version of key is not available.
+     */
+    public static final ErrorType KRB_AP_ERR_BADKEYVER = new ErrorType( 44, "Specified version of key is not available" );
+
+    /**
+     * Service key not available.
+     */
+    public static final ErrorType KRB_AP_ERR_NOKEY = new ErrorType( 45, "Service key not available" );
+
+    /**
+     * Mutual authentication failed.
+     */
+    public static final ErrorType KRB_AP_ERR_MUT_FAIL = new ErrorType( 46, "Mutual authentication failed" );
+
+    /**
+     * Incorrect message direction.
+     */
+    public static final ErrorType KRB_AP_ERR_BADDIRECTION = new ErrorType( 47, "Incorrect message direction" );
+
+    /**
+     * Alternative authentication method required.
+     */
+    public static final ErrorType KRB_AP_ERR_METHOD = new ErrorType( 48, "Alternative authentication method required" );
+
+    /**
+     * Incorrect sequence number in message.
+     */
+    public static final ErrorType KRB_AP_ERR_BADSEQ = new ErrorType( 49, "Incorrect sequence number in message" );
+
+    /**
+     * Inappropriate type of checksum in message.
+     */
+    public static final ErrorType KRB_AP_ERR_INAPP_CKSUM = new ErrorType( 50,
+        "Inappropriate type of checksum in message" );
+
+    /**
+     * Policy rejects transited path.
+     */
+    public static final ErrorType KRB_AP_PATH_NOT_ACCEPTED = new ErrorType( 51, "Policy rejects transited path" );
+
+    /**
+     * Response too big for UDP; retry with TCP.
+     */
+    public static final ErrorType KRB_ERR_RESPONSE_TOO_BIG = new ErrorType( 52,
+        "Response too big for UDP; retry with TCP" );
+
+    /**
+     * Generic error (description in e-text).
+     */
+    public static final ErrorType KRB_ERR_GENERIC = new ErrorType( 60, "Generic error (description in e-text)" );
+
+    /**
+     * Field is too long for this implementation.
+     */
+    public static final ErrorType KRB_ERR_FIELD_TOOLONG = new ErrorType( 61,
+        "Field is too long for this implementation" );
+
+    /**
+     * Client is not trusted.
+     */
+    public static final ErrorType KDC_ERR_CLIENT_NOT_TRUSTED = new ErrorType( 62, "Client is not trusted" );
+
+    /**
+     * KDC is not trusted.
+     */
+    public static final ErrorType KRB_ERR_KDC_NOT_TRUSTED = new ErrorType( 63, "KDC is not trusted" );
+
+    /**
+     * Signature is invalid.
+     */
+    public static final ErrorType KDC_ERR_INVALID_SIG = new ErrorType( 64, "Signature is invalid" );
+
+    /**
+     * Diffie-Hellman (DH) key parameters not accepted.
+     */
+    public static final ErrorType KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED = new ErrorType( 65,
+        "Diffie-Hellman (DH) key parameters not accepted." );
+
+    /**
+     * Certificates do not match.
+     */
+    public static final ErrorType KRB_ERR_CERTIFICATE_MISMATCH = new ErrorType( 66, "Certificates do not match" );
+
+    /**
+     * No TGT available to validate USER-TO-USER.
+     */
+    public static final ErrorType KRB_AP_ERR_NO_TGT = new ErrorType( 67, "No TGT available to validate USER-TO-USER" );
+
+    /**
+     * Wrong realm.
+     */
+    public static final ErrorType KRB_ERR_WRONG_REALM = new ErrorType( 68, "Wrong realm" );
+
+    /**
+     * Ticket must be for USER-TO-USER.
+     */
+    public static final ErrorType KRB_AP_ERR_USER_TO_USER_REQUIRED = new ErrorType( 69,
+        "Ticket must be for USER-TO-USER" );
+
+    /**
+     * Can't verify certificate.
+     */
+    public static final ErrorType KDC_ERR_CANT_VERIFY_CERTIFICATE = new ErrorType( 70, "Can't verify certificate" );
+
+    /**
+     * Invalid certificate.
+     */
+    public static final ErrorType KDC_ERR_INVALID_CERTIFICATE = new ErrorType( 71, "Invalid certificate" );
+
+    /**
+     * Revoked certificate.
+     */
+    public static final ErrorType KDC_ERR_REVOKED_CERTIFICATE = new ErrorType( 72, "Revoked certificate" );
+
+    /**
+     * Revocation status unknown.
+     */
+    public static final ErrorType KDC_ERR_REVOCATION_STATUS_UNKNOWN = new ErrorType( 73, "Revocation status unknown" );
+
+    /**
+     * Revocation status unavailable.
+     */
+    public static final ErrorType KRB_ERR_REVOCATION_STATUS_UNAVAILABLE = new ErrorType( 74,
+        "Revocation status unavailable" );
+
+    /**
+     * Client names do not match.
+     */
+    public static final ErrorType KDC_ERR_CLIENT_NAME_MISMATCH = new ErrorType( 75, "Client names do not match" );
+
+    /**
+     * KDC names do not match.
+     */
+    public static final ErrorType KRB_ERR_KDC_NAME_MISMATCH = new ErrorType( 76, "KDC names do not match" );
+
+    /**
+     * Inconsistent key purpose.
+     */
+    public static final ErrorType KDC_ERR_INCONSISTENT_KEY_PURPOSE = new ErrorType( 77, "Inconsistent key purpose" );
+
+    /**
+     * Digest in certificate not accepted.
+     */
+    public static final ErrorType KDC_ERR_DIGEST_IN_CERT_NOT_ACCEPTED = new ErrorType( 78,
+        "Digest in certificate not accepted" );
+
+    /**
+     * PA checksum must be included.
+     */
+    public static final ErrorType KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED = new ErrorType( 79,
+        "PA checksum must be included" );
+
+    /**
+     * Digest in signed data not accepted.
+     */
+    public static final ErrorType KDC_ERR_DIGEST_IN_SIGNED_DATA_NOT_ACCEPTED = new ErrorType( 80,
+        "Digest in signed data not accepted" );
+
+    /**
+     * Public key encryption not supported.
+     */
+    public static final ErrorType KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED = new ErrorType( 81,
+        "Public key encryption not supported" );
+
+    /**
+     * Array for building a List of VALUES.
+     */
+    private static final ErrorType[] values =
+        { KDC_ERR_NONE, KDC_ERR_NAME_EXP, KDC_ERR_SERVICE_EXP, KDC_ERR_BAD_PVNO, KDC_ERR_C_OLD_MAST_KVNO,
+            KDC_ERR_S_OLD_MAST_KVNO, KDC_ERR_C_PRINCIPAL_UNKNOWN, KDC_ERR_S_PRINCIPAL_UNKNOWN,
+            KDC_ERR_PRINCIPAL_NOT_UNIQUE, KDC_ERR_NULL_KEY, KDC_ERR_CANNOT_POSTDATE, KDC_ERR_NEVER_VALID,
+            KDC_ERR_POLICY, KDC_ERR_BADOPTION, KDC_ERR_ETYPE_NOSUPP, KDC_ERR_SUMTYPE_NOSUPP,
+            KDC_ERR_PADATA_TYPE_NOSUPP, KDC_ERR_TRTYPE_NOSUPP, KDC_ERR_CLIENT_REVOKED, KDC_ERR_SERVICE_REVOKED,
+            KDC_ERR_TGT_REVOKED, KDC_ERR_CLIENT_NOTYET, KDC_ERR_SERVICE_NOTYET, KDC_ERR_KEY_EXPIRED,
+            KDC_ERR_PREAUTH_FAILED, KDC_ERR_PREAUTH_REQUIRED, KDC_ERR_SERVER_NOMATCH, KDC_ERR_MUST_USE_USER2USER,
+            KDC_ERR_PATH_NOT_ACCEPTED, KDC_ERR_SVC_UNAVAILABLE, KRB_AP_ERR_BAD_INTEGRITY, KRB_AP_ERR_TKT_EXPIRED,
+            KRB_AP_ERR_TKT_NYV, KRB_AP_ERR_REPEAT, KRB_AP_ERR_NOT_US, KRB_AP_ERR_BADMATCH, KRB_AP_ERR_SKEW,
+            KRB_AP_ERR_BADADDR, KRB_AP_ERR_BADVERSION, KRB_AP_ERR_MSG_TYPE, KRB_AP_ERR_MODIFIED, KRB_AP_ERR_BADORDER,
+            KRB_AP_ERR_BADKEYVER, KRB_AP_ERR_NOKEY, KRB_AP_ERR_MUT_FAIL, KRB_AP_ERR_BADDIRECTION, KRB_AP_ERR_METHOD,
+            KRB_AP_ERR_BADSEQ, KRB_AP_ERR_INAPP_CKSUM, KRB_AP_PATH_NOT_ACCEPTED, KRB_ERR_RESPONSE_TOO_BIG,
+            KRB_ERR_GENERIC, KRB_ERR_FIELD_TOOLONG, KDC_ERR_CLIENT_NOT_TRUSTED, KRB_ERR_KDC_NOT_TRUSTED,
+            KDC_ERR_INVALID_SIG, KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED, KRB_ERR_CERTIFICATE_MISMATCH,
+            KRB_AP_ERR_NO_TGT, KRB_ERR_WRONG_REALM, KRB_AP_ERR_USER_TO_USER_REQUIRED, KDC_ERR_CANT_VERIFY_CERTIFICATE,
+            KDC_ERR_INVALID_CERTIFICATE, KDC_ERR_REVOKED_CERTIFICATE, KDC_ERR_REVOCATION_STATUS_UNKNOWN,
+            KRB_ERR_REVOCATION_STATUS_UNAVAILABLE, KDC_ERR_CLIENT_NAME_MISMATCH, KRB_ERR_KDC_NAME_MISMATCH,
+            KDC_ERR_INCONSISTENT_KEY_PURPOSE, KDC_ERR_DIGEST_IN_CERT_NOT_ACCEPTED,
+            KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED, KDC_ERR_DIGEST_IN_SIGNED_DATA_NOT_ACCEPTED,
+            KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED };
+
+    /**
+     * A List of all the error type constants.
+     */
+    public static final List<ErrorType> VALUES = Collections.unmodifiableList( Arrays.asList( values ) );
+
+    /**
+     * The name of the error type.
+     */
+    private final String name;
+
+    /**
+     * The value/code for the error type.
+     */
+    private final int ordinal;
+
+
+    /**
+     * Private constructor prevents construction outside of this class.
+     */
+    private ErrorType( int ordinal, String name )
+    {
+        this.ordinal = ordinal;
+        this.name = name;
+    }
+
+
+    /**
+     * Returns the message for this Kerberos error.
+     *
+     * @return the message for this Kerberos error.
+     */
+    public String getMessage()
+    {
+        return name;
+    }
+
+
+    /**
+     * Returns the message for this Kerberos error.
+     *
+     * @return the message for this Kerberos error.
+     */
+    public String toString()
+    {
+        return name;
+    }
+
+
+    /**
+     * Compares this type to another object hopefully one that is of the same
+     * type.
+     *
+     * @param that the object to compare this KerberosError to
+     * @return ordinal - ( ( KerberosError ) that ).ordinal;
+     */
+    public int compareTo( ErrorType that )
+    {
+        return ordinal - that.ordinal;
+    }
+
+
+    /**
+     * Gets the ordinal by its ordinal value.
+     *
+     * @param ordinal the ordinal value of the ordinal
+     * @return the type corresponding to the ordinal value
+     */
+    public static ErrorType getTypeByOrdinal( int ordinal )
+    {
+        for ( int ii = 0; ii < values.length; ii++ )
+        {
+            if ( values[ii].ordinal == ordinal )
+            {
+                return values[ii];
+            }
+        }
+
+        return KRB_ERR_GENERIC;
+    }
+
+
+    /**
+     * Gets the ordinal value associated with this Kerberos error.
+     *
+     * @return the ordinal value associated with this Kerberos error
+     */
+    public int getOrdinal()
+    {
+        return ordinal;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/exceptions/KerberosException.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/exceptions/KerberosException.java
new file mode 100644
index 0000000..3c1757e
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/exceptions/KerberosException.java
@@ -0,0 +1,221 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.exceptions;
+
+
+/**
+ * The root of the Kerberos exception hierarchy.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class KerberosException extends Exception
+{
+    private static final long serialVersionUID = 2968072183596955597L;
+
+    /**
+     * The Kerberos error code associated with this exception.
+     */
+    private final int errorCode;
+
+    /**
+     * Additional data about the error for use by the application
+     * to help it recover from or handle the error.
+     */
+    private byte[] explanatoryData;
+
+
+    /**
+     * Creates a KerberosException with an {@link ErrorType}.
+     *
+     * @param errorType The error type associated with this KerberosException.
+     */
+    public KerberosException( ErrorType errorType )
+    {
+        super( errorType.getMessage() );
+
+        this.errorCode = errorType.getOrdinal();
+    }
+
+
+    /**
+     * Creates a KerberosException with an {@link ErrorType} and an
+     * underlying {@link Throwable} that caused this fault.
+     *
+     * @param errorType The error type associated with this KerberosException.
+     * @param cause The underlying failure, if any.
+     */
+    public KerberosException( ErrorType errorType, Throwable cause )
+    {
+        super( errorType.getMessage(), cause );
+
+        this.errorCode = errorType.getOrdinal();
+    }
+
+
+    /**
+     * Creates a KerberosException with an {@link ErrorType} and a custom error message.
+     *
+     * @param errorType The {@link ErrorType} associated with this KerberosException.
+     * @param msg A custom error message for this KerberosException.
+     */
+    public KerberosException( ErrorType errorType, String msg )
+    {
+        super( msg );
+
+        this.errorCode = errorType.getOrdinal();
+    }
+
+
+    /**
+     * Creates a KerberosException with an {@link ErrorType}, a custom error message, and an
+     * underlying {@link Throwable} that caused this fault.
+     *
+     * @param errorType The error type associated with this KerberosException.
+     * @param msg A custom error message for this KerberosException.
+     * @param cause The underlying failure, if any.
+     */
+    public KerberosException( ErrorType errorType, String msg, Throwable cause )
+    {
+        super( msg, cause );
+
+        this.errorCode = errorType.getOrdinal();
+    }
+
+
+    /**
+     * Creates a KerberosException with an {@link ErrorType} and data helping to
+     * explain what caused this fault.
+     *
+     * @param errorType The error type associated with this KerberosException.
+     * @param explanatoryData Data helping to explain this fault, if any.
+     */
+    public KerberosException( ErrorType errorType, byte[] explanatoryData )
+    {
+        super( errorType.getMessage() );
+
+        this.errorCode = errorType.getOrdinal();
+        this.explanatoryData = explanatoryData;
+    }
+
+
+    /**
+     * Creates a KerberosException with an {@link ErrorType}, data helping to
+     * explain what caused this fault, and an underlying {@link Throwable} that caused this fault.
+     *
+     * @param errorType The error type associated with this KerberosException.
+     * @param explanatoryData Data helping to explain this fault, if any.
+     * @param cause The underlying failure, if any.
+     */
+    public KerberosException( ErrorType errorType, byte[] explanatoryData, Throwable cause )
+    {
+        super( errorType.getMessage(), cause );
+
+        this.errorCode = errorType.getOrdinal();
+        this.explanatoryData = explanatoryData;
+    }
+
+
+    /**
+     * Gets the protocol error code associated with this KerberosException.
+     *
+     * @return The error code associated with this KerberosException.
+     */
+    public int getErrorCode()
+    {
+        return this.errorCode;
+    }
+
+
+    /**
+     * Gets the explanatory data associated with this KerberosException.
+     *
+     * @return The explanatory data associated with this KerberosException.
+     */
+    public byte[] getExplanatoryData()
+    {
+        return explanatoryData;
+    }
+
+
+    /**
+     * Creates a KerberosException with an error code and a message.
+     *
+     * @param errorCode The error code associated with this KerberosException.
+     * @param msg The standard Kerberos error message for this KerberosException.
+     */
+    protected KerberosException( int errorCode, String msg )
+    {
+        super( msg );
+
+        this.errorCode = errorCode;
+    }
+
+
+    /**
+     * Creates a KerberosException with an error code, a message and an
+     * underlying {@link Throwable} that caused this fault.
+     *
+     * @param errorCode The error code associated with this KerberosException.
+     * @param msg The standard Kerberos error message for this KerberosException.
+     * @param cause The underlying failure, if any.
+     */
+    protected KerberosException( int errorCode, String msg, Throwable cause )
+    {
+        super( msg, cause );
+
+        this.errorCode = errorCode;
+    }
+
+
+    /**
+     * Creates a KerberosException with an error code, a message, and data
+     * helping to explain what caused this fault.
+     *
+     * @param errorCode The error code associated with this KerberosException.
+     * @param msg The standard Kerberos error message for this KerberosException.
+     * @param explanatoryData Data helping to explain this fault, if any.
+     */
+    protected KerberosException( int errorCode, String msg, byte[] explanatoryData )
+    {
+        super( msg );
+
+        this.errorCode = errorCode;
+        this.explanatoryData = explanatoryData;
+    }
+
+
+    /**
+     * Creates a KerberosException with an error code, a message, and data
+     * helping to explain what caused this fault.
+     *
+     * @param errorCode The error code associated with this KerberosException.
+     * @param msg The standard Kerberos error message for this KerberosException.
+     * @param explanatoryData Data helping to explain this fault, if any.
+     * @param cause The underlying failure, if any.
+     */
+    protected KerberosException( int errorCode, String msg, byte[] explanatoryData, Throwable cause )
+    {
+        super( msg, cause );
+
+        this.errorCode = errorCode;
+        this.explanatoryData = explanatoryData;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/exceptions/package-info.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/exceptions/package-info.java
new file mode 100644
index 0000000..382897c
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/exceptions/package-info.java
@@ -0,0 +1,29 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+/**
+ * Provides the root of the exception hierarchy, as well as the
+ * enumerator for mapping Kerberos errors to error codes.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+
+package org.apache.directory.server.kerberos.shared.exceptions;
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/ApplicationReplyDecoder.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/ApplicationReplyDecoder.java
new file mode 100644
index 0000000..aedd378
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/ApplicationReplyDecoder.java
@@ -0,0 +1,95 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.io.decoder;
+
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+import org.apache.directory.server.kerberos.shared.messages.application.ApplicationReply;
+import org.apache.directory.shared.asn1.der.ASN1InputStream;
+import org.apache.directory.shared.asn1.der.DERApplicationSpecific;
+import org.apache.directory.shared.asn1.der.DEREncodable;
+import org.apache.directory.shared.asn1.der.DERSequence;
+import org.apache.directory.shared.asn1.der.DERTaggedObject;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 540371 $, $Date: 2007-05-21 17:00:43 -0700 (Mon, 21 May 2007) $
+ */
+public class ApplicationReplyDecoder
+{
+    /**
+     * Decodes a byte array into an {@link ApplicationReply}.
+     *
+     * @param encodedAuthHeader
+     * @return The {@link ApplicationReply}.
+     * @throws IOException
+     */
+    public ApplicationReply decode( byte[] encodedAuthHeader ) throws IOException
+    {
+        ASN1InputStream ais = new ASN1InputStream( encodedAuthHeader );
+
+        DERApplicationSpecific app = ( DERApplicationSpecific ) ais.readObject();
+
+        DERSequence apreq = ( DERSequence ) app.getObject();
+
+        return decodeApplicationRequestSequence( apreq );
+    }
+
+
+    /*
+     AP-REP ::=         [APPLICATION 15] SEQUENCE {
+     pvno[0]                   INTEGER,
+     msg-type[1]               INTEGER,
+     enc-part[2]               EncryptedData
+     }
+     */
+    private ApplicationReply decodeApplicationRequestSequence( DERSequence sequence )
+    {
+        ApplicationReply authHeader = null;
+
+        for ( Enumeration e = sequence.getObjects(); e.hasMoreElements(); )
+        {
+            DERTaggedObject object = ( ( DERTaggedObject ) e.nextElement() );
+            int tag = object.getTagNo();
+            DEREncodable derObject = object.getObject();
+
+            switch ( tag )
+            {
+                case 0:
+                    //DERInteger tag0 = ( DERInteger ) derObject;
+                    //authHeader.setProtocolVersionNumber( tag0.intValue() );
+                    break;
+                case 1:
+                    //DERInteger tag1 = ( DERInteger ) derObject;
+                    //authHeader.setMessageType( MessageType.getTypeByOrdinal( tag1.intValue() ) );
+                    break;
+                case 2:
+                    DERSequence tag2 = ( DERSequence ) derObject;
+                    authHeader = new ApplicationReply( EncryptedDataDecoder.decode( tag2 ) );
+                    break;
+            }
+        }
+
+        return authHeader;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/ApplicationRequestDecoder.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/ApplicationRequestDecoder.java
new file mode 100644
index 0000000..97eccf3
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/ApplicationRequestDecoder.java
@@ -0,0 +1,113 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.io.decoder;
+
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+import org.apache.directory.server.kerberos.shared.KerberosMessageType;
+import org.apache.directory.server.kerberos.shared.messages.ApplicationRequest;
+import org.apache.directory.server.kerberos.shared.messages.value.ApOptions;
+import org.apache.directory.shared.asn1.der.ASN1InputStream;
+import org.apache.directory.shared.asn1.der.DERApplicationSpecific;
+import org.apache.directory.shared.asn1.der.DERBitString;
+import org.apache.directory.shared.asn1.der.DEREncodable;
+import org.apache.directory.shared.asn1.der.DERInteger;
+import org.apache.directory.shared.asn1.der.DERSequence;
+import org.apache.directory.shared.asn1.der.DERTaggedObject;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ApplicationRequestDecoder
+{
+    /**
+     * Decodes a byte array into an {@link ApplicationRequest}.
+     *
+     * @param encodedAuthHeader
+     * @return The {@link ApplicationRequest}.
+     * @throws IOException
+     */
+    public ApplicationRequest decode( byte[] encodedAuthHeader ) throws IOException
+    {
+        ASN1InputStream ais = new ASN1InputStream( encodedAuthHeader );
+
+        DERApplicationSpecific app = ( DERApplicationSpecific ) ais.readObject();
+
+        DERSequence apreq = ( DERSequence ) app.getObject();
+
+        return decodeApplicationRequestSequence( apreq );
+    }
+
+
+    /*
+     AP-REQ ::=      [APPLICATION 14] SEQUENCE {
+     pvno[0]                       INTEGER,
+     msg-type[1]                   INTEGER,
+
+     ap-options[2]                 APOptions,
+     ticket[3]                     Ticket,
+     authenticator[4]              EncryptedData
+     }
+     */
+    private ApplicationRequest decodeApplicationRequestSequence( DERSequence sequence ) throws IOException
+    {
+        ApplicationRequest authHeader = new ApplicationRequest();
+
+        for ( Enumeration<DEREncodable> e = sequence.getObjects(); e.hasMoreElements(); )
+        {
+            DERTaggedObject object = ( ( DERTaggedObject ) e.nextElement() );
+            int tag = object.getTagNo();
+            DEREncodable derObject = object.getObject();
+
+            switch ( tag )
+            {
+                case 0:
+                    DERInteger tag0 = ( DERInteger ) derObject;
+                    authHeader.setProtocolVersionNumber( tag0.intValue() );
+                    break;
+                    
+                case 1:
+                    DERInteger tag1 = ( DERInteger ) derObject;
+                    authHeader.setMessageType( KerberosMessageType.getTypeByOrdinal( tag1.intValue() ) );
+                    break;
+                    
+                case 2:
+                    DERBitString apOptions = ( DERBitString ) derObject;
+                    authHeader.setApOptions( new ApOptions( apOptions.getOctets() ) );
+                    break;
+                case 3:
+                    DERApplicationSpecific tag3 = ( DERApplicationSpecific ) derObject;
+                    authHeader.setTicket( TicketDecoder.decode( tag3 ) );
+                    break;
+                    
+                case 4:
+                    DERSequence tag4 = ( DERSequence ) derObject;
+                    authHeader.setEncPart( EncryptedDataDecoder.decode( tag4 ) );
+                    break;
+            }
+        }
+
+        return authHeader;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/AuthenticatorDecoder.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/AuthenticatorDecoder.java
new file mode 100644
index 0000000..1346094
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/AuthenticatorDecoder.java
@@ -0,0 +1,132 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.io.decoder;
+
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+import org.apache.directory.server.kerberos.shared.messages.Encodable;
+import org.apache.directory.server.kerberos.shared.messages.components.Authenticator;
+import org.apache.directory.server.kerberos.shared.messages.components.AuthenticatorModifier;
+import org.apache.directory.shared.asn1.der.ASN1InputStream;
+import org.apache.directory.shared.asn1.der.DERApplicationSpecific;
+import org.apache.directory.shared.asn1.der.DEREncodable;
+import org.apache.directory.shared.asn1.der.DERGeneralString;
+import org.apache.directory.shared.asn1.der.DERGeneralizedTime;
+import org.apache.directory.shared.asn1.der.DERInteger;
+import org.apache.directory.shared.asn1.der.DERSequence;
+import org.apache.directory.shared.asn1.der.DERTaggedObject;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class AuthenticatorDecoder implements Decoder, DecoderFactory
+{
+    public Decoder getDecoder()
+    {
+        return new AuthenticatorDecoder();
+    }
+
+
+    public Encodable decode( byte[] encodedAuthenticator ) throws IOException
+    {
+        ASN1InputStream ais = new ASN1InputStream( encodedAuthenticator );
+
+        DERApplicationSpecific app = ( DERApplicationSpecific ) ais.readObject();
+
+        DERSequence sequence = ( DERSequence ) app.getObject();
+
+        return decode( sequence );
+    }
+
+
+    /**
+     * -- Unencrypted authenticator
+     * Authenticator ::=    [APPLICATION 2] SEQUENCE
+     * {
+     *                authenticator-vno[0]          INTEGER,
+     *                crealm[1]                     Realm,
+     *                cname[2]                      PrincipalName,
+     *                cksum[3]                      Checksum OPTIONAL,
+     *                cusec[4]                      INTEGER,
+     *                ctime[5]                      KerberosTime,
+     *                subkey[6]                     EncryptionKey OPTIONAL,
+     *                seq-number[7]                 INTEGER OPTIONAL,
+     *  
+     *                authorization-data[8]         AuthorizationData OPTIONAL
+     * }
+     */
+    protected static Authenticator decode( DERSequence sequence )
+    {
+        AuthenticatorModifier modifier = new AuthenticatorModifier();
+
+        for ( Enumeration e = sequence.getObjects(); e.hasMoreElements(); )
+        {
+            DERTaggedObject object = ( DERTaggedObject ) e.nextElement();
+            int tag = object.getTagNo();
+            DEREncodable derObject = object.getObject();
+
+            switch ( tag )
+            {
+                case 0:
+                    DERInteger tag0 = ( DERInteger ) derObject;
+                    modifier.setVersionNumber( tag0.intValue() );
+                    break;
+                case 1:
+                    DERGeneralString tag1 = ( DERGeneralString ) derObject;
+                    modifier.setClientRealm( tag1.getString() );
+                    break;
+                case 2:
+                    DERSequence tag2 = ( DERSequence ) derObject;
+                    modifier.setClientName( PrincipalNameDecoder.decode( tag2 ) );
+                    break;
+                case 3:
+                    DERSequence tag3 = ( DERSequence ) derObject;
+                    modifier.setChecksum( ChecksumDecoder.decode( tag3 ) );
+                    break;
+                case 4:
+                    DERInteger tag4 = ( DERInteger ) derObject;
+                    modifier.setClientMicroSecond( tag4.intValue() );
+                    break;
+                case 5:
+                    DERGeneralizedTime tag5 = ( DERGeneralizedTime ) derObject;
+                    modifier.setClientTime( KerberosTimeDecoder.decode( tag5 ) );
+                    break;
+                case 6:
+                    DERSequence tag6 = ( DERSequence ) derObject;
+                    modifier.setSubSessionKey( EncryptionKeyDecoder.decode( tag6 ) );
+                    break;
+                case 7:
+                    DERInteger tag7 = ( DERInteger ) derObject;
+                    modifier.setSequenceNumber( tag7.intValue() );
+                    break;
+                case 8:
+                    DERSequence tag8 = ( DERSequence ) derObject;
+                    modifier.setAuthorizationData( AuthorizationDataDecoder.decodeSequence( tag8 ) );
+                    break;
+            }
+        }
+
+        return modifier.getAuthenticator();
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/AuthorizationDataDecoder.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/AuthorizationDataDecoder.java
new file mode 100644
index 0000000..fe8cb3b
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/AuthorizationDataDecoder.java
@@ -0,0 +1,108 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.io.decoder;
+
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+import org.apache.directory.server.kerberos.shared.messages.Encodable;
+import org.apache.directory.server.kerberos.shared.messages.value.AuthorizationData;
+import org.apache.directory.server.kerberos.shared.messages.value.AuthorizationDataEntry;
+import org.apache.directory.server.kerberos.shared.messages.value.types.AuthorizationType;
+import org.apache.directory.shared.asn1.der.ASN1InputStream;
+import org.apache.directory.shared.asn1.der.DEREncodable;
+import org.apache.directory.shared.asn1.der.DERInteger;
+import org.apache.directory.shared.asn1.der.DEROctetString;
+import org.apache.directory.shared.asn1.der.DERSequence;
+import org.apache.directory.shared.asn1.der.DERTaggedObject;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class AuthorizationDataDecoder implements Decoder, DecoderFactory
+{
+    public Decoder getDecoder()
+    {
+        return new AuthorizationDataDecoder();
+    }
+
+
+    public Encodable decode( byte[] encodedAuthData ) throws IOException
+    {
+        ASN1InputStream ais = new ASN1InputStream( encodedAuthData );
+
+        DERSequence sequence = ( DERSequence ) ais.readObject();
+
+        return decodeSequence( sequence );
+    }
+
+
+    /**
+     * AuthorizationData ::=   SEQUENCE OF SEQUENCE {
+     *     ad-type[0]               INTEGER,
+     *     ad-data[1]               OCTET STRING
+     * }
+     */
+    protected static AuthorizationData decodeSequence( DERSequence sequence )
+    {
+        AuthorizationData authData = new AuthorizationData();
+
+        for ( Enumeration<DEREncodable> e = sequence.getObjects(); e.hasMoreElements(); )
+        {
+            DERSequence object = ( DERSequence ) e.nextElement();
+            AuthorizationDataEntry entry = decodeAuthorizationEntry( object );
+            authData.add( entry );
+        }
+
+        return authData;
+    }
+
+
+    protected static AuthorizationDataEntry decodeAuthorizationEntry( DERSequence sequence )
+    {
+        AuthorizationType type = AuthorizationType.NULL;
+        byte[] data = null;
+
+        for ( Enumeration<DEREncodable> e = sequence.getObjects(); e.hasMoreElements(); )
+        {
+            DERTaggedObject object = ( DERTaggedObject ) e.nextElement();
+            int tag = object.getTagNo();
+            DEREncodable derObject = object.getObject();
+
+            switch ( tag )
+            {
+                case 0:
+                    DERInteger tag0 = ( DERInteger ) derObject;
+                    type = AuthorizationType.getTypeByOrdinal( tag0.intValue() );
+                    break;
+                    
+                case 1:
+                    DEROctetString tag1 = ( DEROctetString ) derObject;
+                    data = tag1.getOctets();
+                    break;
+            }
+        }
+
+        return new AuthorizationDataEntry( type, data );
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/ChecksumDecoder.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/ChecksumDecoder.java
new file mode 100644
index 0000000..f8a0408
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/ChecksumDecoder.java
@@ -0,0 +1,75 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.io.decoder;
+
+
+import java.util.Enumeration;
+
+import org.apache.directory.server.kerberos.shared.crypto.checksum.ChecksumType;
+import org.apache.directory.server.kerberos.shared.messages.value.Checksum;
+import org.apache.directory.shared.asn1.der.DEREncodable;
+import org.apache.directory.shared.asn1.der.DERInteger;
+import org.apache.directory.shared.asn1.der.DEROctetString;
+import org.apache.directory.shared.asn1.der.DERSequence;
+import org.apache.directory.shared.asn1.der.DERTaggedObject;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ChecksumDecoder
+{
+    /**
+     * Checksum ::=   SEQUENCE {
+     *          cksumtype[0]   INTEGER,
+     *          checksum[1]    OCTET STRING
+     * }
+     * @param sequence 
+     * @return The {@link Checksum}.
+     */
+    public static Checksum decode( DERSequence sequence )
+    {
+        ChecksumType type = ChecksumType.NULL;
+        byte[] data = null;
+
+        for ( Enumeration<DEREncodable> e = sequence.getObjects(); e.hasMoreElements(); )
+        {
+            DERTaggedObject object = ( DERTaggedObject ) e.nextElement();
+            int tag = object.getTagNo();
+            DEREncodable derObject = object.getObject();
+
+            switch ( tag )
+            {
+                case 0:
+                    DERInteger tag0 = ( DERInteger ) derObject;
+                    type = ChecksumType.getTypeByOrdinal( tag0.intValue() );
+                    break;
+                    
+                case 1:
+                    DEROctetString tag1 = ( DEROctetString ) derObject;
+                    data = tag1.getOctets();
+                    break;
+            }
+        }
+
+        return new Checksum( type, data );
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/Decoder.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/Decoder.java
new file mode 100644
index 0000000..70ae53b
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/Decoder.java
@@ -0,0 +1,43 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.kerberos.shared.io.decoder;
+
+
+import java.io.IOException;
+
+import org.apache.directory.server.kerberos.shared.messages.Encodable;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public interface Decoder
+{
+    /**
+     * Decodes the byte array into an {@link Encodable} object.
+     *
+     * @param object
+     * @return The {@link Encodable} object.
+     * @throws IOException
+     */
+    public Encodable decode( byte[] object ) throws IOException;
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/DecoderFactory.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/DecoderFactory.java
new file mode 100644
index 0000000..e7d7ceb
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/DecoderFactory.java
@@ -0,0 +1,36 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.kerberos.shared.io.decoder;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public interface DecoderFactory
+{
+    /**
+     * Returns the {@link Decoder}.
+     *
+     * @return The {@link Decoder}.
+     */
+    public Decoder getDecoder();
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/EncApRepPartDecoder.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/EncApRepPartDecoder.java
new file mode 100644
index 0000000..7d8dc20
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/EncApRepPartDecoder.java
@@ -0,0 +1,108 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.io.decoder;
+
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+import org.apache.directory.server.kerberos.shared.messages.Encodable;
+import org.apache.directory.server.kerberos.shared.messages.components.EncApRepPart;
+import org.apache.directory.server.kerberos.shared.messages.components.EncApRepPartModifier;
+import org.apache.directory.shared.asn1.der.ASN1InputStream;
+import org.apache.directory.shared.asn1.der.DERApplicationSpecific;
+import org.apache.directory.shared.asn1.der.DEREncodable;
+import org.apache.directory.shared.asn1.der.DERGeneralizedTime;
+import org.apache.directory.shared.asn1.der.DERInteger;
+import org.apache.directory.shared.asn1.der.DERSequence;
+import org.apache.directory.shared.asn1.der.DERTaggedObject;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 502338 $, $Date: 2007-02-01 11:59:43 -0800 (Thu, 01 Feb 2007) $
+ */
+public class EncApRepPartDecoder implements Decoder, DecoderFactory
+{
+    public Decoder getDecoder()
+    {
+        return new EncApRepPartDecoder();
+    }
+
+
+    public Encodable decode( byte[] encodedEncApRepPart ) throws IOException
+    {
+        ASN1InputStream ais = new ASN1InputStream( encodedEncApRepPart );
+
+        DERApplicationSpecific app = ( DERApplicationSpecific ) ais.readObject();
+
+        DERSequence apRepPart = ( DERSequence ) app.getObject();
+
+        return decodeEncApRepPartSequence( apRepPart );
+    }
+
+
+    /**
+     * Decodes a {@link DERSequence} into a {@link EncApRepPart}.
+     * 
+     * EncAPRepPart    ::= [APPLICATION 27] SEQUENCE {
+     *         ctime           [0] KerberosTime,
+     *         cusec           [1] Microseconds,
+     *         subkey          [2] EncryptionKey OPTIONAL,
+     *         seq-number      [3] UInt32 OPTIONAL
+     * }
+     *
+     * @param sequence
+     * @return The {@link EncApRepPart}.
+     */
+    private EncApRepPart decodeEncApRepPartSequence( DERSequence sequence )
+    {
+        EncApRepPartModifier modifier = new EncApRepPartModifier();
+
+        for ( Enumeration e = sequence.getObjects(); e.hasMoreElements(); )
+        {
+            DERTaggedObject object = ( DERTaggedObject ) e.nextElement();
+            int tag = object.getTagNo();
+            DEREncodable derObject = object.getObject();
+
+            switch ( tag )
+            {
+                case 0:
+                    DERGeneralizedTime tag0 = ( DERGeneralizedTime ) derObject;
+                    modifier.setClientTime( KerberosTimeDecoder.decode( tag0 ) );
+                    break;
+                case 1:
+                    DERInteger tag1 = ( DERInteger ) derObject;
+                    modifier.setClientMicroSecond( new Integer( tag1.intValue() ) );
+                    break;
+                case 2:
+                    DERSequence tag2 = ( DERSequence ) derObject;
+                    modifier.setSubSessionKey( EncryptionKeyDecoder.decode( tag2 ) );
+                    break;
+                case 3:
+                    DERInteger tag3 = ( DERInteger ) derObject;
+                    modifier.setSequenceNumber( new Integer( tag3.intValue() ) );
+                    break;
+            }
+        }
+
+        return modifier.getEncApRepPart();
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/EncKdcRepPartDecoder.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/EncKdcRepPartDecoder.java
new file mode 100644
index 0000000..ea6c86a
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/EncKdcRepPartDecoder.java
@@ -0,0 +1,149 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.io.decoder;
+
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+import org.apache.directory.server.kerberos.shared.messages.Encodable;
+import org.apache.directory.server.kerberos.shared.messages.components.EncKdcRepPart;
+import org.apache.directory.server.kerberos.shared.messages.value.KerberosPrincipalModifier;
+import org.apache.directory.server.kerberos.shared.messages.value.flags.TicketFlags;
+import org.apache.directory.shared.asn1.der.ASN1InputStream;
+import org.apache.directory.shared.asn1.der.DERApplicationSpecific;
+import org.apache.directory.shared.asn1.der.DERBitString;
+import org.apache.directory.shared.asn1.der.DEREncodable;
+import org.apache.directory.shared.asn1.der.DERGeneralString;
+import org.apache.directory.shared.asn1.der.DERGeneralizedTime;
+import org.apache.directory.shared.asn1.der.DERInteger;
+import org.apache.directory.shared.asn1.der.DERSequence;
+import org.apache.directory.shared.asn1.der.DERTaggedObject;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 502338 $, $Date: 2007-02-01 11:59:43 -0800 (Thu, 01 Feb 2007) $
+ */
+public class EncKdcRepPartDecoder implements Decoder, DecoderFactory
+{
+    public Decoder getDecoder()
+    {
+        return new EncKdcRepPartDecoder();
+    }
+
+
+    public Encodable decode( byte[] encoded ) throws IOException
+    {
+        ASN1InputStream ais = new ASN1InputStream( encoded );
+
+        DERApplicationSpecific app = ( DERApplicationSpecific ) ais.readObject();
+
+        DERSequence sequence = ( DERSequence ) app.getObject();
+
+        return decodeEncKdcRepPartSequence( sequence );
+    }
+
+
+    /**
+     *    EncKDCRepPart ::=   SEQUENCE {
+     *                key[0]                       EncryptionKey,
+     *                last-req[1]                  LastReq,
+     *                nonce[2]                     INTEGER,
+     *                key-expiration[3]            KerberosTime OPTIONAL,
+     *                flags[4]                     TicketFlags,
+     *                authtime[5]                  KerberosTime,
+     *                starttime[6]                 KerberosTime OPTIONAL,
+     *                endtime[7]                   KerberosTime,
+     *                renew-till[8]                KerberosTime OPTIONAL,
+     *                srealm[9]                    Realm,
+     *                sname[10]                    PrincipalName,
+     *                caddr[11]                    HostAddresses OPTIONAL
+     * }
+     */
+    private EncKdcRepPart decodeEncKdcRepPartSequence( DERSequence sequence )
+    {
+        EncKdcRepPart modifier = new EncKdcRepPart();
+        KerberosPrincipalModifier principalModifier = new KerberosPrincipalModifier();
+
+        for ( Enumeration<DEREncodable> e = sequence.getObjects(); e.hasMoreElements(); )
+        {
+            DERTaggedObject object = ( DERTaggedObject ) e.nextElement();
+            int tag = object.getTagNo();
+            DEREncodable derObject = object.getObject();
+
+            switch ( tag )
+            {
+                case 0:
+                    DERSequence tag0 = ( DERSequence ) derObject;
+                    modifier.setKey( EncryptionKeyDecoder.decode( tag0 ) );
+                    break;
+                case 1:
+                    DERSequence tag1 = ( DERSequence ) derObject;
+                    modifier.setLastRequest( LastRequestDecoder.decodeSequence( tag1 ) );
+                    break;
+                case 2:
+                    DERInteger tag2 = ( DERInteger ) derObject;
+                    modifier.setNonce( new Integer( tag2.intValue() ) );
+                    break;
+                case 3:
+                    DERGeneralizedTime tag3 = ( DERGeneralizedTime ) derObject;
+                    modifier.setKeyExpiration( KerberosTimeDecoder.decode( tag3 ) );
+                    break;
+                case 4:
+                    DERBitString tag4 = ( DERBitString ) derObject;
+                    modifier.setFlags( new TicketFlags( tag4.getOctets() ) );
+                    break;
+                case 5:
+                    DERGeneralizedTime tag5 = ( DERGeneralizedTime ) derObject;
+                    modifier.setAuthTime( KerberosTimeDecoder.decode( tag5 ) );
+                    break;
+                case 6:
+                    DERGeneralizedTime tag6 = ( DERGeneralizedTime ) derObject;
+                    modifier.setStartTime( KerberosTimeDecoder.decode( tag6 ) );
+                    break;
+                case 7:
+                    DERGeneralizedTime tag7 = ( DERGeneralizedTime ) derObject;
+                    modifier.setEndTime( KerberosTimeDecoder.decode( tag7 ) );
+                    break;
+                case 8:
+                    DERGeneralizedTime tag8 = ( DERGeneralizedTime ) derObject;
+                    modifier.setRenewTill( KerberosTimeDecoder.decode( tag8 ) );
+                    break;
+                case 9:
+                    DERGeneralString tag9 = ( DERGeneralString ) derObject;
+                    principalModifier.setRealm( tag9.getString() );
+                    break;
+                case 10:
+                    DERSequence tag10 = ( DERSequence ) derObject;
+                    principalModifier.setPrincipalName( PrincipalNameDecoder.decode( tag10 ) );
+                    break;
+                case 11:
+                    DERSequence tag11 = ( DERSequence ) derObject;
+                    modifier.setClientAddresses( HostAddressDecoder.decodeSequence( tag11 ) );
+                    break;
+            }
+        }
+
+        modifier.setServerPrincipal( principalModifier.getKerberosPrincipal() );
+
+        return modifier;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/EncKrbPrivPartDecoder.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/EncKrbPrivPartDecoder.java
new file mode 100644
index 0000000..fb22aec
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/EncKrbPrivPartDecoder.java
@@ -0,0 +1,103 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.io.decoder;
+
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+import org.apache.directory.server.kerberos.shared.messages.Encodable;
+import org.apache.directory.server.kerberos.shared.messages.components.EncKrbPrivPart;
+import org.apache.directory.server.kerberos.shared.messages.components.EncKrbPrivPartModifier;
+import org.apache.directory.shared.asn1.der.ASN1InputStream;
+import org.apache.directory.shared.asn1.der.DERApplicationSpecific;
+import org.apache.directory.shared.asn1.der.DEREncodable;
+import org.apache.directory.shared.asn1.der.DERGeneralizedTime;
+import org.apache.directory.shared.asn1.der.DERInteger;
+import org.apache.directory.shared.asn1.der.DEROctetString;
+import org.apache.directory.shared.asn1.der.DERSequence;
+import org.apache.directory.shared.asn1.der.DERTaggedObject;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class EncKrbPrivPartDecoder implements Decoder, DecoderFactory
+{
+    public Decoder getDecoder()
+    {
+        return new EncKrbPrivPartDecoder();
+    }
+
+
+    public Encodable decode( byte[] encodedPrivatePart ) throws IOException
+    {
+        ASN1InputStream ais = new ASN1InputStream( encodedPrivatePart );
+
+        DERApplicationSpecific app = ( DERApplicationSpecific ) ais.readObject();
+
+        DERSequence privatePart = ( DERSequence ) app.getObject();
+
+        return decodePrivatePartSequence( privatePart );
+    }
+
+
+    private EncKrbPrivPart decodePrivatePartSequence( DERSequence sequence )
+    {
+        EncKrbPrivPartModifier modifier = new EncKrbPrivPartModifier();
+
+        for ( Enumeration e = sequence.getObjects(); e.hasMoreElements(); )
+        {
+            DERTaggedObject object = ( DERTaggedObject ) e.nextElement();
+            int tag = object.getTagNo();
+            DEREncodable derObject = object.getObject();
+
+            switch ( tag )
+            {
+                case 0:
+                    DEROctetString tag0 = ( DEROctetString ) derObject;
+                    modifier.setUserData( tag0.getOctets() );
+                    break;
+                case 1:
+                    DERGeneralizedTime tag1 = ( DERGeneralizedTime ) derObject;
+                    modifier.setTimestamp( KerberosTimeDecoder.decode( tag1 ) );
+                    break;
+                case 2:
+                    DERInteger tag2 = ( DERInteger ) derObject;
+                    modifier.setMicroSecond( new Integer( tag2.intValue() ) );
+                    break;
+                case 3:
+                    DERInteger tag3 = ( DERInteger ) derObject;
+                    modifier.setSequenceNumber( new Integer( tag3.intValue() ) );
+                    break;
+                case 4:
+                    DERSequence tag4 = ( DERSequence ) derObject;
+                    modifier.setSenderAddress( HostAddressDecoder.decode( tag4 ) );
+                    break;
+                case 5:
+                    DERSequence tag5 = ( DERSequence ) derObject;
+                    modifier.setRecipientAddress( HostAddressDecoder.decode( tag5 ) );
+                    break;
+            }
+        }
+        return modifier.getEncKrbPrivPart();
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/EncTicketPartDecoder.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/EncTicketPartDecoder.java
new file mode 100644
index 0000000..584ff8f
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/EncTicketPartDecoder.java
@@ -0,0 +1,188 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.io.decoder;
+
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+import org.apache.directory.server.kerberos.shared.messages.Encodable;
+import org.apache.directory.server.kerberos.shared.messages.components.EncTicketPart;
+import org.apache.directory.server.kerberos.shared.messages.components.EncTicketPartModifier;
+import org.apache.directory.server.kerberos.shared.messages.value.TransitedEncoding;
+import org.apache.directory.server.kerberos.shared.messages.value.flags.TicketFlags;
+import org.apache.directory.server.kerberos.shared.messages.value.types.TransitedEncodingType;
+import org.apache.directory.shared.asn1.der.ASN1InputStream;
+import org.apache.directory.shared.asn1.der.DERApplicationSpecific;
+import org.apache.directory.shared.asn1.der.DERBitString;
+import org.apache.directory.shared.asn1.der.DEREncodable;
+import org.apache.directory.shared.asn1.der.DERGeneralString;
+import org.apache.directory.shared.asn1.der.DERGeneralizedTime;
+import org.apache.directory.shared.asn1.der.DERInteger;
+import org.apache.directory.shared.asn1.der.DEROctetString;
+import org.apache.directory.shared.asn1.der.DERSequence;
+import org.apache.directory.shared.asn1.der.DERTaggedObject;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class EncTicketPartDecoder implements Decoder, DecoderFactory
+{
+    public Decoder getDecoder()
+    {
+        return new EncTicketPartDecoder();
+    }
+
+
+    public Encodable decode( byte[] encodedTicket ) throws IOException
+    {
+        ASN1InputStream ais = new ASN1InputStream( encodedTicket );
+
+        DERApplicationSpecific app = ( DERApplicationSpecific ) ais.readObject();
+
+        DERSequence sequence = ( DERSequence ) app.getObject();
+
+        return decodeEncTicketPartSequence( sequence );
+    }
+
+
+    /*
+     -- Encrypted part of ticket
+     EncTicketPart ::=     [APPLICATION 3] SEQUENCE {
+     flags[0]             TicketFlags,
+     key[1]               EncryptionKey,
+     crealm[2]            Realm,
+     cname[3]             PrincipalName,
+     transited[4]         TransitedEncoding,
+     authtime[5]          KerberosTime,
+     starttime[6]         KerberosTime OPTIONAL,
+     endtime[7]           KerberosTime,
+     renew-till[8]        KerberosTime OPTIONAL,
+     caddr[9]             HostAddresses OPTIONAL,
+     authorization-data[10]   AuthorizationData OPTIONAL
+     }*/
+    private EncTicketPart decodeEncTicketPartSequence( DERSequence sequence )
+    {
+        EncTicketPartModifier modifier = new EncTicketPartModifier();
+
+        for ( Enumeration<DEREncodable> e = sequence.getObjects(); e.hasMoreElements(); )
+        {
+            DERTaggedObject object = ( DERTaggedObject ) e.nextElement();
+            int tag = object.getTagNo();
+            DEREncodable derObject = object.getObject();
+
+            switch ( tag )
+            {
+                case 0:
+                    DERBitString tag0 = ( DERBitString ) derObject;
+                    modifier.setFlags( new TicketFlags( tag0.getOctets() ) );
+                    break;
+                    
+                case 1:
+                    DERSequence tag1 = ( DERSequence ) derObject;
+                    modifier.setSessionKey( EncryptionKeyDecoder.decode( tag1 ) );
+                    break;
+                    
+                case 2:
+                    DERGeneralString tag2 = ( DERGeneralString ) derObject;
+                    modifier.setClientRealm( tag2.getString() );
+                    break;
+                    
+                case 3:
+                    DERSequence tag3 = ( DERSequence ) derObject;
+                    modifier.setClientName( PrincipalNameDecoder.decode( tag3 ) );
+                    break;
+                    
+                case 4:
+                    DERSequence tag4 = ( DERSequence ) derObject;
+                    modifier.setTransitedEncoding( decodeTransitedEncoding( tag4 ) );
+                    break;
+                    
+                case 5:
+                    DERGeneralizedTime tag5 = ( DERGeneralizedTime ) derObject;
+                    modifier.setAuthTime( KerberosTimeDecoder.decode( tag5 ) );
+                    break;
+                    
+                case 6:
+                    DERGeneralizedTime tag6 = ( DERGeneralizedTime ) derObject;
+                    modifier.setStartTime( KerberosTimeDecoder.decode( tag6 ) );
+                    break;
+                    
+                case 7:
+                    DERGeneralizedTime tag7 = ( DERGeneralizedTime ) derObject;
+                    modifier.setEndTime( KerberosTimeDecoder.decode( tag7 ) );
+                    break;
+                    
+                case 8:
+                    DERGeneralizedTime tag8 = ( DERGeneralizedTime ) derObject;
+                    modifier.setRenewTill( KerberosTimeDecoder.decode( tag8 ) );
+                    break;
+                    
+                case 9:
+                    DERSequence tag9 = ( DERSequence ) derObject;
+                    modifier.setClientAddresses( HostAddressDecoder.decodeSequence( tag9 ) );
+                    break;
+                    
+                case 10:
+                    DERSequence tag10 = ( DERSequence ) derObject;
+                    modifier.setAuthorizationData( AuthorizationDataDecoder.decodeSequence( tag10 ) );
+                    break;
+            }
+        }
+        return modifier.getEncTicketPart();
+    }
+
+
+    /*
+     * TransitedEncoding ::= SEQUENCE {
+     *   tr-type[0] INTEGER, -- must be
+     *   registered contents[1] OCTET STRING
+     * }
+     */
+    protected TransitedEncoding decodeTransitedEncoding( DERSequence sequence )
+    {
+        TransitedEncodingType type = TransitedEncodingType.NULL;
+        byte[] contents = null;
+
+        for ( Enumeration<DEREncodable> e = sequence.getObjects(); e.hasMoreElements(); )
+        {
+            DERTaggedObject object = ( DERTaggedObject ) e.nextElement();
+            int tag = object.getTagNo();
+            DEREncodable derObject = object.getObject();
+
+            switch ( tag )
+            {
+                case 0:
+                    DERInteger tag0 = ( DERInteger ) derObject;
+                    type = TransitedEncodingType.getTypeByOrdinal( tag0.intValue() );
+                    break;
+                    
+                case 1:
+                    DEROctetString tag1 = ( DEROctetString ) derObject;
+                    contents = tag1.getOctets();
+                    break;
+            }
+        }
+
+        return new TransitedEncoding( type, contents );
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/EncryptedDataDecoder.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/EncryptedDataDecoder.java
new file mode 100644
index 0000000..710bd91
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/EncryptedDataDecoder.java
@@ -0,0 +1,102 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.io.decoder;
+
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptedData;
+import org.apache.directory.shared.asn1.der.ASN1InputStream;
+import org.apache.directory.shared.asn1.der.DEREncodable;
+import org.apache.directory.shared.asn1.der.DERInteger;
+import org.apache.directory.shared.asn1.der.DEROctetString;
+import org.apache.directory.shared.asn1.der.DERSequence;
+import org.apache.directory.shared.asn1.der.DERTaggedObject;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class EncryptedDataDecoder
+{
+    /**
+     * Decodes a byte array into an {@link EncryptedData}.
+     *
+     * @param encodedEncryptedData
+     * @return The {@link EncryptedData}.
+     * @throws IOException
+     */
+    public static EncryptedData decode( byte[] encodedEncryptedData ) throws IOException
+    {
+        ASN1InputStream ais = new ASN1InputStream( encodedEncryptedData );
+
+        DERSequence sequence = ( DERSequence ) ais.readObject();
+
+        return decode( sequence );
+    }
+
+
+    /**
+     * Decodes a {@link DERSequence} into an {@link EncryptedData}.
+     * 
+     * EncryptedData ::=   SEQUENCE {
+     *             etype[0]     INTEGER, -- EncryptionEngine
+     *             kvno[1]      INTEGER OPTIONAL,
+     *             cipher[2]    OCTET STRING -- ciphertext
+     * }
+     * 
+     * @param sequence 
+     * @return The {@link EncryptedData}.
+     */
+    public static EncryptedData decode( DERSequence sequence )
+    {
+        EncryptedData encryptedData = new EncryptedData();
+
+        for ( Enumeration<DEREncodable> e = sequence.getObjects(); e.hasMoreElements(); )
+        {
+            DERTaggedObject object = ( DERTaggedObject ) e.nextElement();
+            int tag = object.getTagNo();
+            DEREncodable derObject = object.getObject();
+
+            switch ( tag )
+            {
+                case 0:
+                    DERInteger etype = ( DERInteger ) derObject;
+                    encryptedData.setEType( EncryptionType.getTypeByOrdinal( etype.intValue() ) );
+                    break;
+                    
+                case 1:
+                    DERInteger kvno = ( DERInteger ) derObject;
+                    encryptedData.setKvno( kvno.intValue() );
+                    break;
+                    
+                case 2:
+                    DEROctetString cipher = ( DEROctetString ) derObject;
+                    encryptedData.setCipher( cipher.getOctets() );
+                    break;
+            }
+        }
+
+        return encryptedData;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/EncryptedTimestampDecoder.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/EncryptedTimestampDecoder.java
new file mode 100644
index 0000000..9f4d9de
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/EncryptedTimestampDecoder.java
@@ -0,0 +1,92 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.io.decoder;
+
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+import org.apache.directory.server.kerberos.shared.messages.Encodable;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptedTimeStamp;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptedTimeStampModifier;
+import org.apache.directory.shared.asn1.der.ASN1InputStream;
+import org.apache.directory.shared.asn1.der.DEREncodable;
+import org.apache.directory.shared.asn1.der.DERGeneralizedTime;
+import org.apache.directory.shared.asn1.der.DERInteger;
+import org.apache.directory.shared.asn1.der.DERSequence;
+import org.apache.directory.shared.asn1.der.DERTaggedObject;
+
+
+/**
+ * padata-type     ::= PA-ENC-TIMESTAMP
+ * padata-value    ::= EncryptedData -- PA-ENC-TS-ENC
+ * 
+ * PA-ENC-TS-ENC   ::= SEQUENCE {
+ *         patimestamp[0]               KerberosTime, -- client's time
+ *         pausec[1]                    INTEGER OPTIONAL
+ * }
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class EncryptedTimestampDecoder implements Decoder, DecoderFactory
+{
+    public Decoder getDecoder()
+    {
+        return new EncryptedTimestampDecoder();
+    }
+
+
+    public Encodable decode( byte[] encodedEncryptedTimestamp ) throws IOException
+    {
+        ASN1InputStream ais = new ASN1InputStream( encodedEncryptedTimestamp );
+
+        DERSequence sequence = ( DERSequence ) ais.readObject();
+
+        return decodeEncryptedTimestamp( sequence );
+    }
+
+
+    protected EncryptedTimeStamp decodeEncryptedTimestamp( DERSequence sequence )
+    {
+        EncryptedTimeStampModifier modifier = new EncryptedTimeStampModifier();
+
+        for ( Enumeration e = sequence.getObjects(); e.hasMoreElements(); )
+        {
+            DERTaggedObject object = ( DERTaggedObject ) e.nextElement();
+            int tag = object.getTagNo();
+            DEREncodable derObject = object.getObject();
+
+            switch ( tag )
+            {
+                case 0:
+                    DERGeneralizedTime tag0 = ( DERGeneralizedTime ) derObject;
+                    modifier.setKerberosTime( KerberosTimeDecoder.decode( tag0 ) );
+                    break;
+                case 1:
+                    DERInteger tag1 = ( DERInteger ) derObject;
+                    modifier.setMicroSecond( tag1.intValue() );
+                    break;
+            }
+        }
+
+        return modifier.getEncryptedTimestamp();
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/EncryptionKeyDecoder.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/EncryptionKeyDecoder.java
new file mode 100644
index 0000000..319410f
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/EncryptionKeyDecoder.java
@@ -0,0 +1,91 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.io.decoder;
+
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+import org.apache.directory.shared.asn1.der.ASN1InputStream;
+import org.apache.directory.shared.asn1.der.DEREncodable;
+import org.apache.directory.shared.asn1.der.DERInteger;
+import org.apache.directory.shared.asn1.der.DEROctetString;
+import org.apache.directory.shared.asn1.der.DERSequence;
+import org.apache.directory.shared.asn1.der.DERTaggedObject;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class EncryptionKeyDecoder
+{
+    /**
+     * Decodes a byte array into an {@link EncryptionKey}.
+     *
+     * @param encodedEncryptionKey
+     * @return The {@link EncryptionKey}.
+     * @throws IOException
+     */
+    public static EncryptionKey decode( byte[] encodedEncryptionKey ) throws IOException
+    {
+        ASN1InputStream ais = new ASN1InputStream( encodedEncryptionKey );
+
+        DERSequence sequence = ( DERSequence ) ais.readObject();
+
+        return decode( sequence );
+    }
+
+
+    /**
+     * EncryptionKey ::=   SEQUENCE {
+     *     keytype[0]    INTEGER,
+     *     keyvalue[1]   OCTET STRING
+     * }
+     */
+    protected static EncryptionKey decode( DERSequence sequence )
+    {
+        EncryptionType type = EncryptionType.NULL;
+        byte[] data = null;
+
+        for ( Enumeration e = sequence.getObjects(); e.hasMoreElements(); )
+        {
+            DERTaggedObject object = ( DERTaggedObject ) e.nextElement();
+            int tag = object.getTagNo();
+            DEREncodable derObject = object.getObject();
+
+            switch ( tag )
+            {
+                case 0:
+                    DERInteger tag0 = ( DERInteger ) derObject;
+                    type = EncryptionType.getTypeByOrdinal( tag0.intValue() );
+                    break;
+                case 1:
+                    DEROctetString tag1 = ( DEROctetString ) derObject;
+                    data = tag1.getOctets();
+                    break;
+            }
+        }
+
+        return new EncryptionKey( type, data );
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/EncryptionTypeDecoder.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/EncryptionTypeDecoder.java
new file mode 100644
index 0000000..03492ee
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/EncryptionTypeDecoder.java
@@ -0,0 +1,58 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.io.decoder;
+
+
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
+import org.apache.directory.shared.asn1.der.DEREncodable;
+import org.apache.directory.shared.asn1.der.DERInteger;
+import org.apache.directory.shared.asn1.der.DERSequence;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class EncryptionTypeDecoder
+{
+    /**
+     * etype[8]             SEQUENCE OF INTEGER, -- EncryptionType,
+     *             -- in preference order
+     */
+    protected static Set<EncryptionType> decode( DERSequence sequence )
+    {
+        Set<EncryptionType> eTypes = new HashSet<EncryptionType>( sequence.size() );
+
+        int ii = 0;
+        
+        for ( Enumeration<DEREncodable> e = sequence.getObjects(); e.hasMoreElements(); )
+        {
+            DERInteger object = ( DERInteger ) e.nextElement();
+            eTypes.add( EncryptionType.getTypeByOrdinal( object.intValue() ) );
+            ii++;
+        }
+
+        return eTypes;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/EncryptionTypeInfo2Decoder.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/EncryptionTypeInfo2Decoder.java
new file mode 100644
index 0000000..6cc5f64
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/EncryptionTypeInfo2Decoder.java
@@ -0,0 +1,117 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.io.decoder;
+
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionTypeInfo2Entry;
+import org.apache.directory.shared.asn1.der.ASN1InputStream;
+import org.apache.directory.shared.asn1.der.DEREncodable;
+import org.apache.directory.shared.asn1.der.DERGeneralString;
+import org.apache.directory.shared.asn1.der.DERInteger;
+import org.apache.directory.shared.asn1.der.DEROctetString;
+import org.apache.directory.shared.asn1.der.DERSequence;
+import org.apache.directory.shared.asn1.der.DERTaggedObject;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 540371 $, $Date: 2007-05-21 17:00:43 -0700 (Mon, 21 May 2007) $
+ */
+public class EncryptionTypeInfo2Decoder
+{
+    /**
+     * Decodes a byte array into an array of {@link EncryptionTypeInfo2Entry}.
+     *
+     * @param encodedEntries
+     * @return The array of {@link EncryptionTypeInfo2Entry}.
+     * @throws IOException
+     */
+    public EncryptionTypeInfo2Entry[] decode( byte[] encodedEntries ) throws IOException
+    {
+        ASN1InputStream ais = new ASN1InputStream( encodedEntries );
+
+        DERSequence sequence = ( DERSequence ) ais.readObject();
+
+        return decodeSequence( sequence );
+    }
+
+
+    /**
+     * ETYPE-INFO2             ::= SEQUENCE SIZE (1..MAX) OF ETYPE-INFO2-ENTRY
+     */
+    protected static EncryptionTypeInfo2Entry[] decodeSequence( DERSequence sequence )
+    {
+        EncryptionTypeInfo2Entry[] entrySequence = new EncryptionTypeInfo2Entry[sequence.size()];
+
+        int ii = 0;
+        for ( Enumeration<DEREncodable> e = sequence.getObjects(); e.hasMoreElements(); )
+        {
+            DERSequence object = (DERSequence)e.nextElement();
+            entrySequence[ii] = decode( object );
+            ii++;
+        }
+
+        return entrySequence;
+    }
+
+
+    /**
+     * ETYPE-INFO2-ENTRY       ::= SEQUENCE {
+     *         etype           [0] Int32,
+     *         salt            [1] KerberosString OPTIONAL,
+     *         s2kparams       [2] OCTET STRING OPTIONAL
+     * }
+     */
+    protected static EncryptionTypeInfo2Entry decode( DERSequence sequence )
+    {
+        EncryptionType encryptionType = EncryptionType.NULL;
+        String salt = new String();
+        byte[] s2kparams = new byte[0];
+
+        for ( Enumeration<DEREncodable> e = sequence.getObjects(); e.hasMoreElements(); )
+        {
+            DERTaggedObject object = (DERTaggedObject)e.nextElement();
+            int tag = object.getTagNo();
+            DEREncodable derObject = object.getObject();
+
+            switch ( tag )
+            {
+                case 0:
+                    DERInteger tag0 = ( DERInteger ) derObject;
+                    encryptionType = EncryptionType.getTypeByOrdinal( tag0.intValue() );
+                    break;
+                case 1:
+                    DERGeneralString tag1 = ( DERGeneralString ) derObject;
+                    salt = tag1.getString();
+                    break;
+                case 2:
+                    DEROctetString tag2 = ( DEROctetString ) derObject;
+                    s2kparams = tag2.getOctets();
+                    break;
+            }
+        }
+
+        return new EncryptionTypeInfo2Entry( encryptionType, salt, s2kparams );
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/EncryptionTypeInfoDecoder.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/EncryptionTypeInfoDecoder.java
new file mode 100644
index 0000000..d872e25
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/EncryptionTypeInfoDecoder.java
@@ -0,0 +1,110 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.io.decoder;
+
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionTypeInfoEntry;
+import org.apache.directory.shared.asn1.der.ASN1InputStream;
+import org.apache.directory.shared.asn1.der.DEREncodable;
+import org.apache.directory.shared.asn1.der.DERInteger;
+import org.apache.directory.shared.asn1.der.DEROctetString;
+import org.apache.directory.shared.asn1.der.DERSequence;
+import org.apache.directory.shared.asn1.der.DERTaggedObject;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 540371 $, $Date: 2007-05-21 17:00:43 -0700 (Mon, 21 May 2007) $
+ */
+public class EncryptionTypeInfoDecoder
+{
+    /**
+     * Decodes a byte array into an array of {@link EncryptionTypeInfoEntry}.
+     *
+     * @param encodedEntries
+     * @return The array of {@link EncryptionTypeInfoEntry}.
+     * @throws IOException
+     */
+    public EncryptionTypeInfoEntry[] decode( byte[] encodedEntries ) throws IOException
+    {
+        ASN1InputStream ais = new ASN1InputStream( encodedEntries );
+
+        DERSequence sequence = ( DERSequence ) ais.readObject();
+
+        return decodeSequence( sequence );
+    }
+
+
+    /**
+     * ETYPE-INFO              ::= SEQUENCE OF ETYPE-INFO-ENTRY
+     */
+    protected static EncryptionTypeInfoEntry[] decodeSequence( DERSequence sequence )
+    {
+        EncryptionTypeInfoEntry[] entrySequence = new EncryptionTypeInfoEntry[sequence.size()];
+
+        int ii = 0;
+        for ( Enumeration<DEREncodable> e = sequence.getObjects(); e.hasMoreElements(); )
+        {
+            DERSequence object = (DERSequence)e.nextElement();
+            entrySequence[ii] = decode( object );
+            ii++;
+        }
+
+        return entrySequence;
+    }
+
+
+    /**
+     * ETYPE-INFO-ENTRY        ::= SEQUENCE {
+     *     etype               [0] Int32,
+     *     salt                [1] OCTET STRING OPTIONAL
+     * }
+     */
+    protected static EncryptionTypeInfoEntry decode( DERSequence sequence )
+    {
+        EncryptionType encryptionType = EncryptionType.NULL;
+        byte[] salt = new byte[0];
+
+        for ( Enumeration<DEREncodable> e = sequence.getObjects(); e.hasMoreElements(); )
+        {
+            DERTaggedObject object = (DERTaggedObject)e.nextElement();
+            int tag = object.getTagNo();
+            DEREncodable derObject = object.getObject();
+
+            switch ( tag )
+            {
+                case 0:
+                    DERInteger type = ( DERInteger ) derObject;
+                    encryptionType = EncryptionType.getTypeByOrdinal( type.intValue() );
+                    break;
+                case 1:
+                    DEROctetString value = ( DEROctetString ) derObject;
+                    salt = value.getOctets();
+                    break;
+            }
+        }
+
+        return new EncryptionTypeInfoEntry( encryptionType, salt );
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/ErrorMessageDecoder.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/ErrorMessageDecoder.java
new file mode 100644
index 0000000..d38dc40
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/ErrorMessageDecoder.java
@@ -0,0 +1,159 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.io.decoder;
+
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Enumeration;
+
+import org.apache.directory.server.kerberos.shared.messages.ErrorMessage;
+import org.apache.directory.server.kerberos.shared.messages.ErrorMessageModifier;
+import org.apache.directory.server.kerberos.shared.messages.value.KerberosPrincipalModifier;
+import org.apache.directory.shared.asn1.der.ASN1InputStream;
+import org.apache.directory.shared.asn1.der.DERApplicationSpecific;
+import org.apache.directory.shared.asn1.der.DEREncodable;
+import org.apache.directory.shared.asn1.der.DERGeneralString;
+import org.apache.directory.shared.asn1.der.DERGeneralizedTime;
+import org.apache.directory.shared.asn1.der.DERInteger;
+import org.apache.directory.shared.asn1.der.DEROctetString;
+import org.apache.directory.shared.asn1.der.DERSequence;
+import org.apache.directory.shared.asn1.der.DERTaggedObject;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ErrorMessageDecoder
+{
+    /**
+     * Decodes a {@link ByteBuffer} into an {@link ErrorMessage}.
+     * 
+     * KRB-ERROR       ::= [APPLICATION 30] SEQUENCE
+     *
+     * @param in
+     * @return The {@link ErrorMessage}.
+     * @throws IOException
+     */
+    public ErrorMessage decode( ByteBuffer in ) throws IOException
+    {
+        ASN1InputStream ais = new ASN1InputStream( in );
+
+        DERApplicationSpecific app = ( DERApplicationSpecific ) ais.readObject();
+
+        DERSequence errorMessage = ( DERSequence ) app.getObject();
+
+        return decodeErrorMessageSequence( errorMessage );
+    }
+
+
+    /*
+     KRB-ERROR       ::= [APPLICATION 30] SEQUENCE {
+     pvno            [0] INTEGER (5),
+     msg-type        [1] INTEGER (30),
+     ctime           [2] KerberosTime OPTIONAL,
+     cusec           [3] Microseconds OPTIONAL,
+     stime           [4] KerberosTime,
+     susec           [5] Microseconds,
+     error-code      [6] Int32,
+     crealm          [7] Realm OPTIONAL,
+     cname           [8] PrincipalName OPTIONAL,
+     realm           [9] Realm -- service realm --,
+     sname           [10] PrincipalName -- service name --,
+     e-text          [11] KerberosString OPTIONAL,
+     e-data          [12] OCTET STRING OPTIONAL
+     }
+     */
+    private ErrorMessage decodeErrorMessageSequence( DERSequence sequence )
+    {
+        ErrorMessageModifier errorModifier = new ErrorMessageModifier();
+        KerberosPrincipalModifier clientModifier = new KerberosPrincipalModifier();
+        KerberosPrincipalModifier serverModifier = new KerberosPrincipalModifier();
+
+        for ( Enumeration<DEREncodable> e = sequence.getObjects(); e.hasMoreElements(); )
+        {
+            DERTaggedObject object = ( DERTaggedObject ) e.nextElement();
+            int tag = object.getTagNo();
+            DEREncodable derObject = object.getObject();
+
+            switch ( tag )
+            {
+                case 0:
+                    // DERInteger tag0 = ( DERInteger ) derObject;
+                    // int pvno = tag0.intValue();
+                    break;
+                case 1:
+                    // DERInteger tag1 = ( DERInteger ) derObject;
+                    // msgType = MessageType.getTypeByOrdinal( tag1.intValue() );
+                    break;
+                case 2:
+                    DERGeneralizedTime tag2 = ( DERGeneralizedTime ) derObject;
+                    errorModifier.setClientTime( KerberosTimeDecoder.decode( tag2 ) );
+                    break;
+                case 3:
+                    DERInteger tag3 = ( DERInteger ) derObject;
+                    errorModifier.setClientMicroSecond( tag3.intValue() );
+                    break;
+                case 4:
+                    DERGeneralizedTime tag4 = ( DERGeneralizedTime ) derObject;
+                    errorModifier.setServerTime( KerberosTimeDecoder.decode( tag4 ) );
+                    break;
+                case 5:
+                    DERInteger tag5 = ( DERInteger ) derObject;
+                    errorModifier.setServerMicroSecond( tag5.intValue() );
+                    break;
+                case 6:
+                    DERInteger tag6 = ( DERInteger ) derObject;
+                    errorModifier.setErrorCode( tag6.intValue() );
+                    break;
+                case 7:
+                    DERGeneralString tag7 = ( DERGeneralString ) derObject;
+                    clientModifier.setRealm( tag7.getString() );
+                    break;
+                case 8:
+                    DERSequence tag8 = ( DERSequence ) derObject;
+                    clientModifier.setPrincipalName( PrincipalNameDecoder.decode( tag8 ) );
+                    break;
+                case 9:
+                    DERGeneralString tag9 = ( DERGeneralString ) derObject;
+                    serverModifier.setRealm( tag9.getString() );
+                    break;
+                case 10:
+                    DERSequence tag10 = ( DERSequence ) derObject;
+                    serverModifier.setPrincipalName( PrincipalNameDecoder.decode( tag10 ) );
+                    break;
+                case 11:
+                    DERGeneralString tag11 = ( DERGeneralString ) derObject;
+                    errorModifier.setExplanatoryText( tag11.getString() );
+                    break;
+                case 12:
+                    DEROctetString tag12 = ( DEROctetString ) derObject;
+                    errorModifier.setExplanatoryData( tag12.getOctets() );
+                    break;
+            }
+        }
+
+        errorModifier.setClientPrincipal( clientModifier.getKerberosPrincipal() );
+        errorModifier.setServerPrincipal( serverModifier.getKerberosPrincipal() );
+
+        return errorModifier.getErrorMessage();
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/HostAddressDecoder.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/HostAddressDecoder.java
new file mode 100644
index 0000000..7a00800
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/HostAddressDecoder.java
@@ -0,0 +1,98 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.io.decoder;
+
+
+import java.util.Enumeration;
+
+import org.apache.directory.server.kerberos.shared.messages.value.HostAddress;
+import org.apache.directory.server.kerberos.shared.messages.value.types.HostAddrType;
+import org.apache.directory.server.kerberos.shared.messages.value.HostAddresses;
+import org.apache.directory.shared.asn1.der.DEREncodable;
+import org.apache.directory.shared.asn1.der.DERInteger;
+import org.apache.directory.shared.asn1.der.DEROctetString;
+import org.apache.directory.shared.asn1.der.DERSequence;
+import org.apache.directory.shared.asn1.der.DERTaggedObject;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class HostAddressDecoder
+{
+    /**
+     * HostAddress ::=     SEQUENCE  {
+     *                     addr-type[0]             INTEGER,
+     *                     address[1]               OCTET STRING
+     * }
+     */
+    protected static HostAddress decode( DERSequence sequence )
+    {
+        HostAddrType type = HostAddrType.ADDRTYPE_INET;
+        byte[] value = null;
+
+        for ( Enumeration<DEREncodable> e = sequence.getObjects(); e.hasMoreElements(); )
+        {
+            DERTaggedObject object = ( DERTaggedObject ) e.nextElement();
+            int tag = object.getTagNo();
+            DEREncodable derObject = object.getObject();
+
+            switch ( tag )
+            {
+                case 0:
+                    DERInteger addressType = ( DERInteger ) derObject;
+                    type = HostAddrType.getTypeByOrdinal( addressType.intValue() );
+                    break;
+                    
+                case 1:
+                    DEROctetString address = ( DEROctetString ) derObject;
+                    value = address.getOctets();
+                    break;
+            }
+        }
+
+        return new HostAddress( type, value );
+    }
+
+
+    /**
+     * HostAddresses ::=   SEQUENCE OF SEQUENCE {
+     *                     addr-type[0]             INTEGER,
+     *                     address[1]               OCTET STRING
+     * }
+     */
+    protected static HostAddresses decodeSequence( DERSequence sequence )
+    {
+        HostAddress[] addresses = new HostAddress[sequence.size()];
+
+        int ii = 0;
+        
+        for ( Enumeration<DEREncodable> e = sequence.getObjects(); e.hasMoreElements(); )
+        {
+            DERSequence object = ( DERSequence ) e.nextElement();
+            HostAddress address = decode( object );
+            addresses[ii] = address;
+            ii++;
+        }
+
+        return new HostAddresses( addresses );
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/KdcReplyDecoder.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/KdcReplyDecoder.java
new file mode 100644
index 0000000..836d848
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/KdcReplyDecoder.java
@@ -0,0 +1,140 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.io.decoder;
+
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Enumeration;
+
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.apache.directory.server.kerberos.shared.KerberosMessageType;
+import org.apache.directory.server.kerberos.shared.messages.KdcReply;
+import org.apache.directory.server.kerberos.shared.messages.components.Ticket;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptedData;
+import org.apache.directory.server.kerberos.shared.messages.value.KerberosPrincipalModifier;
+import org.apache.directory.server.kerberos.shared.messages.value.PaData;
+import org.apache.directory.shared.asn1.der.ASN1InputStream;
+import org.apache.directory.shared.asn1.der.DERApplicationSpecific;
+import org.apache.directory.shared.asn1.der.DEREncodable;
+import org.apache.directory.shared.asn1.der.DERGeneralString;
+import org.apache.directory.shared.asn1.der.DERInteger;
+import org.apache.directory.shared.asn1.der.DERSequence;
+import org.apache.directory.shared.asn1.der.DERTaggedObject;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 540371 $, $Date: 2007-05-21 17:00:43 -0700 (Mon, 21 May 2007) $
+ */
+public class KdcReplyDecoder
+{
+    /**
+     * Decodes a {@link ByteBuffer} into a {@link KdcReply}.
+     * 
+     * AS-REP ::=    [APPLICATION 11] KDC-REP
+     * TGS-REP ::=   [APPLICATION 13] KDC-REP
+     *
+     * @param in
+     * @return The {@link KdcReply}.
+     * @throws IOException
+     */
+    public KdcReply decode( ByteBuffer in ) throws IOException
+    {
+        ASN1InputStream ais = new ASN1InputStream( in );
+
+        DERApplicationSpecific app = ( DERApplicationSpecific ) ais.readObject();
+
+        DERSequence kdcreq = ( DERSequence ) app.getObject();
+
+        return decodeKdcReplySequence( kdcreq );
+    }
+
+
+    /*
+     KDC-REP ::=   SEQUENCE {
+     pvno[0]                    INTEGER,
+     msg-type[1]                INTEGER,
+     padata[2]                  SEQUENCE OF PA-DATA OPTIONAL,
+     crealm[3]                  Realm,
+     cname[4]                   PrincipalName,
+     ticket[5]                  Ticket,
+     enc-part[6]                EncryptedData
+     }*/
+    private KdcReply decodeKdcReplySequence( DERSequence sequence ) throws IOException
+    {
+        KerberosMessageType msgType = null;
+        PaData[] paData = null;
+        Ticket ticket = null;
+        EncryptedData encPart = null;
+
+        KerberosPrincipalModifier modifier = new KerberosPrincipalModifier();
+
+        for ( Enumeration<DEREncodable> e = sequence.getObjects(); e.hasMoreElements(); )
+        {
+            DERTaggedObject object = ( DERTaggedObject ) e.nextElement();
+            int tag = object.getTagNo();
+            DEREncodable derObject = object.getObject();
+
+            switch ( tag )
+            {
+                case 0:
+                    // DERInteger tag0 = ( DERInteger ) derObject;
+                    // int pvno = tag0.intValue();
+                    break;
+                    
+                case 1:
+                    DERInteger tag1 = ( DERInteger ) derObject;
+                    msgType = KerberosMessageType.getTypeByOrdinal( tag1.intValue() );
+                    break;
+                    
+                case 2:
+                    DERSequence tag2 = ( DERSequence ) derObject;
+                    paData = PreAuthenticationDataDecoder.decodeSequence( tag2 );
+                    break;
+                    
+                case 3:
+                    DERGeneralString tag3 = ( DERGeneralString ) derObject;
+                    modifier.setRealm( tag3.getString() );
+                    break;
+                    
+                case 4:
+                    DERSequence tag4 = ( DERSequence ) derObject;
+                    modifier.setPrincipalName( PrincipalNameDecoder.decode( tag4 ) );
+                    break;
+                    
+                case 5:
+                    DERApplicationSpecific tag5 = ( DERApplicationSpecific ) derObject;
+                    ticket = TicketDecoder.decode( tag5 );
+                    break;
+                    
+                case 6:
+                    DERSequence tag6 = ( DERSequence ) derObject;
+                    encPart = ( EncryptedDataDecoder.decode( tag6 ) );
+                    break;
+            }
+        }
+
+        KerberosPrincipal clientPrincipal = modifier.getKerberosPrincipal();
+
+        return new KdcReply( paData, clientPrincipal, ticket, encPart, msgType );
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/KdcRequestDecoder.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/KdcRequestDecoder.java
new file mode 100644
index 0000000..e0db5e0
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/KdcRequestDecoder.java
@@ -0,0 +1,216 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.io.decoder;
+
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Enumeration;
+
+import org.apache.directory.server.kerberos.shared.KerberosConstants;
+import org.apache.directory.server.kerberos.shared.KerberosMessageType;
+import org.apache.directory.server.kerberos.shared.messages.KdcRequest;
+import org.apache.directory.server.kerberos.shared.messages.value.KdcOptions;
+import org.apache.directory.server.kerberos.shared.messages.value.PaData;
+import org.apache.directory.server.kerberos.shared.messages.value.RequestBody;
+import org.apache.directory.server.kerberos.shared.messages.value.RequestBodyModifier;
+import org.apache.directory.shared.asn1.der.ASN1InputStream;
+import org.apache.directory.shared.asn1.der.DERApplicationSpecific;
+import org.apache.directory.shared.asn1.der.DERBitString;
+import org.apache.directory.shared.asn1.der.DEREncodable;
+import org.apache.directory.shared.asn1.der.DERGeneralString;
+import org.apache.directory.shared.asn1.der.DERGeneralizedTime;
+import org.apache.directory.shared.asn1.der.DERInteger;
+import org.apache.directory.shared.asn1.der.DERSequence;
+import org.apache.directory.shared.asn1.der.DERTaggedObject;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class KdcRequestDecoder
+{
+    /**
+     * Decodes a {@link ByteBuffer} into a {@link KdcRequest}.
+     *
+     * @param in
+     * @return The {@link KdcRequest}.
+     * @throws IOException
+     */
+    public KdcRequest decode( ByteBuffer in ) throws IOException
+    {
+        ASN1InputStream ais = new ASN1InputStream( in );
+
+        DERApplicationSpecific app = ( DERApplicationSpecific ) ais.readObject();
+
+        DERSequence kdcreq = ( DERSequence ) app.getObject();
+
+        return decodeKdcRequestSequence( kdcreq );
+    }
+
+
+    /*
+     AS-REQ ::=         [APPLICATION 10] KDC-REQ
+     TGS-REQ ::=        [APPLICATION 12] KDC-REQ
+     
+     KDC-REQ ::=        SEQUENCE {
+     pvno[1]               INTEGER,
+     msg-type[2]           INTEGER,
+     padata[3]             SEQUENCE OF PA-DATA OPTIONAL,
+     req-body[4]           KDC-REQ-BODY
+     }*/
+    private KdcRequest decodeKdcRequestSequence( DERSequence sequence ) throws IOException
+    {
+        int pvno = KerberosConstants.KERBEROS_V5;
+        KerberosMessageType msgType = null;
+
+        PaData[] paData = null;
+        RequestBody requestBody = null;
+        byte[] bodyBytes = null;
+
+        for ( Enumeration<DEREncodable> e = sequence.getObjects(); e.hasMoreElements(); )
+        {
+            DERTaggedObject object = ( DERTaggedObject ) e.nextElement();
+            int tag = object.getTagNo();
+            DEREncodable derObject = object.getObject();
+
+            switch ( tag )
+            {
+                case 1:
+                    DERInteger tag1 = ( DERInteger ) derObject;
+                    pvno = tag1.intValue();
+                    break;
+                    
+                case 2:
+                    DERInteger tag2 = ( DERInteger ) derObject;
+                    msgType = KerberosMessageType.getTypeByOrdinal( tag2.intValue() );
+                    break;
+                    
+                case 3:
+                    DERSequence tag3 = ( DERSequence ) derObject;
+                    paData = PreAuthenticationDataDecoder.decodeSequence( tag3 );
+                    break;
+                    
+                case 4:
+                    DERSequence tag4 = ( DERSequence ) derObject;
+                    requestBody = decodeRequestBody( tag4 );
+
+                    /**
+                     * Get the raw bytes of the KDC-REQ-BODY for checksum calculation and
+                     * comparison with the authenticator checksum during the verification
+                     * stage of ticket grant processing.
+                     */
+                    bodyBytes = object.getOctets();
+
+                    break;
+            }
+        }
+
+        return new KdcRequest( pvno, msgType, paData, requestBody, bodyBytes );
+    }
+
+
+    /*
+     KDC-REQ-BODY ::=   SEQUENCE {
+     kdc-options[0]       KdcOptions,
+     cname[1]             PrincipalName OPTIONAL,
+     -- Used only in AS-REQ
+     realm[2]             Realm, -- Server's realm
+     -- Also client's in AS-REQ
+     sname[3]             PrincipalName OPTIONAL,
+     from[4]              KerberosTime OPTIONAL,
+     till[5]              KerberosTime,
+     rtime[6]             KerberosTime OPTIONAL,
+     nonce[7]             INTEGER,
+     etype[8]             SEQUENCE OF INTEGER, -- EncryptionType,
+     -- in preference order
+     addresses[9]         HostAddresses OPTIONAL,
+     enc-authorization-data[10]   EncryptedData OPTIONAL,
+     -- Encrypted AuthorizationData encoding
+     additional-tickets[11]       SEQUENCE OF Ticket OPTIONAL
+     }*/
+    private RequestBody decodeRequestBody( DERSequence sequence ) throws IOException
+    {
+        RequestBodyModifier modifier = new RequestBodyModifier();
+
+        for ( Enumeration<DEREncodable> e = sequence.getObjects(); e.hasMoreElements(); )
+        {
+            DERTaggedObject object = ( DERTaggedObject ) e.nextElement();
+            int tag = object.getTagNo();
+            DEREncodable derObject = object.getObject();
+
+            switch ( tag )
+            {
+                case 0:
+                    DERBitString kdcOptions = ( DERBitString ) derObject;
+                    modifier.setKdcOptions( new KdcOptions( kdcOptions.getOctets() ) );
+                    break;
+                case 1:
+                    DERSequence cName = ( DERSequence ) derObject;
+                    modifier.setClientName( PrincipalNameDecoder.decode( cName ) );
+                    break;
+                case 2:
+                    DERGeneralString realm = ( DERGeneralString ) derObject;
+                    modifier.setRealm( realm.getString() );
+                    break;
+                case 3:
+                    DERSequence sname = ( DERSequence ) derObject;
+                    modifier.setServerName( PrincipalNameDecoder.decode( sname ) );
+                    break;
+                case 4:
+                    DERGeneralizedTime from = ( DERGeneralizedTime ) derObject;
+                    modifier.setFrom( KerberosTimeDecoder.decode( from ) );
+                    break;
+                case 5:
+                    DERGeneralizedTime till = ( DERGeneralizedTime ) derObject;
+                    modifier.setTill( KerberosTimeDecoder.decode( till ) );
+                    break;
+                case 6:
+                    DERGeneralizedTime rtime = ( DERGeneralizedTime ) derObject;
+                    modifier.setRtime( KerberosTimeDecoder.decode( rtime ) );
+                    break;
+                case 7:
+                    DERInteger nonce = ( DERInteger ) derObject;
+                    modifier.setNonce( nonce.intValue() );
+                    break;
+                case 8:
+                    DERSequence etype = ( DERSequence ) derObject;
+                    modifier.setEType( EncryptionTypeDecoder.decode( etype ) );
+                    break;
+                    
+                case 9:
+                    DERSequence hostAddresses = ( DERSequence ) derObject;
+                    modifier.setAddresses( HostAddressDecoder.decodeSequence( hostAddresses ) );
+                    break;
+                case 10:
+                    DERSequence encryptedData = ( DERSequence ) derObject;
+                    modifier.setEncAuthorizationData( EncryptedDataDecoder.decode( encryptedData ) );
+                    break;
+                case 11:
+                    DERSequence tag11 = ( DERSequence ) derObject;
+                    modifier.setAdditionalTickets( TicketDecoder.decodeSequence( tag11 ) );
+                    break;
+            }
+        }
+
+        return modifier.getRequestBody();
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/KerberosTimeDecoder.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/KerberosTimeDecoder.java
new file mode 100644
index 0000000..68e44c5
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/KerberosTimeDecoder.java
@@ -0,0 +1,55 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.io.decoder;
+
+
+import java.text.ParseException;
+import java.util.Date;
+
+import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
+import org.apache.directory.shared.asn1.der.DERGeneralizedTime;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class KerberosTimeDecoder
+{
+    /**
+     * KerberosTime ::=   GeneralizedTime
+     *             -- Specifying UTC time zone (Z)
+     */
+    protected static KerberosTime decode( DERGeneralizedTime time )
+    {
+        Date date = null;
+
+        try
+        {
+            date = time.getDate();
+        }
+        catch ( ParseException pe )
+        {
+            pe.printStackTrace();
+        }
+
+        return new KerberosTime( date );
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/LastRequestDecoder.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/LastRequestDecoder.java
new file mode 100644
index 0000000..0825b0d
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/LastRequestDecoder.java
@@ -0,0 +1,91 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.io.decoder;
+
+
+import java.util.Enumeration;
+
+import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
+import org.apache.directory.server.kerberos.shared.messages.value.LastRequest;
+import org.apache.directory.server.kerberos.shared.messages.value.LastRequestEntry;
+import org.apache.directory.server.kerberos.shared.messages.value.LastRequestType;
+import org.apache.directory.shared.asn1.der.DEREncodable;
+import org.apache.directory.shared.asn1.der.DERGeneralizedTime;
+import org.apache.directory.shared.asn1.der.DERInteger;
+import org.apache.directory.shared.asn1.der.DERSequence;
+import org.apache.directory.shared.asn1.der.DERTaggedObject;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class LastRequestDecoder
+{
+    /**
+     * LastReq ::=   SEQUENCE OF SEQUENCE {
+     * lr-type[0]               INTEGER,
+     * lr-value[1]              KerberosTime
+     * }
+     */
+    protected static LastRequest decodeSequence( DERSequence sequence )
+    {
+        LastRequestEntry[] entries = new LastRequestEntry[sequence.size()];
+
+        int ii = 0;
+        for ( Enumeration<DEREncodable> e = sequence.getObjects(); e.hasMoreElements(); )
+        {
+            DERSequence object = ( DERSequence ) e.nextElement();
+            LastRequestEntry entry = decode( object );
+            entries[ii] = entry;
+            ii++;
+        }
+
+        return new LastRequest( entries );
+    }
+
+
+    protected static LastRequestEntry decode( DERSequence sequence )
+    {
+        LastRequestType type = LastRequestType.NONE;
+        KerberosTime value = null;
+
+        for ( Enumeration<DEREncodable> e = sequence.getObjects(); e.hasMoreElements(); )
+        {
+            DERTaggedObject object = ( DERTaggedObject ) e.nextElement();
+            int tag = object.getTagNo();
+            DEREncodable derObject = object.getObject();
+
+            switch ( tag )
+            {
+                case 0:
+                    DERInteger tag0 = ( DERInteger ) derObject;
+                    type = LastRequestType.getTypeByOrdinal( tag0.intValue() );
+                    break;
+                case 1:
+                    DERGeneralizedTime tag1 = ( DERGeneralizedTime ) derObject;
+                    value = KerberosTimeDecoder.decode( tag1 );
+                    break;
+            }
+        }
+
+        return new LastRequestEntry( type, value );
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/PreAuthenticationDataDecoder.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/PreAuthenticationDataDecoder.java
new file mode 100644
index 0000000..2910523
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/PreAuthenticationDataDecoder.java
@@ -0,0 +1,118 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.io.decoder;
+
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+import org.apache.directory.server.kerberos.shared.messages.value.PaData;
+import org.apache.directory.server.kerberos.shared.messages.value.types.PaDataType;
+import org.apache.directory.shared.asn1.der.ASN1InputStream;
+import org.apache.directory.shared.asn1.der.DEREncodable;
+import org.apache.directory.shared.asn1.der.DERInteger;
+import org.apache.directory.shared.asn1.der.DEROctetString;
+import org.apache.directory.shared.asn1.der.DERSequence;
+import org.apache.directory.shared.asn1.der.DERTaggedObject;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class PreAuthenticationDataDecoder
+{
+    /**
+     * Decodes a byte array into {@link PaData}.
+     *
+     * @param encodedPreAuthData
+     * @return The {@link PaData}.
+     * @throws IOException
+     */
+    public PaData decode( byte[] encodedPreAuthData ) throws IOException
+    {
+        ASN1InputStream ais = new ASN1InputStream( encodedPreAuthData );
+
+        DERSequence sequence = ( DERSequence ) ais.readObject();
+
+        return decode( sequence );
+    }
+
+
+    /**
+     * KDC-REQ ::=        SEQUENCE {
+     *            pvno[1]               INTEGER,
+     *            msg-type[2]           INTEGER,
+     *            padata[3]             SEQUENCE OF PA-DATA OPTIONAL,
+     *            req-body[4]           KDC-REQ-BODY
+     * }
+     */
+    protected static PaData[] decodeSequence( DERSequence sequence )
+    {
+        PaData[] paDataSequence = new PaData[sequence.size()];
+
+        int ii = 0;
+        
+        for ( Enumeration<DEREncodable> e = sequence.getObjects(); e.hasMoreElements(); )
+        {
+            DERSequence object = ( DERSequence ) e.nextElement();
+            PaData paData = PreAuthenticationDataDecoder.decode( object );
+            paDataSequence[ii] = paData;
+            ii++;
+        }
+
+        return paDataSequence;
+    }
+
+
+    /**
+     * PA-DATA ::=        SEQUENCE {
+     *            padata-type[1]        INTEGER,
+     *            padata-value[2]       OCTET STRING,
+     *                          -- might be encoded AP-REQ
+     * }
+     */
+    protected static PaData decode( DERSequence sequence )
+    {
+        PaData paData = new PaData();
+
+        for ( Enumeration<DEREncodable> e = sequence.getObjects(); e.hasMoreElements(); )
+        {
+            DERTaggedObject object = ( DERTaggedObject ) e.nextElement();
+            int tag = object.getTagNo();
+            DEREncodable derObject = object.getObject();
+
+            switch ( tag )
+            {
+                case 1:
+                    DERInteger padataType = ( DERInteger ) derObject;
+                    PaDataType type = PaDataType.getTypeByOrdinal( padataType.intValue() );
+                    paData.setPaDataType( type );
+                    break;
+                case 2:
+                    DEROctetString padataValue = ( DEROctetString ) derObject;
+                    paData.setPaDataValue( padataValue.getOctets() );
+                    break;
+            }
+        }
+
+        return paData;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/PrincipalNameDecoder.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/PrincipalNameDecoder.java
new file mode 100644
index 0000000..961c6f4
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/PrincipalNameDecoder.java
@@ -0,0 +1,86 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.io.decoder;
+
+
+import java.util.Enumeration;
+
+import org.apache.directory.server.kerberos.shared.messages.value.PrincipalName;
+import org.apache.directory.shared.asn1.der.DEREncodable;
+import org.apache.directory.shared.asn1.der.DERGeneralString;
+import org.apache.directory.shared.asn1.der.DERInteger;
+import org.apache.directory.shared.asn1.der.DERSequence;
+import org.apache.directory.shared.asn1.der.DERTaggedObject;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class PrincipalNameDecoder
+{
+    /**
+     * Decodes a {@link DERSequence} into a {@link PrincipalName}.
+     * 
+     * PrincipalName ::=   SEQUENCE {
+     *               name-type[0]     INTEGER,
+     *               name-string[1]   SEQUENCE OF GeneralString
+     * }
+     * 
+     * @param sequence 
+     * @return The {@link PrincipalName}.
+     */
+    public static PrincipalName decode( DERSequence sequence )
+    {
+        PrincipalName principalName = new PrincipalName();
+
+        for ( Enumeration<DEREncodable> e = sequence.getObjects(); e.hasMoreElements(); )
+        {
+            DERTaggedObject object = ( DERTaggedObject ) e.nextElement();
+            int tag = object.getTagNo();
+            DEREncodable derObject = object.getObject();
+
+            switch ( tag )
+            {
+                case 0:
+                    DERInteger nameType = ( DERInteger ) derObject;
+                    principalName.setNameType( nameType.intValue() );
+                    break;
+                    
+                case 1:
+                    DERSequence nameString = ( DERSequence ) derObject;
+                    decodeNameString( nameString, principalName );
+                    break;
+            }
+        }
+
+        return principalName;
+    }
+
+
+    private static void decodeNameString( DERSequence sequence, PrincipalName principalName )
+    {
+        for ( Enumeration<DEREncodable> e = sequence.getObjects(); e.hasMoreElements(); )
+        {
+            DERGeneralString object = ( DERGeneralString ) e.nextElement();
+            principalName.addName( object.getString() );
+        }
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/PrivateMessageDecoder.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/PrivateMessageDecoder.java
new file mode 100644
index 0000000..6134544
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/PrivateMessageDecoder.java
@@ -0,0 +1,92 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.io.decoder;
+
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+import org.apache.directory.server.kerberos.shared.KerberosMessageType;
+import org.apache.directory.server.kerberos.shared.messages.application.PrivateMessage;
+import org.apache.directory.shared.asn1.der.ASN1InputStream;
+import org.apache.directory.shared.asn1.der.DERApplicationSpecific;
+import org.apache.directory.shared.asn1.der.DEREncodable;
+import org.apache.directory.shared.asn1.der.DERInteger;
+import org.apache.directory.shared.asn1.der.DERSequence;
+import org.apache.directory.shared.asn1.der.DERTaggedObject;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class PrivateMessageDecoder
+{
+    /**
+     * Decodes a byte array into a {@link PrivateMessage}.
+     *
+     * @param encodedPrivateMessage
+     * @return The {@link PrivateMessage}.
+     * @throws IOException
+     */
+    public PrivateMessage decode( byte[] encodedPrivateMessage ) throws IOException
+    {
+        ASN1InputStream ais = new ASN1InputStream( encodedPrivateMessage );
+
+        DERApplicationSpecific app = ( DERApplicationSpecific ) ais.readObject();
+
+        DERSequence privateMessage = ( DERSequence ) app.getObject();
+
+        return decodePrivateMessageSequence( privateMessage );
+    }
+
+
+    private PrivateMessage decodePrivateMessageSequence( DERSequence sequence )
+    {
+        PrivateMessage message = new PrivateMessage();
+
+        for ( Enumeration<DEREncodable> e = sequence.getObjects(); e.hasMoreElements(); )
+        {
+            DERTaggedObject object = ( DERTaggedObject ) e.nextElement();
+            int tag = object.getTagNo();
+            DEREncodable derObject = object.getObject();
+
+            switch ( tag )
+            {
+                case 0:
+                    DERInteger tag0 = ( DERInteger ) derObject;
+                    message.setProtocolVersionNumber( tag0.intValue() );
+                    break;
+                    
+                case 1:
+                    DERInteger tag1 = ( DERInteger ) derObject;
+                    message.setMessageType( KerberosMessageType.getTypeByOrdinal( tag1.intValue() ) );
+                    break;
+                    
+                case 3:
+                    DERSequence tag3 = ( DERSequence ) derObject;
+                    message.setEncryptedPart( EncryptedDataDecoder.decode( tag3 ) );
+                    break;
+            }
+        }
+
+        return message;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/TicketDecoder.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/TicketDecoder.java
new file mode 100644
index 0000000..46b3dea
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/TicketDecoder.java
@@ -0,0 +1,127 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.io.decoder;
+
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+import org.apache.directory.server.kerberos.shared.messages.components.Ticket;
+import org.apache.directory.shared.asn1.der.ASN1InputStream;
+import org.apache.directory.shared.asn1.der.DERApplicationSpecific;
+import org.apache.directory.shared.asn1.der.DEREncodable;
+import org.apache.directory.shared.asn1.der.DERGeneralString;
+import org.apache.directory.shared.asn1.der.DERInteger;
+import org.apache.directory.shared.asn1.der.DERSequence;
+import org.apache.directory.shared.asn1.der.DERTaggedObject;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class TicketDecoder
+{
+    /**
+     * Decodes a byte array into an {@link Ticket}.
+     *
+     * @param encodedTicket
+     * @return The {@link Ticket}.
+     * @throws IOException
+     */
+    public static Ticket decode( byte[] encodedTicket ) throws IOException
+    {
+        ASN1InputStream ais = new ASN1InputStream( encodedTicket );
+
+        DERApplicationSpecific app = ( DERApplicationSpecific ) ais.readObject();
+
+        return decode( app );
+    }
+
+
+    /**
+     * Decodes a {@link DERSequence} into an array of {@link Ticket}s.
+     *
+     * @param sequence
+     * @return The array of {@link Ticket}s.
+     * @throws IOException
+     */
+    public static Ticket[] decodeSequence( DERSequence sequence ) throws IOException
+    {
+        Ticket[] tickets = new Ticket[sequence.size()];
+
+        int ii = 0;
+        for ( Enumeration<DEREncodable> e = sequence.getObjects(); e.hasMoreElements(); )
+        {
+            DERApplicationSpecific object = ( DERApplicationSpecific ) e.nextElement();
+            tickets[ii] = decode( object );
+        }
+
+        return tickets;
+    }
+
+
+    /**
+     * Ticket ::=                    [APPLICATION 1] SEQUENCE {
+     *     tkt-vno[0]                   INTEGER,
+     *     realm[1]                     Realm,
+     *     sname[2]                     PrincipalName,
+     *     enc-part[3]                  EncryptedData
+     * }
+     */
+    protected static Ticket decode( DERApplicationSpecific app ) throws IOException
+    {
+        DERSequence sequence = ( DERSequence ) app.getObject();
+
+        Ticket ticket = new Ticket();
+
+        for ( Enumeration<DEREncodable> e = sequence.getObjects(); e.hasMoreElements(); )
+        {
+            DERTaggedObject object = ( DERTaggedObject ) e.nextElement();
+            int tag = object.getTagNo();
+            DEREncodable derObject = object.getObject();
+
+            switch ( tag )
+            {
+                case 0:
+                    DERInteger tag0 = ( DERInteger ) derObject;
+                    ticket.setTktVno( tag0.intValue() );
+                    break;
+                    
+                case 1:
+                    DERGeneralString tag1 = ( DERGeneralString ) derObject;
+                    ticket.setRealm( tag1.getString() );
+                    break;
+                    
+                case 2:
+                    DERSequence tag2 = ( DERSequence ) derObject;
+                    ticket.setSName( PrincipalNameDecoder.decode( tag2 ) );
+                    break;
+                    
+                case 3:
+                    DERSequence tag3 = ( DERSequence ) derObject;
+                    ticket.setEncPart( EncryptedDataDecoder.decode( tag3 ) );
+                    break;
+            }
+        }
+
+        return ticket;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/package-info.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/package-info.java
new file mode 100644
index 0000000..35d1e33
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/decoder/package-info.java
@@ -0,0 +1,28 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+/**
+ * Provides the decoders for Kerberos message objects.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+
+package org.apache.directory.server.kerberos.shared.io.decoder;
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/ApplicationReplyEncoder.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/ApplicationReplyEncoder.java
new file mode 100644
index 0000000..06917e6
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/ApplicationReplyEncoder.java
@@ -0,0 +1,76 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.io.encoder;
+
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import org.apache.directory.server.kerberos.shared.messages.application.ApplicationReply;
+import org.apache.directory.shared.asn1.der.ASN1OutputStream;
+import org.apache.directory.shared.asn1.der.DERApplicationSpecific;
+import org.apache.directory.shared.asn1.der.DERInteger;
+import org.apache.directory.shared.asn1.der.DERSequence;
+import org.apache.directory.shared.asn1.der.DERTaggedObject;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ApplicationReplyEncoder
+{
+    /**
+     * Application code constant for the {@link ApplicationReply} (15).
+     */
+    public static final int APPLICATION_CODE = 15;
+
+
+    /**
+     * Encodes an {@link ApplicationReply} into a byte array.
+     *
+     * @param reply
+     * @return The byte array.
+     * @throws IOException
+     */
+    public byte[] encode( ApplicationReply reply ) throws IOException
+    {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        ASN1OutputStream aos = new ASN1OutputStream( baos );
+
+        DERSequence replySequence = encodeReplySequence( reply );
+        aos.writeObject( DERApplicationSpecific.valueOf( APPLICATION_CODE, replySequence ) );
+        aos.close();
+
+        return baos.toByteArray();
+    }
+
+
+    private DERSequence encodeReplySequence( ApplicationReply message )
+    {
+        DERSequence sequence = new DERSequence();
+
+        sequence.add( new DERTaggedObject( 0, DERInteger.valueOf( message.getProtocolVersionNumber() ) ) );
+        sequence.add( new DERTaggedObject( 1, DERInteger.valueOf( message.getMessageType().getOrdinal() ) ) );
+        sequence.add( new DERTaggedObject( 2, EncryptedDataEncoder.encodeSequence( message.getEncPart() ) ) );
+
+        return sequence;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/ApplicationRequestEncoder.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/ApplicationRequestEncoder.java
new file mode 100644
index 0000000..5c40bf0
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/ApplicationRequestEncoder.java
@@ -0,0 +1,88 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.io.encoder;
+
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import org.apache.directory.server.kerberos.shared.messages.ApplicationRequest;
+import org.apache.directory.shared.asn1.der.ASN1OutputStream;
+import org.apache.directory.shared.asn1.der.DERApplicationSpecific;
+import org.apache.directory.shared.asn1.der.DERBitString;
+import org.apache.directory.shared.asn1.der.DERInteger;
+import org.apache.directory.shared.asn1.der.DERSequence;
+import org.apache.directory.shared.asn1.der.DERTaggedObject;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 540371 $, $Date: 2007-05-21 17:00:43 -0700 (Mon, 21 May 2007) $
+ */
+public class ApplicationRequestEncoder
+{
+    /**
+     * Application code constant for the {@link ApplicationRequest} (14).
+     */
+    public static final int APPLICATION_CODE = 14;
+
+
+    /**
+     * Encodes an {@link ApplicationRequest} into a byte array.
+     *
+     * @param request
+     * @return The byte array.
+     * @throws IOException
+     */
+    public byte[] encode( ApplicationRequest request ) throws IOException
+    {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        ASN1OutputStream aos = new ASN1OutputStream( baos );
+
+        DERSequence requestSequence = encodeReplySequence( request );
+        aos.writeObject( DERApplicationSpecific.valueOf( APPLICATION_CODE, requestSequence ) );
+        aos.close();
+
+        return baos.toByteArray();
+    }
+
+
+    /*
+     AP-REQ ::=      [APPLICATION 14] SEQUENCE {
+     pvno[0]                       INTEGER,
+     msg-type[1]                   INTEGER,
+     ap-options[2]                 APOptions,
+     ticket[3]                     Ticket,
+     authenticator[4]              EncryptedData
+     }
+     */
+    private DERSequence encodeReplySequence( ApplicationRequest message )
+    {
+        DERSequence sequence = new DERSequence();
+
+        sequence.add( new DERTaggedObject( 0, DERInteger.valueOf( message.getProtocolVersionNumber() ) ) );
+        sequence.add( new DERTaggedObject( 1, DERInteger.valueOf( message.getMessageType().getOrdinal() ) ) );
+        sequence.add( new DERTaggedObject( 2, new DERBitString( message.getApOptions().getBytes() ) ) );
+        sequence.add( new DERTaggedObject( 3, TicketEncoder.encode( message.getTicket() ) ) );
+        sequence.add( new DERTaggedObject( 4, EncryptedDataEncoder.encodeSequence( message.getEncPart() ) ) );
+
+        return sequence;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/AuthenticatorEncoder.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/AuthenticatorEncoder.java
new file mode 100644
index 0000000..5bb64a0
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/AuthenticatorEncoder.java
@@ -0,0 +1,136 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.io.encoder;
+
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import org.apache.directory.server.kerberos.shared.messages.Encodable;
+import org.apache.directory.server.kerberos.shared.messages.components.Authenticator;
+import org.apache.directory.shared.asn1.der.ASN1OutputStream;
+import org.apache.directory.shared.asn1.der.DERApplicationSpecific;
+import org.apache.directory.shared.asn1.der.DERGeneralString;
+import org.apache.directory.shared.asn1.der.DERInteger;
+import org.apache.directory.shared.asn1.der.DERSequence;
+import org.apache.directory.shared.asn1.der.DERTaggedObject;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class AuthenticatorEncoder implements Encoder, EncoderFactory
+{
+    /**
+     * Application code constant for the {@link Authenticator} (2).
+     */
+    private static final int APPLICATION_CODE = 2;
+
+
+    /**
+     * Encodes an {@link Authenticator} into a byte array.
+     *
+     * @param authenticator
+     * @return The byte array.
+     * @throws IOException
+     */
+    public byte[] encode( Encodable authenticator ) throws IOException
+    {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        ASN1OutputStream aos = new ASN1OutputStream( baos );
+
+        DERSequence replySequence = encodeInitialSequence( ( Authenticator ) authenticator );
+        aos.writeObject( DERApplicationSpecific.valueOf( APPLICATION_CODE, replySequence ) );
+        aos.close();
+
+        return baos.toByteArray();
+    }
+
+
+    public Encoder getEncoder()
+    {
+        return new AuthenticatorEncoder();
+    }
+
+
+    /**
+     * Encodes an {@link Authenticator} into a {@link DERSequence}.
+     * 
+     * -- Unencrypted authenticator
+     * Authenticator ::=    [APPLICATION 2] SEQUENCE
+     * {
+     *                authenticator-vno[0]          INTEGER,
+     *                crealm[1]                     Realm,
+     *                cname[2]                      PrincipalName,
+     *                cksum[3]                      Checksum OPTIONAL,
+     *                cusec[4]                      INTEGER,
+     *                ctime[5]                      KerberosTime,
+     *                subkey[6]                     EncryptionKey OPTIONAL,
+     *                seq-number[7]                 INTEGER OPTIONAL,
+     *  
+     *                authorization-data[8]         AuthorizationData OPTIONAL
+     * }
+     * 
+     * @param authenticator 
+     * @return The {@link DERSequence}.
+     */
+    private DERSequence encodeInitialSequence( Authenticator authenticator )
+    {
+        String clientRealm = authenticator.getClientPrincipal().getRealm();
+
+        DERSequence sequence = new DERSequence();
+
+        sequence.add( new DERTaggedObject( 0, DERInteger.valueOf( authenticator.getVersionNumber() ) ) );
+        sequence.add( new DERTaggedObject( 1, DERGeneralString.valueOf( clientRealm ) ) );
+        sequence.add( new DERTaggedObject( 2, PrincipalNameEncoder.encode( authenticator.getClientPrincipal() ) ) );
+
+        // OPTIONAL
+        if ( authenticator.getChecksum() != null )
+        {
+            sequence.add( new DERTaggedObject( 3, ChecksumEncoder.encode( authenticator.getChecksum() ) ) );
+        }
+
+        sequence.add( new DERTaggedObject( 4, DERInteger.valueOf( authenticator.getClientMicroSecond() ) ) );
+        sequence.add( new DERTaggedObject( 5, KerberosTimeEncoder.encode( authenticator.getClientTime() ) ) );
+
+        // OPTIONAL
+        if ( authenticator.getSubSessionKey() != null )
+        {
+            sequence.add( new DERTaggedObject( 6, EncryptionKeyEncoder
+                .encodeSequence( authenticator.getSubSessionKey() ) ) );
+        }
+
+        // OPTIONAL
+        if ( authenticator.getSequenceNumber() > 0 )
+        {
+            sequence.add( new DERTaggedObject( 7, DERInteger.valueOf( authenticator.getSequenceNumber() ) ) );
+        }
+
+        // OPTIONAL
+        if ( authenticator.getAuthorizationData() != null )
+        {
+            sequence.add( new DERTaggedObject( 8, AuthorizationDataEncoder
+                .encode( authenticator.getAuthorizationData() ) ) );
+        }
+
+        return sequence;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/AuthorizationDataEncoder.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/AuthorizationDataEncoder.java
new file mode 100644
index 0000000..f1d802f
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/AuthorizationDataEncoder.java
@@ -0,0 +1,57 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.io.encoder;
+
+
+import org.apache.directory.server.kerberos.shared.messages.value.AuthorizationData;
+import org.apache.directory.server.kerberos.shared.messages.value.AuthorizationDataEntry;
+import org.apache.directory.shared.asn1.der.DERInteger;
+import org.apache.directory.shared.asn1.der.DEROctetString;
+import org.apache.directory.shared.asn1.der.DERSequence;
+import org.apache.directory.shared.asn1.der.DERTaggedObject;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class AuthorizationDataEncoder
+{
+    /**
+     * AuthorizationData ::=   SEQUENCE OF SEQUENCE {
+     *     ad-type[0]               INTEGER,
+     *     ad-data[1]               OCTET STRING
+     * }
+     */
+    protected static DERSequence encode( AuthorizationData data )
+    {
+        DERSequence outerSequence = new DERSequence();
+
+        for ( AuthorizationDataEntry entry: data.getEntries() )
+        {
+            DERSequence sequence = new DERSequence();
+            sequence.add( new DERTaggedObject( 0, DERInteger.valueOf( entry.getAdType().getOrdinal() ) ) );
+            sequence.add( new DERTaggedObject( 1, new DEROctetString( entry.getAdData() ) ) );
+            outerSequence.add( sequence );
+        }
+
+        return outerSequence;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/ChecksumEncoder.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/ChecksumEncoder.java
new file mode 100644
index 0000000..18c7457
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/ChecksumEncoder.java
@@ -0,0 +1,56 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.io.encoder;
+
+
+import org.apache.directory.server.kerberos.shared.messages.value.Checksum;
+import org.apache.directory.shared.asn1.der.DERInteger;
+import org.apache.directory.shared.asn1.der.DEROctetString;
+import org.apache.directory.shared.asn1.der.DERSequence;
+import org.apache.directory.shared.asn1.der.DERTaggedObject;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ChecksumEncoder
+{
+    /**
+     * Encodes a {@link Checksum} into a {@link DERSequence}.
+     * 
+     * Checksum ::=   SEQUENCE {
+     *          cksumtype[0]   INTEGER,
+     *          checksum[1]    OCTET STRING
+     * }
+     * 
+     * @param checksum 
+     * @return The {@link DERSequence}.
+     */
+    public static DERSequence encode( Checksum checksum )
+    {
+        DERSequence vector = new DERSequence();
+
+        vector.add( new DERTaggedObject( 0, DERInteger.valueOf( checksum.getChecksumType().getOrdinal() ) ) );
+        vector.add( new DERTaggedObject( 1, new DEROctetString( checksum.getChecksumValue() ) ) );
+
+        return vector;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/EncApRepPartEncoder.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/EncApRepPartEncoder.java
new file mode 100644
index 0000000..cfa56f1
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/EncApRepPartEncoder.java
@@ -0,0 +1,85 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.io.encoder;
+
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import org.apache.directory.server.kerberos.shared.messages.Encodable;
+import org.apache.directory.server.kerberos.shared.messages.components.EncApRepPart;
+import org.apache.directory.shared.asn1.der.ASN1OutputStream;
+import org.apache.directory.shared.asn1.der.DERApplicationSpecific;
+import org.apache.directory.shared.asn1.der.DERInteger;
+import org.apache.directory.shared.asn1.der.DERSequence;
+import org.apache.directory.shared.asn1.der.DERTaggedObject;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class EncApRepPartEncoder implements Encoder, EncoderFactory
+{
+    /**
+     * The application code constant for the {@link EncApRepPart} (27).
+     */
+    public static final int APPLICATION_CODE = 27;
+
+
+    public Encoder getEncoder()
+    {
+        return new EncApRepPartEncoder();
+    }
+
+
+    public byte[] encode( Encodable apRepPart ) throws IOException
+    {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        ASN1OutputStream aos = new ASN1OutputStream( baos );
+
+        DERSequence privPartSequence = encodeApRepPartSequence( ( EncApRepPart ) apRepPart );
+        aos.writeObject( DERApplicationSpecific.valueOf( APPLICATION_CODE, privPartSequence ) );
+        aos.close();
+
+        return baos.toByteArray();
+    }
+
+
+    private DERSequence encodeApRepPartSequence( EncApRepPart message )
+    {
+        DERSequence sequence = new DERSequence();
+
+        sequence.add( new DERTaggedObject( 0, KerberosTimeEncoder.encode( message.getClientTime() ) ) );
+        sequence.add( new DERTaggedObject( 1, DERInteger.valueOf( message.getClientMicroSecond() ) ) );
+
+        if ( message.getSubSessionKey() != null )
+        {
+            sequence.add( new DERTaggedObject( 2, EncryptionKeyEncoder.encodeSequence( message.getSubSessionKey() ) ) );
+        }
+
+        if ( message.getSequenceNumber() != null )
+        {
+            sequence.add( new DERTaggedObject( 3, DERInteger.valueOf( message.getSequenceNumber().intValue() ) ) );
+        }
+
+        return sequence;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/EncAsRepPartEncoder.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/EncAsRepPartEncoder.java
new file mode 100644
index 0000000..1abc24b
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/EncAsRepPartEncoder.java
@@ -0,0 +1,53 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.io.encoder;
+
+
+import org.apache.directory.server.kerberos.shared.messages.components.EncAsRepPart;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class EncAsRepPartEncoder extends EncKdcRepPartEncoder implements EncoderFactory
+{
+    /**
+     * The application code constant for an {@link EncAsRepPart}.
+     * 
+     * EncASRepPart ::=    [APPLICATION 25[25]] EncKDCRepPart
+     */
+    public static final int APPLICATION_CODE = 25;
+
+
+    /**
+     * Creates a new instance of EncAsRepPartEncoder.
+     */
+    public EncAsRepPartEncoder()
+    {
+        super( APPLICATION_CODE );
+    }
+
+
+    public Encoder getEncoder()
+    {
+        return new EncAsRepPartEncoder();
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/EncKdcRepPartEncoder.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/EncKdcRepPartEncoder.java
new file mode 100644
index 0000000..2572e7b
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/EncKdcRepPartEncoder.java
@@ -0,0 +1,123 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.io.encoder;
+
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import org.apache.directory.server.kerberos.shared.messages.Encodable;
+import org.apache.directory.server.kerberos.shared.messages.KdcReply;
+import org.apache.directory.shared.asn1.der.ASN1OutputStream;
+import org.apache.directory.shared.asn1.der.DERApplicationSpecific;
+import org.apache.directory.shared.asn1.der.DERBitString;
+import org.apache.directory.shared.asn1.der.DERGeneralString;
+import org.apache.directory.shared.asn1.der.DERInteger;
+import org.apache.directory.shared.asn1.der.DERSequence;
+import org.apache.directory.shared.asn1.der.DERTaggedObject;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public abstract class EncKdcRepPartEncoder implements Encoder
+{
+    private int applicationCode;
+
+
+    protected EncKdcRepPartEncoder(int applicationCode)
+    {
+        this.applicationCode = applicationCode;
+    }
+
+
+    public byte[] encode( Encodable app ) throws IOException
+    {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        ASN1OutputStream aos = new ASN1OutputStream( baos );
+
+        DERSequence initialSequence = encodeInitialSequence( ( KdcReply ) app );
+        aos.writeObject( DERApplicationSpecific.valueOf( applicationCode, initialSequence ) );
+
+        return baos.toByteArray();
+    }
+
+
+    /**
+     *    EncKDCRepPart ::=   SEQUENCE {
+     *                key[0]                       EncryptionKey,
+     *                last-req[1]                  LastReq,
+     * 
+     *                nonce[2]                     INTEGER,
+     *                key-expiration[3]            KerberosTime OPTIONAL,
+     *                flags[4]                     TicketFlags,
+     *                authtime[5]                  KerberosTime,
+     *                starttime[6]                 KerberosTime OPTIONAL,
+     *                endtime[7]                   KerberosTime,
+     *                renew-till[8]                KerberosTime OPTIONAL,
+     *                srealm[9]                    Realm,
+     *                sname[10]                    PrincipalName,
+     *                caddr[11]                    HostAddresses OPTIONAL
+     * }
+     */
+    protected DERSequence encodeInitialSequence( KdcReply reply )
+    {
+        DERSequence sequence = new DERSequence();
+
+        sequence.add( new DERTaggedObject( 0, EncryptionKeyEncoder.encodeSequence( reply.getKey() ) ) );
+        sequence.add( new DERTaggedObject( 1, LastRequestEncoder.encode( reply.getLastRequest() ) ) );
+        sequence.add( new DERTaggedObject( 2, DERInteger.valueOf( reply.getNonce() ) ) );
+
+        // OPTIONAL
+        if ( reply.getKeyExpiration() != null )
+        {
+            sequence.add( new DERTaggedObject( 3, KerberosTimeEncoder.encode( reply.getKeyExpiration() ) ) );
+        }
+
+        sequence.add( new DERTaggedObject( 4, new DERBitString( reply.getFlags().getBytes() ) ) );
+        sequence.add( new DERTaggedObject( 5, KerberosTimeEncoder.encode( reply.getAuthTime() ) ) );
+
+        // OPTIONAL
+        if ( reply.getStartTime() != null )
+        {
+            sequence.add( new DERTaggedObject( 6, KerberosTimeEncoder.encode( reply.getStartTime() ) ) );
+        }
+
+        sequence.add( new DERTaggedObject( 7, KerberosTimeEncoder.encode( reply.getEndTime() ) ) );
+
+        // OPTIONAL
+        if ( reply.getRenewTill() != null )
+        {
+            sequence.add( new DERTaggedObject( 8, KerberosTimeEncoder.encode( reply.getRenewTill() ) ) );
+        }
+
+        sequence.add( new DERTaggedObject( 9, DERGeneralString.valueOf( reply.getServerRealm().toString() ) ) );
+        sequence.add( new DERTaggedObject( 10, PrincipalNameEncoder.encode( reply.getServerPrincipal() ) ) );
+
+        // OPTIONAL
+        if ( reply.getClientAddresses() != null )
+        {
+            sequence.add( new DERTaggedObject( 11, HostAddressesEncoder.encodeSequence( reply.getClientAddresses() ) ) );
+        }
+
+        return sequence;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/EncKrbPrivPartEncoder.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/EncKrbPrivPartEncoder.java
new file mode 100644
index 0000000..18b2438
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/EncKrbPrivPartEncoder.java
@@ -0,0 +1,109 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.io.encoder;
+
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import org.apache.directory.server.kerberos.shared.messages.Encodable;
+import org.apache.directory.server.kerberos.shared.messages.components.EncKrbPrivPart;
+import org.apache.directory.shared.asn1.der.ASN1OutputStream;
+import org.apache.directory.shared.asn1.der.DERApplicationSpecific;
+import org.apache.directory.shared.asn1.der.DERInteger;
+import org.apache.directory.shared.asn1.der.DEROctetString;
+import org.apache.directory.shared.asn1.der.DERSequence;
+import org.apache.directory.shared.asn1.der.DERTaggedObject;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class EncKrbPrivPartEncoder implements Encoder, EncoderFactory
+{
+    private static final int APPLICATION_CODE = 28;
+
+
+    public Encoder getEncoder()
+    {
+        return new EncKrbPrivPartEncoder();
+    }
+
+
+    public byte[] encode( Encodable privPart ) throws IOException
+    {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        ASN1OutputStream aos = new ASN1OutputStream( baos );
+
+        DERSequence privPartSequence = encodePrivatePartSequence( ( EncKrbPrivPart ) privPart );
+        aos.writeObject( DERApplicationSpecific.valueOf( APPLICATION_CODE, privPartSequence ) );
+        aos.close();
+
+        return baos.toByteArray();
+    }
+
+
+    /**
+     * Encodes an {@link EncKrbPrivPart} into a {@link DERSequence}.
+     * 
+     * EncKrbPrivPart  ::= [APPLICATION 28] SEQUENCE {
+     *         user-data       [0] OCTET STRING,
+     *         timestamp       [1] KerberosTime OPTIONAL,
+     *         usec            [2] Microseconds OPTIONAL,
+     *         seq-number      [3] UInt32 OPTIONAL,
+     *         s-address       [4] HostAddress -- sender's addr --,
+     *         r-address       [5] HostAddress OPTIONAL -- recip's addr
+     * }
+     *
+     * @param message
+     * @return The {@link DERSequence};
+     */
+    private DERSequence encodePrivatePartSequence( EncKrbPrivPart message )
+    {
+        DERSequence sequence = new DERSequence();
+
+        sequence.add( new DERTaggedObject( 0, new DEROctetString( message.getUserData() ) ) );
+
+        if ( message.getTimestamp() != null )
+        {
+            sequence.add( new DERTaggedObject( 1, KerberosTimeEncoder.encode( message.getTimestamp() ) ) );
+        }
+
+        if ( message.getMicroSecond() != null )
+        {
+            sequence.add( new DERTaggedObject( 2, DERInteger.valueOf( message.getMicroSecond().intValue() ) ) );
+        }
+
+        if ( message.getSequenceNumber() != null )
+        {
+            sequence.add( new DERTaggedObject( 3, DERInteger.valueOf( message.getSequenceNumber().intValue() ) ) );
+        }
+
+        sequence.add( new DERTaggedObject( 4, HostAddressesEncoder.encode( message.getSenderAddress() ) ) );
+
+        if ( message.getRecipientAddress() != null )
+        {
+            sequence.add( new DERTaggedObject( 5, HostAddressesEncoder.encode( message.getRecipientAddress() ) ) );
+        }
+
+        return sequence;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/EncTgsRepPartEncoder.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/EncTgsRepPartEncoder.java
new file mode 100644
index 0000000..911a297
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/EncTgsRepPartEncoder.java
@@ -0,0 +1,48 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.io.encoder;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class EncTgsRepPartEncoder extends EncKdcRepPartEncoder implements EncoderFactory
+{
+    /**
+     * EncTGSRepPart ::=   [APPLICATION 26] EncKDCRepPart
+     */
+    public static final int APPLICATION_CODE = 26;
+
+
+    /**
+     * Creates a new instance of EncTgsRepPartEncoder.
+     */
+    public EncTgsRepPartEncoder()
+    {
+        super( APPLICATION_CODE );
+    }
+
+
+    public Encoder getEncoder()
+    {
+        return new EncTgsRepPartEncoder();
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/EncTicketPartEncoder.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/EncTicketPartEncoder.java
new file mode 100644
index 0000000..8c41b00
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/EncTicketPartEncoder.java
@@ -0,0 +1,129 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.io.encoder;
+
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import org.apache.directory.server.kerberos.shared.messages.Encodable;
+import org.apache.directory.server.kerberos.shared.messages.components.EncTicketPart;
+import org.apache.directory.shared.asn1.der.ASN1OutputStream;
+import org.apache.directory.shared.asn1.der.DERApplicationSpecific;
+import org.apache.directory.shared.asn1.der.DERBitString;
+import org.apache.directory.shared.asn1.der.DERGeneralString;
+import org.apache.directory.shared.asn1.der.DERSequence;
+import org.apache.directory.shared.asn1.der.DERTaggedObject;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class EncTicketPartEncoder implements Encoder, EncoderFactory
+{
+    /**
+     * Application code constant for the {@link EncTicketPart} (3).
+     */
+    private static final int APPLICATION_CODE = 3;
+
+
+    public byte[] encode( Encodable ticketPart ) throws IOException
+    {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        ASN1OutputStream aos = new ASN1OutputStream( baos );
+
+        DERSequence ticketSequence = encodeInitialSequence( ( EncTicketPart ) ticketPart );
+        aos.writeObject( DERApplicationSpecific.valueOf( APPLICATION_CODE, ticketSequence ) );
+        aos.close();
+
+        return baos.toByteArray();
+    }
+
+
+    public Encoder getEncoder()
+    {
+        return new EncTicketPartEncoder();
+    }
+
+
+    /**
+     * Encodes an {@link EncTicketPart} into a {@link DERSequence}.
+     * 
+     * -- Encrypted part of ticket
+     * EncTicketPart ::=     [APPLICATION 3] SEQUENCE {
+     *                       flags[0]             TicketFlags,
+     *                       key[1]               EncryptionKey,
+     *                       crealm[2]            Realm,
+     *                       cname[3]             PrincipalName,
+     *                       transited[4]         TransitedEncoding,
+     *                       authtime[5]          KerberosTime,
+     *                       starttime[6]         KerberosTime OPTIONAL,
+     *                       endtime[7]           KerberosTime,
+     *                       renew-till[8]        KerberosTime OPTIONAL,
+     *                       caddr[9]             HostAddresses OPTIONAL,
+     *                       authorization-data[10]   AuthorizationData OPTIONAL
+     * }
+     * 
+     * @param ticketPart 
+     * @return The {@link DERSequence}.
+     */
+    public DERSequence encodeInitialSequence( EncTicketPart ticketPart )
+    {
+        DERSequence sequence = new DERSequence();
+
+        sequence.add( new DERTaggedObject( 0, new DERBitString( ticketPart.getFlags().getBytes() ) ) );
+        sequence.add( new DERTaggedObject( 1, EncryptionKeyEncoder.encodeSequence( ticketPart.getSessionKey() ) ) );
+        sequence.add( new DERTaggedObject( 2, DERGeneralString.valueOf( ticketPart.getClientRealm().toString() ) ) );
+        sequence.add( new DERTaggedObject( 3, PrincipalNameEncoder.encode( ticketPart.getClientPrincipal() ) ) );
+        sequence.add( new DERTaggedObject( 4, TransitedEncodingEncoder.encode( ticketPart.getTransitedEncoding() ) ) );
+        sequence.add( new DERTaggedObject( 5, KerberosTimeEncoder.encode( ticketPart.getAuthTime() ) ) );
+
+        // OPTIONAL
+        if ( ticketPart.getStartTime() != null )
+        {
+            sequence.add( new DERTaggedObject( 6, KerberosTimeEncoder.encode( ticketPart.getStartTime() ) ) );
+        }
+
+        sequence.add( new DERTaggedObject( 7, KerberosTimeEncoder.encode( ticketPart.getEndTime() ) ) );
+
+        // OPTIONAL
+        if ( ticketPart.getRenewTill() != null )
+        {
+            sequence.add( new DERTaggedObject( 8, KerberosTimeEncoder.encode( ticketPart.getRenewTill() ) ) );
+        }
+
+        // OPTIONAL
+        if ( ticketPart.getClientAddresses() != null )
+        {
+            sequence
+                .add( new DERTaggedObject( 9, HostAddressesEncoder.encodeSequence( ticketPart.getClientAddresses() ) ) );
+        }
+
+        // OPTIONAL
+        if ( ticketPart.getAuthorizationData() != null )
+        {
+            sequence
+                .add( new DERTaggedObject( 10, AuthorizationDataEncoder.encode( ticketPart.getAuthorizationData() ) ) );
+        }
+
+        return sequence;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/Encoder.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/Encoder.java
new file mode 100644
index 0000000..01b385c
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/Encoder.java
@@ -0,0 +1,43 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.kerberos.shared.io.encoder;
+
+
+import java.io.IOException;
+
+import org.apache.directory.server.kerberos.shared.messages.Encodable;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public interface Encoder
+{
+    /**
+     * Encodes an ASN.1 {@link Encodable} object into a byte array.
+     *
+     * @param object
+     * @return The byte array.
+     * @throws IOException
+     */
+    public byte[] encode( Encodable object ) throws IOException;
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/EncoderFactory.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/EncoderFactory.java
new file mode 100644
index 0000000..22389e2
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/EncoderFactory.java
@@ -0,0 +1,36 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.kerberos.shared.io.encoder;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public interface EncoderFactory
+{
+    /**
+     * Returns an {@link Encoder}.
+     *
+     * @return The {@link Encoder}.
+     */
+    public Encoder getEncoder();
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/EncryptedDataEncoder.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/EncryptedDataEncoder.java
new file mode 100644
index 0000000..c175489
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/EncryptedDataEncoder.java
@@ -0,0 +1,86 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.io.encoder;
+
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptedData;
+import org.apache.directory.shared.asn1.der.ASN1OutputStream;
+import org.apache.directory.shared.asn1.der.DERInteger;
+import org.apache.directory.shared.asn1.der.DEROctetString;
+import org.apache.directory.shared.asn1.der.DERSequence;
+import org.apache.directory.shared.asn1.der.DERTaggedObject;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class EncryptedDataEncoder
+{
+    /**
+     * Encodes an {@link EncryptedData} into a byte array.
+     *
+     * @param encryptedData
+     * @return The byte array.
+     * @throws IOException
+     */
+    public static byte[] encode( EncryptedData encryptedData ) throws IOException
+    {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        ASN1OutputStream aos = new ASN1OutputStream( baos );
+
+        aos.writeObject( encodeSequence( encryptedData ) );
+        aos.close();
+
+        return baos.toByteArray();
+    }
+
+
+    /**
+     * Encodes an {@link EncryptedData} into a {@link DERSequence}.
+     * 
+     * EncryptedData ::=   SEQUENCE {
+     *             etype[0]     INTEGER, -- EncryptionEngine
+     *             kvno[1]      INTEGER OPTIONAL,
+     *             cipher[2]    OCTET STRING -- ciphertext
+     * }
+     * 
+     * @param encryptedData 
+     * @return The {@link DERSequence}.
+     */
+    public static DERSequence encodeSequence( EncryptedData encryptedData )
+    {
+        DERSequence sequence = new DERSequence();
+
+        sequence.add( new DERTaggedObject( 0, DERInteger.valueOf( encryptedData.getEType().getOrdinal() ) ) );
+
+        if ( encryptedData.hasKvno() )
+        {
+            sequence.add( new DERTaggedObject( 1, DERInteger.valueOf( encryptedData.getKvno() ) ) );
+        }
+
+        sequence.add( new DERTaggedObject( 2, new DEROctetString( encryptedData.getCipher() ) ) );
+
+        return sequence;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/EncryptedTimestampEncoder.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/EncryptedTimestampEncoder.java
new file mode 100644
index 0000000..035b0f1
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/EncryptedTimestampEncoder.java
@@ -0,0 +1,77 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.io.encoder;
+
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import org.apache.directory.server.kerberos.shared.messages.Encodable;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptedTimeStamp;
+import org.apache.directory.shared.asn1.der.ASN1OutputStream;
+import org.apache.directory.shared.asn1.der.DERInteger;
+import org.apache.directory.shared.asn1.der.DERSequence;
+import org.apache.directory.shared.asn1.der.DERTaggedObject;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class EncryptedTimestampEncoder implements Encoder, EncoderFactory
+{
+    public byte[] encode( Encodable encryptedTimestamp ) throws IOException
+    {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        ASN1OutputStream aos = new ASN1OutputStream( baos );
+
+        aos.writeObject( encodeTimestamp( ( EncryptedTimeStamp ) encryptedTimestamp ) );
+        aos.close();
+
+        return baos.toByteArray();
+    }
+
+
+    public Encoder getEncoder()
+    {
+        return new EncryptedTimestampEncoder();
+    }
+
+
+    /**
+     * PA-ENC-TS-ENC   ::= SEQUENCE {
+     *         patimestamp[0]               KerberosTime, -- client's time
+     *         pausec[1]                    INTEGER OPTIONAL
+     * }
+     */
+    private DERSequence encodeTimestamp( EncryptedTimeStamp encryptedTimestamp )
+    {
+        DERSequence sequence = new DERSequence();
+
+        sequence.add( new DERTaggedObject( 0, KerberosTimeEncoder.encode( encryptedTimestamp.getTimeStamp() ) ) );
+
+        if ( encryptedTimestamp.getMicroSeconds() > 0 )
+        {
+            sequence.add( new DERTaggedObject( 1, DERInteger.valueOf( encryptedTimestamp.getMicroSeconds() ) ) );
+        }
+
+        return sequence;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/EncryptionKeyEncoder.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/EncryptionKeyEncoder.java
new file mode 100644
index 0000000..48d5ad0
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/EncryptionKeyEncoder.java
@@ -0,0 +1,68 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.io.encoder;
+
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+import org.apache.directory.shared.asn1.der.ASN1OutputStream;
+import org.apache.directory.shared.asn1.der.DERInteger;
+import org.apache.directory.shared.asn1.der.DEROctetString;
+import org.apache.directory.shared.asn1.der.DERSequence;
+import org.apache.directory.shared.asn1.der.DERTaggedObject;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class EncryptionKeyEncoder
+{
+    /**
+     * Encodes an {@link EncryptionKey} into a byte array.
+     *
+     * @param key
+     * @return The byte array.
+     * @throws IOException
+     */
+    public static byte[] encode( EncryptionKey key ) throws IOException
+    {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        ASN1OutputStream aos = new ASN1OutputStream( baos );
+
+        aos.writeObject( encodeSequence( key ) );
+        aos.close();
+
+        return baos.toByteArray();
+    }
+
+
+    protected static DERSequence encodeSequence( EncryptionKey key )
+    {
+        DERSequence vector = new DERSequence();
+
+        vector.add( new DERTaggedObject( 0, DERInteger.valueOf( key.getKeyType().getOrdinal() ) ) );
+        vector.add( new DERTaggedObject( 1, new DEROctetString( key.getKeyValue() ) ) );
+
+        return vector;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/EncryptionTypeEncoder.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/EncryptionTypeEncoder.java
new file mode 100644
index 0000000..5fe52a4
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/EncryptionTypeEncoder.java
@@ -0,0 +1,51 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.io.encoder;
+
+
+import java.util.Set;
+
+import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
+import org.apache.directory.shared.asn1.der.DERInteger;
+import org.apache.directory.shared.asn1.der.DERSequence;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class EncryptionTypeEncoder
+{
+    /**
+     * etype[8]             SEQUENCE OF INTEGER, -- EncryptionEngine,
+     *             -- in preference order
+     */
+    protected static DERSequence encode( Set<EncryptionType> eType )
+    {
+        DERSequence sequence = new DERSequence();
+
+        for ( EncryptionType encryptionType:eType )
+        {
+            sequence.add( DERInteger.valueOf( encryptionType.getOrdinal() ) );
+        }
+
+        return sequence;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/EncryptionTypeInfo2Encoder.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/EncryptionTypeInfo2Encoder.java
new file mode 100644
index 0000000..070964f
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/EncryptionTypeInfo2Encoder.java
@@ -0,0 +1,100 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.io.encoder;
+
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionTypeInfo2Entry;
+import org.apache.directory.shared.asn1.der.ASN1OutputStream;
+import org.apache.directory.shared.asn1.der.DERGeneralString;
+import org.apache.directory.shared.asn1.der.DERInteger;
+import org.apache.directory.shared.asn1.der.DEROctetString;
+import org.apache.directory.shared.asn1.der.DERSequence;
+import org.apache.directory.shared.asn1.der.DERTaggedObject;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 540371 $, $Date: 2007-05-21 17:00:43 -0700 (Mon, 21 May 2007) $
+ */
+public class EncryptionTypeInfo2Encoder
+{
+    /**
+     * Encodes an array of {@link EncryptionTypeInfo2Entry}s into a byte array.
+     *
+     * @param entries
+     * @return The byte array.
+     * @throws IOException
+     */
+    public static byte[] encode( EncryptionTypeInfo2Entry[] entries ) throws IOException
+    {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        ASN1OutputStream aos = new ASN1OutputStream( baos );
+        aos.writeObject( encodeSequence( entries ) );
+        aos.close();
+
+        return baos.toByteArray();
+    }
+
+
+    /**
+     * ETYPE-INFO2             ::= SEQUENCE SIZE (1..MAX) OF ETYPE-INFO2-ENTRY
+     */
+    protected static DERSequence encodeSequence( EncryptionTypeInfo2Entry[] entries )
+    {
+        DERSequence sequence = new DERSequence();
+
+        for ( int ii = 0; ii < entries.length; ii++ )
+        {
+            sequence.add( encode( entries[ii] ) );
+        }
+
+        return sequence;
+    }
+
+
+    /**
+     * ETYPE-INFO2-ENTRY       ::= SEQUENCE {
+     *         etype           [0] Int32,
+     *         salt            [1] KerberosString OPTIONAL,
+     *         s2kparams       [2] OCTET STRING OPTIONAL
+     * }
+     */
+    protected static DERSequence encode( EncryptionTypeInfo2Entry entry )
+    {
+        DERSequence sequence = new DERSequence();
+
+        sequence.add( new DERTaggedObject( 0, DERInteger.valueOf( entry.getEncryptionType().getOrdinal() ) ) );
+
+        if ( entry.getSalt() != null )
+        {
+            sequence.add( new DERTaggedObject( 1, DERGeneralString.valueOf( entry.getSalt() ) ) );
+        }
+
+        if ( entry.getS2kParams() != null )
+        {
+            sequence.add( new DERTaggedObject( 2, new DEROctetString( entry.getS2kParams() ) ) );
+        }
+
+        return sequence;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/EncryptionTypeInfoEncoder.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/EncryptionTypeInfoEncoder.java
new file mode 100644
index 0000000..976fb5c
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/EncryptionTypeInfoEncoder.java
@@ -0,0 +1,93 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.io.encoder;
+
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionTypeInfoEntry;
+import org.apache.directory.shared.asn1.der.ASN1OutputStream;
+import org.apache.directory.shared.asn1.der.DERInteger;
+import org.apache.directory.shared.asn1.der.DEROctetString;
+import org.apache.directory.shared.asn1.der.DERSequence;
+import org.apache.directory.shared.asn1.der.DERTaggedObject;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class EncryptionTypeInfoEncoder
+{
+    /**
+     * Encodes an array of {@link EncryptionTypeInfoEntry}s into a byte array.
+     *
+     * @param entries
+     * @return The byte array.
+     * @throws IOException
+     */
+    public static byte[] encode( EncryptionTypeInfoEntry[] entries ) throws IOException
+    {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        ASN1OutputStream aos = new ASN1OutputStream( baos );
+        aos.writeObject( encodeSequence( entries ) );
+        aos.close();
+
+        return baos.toByteArray();
+    }
+
+
+    /**
+     * ETYPE-INFO              ::= SEQUENCE OF ETYPE-INFO-ENTRY
+     */
+    protected static DERSequence encodeSequence( EncryptionTypeInfoEntry[] entries )
+    {
+        DERSequence sequence = new DERSequence();
+
+        for ( int ii = 0; ii < entries.length; ii++ )
+        {
+            sequence.add( encode( entries[ii] ) );
+        }
+
+        return sequence;
+    }
+
+
+    /**
+     * ETYPE-INFO-ENTRY        ::= SEQUENCE {
+     *     etype               [0] Int32,
+     *     salt                [1] OCTET STRING OPTIONAL
+     * }
+     */
+    protected static DERSequence encode( EncryptionTypeInfoEntry entry )
+    {
+        DERSequence sequence = new DERSequence();
+
+        sequence.add( new DERTaggedObject( 0, DERInteger.valueOf( entry.getEncryptionType().getOrdinal() ) ) );
+
+        if ( entry.getSalt() != null )
+        {
+            sequence.add( new DERTaggedObject( 1, new DEROctetString( entry.getSalt() ) ) );
+        }
+
+        return sequence;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/ErrorMessageEncoder.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/ErrorMessageEncoder.java
new file mode 100644
index 0000000..f958835
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/ErrorMessageEncoder.java
@@ -0,0 +1,133 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.io.encoder;
+
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+import org.apache.directory.server.kerberos.shared.messages.ErrorMessage;
+import org.apache.directory.shared.asn1.der.ASN1OutputStream;
+import org.apache.directory.shared.asn1.der.DERApplicationSpecific;
+import org.apache.directory.shared.asn1.der.DERGeneralString;
+import org.apache.directory.shared.asn1.der.DERInteger;
+import org.apache.directory.shared.asn1.der.DEROctetString;
+import org.apache.directory.shared.asn1.der.DERSequence;
+import org.apache.directory.shared.asn1.der.DERTaggedObject;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ErrorMessageEncoder
+{
+    /**
+     * Encodes an {@link ErrorMessage} into a {@link ByteBuffer}.
+     *
+     * @param message
+     * @param out
+     * @throws IOException
+     */
+    public void encode( ErrorMessage message, ByteBuffer out ) throws IOException
+    {
+        ASN1OutputStream aos = new ASN1OutputStream( out );
+
+        DERSequence errorReply = encodeErrorMessageSequence( message );
+        aos.writeObject( DERApplicationSpecific.valueOf( message.getMessageType().getOrdinal(), errorReply ) );
+
+        aos.close();
+    }
+
+
+    /**
+     * Encodes an {@link ErrorMessage} into a byte array.
+     *
+     * @param message
+     * @return The byte array.
+     * @throws IOException
+     */
+    public byte[] encode( ErrorMessage message ) throws IOException
+    {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        ASN1OutputStream aos = new ASN1OutputStream( baos );
+
+        DERSequence errorReply = encodeErrorMessageSequence( message );
+        aos.writeObject( DERApplicationSpecific.valueOf( message.getMessageType().getOrdinal(), errorReply ) );
+
+        aos.close();
+
+        return baos.toByteArray();
+    }
+
+
+    private DERSequence encodeErrorMessageSequence( ErrorMessage message )
+    {
+        DERSequence sequence = new DERSequence();
+
+        sequence.add( new DERTaggedObject( 0, DERInteger.valueOf( message.getProtocolVersionNumber() ) ) );
+
+        sequence.add( new DERTaggedObject( 1, DERInteger.valueOf( message.getMessageType().getOrdinal() ) ) );
+
+        if ( message.getClientTime() != null )
+        {
+            sequence.add( new DERTaggedObject( 2, KerberosTimeEncoder.encode( message.getClientTime() ) ) );
+        }
+
+        if ( message.getClientMicroSecond() != null )
+        {
+            sequence.add( new DERTaggedObject( 3, DERInteger.valueOf( message.getClientMicroSecond().intValue() ) ) );
+        }
+
+        sequence.add( new DERTaggedObject( 4, KerberosTimeEncoder.encode( message.getServerTime() ) ) );
+
+        sequence.add( new DERTaggedObject( 5, DERInteger.valueOf( message.getServerMicroSecond() ) ) );
+
+        sequence.add( new DERTaggedObject( 6, DERInteger.valueOf( message.getErrorCode() ) ) );
+
+        if ( message.getClientPrincipal() != null )
+        {
+            sequence.add( new DERTaggedObject( 7, DERGeneralString.valueOf( message.getClientPrincipal().getRealm()
+                .toString() ) ) );
+        }
+
+        if ( message.getClientPrincipal() != null )
+        {
+            sequence.add( new DERTaggedObject( 8, PrincipalNameEncoder.encode( message.getClientPrincipal() ) ) );
+        }
+
+        sequence.add( new DERTaggedObject( 9, DERGeneralString.valueOf( message.getServerPrincipal().getRealm() ) ) );
+
+        sequence.add( new DERTaggedObject( 10, PrincipalNameEncoder.encode( message.getServerPrincipal() ) ) );
+
+        if ( message.getExplanatoryText() != null )
+        {
+            sequence.add( new DERTaggedObject( 11, DERGeneralString.valueOf( message.getExplanatoryText() ) ) );
+        }
+
+        if ( message.getExplanatoryData() != null )
+        {
+            sequence.add( new DERTaggedObject( 12, new DEROctetString( message.getExplanatoryData() ) ) );
+        }
+
+        return sequence;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/HostAddressesEncoder.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/HostAddressesEncoder.java
new file mode 100644
index 0000000..26adc6b
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/HostAddressesEncoder.java
@@ -0,0 +1,72 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.io.encoder;
+
+
+import org.apache.directory.server.kerberos.shared.messages.value.HostAddress;
+import org.apache.directory.server.kerberos.shared.messages.value.HostAddresses;
+import org.apache.directory.shared.asn1.der.DERInteger;
+import org.apache.directory.shared.asn1.der.DEROctetString;
+import org.apache.directory.shared.asn1.der.DERSequence;
+import org.apache.directory.shared.asn1.der.DERTaggedObject;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class HostAddressesEncoder
+{
+    /**
+     * HostAddresses ::=   SEQUENCE OF SEQUENCE {
+     *                     addr-type[0]             INTEGER,
+     *                     address[1]               OCTET STRING
+     * }
+     */
+    protected static DERSequence encodeSequence( HostAddresses hosts )
+    {
+        HostAddress[] addresses = hosts.getAddresses();
+        DERSequence sequence = new DERSequence();
+
+        for ( int ii = 0; ii < addresses.length; ii++ )
+        {
+            sequence.add( encode( addresses[ii] ) );
+        }
+
+        return sequence;
+    }
+
+
+    /**
+     *  HostAddress ::=     SEQUENCE  {
+     *                     addr-type[0]             INTEGER,
+     *                     address[1]               OCTET STRING
+     * }
+     */
+    protected static DERSequence encode( HostAddress host )
+    {
+        DERSequence sequence = new DERSequence();
+
+        sequence.add( new DERTaggedObject( 0, DERInteger.valueOf( host.getAddrType().getOrdinal() ) ) );
+        sequence.add( new DERTaggedObject( 1, new DEROctetString( host.getAddress() ) ) );
+
+        return sequence;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/KdcReplyEncoder.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/KdcReplyEncoder.java
new file mode 100644
index 0000000..2e3052d
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/KdcReplyEncoder.java
@@ -0,0 +1,120 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.io.encoder;
+
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+import org.apache.directory.server.kerberos.shared.messages.KdcReply;
+import org.apache.directory.server.kerberos.shared.messages.value.PaData;
+import org.apache.directory.shared.asn1.der.ASN1OutputStream;
+import org.apache.directory.shared.asn1.der.DERApplicationSpecific;
+import org.apache.directory.shared.asn1.der.DERGeneralString;
+import org.apache.directory.shared.asn1.der.DERInteger;
+import org.apache.directory.shared.asn1.der.DEROctetString;
+import org.apache.directory.shared.asn1.der.DERSequence;
+import org.apache.directory.shared.asn1.der.DERTaggedObject;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class KdcReplyEncoder
+{
+    /**
+     * Encodes a {@link KdcReply} into a {@link ByteBuffer}.
+     * 
+     * AS-REP ::=    [APPLICATION 11] KDC-REP
+     * TGS-REP ::=   [APPLICATION 13] KDC-REP
+     *
+     * @param app
+     * @param out
+     * @throws IOException
+     */
+    public void encode( KdcReply app, ByteBuffer out ) throws IOException
+    {
+        ASN1OutputStream aos = new ASN1OutputStream( out );
+
+        DERSequence kdcrep = encodeKdcReplySequence( app );
+        aos.writeObject( DERApplicationSpecific.valueOf( app.getMessageType().getOrdinal(), kdcrep ) );
+
+        aos.close();
+    }
+
+
+    /*
+     KDC-REP ::=   SEQUENCE {
+     pvno[0]                    INTEGER,
+     msg-type[1]                INTEGER,
+     padata[2]                  SEQUENCE OF PA-DATA OPTIONAL,
+     crealm[3]                  Realm,
+     cname[4]                   PrincipalName,
+     ticket[5]                  Ticket,
+     enc-part[6]                EncryptedData
+     }*/
+    private DERSequence encodeKdcReplySequence( KdcReply app )
+    {
+        DERSequence sequence = new DERSequence();
+
+        sequence.add( new DERTaggedObject( 0, DERInteger.valueOf( app.getProtocolVersionNumber() ) ) );
+
+        sequence.add( new DERTaggedObject( 1, DERInteger.valueOf( app.getMessageType().getOrdinal() ) ) );
+
+        if ( app.getPaData() != null )
+        {
+            sequence.add( new DERTaggedObject( 2, encodePreAuthData( app.getPaData() ) ) );
+        }
+
+        sequence.add( new DERTaggedObject( 3, DERGeneralString.valueOf( app.getClientRealm().toString() ) ) );
+
+        sequence.add( new DERTaggedObject( 4, PrincipalNameEncoder.encode( app.getClientPrincipal() ) ) );
+
+        sequence.add( new DERTaggedObject( 5, TicketEncoder.encode( app.getTicket() ) ) );
+
+        sequence.add( new DERTaggedObject( 6, EncryptedDataEncoder.encodeSequence( app.getEncPart() ) ) );
+
+        return sequence;
+    }
+
+
+    /*
+     PA-DATA ::=        SEQUENCE {
+     padata-type[1]        INTEGER,
+     padata-value[2]       OCTET STRING,
+     -- might be encoded AP-REQ
+     }*/
+    private DERSequence encodePreAuthData( PaData[] preAuthData )
+    {
+        DERSequence preAuth = new DERSequence();
+
+        for ( int ii = 0; ii < preAuthData.length; ii++ )
+        {
+            DERSequence sequence = new DERSequence();
+
+            sequence.add( new DERTaggedObject( 1, DERInteger.valueOf( preAuthData[ii].getPaDataType().getOrdinal() ) ) );
+            sequence.add( new DERTaggedObject( 2, new DEROctetString( preAuthData[ii].getPaDataValue() ) ) );
+            preAuth.add( sequence );
+        }
+
+        return preAuth;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/KdcRequestEncoder.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/KdcRequestEncoder.java
new file mode 100644
index 0000000..c88dc35
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/KdcRequestEncoder.java
@@ -0,0 +1,216 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.io.encoder;
+
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+import org.apache.directory.server.kerberos.shared.messages.KdcRequest;
+import org.apache.directory.server.kerberos.shared.messages.value.PaData;
+import org.apache.directory.server.kerberos.shared.messages.value.RequestBody;
+import org.apache.directory.shared.asn1.der.ASN1OutputStream;
+import org.apache.directory.shared.asn1.der.DERApplicationSpecific;
+import org.apache.directory.shared.asn1.der.DERBitString;
+import org.apache.directory.shared.asn1.der.DERGeneralString;
+import org.apache.directory.shared.asn1.der.DERInteger;
+import org.apache.directory.shared.asn1.der.DEROctetString;
+import org.apache.directory.shared.asn1.der.DERSequence;
+import org.apache.directory.shared.asn1.der.DERTaggedObject;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class KdcRequestEncoder
+{
+    /**
+     * Encodes a {@link KdcRequest} into a {@link ByteBuffer}.
+     * 
+     * AS-REQ ::=         [APPLICATION 10] KDC-REQ
+     * TGS-REQ ::=        [APPLICATION 12] KDC-REQ
+     *
+     * @param request
+     * @param out
+     * @throws IOException
+     */
+    public void encode( KdcRequest request, ByteBuffer out ) throws IOException
+    {
+        ASN1OutputStream aos = new ASN1OutputStream( out );
+
+        DERSequence kdcRequest = encodeInitialSequence( request );
+        aos.writeObject( DERApplicationSpecific.valueOf( request.getMessageType().getOrdinal(), kdcRequest ) );
+        aos.close();
+    }
+
+
+    /*
+     KDC-REQ ::=        SEQUENCE {
+     pvno[1]               INTEGER,
+     msg-type[2]           INTEGER,
+     padata[3]             SEQUENCE OF PA-DATA OPTIONAL,
+     req-body[4]           KDC-REQ-BODY
+     }*/
+    private DERSequence encodeInitialSequence( KdcRequest app )
+    {
+        DERSequence sequence = new DERSequence();
+
+        sequence.add( new DERTaggedObject( 1, DERInteger.valueOf( app.getProtocolVersionNumber() ) ) );
+
+        sequence.add( new DERTaggedObject( 2, DERInteger.valueOf( app.getMessageType().getOrdinal() ) ) );
+
+        if ( app.getPreAuthData() != null )
+        {
+            sequence.add( new DERTaggedObject( 3, encodePreAuthData( app.getPreAuthData() ) ) );
+        }
+
+        sequence.add( new DERTaggedObject( 4, encodeKdcRequestBody( app.getRequestBody() ) ) );
+
+        return sequence;
+    }
+
+
+    /**
+     * Encodes a {@link KdcRequest} into a byte[].
+     *
+     * @param requestBody
+     * @return The encoded {@link KdcRequest}.
+     * @throws IOException
+     */
+    public byte[] encodeRequestBody( RequestBody requestBody ) throws IOException
+    {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        ASN1OutputStream aos = new ASN1OutputStream( baos );
+
+        aos.writeObject( encodeKdcRequestBody( requestBody ) );
+        aos.close();
+
+        return baos.toByteArray();
+    }
+
+
+    /**
+     * KDC-REQ-BODY ::=   SEQUENCE {
+     *     kdc-options[0]       KDCOptions,
+     *     cname[1]             PrincipalName OPTIONAL,
+     *                  -- Used only in AS-REQ
+     *     realm[2]             Realm, -- Server's realm
+     *                  -- Also client's in AS-REQ
+     *     sname[3]             PrincipalName OPTIONAL,
+     *     from[4]              KerberosTime OPTIONAL,
+     *     till[5]              KerberosTime,
+     *     rtime[6]             KerberosTime OPTIONAL,
+     *     nonce[7]             INTEGER,
+     * 
+     *     etype[8]             SEQUENCE OF INTEGER, -- EncryptionEngine,
+     *                  -- in preference order
+     *     addresses[9]         HostAddresses OPTIONAL,
+     *     enc-authorization-data[10]   EncryptedData OPTIONAL,
+     *                  -- Encrypted AuthorizationData encoding
+     *     additional-tickets[11]       SEQUENCE OF Ticket OPTIONAL
+     * }
+     */
+    private DERSequence encodeKdcRequestBody( RequestBody requestBody )
+    {
+        DERSequence sequence = new DERSequence();
+
+        sequence.add( new DERTaggedObject( 0, new DERBitString( requestBody.getKdcOptions().getBytes() ) ) );
+
+        // OPTIONAL
+        if ( requestBody.getClientPrincipal() != null )
+        {
+            sequence.add( new DERTaggedObject( 1, PrincipalNameEncoder.encode( requestBody.getClientPrincipal() ) ) );
+        }
+
+        sequence.add( new DERTaggedObject( 2, DERGeneralString.valueOf( requestBody.getServerPrincipal().getRealm()
+            .toString() ) ) );
+
+        // OPTIONAL
+        if ( requestBody.getServerPrincipal() != null )
+        {
+            sequence.add( new DERTaggedObject( 3, PrincipalNameEncoder.encode( requestBody.getServerPrincipal() ) ) );
+        }
+
+        // OPTIONAL
+        if ( requestBody.getFrom() != null )
+        {
+            sequence.add( new DERTaggedObject( 4, KerberosTimeEncoder.encode( requestBody.getFrom() ) ) );
+        }
+
+        sequence.add( new DERTaggedObject( 5, KerberosTimeEncoder.encode( requestBody.getTill() ) ) );
+
+        // OPTIONAL
+        if ( requestBody.getRtime() != null )
+        {
+            sequence.add( new DERTaggedObject( 6, KerberosTimeEncoder.encode( requestBody.getRtime() ) ) );
+        }
+
+        sequence.add( new DERTaggedObject( 7, DERInteger.valueOf( requestBody.getNonce() ) ) );
+
+        sequence.add( new DERTaggedObject( 8, EncryptionTypeEncoder.encode( requestBody.getEType() ) ) );
+
+        // OPTIONAL
+        if ( requestBody.getAddresses() != null )
+        {
+            sequence.add( new DERTaggedObject( 9, HostAddressesEncoder.encodeSequence( requestBody.getAddresses() ) ) );
+        }
+
+        // OPTIONAL
+        if ( requestBody.getEncAuthorizationData() != null )
+        {
+            sequence.add( new DERTaggedObject( 10, EncryptedDataEncoder.encodeSequence( requestBody
+                .getEncAuthorizationData() ) ) );
+        }
+
+        // OPTIONAL
+        if ( requestBody.getAdditionalTickets() != null )
+        {
+            sequence
+                .add( new DERTaggedObject( 11, TicketEncoder.encodeSequence( requestBody.getAdditionalTickets() ) ) );
+        }
+
+        return sequence;
+    }
+
+
+    /*
+     PA-DATA ::=        SEQUENCE {
+     padata-type[1]        INTEGER,
+     padata-value[2]       OCTET STRING,
+     -- might be encoded AP-REQ
+     }*/
+    private DERSequence encodePreAuthData( PaData[] preAuthData )
+    {
+        DERSequence preAuth = new DERSequence();
+
+        for ( int ii = 0; ii < preAuthData.length; ii++ )
+        {
+            DERSequence sequence = new DERSequence();
+
+            sequence.add( new DERTaggedObject( 1, DERInteger.valueOf( preAuthData[ii].getPaDataType().getOrdinal() ) ) );
+            sequence.add( new DERTaggedObject( 2, new DEROctetString( preAuthData[ii].getPaDataValue() ) ) );
+            preAuth.add( sequence );
+        }
+
+        return preAuth;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/KerberosTimeEncoder.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/KerberosTimeEncoder.java
new file mode 100644
index 0000000..19f4f5e
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/KerberosTimeEncoder.java
@@ -0,0 +1,41 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.io.encoder;
+
+
+import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
+import org.apache.directory.shared.asn1.der.DERGeneralizedTime;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class KerberosTimeEncoder
+{
+    /**
+     * KerberosTime ::=   GeneralizedTime
+     *             -- Specifying UTC time zone (Z)
+     */
+    protected static DERGeneralizedTime encode( KerberosTime time )
+    {
+        return DERGeneralizedTime.valueOf( time.toDate() );
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/LastRequestEncoder.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/LastRequestEncoder.java
new file mode 100644
index 0000000..0210256
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/LastRequestEncoder.java
@@ -0,0 +1,60 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.io.encoder;
+
+
+import org.apache.directory.server.kerberos.shared.messages.value.LastRequest;
+import org.apache.directory.server.kerberos.shared.messages.value.LastRequestEntry;
+import org.apache.directory.shared.asn1.der.DERGeneralizedTime;
+import org.apache.directory.shared.asn1.der.DERInteger;
+import org.apache.directory.shared.asn1.der.DERSequence;
+import org.apache.directory.shared.asn1.der.DERTaggedObject;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class LastRequestEncoder
+{
+    /**
+     * LastReq ::=   SEQUENCE OF SEQUENCE {
+     * lr-type[0]               INTEGER,
+     * lr-value[1]              KerberosTime
+     * }
+     */
+    protected static DERSequence encode( LastRequest lastReq )
+    {
+        LastRequestEntry[] entries = lastReq.getEntries();
+        DERSequence outerSequence = new DERSequence();
+
+        for ( int ii = 0; ii < entries.length; ii++ )
+        {
+            DERSequence sequence = new DERSequence();
+            sequence
+                .add( new DERTaggedObject( 0, DERInteger.valueOf( entries[ii].getLastRequestType().getOrdinal() ) ) );
+            sequence.add( new DERTaggedObject( 1, DERGeneralizedTime.valueOf( entries[ii].getLastRequestValue()
+                .toDate() ) ) );
+            outerSequence.add( sequence );
+        }
+
+        return outerSequence;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/PreAuthenticationDataEncoder.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/PreAuthenticationDataEncoder.java
new file mode 100644
index 0000000..6356c41
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/PreAuthenticationDataEncoder.java
@@ -0,0 +1,94 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.io.encoder;
+
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import org.apache.directory.server.kerberos.shared.messages.value.PaData;
+import org.apache.directory.shared.asn1.der.ASN1OutputStream;
+import org.apache.directory.shared.asn1.der.DERInteger;
+import org.apache.directory.shared.asn1.der.DEROctetString;
+import org.apache.directory.shared.asn1.der.DERSequence;
+import org.apache.directory.shared.asn1.der.DERTaggedObject;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class PreAuthenticationDataEncoder
+{
+    /**
+     * Encodes an array of {@link PaData}s into a byte array.
+     *
+     * @param preAuth
+     * @return The byte array.
+     * @throws IOException
+     */
+    public static byte[] encode( PaData[] preAuth ) throws IOException
+    {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        ASN1OutputStream aos = new ASN1OutputStream( baos );
+
+        aos.writeObject( encodeSequence( preAuth ) );
+        aos.close();
+
+        return baos.toByteArray();
+    }
+
+
+    /**
+     * METHOD-DATA     ::= SEQUENCE OF PA-DATA
+     */
+    protected static DERSequence encodeSequence( PaData[] preAuth )
+    {
+        DERSequence sequence = new DERSequence();
+
+        for ( int ii = 0; ii < preAuth.length; ii++ )
+        {
+            sequence.add( encode( preAuth[ii] ) );
+        }
+
+        return sequence;
+    }
+
+
+    /**
+     * PA-DATA ::=        SEQUENCE {
+     *         padata-type[1]        INTEGER,
+     *         padata-value[2]       OCTET STRING
+     * }
+     */
+    protected static DERSequence encode( PaData preAuth )
+    {
+        DERSequence sequence = new DERSequence();
+
+        sequence.add( new DERTaggedObject( 1, DERInteger.valueOf( preAuth.getPaDataType().getOrdinal() ) ) );
+
+        if ( preAuth.getPaDataValue() != null )
+        {
+            sequence.add( new DERTaggedObject( 2, new DEROctetString( preAuth.getPaDataValue() ) ) );
+        }
+
+        return sequence;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/PrincipalNameEncoder.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/PrincipalNameEncoder.java
new file mode 100644
index 0000000..5b38928
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/PrincipalNameEncoder.java
@@ -0,0 +1,119 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.io.encoder;
+
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.apache.directory.server.kerberos.shared.messages.value.PrincipalName;
+import org.apache.directory.shared.asn1.der.DERGeneralString;
+import org.apache.directory.shared.asn1.der.DERInteger;
+import org.apache.directory.shared.asn1.der.DERSequence;
+import org.apache.directory.shared.asn1.der.DERTaggedObject;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class PrincipalNameEncoder
+{
+    private static final String COMPONENT_SEPARATOR = "/";
+    private static final String REALM_SEPARATOR = "@";
+
+
+    /**
+     * Encodes a {@link KerberosPrincipal} into a {@link DERSequence}.
+     * 
+     * PrincipalName ::=   SEQUENCE {
+     *               name-type[0]     INTEGER,
+     *               name-string[1]   SEQUENCE OF GeneralString
+     * }
+     * 
+     * @param principal 
+     * @return The {@link DERSequence}.
+     */
+    public static DERSequence encode( KerberosPrincipal principal )
+    {
+        DERSequence vector = new DERSequence();
+
+        vector.add( new DERTaggedObject( 0, DERInteger.valueOf( principal.getNameType() ) ) );
+        vector.add( new DERTaggedObject( 1, encodeNameSequence( principal ) ) );
+
+        return vector;
+    }
+
+
+    /**
+     * Encodes a {@link PrincipalName} into a {@link DERSequence}.
+     *
+     * @param name
+     * @return The {@link DERSequence}.
+     */
+    public static DERSequence encode( PrincipalName name )
+    {
+        DERSequence vector = new DERSequence();
+
+        vector.add( new DERTaggedObject( 0, DERInteger.valueOf( name.getNameType().getOrdinal() ) ) );
+        vector.add( new DERTaggedObject( 1, encodeNameSequence( name ) ) );
+
+        return vector;
+    }
+
+
+    private static DERSequence encodeNameSequence( KerberosPrincipal principal )
+    {
+        Iterator<String> it = getNameStrings( principal ).iterator();
+
+        DERSequence vector = new DERSequence();
+
+        while ( it.hasNext() )
+        {
+            vector.add( DERGeneralString.valueOf( it.next() ) );
+        }
+
+        return vector;
+    }
+
+
+    private static List<String> getNameStrings( KerberosPrincipal principal )
+    {
+        String nameComponent = principal.getName().split( REALM_SEPARATOR )[0];
+        String[] components = nameComponent.split( COMPONENT_SEPARATOR );
+        return Arrays.asList( components );
+    }
+
+
+    private static DERSequence encodeNameSequence( PrincipalName principalName )
+    {
+        DERSequence vector = new DERSequence();
+
+        for ( String name:principalName.getNames() )
+        {
+            vector.add( DERGeneralString.valueOf( name ) );
+        }
+
+        return vector;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/PrivateMessageEncoder.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/PrivateMessageEncoder.java
new file mode 100644
index 0000000..e3038e0
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/PrivateMessageEncoder.java
@@ -0,0 +1,72 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.io.encoder;
+
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import org.apache.directory.server.kerberos.shared.messages.application.PrivateMessage;
+import org.apache.directory.shared.asn1.der.ASN1OutputStream;
+import org.apache.directory.shared.asn1.der.DERApplicationSpecific;
+import org.apache.directory.shared.asn1.der.DERInteger;
+import org.apache.directory.shared.asn1.der.DERSequence;
+import org.apache.directory.shared.asn1.der.DERTaggedObject;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class PrivateMessageEncoder
+{
+    /**
+     * Encodes a {@link PrivateMessage} into a byte array.
+     *
+     * @param message
+     * @return The byte array.
+     * @throws IOException
+     */
+    public byte[] encode( PrivateMessage message ) throws IOException
+    {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        ASN1OutputStream aos = new ASN1OutputStream( baos );
+
+        DERSequence privateMessageSequence = encodePrivateMessageSequence( message );
+        aos
+            .writeObject( DERApplicationSpecific
+                .valueOf( message.getMessageType().getOrdinal(), privateMessageSequence ) );
+        aos.close();
+
+        return baos.toByteArray();
+    }
+
+
+    private DERSequence encodePrivateMessageSequence( PrivateMessage message )
+    {
+        DERSequence sequence = new DERSequence();
+
+        sequence.add( new DERTaggedObject( 0, DERInteger.valueOf( message.getProtocolVersionNumber() ) ) );
+        sequence.add( new DERTaggedObject( 1, DERInteger.valueOf( message.getMessageType().getOrdinal() ) ) );
+        sequence.add( new DERTaggedObject( 3, EncryptedDataEncoder.encodeSequence( message.getEncryptedPart() ) ) );
+
+        return sequence;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/TicketEncoder.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/TicketEncoder.java
new file mode 100644
index 0000000..973fcca
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/TicketEncoder.java
@@ -0,0 +1,105 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.io.encoder;
+
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import org.apache.directory.server.kerberos.shared.messages.components.Ticket;
+import org.apache.directory.shared.asn1.der.ASN1OutputStream;
+import org.apache.directory.shared.asn1.der.DERApplicationSpecific;
+import org.apache.directory.shared.asn1.der.DERGeneralString;
+import org.apache.directory.shared.asn1.der.DERInteger;
+import org.apache.directory.shared.asn1.der.DERSequence;
+import org.apache.directory.shared.asn1.der.DERTaggedObject;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class TicketEncoder
+{
+    /**
+     * Encodes a {@link Ticket} into a its ASN.1 DER encoding.
+     * 
+     * @param ticket
+     * @return The byte[] containing the ASN.1 DER encoding of the {@link Ticket}.
+     * @throws IOException
+     */
+    public static byte[] encodeTicket( Ticket ticket ) throws IOException
+    {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        ASN1OutputStream aos = new ASN1OutputStream( baos );
+
+        aos.writeObject( encode( ticket ) );
+        aos.close();
+
+        return baos.toByteArray();
+    }
+
+
+    /**
+     * Ticket ::=                    [APPLICATION 1] SEQUENCE {
+     *     tkt-vno[0]                   INTEGER,
+     *     realm[1]                     Realm,
+     *     sname[2]                     PrincipalName,
+     *     enc-part[3]                  EncryptedData
+     * }
+     */
+    protected static DERApplicationSpecific encode( Ticket ticket )
+    {
+        DERSequence vector = new DERSequence();
+
+        vector.add( new DERTaggedObject( 0, DERInteger.valueOf( ticket.getTktVno() ) ) );
+        vector.add( new DERTaggedObject( 1, DERGeneralString.valueOf( ticket.getRealm() ) ) );
+        vector.add( new DERTaggedObject( 2, PrincipalNameEncoder.encode( ticket.getSName() ) ) );
+        vector.add( new DERTaggedObject( 3, EncryptedDataEncoder.encodeSequence( ticket.getEncPart() ) ) );
+
+        DERApplicationSpecific ticketSequence = null;
+
+        try
+        {
+            ticketSequence = DERApplicationSpecific.valueOf( 1, vector );
+        }
+        catch ( Exception e )
+        {
+            e.printStackTrace();
+        }
+
+        return ticketSequence;
+    }
+
+
+    protected static DERSequence encodeSequence( Ticket[] tickets )
+    {
+        DERSequence outerVector = new DERSequence();
+
+        for ( int ii = 0; ii < tickets.length; ii++ )
+        {
+            DERSequence vector = new DERSequence();
+            vector.add( encode( tickets[ii] ) );
+            outerVector.add( vector );
+        }
+
+        return outerVector;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/TransitedEncodingEncoder.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/TransitedEncodingEncoder.java
new file mode 100644
index 0000000..d3a60ad
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/TransitedEncodingEncoder.java
@@ -0,0 +1,52 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.io.encoder;
+
+
+import org.apache.directory.server.kerberos.shared.messages.value.TransitedEncoding;
+import org.apache.directory.shared.asn1.der.DERInteger;
+import org.apache.directory.shared.asn1.der.DEROctetString;
+import org.apache.directory.shared.asn1.der.DERSequence;
+import org.apache.directory.shared.asn1.der.DERTaggedObject;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class TransitedEncodingEncoder
+{
+    /**
+     * TransitedEncoding ::=         SEQUENCE {
+     *     tr-type[0]  INTEGER, -- must be registered
+     *     contents[1]          OCTET STRING
+     * }
+     */
+    protected static DERSequence encode( TransitedEncoding te )
+    {
+
+        DERSequence sequence = new DERSequence();
+
+        sequence.add( new DERTaggedObject( 0, DERInteger.valueOf( te.getTrType().getOrdinal() ) ) );
+        sequence.add( new DERTaggedObject( 1, new DEROctetString( te.getContents() ) ) );
+
+        return sequence;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/package-info.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/package-info.java
new file mode 100644
index 0000000..69dd6d7
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/io/encoder/package-info.java
@@ -0,0 +1,28 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+/**
+ * Provides the encoders for Kerberos message objects.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+
+package org.apache.directory.server.kerberos.shared.io.encoder;
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/jaas/CallbackHandlerBean.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/jaas/CallbackHandlerBean.java
new file mode 100755
index 0000000..4f6768f
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/jaas/CallbackHandlerBean.java
@@ -0,0 +1,79 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.jaas;
+
+
+import java.io.IOException;
+
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class CallbackHandlerBean implements CallbackHandler
+{
+    private String name;
+    private String password;
+
+
+    /**
+     * Creates a new instance of CallbackHandlerBean.
+     *
+     * @param name
+     * @param password
+     */
+    public CallbackHandlerBean( String name, String password )
+    {
+        this.name = name;
+        this.password = password;
+    }
+
+
+    public void handle( Callback[] callbacks ) throws UnsupportedCallbackException, IOException
+    {
+        for ( int ii = 0; ii < callbacks.length; ii++ )
+        {
+            Callback callBack = callbacks[ii];
+
+            // Handles username callback.
+            if ( callBack instanceof NameCallback )
+            {
+                NameCallback nameCallback = ( NameCallback ) callBack;
+                nameCallback.setName( name );
+                // Handles password callback.
+            }
+            else if ( callBack instanceof PasswordCallback )
+            {
+                PasswordCallback passwordCallback = ( PasswordCallback ) callBack;
+                passwordCallback.setPassword( password.toCharArray() );
+            }
+            else
+            {
+                throw new UnsupportedCallbackException( callBack, "Callback not supported" );
+            }
+        }
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/jaas/Krb5LoginConfiguration.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/jaas/Krb5LoginConfiguration.java
new file mode 100644
index 0000000..3d85ac6
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/jaas/Krb5LoginConfiguration.java
@@ -0,0 +1,67 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.jaas;
+
+
+import java.util.HashMap;
+
+import javax.security.auth.login.AppConfigurationEntry;
+import javax.security.auth.login.Configuration;
+import javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class Krb5LoginConfiguration extends Configuration
+{
+    private static AppConfigurationEntry[] configList = new AppConfigurationEntry[1];
+
+
+    /**
+     * Creates a new instance of Krb5LoginConfiguration.
+     */
+    public Krb5LoginConfiguration()
+    {
+        String loginModule = "com.sun.security.auth.module.Krb5LoginModule";
+        LoginModuleControlFlag flag = LoginModuleControlFlag.REQUIRED;
+        configList[0] = new AppConfigurationEntry( loginModule, flag, new HashMap<String, Object>() );
+    }
+
+
+    /**
+     * Interface method requiring us to return all the LoginModules we know about.
+     */
+    public AppConfigurationEntry[] getAppConfigurationEntry( String applicationName )
+    {
+        // We will ignore the applicationName, since we want all apps to use Kerberos V5
+        return configList;
+    }
+
+
+    /**
+     * Interface method for reloading the configuration.  We don't need this.
+     */
+    public void refresh()
+    {
+        // Right now this is a load once scheme and we will not implement the refresh method
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/jaas/package-info.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/jaas/package-info.java
new file mode 100644
index 0000000..81d03b7
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/jaas/package-info.java
@@ -0,0 +1,28 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+/**
+ * Provides utility classes for working with JAAS.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+
+package org.apache.directory.server.kerberos.shared.jaas;
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/keytab/Keytab.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/keytab/Keytab.java
new file mode 100644
index 0000000..616f72f
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/keytab/Keytab.java
@@ -0,0 +1,240 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.keytab;
+
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.channels.FileChannel;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.mina.common.ByteBuffer;
+
+
+/**
+ * Keytab file.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class Keytab
+{
+    /**
+     * Byte array constant for keytab file format 5.1.
+     */
+    public static final byte[] VERSION_51 = new byte[]
+        { ( byte ) 0x05, ( byte ) 0x01 };
+
+    /**
+     * Byte array constant for keytab file format 5.2.
+     */
+    public static final byte[] VERSION_52 = new byte[]
+        { ( byte ) 0x05, ( byte ) 0x02 };
+
+    private byte[] keytabVersion = VERSION_52;
+    private List<KeytabEntry> entries = new ArrayList<KeytabEntry>();
+
+
+    /**
+     * Read a keytab file.
+     *
+     * @param file
+     * @return The keytab.
+     * @throws IOException
+     */
+    public static Keytab read( File file ) throws IOException
+    {
+        ByteBuffer buffer = ByteBuffer.wrap( getBytesFromFile( file ) );
+        return readKeytab( buffer );
+    }
+
+
+    /**
+     * Returns a new instance of a keytab with the version
+     * defaulted to 5.2.
+     *
+     * @return The keytab.
+     */
+    public static Keytab getInstance()
+    {
+        return new Keytab();
+    }
+
+
+    /**
+     * Write the keytab to a {@link File}.
+     *
+     * @param file
+     * @throws IOException
+     */
+    public void write( File file ) throws IOException
+    {
+        KeytabEncoder writer = new KeytabEncoder();
+        ByteBuffer buffer = writer.write( keytabVersion, entries );
+        writeFile( buffer, file );
+    }
+
+
+    /**
+     * @param entries The entries to set.
+     */
+    public void setEntries( List<KeytabEntry> entries )
+    {
+        this.entries = entries;
+    }
+
+
+    /**
+     * @param keytabVersion The keytabVersion to set.
+     */
+    public void setKeytabVersion( byte[] keytabVersion )
+    {
+        this.keytabVersion = keytabVersion;
+    }
+
+
+    /**
+     * @return The entries.
+     */
+    public List<KeytabEntry> getEntries()
+    {
+        return Collections.unmodifiableList( entries );
+    }
+
+
+    /**
+     * @return The keytabVersion.
+     */
+    public byte[] getKeytabVersion()
+    {
+        return keytabVersion;
+    }
+
+
+    /**
+     * Read bytes into a keytab.
+     *
+     * @param bytes
+     * @return The keytab.
+     */
+    static Keytab read( byte[] bytes )
+    {
+        ByteBuffer buffer = ByteBuffer.wrap( bytes );
+        return readKeytab( buffer );
+    }
+
+
+    /**
+     * Write the keytab to a {@link ByteBuffer}.
+     * @return The buffer.
+     */
+    ByteBuffer write()
+    {
+        KeytabEncoder writer = new KeytabEncoder();
+        return writer.write( keytabVersion, entries );
+    }
+
+
+    /**
+     * Read the contents of the buffer into a keytab.
+     *
+     * @param buffer
+     * @return The keytab.
+     */
+    private static Keytab readKeytab( ByteBuffer buffer )
+    {
+        KeytabDecoder reader = new KeytabDecoder();
+        byte[] keytabVersion = reader.getKeytabVersion( buffer );
+        List<KeytabEntry> entries = reader.getKeytabEntries( buffer );
+
+        Keytab keytab = new Keytab();
+
+        keytab.setKeytabVersion( keytabVersion );
+        keytab.setEntries( entries );
+
+        return keytab;
+    }
+
+
+    /**
+     * Returns the contents of the {@link File} in a byte array.
+     *
+     * @param file
+     * @return The byte array of the file contents.
+     * @throws IOException
+     */
+    protected static byte[] getBytesFromFile( File file ) throws IOException
+    {
+        InputStream is = new FileInputStream( file );
+
+        long length = file.length();
+
+        // Check to ensure that file is not larger than Integer.MAX_VALUE.
+        if ( length > Integer.MAX_VALUE )
+        {
+            throw new IOException( "File is too large " + file.getName() );
+        }
+
+        // Create the byte array to hold the data.
+        byte[] bytes = new byte[( int ) length];
+
+        // Read in the bytes
+        int offset = 0;
+        int numRead = 0;
+        while ( offset < bytes.length && ( numRead = is.read( bytes, offset, bytes.length - offset ) ) >= 0 )
+        {
+            offset += numRead;
+        }
+
+        // Ensure all the bytes have been read in.
+        if ( offset < bytes.length )
+        {
+            throw new IOException( "Could not completely read file " + file.getName() );
+        }
+
+        // Close the input stream and return bytes.
+        is.close();
+        return bytes;
+    }
+
+
+    /**
+     * Write the contents of the {@link ByteBuffer} to a {@link File}.
+     *
+     * @param buffer
+     * @param file
+     * @throws IOException
+     */
+    protected void writeFile( ByteBuffer buffer, File file ) throws IOException
+    {
+        // Set append false to replace existing.
+        FileChannel wChannel = new FileOutputStream( file, false ).getChannel();
+
+        // Write the bytes between the position and limit.
+        wChannel.write( buffer.buf() );
+
+        wChannel.close();
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/keytab/KeytabDecoder.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/keytab/KeytabDecoder.java
new file mode 100644
index 0000000..edeb805
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/keytab/KeytabDecoder.java
@@ -0,0 +1,180 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.keytab;
+
+
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
+import org.apache.mina.common.ByteBuffer;
+
+
+/**
+ * Decode a {@link ByteBuffer} into keytab fields.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+class KeytabDecoder
+{
+    /**
+     * Read the keytab 16-bit file format version.  This
+     * keytab reader currently only supports version 5.2.
+     */
+    byte[] getKeytabVersion( ByteBuffer buffer )
+    {
+        byte[] version = new byte[2];
+        buffer.get( version );
+
+        return version;
+    }
+
+
+    /**
+     * Read keytab entries until there is no remaining data
+     * in the buffer.
+     *
+     * @param buffer
+     * @return The keytab entries.
+     */
+    List<KeytabEntry> getKeytabEntries( ByteBuffer buffer )
+    {
+        List<KeytabEntry> entries = new ArrayList<KeytabEntry>();
+
+        while ( buffer.remaining() > 0 )
+        {
+            int size = buffer.getInt();
+            byte[] entry = new byte[size];
+
+            buffer.get( entry );
+            entries.add( getKeytabEntry( ByteBuffer.wrap( entry ) ) );
+        }
+
+        return entries;
+    }
+
+
+    /**
+     * Reads off a "keytab entry," which consists of a principal name,
+     * principal type, key version number, and key material.
+     */
+    private KeytabEntry getKeytabEntry( ByteBuffer buffer )
+    {
+        String principalName = getPrincipalName( buffer );
+
+        long principalType = buffer.getUnsignedInt();
+
+        long time = buffer.getUnsignedInt();
+        KerberosTime timeStamp = new KerberosTime( time * 1000 );
+
+        byte keyVersion = buffer.get();
+
+        EncryptionKey key = getKeyBlock( buffer, keyVersion );
+
+        return new KeytabEntry( principalName, principalType, timeStamp, keyVersion, key );
+    }
+
+
+    /**
+     * Reads off a principal name.
+     *
+     * @param buffer
+     * @return The principal name.
+     */
+    private String getPrincipalName( ByteBuffer buffer )
+    {
+        int count = buffer.getUnsignedShort();
+
+        // decrement for v1
+        String realm = getCountedString( buffer );
+
+        StringBuffer principalNameBuffer = new StringBuffer();
+
+        for ( int ii = 0; ii < count; ii++ )
+        {
+            String nameComponent = getCountedString( buffer );
+
+            principalNameBuffer.append( nameComponent );
+
+            if ( ii < count - 1 )
+            {
+                principalNameBuffer.append( "\\" );
+            }
+        }
+
+        principalNameBuffer.append( "@" + realm );
+
+        return principalNameBuffer.toString();
+    }
+
+
+    /**
+     * Read off a 16-bit encryption type and symmetric key material.
+     */
+    private EncryptionKey getKeyBlock( ByteBuffer buffer, int keyVersion )
+    {
+        int type = buffer.getUnsignedShort();
+        byte[] keyblock = getCountedBytes( buffer );
+
+        EncryptionType encryptionType = EncryptionType.getTypeByOrdinal( type );
+        EncryptionKey key = new EncryptionKey( encryptionType, keyblock );
+
+        return key;
+    }
+
+
+    /**
+     * Use a prefixed 16-bit length to read off a String.  Realm and name
+     * components are ASCII encoded text with no zero terminator.
+     */
+    private String getCountedString( ByteBuffer buffer )
+    {
+        int length = buffer.getUnsignedShort();
+        byte[] data = new byte[length];
+        buffer.get( data );
+
+        try
+        {
+            return new String( data, "ASCII" );
+        }
+        catch ( UnsupportedEncodingException uee )
+        {
+            // Should never happen for ASCII
+            return "";
+        }
+    }
+
+
+    /**
+     * Use a prefixed 16-bit length to read off raw bytes.
+     */
+    private byte[] getCountedBytes( ByteBuffer buffer )
+    {
+        int length = buffer.getUnsignedShort();
+        byte[] data = new byte[length];
+        buffer.get( data );
+
+        return data;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/keytab/KeytabEncoder.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/keytab/KeytabEncoder.java
new file mode 100644
index 0000000..cd22eca
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/keytab/KeytabEncoder.java
@@ -0,0 +1,168 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.keytab;
+
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+import org.apache.mina.common.ByteBuffer;
+
+
+/**
+ * Encode keytab fields into a {@link ByteBuffer}.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+class KeytabEncoder
+{
+    /**
+     * Write the keytab version and entries into a {@link ByteBuffer}.
+     *
+     * @param keytabVersion
+     * @param entries
+     * @return The ByteBuffer.
+     */
+    ByteBuffer write( byte[] keytabVersion, List<KeytabEntry> entries )
+    {
+        ByteBuffer buffer = ByteBuffer.allocate( 512 );
+        putKeytabVersion( buffer, keytabVersion );
+        putKeytabEntries( buffer, entries );
+        buffer.flip();
+
+        return buffer;
+    }
+
+
+    /**
+     * Encode the 16-bit file format version.  This
+     * keytab reader currently only support verision 5.2.
+     */
+    private void putKeytabVersion( ByteBuffer buffer, byte[] version )
+    {
+        buffer.put( version );
+    }
+
+
+    /**
+     * Encode the keytab entries.
+     *
+     * @param buffer
+     * @param entries
+     */
+    private void putKeytabEntries( ByteBuffer buffer, List<KeytabEntry> entries )
+    {
+        Iterator<KeytabEntry> iterator = entries.iterator();
+
+        while ( iterator.hasNext() )
+        {
+            ByteBuffer entryBuffer = putKeytabEntry( iterator.next() );
+            int size = entryBuffer.position();
+
+            entryBuffer.flip();
+
+            buffer.putInt( size );
+            buffer.put( entryBuffer );
+        }
+    }
+
+
+    /**
+     * Encode a "keytab entry," which consists of a principal name,
+     * principal type, key version number, and key material.
+     */
+    private ByteBuffer putKeytabEntry( KeytabEntry entry )
+    {
+        ByteBuffer buffer = ByteBuffer.allocate( 100 );
+
+        putPrincipalName( buffer, entry.getPrincipalName() );
+
+        buffer.putInt( ( int ) entry.getPrincipalType() );
+
+        buffer.putInt( ( int ) ( entry.getTimeStamp().getTime() / 1000 ) );
+
+        buffer.put( entry.getKeyVersion() );
+
+        putKeyBlock( buffer, entry.getKey() );
+
+        return buffer;
+    }
+
+
+    /**
+     * Encode a principal name.
+     *
+     * @param buffer
+     * @param principalName
+     */
+    private void putPrincipalName( ByteBuffer buffer, String principalName )
+    {
+        String[] split = principalName.split( "@" );
+        String nameComponent = split[0];
+        String realm = split[1];
+
+        String[] nameComponents = nameComponent.split( "/" );
+
+        // increment for v1
+        buffer.putShort( ( short ) nameComponents.length );
+
+        putCountedString( buffer, realm );
+        // write components
+
+        for ( int ii = 0; ii < nameComponents.length; ii++ )
+        {
+            putCountedString( buffer, nameComponents[ii] );
+        }
+    }
+
+
+    /**
+     * Encode a 16-bit encryption type and symmetric key material.
+     */
+    private void putKeyBlock( ByteBuffer buffer, EncryptionKey key )
+    {
+        buffer.putShort( ( short ) key.getKeyType().getOrdinal() );
+        putCountedBytes( buffer, key.getKeyValue() );
+    }
+
+
+    /**
+     * Use a prefixed 16-bit length to encode a String.  Realm and name
+     * components are ASCII encoded text with no zero terminator.
+     */
+    private void putCountedString( ByteBuffer buffer, String string )
+    {
+        byte[] data = string.getBytes();
+        buffer.putShort( ( short ) data.length );
+        buffer.put( data );
+    }
+
+
+    /**
+     * Use a prefixed 16-bit length to encode raw bytes.
+     */
+    private void putCountedBytes( ByteBuffer buffer, byte[] data )
+    {
+        buffer.putShort( ( short ) data.length );
+        buffer.put( data );
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/keytab/KeytabEntry.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/keytab/KeytabEntry.java
new file mode 100644
index 0000000..ec2cdb4
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/keytab/KeytabEntry.java
@@ -0,0 +1,109 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.keytab;
+
+
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
+
+
+/**
+ * An entry within a keytab file.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class KeytabEntry
+{
+    private String principalName;
+
+    private long principalType;
+
+    private KerberosTime timeStamp;
+
+    private byte keyVersion;
+
+    private EncryptionKey key;
+
+
+    /**
+     * Creates a new instance of Entry.
+     *
+     * @param principalName
+     * @param principalType
+     * @param timeStamp
+     * @param keyVersion
+     * @param key
+     */
+    public KeytabEntry( String principalName, long principalType, KerberosTime timeStamp, byte keyVersion,
+        EncryptionKey key )
+    {
+        this.principalName = principalName;
+        this.principalType = principalType;
+        this.timeStamp = timeStamp;
+        this.keyVersion = keyVersion;
+        this.key = key;
+    }
+
+
+    /**
+     * @return The key.
+     */
+    public EncryptionKey getKey()
+    {
+        return key;
+    }
+
+
+    /**
+     * @return The keyVersion.
+     */
+    public byte getKeyVersion()
+    {
+        return keyVersion;
+    }
+
+
+    /**
+     * @return The principalName.
+     */
+    public String getPrincipalName()
+    {
+        return principalName;
+    }
+
+
+    /**
+     * @return The principalType.
+     */
+    public long getPrincipalType()
+    {
+        return principalType;
+    }
+
+
+    /**
+     * @return The timeStamp.
+     */
+    public KerberosTime getTimeStamp()
+    {
+        return timeStamp;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/ApplicationRequest.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/ApplicationRequest.java
new file mode 100644
index 0000000..199384b
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/ApplicationRequest.java
@@ -0,0 +1,164 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages;
+
+
+import org.apache.directory.server.kerberos.shared.KerberosMessageType;
+import org.apache.directory.server.kerberos.shared.messages.components.Ticket;
+import org.apache.directory.server.kerberos.shared.messages.value.ApOptions;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptedData;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ApplicationRequest extends KerberosMessage
+{
+    private ApOptions apOptions;
+    private Ticket ticket;
+    private EncryptedData encPart; // Authenticator
+
+
+    /**
+     * Creates a new instance of ApplicationRequest.
+     */
+    public ApplicationRequest()
+    {
+        super( KerberosMessageType.AP_REQ );
+        // used by ASN1 decoder
+    }
+
+
+    /**
+     * Creates a new instance of ApplicationRequest.
+     *
+     * @param apOptions
+     * @param ticket
+     * @param encPart
+     */
+    public ApplicationRequest( ApOptions apOptions, Ticket ticket, EncryptedData encPart )
+    {
+        super( KerberosMessageType.AP_REQ );
+        this.apOptions = apOptions;
+        this.ticket = ticket;
+        this.encPart = encPart;
+    }
+
+
+    /**
+     * Returns the {@link ApOptions}.
+     *
+     * @return The {@link ApOptions}.
+     */
+    public ApOptions getApOptions()
+    {
+        return apOptions;
+    }
+
+
+    /**
+     * Returns the {@link Ticket}.
+     *
+     * @return The {@link Ticket}.
+     */
+    public Ticket getTicket()
+    {
+        return ticket;
+    }
+
+
+    /**
+     * Returns the option at a specified index.
+     *
+     * @param option
+     * @return The option.
+     */
+    public boolean getOption( int option )
+    {
+        return apOptions.get( option );
+    }
+
+
+    /**
+     * Sets the option at a specified index.
+     *
+     * @param option
+     */
+    public void setOption( int option )
+    {
+        apOptions.set( option );
+    }
+
+
+    /**
+     * Clears the option at a specified index.
+     *
+     * @param option
+     */
+    public void clearOption( int option )
+    {
+        apOptions.clear( option );
+    }
+
+
+    /**
+     * Returns the {@link EncryptedData}.
+     *
+     * @return The {@link EncryptedData}.
+     */
+    public EncryptedData getEncPart()
+    {
+        return encPart;
+    }
+
+
+    /**
+     * Sets the {@link EncryptedData}.
+     *
+     * @param data
+     */
+    public void setEncPart( EncryptedData data )
+    {
+        encPart = data;
+    }
+
+
+    /**
+     * Sets the {@link ApOptions}.
+     *
+     * @param options
+     */
+    public void setApOptions( ApOptions options )
+    {
+        apOptions = options;
+    }
+
+
+    /**
+     * Sets the {@link Ticket}.
+     *
+     * @param ticket
+     */
+    public void setTicket( Ticket ticket )
+    {
+        this.ticket = ticket;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/AuthenticationReply.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/AuthenticationReply.java
new file mode 100644
index 0000000..5fb6a69
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/AuthenticationReply.java
@@ -0,0 +1,59 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages;
+
+
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.apache.directory.server.kerberos.shared.KerberosMessageType;
+import org.apache.directory.server.kerberos.shared.messages.components.Ticket;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptedData;
+import org.apache.directory.server.kerberos.shared.messages.value.PaData;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class AuthenticationReply extends KdcReply
+{
+    /**
+     * Creates a new instance of AuthenticationReply.
+     */
+    public AuthenticationReply()
+    {
+        super( KerberosMessageType.AS_REP );
+    }
+
+
+    /**
+     * Creates a new instance of AuthenticationReply.
+     *
+     * @param paData
+     * @param clientPrincipal
+     * @param ticket
+     * @param encPart
+     */
+    public AuthenticationReply( PaData[] paData, KerberosPrincipal clientPrincipal, Ticket ticket,
+        EncryptedData encPart )
+    {
+        super( paData, clientPrincipal, ticket, encPart, KerberosMessageType.AS_REP );
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/Encodable.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/Encodable.java
new file mode 100644
index 0000000..76ce06f
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/Encodable.java
@@ -0,0 +1,33 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.kerberos.shared.messages;
+
+
+/**
+ * Marker interface for ASN.1 encodable objects.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public interface Encodable
+{
+    // Marker interface; no methods to implement.
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/ErrorMessage.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/ErrorMessage.java
new file mode 100644
index 0000000..21b98e2
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/ErrorMessage.java
@@ -0,0 +1,174 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages;
+
+
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.apache.directory.server.kerberos.shared.KerberosMessageType;
+import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ErrorMessage extends KerberosMessage
+{
+    private KerberosTime clientTime; //optional
+    private Integer clientMicroSecond; //optional
+    private KerberosTime serverTime;
+    private int serverMicroSecond;
+    private int errorCode;
+    private KerberosPrincipal clientPrincipal; //optional
+    private KerberosPrincipal serverPrincipal;
+    private String explanatoryText; //optional
+    private byte[] explanatoryData; //optional
+
+
+    /**
+     * Creates a new instance of ErrorMessage.
+     *
+     * @param clientTime
+     * @param clientMicroSecond
+     * @param serverTime
+     * @param serverMicroSecond
+     * @param errorCode
+     * @param clientPrincipal
+     * @param serverPrincipal
+     * @param explanatoryText
+     * @param explanatoryData
+     */
+    public ErrorMessage( KerberosTime clientTime, Integer clientMicroSecond, KerberosTime serverTime,
+        int serverMicroSecond, int errorCode, KerberosPrincipal clientPrincipal, KerberosPrincipal serverPrincipal,
+        String explanatoryText, byte[] explanatoryData )
+    {
+        super( KerberosMessageType.KRB_ERROR );
+
+        this.clientTime = clientTime;
+        this.clientMicroSecond = clientMicroSecond;
+        this.serverTime = serverTime;
+        this.serverMicroSecond = serverMicroSecond;
+        this.errorCode = errorCode;
+        this.clientPrincipal = clientPrincipal;
+        this.serverPrincipal = serverPrincipal;
+        this.explanatoryText = explanatoryText;
+        this.explanatoryData = explanatoryData;
+    }
+
+
+    /**
+     * Returns the client {@link KerberosPrincipal}.
+     *
+     * @return The client {@link KerberosPrincipal}.
+     */
+    public KerberosPrincipal getClientPrincipal()
+    {
+        return clientPrincipal;
+    }
+
+
+    /**
+     * Returns the client {@link KerberosTime}.
+     *
+     * @return The client {@link KerberosTime}.
+     */
+    public KerberosTime getClientTime()
+    {
+        return clientTime;
+    }
+
+
+    /**
+     * Returns the client microsecond.
+     *
+     * @return The client microsecond.
+     */
+    public Integer getClientMicroSecond()
+    {
+        return clientMicroSecond;
+    }
+
+
+    /**
+     * Returns the explanatory data.
+     *
+     * @return The explanatory data.
+     */
+    public byte[] getExplanatoryData()
+    {
+        return explanatoryData;
+    }
+
+
+    /**
+     * Returns the error code.
+     *
+     * @return The error code.
+     */
+    public int getErrorCode()
+    {
+        return errorCode;
+    }
+
+
+    /**
+     * Returns the explanatory text.
+     *
+     * @return The explanatory text.
+     */
+    public String getExplanatoryText()
+    {
+        return explanatoryText;
+    }
+
+
+    /**
+     * Returns the server {@link KerberosPrincipal}.
+     *
+     * @return The server {@link KerberosPrincipal}.
+     */
+    public KerberosPrincipal getServerPrincipal()
+    {
+        return serverPrincipal;
+    }
+
+
+    /**
+     * Returns the server {@link KerberosTime}.
+     *
+     * @return The server {@link KerberosTime}.
+     */
+    public KerberosTime getServerTime()
+    {
+        return serverTime;
+    }
+
+
+    /**
+     * Returns the server microsecond.
+     *
+     * @return The server microsecond.
+     */
+    public int getServerMicroSecond()
+    {
+        return serverMicroSecond;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/ErrorMessageModifier.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/ErrorMessageModifier.java
new file mode 100644
index 0000000..1b3fba6
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/ErrorMessageModifier.java
@@ -0,0 +1,154 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages;
+
+
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ErrorMessageModifier
+{
+    private KerberosTime clientTime; //optional
+    private Integer clientMicroSecond; //optional
+    private KerberosTime serverTime;
+    private int serverMicroSecond;
+    private int errorCode;
+    private KerberosPrincipal clientPrincipal; //optional
+    private KerberosPrincipal serverPrincipal;
+    private String explanatoryText; //optional
+    private byte[] explanatoryData; //optional
+
+
+    /**
+     * Returns the {@link ErrorMessage}.
+     *
+     * @return The {@link ErrorMessage}.
+     */
+    public ErrorMessage getErrorMessage()
+    {
+        return new ErrorMessage( clientTime, clientMicroSecond, serverTime, serverMicroSecond, errorCode,
+            clientPrincipal, serverPrincipal, explanatoryText, explanatoryData );
+    }
+
+
+    /**
+     * Sets the client {@link KerberosPrincipal}.
+     *
+     * @param principal
+     */
+    public void setClientPrincipal( KerberosPrincipal principal )
+    {
+        this.clientPrincipal = principal;
+    }
+
+
+    /**
+     * Sets the client {@link KerberosTime}.
+     *
+     * @param time
+     */
+    public void setClientTime( KerberosTime time )
+    {
+        this.clientTime = time;
+    }
+
+
+    /**
+     * Sets the client microsecond.
+     *
+     * @param clientMicroSecond
+     */
+    public void setClientMicroSecond( Integer clientMicroSecond )
+    {
+        this.clientMicroSecond = clientMicroSecond;
+    }
+
+
+    /**
+     * Sets the explanatory data.
+     *
+     * @param data
+     */
+    public void setExplanatoryData( byte[] data )
+    {
+        this.explanatoryData = data;
+    }
+
+
+    /**
+     * Sets the error code.
+     *
+     * @param code
+     */
+    public void setErrorCode( int code )
+    {
+        this.errorCode = code;
+    }
+
+
+    /**
+     * Sets the explanatory text.
+     *
+     * @param text
+     */
+    public void setExplanatoryText( String text )
+    {
+        this.explanatoryText = text;
+    }
+
+
+    /**
+     * Sets the server {@link KerberosPrincipal}.
+     *
+     * @param principal
+     */
+    public void setServerPrincipal( KerberosPrincipal principal )
+    {
+        this.serverPrincipal = principal;
+    }
+
+
+    /**
+     * Sets the server {@link KerberosTime}.
+     *
+     * @param time
+     */
+    public void setServerTime( KerberosTime time )
+    {
+        this.serverTime = time;
+    }
+
+
+    /**
+     * Sets the server microsecond.
+     *
+     * @param serverMicroSecond
+     */
+    public void setServerMicroSecond( int serverMicroSecond )
+    {
+        this.serverMicroSecond = serverMicroSecond;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/KdcReply.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/KdcReply.java
new file mode 100644
index 0000000..e7a7ae0
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/KdcReply.java
@@ -0,0 +1,447 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages;
+
+
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.apache.directory.server.kerberos.shared.KerberosMessageType;
+import org.apache.directory.server.kerberos.shared.messages.components.EncKdcRepPart;
+import org.apache.directory.server.kerberos.shared.messages.components.Ticket;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptedData;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+import org.apache.directory.server.kerberos.shared.messages.value.HostAddresses;
+import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
+import org.apache.directory.server.kerberos.shared.messages.value.LastRequest;
+import org.apache.directory.server.kerberos.shared.messages.value.PaData;
+import org.apache.directory.server.kerberos.shared.messages.value.flags.TicketFlags;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class KdcReply extends KerberosMessage implements Encodable
+{
+    private PaData[] paData; //optional
+    private KerberosPrincipal clientPrincipal;
+    private Ticket ticket;
+
+    private EncKdcRepPart encKDCRepPart = new EncKdcRepPart();
+    private EncryptedData encPart;
+
+
+    /**
+     * Creates a new instance of KdcReply.
+     *
+     * @param msgType
+     */
+    public KdcReply( KerberosMessageType msgType )
+    {
+        super( msgType );
+    }
+
+
+    /**
+     * Creates a new instance of KdcReply.
+     *
+     * @param paData
+     * @param clientPrincipal
+     * @param ticket
+     * @param encPart
+     * @param msgType
+     */
+    public KdcReply( PaData[] paData, KerberosPrincipal clientPrincipal, Ticket ticket,
+        EncryptedData encPart, KerberosMessageType msgType )
+    {
+        this( msgType );
+        this.paData = paData;
+        this.clientPrincipal = clientPrincipal;
+        this.ticket = ticket;
+        this.encPart = encPart;
+    }
+
+
+    /**
+     * Returns the client {@link KerberosPrincipal}.
+     *
+     * @return The client {@link KerberosPrincipal}.
+     */
+    public KerberosPrincipal getClientPrincipal()
+    {
+        return clientPrincipal;
+    }
+
+
+    /**
+     * Returns the client realm.
+     *
+     * @return The client realm.
+     */
+    public String getClientRealm()
+    {
+        return clientPrincipal.getRealm();
+    }
+
+
+    /**
+     * Returns the {@link EncryptedData}.
+     *
+     * @return The {@link EncryptedData}.
+     */
+    public EncryptedData getEncPart()
+    {
+        return encPart;
+    }
+
+
+    /**
+     * Returns an array of {@link PaData}s.
+     *
+     * @return The array of {@link PaData}s.
+     */
+    public PaData[] getPaData()
+    {
+        return paData;
+    }
+
+
+    /**
+     * Returns the {@link Ticket}.
+     *
+     * @return The {@link Ticket}.
+     */
+    public Ticket getTicket()
+    {
+        return ticket;
+    }
+
+
+    /**
+     * Sets the client {@link KerberosPrincipal}.
+     *
+     * @param clientPrincipal
+     */
+    public void setClientPrincipal( KerberosPrincipal clientPrincipal )
+    {
+        this.clientPrincipal = clientPrincipal;
+    }
+
+
+    /**
+     * Sets the {@link EncKdcRepPart}.
+     *
+     * @param repPart
+     */
+    public void setEncKDCRepPart( EncKdcRepPart repPart )
+    {
+        encKDCRepPart = repPart;
+    }
+
+
+    /**
+     * Sets the {@link EncryptedData}.
+     *
+     * @param part
+     */
+    public void setEncPart( EncryptedData part )
+    {
+        encPart = part;
+    }
+
+
+    /**
+     * Sets the array of {@link PaData}s.
+     *
+     * @param data
+     */
+    public void setPaData( PaData[] data )
+    {
+        paData = data;
+    }
+
+
+    /**
+     * Sets the {@link Ticket}.
+     *
+     * @param ticket
+     */
+    public void setTicket( Ticket ticket )
+    {
+        this.ticket = ticket;
+    }
+
+
+    // EncKdcRepPart delegate getters
+
+    /**
+     * Returns the auth {@link KerberosTime}.
+     *
+     * @return The auth {@link KerberosTime}.
+     */
+    public KerberosTime getAuthTime()
+    {
+        return encKDCRepPart.getAuthTime();
+    }
+
+
+    /**
+     * Returns the client {@link HostAddresses}.
+     *
+     * @return The client {@link HostAddresses}.
+     */
+    public HostAddresses getClientAddresses()
+    {
+        return encKDCRepPart.getClientAddresses();
+    }
+
+
+    /**
+     * Return the end {@link KerberosTime}.
+     *
+     * @return The end {@link KerberosTime}.
+     */
+    public KerberosTime getEndTime()
+    {
+        return encKDCRepPart.getEndTime();
+    }
+
+
+    /**
+     * Returns the {@link TicketFlags}.
+     *
+     * @return The {@link TicketFlags}.
+     */
+    public TicketFlags getFlags()
+    {
+        return encKDCRepPart.getFlags();
+    }
+
+
+    /**
+     * Returns the {@link EncryptionKey}.
+     *
+     * @return The {@link EncryptionKey}.
+     */
+    public EncryptionKey getKey()
+    {
+        return encKDCRepPart.getKey();
+    }
+
+
+    /**
+     * Returns the key expiration {@link KerberosTime}.
+     *
+     * @return The key expiration {@link KerberosTime}.
+     */
+    public KerberosTime getKeyExpiration()
+    {
+        return encKDCRepPart.getKeyExpiration();
+    }
+
+
+    /**
+     * Returns the {@link LastRequest}.
+     *
+     * @return The {@link LastRequest}.
+     */
+    public LastRequest getLastRequest()
+    {
+        return encKDCRepPart.getLastRequest();
+    }
+
+
+    /**
+     * Returns the nonce.
+     *
+     * @return The nonce.
+     */
+    public int getNonce()
+    {
+        return encKDCRepPart.getNonce();
+    }
+
+
+    /**
+     * Returns the renew till {@link KerberosTime}.
+     *
+     * @return The renew till {@link KerberosTime}.
+     */
+    public KerberosTime getRenewTill()
+    {
+        return encKDCRepPart.getRenewTill();
+    }
+
+
+    /**
+     * Returns the server {@link KerberosPrincipal}.
+     *
+     * @return The server {@link KerberosPrincipal}.
+     */
+    public KerberosPrincipal getServerPrincipal()
+    {
+        return encKDCRepPart.getServerPrincipal();
+    }
+
+
+    /**
+     * Return the server realm.
+     *
+     * @return The server realm.
+     */
+    public String getServerRealm()
+    {
+        return encKDCRepPart.getServerRealm();
+    }
+
+
+    /**
+     * Returns the start {@link KerberosTime}.
+     *
+     * @return The start {@link KerberosTime}.
+     */
+    public KerberosTime getStartTime()
+    {
+        return encKDCRepPart.getStartTime();
+    }
+
+
+    // EncKdcRepPart delegate setters
+
+    /**
+     * Sets the auth {@link KerberosTime}.
+     *
+     * @param time
+     */
+    public void setAuthTime( KerberosTime time )
+    {
+        encKDCRepPart.setAuthTime( time );
+    }
+
+
+    /**
+     * Sets the client {@link HostAddresses}.
+     *
+     * @param addresses
+     */
+    public void setClientAddresses( HostAddresses addresses )
+    {
+        encKDCRepPart.setClientAddresses( addresses );
+    }
+
+
+    /**
+     * Sets the end {@link KerberosTime}.
+     *
+     * @param time
+     */
+    public void setEndTime( KerberosTime time )
+    {
+        encKDCRepPart.setEndTime( time );
+    }
+
+
+    /**
+     * Sets the {@link TicketFlags}.
+     *
+     * @param flags
+     */
+    public void setFlags( TicketFlags flags )
+    {
+        encKDCRepPart.setFlags( flags );
+    }
+
+
+    /**
+     * Sets the {@link EncryptionKey}.
+     *
+     * @param key
+     */
+    public void setKey( EncryptionKey key )
+    {
+        encKDCRepPart.setKey( key );
+    }
+
+
+    /**
+     * Sets the key expiration {@link KerberosTime}.
+     *
+     * @param expiration
+     */
+    public void setKeyExpiration( KerberosTime expiration )
+    {
+        encKDCRepPart.setKeyExpiration( expiration );
+    }
+
+
+    /**
+     * Sets the {@link LastRequest}.
+     *
+     * @param request
+     */
+    public void setLastRequest( LastRequest request )
+    {
+        encKDCRepPart.setLastRequest( request );
+    }
+
+
+    /**
+     * Sets the nonce.
+     *
+     * @param nonce
+     */
+    public void setNonce( int nonce )
+    {
+        encKDCRepPart.setNonce( nonce );
+    }
+
+
+    /**
+     * Sets the renew till {@link KerberosTime}.
+     *
+     * @param till
+     */
+    public void setRenewTill( KerberosTime till )
+    {
+        encKDCRepPart.setRenewTill( till );
+    }
+
+
+    /**
+     * Sets the server {@link KerberosPrincipal}.
+     *
+     * @param principal
+     */
+    public void setServerPrincipal( KerberosPrincipal principal )
+    {
+        encKDCRepPart.setServerPrincipal( principal );
+    }
+
+
+    /**
+     * Sets the start {@link KerberosTime}.
+     *
+     * @param time
+     */
+    public void setStartTime( KerberosTime time )
+    {
+        encKDCRepPart.setStartTime( time );
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/KdcRequest.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/KdcRequest.java
new file mode 100644
index 0000000..4111af5
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/KdcRequest.java
@@ -0,0 +1,284 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages;
+
+
+import java.util.Set;
+
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.apache.directory.server.kerberos.shared.KerberosMessageType;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
+import org.apache.directory.server.kerberos.shared.messages.components.Ticket;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptedData;
+import org.apache.directory.server.kerberos.shared.messages.value.HostAddresses;
+import org.apache.directory.server.kerberos.shared.messages.value.KdcOptions;
+import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
+import org.apache.directory.server.kerberos.shared.messages.value.PaData;
+import org.apache.directory.server.kerberos.shared.messages.value.RequestBody;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class KdcRequest extends KerberosMessage
+{
+    private PaData[] preAuthData; //optional
+    private RequestBody requestBody;
+    private byte[] bodyBytes;
+
+
+    /**
+     * Creates a new instance of KdcRequest.
+     *
+     * @param pvno
+     * @param messageType
+     * @param preAuthData
+     * @param requestBody
+     */
+    public KdcRequest( int pvno, KerberosMessageType messageType, PaData[] preAuthData, RequestBody requestBody )
+    {
+        super( pvno, messageType );
+        this.preAuthData = preAuthData;
+        this.requestBody = requestBody;
+    }
+
+
+    /**
+     * Creates a new instance of KdcRequest.
+     *
+     * @param pvno
+     * @param messageType
+     * @param preAuthData
+     * @param requestBody
+     * @param bodyBytes
+     */
+    public KdcRequest( int pvno, KerberosMessageType messageType, PaData[] preAuthData, RequestBody requestBody,
+        byte[] bodyBytes )
+    {
+        this( pvno, messageType, preAuthData, requestBody );
+        this.bodyBytes = bodyBytes;
+    }
+
+
+    /**
+     * Returns an array of {@link PaData}s.
+     *
+     * @return The array of {@link PaData}s.
+     */
+    public PaData[] getPreAuthData()
+    {
+        return preAuthData;
+    }
+
+
+    /**
+     * Returns the request body.
+     * 
+     * @return The request body.
+     */
+    public RequestBody getRequestBody()
+    {
+        return requestBody;
+    }
+
+
+    /**
+     * Returns the bytes of the body.  This is used for verifying checksums in
+     * the Ticket-Granting Service (TGS).
+     *
+     * @return The bytes of the body.
+     */
+    public byte[] getBodyBytes()
+    {
+        return bodyBytes;
+    }
+
+
+    // RequestBody delegate methods
+
+    /**
+     * Returns additional {@link Ticket}s.
+     *
+     * @return The {@link Ticket}s.
+     */
+    public Ticket[] getAdditionalTickets()
+    {
+        return requestBody.getAdditionalTickets();
+    }
+
+
+    /**
+     * Returns the {@link HostAddresses}.
+     *
+     * @return The {@link HostAddresses}.
+     */
+    public HostAddresses getAddresses()
+    {
+        return requestBody.getAddresses();
+    }
+
+
+    /**
+     * Returns the client {@link KerberosPrincipal}.
+     *
+     * @return The client {@link KerberosPrincipal}.
+     */
+    public KerberosPrincipal getClientPrincipal()
+    {
+        return requestBody.getClientPrincipal();
+    }
+
+
+    /**
+     * Returns the realm of the server principal.
+     *
+     * @return The realm.
+     */
+    public String getRealm()
+    {
+        return requestBody.getServerPrincipal().getRealm();
+    }
+
+
+    /**
+     * Returns the {@link EncryptedData}.
+     *
+     * @return The {@link EncryptedData}.
+     */
+    public EncryptedData getEncAuthorizationData()
+    {
+        return requestBody.getEncAuthorizationData();
+    }
+
+
+    /**
+     * Returns an array of requested {@link EncryptionType}s.
+     *
+     * @return The array of {@link EncryptionType}s.
+     */
+    public Set<EncryptionType> getEType()
+    {
+        return requestBody.getEType();
+    }
+
+
+    /**
+     * Returns the from {@link KerberosTime}.
+     *
+     * @return The from {@link KerberosTime}.
+     */
+    public KerberosTime getFrom()
+    {
+        return requestBody.getFrom();
+    }
+
+
+    /**
+     * Returns the {@link KdcOptions}.
+     *
+     * @return The {@link KdcOptions}.
+     */
+    public KdcOptions getKdcOptions()
+    {
+        return requestBody.getKdcOptions();
+    }
+
+
+    /**
+     * Returns the nonce.
+     *
+     * @return The nonce.
+     */
+    public int getNonce()
+    {
+        return requestBody.getNonce();
+    }
+
+
+    /**
+     * Returns the "R" {@link KerberosTime}.
+     *
+     * @return The "R" {@link KerberosTime}.
+     */
+    public KerberosTime getRtime()
+    {
+        return requestBody.getRtime();
+    }
+
+
+    /**
+     * Returns the server {@link KerberosPrincipal}.
+     *
+     * @return The server {@link KerberosPrincipal}.
+     */
+    public KerberosPrincipal getServerPrincipal()
+    {
+        return requestBody.getServerPrincipal();
+    }
+
+
+    /**
+     * Returns the till {@link KerberosTime}.
+     *
+     * @return The till {@link KerberosTime}.
+     */
+    public KerberosTime getTill()
+    {
+        return requestBody.getTill();
+    }
+
+
+    // RequestBody KdcOptions delegate accesors
+
+    /**
+     * Returns the option at the specified index.
+     *
+     * @param option
+     * @return The option.
+     */
+    public boolean getOption( int option )
+    {
+        return requestBody.getKdcOptions().get( option );
+    }
+
+
+    /**
+     * Sets the option at the specified index.
+     *
+     * @param option
+     */
+    public void setOption( int option )
+    {
+        requestBody.getKdcOptions().set( option );
+    }
+
+
+    /**
+     * Clears the option at the specified index.
+     *
+     * @param option
+     */
+    public void clearOption( int option )
+    {
+        requestBody.getKdcOptions().clear( option );
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/KerberosMessage.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/KerberosMessage.java
new file mode 100644
index 0000000..d3ae91b
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/KerberosMessage.java
@@ -0,0 +1,107 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages;
+
+import org.apache.directory.server.kerberos.shared.KerberosConstants;
+import org.apache.directory.server.kerberos.shared.KerberosMessageType;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class KerberosMessage
+{
+    /**
+     * The Kerberos protocol version number (5).
+     */
+    public static final int PVNO = KerberosConstants.KERBEROS_V5;
+
+    private int protocolVersionNumber;
+    private KerberosMessageType messageType;
+
+
+    /**
+     * Creates a new instance of KerberosMessage.
+     *
+     * @param type
+     */
+    public KerberosMessage( KerberosMessageType type )
+    {
+        this( PVNO, type );
+    }
+
+
+    /**
+     * Creates a new instance of KerberosMessage.
+     *
+     * @param versionNumber
+     * @param type
+     */
+    public KerberosMessage( int versionNumber, KerberosMessageType type )
+    {
+        protocolVersionNumber = versionNumber;
+        messageType = type;
+    }
+
+
+    /**
+     * Returns the {@link org.apache.directory.server.kerberos.shared.KerberosMessageType}.
+     *
+     * @return The {@link org.apache.directory.server.kerberos.shared.KerberosMessageType}.
+     */
+    public KerberosMessageType getMessageType()
+    {
+        return messageType;
+    }
+
+
+    /**
+     * Sets the {@link org.apache.directory.server.kerberos.shared.KerberosMessageType}.
+     *
+     * @param type
+     */
+    public void setMessageType( KerberosMessageType type )
+    {
+        messageType = type;
+    }
+
+
+    /**
+     * Returns the protocol version number.
+     *
+     * @return The protocol version number.
+     */
+    public int getProtocolVersionNumber()
+    {
+        return protocolVersionNumber;
+    }
+
+
+    /**
+     * Sets the protocol version number.
+     *
+     * @param versionNumber
+     */
+    public void setProtocolVersionNumber( int versionNumber )
+    {
+        protocolVersionNumber = versionNumber;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/TicketGrantReply.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/TicketGrantReply.java
new file mode 100644
index 0000000..a3f8775
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/TicketGrantReply.java
@@ -0,0 +1,59 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages;
+
+
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.apache.directory.server.kerberos.shared.KerberosMessageType;
+import org.apache.directory.server.kerberos.shared.messages.components.Ticket;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptedData;
+import org.apache.directory.server.kerberos.shared.messages.value.PaData;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class TicketGrantReply extends KdcReply
+{
+    /**
+     * Creates a new instance of TicketGrantReply.
+     */
+    public TicketGrantReply()
+    {
+        super( KerberosMessageType.TGS_REP );
+    }
+
+
+    /**
+     * Creates a new instance of TicketGrantReply.
+     *
+     * @param pAData
+     * @param clientPrincipal
+     * @param ticket
+     * @param encPart
+     */
+    public TicketGrantReply( PaData[] pAData, KerberosPrincipal clientPrincipal, Ticket ticket,
+        EncryptedData encPart )
+    {
+        super( pAData, clientPrincipal, ticket, encPart, KerberosMessageType.TGS_REP );
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/application/ApplicationReply.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/application/ApplicationReply.java
new file mode 100644
index 0000000..bdfc609
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/application/ApplicationReply.java
@@ -0,0 +1,58 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.application;
+
+
+import org.apache.directory.server.kerberos.shared.KerberosMessageType;
+import org.apache.directory.server.kerberos.shared.messages.KerberosMessage;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptedData;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ApplicationReply extends KerberosMessage
+{
+    private EncryptedData encryptedPart;
+
+
+    /**
+     * Creates a new instance of ApplicationReply.
+     *
+     * @param encPart
+     */
+    public ApplicationReply( EncryptedData encPart )
+    {
+        super( KerberosMessageType.AP_REP );
+        encryptedPart = encPart;
+    }
+
+
+    /**
+     * Returns the {@link EncryptedData}.
+     *
+     * @return The {@link EncryptedData}.
+     */
+    public EncryptedData getEncPart()
+    {
+        return encryptedPart;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/application/CredentialMessage.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/application/CredentialMessage.java
new file mode 100644
index 0000000..1fe6f0c
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/application/CredentialMessage.java
@@ -0,0 +1,73 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.application;
+
+
+import org.apache.directory.server.kerberos.shared.KerberosMessageType;
+import org.apache.directory.server.kerberos.shared.messages.KerberosMessage;
+import org.apache.directory.server.kerberos.shared.messages.components.Ticket;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptedData;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class CredentialMessage extends KerberosMessage
+{
+    private EncryptedData encPart;
+    private Ticket[] tickets;
+
+
+    /**
+     * Creates a new instance of CredentialMessage.
+     *
+     * @param encPart
+     * @param tickets
+     */
+    public CredentialMessage( EncryptedData encPart, Ticket[] tickets )
+    {
+        super( KerberosMessageType.KRB_CRED );
+        this.encPart = encPart;
+        this.tickets = tickets;
+    }
+
+
+    /**
+     * Returns the {@link EncryptedData}.
+     *
+     * @return The {@link EncryptedData}.
+     */
+    public EncryptedData getEncPart()
+    {
+        return encPart;
+    }
+
+
+    /**
+     * Returns an array of {@link Ticket}s.
+     *
+     * @return The array of {@link Ticket}s.
+     */
+    public Ticket[] getTickets()
+    {
+        return tickets;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/application/PrivateMessage.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/application/PrivateMessage.java
new file mode 100644
index 0000000..7534845
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/application/PrivateMessage.java
@@ -0,0 +1,79 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.application;
+
+
+import org.apache.directory.server.kerberos.shared.KerberosMessageType;
+import org.apache.directory.server.kerberos.shared.messages.KerberosMessage;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptedData;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class PrivateMessage extends KerberosMessage
+{
+    private EncryptedData encryptedPart;
+
+
+    /**
+     * Creates a new instance of PrivateMessage.
+     */
+    public PrivateMessage()
+    {
+        super( KerberosMessageType.KRB_PRIV );
+        // used by ASN.1 decoder
+    }
+
+
+    /**
+     * Creates a new instance of PrivateMessage.
+     *
+     * @param encryptedPart
+     */
+    public PrivateMessage( EncryptedData encryptedPart )
+    {
+        super( KerberosMessageType.KRB_PRIV );
+        this.encryptedPart = encryptedPart;
+    }
+
+
+    /**
+     * Returns the {@link EncryptedData}.
+     *
+     * @return The {@link EncryptedData}.
+     */
+    public EncryptedData getEncryptedPart()
+    {
+        return encryptedPart;
+    }
+
+
+    /**
+     * Sets the {@link EncryptedData}.
+     *
+     * @param encryptedData
+     */
+    public void setEncryptedPart( EncryptedData encryptedData )
+    {
+        encryptedPart = encryptedData;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/application/SafeBody.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/application/SafeBody.java
new file mode 100644
index 0000000..39a5711
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/application/SafeBody.java
@@ -0,0 +1,127 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.application;
+
+
+import org.apache.directory.server.kerberos.shared.messages.value.HostAddress;
+import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class SafeBody
+{
+    private byte[] userData;
+    private KerberosTime timestamp; //optional
+    private Integer usec; //optional
+    private Integer seqNumber; //optional
+    private HostAddress sAddress; //optional
+    private HostAddress rAddress; //optional
+
+
+    /**
+     * Creates a new instance of SafeBody.
+     *
+     * @param userData
+     * @param timestamp
+     * @param usec
+     * @param seqNumber
+     * @param sAddress
+     * @param rAddress
+     */
+    public SafeBody( byte[] userData, KerberosTime timestamp, Integer usec, Integer seqNumber, HostAddress sAddress,
+        HostAddress rAddress )
+    {
+        this.userData = userData;
+        this.timestamp = timestamp;
+        this.usec = usec;
+        this.seqNumber = seqNumber;
+        this.sAddress = sAddress;
+        this.rAddress = rAddress;
+    }
+
+
+    /**
+     * Returns the "R" {@link HostAddress}.
+     *
+     * @return The "R" {@link HostAddress}.
+     */
+    public HostAddress getRAddress()
+    {
+        return rAddress;
+    }
+
+
+    /**
+     * Returns the "S" {@link HostAddress}.
+     *
+     * @return The "S" {@link HostAddress}.
+     */
+    public HostAddress getSAddress()
+    {
+        return sAddress;
+    }
+
+
+    /**
+     * Returns the sequence number.
+     *
+     * @return The sequence number.
+     */
+    public Integer getSeqNumber()
+    {
+        return seqNumber;
+    }
+
+
+    /**
+     * Returns the {@link KerberosTime} timestamp.
+     *
+     * @return The {@link KerberosTime} timestamp.
+     */
+    public KerberosTime getTimestamp()
+    {
+        return timestamp;
+    }
+
+
+    /**
+     * Returns the microsecond.
+     *
+     * @return The microsecond.
+     */
+    public Integer getUsec()
+    {
+        return usec;
+    }
+
+
+    /**
+     * Returns the user data.
+     *
+     * @return The user data.
+     */
+    public byte[] getUserData()
+    {
+        return userData;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/application/SafeMessage.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/application/SafeMessage.java
new file mode 100644
index 0000000..65ba1d4
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/application/SafeMessage.java
@@ -0,0 +1,131 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.application;
+
+
+import org.apache.directory.server.kerberos.shared.KerberosMessageType;
+import org.apache.directory.server.kerberos.shared.messages.KerberosMessage;
+import org.apache.directory.server.kerberos.shared.messages.value.Checksum;
+import org.apache.directory.server.kerberos.shared.messages.value.HostAddress;
+import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class SafeMessage extends KerberosMessage
+{
+    private SafeBody safeBody;
+    private Checksum cksum;
+
+
+    /**
+     * Creates a new instance of SafeMessage.
+     *
+     * @param safeBody
+     * @param cksum
+     */
+    public SafeMessage( SafeBody safeBody, Checksum cksum )
+    {
+        super( KerberosMessageType.KRB_SAFE );
+        this.safeBody = safeBody;
+        this.cksum = cksum;
+    }
+
+
+    /**
+     * Returns the {@link Checksum}.
+     *
+     * @return The {@link Checksum}.
+     */
+    public Checksum getCksum()
+    {
+        return cksum;
+    }
+
+
+    // SafeBody delegate methods
+
+    /**
+     * Returns the "R" {@link HostAddress}.
+     *
+     * @return The "R" {@link HostAddress}.
+     */
+    public HostAddress getRAddress()
+    {
+        return safeBody.getRAddress();
+    }
+
+
+    /**
+     * Returns the "S" {@link HostAddress}.
+     *
+     * @return The "S" {@link HostAddress}.
+     */
+    public HostAddress getSAddress()
+    {
+        return safeBody.getSAddress();
+    }
+
+
+    /**
+     * Returns the sequence number.
+     *
+     * @return The sequence number.
+     */
+    public Integer getSeqNumber()
+    {
+        return safeBody.getSeqNumber();
+    }
+
+
+    /**
+     * Returns the {@link KerberosTime} timestamp.
+     *
+     * @return The {@link KerberosTime} timestamp.
+     */
+    public KerberosTime getTimestamp()
+    {
+        return safeBody.getTimestamp();
+    }
+
+
+    /**
+     * Returns the microsecond.
+     *
+     * @return The microsecond.
+     */
+    public Integer getUsec()
+    {
+        return safeBody.getUsec();
+    }
+
+
+    /**
+     * Returns the user data.
+     *
+     * @return The user data.
+     */
+    public byte[] getUserData()
+    {
+        return safeBody.getUserData();
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/application/package-info.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/application/package-info.java
new file mode 100644
index 0000000..cfd3b24
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/application/package-info.java
@@ -0,0 +1,28 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+/**
+ * Provides message objects for Kerberos application messages.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+
+package org.apache.directory.server.kerberos.shared.messages.application;
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/components/Authenticator.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/components/Authenticator.java
new file mode 100644
index 0000000..a217bdc
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/components/Authenticator.java
@@ -0,0 +1,186 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.components;
+
+
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.apache.directory.server.kerberos.shared.KerberosConstants;
+import org.apache.directory.server.kerberos.shared.messages.Encodable;
+import org.apache.directory.server.kerberos.shared.messages.value.AuthorizationData;
+import org.apache.directory.server.kerberos.shared.messages.value.Checksum;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class Authenticator implements Encodable
+{
+    /**
+     * Constant for the authenticator version number.
+     */
+    public static final int AUTHENTICATOR_VNO = KerberosConstants.KERBEROS_V5;
+
+    private int versionNumber;
+    private KerberosPrincipal clientPrincipal;
+    private Checksum checksum;
+    private int clientMicroSecond;
+    private KerberosTime clientTime;
+    private EncryptionKey subSessionKey;
+    private int sequenceNumber;
+    private AuthorizationData authorizationData;
+
+
+    /**
+     * Creates a new instance of Authenticator.
+     *
+     * @param clientPrincipal
+     * @param checksum
+     * @param clientMicroSecond
+     * @param clientTime
+     * @param subSessionKey
+     * @param sequenceNumber
+     * @param authorizationData
+     */
+    public Authenticator( KerberosPrincipal clientPrincipal, Checksum checksum, int clientMicroSecond,
+        KerberosTime clientTime, EncryptionKey subSessionKey, int sequenceNumber, AuthorizationData authorizationData )
+    {
+        this( AUTHENTICATOR_VNO, clientPrincipal, checksum, clientMicroSecond, clientTime, subSessionKey,
+            sequenceNumber, authorizationData );
+    }
+
+
+    /**
+     * Creates a new instance of Authenticator.
+     *
+     * @param versionNumber
+     * @param clientPrincipal
+     * @param checksum
+     * @param clientMicroSecond
+     * @param clientTime
+     * @param subSessionKey
+     * @param sequenceNumber
+     * @param authorizationData
+     */
+    public Authenticator( int versionNumber, KerberosPrincipal clientPrincipal, Checksum checksum,
+        int clientMicroSecond, KerberosTime clientTime, EncryptionKey subSessionKey, int sequenceNumber,
+        AuthorizationData authorizationData )
+    {
+        this.versionNumber = versionNumber;
+        this.clientPrincipal = clientPrincipal;
+        this.checksum = checksum;
+        this.clientMicroSecond = clientMicroSecond;
+        this.clientTime = clientTime;
+        this.subSessionKey = subSessionKey;
+        this.sequenceNumber = sequenceNumber;
+        this.authorizationData = authorizationData;
+    }
+
+
+    /**
+     * Returns the client {@link KerberosPrincipal}.
+     *
+     * @return The client {@link KerberosPrincipal}.
+     */
+    public KerberosPrincipal getClientPrincipal()
+    {
+        return clientPrincipal;
+    }
+
+
+    /**
+     * Returns the client {@link KerberosTime}.
+     *
+     * @return The client {@link KerberosTime}.
+     */
+    public KerberosTime getClientTime()
+    {
+        return clientTime;
+    }
+
+
+    /**
+     * Returns the client microsecond.
+     *
+     * @return The client microsecond.
+     */
+    public int getClientMicroSecond()
+    {
+        return clientMicroSecond;
+    }
+
+
+    /**
+     * Returns the {@link AuthorizationData}.
+     *
+     * @return The {@link AuthorizationData}.
+     */
+    public AuthorizationData getAuthorizationData()
+    {
+        return authorizationData;
+    }
+
+
+    /**
+     * Returns the {@link Checksum}.
+     *
+     * @return The {@link Checksum}.
+     */
+    public Checksum getChecksum()
+    {
+        return checksum;
+    }
+
+
+    /**
+     * Returns the sequence number.
+     *
+     * @return The sequence number.
+     */
+    public int getSequenceNumber()
+    {
+        return sequenceNumber;
+    }
+
+
+    /**
+     * Returns the sub-session key.
+     *
+     * @return The sub-session key.
+     */
+    public EncryptionKey getSubSessionKey()
+    {
+        return subSessionKey;
+    }
+
+
+    /**
+     * Returns the version number of the {@link Authenticator}.
+     *
+     * @return The version number of the {@link Authenticator}.
+     */
+    public int getVersionNumber()
+    {
+        return versionNumber;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/components/AuthenticatorModifier.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/components/AuthenticatorModifier.java
new file mode 100644
index 0000000..025f3ab
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/components/AuthenticatorModifier.java
@@ -0,0 +1,175 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.components;
+
+
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.apache.directory.server.kerberos.shared.messages.value.AuthorizationData;
+import org.apache.directory.server.kerberos.shared.messages.value.Checksum;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+import org.apache.directory.server.kerberos.shared.messages.value.KerberosPrincipalModifier;
+import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
+import org.apache.directory.server.kerberos.shared.messages.value.PrincipalName;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class AuthenticatorModifier
+{
+    private int versionNumber;
+    private KerberosPrincipalModifier clientModifier = new KerberosPrincipalModifier();
+    private KerberosPrincipal clientPrincipal;
+    private Checksum checksum;
+    private int clientMicroSecond;
+    private KerberosTime clientTime;
+    private EncryptionKey subSessionKey;
+    private int sequenceNumber;
+    private AuthorizationData authorizationData;
+
+
+    /**
+     * Returns the {@link Authenticator}.
+     *
+     * @return The {@link Authenticator}.
+     */
+    public Authenticator getAuthenticator()
+    {
+        if ( clientPrincipal == null )
+        {
+            clientPrincipal = clientModifier.getKerberosPrincipal();
+        }
+
+        return new Authenticator( versionNumber, clientPrincipal, checksum, clientMicroSecond, clientTime,
+            subSessionKey, sequenceNumber, authorizationData );
+    }
+
+
+    /**
+     * Sets the version number.
+     *
+     * @param versionNumber
+     */
+    public void setVersionNumber( int versionNumber )
+    {
+        this.versionNumber = versionNumber;
+    }
+
+
+    /**
+     * Sets the client {@link PrincipalName}.
+     *
+     * @param name
+     */
+    public void setClientName( PrincipalName name )
+    {
+        clientModifier.setPrincipalName( name );
+    }
+
+
+    /**
+     * Sets the client realm.
+     *
+     * @param realm
+     */
+    public void setClientRealm( String realm )
+    {
+        clientModifier.setRealm( realm );
+    }
+
+
+    /**
+     * Sets the client {@link KerberosPrincipal}.
+     *
+     * @param clientPrincipal
+     */
+    public void setClientPrincipal( KerberosPrincipal clientPrincipal )
+    {
+        this.clientPrincipal = clientPrincipal;
+    }
+
+
+    /**
+     * Sets the {@link AuthorizationData}.
+     *
+     * @param data
+     */
+    public void setAuthorizationData( AuthorizationData data )
+    {
+        authorizationData = data;
+    }
+
+
+    /**
+     * Sets the {@link Checksum}.
+     *
+     * @param checksum
+     */
+    public void setChecksum( Checksum checksum )
+    {
+        this.checksum = checksum;
+    }
+
+
+    /**
+     * Sets the client microsecond.
+     *
+     * @param microSecond
+     */
+    public void setClientMicroSecond( int microSecond )
+    {
+        clientMicroSecond = microSecond;
+    }
+
+
+    /**
+     * Sets the client {@link KerberosTime}.
+     *
+     * @param time
+     */
+    public void setClientTime( KerberosTime time )
+    {
+        clientTime = time;
+    }
+
+
+    /**
+     * Sets the sequence number.
+     *
+     * @param number
+     */
+    public void setSequenceNumber( int number )
+    {
+        sequenceNumber = number;
+    }
+
+
+    /**
+     * Sets the sub-session {@link EncryptionKey}.
+     *
+     * @param sessionKey
+     */
+    public void setSubSessionKey( EncryptionKey sessionKey )
+    {
+        subSessionKey = sessionKey;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/components/EncApRepPart.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/components/EncApRepPart.java
new file mode 100644
index 0000000..5c5d328
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/components/EncApRepPart.java
@@ -0,0 +1,105 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.components;
+
+
+import org.apache.directory.server.kerberos.shared.KerberosMessageType;
+import org.apache.directory.server.kerberos.shared.messages.Encodable;
+import org.apache.directory.server.kerberos.shared.messages.KerberosMessage;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
+
+
+/**
+ * Encrypted part of the application response.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class EncApRepPart extends KerberosMessage implements Encodable
+{
+    private KerberosTime clientTime;
+    private int cusec;
+    private EncryptionKey subSessionKey; //optional
+    private Integer sequenceNumber; //optional
+
+
+    /**
+     * Creates a new instance of EncApRepPart.
+     *
+     * @param clientTime
+     * @param cusec
+     * @param subSessionKey
+     * @param sequenceNumber
+     */
+    public EncApRepPart( KerberosTime clientTime, int cusec, EncryptionKey subSessionKey, Integer sequenceNumber )
+    {
+        super( KerberosMessageType.ENC_AP_REP_PART );
+
+        this.clientTime = clientTime;
+        this.cusec = cusec;
+        this.subSessionKey = subSessionKey;
+        this.sequenceNumber = sequenceNumber;
+    }
+
+
+    /**
+     * Returns the client {@link KerberosTime}.
+     *
+     * @return The client {@link KerberosTime}.
+     */
+    public KerberosTime getClientTime()
+    {
+        return clientTime;
+    }
+
+
+    /**
+     * Returns the client microsecond.
+     *
+     * @return The client microsecond.
+     */
+    public int getClientMicroSecond()
+    {
+        return cusec;
+    }
+
+
+    /**
+     * Returns the sequence number.
+     *
+     * @return The sequence number.
+     */
+    public Integer getSequenceNumber()
+    {
+        return sequenceNumber;
+    }
+
+
+    /**
+     * Returns the sub-session {@link EncryptionKey}.
+     *
+     * @return The sub-session {@link EncryptionKey}.
+     */
+    public EncryptionKey getSubSessionKey()
+    {
+        return subSessionKey;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/components/EncApRepPartModifier.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/components/EncApRepPartModifier.java
new file mode 100644
index 0000000..9e0fbd6
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/components/EncApRepPartModifier.java
@@ -0,0 +1,92 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.components;
+
+
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class EncApRepPartModifier
+{
+    private KerberosTime clientTime;
+    private int cusec;
+    private EncryptionKey subSessionKey; //optional
+    private Integer sequenceNumber; //optional
+
+
+    /**
+     * Returns the {@link EncApRepPart}.
+     *
+     * @return The {@link EncApRepPart}.
+     */
+    public EncApRepPart getEncApRepPart()
+    {
+        return new EncApRepPart( clientTime, cusec, subSessionKey, sequenceNumber );
+    }
+
+
+    /**
+     * Sets the client {@link KerberosTime}.
+     *
+     * @param clientTime
+     */
+    public void setClientTime( KerberosTime clientTime )
+    {
+        this.clientTime = clientTime;
+    }
+
+
+    /**
+     * Sets the client microsecond.
+     *
+     * @param cusec
+     */
+    public void setClientMicroSecond( int cusec )
+    {
+        this.cusec = cusec;
+    }
+
+
+    /**
+     * Sets the sub-session {@link EncryptionKey}.
+     *
+     * @param subSessionKey
+     */
+    public void setSubSessionKey( EncryptionKey subSessionKey )
+    {
+        this.subSessionKey = subSessionKey;
+    }
+
+
+    /**
+     * Sets the sequence number.
+     *
+     * @param sequenceNumber
+     */
+    public void setSequenceNumber( Integer sequenceNumber )
+    {
+        this.sequenceNumber = sequenceNumber;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/components/EncAsRepPart.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/components/EncAsRepPart.java
new file mode 100644
index 0000000..6ba9e0b
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/components/EncAsRepPart.java
@@ -0,0 +1,62 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.components;
+
+
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+import org.apache.directory.server.kerberos.shared.messages.value.HostAddresses;
+import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
+import org.apache.directory.server.kerberos.shared.messages.value.LastRequest;
+import org.apache.directory.server.kerberos.shared.messages.value.flags.TicketFlags;
+
+
+/**
+ * Encrypted part of the authentication service response.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class EncAsRepPart extends EncKdcRepPart
+{
+    /**
+     * Creates a new instance of EncAsRepPart.
+     *
+     * @param key
+     * @param lastReq
+     * @param nonce
+     * @param keyExpiration
+     * @param flags
+     * @param authTime
+     * @param startTime
+     * @param endTime
+     * @param renewTill
+     * @param serverPrincipal
+     * @param caddr
+     */
+    public EncAsRepPart(EncryptionKey key, LastRequest lastReq, int nonce, KerberosTime keyExpiration,
+        TicketFlags flags, KerberosTime authTime, KerberosTime startTime, KerberosTime endTime, KerberosTime renewTill,
+        KerberosPrincipal serverPrincipal, HostAddresses caddr)
+    {
+        super( key, lastReq, nonce, keyExpiration, flags, authTime, startTime, endTime, renewTill, serverPrincipal,
+            caddr, MessageComponentType.KRB_ENC_AS_REP_PART );
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/components/EncKdcRepPart.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/components/EncKdcRepPart.java
new file mode 100644
index 0000000..6a9e27a
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/components/EncKdcRepPart.java
@@ -0,0 +1,361 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.components;
+
+
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.apache.directory.server.kerberos.shared.messages.Encodable;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+import org.apache.directory.server.kerberos.shared.messages.value.HostAddresses;
+import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
+import org.apache.directory.server.kerberos.shared.messages.value.LastRequest;
+import org.apache.directory.server.kerberos.shared.messages.value.flags.TicketFlags;
+
+
+/**
+ * Base class for encrypted parts of KDC responses.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class EncKdcRepPart implements Encodable
+{
+    private EncryptionKey key;
+    private LastRequest lastRequest;
+    private int nonce;
+    private KerberosTime keyExpiration; //optional
+    private TicketFlags flags = new TicketFlags();
+    private KerberosTime authTime;
+    private KerberosTime startTime; //optional
+    private KerberosTime endTime;
+    private KerberosTime renewTill; //optional
+    private KerberosPrincipal serverPrincipal;
+    private HostAddresses clientAddresses; //optional
+    private MessageComponentType componentType;
+
+
+    /**
+     * Creates a new instance of EncKdcRepPart.
+     */
+    public EncKdcRepPart()
+    {
+        // built up by setter during reply generation
+    }
+
+
+    /**
+     * Creates a new instance of EncKdcRepPart.
+     *
+     * @param key
+     * @param lastReq
+     * @param nonce
+     * @param keyExpiration
+     * @param flags
+     * @param authtime
+     * @param starttime
+     * @param endtime
+     * @param renewTill
+     * @param serverPrincipal
+     * @param caddr
+     * @param componentType
+     */
+    public EncKdcRepPart( EncryptionKey key, LastRequest lastReq, int nonce, KerberosTime keyExpiration,
+        TicketFlags flags, KerberosTime authtime, KerberosTime starttime, KerberosTime endtime, KerberosTime renewTill,
+        KerberosPrincipal serverPrincipal, HostAddresses caddr, MessageComponentType componentType )
+    {
+        this.key = key;
+        this.lastRequest = lastReq;
+        this.nonce = nonce;
+        this.keyExpiration = keyExpiration;
+        this.flags = flags;
+        this.authTime = authtime;
+        this.startTime = starttime;
+        this.endTime = endtime;
+        this.renewTill = renewTill;
+        this.serverPrincipal = serverPrincipal;
+        this.clientAddresses = caddr;
+        this.componentType = componentType;
+    }
+
+
+    /**
+     * Returns the auth {@link KerberosTime}.
+     *
+     * @return The auth {@link KerberosTime}.
+     */
+    public KerberosTime getAuthTime()
+    {
+        return authTime;
+    }
+
+
+    /**
+     * Returns the client {@link HostAddresses}.
+     *
+     * @return The client {@link HostAddresses}.
+     */
+    public HostAddresses getClientAddresses()
+    {
+        return clientAddresses;
+    }
+
+
+    /**
+     * Returns the end {@link KerberosTime}.
+     *
+     * @return The end {@link KerberosTime}.
+     */
+    public KerberosTime getEndTime()
+    {
+        return endTime;
+    }
+
+
+    /**
+     * Returns the {@link TicketFlags}.
+     *
+     * @return The {@link TicketFlags}.
+     */
+    public TicketFlags getFlags()
+    {
+        return flags;
+    }
+
+
+    /**
+     * Returns the {@link EncryptionKey}.
+     *
+     * @return The {@link EncryptionKey}.
+     */
+    public EncryptionKey getKey()
+    {
+        return key;
+    }
+
+
+    /**
+     * Returns the key expiration {@link KerberosTime}.
+     *
+     * @return The key expiration {@link KerberosTime}.
+     */
+    public KerberosTime getKeyExpiration()
+    {
+        return keyExpiration;
+    }
+
+
+    /**
+     * Returns the {@link LastRequest}.
+     *
+     * @return The {@link LastRequest}.
+     */
+    public LastRequest getLastRequest()
+    {
+        return lastRequest;
+    }
+
+
+    /**
+     * Returns the nonce.
+     *
+     * @return The nonce.
+     */
+    public int getNonce()
+    {
+        return nonce;
+    }
+
+
+    /**
+     * Returns the renew till {@link KerberosTime}.
+     *
+     * @return The renew till {@link KerberosTime}.
+     */
+    public KerberosTime getRenewTill()
+    {
+        return renewTill;
+    }
+
+
+    /**
+     * Returns the server {@link KerberosPrincipal}.
+     *
+     * @return The server {@link KerberosPrincipal}.
+     */
+    public KerberosPrincipal getServerPrincipal()
+    {
+        return serverPrincipal;
+    }
+
+
+    /**
+     * Returns the server realm.
+     *
+     * @return The server realm.
+     */
+    public String getServerRealm()
+    {
+        return serverPrincipal.getRealm();
+    }
+
+
+    /**
+     * Returns the start {@link KerberosTime}.
+     *
+     * @return The start {@link KerberosTime}.
+     */
+    public KerberosTime getStartTime()
+    {
+        return startTime;
+    }
+
+
+    /**
+     * Returns the {@link MessageComponentType}.
+     *
+     * @return The {@link MessageComponentType}.
+     */
+    public MessageComponentType getComponentType()
+    {
+        return componentType;
+    }
+
+
+    /**
+     * Sets the auth {@link KerberosTime}.
+     *
+     * @param time
+     */
+    public void setAuthTime( KerberosTime time )
+    {
+        authTime = time;
+    }
+
+
+    /**
+     * Sets the client {@link HostAddresses}.
+     *
+     * @param addresses
+     */
+    public void setClientAddresses( HostAddresses addresses )
+    {
+        clientAddresses = addresses;
+    }
+
+
+    /**
+     * Sets the end {@link KerberosTime}.
+     *
+     * @param time
+     */
+    public void setEndTime( KerberosTime time )
+    {
+        endTime = time;
+    }
+
+
+    /**
+     * Sets the {@link TicketFlags}.
+     *
+     * @param flags
+     */
+    public void setFlags( TicketFlags flags )
+    {
+        this.flags = flags;
+    }
+
+
+    /**
+     * Sets the {@link EncryptionKey}.
+     *
+     * @param key
+     */
+    public void setKey( EncryptionKey key )
+    {
+        this.key = key;
+    }
+
+
+    /**
+     * Sets the key expiration {@link KerberosTime}.
+     *
+     * @param expiration
+     */
+    public void setKeyExpiration( KerberosTime expiration )
+    {
+        keyExpiration = expiration;
+    }
+
+
+    /**
+     * Sets the {@link LastRequest}.
+     *
+     * @param request
+     */
+    public void setLastRequest( LastRequest request )
+    {
+        lastRequest = request;
+    }
+
+
+    /**
+     * Sets the nonce.
+     *
+     * @param nonce
+     */
+    public void setNonce( int nonce )
+    {
+        this.nonce = nonce;
+    }
+
+
+    /**
+     * Sets the renew till {@link KerberosTime}.
+     *
+     * @param till
+     */
+    public void setRenewTill( KerberosTime till )
+    {
+        renewTill = till;
+    }
+
+
+    /**
+     * Sets the server {@link KerberosPrincipal}.
+     *
+     * @param principal
+     */
+    public void setServerPrincipal( KerberosPrincipal principal )
+    {
+        serverPrincipal = principal;
+    }
+
+
+    /**
+     * Sets the start {@link KerberosTime}.
+     *
+     * @param time
+     */
+    public void setStartTime( KerberosTime time )
+    {
+        startTime = time;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/components/EncKrbCredPart.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/components/EncKrbCredPart.java
new file mode 100644
index 0000000..cdeae48
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/components/EncKrbCredPart.java
@@ -0,0 +1,131 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.components;
+
+
+import org.apache.directory.server.kerberos.shared.messages.value.HostAddress;
+import org.apache.directory.server.kerberos.shared.messages.value.HostAddresses;
+import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
+import org.apache.directory.server.kerberos.shared.messages.value.KrbCredInfo;
+
+
+/**
+ * Encrypted part of credential message types.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class EncKrbCredPart
+{
+    private KrbCredInfo[] ticketInfo;
+    private Integer nonce; //optional
+    private KerberosTime timeStamp; //optional
+    private Integer usec; //optional
+    private HostAddress sAddress; //optional
+    private HostAddresses rAddress; //optional
+
+
+    /**
+     * Creates a new instance of EncKrbCredPart.
+     *
+     * @param ticketInfo
+     * @param timeStamp
+     * @param usec
+     * @param nonce
+     * @param sAddress
+     * @param rAddress
+     */
+    public EncKrbCredPart( KrbCredInfo[] ticketInfo, KerberosTime timeStamp, Integer usec, Integer nonce,
+        HostAddress sAddress, HostAddresses rAddress )
+    {
+        this.ticketInfo = ticketInfo;
+        this.nonce = nonce;
+        this.timeStamp = timeStamp;
+        this.usec = usec;
+        this.sAddress = sAddress;
+        this.rAddress = rAddress;
+    }
+
+
+    /**
+     * Returns the nonce.
+     * 
+     * @return The nonce.
+     */
+    public Integer getNonce()
+    {
+        return nonce;
+    }
+
+
+    /**
+     * Returns the "R" {@link HostAddresses}.
+     * 
+     * @return The "R" {@link HostAddresses}.
+     */
+    public HostAddresses getRAddress()
+    {
+        return rAddress;
+    }
+
+
+    /**
+     * Returns the "S" {@link HostAddresses}.
+     * 
+     * @return The "S" {@link HostAddresses}.
+     */
+    public HostAddress getSAddress()
+    {
+        return sAddress;
+    }
+
+
+    /**
+     * Returns the {@link KrbCredInfo}s.
+     * 
+     * @return The {@link KrbCredInfo}s.
+     */
+    public KrbCredInfo[] getTicketInfo()
+    {
+        return ticketInfo;
+    }
+
+
+    /**
+     * Returns the timestamp.
+     * 
+     * @return The timeStamp.
+     */
+    public KerberosTime getTimeStamp()
+    {
+        return timeStamp;
+    }
+
+
+    /**
+     * Returns the microseconds.
+     * 
+     * @return The microseconds.
+     */
+    public Integer getUsec()
+    {
+        return usec;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/components/EncKrbPrivPart.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/components/EncKrbPrivPart.java
new file mode 100644
index 0000000..e037ce6
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/components/EncKrbPrivPart.java
@@ -0,0 +1,134 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.components;
+
+
+import org.apache.directory.server.kerberos.shared.KerberosMessageType;
+import org.apache.directory.server.kerberos.shared.messages.Encodable;
+import org.apache.directory.server.kerberos.shared.messages.KerberosMessage;
+import org.apache.directory.server.kerberos.shared.messages.value.HostAddress;
+import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
+
+
+/**
+ * Encrypted part of private messages.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class EncKrbPrivPart extends KerberosMessage implements Encodable
+{
+    private byte[] userData;
+    private KerberosTime timestamp; //optional
+    private Integer usec; //optional
+    private Integer sequenceNumber; //optional
+    private HostAddress senderAddress; //optional
+    private HostAddress recipientAddress; //optional
+
+
+    /**
+     * Creates a new instance of EncKrbPrivPart.
+     *
+     * @param userData
+     * @param timestamp
+     * @param usec
+     * @param sequenceNumber
+     * @param senderAddress
+     * @param recipientAddress
+     */
+    public EncKrbPrivPart( byte[] userData, KerberosTime timestamp, Integer usec, Integer sequenceNumber,
+        HostAddress senderAddress, HostAddress recipientAddress )
+    {
+        super( KerberosMessageType.ENC_PRIV_PART );
+
+        this.userData = userData;
+        this.timestamp = timestamp;
+        this.usec = usec;
+        this.sequenceNumber = sequenceNumber;
+        this.senderAddress = senderAddress;
+        this.recipientAddress = recipientAddress;
+    }
+
+
+    /**
+     * Returns the recipient {@link HostAddress}.
+     *
+     * @return The recipient {@link HostAddress}.
+     */
+    public HostAddress getRecipientAddress()
+    {
+        return recipientAddress;
+    }
+
+
+    /**
+     * Returns the sender {@link HostAddress}.
+     *
+     * @return The sender {@link HostAddress}.
+     */
+    public HostAddress getSenderAddress()
+    {
+        return senderAddress;
+    }
+
+
+    /**
+     * Returns the sequence number.
+     *
+     * @return The sequence number.
+     */
+    public Integer getSequenceNumber()
+    {
+        return sequenceNumber;
+    }
+
+
+    /**
+     * Returns the {@link KerberosTime} timestamp.
+     *
+     * @return The {@link KerberosTime} timestamp.
+     */
+    public KerberosTime getTimestamp()
+    {
+        return timestamp;
+    }
+
+
+    /**
+     * Returns the microsecond.
+     *
+     * @return The microsecond.
+     */
+    public Integer getMicroSecond()
+    {
+        return usec;
+    }
+
+
+    /**
+     * Returns the user data.
+     *
+     * @return The user data.
+     */
+    public byte[] getUserData()
+    {
+        return userData;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/components/EncKrbPrivPartModifier.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/components/EncKrbPrivPartModifier.java
new file mode 100644
index 0000000..57e7611
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/components/EncKrbPrivPartModifier.java
@@ -0,0 +1,116 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.components;
+
+
+import org.apache.directory.server.kerberos.shared.messages.value.HostAddress;
+import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class EncKrbPrivPartModifier
+{
+    private byte[] userData;
+    private KerberosTime timestamp; //optional
+    private Integer usec; //optional
+    private Integer sequenceNumber; //optional
+    private HostAddress senderAddress; //optional
+    private HostAddress recipientAddress; //optional
+
+
+    /**
+     * Returns the {@link EncKrbPrivPart}.
+     *
+     * @return The {@link EncKrbPrivPart}.
+     */
+    public EncKrbPrivPart getEncKrbPrivPart()
+    {
+        return new EncKrbPrivPart( userData, timestamp, usec, sequenceNumber, senderAddress, recipientAddress );
+    }
+
+
+    /**
+     * Sets the recipient {@link HostAddress}.
+     *
+     * @param address
+     */
+    public void setRecipientAddress( HostAddress address )
+    {
+        recipientAddress = address;
+    }
+
+
+    /**
+     * Sets the sender {@link HostAddress}.
+     *
+     * @param address
+     */
+    public void setSenderAddress( HostAddress address )
+    {
+        senderAddress = address;
+    }
+
+
+    /**
+     * Sets the sequence number.
+     *
+     * @param number
+     */
+    public void setSequenceNumber( Integer number )
+    {
+        sequenceNumber = number;
+    }
+
+
+    /**
+     * Sets the {@link KerberosTime} timestamp.
+     *
+     * @param timestamp
+     */
+    public void setTimestamp( KerberosTime timestamp )
+    {
+        this.timestamp = timestamp;
+    }
+
+
+    /**
+     * Sets the microsecond.
+     *
+     * @param usec
+     */
+    public void setMicroSecond( Integer usec )
+    {
+        this.usec = usec;
+    }
+
+
+    /**
+     * Sets the user data.
+     *
+     * @param data
+     */
+    public void setUserData( byte[] data )
+    {
+        userData = data;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/components/EncTgsRepPart.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/components/EncTgsRepPart.java
new file mode 100644
index 0000000..0838eb1
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/components/EncTgsRepPart.java
@@ -0,0 +1,62 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.components;
+
+
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+import org.apache.directory.server.kerberos.shared.messages.value.HostAddresses;
+import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
+import org.apache.directory.server.kerberos.shared.messages.value.LastRequest;
+import org.apache.directory.server.kerberos.shared.messages.value.flags.TicketFlags;
+
+
+/**
+ * Encrypted part of TGS responses.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class EncTgsRepPart extends EncKdcRepPart
+{
+    /**
+     * Creates a new instance of EncTgsRepPart.
+     *
+     * @param key
+     * @param lastReq
+     * @param nonce
+     * @param keyExpiration
+     * @param flags
+     * @param authtime
+     * @param starttime
+     * @param endtime
+     * @param renewTill
+     * @param serverPrincipal
+     * @param caddr
+     */
+    public EncTgsRepPart(EncryptionKey key, LastRequest lastReq, int nonce, KerberosTime keyExpiration,
+        TicketFlags flags, KerberosTime authtime, KerberosTime starttime, KerberosTime endtime, KerberosTime renewTill,
+        KerberosPrincipal serverPrincipal, HostAddresses caddr)
+    {
+        super( key, lastReq, nonce, keyExpiration, flags, authtime, starttime, endtime, renewTill, serverPrincipal,
+            caddr, MessageComponentType.KRB_ENC_TGS_REP_PART );
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/components/EncTicketPart.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/components/EncTicketPart.java
new file mode 100644
index 0000000..8ce2c69
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/components/EncTicketPart.java
@@ -0,0 +1,204 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.components;
+
+
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.apache.directory.server.kerberos.shared.messages.Encodable;
+import org.apache.directory.server.kerberos.shared.messages.value.AuthorizationData;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+import org.apache.directory.server.kerberos.shared.messages.value.HostAddresses;
+import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
+import org.apache.directory.server.kerberos.shared.messages.value.TransitedEncoding;
+import org.apache.directory.server.kerberos.shared.messages.value.flags.TicketFlags;
+
+
+/**
+ * Encrypted part of Tickets.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class EncTicketPart implements Encodable
+{
+    private TicketFlags flags;
+    private EncryptionKey sessionKey;
+    private KerberosPrincipal clientPrincipal;
+    private TransitedEncoding transitedEncoding;
+    private KerberosTime authtime;
+    private KerberosTime startTime; //optional
+    private KerberosTime endTime;
+    private KerberosTime renewTill; //optional
+    private HostAddresses clientAddresses; //optional
+    private AuthorizationData authorizationData; //optional
+
+
+    /**
+     * Creates a new instance of EncTicketPart.
+     *
+     * @param flags
+     * @param key
+     * @param clientPrincipal
+     * @param transited
+     * @param authtime
+     * @param starttime
+     * @param endtime
+     * @param renewTill
+     * @param caddr
+     * @param authorizationData
+     */
+    public EncTicketPart( TicketFlags flags, EncryptionKey key, KerberosPrincipal clientPrincipal,
+        TransitedEncoding transited, KerberosTime authtime, KerberosTime starttime, KerberosTime endtime,
+        KerberosTime renewTill, HostAddresses caddr, AuthorizationData authorizationData )
+    {
+        this.flags = flags;
+        this.sessionKey = key;
+        this.clientPrincipal = clientPrincipal;
+        this.transitedEncoding = transited;
+        this.authtime = authtime;
+        this.startTime = starttime;
+        this.endTime = endtime;
+        this.renewTill = renewTill;
+        this.clientAddresses = caddr;
+        this.authorizationData = authorizationData;
+    }
+
+
+    /**
+     * Returns the {@link AuthorizationData}.
+     *
+     * @return The {@link AuthorizationData}.
+     */
+    public AuthorizationData getAuthorizationData()
+    {
+        return authorizationData;
+    }
+
+
+    /**
+     * Returns the auth {@link KerberosTime}
+     *
+     * @return The auth {@link KerberosTime}
+     */
+    public KerberosTime getAuthTime()
+    {
+        return authtime;
+    }
+
+
+    /**
+     * Returns the client {@link HostAddresses}.
+     *
+     * @return The client {@link HostAddresses}.
+     */
+    public HostAddresses getClientAddresses()
+    {
+        return clientAddresses;
+    }
+
+
+    /**
+     * Returns the client {@link KerberosPrincipal}.
+     *
+     * @return The client {@link KerberosPrincipal}.
+     */
+    public KerberosPrincipal getClientPrincipal()
+    {
+        return clientPrincipal;
+    }
+
+
+    /**
+     * Returns the client realm.
+     *
+     * @return The client realm.
+     */
+    public String getClientRealm()
+    {
+        return clientPrincipal.getRealm();
+    }
+
+
+    /**
+     * Returns the end {@link KerberosTime}
+     *
+     * @return The end {@link KerberosTime}
+     */
+    public KerberosTime getEndTime()
+    {
+        return endTime;
+    }
+
+
+    /**
+     * Returns the {@link TicketFlags}.
+     *
+     * @return The {@link TicketFlags}.
+     */
+    public TicketFlags getFlags()
+    {
+        return flags;
+    }
+
+
+    /**
+     * Returns the session {@link EncryptionKey}.
+     *
+     * @return The session {@link EncryptionKey}.
+     */
+    public EncryptionKey getSessionKey()
+    {
+        return sessionKey;
+    }
+
+
+    /**
+     * Returns the renew till {@link KerberosTime}
+     *
+     * @return The renew till {@link KerberosTime}
+     */
+    public KerberosTime getRenewTill()
+    {
+        return renewTill;
+    }
+
+
+    /**
+     * Returns the start {@link KerberosTime}
+     *
+     * @return The start {@link KerberosTime}
+     */
+    public KerberosTime getStartTime()
+    {
+        return startTime;
+    }
+
+
+    /**
+     * Returns the {@link TransitedEncoding}.
+     *
+     * @return The {@link TransitedEncoding}.
+     */
+    public TransitedEncoding getTransitedEncoding()
+    {
+        return transitedEncoding;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/components/EncTicketPartModifier.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/components/EncTicketPartModifier.java
new file mode 100644
index 0000000..3a1a638
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/components/EncTicketPartModifier.java
@@ -0,0 +1,248 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.components;
+
+
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.apache.directory.server.kerberos.shared.messages.value.AuthorizationData;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+import org.apache.directory.server.kerberos.shared.messages.value.HostAddresses;
+import org.apache.directory.server.kerberos.shared.messages.value.KerberosPrincipalModifier;
+import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
+import org.apache.directory.server.kerberos.shared.messages.value.PrincipalName;
+import org.apache.directory.server.kerberos.shared.messages.value.TransitedEncoding;
+import org.apache.directory.server.kerberos.shared.messages.value.flags.KerberosFlag;
+import org.apache.directory.server.kerberos.shared.messages.value.flags.TicketFlags;
+
+
+/**
+ * Encrypted part of Tickets.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class EncTicketPartModifier
+{
+    private TicketFlags flags = new TicketFlags();
+    private EncryptionKey sessionKey;
+    private KerberosPrincipalModifier modifier = new KerberosPrincipalModifier();
+    private KerberosPrincipal clientPrincipal;
+    private TransitedEncoding transitedEncoding;
+    private KerberosTime authTime;
+    private KerberosTime startTime; //optional
+    private KerberosTime endTime;
+    private KerberosTime renewTill; //optional
+    private HostAddresses clientAddresses; //optional
+    private AuthorizationData authorizationData; //optional
+
+
+    /**
+     * Returns the {@link EncTicketPart}.
+     *
+     * @return The {@link EncTicketPart}.
+     */
+    public EncTicketPart getEncTicketPart()
+    {
+        if ( clientPrincipal == null )
+        {
+            clientPrincipal = modifier.getKerberosPrincipal();
+        }
+
+        return new EncTicketPart( flags, sessionKey, clientPrincipal, transitedEncoding, authTime, startTime, endTime,
+            renewTill, clientAddresses, authorizationData );
+    }
+
+
+    /**
+     * Sets the client {@link PrincipalName}.
+     *
+     * @param name
+     */
+    public void setClientName( PrincipalName name )
+    {
+        modifier.setPrincipalName( name );
+    }
+
+
+    /**
+     * Sets the client realm.
+     *
+     * @param realm
+     */
+    public void setClientRealm( String realm )
+    {
+        modifier.setRealm( realm );
+    }
+
+
+    /**
+     * Sets the client {@link KerberosPrincipal}.
+     *
+     * @param clientPrincipal
+     */
+    public void setClientPrincipal( KerberosPrincipal clientPrincipal )
+    {
+        this.clientPrincipal = clientPrincipal;
+    }
+
+
+    /**
+     * Sets the {@link AuthorizationData}.
+     *
+     * @param data
+     */
+    public void setAuthorizationData( AuthorizationData data )
+    {
+        authorizationData = data;
+    }
+
+
+    /**
+     * Sets the auth {@link KerberosTime}.
+     *
+     * @param authtime
+     */
+    public void setAuthTime( KerberosTime authtime )
+    {
+        authTime = authtime;
+    }
+
+
+    /**
+     * Sets the client {@link HostAddresses}.
+     *
+     * @param addresses
+     */
+    public void setClientAddresses( HostAddresses addresses )
+    {
+        clientAddresses = addresses;
+    }
+
+
+    /**
+     * Sets the end {@link KerberosTime}.
+     *
+     * @param time
+     */
+    public void setEndTime( KerberosTime time )
+    {
+        endTime = time;
+    }
+
+
+    /**
+     * Sets the {@link TicketFlags}.
+     *
+     * @param flags
+     */
+    public void setFlags( TicketFlags flags )
+    {
+        this.flags = flags;
+    }
+
+
+    /**
+     * Sets the flag at the given index.
+     *
+     * @param flag
+     */
+    public void setFlag( int flag )
+    {
+        flags.setFlag( flag );
+    }
+
+
+    /**
+     * Sets the flag at the given index.
+     *
+     * @param flag
+     */
+    public void setFlag( KerberosFlag flag )
+    {
+        flags.setFlag( flag );
+    }
+
+
+    /**
+     * Clears the flag at the given index.
+     *
+     * @param flag
+     */
+    public void clearFlag( int flag )
+    {
+        flags.clearFlag( flag );
+    }
+
+
+    /**
+     * Clears the flag at the given index.
+     *
+     * @param flag
+     */
+    public void clearFlag( KerberosFlag flag )
+    {
+        flags.clearFlag( flag );
+    }
+
+
+    /**
+     * Sets the renew till {@link KerberosTime}.
+     *
+     * @param till
+     */
+    public void setRenewTill( KerberosTime till )
+    {
+        renewTill = till;
+    }
+
+
+    /**
+     * Sets the sesson {@link EncryptionKey}.
+     *
+     * @param key
+     */
+    public void setSessionKey( EncryptionKey key )
+    {
+        sessionKey = key;
+    }
+
+
+    /**
+     * Sets the start {@link KerberosTime}.
+     *
+     * @param time
+     */
+    public void setStartTime( KerberosTime time )
+    {
+        startTime = time;
+    }
+
+
+    /**
+     * Sets the {@link TransitedEncoding}.
+     *
+     * @param encoding
+     */
+    public void setTransitedEncoding( TransitedEncoding encoding )
+    {
+        transitedEncoding = encoding;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/components/InvalidTicketException.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/components/InvalidTicketException.java
new file mode 100644
index 0000000..740dc2d
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/components/InvalidTicketException.java
@@ -0,0 +1,45 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.components;
+
+import org.apache.directory.server.kerberos.shared.exceptions.ErrorType;
+import org.apache.directory.server.kerberos.shared.exceptions.KerberosException;
+
+/**
+ * A exception used when there was an error while creating a Ticket
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */ 
+public class InvalidTicketException extends KerberosException
+{
+    static final long serialVersionUID = 1L;
+
+
+    public InvalidTicketException( ErrorType errorType )
+    {
+        super( errorType );
+    }
+
+
+    public InvalidTicketException( ErrorType errorType, String explanation )
+    {
+        super( errorType, explanation );
+    }
+
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/components/MessageComponentType.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/components/MessageComponentType.java
new file mode 100644
index 0000000..3d767a2
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/components/MessageComponentType.java
@@ -0,0 +1,160 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.components;
+
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+
+/**
+ * Type-safe enumerator for message component types.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class MessageComponentType implements Comparable<MessageComponentType>
+{
+    /**
+     * Constant for the "null" message component type.
+     */
+    public static final MessageComponentType NULL = new MessageComponentType( 0, "null" );
+
+    /**
+     * Constant for the "ticket" message component type.
+     */
+    public static final MessageComponentType KRB_TKT = new MessageComponentType( 1, "ticket" );
+
+    /**
+     * Constant for the "authenticator" message component type.
+     */
+    public static final MessageComponentType KRB_AUTHENTICATOR = new MessageComponentType( 2, "authenticator" );
+
+    /**
+     * Constant for the "encrypted ticket part" message component type.
+     */
+    public static final MessageComponentType KRB_ENC_TKT_PART = new MessageComponentType( 3, "encrypted ticket part" );
+
+    /**
+     * Constant for the "encrypted initial authentication part" message component type.
+     */
+    public static final MessageComponentType KRB_ENC_AS_REP_PART = new MessageComponentType( 25,
+        "encrypted initial authentication part" );
+
+    /**
+     * Constant for the "encrypted TGS request part" message component type.
+     */
+    public static final MessageComponentType KRB_ENC_TGS_REP_PART = new MessageComponentType( 26,
+        "encrypted TGS request part" );
+
+    /**
+     * Constant for the "encrypted application request part" message component type.
+     */
+    public static final MessageComponentType KRB_ENC_AP_REP_PART = new MessageComponentType( 27,
+        "encrypted application request part" );
+
+    /**
+     * Constant for the "encrypted application message part" message component type.
+     */
+    public static final MessageComponentType KRB_ENC_KRB_PRIV_PART = new MessageComponentType( 28,
+        "encrypted application message part" );
+
+    /**
+     * Constant for the "encrypted credentials forward part" message component type.
+     */
+    public static final MessageComponentType KRB_ENC_KRB_CRED_PART = new MessageComponentType( 29,
+        "encrypted credentials forward part" );
+
+    /**
+     * Array for building a List of VALUES.
+     */
+    private static final MessageComponentType[] values =
+        { NULL, KRB_TKT, KRB_AUTHENTICATOR, KRB_ENC_TKT_PART, KRB_ENC_AS_REP_PART, KRB_ENC_TGS_REP_PART,
+            KRB_ENC_AP_REP_PART, KRB_ENC_KRB_PRIV_PART, KRB_ENC_KRB_CRED_PART };
+
+    /**
+     * A List of all the message component type constants.
+     */
+    public static final List<MessageComponentType> VALUES = Collections.unmodifiableList( Arrays.asList( values ) );
+
+    /**
+     * The name of the message component type.
+     */
+    private final String name;
+
+    /**
+     * The value/code for the message component type.
+     */
+    private final int ordinal;
+
+
+    /**
+     * Private constructor prevents construction outside of this class.
+     */
+    private MessageComponentType( int ordinal, String name )
+    {
+        this.ordinal = ordinal;
+        this.name = name;
+    }
+
+
+    /**
+     * Returns the message component type when specified by its ordinal.
+     *
+     * @param type
+     * @return The message component type.
+     */
+    public static MessageComponentType getTypeByOrdinal( int type )
+    {
+        for ( int ii = 0; ii < values.length; ii++ )
+        {
+            if ( values[ii].ordinal == type )
+            {
+                return values[ii];
+            }
+        }
+
+        return NULL;
+    }
+
+
+    /**
+     * Returns the number associated with this message component type.
+     *
+     * @return The message component type ordinal.
+     */
+    public int getOrdinal()
+    {
+        return ordinal;
+    }
+
+
+    public int compareTo( MessageComponentType that )
+    {
+        return ordinal - that.ordinal;
+    }
+
+
+    public String toString()
+    {
+        return name + " (" + ordinal + ")";
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/components/Ticket.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/components/Ticket.java
new file mode 100644
index 0000000..0261a59
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/components/Ticket.java
@@ -0,0 +1,542 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.components;
+
+
+import java.nio.BufferOverflowException;
+import java.nio.ByteBuffer;
+import java.text.ParseException;
+
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.apache.directory.server.kerberos.shared.KerberosConstants;
+import org.apache.directory.server.kerberos.shared.KerberosUtils;
+import org.apache.directory.server.kerberos.shared.exceptions.ErrorType;
+import org.apache.directory.server.kerberos.shared.messages.value.AuthorizationData;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptedData;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+import org.apache.directory.server.kerberos.shared.messages.value.HostAddresses;
+import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
+import org.apache.directory.server.kerberos.shared.messages.value.PrincipalName;
+import org.apache.directory.server.kerberos.shared.messages.value.TransitedEncoding;
+import org.apache.directory.server.kerberos.shared.messages.value.flags.TicketFlags;
+import org.apache.directory.shared.asn1.AbstractAsn1Object;
+import org.apache.directory.shared.asn1.ber.tlv.TLV;
+import org.apache.directory.shared.asn1.ber.tlv.UniversalTag;
+import org.apache.directory.shared.asn1.ber.tlv.Value;
+import org.apache.directory.shared.asn1.codec.EncoderException;
+import org.apache.directory.shared.ldap.util.StringTools;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * Ticket message component as handed out by the ticket granting service.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class Ticket extends AbstractAsn1Object
+{
+    /** The logger */
+    private static final Logger LOG = LoggerFactory.getLogger( Ticket.class );
+
+    /** Speedup for logs */
+    private static final boolean IS_DEBUG = LOG.isDebugEnabled();
+    
+    /** Constant for the {@link Ticket} version number (5) */
+    public static final int TICKET_VNO = KerberosConstants.KERBEROS_V5;
+
+    /** The Kerberos version number. Should be 5 */
+    private int tktvno;
+    
+    /** A storage for a byte array representation of the realm */
+    private byte[] realmBytes;
+    
+    /** The server principal name */
+    private PrincipalName sName;
+    
+    /** The server realm */
+    private String realm;
+    
+    /** The encoded part */
+    private EncryptedData encPart;
+    
+    /** The decoded ticket part */
+    private EncTicketPart encTicketPart;
+
+    // Storage for computed lengths
+    private transient int tktvnoLength;
+    private transient int realmLength;
+    private transient int sNameLength;
+    private transient int encPartLength;
+    private transient int ticketSeqLength;
+    private transient int ticketLength;
+
+    /**
+     * Creates a new instance of Ticket.
+     *
+     * @param serverPrincipal The server principal
+     * @param encPart The encoded part
+     */
+    public Ticket( KerberosPrincipal serverPrincipal, EncryptedData encPart ) throws InvalidTicketException
+    {
+        this( TICKET_VNO, serverPrincipal, encPart );
+
+        setServerPrincipal( serverPrincipal );
+    }
+
+
+    /**
+     * Creates a new instance of Ticket.
+     */
+    public Ticket()
+    {
+    }
+    
+    
+    /**
+     * Creates a new instance of Ticket.
+     *
+     * @param tktvno The Kerberos version number
+     * @param serverPrincipal The server principal
+     * @param encPart The encoded part
+     */
+    public Ticket( int tktvno, KerberosPrincipal serverPrincipal, EncryptedData encPart ) throws InvalidTicketException
+    {
+        this.tktvno = tktvno;
+        this.encPart = encPart;
+        setServerPrincipal( serverPrincipal );
+    }
+
+
+    /**
+     * Sets the {@link EncTicketPart}.
+     *
+     * @param decryptedPart
+     */
+    public void setEncTicketPart( EncTicketPart decryptedPart )
+    {
+        encTicketPart = decryptedPart;
+    }
+
+
+    /**
+     * Returns the version number.
+     *
+     * @return The version number.
+     */
+    public int getTktVno()
+    {
+        return tktvno;
+    }
+    
+    
+    /**
+     * Set the ticket version number
+     * @param tktvno the ticket version number
+     */
+    public void setTktVno( int tktvno )
+    {
+        this.tktvno = tktvno;
+    }
+
+
+    /**
+     * Returns the server {@link PrincipalName}.
+     *
+     * @return The server {@link PrincipalName}.
+     */
+    public PrincipalName getSName()
+    {
+        return sName;
+    }
+
+    
+    /**
+     * Returns the server {@link KerberosPrincipal}.
+     *
+     * @return The server {@link KerberosPrincipal}.
+     */
+    public KerberosPrincipal getServerPrincipal()
+    {
+        return KerberosUtils.getKerberosPrincipal( sName, realm );
+    }
+
+    
+    /**
+     * Set the server principalName
+     * @param sName the server principalName
+     */
+    public void setSName( PrincipalName sName )
+    {
+        this.sName = sName;
+    }
+    
+
+    /**
+     * Set the server KerberosPrincipal
+     * @param serverPrincipal the server KerberosPrincipal
+     */
+    public void setServerPrincipal( KerberosPrincipal serverPrincipal ) throws InvalidTicketException
+    {
+        try
+        {
+            sName = new PrincipalName( serverPrincipal.getName(), serverPrincipal.getNameType() );
+            realm = serverPrincipal.getRealm();
+        }
+        catch ( ParseException pe )
+        {
+            LOG.error( "Cannot create a ticket for the {} KerberosPrincipal, error : {}", serverPrincipal, pe.getMessage() );
+            throw new InvalidTicketException( ErrorType.KRB_ERR_GENERIC, "Cannot create a ticket : " + pe.getMessage() );
+        }
+    }
+    
+
+    /**
+     * Returns the server realm.
+     *
+     * @return The server realm.
+     */
+    public String getRealm()
+    {
+        return realm;
+    }
+
+
+    /**
+     * Set the server realm
+     * @param realm the server realm
+     */
+    public void setRealm( String realm )
+    {
+        this.realm = realm;
+    }
+    
+   
+    /**
+     * Returns the {@link EncryptedData}.
+     *
+     * @return The {@link EncryptedData}.
+     */
+    public EncryptedData getEncPart()
+    {
+        return encPart;
+    }
+
+    
+    /**
+     * Set the encrypted ticket part
+     * @param encPart the encrypted ticket part
+     */
+    public void setEncPart( EncryptedData encPart )
+    {
+        this.encPart = encPart; 
+    }
+    
+
+    /**
+     * Returns the {@link EncTicketPart}.
+     *
+     * @return The {@link EncTicketPart}.
+     */
+    public EncTicketPart getEncTicketPart()
+    {
+        return encTicketPart;
+    }
+
+
+    /**
+     * Returns the {@link AuthorizationData}.
+     *
+     * @return The {@link AuthorizationData}.
+     *
+    public AuthorizationData getAuthorizationData()
+    {
+        return encTicketPart.getAuthorizationData();
+    }
+    */
+
+    /**
+     * Returns the auth {@link KerberosTime}.
+     *
+     * @return The auth {@link KerberosTime}.
+     *
+    public KerberosTime getAuthTime()
+    {
+        return encTicketPart.getAuthTime();
+    }
+    */
+
+    /**
+     * Returns the client {@link HostAddresses}.
+     *
+     * @return The client {@link HostAddresses}.
+     *
+    public HostAddresses getClientAddresses()
+    {
+        return encTicketPart.getClientAddresses();
+    }
+    */
+
+    /**
+     * Returns the client {@link KerberosPrincipal}.
+     *
+     * @return The client {@link KerberosPrincipal}.
+     *
+    public KerberosPrincipal getClientPrincipal()
+    {
+        return encTicketPart.getClientPrincipal();
+    }
+    */
+    
+    /**
+     * Returns the client {@link PrincipalName}.
+     *
+     * @return The client {@link PrincipalName}.
+     *
+    public PrincipalName getClientPrincipalName()
+    {
+        return encTicketPart.getClientPrincipalName();
+    }
+    */
+
+    /**
+     * Returns the client realm.
+     *
+     * @return The client realm.
+     *
+    public String getClientRealm()
+    {
+        return encTicketPart.getClientRealm();
+    }
+    */
+
+    /**
+     * Returns the end {@link KerberosTime}.
+     *
+     * @return The end {@link KerberosTime}.
+     *
+    public KerberosTime getEndTime()
+    {
+        return encTicketPart.getEndTime();
+    }
+    */
+
+    /**
+     * Returns the {@link TicketFlags}.
+     *
+     * @return The {@link TicketFlags}.
+     *
+    public TicketFlags getFlags()
+    {
+        return encTicketPart.getFlags();
+    }
+    */
+    
+    /**
+     * Returns the integer value for the {@link TicketFlags}.
+     *
+     * @return The {@link TicketFlags}.
+     *
+    public int getFlagsIntValue()
+    {
+        return encTicketPart.getFlags().getIntValue();
+    }
+    */
+
+    /**
+     * Returns the renew till {@link KerberosTime}.
+     *
+     * @return The renew till {@link KerberosTime}.
+     *
+    public KerberosTime getRenewTill()
+    {
+        return encTicketPart.getRenewTill();
+    }
+    */
+
+    /**
+     * Returns the session {@link EncryptionKey}.
+     *
+     * @return The session {@link EncryptionKey}.
+     *
+    public EncryptionKey getSessionKey()
+    {
+        return encTicketPart.getSessionKey();
+    }
+    */
+
+    /**
+     * Returns the start {@link KerberosTime}.
+     *
+     * @return The start {@link KerberosTime}.
+     *
+    public KerberosTime getStartTime()
+    {
+        return encTicketPart.getStartTime();
+    }
+    */
+
+    /**
+     * Returns the {@link TransitedEncoding}.
+     *
+     * @return The {@link TransitedEncoding}.
+     *
+    public TransitedEncoding getTransitedEncoding()
+    {
+        return encTicketPart.getTransitedEncoding();
+    }
+    */
+
+    /**
+     * Returns the flag at the given index.
+     *
+     * @param flag
+     * @return true if the flag at the given index is set.
+     *
+    public boolean getFlag( int flag )
+    {
+        return encTicketPart.getFlags().isFlagSet( flag );
+    }
+    */
+    
+    /**
+     * Compute the Ticket length
+     * 
+     * Ticket :
+     * 
+     * 0x61 L1 Ticket [APPLICATION 1]
+     *  |
+     *  +--> 0x30 L2 Ticket SEQUENCE
+     *        |
+     *        +--> 0xA0 L3 tkt-vno tag
+     *        |     |
+     *        |     +--> 0x02 L3-1 tkt-vno (int, 5)
+     *        |
+     *        +--> 0xA1 L4 realm tag
+     *        |     |
+     *        |     +--> 0x1B L4-1 realm (KerberosString)
+     *        |
+     *        +--> 0xA2 L5 sname (PrincipalName)
+     *        |
+     *        +--> 0xA3 L6 enc-part (EncryptedData)
+     */
+    public int computeLength()
+    {
+        // Compute the Ticket version length.
+        tktvnoLength = 1 + TLV.getNbBytes( tktvno ) + Value.getNbBytes( tktvno );
+
+        // Compute the Ticket realm length.
+        realmBytes = StringTools.getBytesUtf8( realm );
+        realmLength = 1 + TLV.getNbBytes( realmBytes.length ) + realmBytes.length;
+
+        // Compute the principal length
+        sNameLength = sName.computeLength();
+        
+        // Compute the encrypted data
+        encPartLength = encPart.computeLength();
+
+        // Compute the sequence size
+        ticketSeqLength = 
+            1 + TLV.getNbBytes( tktvnoLength ) + tktvnoLength +
+            1 + TLV.getNbBytes( realmLength ) + realmLength +
+            1 + TLV.getNbBytes( sNameLength ) + sNameLength + 
+            1 + TLV.getNbBytes( encPartLength ) + encPartLength;
+        
+        // compute the global size
+        ticketLength = 1 + TLV.getNbBytes( ticketSeqLength ) + ticketSeqLength;
+        
+        return 1 + TLV.getNbBytes( ticketLength ) + ticketLength;
+    }
+    
+    /**
+     * Encode the Ticket message to a PDU. 
+     * 
+     * Ticket :
+     * 
+     * 0x61 LL
+     *   0x30 LL
+     *     0xA0 LL tktvno 
+     *     0xA1 LL realm
+     *     0xA2 LL
+     *       sname (PrincipalName)
+     *     0xA3 LL
+     *       enc-part (EncryptedData)
+     * 
+     * @param buffer The buffer where to put the PDU. It should have been allocated
+     * before, with the right size.
+     * @return The constructed PDU.
+     */
+    public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException
+    {
+        if ( buffer == null )
+        {
+            buffer = ByteBuffer.allocate( computeLength() );
+        }
+
+        try
+        {
+            // The Ticket APPLICATION Tag
+            buffer.put( (byte)0x61 );
+            buffer.put( TLV.getBytes( ticketLength ) );
+
+            // The Ticket SEQUENCE Tag
+            buffer.put( UniversalTag.SEQUENCE_TAG );
+            buffer.put( TLV.getBytes( ticketSeqLength ) );
+
+            // The tkt-vno Tag and value
+            buffer.put( ( byte ) 0xA0 );
+            buffer.put( TLV.getBytes( tktvnoLength ) );
+            Value.encode( buffer, tktvno );
+
+            // The realm Tag and value
+            buffer.put( ( byte ) 0xA1 );
+            buffer.put( TLV.getBytes( realmLength ) );
+            buffer.put( UniversalTag.GENERALIZED_STRING_TAG );
+            buffer.put( TLV.getBytes( realmBytes.length ) );
+            buffer.put( realmBytes );
+
+            // The sname Tag and value
+            buffer.put( ( byte ) 0xA2 );
+            buffer.put( TLV.getBytes( sNameLength ) );
+            sName.encode( buffer );
+            
+            // The encPartLength Tag and value
+            buffer.put( ( byte ) 0xA3 );
+            buffer.put( TLV.getBytes( encPartLength ) );
+            encPart.encode( buffer );
+        }
+        catch ( BufferOverflowException boe )
+        {
+            LOG.error( "Cannot encode the Ticket object, the PDU size is {} when only {} bytes has been allocated", 1
+                + TLV.getNbBytes( ticketLength ) + ticketLength, buffer.capacity() );
+            throw new EncoderException( "The PDU buffer size is too small !" );
+        }
+
+        if ( IS_DEBUG )
+        {
+            LOG.debug( "Ticket encoding : {}", StringTools.dumpBytes( buffer.array() ) );
+            LOG.debug( "Ticket initial value : {}", toString() );
+        }
+
+        return buffer;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/components/package-info.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/components/package-info.java
new file mode 100644
index 0000000..af6cf9f
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/components/package-info.java
@@ -0,0 +1,28 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+/**
+ * Provides major components of Kerberos messages.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+
+package org.apache.directory.server.kerberos.shared.messages.components;
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/package-info.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/package-info.java
new file mode 100644
index 0000000..a87118b
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/package-info.java
@@ -0,0 +1,29 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+/**
+ * Provides base message objects for Kerberos request and
+ * reply messages.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+
+package org.apache.directory.server.kerberos.shared.messages;
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/ApOptions.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/ApOptions.java
new file mode 100644
index 0000000..8707642
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/ApOptions.java
@@ -0,0 +1,95 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.value;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ApOptions extends Options
+{
+    /**
+     * AP Request option - reserved
+     */
+    public static final int RESERVED = 0;
+
+    /**
+     * AP Request option - use session key
+     */
+    public static final int USE_SESSION_KEY = 1;
+
+    /**
+     * AP Request option - mutual authentication required
+     */
+    public static final int MUTUAL_REQUIRED = 2;
+
+    /**
+     * AP Request option - maximum value
+     */
+    public static final int MAX_VALUE = 32;
+
+
+    /**
+     * Creates a new instance of ApOptions.
+     */
+    public ApOptions()
+    {
+        super( MAX_VALUE );
+    }
+
+
+    /**
+     * Creates a new instance of ApOptions.
+     *
+     * @param options
+     */
+    public ApOptions( byte[] options )
+    {
+        super( MAX_VALUE );
+        setBytes( options );
+    }
+
+
+    /**
+     * Converts the object to a printable string.
+     */
+    public String toString()
+    {
+        StringBuffer result = new StringBuffer();
+
+        if ( get( MUTUAL_REQUIRED ) )
+        {
+            result.append( "MUTUAL_REQUIRED " );
+        }
+
+        if ( get( RESERVED ) )
+        {
+            result.append( "RESERVED " );
+        }
+
+        if ( get( USE_SESSION_KEY ) )
+        {
+            result.append( "USE_SESSION_KEY " );
+        }
+
+        return result.toString().trim();
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/AuthorizationData.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/AuthorizationData.java
new file mode 100644
index 0000000..f7fec4f
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/AuthorizationData.java
@@ -0,0 +1,241 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.value;
+
+
+import java.nio.BufferOverflowException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.directory.server.kerberos.shared.messages.Encodable;
+import org.apache.directory.shared.asn1.AbstractAsn1Object;
+import org.apache.directory.shared.asn1.ber.tlv.TLV;
+import org.apache.directory.shared.asn1.ber.tlv.UniversalTag;
+import org.apache.directory.shared.asn1.codec.EncoderException;
+import org.apache.directory.shared.ldap.util.StringTools;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * An authorization data container.
+ * 
+ * The ASN.1 grammar is :
+ * 
+ * -- NOTE: AuthorizationData is always used as an OPTIONAL field and
+ * -- should not be empty.
+ * AuthorizationData       ::= SEQUENCE OF AuthorizationDataEntry
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class AuthorizationData extends AbstractAsn1Object implements Encodable
+{
+    /** The logger */
+    private static final Logger log = LoggerFactory.getLogger( AuthorizationData.class );
+
+    /** Speedup for logs */
+    private static final boolean IS_DEBUG = log.isDebugEnabled();
+
+    /** The list of AuthrorizationData elements */
+    private List<AuthorizationDataEntry> entries = new ArrayList<AuthorizationDataEntry>();
+
+    // Storage for computed lengths
+    private transient int authorizationDataLength;
+
+
+    /**
+     * Creates a new instance of AuthorizationData.
+     */
+    public AuthorizationData()
+    {
+        // used by ASN.1 decoder
+    }
+
+
+    /**
+     * Adds all {@link AuthorizationData} entries to this {@link AuthorizationData}.
+     *
+     * @param data
+     */
+    public void add( AuthorizationData data )
+    {
+        entries.addAll( data.entries );
+    }
+
+
+    /**
+     * Adds an {@link AuthorizationDataEntry} to this {@link AuthorizationData}.
+     *
+     * @param entry
+     */
+    public void add( AuthorizationDataEntry entry )
+    {
+        entries.add( entry );
+    }
+
+
+    /**
+     * @return The AuthorizationdataEntry list
+     */
+    public List<AuthorizationDataEntry> getEntries()
+    {
+        return entries;
+    }
+
+
+    /**
+     * Compute the AuthorizationData length
+     * 
+     * AuthorizationData :
+     * 
+     * 0x30 L1 AuthorizationData
+     *  |
+     *  +--> 0x30 L2 AuthorizationDataEntry
+     *  |
+     *  +--> 0x30 L2 AuthorizationDataEntry
+     *  |
+     *  ...
+     *  |
+     *  +--> 0x30 L2 AuthorizationDataEntry
+     */
+    public int computeLength()
+    {
+        if ( ( entries == null ) || ( entries.size() == 0 ) )
+        {
+            authorizationDataLength = 1;
+
+            return authorizationDataLength + 1;
+        }
+        else
+        {
+            authorizationDataLength = 0;
+
+            for ( AuthorizationDataEntry entry : entries )
+            {
+                authorizationDataLength += entry.computeLength();
+            }
+
+            return 1 + TLV.getNbBytes( authorizationDataLength ) + authorizationDataLength;
+        }
+    }
+
+
+    /**
+     * Encode the AuthorizationData message to a PDU. 
+     * 
+     * AuthorizationData :
+     * 
+     * 0x30 LL
+     *   0x30 LL AuthorizationDataEntry 
+     *   0x30 LL AuthorizationDataEntry
+     *   ... 
+     *   0x30 LL AuthorizationDataEntry 
+     * 
+     * @param buffer The buffer where to put the PDU. It should have been allocated
+     * before, with the right size.
+     * @return The constructed PDU.
+     */
+    public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException
+    {
+        if ( buffer == null )
+        {
+            throw new EncoderException( "Cannot put a PDU in a null buffer !" );
+        }
+
+        try
+        {
+            // The AuthorizationData SEQ Tag
+            buffer.put( UniversalTag.SEQUENCE_TAG );
+            buffer.put( TLV.getBytes( authorizationDataLength ) );
+
+            // Each entry, if any
+            if ( ( entries != null ) && ( entries.size() != 0 ) )
+            {
+                for ( AuthorizationDataEntry entry : entries )
+                {
+                    entry.encode( buffer );
+                }
+            }
+        }
+        catch ( BufferOverflowException boe )
+        {
+            log.error(
+                "Cannot encode the AuthorizationData object, the PDU size is {} when only {} bytes has been allocated",
+                1 + TLV.getNbBytes( authorizationDataLength ) + authorizationDataLength, buffer.capacity() );
+            throw new EncoderException( "The PDU buffer size is too small !" );
+        }
+
+        if ( IS_DEBUG )
+        {
+            log.debug( "AuthorizationData encoding : {}", StringTools.dumpBytes( buffer.array() ) );
+            log.debug( "AuthorizationData initial value : {}", toString() );
+        }
+
+        return buffer;
+    }
+
+
+    /**
+     * @see Object#toString()
+     */
+    public String toString()
+    {
+        return toString( "" );
+    }
+
+
+    /**
+     * @see Object#toString()
+     */
+    public String toString( String tabs )
+    {
+        StringBuilder sb = new StringBuilder();
+
+        if ( ( entries == null ) || ( entries.size() == 0 ) )
+        {
+            sb.append( tabs ).append( "AuthorizationData : {}\n" );
+        }
+        else
+        {
+            sb.append( tabs ).append( "AuthorizationData : {\n" );
+            boolean isFirst = true;
+
+            for ( AuthorizationDataEntry entry : entries )
+            {
+                if ( isFirst )
+                {
+                    isFirst = false;
+                }
+                else
+                {
+                    sb.append( '\n' );
+                }
+
+                sb.append( entry.toString( tabs + "    " ) ).append( '\n' );
+            }
+
+            sb.append( tabs + "}" );
+        }
+
+        return sb.toString();
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/AuthorizationDataEntry.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/AuthorizationDataEntry.java
new file mode 100644
index 0000000..01d6967
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/AuthorizationDataEntry.java
@@ -0,0 +1,280 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.value;
+
+
+import java.nio.BufferOverflowException;
+import java.nio.ByteBuffer;
+
+import org.apache.directory.server.kerberos.shared.messages.value.types.AuthorizationType;
+import org.apache.directory.shared.asn1.AbstractAsn1Object;
+import org.apache.directory.shared.asn1.ber.tlv.TLV;
+import org.apache.directory.shared.asn1.ber.tlv.UniversalTag;
+import org.apache.directory.shared.asn1.ber.tlv.Value;
+import org.apache.directory.shared.asn1.codec.EncoderException;
+import org.apache.directory.shared.ldap.util.StringTools;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * A single AuthorizationData
+ * 
+ * The ASN.1 grammar is :
+ * -- NOTE: AuthorizationData is always used as an OPTIONAL field and
+ * -- should not be empty.
+ * AuthorizationDataEntry       ::= SEQUENCE {
+ *        ad-type         [0] Int32,
+ *        ad-data         [1] OCTET STRING
+ * }
+ *  
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class AuthorizationDataEntry extends AbstractAsn1Object
+{
+    /** The logger */
+    private static final Logger log = LoggerFactory.getLogger( AuthorizationDataEntry.class );
+
+    /** Speedup for logs */
+    private static final boolean IS_DEBUG = log.isDebugEnabled();
+
+    /** The Authorization type. One of :
+     * DER encoding of AD-IF-RELEVANT        1
+     * DER encoding of AD-KDCIssued          4
+     * DER encoding of AD-AND-OR             5
+     * DER encoding of AD-MANDATORY-FOR-KDC  8 
+     **/
+    private AuthorizationType adType;
+
+    /** The data, encrypted */
+    private byte[] adData;
+
+    // Storage for computed lengths
+    private transient int adTypeLength;
+    private transient int adDataLength;
+    private transient int authorizationDataEntryLength;
+
+
+    /**
+     * Creates a new instance of AuthorizationDataEntry.
+     */
+    public AuthorizationDataEntry()
+    {
+    }
+
+    
+    /**
+     * Creates a new instance of AuthorizationDataEntry.
+     *
+     * @param adType The authorizationType
+     * @param adData The authorization data
+     */
+    public AuthorizationDataEntry( AuthorizationType adType, byte[] adData )
+    {
+        this.adType = adType;
+        this.adData = adData;
+    }
+
+
+    /**
+     * Returns the raw bytes of the authorization data.
+     *
+     * @return The raw bytes of the authorization data.
+     */
+    public byte[] getAdData()
+    {
+        return adData;
+    }
+
+
+    /**
+     * Set the authorization data
+     * 
+     * @param adData The data
+     */
+    public void setAdData( byte[] adData ) 
+    {
+        this.adData = adData;
+    }
+
+    
+    /**
+     * Returns the {@link AuthorizationType}.
+     *
+     * @return The {@link AuthorizationType}.
+     */
+    public AuthorizationType getAdType()
+    {
+        return adType;
+    }
+
+
+    /**
+     * Set the authorization type
+     * @param adType The authorization type
+     */
+    public void setAdType( int adType ) 
+    {
+        this.adType = AuthorizationType.getTypeByOrdinal( adType );
+    }
+
+    
+    /**
+     * Set the authorization type
+     * @param adType The authorization type
+     */
+    public void setAdType( AuthorizationType adType ) 
+    {
+        this.adType = adType;
+    }
+
+    
+    /**
+     * Compute the AuthorizationDataEntry length
+     * 
+     * AuthorizationDataEntry :
+     * 
+     * 0x30 L1 AuthorizationDataEntry
+     *  |
+     *  +--> 0xA0 L2 adType tag
+     *  |     |
+     *  |     +--> 0x02 L2-1 adType (int)
+     *  |
+     *  +--> 0xA1 L3 adData tag
+     *        |
+     *        +--> 0x04 L3-1 adData (OCTET STRING)
+     *        
+     *  where L1 = L2 + lenght(0xA0) + length(L2) +
+     *             L3 + lenght(0xA1) + length(L3) 
+     *  and
+     *  L2 = L2-1 + length(0x02) + length( L2-1) 
+     *  L3 = L3-1 + length(0x04) + length( L3-1) 
+     */
+    public int computeLength()
+    {
+        // Compute the adType. The Length will always be contained in 1 byte
+        adTypeLength = 1 + 1 + Value.getNbBytes( adType.getOrdinal() );
+        authorizationDataEntryLength = 1 + TLV.getNbBytes( adTypeLength ) + adTypeLength;
+
+        // Compute the keyValue
+        if ( adData == null )
+        {
+            adDataLength = 1 + 1;
+        }
+        else
+        {
+            adDataLength = 1 + TLV.getNbBytes( adData.length ) + adData.length;
+        }
+
+        authorizationDataEntryLength += 1 + TLV.getNbBytes( adDataLength ) + adDataLength;
+
+        // Compute the whole sequence length
+        int authorizationDataEntrySeqLength = 1 + Value.getNbBytes( authorizationDataEntryLength )
+            + authorizationDataEntryLength;
+
+        return authorizationDataEntrySeqLength;
+
+    }
+
+
+    /**
+     * Encode the AuthorizationDataEntry message to a PDU. 
+     * 
+     * AuthorizationDataEntry :
+     * 
+     * 0x30 LL
+     *   0xA0 LL 
+     *     0x02 0x01 adType
+     *   0xA1 LL 
+     *     0x04 LL adData
+     * 
+     * @param buffer The buffer where to put the PDU. It should have been allocated
+     * before, with the right size.
+     * @return The constructed PDU.
+     */
+    public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException
+    {
+        if ( buffer == null )
+        {
+            throw new EncoderException( "Cannot put a PDU in a null buffer !" );
+        }
+
+        try
+        {
+            // The AuthorizationDataEntry SEQ Tag
+            buffer.put( UniversalTag.SEQUENCE_TAG );
+            buffer.put( TLV.getBytes( authorizationDataEntryLength ) );
+
+            // The adType, first the tag, then the value
+            buffer.put( ( byte ) 0xA0 );
+            buffer.put( TLV.getBytes( adTypeLength ) );
+            Value.encode( buffer, adType.getOrdinal() );
+
+            // The adData, first the tag, then the value
+            buffer.put( ( byte ) 0xA1 );
+            buffer.put( TLV.getBytes( adDataLength ) );
+            Value.encode( buffer, adData );
+        }
+        catch ( BufferOverflowException boe )
+        {
+            log
+                .error(
+                    "Cannot encode the AuthorizationDataEntry object, the PDU size is {} when only {} bytes has been allocated",
+                    1 + TLV.getNbBytes( authorizationDataEntryLength ) + authorizationDataEntryLength, buffer
+                        .capacity() );
+            throw new EncoderException( "The PDU buffer size is too small !" );
+        }
+
+        if ( IS_DEBUG )
+        {
+            log.debug( "AuthorizationDataEntry encoding : {}", StringTools.dumpBytes( buffer.array() ) );
+            log.debug( "AuthorizationDataEntry initial value : {}", toString() );
+        }
+
+        return buffer;
+    }
+
+
+    /**
+     * @see Object#toString()
+     */
+    public String toString()
+    {
+        return toString( "    " );
+    }
+
+
+    /**
+     * @see Object#toString()
+     */
+    public String toString( String tabs )
+    {
+        StringBuilder sb = new StringBuilder();
+
+        sb.append( tabs ).append( "AuthorizationDataEntry : {\n" );
+        sb.append( tabs ).append( "    ad-type: " ).append( adType ).append( '\n' );
+
+        sb.append( tabs ).append( "    ad-data: " ).append( StringTools.dumpBytes( adData ) )
+            .append( "\n" + tabs + "}" );
+
+        return sb.toString();
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/Checksum.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/Checksum.java
new file mode 100644
index 0000000..3e10f3a
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/Checksum.java
@@ -0,0 +1,285 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.value;
+
+
+import java.nio.BufferOverflowException;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+import org.apache.directory.server.kerberos.shared.crypto.checksum.ChecksumType;
+import org.apache.directory.shared.asn1.AbstractAsn1Object;
+import org.apache.directory.shared.asn1.ber.tlv.TLV;
+import org.apache.directory.shared.asn1.ber.tlv.UniversalTag;
+import org.apache.directory.shared.asn1.ber.tlv.Value;
+import org.apache.directory.shared.asn1.codec.EncoderException;
+import org.apache.directory.shared.ldap.util.StringTools;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * The Checksum structure is used to store a checksum associated to a type.
+ * 
+ * The ASN.1 grammar is :
+ * Checksum        ::= SEQUENCE {
+ *       cksumtype       [0] Int32,
+ *       checksum        [1] OCTET STRING
+ * }
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class Checksum extends AbstractAsn1Object
+{
+    /** The logger */
+    private static final Logger log = LoggerFactory.getLogger( Checksum.class );
+
+    /** Speedup for logs */
+    private static final boolean IS_DEBUG = log.isDebugEnabled();
+
+    /** The checksum type used */
+    private ChecksumType cksumtype;
+
+    /** The byte array containing the checksum */
+    private byte[] checksum;
+
+    // Storage for computed lengths
+    private transient int checksumTypeLength;
+    private transient int checksumBytesLength;
+    private transient int checksumLength;
+
+
+    /**
+     * Creates a new instance of Checksum.
+     */
+    public Checksum()
+    {
+    }
+
+    
+    /**
+     * Creates a new instance of Checksum.
+     *
+     * @param cksumtype The checksum type used
+     * @param checksum The checksum value
+     */
+    public Checksum( ChecksumType cksumtype, byte[] checksum )
+    {
+        this.cksumtype = cksumtype;
+        this.checksum = checksum;
+    }
+
+
+    /**
+     * @see Object#equals(Object)
+     */
+    public boolean equals( Object o )
+    {
+        if ( this == o )
+        {
+            return true;
+        }
+
+        if ( !( o instanceof Checksum ) )
+        {
+            return false;
+        }
+
+        Checksum that = ( Checksum ) o;
+
+        return ( cksumtype == that.cksumtype ) && ( Arrays.equals( checksum, that.checksum ) );
+    }
+
+
+    /**
+     * Returns the checksum value.
+     *
+     * @return The checksum value.
+     */
+    public byte[] getChecksumValue()
+    {
+        return checksum;
+    }
+
+
+    /**
+     * Set the checksum Value.
+     *
+     * @param checksum The checksum value
+     */
+    public void setChecksumValue( byte[] checksum )
+    {
+        this.checksum = checksum;
+    }
+
+
+    /**
+     * Returns the {@link ChecksumType}.
+     *
+     * @return The {@link ChecksumType}.
+     */
+    public ChecksumType getChecksumType()
+    {
+        return cksumtype;
+    }
+
+
+    /**
+     * Set the {@link ChecksumType}.
+     *
+     * @param cksumType The checksum algorithm used
+     */
+    public void setChecksumType( ChecksumType cksumType )
+    {
+        this.cksumtype = cksumType;
+    }
+
+
+    /**
+     * Compute the checksum length
+     * 
+     * Checksum :
+     * 
+     * 0x30 L1 checksum sequence
+     *  |
+     *  +--> 0xA0 L2 cksumtype tag
+     *  |     |
+     *  |     +--> 0x02 L2-1 cksumtype (int)
+     *  |
+     *  +--> 0xA1 L3 checksum tag
+     *        |
+     *        +--> 0x04 L3-1 checksum (OCTET STRING)
+     *        
+     *  where L1 = L2 + lenght(0xA0) + length(L2) +
+     *             L3 + lenght(0xA1) + length(L3) 
+     *  and
+     *  L2 = L2-1 + length(0x02) + length( L2-1) 
+     *  L3 = L3-1 + length(0x04) + length( L3-1) 
+     */
+    public int computeLength()
+    {
+        // Compute the checksulType. The Length will always be contained in 1 byte
+        checksumTypeLength = 1 + 1 + Value.getNbBytes( cksumtype.getOrdinal() );
+        checksumLength = 1 + TLV.getNbBytes( checksumTypeLength ) + checksumTypeLength;
+
+        // Compute the checksum Value
+        if ( checksum == null )
+        {
+            checksumBytesLength = 1 + 1;
+        }
+        else
+        {
+            checksumBytesLength = 1 + TLV.getNbBytes( checksum.length ) + checksum.length;
+        }
+
+        checksumLength += 1 + TLV.getNbBytes( checksumBytesLength ) + checksumBytesLength;
+
+        // Compute the whole sequence length
+        int checksumSeqLength = 1 + Value.getNbBytes( checksumLength ) + checksumLength;
+
+        return checksumSeqLength;
+
+    }
+
+
+    /**
+     * Encode the Checksum message to a PDU. 
+     * 
+     * Checksum :
+     * 
+     * 0x30 LL
+     *   0xA0 LL 
+     *     0x02 0x01 cksumtype
+     *   0xA1 LL 
+     *     0x04 LL Checksum
+     * 
+     * @param buffer The buffer where to put the PDU. It should have been allocated
+     * before, with the right size.
+     * @return The constructed PDU.
+     */
+    public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException
+    {
+        if ( buffer == null )
+        {
+            throw new EncoderException( "Cannot put a PDU in a null buffer !" );
+        }
+
+        try
+        {
+            // The Checksum SEQ Tag
+            buffer.put( UniversalTag.SEQUENCE_TAG );
+            buffer.put( TLV.getBytes( checksumLength ) );
+
+            // The cksumtype, first the tag, then the value
+            buffer.put( ( byte ) 0xA0 );
+            buffer.put( TLV.getBytes( checksumTypeLength ) );
+            Value.encode( buffer, cksumtype.getOrdinal() );
+
+            // The checksum, first the tag, then the value
+            buffer.put( ( byte ) 0xA1 );
+            buffer.put( TLV.getBytes( checksumBytesLength ) );
+            Value.encode( buffer, checksum );
+        }
+        catch ( BufferOverflowException boe )
+        {
+            log.error( "Cannot encode the Checksum object, the PDU size is {} when only {} bytes has been allocated", 1
+                + TLV.getNbBytes( checksumLength ) + checksumLength, buffer.capacity() );
+            throw new EncoderException( "The PDU buffer size is too small !" );
+        }
+
+        if ( IS_DEBUG )
+        {
+            log.debug( "Checksum encoding : {}", StringTools.dumpBytes( buffer.array() ) );
+            log.debug( "Checksum initial value : {}", toString() );
+        }
+
+        return buffer;
+    }
+
+    /**
+     * @see Object#toString()
+     */
+    public String toString()
+    {
+        return toString( "" );
+    }
+
+
+    /**
+     * @see Object#toString()
+     */
+    public String toString( String tabs )
+    {
+        StringBuilder sb = new StringBuilder();
+
+        sb.append( tabs ).append( "Checksum : {\n" );
+        sb.append( tabs ).append( "    cksumtype: " ).append(  cksumtype ).append( '\n' );
+
+        if ( checksum != null )
+        {
+            sb.append( tabs + "    checksum:" ).append( StringTools.dumpBytes( checksum ) ).append( '\n' );
+        }
+
+        sb.append( tabs + "}\n" );
+
+        return sb.toString();
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/EncryptedData.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/EncryptedData.java
new file mode 100644
index 0000000..6d97eb0
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/EncryptedData.java
@@ -0,0 +1,333 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.value;
+
+
+import java.nio.BufferOverflowException;
+import java.nio.ByteBuffer;
+
+import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
+import org.apache.directory.shared.asn1.AbstractAsn1Object;
+import org.apache.directory.shared.asn1.ber.tlv.TLV;
+import org.apache.directory.shared.asn1.ber.tlv.UniversalTag;
+import org.apache.directory.shared.asn1.ber.tlv.Value;
+import org.apache.directory.shared.asn1.codec.EncoderException;
+import org.apache.directory.shared.ldap.util.StringTools;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * A structure storing an encrypted data element. The ASN.1 grammar is :
+ * 
+ * EncryptedData   ::= SEQUENCE {
+ *        etype   [0] Int32 -- EncryptionType --,
+ *        kvno    [1] UInt32 OPTIONAL,
+ *        cipher  [2] OCTET STRING -- ciphertext
+ * }
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class EncryptedData extends AbstractAsn1Object
+{
+    /** The logger */
+    private static final Logger log = LoggerFactory.getLogger( EncryptedData.class );
+
+    /** Speedup for logs */
+    private static final boolean IS_DEBUG = log.isDebugEnabled();
+
+    /** The used encryption algorithm */
+    private EncryptionType eType;
+
+    /** Version number of the key under which data is encrypted */
+    private int kvno;
+
+    /** A flag used to tell if a kvno has been added, as the kvno is optional. */
+    private boolean hasKvno;
+
+    /** The field containing the enciphered text */
+    private byte[] cipher;
+
+    /** A constant used when the key is not present */
+    public static final boolean HAS_KVNO = true;
+
+    // Storage for computed lengths
+    private transient int eTypeTagLength;
+    private transient int kvnoTagLength;
+    private transient int cipherTagLength;
+    private transient int encryptedDataSeqLength;
+
+
+    /**
+     * Creates a new instance of EncryptedData.
+     */
+    public EncryptedData()
+    {
+        hasKvno = !HAS_KVNO;
+    }
+    
+    /**
+     * Creates a new instance of EncryptedData.
+     *
+     * @param eType The encription algorithm
+     * @param kvno The key version
+     * @param cipher the encrypted text
+     */
+    public EncryptedData( EncryptionType eType, int kvno, byte[] cipher )
+    {
+        this.eType = eType;
+        this.hasKvno = kvno > 0;
+        this.kvno = kvno;
+        this.cipher = cipher;
+    }
+
+
+    /**
+     * Creates a new instance of EncryptedData.
+     *
+     * @param eType The encription algorithm
+     * @param cipher the encrypted text
+     */
+    public EncryptedData( EncryptionType eType, byte[] cipher )
+    {
+        this.eType = eType;
+        this.hasKvno = !HAS_KVNO;
+        kvno = -1;
+        this.cipher = cipher;
+    }
+
+
+    /**
+     * Returns the {@link EncryptionType}.
+     *
+     * @return The {@link EncryptionType}.
+     */
+    public EncryptionType getEType()
+    {
+        return eType;
+    }
+
+
+    /**
+     * Set the EncryptionType
+     * @param eType the EncryptionType
+     */
+    public void setEType( EncryptionType eType )
+    {
+        this.eType = eType;
+    }
+
+    /**
+     * Returns the key version.
+     *
+     * @return The key version.
+     */
+    public int getKvno()
+    {
+        return hasKvno ? kvno : -1;
+    }
+
+    /**
+     * Set the key version
+     * @param kvno The key version
+     */
+    public void setKvno( int kvno )
+    {
+        this.kvno = kvno;
+    }
+
+    /**
+     * Tells if there is a key version.
+     *
+     * @return <code>true</code> if there is a key version.
+     */
+    public boolean hasKvno()
+    {
+        return hasKvno;
+    }
+
+
+    /**
+     * Returns the raw cipher text.
+     *
+     * @return The raw cipher text.
+     */
+    public byte[] getCipher()
+    {
+        return cipher;
+    }
+
+    /**
+     * Set the cipher text
+     * @param cipher The cipher text
+     */
+    public void setCipher( byte[] cipher )
+    {
+        this.cipher = cipher;
+    }
+    
+
+    /**
+     * Compute the EncryptedData length
+     * 
+     * EncryptedData :
+     * 
+     * 0x30 L1 EncryptedData sequence
+     *  |
+     *  +--> 0xA1 L2 etype tag
+     *  |     |
+     *  |     +--> 0x02 L2-1 etype (int)
+     *  |
+     *  +--> [0xA2 L3 kvno tag
+     *  |     |
+     *  |     +--> 0x30 L3-1 kvno (int)] (optional)
+     *  |
+     *  +--> 0xA2 L4 cipher tag
+     *        |
+     *        +--> 0x04 L4-1 cipher (OCTET STRING)
+     */
+    public int computeLength()
+    {
+        encryptedDataSeqLength = 0;
+
+        // Compute the encryption Type length
+        int eTypeLength = Value.getNbBytes( eType.getOrdinal() );
+        eTypeTagLength = 1 + TLV.getNbBytes( eTypeLength ) + eTypeLength;
+        encryptedDataSeqLength = 1 + TLV.getNbBytes( eTypeTagLength ) + eTypeTagLength; 
+
+
+        // Compute the kvno length if any
+        if ( hasKvno )
+        {
+            int kvnoLength = Value.getNbBytes( kvno );
+            kvnoTagLength = 1 + TLV.getNbBytes( kvnoLength ) + kvnoLength;
+            encryptedDataSeqLength += 1 + TLV.getNbBytes( kvnoTagLength ) + kvnoTagLength;
+        }
+        else
+        {
+            kvnoTagLength = 0;
+        }
+
+        // Compute the cipher
+        if ( ( cipher == null ) || ( cipher.length == 0 ) )
+        {
+            cipherTagLength = 1 + 1;
+        }
+        else
+        {
+            cipherTagLength = 1 + TLV.getNbBytes( cipher.length ) + cipher.length;
+        }
+
+        encryptedDataSeqLength += 1 + TLV.getNbBytes( cipherTagLength ) + cipherTagLength;
+
+        // Compute the whole sequence length
+        return 1 + TLV.getNbBytes( encryptedDataSeqLength ) + encryptedDataSeqLength;
+    }
+
+
+    /**
+     * Encode the EncryptedData message to a PDU. 
+     * 
+     * EncryptedData :
+     * 
+     * 0x30 LL
+     *   0xA0 LL 
+     *     0x02 0x01 etype (integer)
+     *   [0xA1 LL 
+     *     0x02 0x01 kvno (integer)] (optional)
+     *   0xA2 LL 
+     *     0x04 LL cipher (OCTET STRING)
+     * 
+     * @param buffer The buffer where to put the PDU. It should have been allocated
+     * before, with the right size.
+     * @return The constructed PDU.
+     */
+    public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException
+    {
+        if ( buffer == null )
+        {
+            throw new EncoderException( "Cannot put a PDU in a null buffer !" );
+        }
+
+        try
+        {
+            // The EncryptedData SEQ Tag
+            buffer.put( UniversalTag.SEQUENCE_TAG );
+            buffer.put( TLV.getBytes( encryptedDataSeqLength ) );
+
+            // The etype, first the tag, then the value
+            buffer.put( ( byte ) 0xA0 );
+            buffer.put( TLV.getBytes( eTypeTagLength ) );
+
+            Value.encode( buffer, eType.getOrdinal() );
+
+            // The kvno, if any, first the tag, then the value
+            if ( hasKvno )
+            {
+                buffer.put( ( byte ) 0xA1 );
+                buffer.put( TLV.getBytes( kvnoTagLength ) );
+
+                Value.encode( buffer, kvno );
+            }
+
+            // The cipher tag
+            buffer.put( ( byte ) 0xA2 );
+            buffer.put( TLV.getBytes( cipherTagLength ) );
+            Value.encode( buffer, cipher );
+        }
+        catch ( BufferOverflowException boe )
+        {
+            log.error(
+                "Cannot encode the EncryptedData object, the PDU size is {} when only {} bytes has been allocated", 1
+                    + TLV.getNbBytes( encryptedDataSeqLength ) + encryptedDataSeqLength, buffer.capacity() );
+            throw new EncoderException( "The PDU buffer size is too small !" );
+        }
+
+        if ( IS_DEBUG )
+        {
+            log.debug( "EncryptedData encoding : {}", StringTools.dumpBytes( buffer.array() ) );
+            log.debug( "EncryptedData initial value : {}", toString() );
+        }
+
+        return buffer;
+    }
+
+
+    /**
+     * @see Object#toString()
+     */
+    public String toString()
+    {
+        StringBuilder sb = new StringBuilder();
+
+        sb.append( "EncryptedData : {\n" );
+        sb.append( "    etype: " ).append( eType ).append( '\n' );
+
+        if ( hasKvno )
+        {
+            sb.append( "    kvno: " ).append( kvno ).append( '\n' );
+        }
+
+        sb.append( "    cipher: " ).append( StringTools.dumpBytes( cipher ) ).append( "\n}\n" );
+
+        return sb.toString();
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/EncryptedTimeStamp.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/EncryptedTimeStamp.java
new file mode 100644
index 0000000..3d1e519
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/EncryptedTimeStamp.java
@@ -0,0 +1,71 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.value;
+
+
+import org.apache.directory.server.kerberos.shared.messages.Encodable;
+
+
+/**
+ * Pre-authentication encrypted timestamp.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class EncryptedTimeStamp implements Encodable
+{
+    private KerberosTime timeStamp;
+    private int microSeconds; //optional
+
+
+    /**
+     * Creates a new instance of EncryptedTimeStamp.
+     *
+     * @param timeStamp
+     * @param microSeconds
+     */
+    public EncryptedTimeStamp( KerberosTime timeStamp, int microSeconds )
+    {
+        this.timeStamp = timeStamp;
+        this.microSeconds = microSeconds;
+    }
+
+
+    /**
+     * Returns the {@link KerberosTime}.
+     *
+     * @return The {@link KerberosTime}.
+     */
+    public KerberosTime getTimeStamp()
+    {
+        return timeStamp;
+    }
+
+
+    /**
+     * Returns the microseconds.
+     *
+     * @return The microseconds.
+     */
+    public int getMicroSeconds()
+    {
+        return microSeconds;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/EncryptedTimeStampModifier.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/EncryptedTimeStampModifier.java
new file mode 100644
index 0000000..c0606d4
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/EncryptedTimeStampModifier.java
@@ -0,0 +1,64 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.value;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class EncryptedTimeStampModifier
+{
+    private KerberosTime timeStamp;
+    private int microSecond; //optional
+
+
+    /**
+     * Returns the {@link EncryptedTimeStamp}.
+     *
+     * @return The {@link EncryptedTimeStamp}.
+     */
+    public EncryptedTimeStamp getEncryptedTimestamp()
+    {
+        return new EncryptedTimeStamp( timeStamp, microSecond );
+    }
+
+
+    /**
+     * Sets the {@link KerberosTime}.
+     *
+     * @param timeStamp
+     */
+    public void setKerberosTime( KerberosTime timeStamp )
+    {
+        this.timeStamp = timeStamp;
+    }
+
+
+    /**
+     * Sets the microseconds.
+     *
+     * @param microSecond
+     */
+    public void setMicroSecond( int microSecond )
+    {
+        this.microSecond = microSecond;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/EncryptionKey.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/EncryptionKey.java
new file mode 100644
index 0000000..1e6e385
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/EncryptionKey.java
@@ -0,0 +1,315 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.value;
+
+
+import java.nio.BufferOverflowException;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
+import org.apache.directory.shared.asn1.AbstractAsn1Object;
+import org.apache.directory.shared.asn1.ber.tlv.TLV;
+import org.apache.directory.shared.asn1.ber.tlv.UniversalTag;
+import org.apache.directory.shared.asn1.ber.tlv.Value;
+import org.apache.directory.shared.asn1.codec.EncoderException;
+import org.apache.directory.shared.ldap.util.StringTools;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * A Kerberos symmetric encryption key, which includes metadata support for
+ * the associated key type and key version number.
+ * 
+ * The ASN.1 description for this structure is :
+ * EncryptionKey   ::= SEQUENCE {
+ *       keytype         [0] Int32 -- actually encryption type --,
+ *       keyvalue        [1] OCTET STRING
+ * }
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class EncryptionKey extends AbstractAsn1Object
+{
+    /** The logger */
+    private static final Logger log = LoggerFactory.getLogger( EncryptionKey.class );
+
+    /** Speedup for logs */
+    private static final boolean IS_DEBUG = log.isDebugEnabled();
+
+    // The encryption type
+    private EncryptionType keyType;
+
+    // The encrypted value
+    private byte[] keyValue;
+    
+    // The key version
+    private int keyVersion;
+
+    // Storage for computed lengths
+    private transient int keyTypeLength;
+    private transient int keyValueLength;
+    private transient int encryptionKeyLength;
+
+
+    /**
+     * Creates a new instance of EncryptionKey.
+     */
+    public EncryptionKey()
+    {
+    }
+    
+
+    /**
+     * Creates a new instance of EncryptionKey.
+     *
+     * @param keyType The encryptionType 
+     * @param keyValue The value
+     */
+    public EncryptionKey( EncryptionType keyType, byte[] keyValue )
+    {
+        this.keyType = keyType;
+        this.keyValue = keyValue;
+    }
+
+
+    /**
+     * Creates a new instance of EncryptionKey.
+     *
+     * @param keyType The encryptionType 
+     * @param keyValue The value
+     * @param keyVersion ???
+     */
+    public EncryptionKey( EncryptionType keyType, byte[] keyValue, int keyVersion )
+    {
+        this.keyType = keyType;
+        this.keyValue = keyValue;
+        this.keyVersion = keyVersion;
+    }
+
+
+    /**
+     * Destroys this key by overwriting the symmetric key material with zeros.
+     */
+    public synchronized void destroy()
+    {
+        if ( keyValue != null )
+        {
+            Arrays.fill( keyValue, ( byte ) 0x00 );
+        }
+    }
+
+
+    /**
+     * Returns the key type.
+     *
+     * @return The key type.
+     */
+    public EncryptionType getKeyType()
+    {
+        return keyType;
+    }
+
+
+    /**
+     * Set the encryption type
+     * @param keyType The encryption type
+     */
+    public void setKeyType( EncryptionType keyType ) 
+    {
+        this.keyType = keyType;
+    }
+
+
+    /**
+     * Returns the key value.
+     *
+     * @return The key value.
+     */
+    public byte[] getKeyValue()
+    {
+        return keyValue;
+    }
+
+
+    /**
+     * Returns the key version.
+     *
+     * @return The key version.
+     */
+    public int getKeyVersion()
+    {
+        return keyVersion;
+    }
+
+    
+    /**
+     * Set the key value
+     * @param keyVersion The key version
+     */
+    public void setKeyVersion( int keyVersion)
+    {
+        this.keyVersion = keyVersion;
+    }
+
+    
+    /**
+     * Set the key value
+     * @param keyValue The key value
+     */
+    public void setKeyValue( byte[] keyValue ) 
+    {
+        this.keyValue = keyValue;
+    }
+
+
+    /**
+     * @see Object#equals(Object)
+     */
+    public boolean equals( Object o )
+    {
+        if ( this == o )
+        {
+            return true;
+        }
+
+        if ( ( o == null ) || !( o instanceof EncryptionKey ) )
+        {
+            return false;
+        }
+
+        EncryptionKey that = ( EncryptionKey ) o;
+        return ( this.keyType == that.keyType ) && ( Arrays.equals( this.keyValue, that.keyValue ) );
+    }
+
+
+    /**
+     * Compute the EncryptionKey length
+     * 
+     * EncryptionKey :
+     * 
+     * 0x30 L1 EncryptionKey
+     *  |
+     *  +--> 0xA0 L2 keyType tag
+     *  |     |
+     *  |     +--> 0x02 L2-1 keyType (int)
+     *  |
+     *  +--> 0xA1 L3 keyValue tag
+     *        |
+     *        +--> 0x04 L3-1 keyValue (OCTET STRING)
+     *        
+     *  where L1 = L2 + lenght(0xA0) + length(L2) +
+     *             L3 + lenght(0xA1) + length(L3) 
+     *  and
+     *  L2 = L2-1 + length(0x02) + length( L2-1) 
+     *  L3 = L3-1 + length(0x04) + length( L3-1) 
+     */
+    public int computeLength()
+    {
+        // Compute the keyType. The Length will always be cobntained in 1 byte
+        keyTypeLength = 1 + 1 + Value.getNbBytes( keyType.getOrdinal() );
+        encryptionKeyLength = 1 + TLV.getNbBytes( keyTypeLength ) + keyTypeLength;
+
+        // Compute the keyValue
+        if ( keyValue == null )
+        {
+            keyValueLength = 1 + 1;
+        }
+        else
+        {
+            keyValueLength = 1 + TLV.getNbBytes( keyValue.length ) + keyValue.length;
+        }
+
+        encryptionKeyLength += 1 + TLV.getNbBytes( keyValueLength ) + keyValueLength;
+
+        // Compute the whole sequence length
+        int encryptionKeySeqLength = 1 + Value.getNbBytes( encryptionKeyLength ) + encryptionKeyLength;
+
+        return encryptionKeySeqLength;
+
+    }
+
+
+    /**
+     * Encode the EncryptionKey message to a PDU. 
+     * 
+     * EncryptionKey :
+     * 
+     * 0x30 LL
+     *   0xA0 LL 
+     *     0x02 0x01 keyType
+     *   0xA1 LL 
+     *     0x04 LL keyValue
+     * 
+     * @param buffer The buffer where to put the PDU. It should have been allocated
+     * before, with the right size.
+     * @return The constructed PDU.
+     */
+    public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException
+    {
+        if ( buffer == null )
+        {
+            throw new EncoderException( "Cannot put a PDU in a null buffer !" );
+        }
+
+        try
+        {
+            // The EncryptionKey SEQ Tag
+            buffer.put( UniversalTag.SEQUENCE_TAG );
+            buffer.put( TLV.getBytes( encryptionKeyLength ) );
+
+            // The keyType, first the tag, then the value
+            buffer.put( ( byte ) 0xA0 );
+            buffer.put( TLV.getBytes( keyTypeLength ) );
+            Value.encode( buffer, keyType.getOrdinal() );
+
+            // The keyValue, first the tag, then the value
+            buffer.put( ( byte ) 0xA1 );
+            buffer.put( TLV.getBytes( keyValueLength ) );
+            Value.encode( buffer, keyValue );
+        }
+        catch ( BufferOverflowException boe )
+        {
+            log.error(
+                "Cannot encode the EncryptionKey object, the PDU size is {} when only {} bytes has been allocated", 1
+                    + TLV.getNbBytes( encryptionKeyLength ) + encryptionKeyLength, buffer.capacity() );
+            throw new EncoderException( "The PDU buffer size is too small !" );
+        }
+
+        if ( IS_DEBUG )
+        {
+            log.debug( "EncryptionKey encoding : {}", StringTools.dumpBytes( buffer.array() ) );
+            log.debug( "EncryptionKey initial value : {}", toString() );
+        }
+
+        return buffer;
+    }
+
+
+    /**
+     * @see Object#toString()
+     */
+    public String toString()
+    {
+        return keyType.toString() + " (" + keyType.getOrdinal() + ")";
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/EncryptionTypeInfo2Entry.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/EncryptionTypeInfo2Entry.java
new file mode 100644
index 0000000..3703f9f
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/EncryptionTypeInfo2Entry.java
@@ -0,0 +1,83 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.value;
+
+
+import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 540371 $, $Date: 2007-05-21 17:00:43 -0700 (Mon, 21 May 2007) $
+ */
+public class EncryptionTypeInfo2Entry
+{
+    private EncryptionType encryptionType;
+    private String salt;
+    private byte[] s2kparams;
+
+
+    /**
+     * Creates a new instance of {@link EncryptionTypeInfo2Entry}.
+     *
+     * @param encryptionType
+     * @param salt
+     * @param s2kparams
+     */
+    public EncryptionTypeInfo2Entry( EncryptionType encryptionType, String salt, byte[] s2kparams )
+    {
+        this.encryptionType = encryptionType;
+        this.salt = salt;
+        this.s2kparams = s2kparams;
+    }
+
+
+    /**
+     * Returns the {@link EncryptionType}.
+     *
+     * @return The {@link EncryptionType}.
+     */
+    public EncryptionType getEncryptionType()
+    {
+        return encryptionType;
+    }
+
+
+    /**
+     * Returns the salt.
+     *
+     * @return The salt.
+     */
+    public String getSalt()
+    {
+        return salt;
+    }
+
+
+    /**
+     * Returns the s2kparams.
+     * 
+     * @return The s2kparams.
+     */
+    public byte[] getS2kParams()
+    {
+        return s2kparams;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/EncryptionTypeInfoEntry.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/EncryptionTypeInfoEntry.java
new file mode 100644
index 0000000..89bade1
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/EncryptionTypeInfoEntry.java
@@ -0,0 +1,69 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.value;
+
+
+import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class EncryptionTypeInfoEntry
+{
+    private EncryptionType encryptionType;
+    private byte[] salt;
+
+
+    /**
+     * Creates a new instance of EncryptionTypeInfoEntry.
+     *
+     * @param encryptionType
+     * @param salt
+     */
+    public EncryptionTypeInfoEntry( EncryptionType encryptionType, byte[] salt )
+    {
+        this.encryptionType = encryptionType;
+        this.salt = salt;
+    }
+
+
+    /**
+     * Returns the salt.
+     *
+     * @return The salt.
+     */
+    public byte[] getSalt()
+    {
+        return salt;
+    }
+
+
+    /**
+     * Returns the {@link EncryptionType}.
+     *
+     * @return The {@link EncryptionType}.
+     */
+    public EncryptionType getEncryptionType()
+    {
+        return encryptionType;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/HostAddress.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/HostAddress.java
new file mode 100644
index 0000000..2ccd1d0
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/HostAddress.java
@@ -0,0 +1,333 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.value;
+
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.nio.BufferOverflowException;
+import java.nio.ByteBuffer;
+
+import org.apache.directory.server.kerberos.shared.messages.value.types.HostAddrType;
+import org.apache.directory.shared.asn1.AbstractAsn1Object;
+import org.apache.directory.shared.asn1.ber.tlv.TLV;
+import org.apache.directory.shared.asn1.ber.tlv.UniversalTag;
+import org.apache.directory.shared.asn1.ber.tlv.Value;
+import org.apache.directory.shared.asn1.codec.EncoderException;
+import org.apache.directory.shared.ldap.util.StringTools;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * Provides host address information.
+ * 
+ * The ASN.1 grammaor for this structure is :
+ * 
+ * HostAddress     ::= SEQUENCE  {
+ *        addr-type       [0] Int32,
+ *        address         [1] OCTET STRING
+ * }
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class HostAddress extends AbstractAsn1Object
+{
+    /** The logger */
+    private static final Logger LOG = LoggerFactory.getLogger( HostAddress.class );
+
+    /** Speedup for logs */
+    private static final boolean IS_DEBUG = LOG.isDebugEnabled();
+
+    /** The host address type. One of :
+     *    Address Type                   Value
+     *
+     *    IPv4                             2
+     *    Directional                      3
+     *    ChaosNet                         5
+     *    XNS                              6
+     *    ISO                              7
+     *    DECNET Phase IV                 12
+     *    AppleTalk DDP                   16
+     *    NetBios                         20
+     *    IPv6                            24
+     */
+    private HostAddrType addrType;
+
+    /** The address */
+    private byte[] address;
+
+    // Storage for computed lengths
+    private transient int addrTypeLength;
+    private transient int addressLength;
+    private transient int hostAddressLength;
+    private transient int hostAddressSeqLength;
+
+
+    /**
+     * Creates a new instance of HostAddress.
+     *
+     * @param addrType
+     * @param address
+     */
+    public HostAddress( HostAddrType addrType, byte[] address )
+    {
+        this.addrType = addrType;
+        this.address = address;
+    }
+
+
+    /**
+     * Creates a new instance of HostAddress.
+     *
+     * @param internetAddress
+     */
+    public HostAddress( InetAddress internetAddress )
+    {
+        addrType = HostAddrType.ADDRTYPE_INET;
+        byte[] newAddress = internetAddress.getAddress();
+        address = new byte[newAddress.length];
+        System.arraycopy( newAddress, 0, address, 0, newAddress.length );
+    }
+
+
+    /**
+     * Returns whether one {@link HostAddress} is equal to another.
+     *
+     * @param that The {@link HostAddress} to compare with
+     * @return true if the {@link HostAddress}'s are equal.
+     */
+    public boolean equals( Object that )
+    {
+        if ( this == that )
+        {
+            return true;
+        }
+        
+        if ( !(that instanceof HostAddress ) )
+        {
+            return false;
+        }
+        
+        HostAddress hostAddress = (HostAddress)that;
+        
+        if ( addrType != hostAddress.addrType || ( address != null && hostAddress.address == null )
+            || ( address == null && hostAddress.address != null ) )
+        {
+            return false;
+        }
+
+        if ( address != null && hostAddress.address != null )
+        {
+            if ( address.length != hostAddress.address.length )
+            {
+                return false;
+            }
+
+            for ( int ii = 0; ii < address.length; ii++ )
+            {
+                if ( address[ii] != hostAddress.address[ii] )
+                {
+                    return false;
+                }
+            }
+        }
+
+        return true;
+    }
+
+
+    /**
+     * Get the bytes for this address.
+     *
+     * @return The bytes of this address.
+     */
+    public byte[] getAddress()
+    {
+        return address;
+    }
+
+
+    /**
+     * Set the address 
+     *
+     * @param addresse The address
+     */
+    public void setAddress( byte[] addresse )
+    {
+        this.address = addresse;
+    }
+
+
+    /**
+     * Compute the host address length
+     * 
+     * HostAddress :
+     * 
+     * 0x30 L1 hostAddress sequence
+     *  |
+     *  +--> 0xA0 L2 addrType tag
+     *  |     |
+     *  |     +--> 0x02 L2-1 addrType (int)
+     *  |
+     *  +--> 0xA1 L3 address tag
+     *        |
+     *        +--> 0x04 L3-1 address (OCTET STRING)
+     *        
+     *  where L1 = L2 + length(0xA0) + length(L2) +
+     *             L3 + length(0xA1) + length(L3) 
+     *  and
+     *  L2 = L2-1 + length(0x02) + length( L2-1) 
+     *  L3 = L3-1 + length(0x04) + length( L3-1) 
+     */
+    public int computeLength()
+    {
+        // Compute the keyType. The Length will always be contained in 1 byte
+        addrTypeLength = 1 + 1 + Value.getNbBytes( addrType.getOrdinal() );
+        hostAddressLength = 1 + TLV.getNbBytes( addrTypeLength ) + addrTypeLength;
+
+        // Compute the keyValue
+        if ( address == null )
+        {
+            addressLength = 1 + 1;
+        }
+        else
+        {
+            addressLength = 1 + TLV.getNbBytes( address.length ) + address.length;
+        }
+
+        hostAddressLength += 1 + TLV.getNbBytes( addressLength ) + addressLength;
+
+        // Compute the whole sequence length
+        hostAddressSeqLength = 1 + Value.getNbBytes( hostAddressLength ) + hostAddressLength;
+
+        return hostAddressSeqLength;
+    }
+
+
+    /**
+     * Encode the HostAddress message to a PDU. 
+     * 
+     * HostAddress :
+     * 
+     * 0x30 LL
+     *   0xA0 LL 
+     *     0x02 0x01 addr-type
+     *   0xA1 LL 
+     *     0x04 LL address
+     * 
+     * @param buffer The buffer where to put the PDU. It should have been allocated
+     * before, with the right size.
+     * @return The constructed PDU.
+     */
+    public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException
+    {
+        if ( buffer == null )
+        {
+            throw new EncoderException( "Cannot put a PDU in a null buffer !" );
+        }
+
+        try
+        {
+            // The HostAddress SEQ Tag
+            buffer.put( UniversalTag.SEQUENCE_TAG );
+            buffer.put( TLV.getBytes( hostAddressLength ) );
+
+            // The addr-type, first the tag, then the value
+            buffer.put( ( byte ) 0xA0 );
+            buffer.put( TLV.getBytes( addrTypeLength ) );
+            Value.encode( buffer, addrType.getOrdinal() );
+
+            // The address, first the tag, then the value
+            buffer.put( ( byte ) 0xA1 );
+            buffer.put( TLV.getBytes( addressLength ) );
+            Value.encode( buffer, address );
+        }
+        catch ( BufferOverflowException boe )
+        {
+            LOG.error(
+                "Cannot encode the HostAddress object, the PDU size is {} when only {} bytes has been allocated", 1
+                    + TLV.getNbBytes( hostAddressLength ) + hostAddressLength, buffer.capacity() );
+            throw new EncoderException( "The PDU buffer size is too small !" );
+        }
+
+        if ( IS_DEBUG )
+        {
+            LOG.debug( "Checksum encoding : {}", StringTools.dumpBytes( buffer.array() ) );
+            LOG.debug( "Checksum initial value : {}", toString() );
+        }
+
+        return buffer;
+    }
+
+
+    /**
+     * Returns the {@link HostAddrType} of this {@link HostAddress}.
+     *
+     * @return The {@link HostAddrType}.
+     */
+    public HostAddrType getAddrType()
+    {
+        return addrType;
+    }
+
+
+    /**
+     * Set the addr-type field
+     *
+     * @param addrType The address type
+     */
+    public void setAddrType( HostAddrType addrType )
+    {
+        this.addrType = addrType;
+    }
+
+
+    /**
+     * Set the addr-type field
+     *
+     * @param addrType The address type
+     */
+    public void setAddrType( int addrType )
+    {
+        this.addrType = HostAddrType.getTypeByOrdinal( addrType );
+    }
+
+
+    /**
+     * @see Object#toString()
+     */
+    public String toString()
+    {
+        String result = "";
+
+        try
+        {
+            result = InetAddress.getByAddress( address ).getHostAddress();
+        }
+        catch ( UnknownHostException uhe )
+        {
+            // Allow default to return.
+        }
+
+        return result;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/HostAddresses.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/HostAddresses.java
new file mode 100644
index 0000000..f1b2981
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/HostAddresses.java
@@ -0,0 +1,279 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.value;
+
+
+import java.nio.BufferOverflowException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.directory.shared.asn1.AbstractAsn1Object;
+import org.apache.directory.shared.asn1.ber.tlv.TLV;
+import org.apache.directory.shared.asn1.ber.tlv.UniversalTag;
+import org.apache.directory.shared.asn1.codec.EncoderException;
+import org.apache.directory.shared.ldap.util.StringTools;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * Store a list of addresses.
+ * 
+ * The ASN.1 grammar is :
+ * 
+ * -- NOTE: HostAddresses is always used as an OPTIONAL field and
+ * -- should not be empty.
+ * HostAddresses   -- NOTE: subtly different from rfc1510,
+ *                 -- but has a value mapping and encodes the same
+ *         ::= SEQUENCE OF HostAddress
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class HostAddresses extends AbstractAsn1Object
+{
+    /** The logger */
+    private static final Logger LOG = LoggerFactory.getLogger( HostAddresses.class );
+
+    /** Speedup for logs */
+    private static final boolean IS_DEBUG = LOG.isDebugEnabled();
+
+    /** List of all HostAddress stored */
+    private List<HostAddress> addresses;
+
+    // Storage for computed lengths
+    private transient int addressesLength;
+
+
+    /**
+     * Creates a new instance of HostAddresses.
+     */
+    public HostAddresses()
+    {
+        this.addresses = new ArrayList<HostAddress>();
+    }
+    
+
+    /**
+     * Creates a new instance of HostAddresses.
+     *
+     * @param addresses The associated addresses
+     */
+    public HostAddresses( HostAddress[] addresses )
+    {
+        if ( addresses == null )
+        {
+            this.addresses = new ArrayList<HostAddress>();
+        }
+        else
+        {
+            this.addresses = Arrays.asList( addresses );
+        }
+    }
+
+
+    public void addHostAddress( HostAddress hostAddress )
+    {
+        addresses.add( hostAddress );
+    }
+
+
+    /**
+     * Returns true if this {@link HostAddresses} contains a specified {@link HostAddress}.
+     *
+     * @param address
+     * @return true if this {@link HostAddresses} contains a specified {@link HostAddress}.
+     */
+    public boolean contains( HostAddress address )
+    {
+        if ( addresses != null )
+        {
+            return addresses.contains( address );
+        }
+
+        return false;
+    }
+
+
+    /**
+     * Returns true if two {@link HostAddresses} are equal.
+     *
+     * @param that
+     * @return true if two {@link HostAddresses} are equal.
+     */
+    public boolean equals( HostAddresses that )
+    {
+        if ( ( addresses == null && that.addresses != null )
+            || ( addresses != null && that.addresses == null ) )
+        {
+            return false;
+        }
+
+        if ( addresses != null && that.addresses != null )
+        {
+            if ( addresses.size() != that.addresses.size() )
+            {
+                return false;
+            }
+
+            HostAddress[] thisHostAddresses = ( HostAddress[] ) addresses.toArray();
+            HostAddress[] thatHostAddresses = ( HostAddress[] ) that.addresses.toArray();
+
+            for ( int i = 0; i < thisHostAddresses.length; i++ )
+            {
+                if ( !thisHostAddresses[i].equals( thatHostAddresses[i] ) )
+                {
+                    return false;
+                }
+            }
+        }
+
+        return true;
+    }
+
+
+    /**
+     * Returns the contained {@link HostAddress}s as an array.
+     *
+     * @return An array of {@link HostAddress}s.
+     */
+    public HostAddress[] getAddresses()
+    {
+        return ( HostAddress[] ) addresses.toArray();
+    }
+
+
+    /**
+     * Compute the hostAddresses length
+     * 
+     * HostAddresses :
+     * 
+     * 0x30 L1 hostAddresses sequence of HostAddresses
+     *  |
+     *  +--> 0x30 L2[1] Hostaddress[1]
+     *  |
+     *  +--> 0x30 L2[2] Hostaddress[2]
+     *  |
+     *  ...
+     *  |
+     *  +--> 0x30 L2[n] Hostaddress[n]
+     *        
+     *  where L1 = sum( L2[1], l2[2], ..., L2[n] )
+     */
+    public int computeLength()
+    {
+        // Compute the addresses length.
+        addressesLength = 0;
+
+        if ( ( addresses != null ) && ( addresses.size() != 0 ) )
+        {
+            for ( HostAddress hostAddress : addresses )
+            {
+                int length = hostAddress.computeLength();
+                addressesLength += length;
+            }
+        }
+
+        return 1 + TLV.getNbBytes( addressesLength ) + addressesLength;
+    }
+
+
+    /**
+     * Encode the HostAddress message to a PDU. 
+     * 
+     * HostAddress :
+     * 
+     * 0x30 LL
+     *   0x30 LL hostaddress[1] 
+     *   0x30 LL hostaddress[1]
+     *   ... 
+     *   0x30 LL hostaddress[1] 
+     * 
+     * @param buffer The buffer where to put the PDU. It should have been allocated
+     * before, with the right size.
+     * @return The constructed PDU.
+     */
+    public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException
+    {
+        if ( buffer == null )
+        {
+            throw new EncoderException( "Cannot put a PDU in a null buffer !" );
+        }
+
+        try
+        {
+            // The HostAddresses SEQ Tag
+            buffer.put( UniversalTag.SEQUENCE_TAG );
+            buffer.put( TLV.getBytes( addressesLength ) );
+
+            // The hostAddress list, if it's not empty
+            if ( ( addresses != null ) && ( addresses.size() != 0 ) )
+            {
+                for ( HostAddress hostAddress : addresses )
+                {
+                    hostAddress.encode( buffer );
+                }
+            }
+        }
+        catch ( BufferOverflowException boe )
+        {
+            LOG.error(
+                "Cannot encode the HostAddresses object, the PDU size is {} when only {} bytes has been allocated", 1
+                    + TLV.getNbBytes( addressesLength ) + addressesLength, buffer.capacity() );
+            throw new EncoderException( "The PDU buffer size is too small !" );
+        }
+
+        if ( IS_DEBUG )
+        {
+            LOG.debug( "HostAddresses encoding : {}", StringTools.dumpBytes( buffer.array() ) );
+            LOG.debug( "HostAddresses initial value : {}", toString() );
+        }
+
+        return buffer;
+    }
+
+
+    /**
+     * @see Object#toString()
+     */
+    public String toString()
+    {
+        StringBuilder sb = new StringBuilder();
+        boolean isFirst = true;
+
+        for ( HostAddress hostAddress : addresses )
+        {
+            if ( isFirst )
+            {
+                isFirst = false;
+            }
+            else
+            {
+                sb.append( ", " );
+            }
+
+            sb.append( hostAddress.toString() );
+        }
+
+        return sb.toString();
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/KdcOptions.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/KdcOptions.java
new file mode 100644
index 0000000..79b30e3
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/KdcOptions.java
@@ -0,0 +1,225 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.value;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class KdcOptions extends Options
+{
+    /**
+     * KDC option - reserved.
+     */
+    public static final int RESERVED = 0;
+
+    /**
+     * KDC option - forwardable.
+     */
+    public static final int FORWARDABLE = 1;
+
+    /**
+     * KDC option - forwarded.
+     */
+    public static final int FORWARDED = 2;
+
+    /**
+     * KDC option - proxiable.
+     */
+    public static final int PROXIABLE = 3;
+
+    /**
+     * KDC option - proxy.
+     */
+    public static final int PROXY = 4;
+
+    /**
+     * KDC option - allow postdate.
+     */
+    public static final int ALLOW_POSTDATE = 5;
+
+    /**
+     * KDC option - postdated.
+     */
+    public static final int POSTDATED = 6;
+
+    /**
+     * KDC option - unused7.
+     */
+    public static final int UNUSED7 = 7;
+
+    /**
+     * KDC option - renewable.
+     */
+    public static final int RENEWABLE = 8;
+
+    /**
+     * KDC option - unused9.
+     */
+    public static final int UNUSED9 = 9;
+
+    /**
+     * KDC option - unused10.
+     */
+    public static final int UNUSED10 = 10;
+
+    /**
+     * KDC option - unused11.
+     */
+    public static final int UNUSED11 = 11;
+
+    /**
+     * KDC option - unused12.
+     */
+    public static final int UNUSED12 = 12;
+
+    /**
+     * KDC option - unused13.
+     */
+    public static final int UNUSED13 = 13;
+
+    /**
+     * KDC option - disable transisted checked.
+     */
+    public static final int DISABLE_TRANSISTED_CHECKED = 26;
+
+    /**
+     * KDC option - renewable is ok.
+     */
+    public static final int RENEWABLE_OK = 27;
+
+    /**
+     * KDC option - encrypted key in skey.
+     */
+    public static final int ENC_TKT_IN_SKEY = 28;
+
+    /**
+     * KDC option - renew.
+     */
+    public static final int RENEW = 30;
+
+    /**
+     * KDC option - validate.
+     */
+    public static final int VALIDATE = 31;
+
+    /**
+     * KDC option - maximum value.
+     */
+    public static final int MAX_VALUE = 32;
+
+
+    /**
+     * Creates a new instance of KdcOptions.
+     */
+    public KdcOptions()
+    {
+        super( MAX_VALUE );
+    }
+
+
+    /**
+     * Creates a new instance of KdcOptions.
+     *
+     * @param bytes
+     */
+    public KdcOptions( byte[] bytes )
+    {
+        super( MAX_VALUE );
+        setBytes( bytes );
+    }
+
+
+    /**
+     * Converts the object to a printable string.
+     */
+    public String toString()
+    {
+        StringBuffer result = new StringBuffer();
+
+        if ( get( ALLOW_POSTDATE ) )
+        {
+            result.append( "ALLOW_POSTDATE " );
+        }
+
+        if ( get( DISABLE_TRANSISTED_CHECKED ) )
+        {
+            result.append( "DISABLE_TRANSISTED_CHECKED " );
+        }
+
+        if ( get( ENC_TKT_IN_SKEY ) )
+        {
+            result.append( "ENC_TKT_IN_SKEY " );
+        }
+
+        if ( get( FORWARDABLE ) )
+        {
+            result.append( "FORWARDABLE " );
+        }
+
+        if ( get( FORWARDED ) )
+        {
+            result.append( "FORWARDED " );
+        }
+
+        if ( get( POSTDATED ) )
+        {
+            result.append( "POSTDATED " );
+        }
+
+        if ( get( PROXIABLE ) )
+        {
+            result.append( "PROXIABLE " );
+        }
+
+        if ( get( PROXY ) )
+        {
+            result.append( "PROXY " );
+        }
+
+        if ( get( RENEW ) )
+        {
+            result.append( "RENEW " );
+        }
+
+        if ( get( RENEWABLE ) )
+        {
+            result.append( "RENEWABLE " );
+        }
+
+        if ( get( RENEWABLE_OK ) )
+        {
+            result.append( "RENEWABLE_OK " );
+        }
+
+        if ( get( RESERVED ) )
+        {
+            result.append( "RESERVED " );
+        }
+
+        if ( get( VALIDATE ) )
+        {
+            result.append( "VALIDATE " );
+        }
+
+        return result.toString().trim();
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/KerberosPrincipalModifier.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/KerberosPrincipalModifier.java
new file mode 100644
index 0000000..fdaaa76
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/KerberosPrincipalModifier.java
@@ -0,0 +1,83 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.value;
+
+
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class KerberosPrincipalModifier
+{
+    private static final String REALM_SEPARATOR = "@";
+
+    PrincipalName nameComponent;
+    String realm;
+
+
+    /**
+     * Returns the {@link KerberosPrincipal}.
+     *
+     * @return The {@link KerberosPrincipal}.
+     */
+    public KerberosPrincipal getKerberosPrincipal()
+    {
+        if ( nameComponent != null )
+        {
+            StringBuffer sb = new StringBuffer();
+            sb.append( nameComponent.getNameString() );
+
+            if ( realm != null )
+            {
+                sb.append( REALM_SEPARATOR );
+                sb.append( realm );
+            }
+
+            return new KerberosPrincipal( sb.toString(), nameComponent.getNameType().getOrdinal() );
+        }
+
+        return null;
+    }
+
+
+    /**
+     * Sets the {@link PrincipalName}.
+     *
+     * @param principalName
+     */
+    public void setPrincipalName( PrincipalName principalName )
+    {
+        nameComponent = principalName;
+    }
+
+
+    /**
+     * Sets the realm.
+     *
+     * @param realm
+     */
+    public void setRealm( String realm )
+    {
+        this.realm = realm;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/KerberosTime.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/KerberosTime.java
new file mode 100644
index 0000000..a6ea80e
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/KerberosTime.java
@@ -0,0 +1,226 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.value;
+
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.TimeZone;
+
+
+/**
+ * Implementation of the time object for Kerberos.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class KerberosTime implements Comparable<KerberosTime>
+{
+    /** The number of milliseconds in a minute. */
+    public static final int MINUTE = 60000;
+
+    /** The number of milliseconds in a day. */
+    public static final int DAY = MINUTE * 1440;
+
+    /** The number of milliseconds in a week. */
+    public static final int WEEK = MINUTE * 10080;
+
+    /** Constant for the {@link KerberosTime} "infinity." */
+    public static final KerberosTime INFINITY = new KerberosTime( Long.MAX_VALUE );
+
+    private static final TimeZone UTC_TIME_ZONE = TimeZone.getTimeZone( "UTC" );
+    private static final SimpleDateFormat dateFormat = new SimpleDateFormat( "yyyyMMddHHmmss'Z'" );
+
+    static
+    {
+        dateFormat.setTimeZone( UTC_TIME_ZONE );
+    }
+
+    private long kerberosTime;
+
+
+    /**
+     * Creates a new instance of KerberosTime.
+     */
+    public KerberosTime()
+    {
+        kerberosTime = System.currentTimeMillis();
+    }
+
+
+    /**
+     * Creates a new instance of KerberosTime.
+     *
+     * @param time
+     */
+    public KerberosTime( long time )
+    {
+        kerberosTime = time;
+    }
+
+
+    /**
+     * Creates a new instance of KerberosTime.
+     *
+     * @param time
+     */
+    public KerberosTime( Date time )
+    {
+        kerberosTime = time.getTime();
+    }
+
+
+    /**
+     * Returns the {@link KerberosTime} for a given zulu time.
+     *
+     * @param zuluTime
+     * @return The {@link KerberosTime}.
+     * @throws ParseException
+     */
+    public static KerberosTime getTime( String zuluTime ) throws ParseException
+    {
+        Date date = null;
+        synchronized ( dateFormat )
+        {
+            date = dateFormat.parse( zuluTime );
+        }
+        return new KerberosTime( date );
+    }
+
+
+    public int compareTo( KerberosTime that )
+    {
+        final int BEFORE = -1;
+        final int EQUAL = 0;
+        final int AFTER = 1;
+
+        // this optimization is usually worthwhile, and can always be added
+        if ( this == that )
+        {
+            return EQUAL;
+        }
+
+        // primitive numbers follow this form
+        if ( this.kerberosTime < that.kerberosTime )
+        {
+            return BEFORE;
+        }
+
+        if ( this.kerberosTime > that.kerberosTime )
+        {
+            return AFTER;
+        }
+
+        return EQUAL;
+    }
+
+
+    /**
+     * Returns the {@link KerberosTime} as a long.
+     *
+     * @return The {@link KerberosTime} as a long.
+     */
+    public long getTime()
+    {
+        return kerberosTime;
+    }
+
+
+    /**
+     * Returns the {@link KerberosTime} as a {@link Date}.
+     *
+     * @return The {@link KerberosTime} as a {@link Date}.
+     */
+    public Date toDate()
+    {
+        return new Date( kerberosTime );
+    }
+
+
+    /**
+     * Returns whether this {@link KerberosTime} is within the given clockskew.
+     *
+     * @param clockSkew
+     * @return true if this {@link KerberosTime} is within the given clockskew.
+     */
+    public boolean isInClockSkew( long clockSkew )
+    {
+        return Math.abs( kerberosTime - System.currentTimeMillis() ) < clockSkew;
+    }
+
+
+    /**
+     * Returns whether this {@link KerberosTime} is greater than a given {@link KerberosTime}.
+     *
+     * @param time
+     * @return true if this {@link KerberosTime} is greater than a given {@link KerberosTime}. 
+     */
+    public boolean greaterThan( KerberosTime time )
+    {
+        return kerberosTime > time.kerberosTime;
+    }
+
+
+    /**
+     * Returns whether this {@link KerberosTime} is less than a given {@link KerberosTime}.
+     *
+     * @param time
+     * @return true if this {@link KerberosTime} is less than a given {@link KerberosTime}. 
+     */
+    public boolean lessThan( KerberosTime time )
+    {
+        return kerberosTime < time.kerberosTime;
+    }
+
+
+    /**
+     * Returns whether this {@link KerberosTime} is equal to another {@link KerberosTime}.
+     *
+     * @param time
+     * @return true if the two {@link KerberosTime}s are equal.
+     */
+    public boolean equals( KerberosTime time )
+    {
+        return kerberosTime == time.kerberosTime;
+    }
+
+
+    /**
+     * Returns whether this {@link KerberosTime} is zero.
+     *
+     * @return true if this {@link KerberosTime} is zero.
+     */
+    public boolean isZero()
+    {
+        return kerberosTime == 0;
+    }
+
+
+    public String toString()
+    {
+        Date kerberosDate = new Date( kerberosTime );
+
+        synchronized ( dateFormat )
+        {
+            return dateFormat.format( kerberosDate );
+        }
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/KrbCredInfo.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/KrbCredInfo.java
new file mode 100644
index 0000000..8493052
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/KrbCredInfo.java
@@ -0,0 +1,173 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.value;
+
+
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.apache.directory.server.kerberos.shared.messages.value.flags.TicketFlags;
+
+
+/**
+ * Kerberos credential information.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class KrbCredInfo
+{
+    private EncryptionKey key;
+    private KerberosPrincipal clientPrincipal; //optional
+    private TicketFlags flags; //optional
+    private KerberosTime authTime; //optional
+    private KerberosTime startTime; //optional
+    private KerberosTime endTime; //optional
+    private KerberosTime renewTill; //optional
+    private KerberosPrincipal serverPrincipal; //optional
+    private HostAddresses clientAddresses; //optional
+
+
+    /**
+     * Creates a new instance of KrbCredInfo.
+     *
+     * @param key
+     * @param clientPrincipal
+     * @param flags
+     * @param authTime
+     * @param startTime
+     * @param endTime
+     * @param renewTill
+     * @param serverPrincipal
+     * @param clientAddresses
+     */
+    public KrbCredInfo( EncryptionKey key, KerberosPrincipal clientPrincipal, TicketFlags flags, KerberosTime authTime,
+        KerberosTime startTime, KerberosTime endTime, KerberosTime renewTill, KerberosPrincipal serverPrincipal,
+        HostAddresses clientAddresses )
+    {
+        this.key = key;
+        this.clientPrincipal = clientPrincipal;
+        this.flags = flags;
+        this.authTime = authTime;
+        this.startTime = startTime;
+        this.endTime = endTime;
+        this.renewTill = renewTill;
+        this.serverPrincipal = serverPrincipal;
+        this.clientAddresses = clientAddresses;
+    }
+
+
+    /**
+     * Returns the auth {@link KerberosTime}.
+     *
+     * @return The auth {@link KerberosTime}.
+     */
+    public KerberosTime getAuthTime()
+    {
+        return authTime;
+    }
+
+
+    /**
+     * Returns the client {@link HostAddresses}.
+     *
+     * @return The client {@link HostAddresses}.
+     */
+    public HostAddresses getClientAddresses()
+    {
+        return clientAddresses;
+    }
+
+
+    /**
+     * Returns the end {@link KerberosTime}.
+     *
+     * @return The end {@link KerberosTime}.
+     */
+    public KerberosTime getEndTime()
+    {
+        return endTime;
+    }
+
+
+    /**
+     * Returns the {@link TicketFlags}.
+     *
+     * @return The {@link TicketFlags}.
+     */
+    public TicketFlags getFlags()
+    {
+        return flags;
+    }
+
+
+    /**
+     * Returns the {@link EncryptionKey}.
+     *
+     * @return The {@link EncryptionKey}.
+     */
+    public EncryptionKey getKey()
+    {
+        return key;
+    }
+
+
+    /**
+     * Returns the client {@link KerberosPrincipal}.
+     *
+     * @return The client {@link KerberosPrincipal}.
+     */
+    public KerberosPrincipal getClientPrincipal()
+    {
+        return clientPrincipal;
+    }
+
+
+    /**
+     * Returns the renew till {@link KerberosTime}.
+     *
+     * @return The renew till {@link KerberosTime}.
+     */
+    public KerberosTime getRenewTill()
+    {
+        return renewTill;
+    }
+
+
+    /**
+     * Returns the server {@link KerberosPrincipal}.
+     *
+     * @return The server {@link KerberosPrincipal}.
+     */
+    public KerberosPrincipal getServerPrincipal()
+    {
+        return serverPrincipal;
+    }
+
+
+    /**
+     * Returns the start {@link KerberosTime}.
+     *
+     * @return The start {@link KerberosTime}.
+     */
+    public KerberosTime getStartTime()
+    {
+        return startTime;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/LastRequest.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/LastRequest.java
new file mode 100644
index 0000000..db79de8
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/LastRequest.java
@@ -0,0 +1,61 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.value;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class LastRequest
+{
+    private LastRequestEntry[] entries = new LastRequestEntry[1];
+
+
+    /**
+     * Creates a new instance of LastRequest.
+     */
+    public LastRequest()
+    {
+        entries[0] = new LastRequestEntry( LastRequestType.NONE, new KerberosTime() );
+    }
+
+
+    /**
+     * Creates a new instance of LastRequest.
+     *
+     * @param entries
+     */
+    public LastRequest( LastRequestEntry[] entries )
+    {
+        this.entries = entries;
+    }
+
+
+    /**
+     * Returns an array of {@link LastRequestEntry}s.
+     *
+     * @return The array of {@link LastRequestEntry}s.
+     */
+    public LastRequestEntry[] getEntries()
+    {
+        return entries;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/LastRequestEntry.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/LastRequestEntry.java
new file mode 100644
index 0000000..0221b3b
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/LastRequestEntry.java
@@ -0,0 +1,66 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.value;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class LastRequestEntry
+{
+    private LastRequestType lastRequestType;
+    private KerberosTime lastRequestValue;
+
+
+    /**
+     * Creates a new instance of LastRequestEntry.
+     *
+     * @param type
+     * @param value
+     */
+    public LastRequestEntry( LastRequestType type, KerberosTime value )
+    {
+        lastRequestType = type;
+        lastRequestValue = value;
+    }
+
+
+    /**
+     * Returns the {@link LastRequestType}.
+     *
+     * @return The {@link LastRequestType}.
+     */
+    public LastRequestType getLastRequestType()
+    {
+        return lastRequestType;
+    }
+
+
+    /**
+     * Returns the {@link KerberosTime} of the last request.
+     *
+     * @return The {@link KerberosTime} of the last request.
+     */
+    public KerberosTime getLastRequestValue()
+    {
+        return lastRequestValue;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/LastRequestType.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/LastRequestType.java
new file mode 100644
index 0000000..90377fc
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/LastRequestType.java
@@ -0,0 +1,145 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.value;
+
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.directory.shared.ldap.constants.AuthenticationLevel;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public final class LastRequestType implements Comparable<LastRequestType>
+{
+    /**
+     * Constant for the "none" last request type.
+     */
+    public static final LastRequestType NONE = new LastRequestType( 0, AuthenticationLevel.NONE.toString() );
+
+    /**
+     * Constant for the "time of initial ticket" last request type.
+     */
+    public static final LastRequestType TIME_OF_INITIAL_TGT = new LastRequestType( 1, "time of initial ticket" );
+
+    /**
+     * Constant for the "time of initial request" last request type.
+     */
+    public static final LastRequestType TIME_OF_INITIAL_REQ = new LastRequestType( 2, "time of initial request" );
+
+    /**
+     * Constant for the "time of newest ticket" last request type.
+     */
+    public static final LastRequestType TIME_OF_NEWEST_TGT = new LastRequestType( 3, "time of newest ticket" );
+
+    /**
+     * Constant for the "time of last renewal" last request type.
+     */
+    public static final LastRequestType TIME_OF_LAST_RENEWAL = new LastRequestType( 4, "time of last renewal" );
+
+    /**
+     * Constant for the "time of last request" last request type.
+     */
+    public static final LastRequestType TIME_OF_LAST_REQ = new LastRequestType( 5, "time of last request" );
+
+    /**
+     * Constant for the "time of password expiration" last request type.
+     */
+    public static final LastRequestType TIME_OF_PASSWORD_EXP = new LastRequestType( 6, "time of password expiration" );
+
+    /**
+     * Array for building a List of VALUES.
+     */
+    private static final LastRequestType[] values =
+        { NONE, TIME_OF_INITIAL_TGT, TIME_OF_INITIAL_REQ, TIME_OF_NEWEST_TGT, TIME_OF_LAST_RENEWAL, TIME_OF_LAST_REQ,
+            TIME_OF_PASSWORD_EXP };
+
+    /**
+     * A List of all the last request type constants.
+     */
+    public static final List<LastRequestType> VALUES = Collections.unmodifiableList( Arrays.asList( values ) );
+
+    /**
+     * The name of the checksum type.
+     */
+    private final String name;
+
+    /**
+     * The value/code for the checksum type.
+     */
+    private final int ordinal;
+
+
+    /**
+     * Private constructor prevents construction outside of this class.
+     */
+    private LastRequestType( int ordinal, String name )
+    {
+        this.ordinal = ordinal;
+        this.name = name;
+    }
+
+
+    /**
+     * Returns the last request type when specified by its ordinal.
+     *
+     * @param type
+     * @return The last request type.
+     */
+    public static LastRequestType getTypeByOrdinal( int type )
+    {
+        for ( int ii = 0; ii < values.length; ii++ )
+        {
+            if ( values[ii].ordinal == type )
+            {
+                return values[ii];
+            }
+        }
+
+        return NONE;
+    }
+
+
+    /**
+     * Returns the number associated with this last request type.
+     *
+     * @return The last request type ordinal.
+     */
+    public int getOrdinal()
+    {
+        return ordinal;
+    }
+
+
+    public int compareTo( LastRequestType that )
+    {
+        return ordinal - that.ordinal;
+    }
+
+
+    public String toString()
+    {
+        return name + " (" + ordinal + ")";
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/Options.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/Options.java
new file mode 100644
index 0000000..553918f
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/Options.java
@@ -0,0 +1,128 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.value;
+
+
+import java.util.BitSet;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public abstract class Options
+{
+    private BitSet options;
+    private int maxSize;
+
+
+    protected Options( int maxSize )
+    {
+        this.maxSize = maxSize;
+        options = new BitSet( maxSize );
+    }
+
+
+    /**
+     * Returns whether the option at a given index matches the option in this {@link Options}.
+     *
+     * @param options
+     * @param option
+     * @return true if two options are the same.
+     */
+    public boolean match( Options options, int option )
+    {
+        return options.get( option ) == this.get( option );
+    }
+
+
+    /**
+     * Returns the value of the option at the given index.
+     *
+     * @param index
+     * @return true if the option at the given index is set.
+     */
+    public boolean get( int index )
+    {
+        return options.get( index );
+    }
+
+
+    /**
+     * Sets the option at a given index.
+     *
+     * @param index
+     */
+    public void set( int index )
+    {
+        options.set( index );
+    }
+
+
+    /**
+     * Clears (sets false) the option at a given index.
+     *
+     * @param index
+     */
+    public void clear( int index )
+    {
+        options.clear( index );
+    }
+
+
+    /**
+     * Byte-reversing methods are an anomaly of the BouncyCastle
+     * DERBitString endianness.  Thes methods can be removed if the
+     * Apache Directory Snickers codecs operate differently.
+     * 
+     * @return The raw {@link Options} bytes.
+     */
+    public byte[] getBytes()
+    {
+        byte[] bytes = new byte[maxSize / 8];
+
+        for ( int ii = 0; ii < maxSize; ii++ )
+        {
+            if ( options.get( reversePosition( ii ) ) )
+            {
+                bytes[bytes.length - ii / 8 - 1] |= 1 << ( ii % 8 );
+            }
+        }
+        return bytes;
+    }
+
+
+    protected void setBytes( byte[] bytes )
+    {
+        for ( int ii = 0; ii < bytes.length * 8; ii++ )
+        {
+            if ( ( bytes[bytes.length - ii / 8 - 1] & ( 1 << ( ii % 8 ) ) ) > 0 )
+            {
+                options.set( reversePosition( ii ) );
+            }
+        }
+    }
+
+
+    private int reversePosition( int position )
+    {
+        return maxSize - 1 - position;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/PaData.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/PaData.java
new file mode 100644
index 0000000..7a5a18e
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/PaData.java
@@ -0,0 +1,273 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.value;
+
+
+import java.nio.BufferOverflowException;
+import java.nio.ByteBuffer;
+
+import org.apache.directory.server.kerberos.shared.messages.value.types.PaDataType;
+import org.apache.directory.shared.asn1.AbstractAsn1Object;
+import org.apache.directory.shared.asn1.ber.tlv.TLV;
+import org.apache.directory.shared.asn1.ber.tlv.UniversalTag;
+import org.apache.directory.shared.asn1.ber.tlv.Value;
+import org.apache.directory.shared.asn1.codec.EncoderException;
+import org.apache.directory.shared.ldap.util.StringTools;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * The Pre-Authentication data. Tha ASN.1 GRAMMAR IS :
+ * 
+ * PA-DATA         ::= SEQUENCE {
+ *         -- NOTE: first tag is [1], not [0]
+ *         padata-type     [1] Int32,
+ *         padata-value    [2] OCTET STRING -- might be encoded AP-REQ
+ * }
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class PaData extends AbstractAsn1Object
+{
+    /** The logger */
+    private static final Logger log = LoggerFactory.getLogger( PaData.class );
+
+    /** Speedup for logs */
+    private static final boolean IS_DEBUG = log.isDebugEnabled();
+
+    /** The Pre-authentication type */
+    private PaDataType paDataType;
+    
+    /** The authentication data */
+    private byte[] paDataValue;
+
+    // Storage for computed lengths
+    private transient int paDataTypeTagLength;
+    private transient int paDataValueTagLength;
+    private transient int preAuthenticationDataSeqLength;
+    
+
+    /**
+     * Creates a new instance of PreAuthenticationData.
+     */
+    public PaData()
+    {
+    }
+
+    
+    /**
+     * Creates a new instance of PreAuthenticationData.
+     *
+     * @param paDataType
+     * @param paDataValue
+     */
+    public PaData( PaDataType paDataType, byte[] paDataValue )
+    {
+        this.paDataType = paDataType;
+        this.paDataValue = paDataValue;
+    }
+
+
+    /**
+     * Returns the {@link PaDataType}.
+     *
+     * @return The {@link PaDataType}.
+     */
+    public PaDataType getPaDataType()
+    {
+        return paDataType;
+    }
+
+
+    /**
+     * Set the PA-DATA type
+     *
+     * @param paDataType The PA-DATA type
+     */
+    public void setPaDataType( int paDataType )
+    {
+        this.paDataType = PaDataType.getTypeByOrdinal( paDataType );
+    }
+
+    
+    /**
+     * Set the PA-DATA type
+     *
+     * @param paDataType The PA-DATA type
+     */
+    public void setPaDataType( PaDataType paDataType )
+    {
+        this.paDataType = paDataType;
+    }
+
+    
+    /**
+     * Returns the raw bytes of the {@link PaData}.
+     *
+     * @return The raw bytes of the {@link PaData}.
+     */
+    public byte[] getPaDataValue()
+    {
+        return paDataValue;
+    }
+
+
+    /**
+     * Set the PA-DATA value
+     *
+     * @param paDataValue The PA-DATA value
+     */
+    public void setPaDataValue( byte[] paDataValue )
+    {
+        this.paDataValue = paDataValue;
+    }
+
+    
+    /**
+     * Compute the PreAuthenticationData length
+     * 
+     * PreAuthenticationData :
+     * 
+     * 0x30 L1 PreAuthenticationData sequence
+     *  |
+     *  +--> 0xA0 L2 padata-type tag
+     *  |     |
+     *  |     +--> 0x02 L2-1 padata-type (int)
+     *  |
+     *  +--> 0xA1 L3 padata-value tag
+     *        |
+     *        +--> 0x04 L3-1 padata-value (OCTET STRING)
+     *        
+     *  where L1 = L2 + lenght(0xA0) + length(L2) +
+     *             L3 + lenght(0xA1) + length(L3) 
+     *  and
+     *  L2 = L2-1 + length(0x02) + length( L2-1) 
+     *  L3 = L3-1 + length(0x04) + length( L3-1) 
+     */
+    public int computeLength()
+    {
+        // Compute the paDataType. The Length will always be contained in 1 byte
+        int paDataTypeLength = Value.getNbBytes( paDataType.getOrdinal() );
+        paDataTypeTagLength = 1 + TLV.getNbBytes( paDataTypeLength ) + paDataTypeLength;
+        preAuthenticationDataSeqLength = 1 + TLV.getNbBytes( paDataTypeTagLength ) + paDataTypeTagLength;
+
+        // Compute the paDataValue
+        if ( paDataValue == null )
+        {
+            paDataValueTagLength = 1 + 1;
+        }
+        else
+        {
+            paDataValueTagLength = 1 + TLV.getNbBytes( paDataValue.length ) + paDataValue.length;
+        }
+
+        // Compute the whole sequence length
+        preAuthenticationDataSeqLength += 1 + TLV.getNbBytes( paDataValueTagLength ) + paDataValueTagLength;
+
+        return 1 + TLV.getNbBytes( preAuthenticationDataSeqLength ) + preAuthenticationDataSeqLength;
+
+    }
+
+
+    /**
+     * Encode the PreAuthenticationData message to a PDU. 
+     * 
+     * PreAuthenticationData :
+     * 
+     * 0x30 LL
+     *   0xA0 LL 
+     *     0x02 0x01 padata-type
+     *   0xA1 LL 
+     *     0x04 LL padata-value
+     * 
+     * @param buffer The buffer where to put the PDU. It should have been allocated
+     * before, with the right size.
+     * @return The constructed PDU.
+     */
+    public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException
+    {
+        if ( buffer == null )
+        {
+            throw new EncoderException( "Cannot put a PDU in a null buffer !" );
+        }
+
+        try
+        {
+            // The Checksum SEQ Tag
+            buffer.put( UniversalTag.SEQUENCE_TAG );
+            buffer.put( TLV.getBytes( preAuthenticationDataSeqLength ) );
+
+            // The cksumtype, first the tag, then the value
+            buffer.put( ( byte ) 0xA1 );
+            buffer.put( TLV.getBytes( paDataTypeTagLength ) );
+            Value.encode( buffer, paDataType.getOrdinal() );
+
+            // The checksum, first the tag, then the value
+            buffer.put( ( byte ) 0xA2 );
+            buffer.put( TLV.getBytes( paDataValueTagLength ) );
+            Value.encode( buffer, paDataValue );
+        }
+        catch ( BufferOverflowException boe )
+        {
+            log.error( "Cannot encode the PreAuthenticationData object, the PDU size is {} when only {} bytes has been allocated", 1
+                + TLV.getNbBytes( preAuthenticationDataSeqLength ) + preAuthenticationDataSeqLength, buffer.capacity() );
+            throw new EncoderException( "The PDU buffer size is too small !" );
+        }
+
+        if ( IS_DEBUG )
+        {
+            log.debug( "PreAuthenticationData encoding : {}", StringTools.dumpBytes( buffer.array() ) );
+            log.debug( "PreAuthenticationData initial value : {}", toString() );
+        }
+
+        return buffer;
+    }
+
+    /**
+     * @see Object#toString()
+     */
+    public String toString()
+    {
+        return toString( "" );
+    }
+
+
+    /**
+     * @see Object#toString()
+     */
+    public String toString( String tabs )
+    {
+        StringBuilder sb = new StringBuilder();
+
+        sb.append( tabs ).append( "PreAuthenticationData : {\n" );
+        sb.append( tabs ).append( "    padata-type: " ).append( paDataType ).append( '\n' );
+
+        if ( paDataValue != null )
+        {
+            sb.append( tabs + "    padata-value:" ).append( StringTools.dumpBytes( paDataValue ) ).append( '\n' );
+        }
+
+        sb.append( tabs + "}\n" );
+
+        return sb.toString();
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/PrincipalName.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/PrincipalName.java
new file mode 100644
index 0000000..3d3537d
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/PrincipalName.java
@@ -0,0 +1,481 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+//package org.apache.directory.server.kerberos.shared.messages.value;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ *
+public class PrincipalName
+{
+    private String nameComponent;
+    private int nameType;
+
+
+    /**
+     * Creates a new instance of PrincipalName.
+     *
+     * @param nameComponent
+     * @param nameType
+     *
+    public PrincipalName( String nameComponent, int nameType )
+    {
+        this.nameComponent = nameComponent;
+        this.nameType = nameType;
+    }
+
+
+    /**
+     * Returns the type of the {@link PrincipalName}.
+     *
+     * @return The type of the {@link PrincipalName}.
+     *
+    public int getNameType()
+    {
+        return nameType;
+    }
+
+
+    /**
+     * Returns the name component.
+     *
+     * @return The name component.
+     *
+    public String getNameComponent()
+    {
+        return nameComponent;
+    }
+}*/
+
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.value;
+
+
+import java.nio.BufferOverflowException;
+import java.nio.ByteBuffer;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.apache.directory.server.kerberos.shared.KerberosUtils;
+import org.apache.directory.server.kerberos.shared.messages.value.types.PrincipalNameType;
+import org.apache.directory.shared.asn1.AbstractAsn1Object;
+import org.apache.directory.shared.asn1.ber.tlv.TLV;
+import org.apache.directory.shared.asn1.ber.tlv.UniversalTag;
+import org.apache.directory.shared.asn1.ber.tlv.Value;
+import org.apache.directory.shared.asn1.codec.EncoderException;
+import org.apache.directory.shared.ldap.util.StringTools;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * A principal Name, composed of a type and N names.
+ * 
+ * PrincipalName   ::= SEQUENCE {
+ *        name-type       [0] Int32,
+ *        name-string     [1] SEQUENCE OF KerberosString
+ * }
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class PrincipalName extends AbstractAsn1Object
+{
+    /** The logger */
+    private static final Logger LOG = LoggerFactory.getLogger( PrincipalName.class );
+
+    /** Speedup for logs */
+    private static final boolean IS_DEBUG = LOG.isDebugEnabled();
+
+    /** The type for this principal */
+    private PrincipalNameType nameType;
+
+    /** The principal name - we may have more than one - */
+    private List<String> nameString;
+    
+    /** The principal name as a byte[], for encoding purpose */
+    private transient List<byte[]> nameBytes;
+    
+    // Storage for computed lengths
+    private transient int principalNameSeqLength;
+    private transient int principalTypeTagLength;
+    private transient int principalTypeLength;
+    private transient int principalStringsTagLength;
+    private transient int principalStringsSeqLength;
+
+    /**
+     * Creates a new empty instance of PrincipalName.
+     */
+    public PrincipalName()
+    {
+    }
+
+    /**
+     * Creates a new instance of PrincipalName, given a KerberosPrincipal.
+     * 
+     * We assume that a principal has only one type, even if there are
+     * more than one name component.
+     *
+     * @param principal A Sun kerberosPrincipal instance
+     */
+    public PrincipalName( KerberosPrincipal principal )
+    {
+        try
+        {
+            nameString = KerberosUtils.getNames( principal );
+        }
+        catch ( ParseException pe )
+        {
+            nameString = KerberosUtils.EMPTY_PRINCIPAL_NAME;
+        }
+
+        this.nameType = PrincipalNameType.getTypeByOrdinal( principal.getNameType() );
+    }
+    
+    /**
+     * Creates a new instance of PrincipalName given a String and an 
+     * prinipal type.
+     * 
+     * @param nameString The name string, which can contains more than one nameComponent
+     * @param nameType The principal name
+     */
+    public PrincipalName( String nameString, PrincipalNameType nameType )  throws ParseException
+    {
+        this.nameString = KerberosUtils.getNames( nameString );
+        
+        this.nameType = nameType;
+    }
+
+
+    /**
+     * Creates a new instance of PrincipalName.
+     *
+     * @param nameString
+     * @param nameType
+     */
+    public PrincipalName( String nameString, int nameType ) throws ParseException
+    {
+        this.nameString = KerberosUtils.getNames( nameString );
+        
+        this.nameType = PrincipalNameType.getTypeByOrdinal( nameType );
+    }
+
+
+    /**
+     * Returns the type of the {@link PrincipalName}.
+     *
+     * @return The type of the {@link PrincipalName}.
+     */
+    public PrincipalNameType getNameType()
+    {
+        return nameType;
+    }
+                    
+    /** 
+     * Set the Principal name Type
+     * @param nameType the Principal name Type
+     */
+    public void setNameType( PrincipalNameType nameType )
+    {
+        this.nameType = nameType;
+    }
+
+    /** 
+     * Set the Principal name Type
+     * @param nameType the Principal name Type
+     */
+    public void setNameType( int nameType )
+    {
+        this.nameType = PrincipalNameType.getTypeByOrdinal( nameType );
+    }
+
+    /**
+     * Returns the name components.
+     *
+     * @return The name components.
+     */
+    public List<String> getNames()
+    {
+        return nameString;
+    }
+
+
+    /**
+     * @return A String representing the principal names as a String 
+     */
+    public String getNameString()
+    {
+        if ( ( nameString == null ) || ( nameString.size() == 0 ) )
+        {
+            return "";
+        }
+        else
+        {
+            StringBuilder sb = new StringBuilder();
+            boolean isFirst = true;
+
+            for ( String name : nameString )
+            {
+                if ( isFirst )
+                {
+                    isFirst = false;
+                }
+                else
+                {
+                    sb.append( '/' );
+                }
+
+                sb.append( name );
+            }
+
+            return sb.toString();
+        }
+    }
+
+
+    /**
+     * Add a new name to the PrincipalName
+     * @param name The name to add
+     */
+    public void addName( String name )
+    {
+        if ( nameString == null )
+        {
+            nameString = new ArrayList<String>();
+        }
+
+        nameString.add( name );
+    }
+
+
+    /**
+     * Compute the PrincipalName length
+     * 
+     * PrincipalName :
+     * 
+     * 0x30 L1 PrincipalName sequence
+     *  |
+     *  +--> 0xA1 L2 name-type tag
+     *  |     |
+     *  |     +--> 0x02 L2-1 addressType (int)
+     *  |
+     *  +--> 0xA2 L3 name-string tag
+     *        |
+     *        +--> 0x30 L3-1 name-string (SEQUENCE OF KerberosString)
+     *              |
+     *              +--> 0x1B L4[1] value (KerberosString)
+     *              |
+     *              +--> 0x1B L4[2] value (KerberosString)
+     *              |
+     *              ...
+     *              |
+     *              +--> 0x1B L4[n] value (KerberosString)
+     */
+    public int computeLength()
+    {
+        // The principalName can't be empty.
+        principalTypeLength = Value.getNbBytes( nameType.getOrdinal() );
+        principalTypeTagLength = 1 + TLV.getNbBytes( principalTypeLength ) + principalTypeLength;
+        
+        principalNameSeqLength = 1 + TLV.getNbBytes( principalTypeTagLength ) + principalTypeTagLength;
+
+        // Compute the keyValue
+        if ( ( nameString == null ) || ( nameString.size() == 0 ) )
+        {
+            principalStringsSeqLength = 0;
+        }
+        else
+        {
+            principalStringsSeqLength = 0;
+            nameBytes = new ArrayList<byte[]>( nameString.size() );
+
+            for ( String name : nameString )
+            {
+                if ( name != null )
+                {
+                    byte[] bytes = StringTools.getBytesUtf8( name );
+                    nameBytes.add( bytes );
+                    principalStringsSeqLength += 1 + TLV.getNbBytes( bytes.length ) + bytes.length;
+                }
+                else
+                {
+                    nameBytes.add( StringTools.EMPTY_BYTES );
+                    principalStringsSeqLength += 1 + 1;
+                }
+            }
+        }
+
+        principalStringsTagLength = 1 + TLV.getNbBytes( principalStringsSeqLength ) + principalStringsSeqLength;
+        principalNameSeqLength += 1 + TLV.getNbBytes( principalStringsTagLength ) + principalStringsTagLength;
+
+        // Compute the whole sequence length
+        return 1 + TLV.getNbBytes( principalNameSeqLength ) + principalNameSeqLength;
+    }
+
+
+    /**
+     * Encode the PrincipalName message to a PDU. 
+     * 
+     * PrincipalName :
+     * 
+     * 0x30 LL
+     *   0xA0 LL 
+     *     0x02 0x01 name-type (integer)
+     *   0xA1 LL 
+     *     0x30 LL name-string (SEQUENCE OF KerberosString)
+     *       0x1B LL name-string[1]
+     *       0x1B LL name-string[2]
+     *       ...
+     *       0x1B LL name-string[n]
+     * 
+     * @param buffer The buffer where to put the PDU. It should have been allocated
+     * before, with the right size.
+     * @return The constructed PDU.
+     */
+    public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException
+    {
+        if ( buffer == null )
+        {
+            throw new EncoderException( "Cannot put a PDU in a null buffer !" );
+        }
+
+        try
+        {
+            // The PrincipalName SEQ Tag
+            buffer.put( UniversalTag.SEQUENCE_TAG );
+            buffer.put( TLV.getBytes( principalNameSeqLength ) );
+
+            // The name-type, first the tag, then the value
+            buffer.put( ( byte ) 0xA0 );
+            buffer.put( TLV.getBytes( principalTypeTagLength ) );
+            Value.encode( buffer, nameType.getOrdinal() );
+
+            // The name-string tag
+            buffer.put( ( byte ) 0xA1 );
+            buffer.put( TLV.getBytes( principalStringsTagLength ) );
+
+            // The name-string sequence
+            buffer.put( UniversalTag.SEQUENCE_TAG );
+
+            if ( ( nameString == null ) || ( nameString.size() == 0 ) )
+            {
+                buffer.put( ( byte ) 0x00 );
+            }
+            else
+            {
+                buffer.put( TLV.getBytes( principalStringsSeqLength ) );
+
+                // The kerberosStrings
+                for ( byte[] name : nameBytes )
+                {
+                    buffer.put( UniversalTag.GENERALIZED_STRING_TAG );
+
+                    if ( ( name == null ) || ( name.length == 0 ) )
+                    {
+                        buffer.put( ( byte ) 0x00 );
+                    }
+                    else
+                    {
+                        buffer.put( TLV.getBytes( name.length ) );
+                        buffer.put( name );
+                    }
+                }
+            }
+        }
+        catch ( BufferOverflowException boe )
+        {
+            LOG.error(
+                "Cannot encode the principalName object, the PDU size is {} when only {} bytes has been allocated", 1
+                    + TLV.getNbBytes( principalNameSeqLength ) + principalNameSeqLength, buffer.capacity() );
+            throw new EncoderException( "The PDU buffer size is too small !" );
+        }
+
+        if ( IS_DEBUG )
+        {
+            LOG.debug( "PrinipalName encoding : {}", StringTools.dumpBytes( buffer.array() ) );
+            LOG.debug( "PrinipalName initial value : {}", toString() );
+        }
+
+        return buffer;
+    }
+
+
+    /**
+     * @see Object#toString()
+     */
+    public String toString()
+    {
+        StringBuilder sb = new StringBuilder();
+
+        sb.append( "PincipalName : {\n" );
+
+        sb.append( "    name-type: " ).append( nameType ).append( '\n' );
+
+        if ( ( nameString != null ) && ( nameString.size() != 0 ) )
+        {
+            sb.append( "    name-string : <" );
+            boolean isFirst = true;
+
+            for ( String name : nameString )
+            {
+                if ( isFirst )
+                {
+                    isFirst = false;
+                }
+                else
+                {
+                    sb.append( ", " );
+                }
+
+                sb.append( '\'' ).append( name ).append( '\'' );
+            }
+
+            sb.append( ">\n}" );
+        }
+        else
+        {
+            sb.append( "    no name-string\n}" );
+        }
+
+        return sb.toString();
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/RequestBody.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/RequestBody.java
new file mode 100644
index 0000000..e37808d
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/RequestBody.java
@@ -0,0 +1,202 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.value;
+
+
+import java.util.Set;
+
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
+import org.apache.directory.server.kerberos.shared.messages.components.Ticket;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class RequestBody
+{
+    private KdcOptions kdcOptions;
+    private KerberosPrincipal clientPrincipal; //optional in TgsReq only
+    private KerberosPrincipal serverPrincipal;
+    private KerberosTime from; //optional
+    private KerberosTime till;
+    private KerberosTime rtime; //optional
+    private int nonce;
+    private Set<EncryptionType> eType;
+    private HostAddresses addresses; //optional
+    private EncryptedData encAuthorizationData; //optional
+    private Ticket[] additionalTickets; //optional
+
+
+    /**
+     * Creates a new instance of RequestBody.
+     *
+     * @param kdcOptions
+     * @param clientPrincipal
+     * @param serverPrincipal
+     * @param from
+     * @param till
+     * @param rtime
+     * @param nonce
+     * @param eType
+     * @param addresses
+     * @param encAuthorizationData
+     * @param additionalTickets
+     */
+    public RequestBody( KdcOptions kdcOptions, KerberosPrincipal clientPrincipal, KerberosPrincipal serverPrincipal,
+        KerberosTime from, KerberosTime till, KerberosTime rtime, int nonce, Set<EncryptionType> eType,
+        HostAddresses addresses, EncryptedData encAuthorizationData, Ticket[] additionalTickets )
+    {
+        this.kdcOptions = kdcOptions;
+        this.clientPrincipal = clientPrincipal;
+        this.serverPrincipal = serverPrincipal;
+        this.from = from;
+        this.till = till;
+        this.rtime = rtime;
+        this.nonce = nonce;
+        this.eType = eType;
+        this.addresses = addresses;
+        this.encAuthorizationData = encAuthorizationData;
+        this.additionalTickets = additionalTickets;
+    }
+
+
+    /**
+     * Returns the additional {@link Ticket}s.
+     *
+     * @return The additional {@link Ticket}s.
+     */
+    public Ticket[] getAdditionalTickets()
+    {
+        return additionalTickets;
+    }
+
+
+    /**
+     * Returns the {@link HostAddresses}.
+     *
+     * @return The {@link HostAddresses}.
+     */
+    public HostAddresses getAddresses()
+    {
+        return addresses;
+    }
+
+
+    /**
+     * Returns the client {@link KerberosPrincipal}.
+     *
+     * @return The client {@link KerberosPrincipal}.
+     */
+    public KerberosPrincipal getClientPrincipal()
+    {
+        return clientPrincipal;
+    }
+
+
+    /**
+     * Returns the server {@link KerberosPrincipal}.
+     *
+     * @return The server {@link KerberosPrincipal}.
+     */
+    public KerberosPrincipal getServerPrincipal()
+    {
+        return serverPrincipal;
+    }
+
+
+    /**
+     * Returns the encrypted {@link AuthorizationData} as {@link EncryptedData}.
+     *
+     * @return The encrypted {@link AuthorizationData} as {@link EncryptedData}.
+     */
+    public EncryptedData getEncAuthorizationData()
+    {
+        return encAuthorizationData;
+    }
+
+
+    /**
+     * Returns the requested {@link EncryptionType}s.
+     *
+     * @return The requested {@link EncryptionType}s.
+     */
+    public Set<EncryptionType> getEType()
+    {
+        return eType;
+    }
+
+
+    /**
+     * Returns the from {@link KerberosTime}.
+     *
+     * @return The from {@link KerberosTime}.
+     */
+    public KerberosTime getFrom()
+    {
+        return from;
+    }
+
+
+    /**
+     * Returns the {@link KdcOptions}.
+     *
+     * @return The {@link KdcOptions}.
+     */
+    public KdcOptions getKdcOptions()
+    {
+        return kdcOptions;
+    }
+
+
+    /**
+     * Returns the nonce.
+     *
+     * @return The nonce.
+     */
+    public int getNonce()
+    {
+        return nonce;
+    }
+
+
+    /**
+     * Returns the "R" {@link KerberosTime}.
+     *
+     * @return The "R" {@link KerberosTime}.
+     */
+    public KerberosTime getRtime()
+    {
+        return rtime;
+    }
+
+
+    /**
+     * Returns the till {@link KerberosTime}.
+     *
+     * @return The till {@link KerberosTime}.
+     */
+    public KerberosTime getTill()
+    {
+        return till;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/RequestBodyModifier.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/RequestBodyModifier.java
new file mode 100644
index 0000000..ba6fc15
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/RequestBodyModifier.java
@@ -0,0 +1,196 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.value;
+
+
+import java.util.Set;
+
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
+import org.apache.directory.server.kerberos.shared.messages.components.Ticket;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class RequestBodyModifier
+{
+    private KerberosPrincipalModifier clientModifier = new KerberosPrincipalModifier(); //optional in TgsReq only
+    private KerberosPrincipalModifier serverModifier = new KerberosPrincipalModifier();
+    private KdcOptions kdcOptions;
+    private KerberosTime from; //optional
+    private KerberosTime till;
+    private KerberosTime rtime; //optional
+    private int nonce;
+    private Set<EncryptionType> eType;
+    private HostAddresses addresses; //optional
+    private EncryptedData encAuthorizationData; //optional
+    private Ticket[] additionalTickets; //optional
+
+
+    /**
+     * Returns the {@link RequestBody}.
+     *
+     * @return The {@link RequestBody}.
+     */
+    public RequestBody getRequestBody()
+    {
+        KerberosPrincipal clientPrincipal = clientModifier.getKerberosPrincipal();
+        KerberosPrincipal serverPrincipal = serverModifier.getKerberosPrincipal();
+
+        return new RequestBody( kdcOptions, clientPrincipal, serverPrincipal, from, till, rtime, nonce, eType,
+            addresses, encAuthorizationData, additionalTickets );
+    }
+
+
+    /**
+     * Sets the client {@link PrincipalName}.
+     *
+     * @param clientName
+     */
+    public void setClientName( PrincipalName clientName )
+    {
+        clientModifier.setPrincipalName( clientName );
+    }
+
+
+    /**
+     * Sets the server {@link PrincipalName}.
+     *
+     * @param serverName
+     */
+    public void setServerName( PrincipalName serverName )
+    {
+        serverModifier.setPrincipalName( serverName );
+    }
+
+
+    /**
+     * Sets the realm.
+     *
+     * @param realm
+     */
+    public void setRealm( String realm )
+    {
+        clientModifier.setRealm( realm );
+        serverModifier.setRealm( realm );
+    }
+
+
+    /**
+     * Sets additional {@link Ticket}s.
+     *
+     * @param tickets
+     */
+    public void setAdditionalTickets( Ticket[] tickets )
+    {
+        additionalTickets = tickets;
+    }
+
+
+    /**
+     * Sets the {@link HostAddresses}.
+     *
+     * @param addresses
+     */
+    public void setAddresses( HostAddresses addresses )
+    {
+        this.addresses = addresses;
+    }
+
+
+    /**
+     * Sets the encrypted authorization data.
+     *
+     * @param authorizationData
+     */
+    public void setEncAuthorizationData( EncryptedData authorizationData )
+    {
+        encAuthorizationData = authorizationData;
+    }
+
+
+    /**
+     * Sets the requested {@link EncryptionType}s.
+     *
+     * @param type
+     */
+    public void setEType( Set<EncryptionType> type )
+    {
+        eType = type;
+    }
+
+
+    /**
+     * Sets the from {@link KerberosTime}.
+     *
+     * @param from
+     */
+    public void setFrom( KerberosTime from )
+    {
+        this.from = from;
+    }
+
+
+    /**
+     * Sets the {@link KdcOptions}.
+     *
+     * @param options
+     */
+    public void setKdcOptions( KdcOptions options )
+    {
+        kdcOptions = options;
+    }
+
+
+    /**
+     * Sets the nonce.
+     *
+     * @param nonce
+     */
+    public void setNonce( int nonce )
+    {
+        this.nonce = nonce;
+    }
+
+
+    /**
+     * Sets the "R" {@link KerberosTime}.
+     *
+     * @param rtime
+     */
+    public void setRtime( KerberosTime rtime )
+    {
+        this.rtime = rtime;
+    }
+
+
+    /**
+     * Sets the till {@link KerberosTime}.
+     *
+     * @param till
+     */
+    public void setTill( KerberosTime till )
+    {
+        this.till = till;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/SamType.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/SamType.java
new file mode 100644
index 0000000..8bc4e4c
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/SamType.java
@@ -0,0 +1,140 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.value;
+
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+
+/**
+ * Type safe enumeration of Single-use Authentication Mechanism types
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public final class SamType implements Comparable<SamType>
+{
+    /*
+     * Enumeration elements are constructed once upon class loading.
+     * Order of appearance here determines the order of compareTo.
+     */
+
+    /** safe SAM type enum for Enigma Logic */
+    public static final SamType PA_SAM_TYPE_ENIGMA = new SamType( 1, "Enigma Logic" );
+
+    /** safe SAM type enum for Digital Pathways */
+    public static final SamType PA_SAM_TYPE_DIGI_PATH = new SamType( 2, "Digital Pathways" );
+
+    /** safe SAM type enum for S/key where KDC has key 0 */
+    public static final SamType PA_SAM_TYPE_SKEY_K0 = new SamType( 3, "S/key where KDC has key 0" );
+
+    /** safe SAM type enum for Traditional S/Key */
+    public static final SamType PA_SAM_TYPE_SKEY = new SamType( 4, "Traditional S/Key" );
+
+    /** safe SAM type enum for Security Dynamics */
+    public static final SamType PA_SAM_TYPE_SECURID = new SamType( 5, "Security Dynamics" );
+
+    /** safe SAM type enum for CRYPTOCard */
+    public static final SamType PA_SAM_TYPE_CRYPTOCARD = new SamType( 6, "CRYPTOCard" );
+
+    /** safe SAM type enum for Apache Software Foundation */
+    public static final SamType PA_SAM_TYPE_APACHE = new SamType( 7, "Apache Software Foundation" );
+
+    /** Array for building a List of VALUES. */
+    private static final SamType[] values =
+        { PA_SAM_TYPE_ENIGMA, PA_SAM_TYPE_DIGI_PATH, PA_SAM_TYPE_SKEY_K0, PA_SAM_TYPE_SKEY, PA_SAM_TYPE_SECURID,
+            PA_SAM_TYPE_CRYPTOCARD, PA_SAM_TYPE_APACHE };
+
+    /** a list of all the sam type constants */
+    public static final List<SamType> VALUES = Collections.unmodifiableList( Arrays.asList( values ) );
+
+    /** the name of the sam type */
+    private final String name;
+
+    /** the value/code for the sam type */
+    private final int ordinal;
+
+
+    /**
+     * Private constructor prevents construction outside of this class.
+     */
+    private SamType( int ordinal, String name )
+    {
+        this.ordinal = ordinal;
+        this.name = name;
+    }
+
+
+    /**
+     * Returns the name of the SamType.
+     *
+     * @return the name of the SAM type
+     */
+    public String toString()
+    {
+        return name;
+    }
+
+
+    /**
+     * Compares this type to another object hopefully one that is of the same
+     * type.
+     *
+     * @param that the object to compare this SamType to
+     * @return ordinal - ( ( SamType ) that ).ordinal;
+     */
+    public int compareTo( SamType that )
+    {
+        return ordinal - that.ordinal;
+    }
+
+
+    /**
+     * Gets the ordinal by its ordinal value.
+     *
+     * @param ordinal the ordinal value of the ordinal
+     * @return the type corresponding to the ordinal value
+     */
+    public static SamType getTypeByOrdinal( int ordinal )
+    {
+        for ( int ii = 0; ii < values.length; ii++ )
+        {
+            if ( values[ii].ordinal == ordinal )
+            {
+                return values[ii];
+            }
+        }
+
+        return PA_SAM_TYPE_APACHE;
+    }
+
+
+    /**
+     * Gets the ordinal value associated with this SAM type.
+     *
+     * @return the ordinal value associated with this SAM type
+     */
+    public int getOrdinal()
+    {
+        return ordinal;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/TransitedEncoding.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/TransitedEncoding.java
new file mode 100644
index 0000000..1bc17fc
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/TransitedEncoding.java
@@ -0,0 +1,256 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.value;
+
+
+import java.nio.BufferOverflowException;
+import java.nio.ByteBuffer;
+
+import org.apache.directory.server.kerberos.shared.messages.value.types.TransitedEncodingType;
+import org.apache.directory.shared.asn1.AbstractAsn1Object;
+import org.apache.directory.shared.asn1.ber.tlv.TLV;
+import org.apache.directory.shared.asn1.ber.tlv.UniversalTag;
+import org.apache.directory.shared.asn1.ber.tlv.Value;
+import org.apache.directory.shared.asn1.codec.EncoderException;
+import org.apache.directory.shared.ldap.util.StringTools;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * The TransitedEncoding structure.
+ * 
+ * The ASN.1 grammar is :
+ * 
+ * -- encoded Transited field
+ * TransitedEncoding       ::= SEQUENCE {
+ *         tr-type         [0] Int32 -- must be registered --,
+ *         contents        [1] OCTET STRING
+ * }
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class TransitedEncoding extends AbstractAsn1Object
+{
+    /** The logger */
+    private static final Logger log = LoggerFactory.getLogger( TransitedEncoding.class );
+
+    /** Speedup for logs */
+    private static final boolean IS_DEBUG = log.isDebugEnabled();
+
+    /** The transited type. One of :
+     * NULL
+     * DOMAIN_X500_COMPRESS
+     */
+    private TransitedEncodingType trType;
+
+    /** The transited data */
+    private byte[] contents;
+
+    // Storage for computed lengths
+    private transient int trTypeLength;
+    private transient int contentsLength;
+    private transient int transitedEncodingLength;
+
+
+    /**
+     * Creates a new instance of TransitedEncoding.
+     */
+    public TransitedEncoding()
+    {
+        trType = TransitedEncodingType.NULL;
+        contents = new byte[0];
+    }
+
+
+    /**
+     * Creates a new instance of TransitedEncoding.
+     *
+     * @param trType
+     * @param contents
+     */
+    public TransitedEncoding( TransitedEncodingType trType, byte[] contents )
+    {
+        this.trType = trType;
+        this.contents = contents;
+    }
+
+
+    /**
+     * Returns the contents.
+     *
+     * @return The contents.
+     */
+    public byte[] getContents()
+    {
+        return contents;
+    }
+    
+    
+    /**
+     * Set the contents
+     * @param contents The contents
+     */
+    public void setContents( byte[] contents )
+    {
+        this.contents = contents;
+    }
+
+
+    /**
+     * Returns the {@link TransitedEncodingType}.
+     *
+     * @return The {@link TransitedEncodingType}.
+     */
+    public TransitedEncodingType getTrType()
+    {
+        return trType;
+    }
+    
+    
+    /**
+     * Set the transited encoding type
+     * @param trType The transited encoding type
+     */
+    public void setTrType( TransitedEncodingType trType )
+    {
+        this.trType = trType;
+    }
+
+
+    /**
+     * Compute the TransitedEncoding length
+     * 
+     * TransitedEncoding :
+     * 
+     * 0x30 L1 TransitedEncoding
+     *  |
+     *  +--> 0xA0 L2 trType tag
+     *  |     |
+     *  |     +--> 0x02 L2-1 trType (int)
+     *  |
+     *  +--> 0xA1 L3 contents tag
+     *        |
+     *        +--> 0x04 L3-1 contents (OCTET STRING)
+     *        
+     *  where L1 = L2 + lenght(0xA0) + length(L2) +
+     *             L3 + lenght(0xA1) + length(L3) 
+     *  and
+     *  L2 = L2-1 + length(0x02) + length( L2-1) 
+     *  L3 = L3-1 + length(0x04) + length( L3-1) 
+     */
+    public int computeLength()
+    {
+        // Compute the trType. The Length will always be contained in 1 byte
+        trTypeLength = 1 + 1 + Value.getNbBytes( trType.getOrdinal() );
+        transitedEncodingLength = 1 + TLV.getNbBytes( trTypeLength ) + trTypeLength;
+
+        // Compute the keyValue
+        if ( contents == null )
+        {
+            contentsLength = 1 + 1;
+        }
+        else
+        {
+            contentsLength = 1 + TLV.getNbBytes( contents.length ) + contents.length;
+        }
+
+        transitedEncodingLength += 1 + TLV.getNbBytes( contentsLength ) + contentsLength;
+
+        // Compute the whole sequence length
+        int transitedEncodingSeqLength = 1 + Value.getNbBytes( transitedEncodingLength ) + transitedEncodingLength;
+
+        return transitedEncodingSeqLength;
+
+    }
+
+
+    /**
+     * Encode the TransitedEncoding message to a PDU. 
+     * 
+     * TransitedEncoding :
+     * 
+     * 0x30 LL
+     *   0xA0 LL 
+     *     0x02 0x01 trType
+     *   0xA1 LL 
+     *     0x04 LL adData
+     * 
+     * @param buffer The buffer where to put the PDU. It should have been allocated
+     * before, with the right size.
+     * @return The constructed PDU.
+     */
+    public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException
+    {
+        if ( buffer == null )
+        {
+            throw new EncoderException( "Cannot put a PDU in a null buffer !" );
+        }
+
+        try
+        {
+            // The AuthorizationDataEntry SEQ Tag
+            buffer.put( UniversalTag.SEQUENCE_TAG );
+            buffer.put( TLV.getBytes( transitedEncodingLength ) );
+
+            // The tr-type, first the tag, then the value
+            buffer.put( ( byte ) 0xA0 );
+            buffer.put( TLV.getBytes( trTypeLength ) );
+            Value.encode( buffer, trType.getOrdinal() );
+
+            // The contents, first the tag, then the value
+            buffer.put( ( byte ) 0xA1 );
+            buffer.put( TLV.getBytes( contentsLength ) );
+            Value.encode( buffer, contents );
+        }
+        catch ( BufferOverflowException boe )
+        {
+            log.error(
+                "Cannot encode the TransitedEncoding object, the PDU size is {} when only {} bytes has been allocated",
+                1 + TLV.getNbBytes( transitedEncodingLength ) + transitedEncodingLength, buffer.capacity() );
+            throw new EncoderException( "The PDU buffer size is too small !" );
+        }
+
+        if ( IS_DEBUG )
+        {
+            log.debug( "TransitedEncoding encoding : {}", StringTools.dumpBytes( buffer.array() ) );
+            log.debug( "TransitedEncoding initial value : {}", toString() );
+        }
+
+        return buffer;
+    }
+
+
+    /**
+     * @see Object#toString()
+     */
+    public String toString()
+    {
+        StringBuilder sb = new StringBuilder();
+
+        sb.append( "TransitedEncoding : {\n" );
+        sb.append( "    tr-type: " ).append( trType ).append( '\n' );
+
+        sb.append( "    contents: " ).append( StringTools.dumpBytes( contents ) ).append( "\n}\n" );
+
+        return sb.toString();
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/flags/AbstractKerberosFlags.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/flags/AbstractKerberosFlags.java
new file mode 100644
index 0000000..d748262
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/flags/AbstractKerberosFlags.java
@@ -0,0 +1,203 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.value.flags;
+
+import org.apache.directory.shared.asn1.primitives.BitString;
+
+/**
+ * An implementation of a BitString for any KerberosFlags. The different values
+ * are stored in an int, as there can't be more than 32 flags (TicketFlag).
+ * 
+ * Some basic operations are implemented in this abstract class, like those
+ * manipulating flags.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 540371 $, $Date: 2007-05-22 02:00:43 +0200 (Tue, 22 May 2007) $
+ */
+public abstract class AbstractKerberosFlags extends BitString implements KerberosFlags
+{
+    /**
+     * The maximum size of the BitString as specified for Kerberos flags.
+     */
+    public static final int MAX_SIZE = 32;
+
+    /** The associated value */
+    protected int value;
+    
+    
+    /**
+     * Standard constructor, which create a BitString containing 32 bits
+     */
+    public AbstractKerberosFlags()
+    {
+        super( MAX_SIZE );
+    }
+
+    
+    /**
+     * Standard constructor, taking a byte array
+     */
+    public AbstractKerberosFlags( byte[] flags )
+    {
+        super( flags );
+        value = ( ( getBytes()[0] & 0x00F ) << 24 ) | ( ( getBytes()[1] & 0x00FF ) << 16 ) | ( ( getBytes()[2] & 0x00FF ) << 8 ) | ( 0x00FF & getBytes()[3] ); 
+    }
+    
+    
+    /**
+     * A static method to get the bayte array representation of an int
+     * @return The byte array for a list of flags.
+     */
+    public static byte[] getBytes( int flags )
+    {
+        return new byte[]{
+                (byte)( flags >>> 24), 
+                (byte)( ( flags >> 16 ) & 0x00ff ), 
+                (byte)( ( flags >> 8 ) & 0x00ff ), 
+                (byte)( flags & 0x00ff ) };
+    }
+    
+    
+    /**
+     * @return The byte array for a KerberosFlags
+     */
+    public byte[] getBytes()
+    {
+        return getData();
+    }
+
+    
+    /**
+     * Returns the int value associated with the flags
+     */
+    public int getIntValue()
+    {
+        return value;
+    }
+    
+    
+    /**
+     * Check if a flag is set
+     * @param flags The flags to test
+     * @param flag The flag to check
+     * @return True if the flag is set in the list of flags
+     */
+    public static boolean isFlagSet( int flags, int flag )
+    {
+        return ( flags & ( 1 << flag) ) != 0;
+    }
+    
+
+    /**
+     * Check if a flag is set for the actual value
+     * 
+     * @param flag The flag to check
+     * @return True if the flag is set in the list of flags
+     */
+    public boolean isFlagSet( KerberosFlag flag )
+    {
+        return ( value & ( 1 << flag.getOrdinal() ) ) != 0;
+    }
+    
+    
+    /**
+     * Check if a flag is set
+     * @param flag The flags to test
+     * @return True if the flag is set in the list of flags
+     */
+    public boolean isFlagSet( int flag )
+    {
+        return ( flag & ( 1 << value ) ) != 0;
+    }
+    
+    
+    /**
+     * Set a flag in a list of flags
+     * 
+     * @param flag The flag to set
+     */
+    public void setFlag( KerberosFlag flag )
+    {
+        value |= 1 << flag.getOrdinal();
+        setBit( flag.getOrdinal() );
+    }
+    
+    
+    /**
+     * Set a flag in a list of flags
+     * 
+     * @param flag The flag to set
+     */
+    public void setFlag( int flag )
+    {
+        value |= 1 << flag;
+        setBit( flag );
+    }
+    
+    
+    /**
+     * Modify a byte array to an integer value
+     * @param bytes The 4 bytes byte array to transform.
+     */
+    public void setFlags( byte[] bytes )
+    {
+        if ( (bytes== null ) || ( bytes.length != 4 ) )
+        {
+            value = -1;
+        }
+        
+        value = ( ( getBytes()[0] & 0x00F ) << 24 ) | ( ( getBytes()[1] & 0x00FF ) << 16 ) | ( ( getBytes()[2] & 0x00FF ) << 8 ) | ( 0x00FF & getBytes()[3] ); 
+        setData( bytes );
+    }
+    
+
+    /**
+     * clear a flag in a list of flags
+     * 
+     * @param flag The flag to set
+     */
+    public void clearFlag( KerberosFlag flag )
+    {
+        value &= ~( 1 << flag.getOrdinal() );
+        clearBit( flag.getOrdinal() );
+    }
+    
+    
+    /**
+     * clear a flag in a list of flags
+     * 
+     * @param flag The flag to set
+     */
+    public void clearFlag( int flag )
+    {
+        value &= ~( 1 << flag );
+        clearBit( flag );
+    }
+    
+    
+    /**
+     * @return The hex value for this flag, in its position.
+     * For instance, getting the flag 5 will return 0x0000 0010 
+     */
+    public int getHexValue()
+    {
+        return 1 << value;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/flags/KerberosFlag.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/flags/KerberosFlag.java
new file mode 100644
index 0000000..d7cc39a
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/flags/KerberosFlag.java
@@ -0,0 +1,37 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.value.flags;
+
+/**
+ * This interface must be implemented by all the inherited Flag classes :
+ * - TicketFlag
+ * - ApOption
+ * - KdcOption
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ *
+ */
+public interface KerberosFlag
+{
+    /**
+     * @return The ordinal value associated with this flag
+     */
+    int getOrdinal();
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/flags/KerberosFlags.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/flags/KerberosFlags.java
new file mode 100644
index 0000000..3d43ead
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/flags/KerberosFlags.java
@@ -0,0 +1,88 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.value.flags;
+
+/**
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ *
+ */
+public interface KerberosFlags
+{
+    /**
+     * @return The byte array for a KerberosFlags
+     */
+    byte[] getBytes();
+    
+    
+    /**
+     * Returns the int value associated with the flags
+     */
+    int getIntValue();
+    
+    
+    /**
+     * Check if a flag is set for the actual value
+     * 
+     * @param flag The flag to check
+     * @return True if the flag is set in the list of flags
+     */
+    boolean isFlagSet( KerberosFlag flag );
+    
+    
+    /**
+     * Check if a flag is set
+     * @param flag The flags to test
+     * @return True if the flag is set in the list of flags
+     */
+    boolean isFlagSet( int flag );
+    
+    
+    /**
+     * Set a flag in a list of flags
+     * 
+     * @param flag The flag to set
+     */
+    void setFlag( KerberosFlag flag );
+
+    
+    /**
+     * Set a flag in a list of flags
+     * 
+     * @param flag The flag to set
+     */
+    void setFlag( int flag );
+    
+    
+    /**
+     * clear a flag in a list of flags
+     * 
+     * @param flag The flag to set
+     */
+    void clearFlag( KerberosFlag flag );
+
+    
+    /**
+     * clear a flag in a list of flags
+     * 
+     * @param flag The flag to set
+     */
+    void clearFlag( int flag );
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/flags/TicketFlag.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/flags/TicketFlag.java
new file mode 100644
index 0000000..1ebc794
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/flags/TicketFlag.java
@@ -0,0 +1,142 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.value.flags;
+
+/**
+ * An enum to describe all the TicketFlag possible values.
+ * 
+ *  TicketFlags     ::= KerberosFlags
+ *           -- reserved(0),
+ *           -- forwardable(1),
+ *           -- forwarded(2),
+ *           -- proxiable(3),
+ *           -- proxy(4),
+ *           -- may-postdate(5),
+ *           -- postdated(6),
+ *           -- invalid(7),
+ *           -- renewable(8),
+ *           -- initial(9),
+ *           -- pre-authent(10),
+ *           -- hw-authent(11),
+ *       -- the following are new since 1510
+ *           -- transited-policy-checked(12),
+ *           -- ok-as-delegate(13)
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 540371 $, $Date: 2007-05-22 02:00:43 +0200 (Tue, 22 May 2007) $
+ */
+public enum TicketFlag implements KerberosFlag
+{
+    /**
+     * Ticket flag - reserved
+     */
+    RESERVED(0),
+
+    /**
+     * Ticket flag - forwardable
+     */
+    FORWARDABLE(1),
+
+    /**
+     * Ticket flag - forwarded
+     */
+    FORWARDED(2),
+
+    /**
+     * Ticket flag - proxiable
+     */
+    PROXIABLE(3),
+
+    /**
+     * Ticket flag - proxy
+     */
+    PROXY(4),
+
+    /**
+     * Ticket flag - may be postdated
+     */
+    MAY_POSTDATE(5),
+
+    /**
+     * Ticket flag - postdated
+     */
+    POSTDATED(6),
+    /**
+     * Ticket flag - invalid
+     */
+    INVALID(7),
+
+    /**
+     * Ticket flag - renewable
+     */
+    RENEWABLE(8),
+
+    /**
+     * Ticket flag - initial
+     */
+    INITIAL(9),
+
+    /**
+     * Ticket flag - pre-authentication
+     */
+    PRE_AUTHENT(10),
+
+    /**
+     * Ticket flag - hardware authentication
+     */
+    HW_AUTHENT(11),
+
+    /**
+     * Ticket flag - transitedEncoding policy checked
+     */
+    TRANSITED_POLICY_CHECKED(12),
+
+    /**
+     * Ticket flag - OK as delegate
+     */
+    OK_AS_DELEGATE(13),
+
+    /**
+     * Ticket flag - maximum value
+     */
+    MAX_VALUE(32);
+
+    
+    // The interned value.
+    private int value;
+    
+    
+    /**
+     * Class constructor
+     */
+    private TicketFlag( int value )
+    {
+        this.value = value;
+    }
+    
+    
+    /**
+     * @return The ordinal value associated with this flag
+     */
+    public int getOrdinal()
+    {
+        return value;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/flags/TicketFlags.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/flags/TicketFlags.java
new file mode 100644
index 0000000..bb03d84
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/flags/TicketFlags.java
@@ -0,0 +1,348 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.value.flags;
+
+
+/**
+ * An implementation of a BitString for the TicketFlags. The different values
+ * are stored in an int, as there can't be more than 32 flags (TicketFlag).
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 540371 $, $Date: 2007-05-22 02:00:43 +0200 (Tue, 22 May 2007) $
+ */
+public class TicketFlags extends AbstractKerberosFlags
+{
+    private static final long serialVersionUID = 1L;
+
+    
+   /**
+     * Basic constructor of a TicketFlags BitString
+     */
+    public TicketFlags()
+    {
+        super();
+    }
+    
+    
+    /**
+     * Constructor of a TicketFlags BitString with an int value
+     */
+    public TicketFlags( int flags )
+    {
+        super( getBytes( flags ) );
+    }
+    
+    
+    /**
+     * Basic constructor of a TicketFlags BitString with a byte array
+     */
+    public TicketFlags( byte[] flags )
+    {
+        super( flags );
+    }
+    
+    
+    /**
+     * Ticket flag - reserved
+     */
+    public boolean isReserved()
+    {
+        return isFlagSet( TicketFlag.RESERVED );
+    }
+
+    
+    /**
+     * Ticket flag - forwardable
+     */
+    public boolean isForwardable()
+    {
+        return isFlagSet( TicketFlag.FORWARDABLE );
+    }
+    
+
+    /**
+     * Ticket flag - forwarded
+     */
+    public boolean isForwarded()
+    {
+        return isFlagSet( TicketFlag.FORWARDED );
+    }
+
+    
+    /**
+     * Ticket flag - proxiable
+     */
+    public boolean isProxiable()
+    {
+        return isFlagSet( TicketFlag.PROXIABLE );
+    }
+
+    
+    /**
+     * Ticket flag - proxy
+     */
+    public boolean isProxy()
+    {
+        return isFlagSet( TicketFlag.PROXY );
+    }
+
+    
+    /**
+     * Ticket flag - may be postdated
+     */
+    public boolean isMayPosdate()
+    {
+        return isFlagSet( TicketFlag.MAY_POSTDATE );
+    }
+
+    
+    /**
+     * Ticket flag - postdated
+     */
+    public boolean isPostdated()
+    {
+        return isFlagSet( TicketFlag.POSTDATED );
+    }
+
+    
+    /**
+     * Ticket flag - invalid
+     */
+    public boolean isInvalid()
+    {
+        return isFlagSet( TicketFlag.INVALID );
+    }
+
+    
+    /**
+     * Ticket flag - renewable
+     */
+    public boolean isRenewable()
+    {
+        return isFlagSet( TicketFlag.RENEWABLE );
+    }
+
+    
+    /**
+     * Ticket flag - initial
+     */
+    public boolean isInitial()
+    {
+        return isFlagSet( TicketFlag.INITIAL );
+    }
+
+    
+    /**
+     * Ticket flag - pre-authentication
+     */
+    public boolean isPreAuth()
+    {
+        return isFlagSet( TicketFlag.PRE_AUTHENT );
+    }
+
+    
+    /**
+     * Ticket flag - hardware authentication
+     */
+    public boolean isHwAuthent()
+    {
+        return isFlagSet( TicketFlag.HW_AUTHENT );
+    }
+
+    
+    /**
+     * Ticket flag - transitedEncoding policy checked
+     */
+    public boolean isTransitedPolicyChecked()
+    {
+        return isFlagSet( TicketFlag.TRANSITED_POLICY_CHECKED );
+    }
+    
+
+    /**
+     * Ticket flag - OK as delegate
+     */
+    public boolean isOkAsDelegate()
+    {
+        return isFlagSet( TicketFlag.OK_AS_DELEGATE );
+    }
+    
+    
+    /**
+     * Converts the object to a printable string.
+     */
+    /*public static String toString( int flags )
+    {
+        StringBuilder result = new StringBuilder();
+
+        if ( ( flags & ( 1 << TicketFlag.RESERVED.getOrdinal() ) ) != 0 )
+        {
+            result.append( "RESERVED " );
+        }
+
+        if ( ( flags & ( 1 << TicketFlag.FORWARDABLE.getOrdinal() ) ) != 0 )
+        {
+            result.append( "FORWARDABLE " );
+        }
+
+        if ( ( flags & ( 1 << TicketFlag.FORWARDED.getOrdinal() ) ) != 0 )
+        {
+            result.append( "FORWARDED " );
+        }
+
+        if ( ( flags & ( 1 << TicketFlag.PROXIABLE.getOrdinal() ) ) != 0 )
+        {
+            result.append( "PROXIABLE " );
+        }
+
+        if ( ( flags & ( 1 << TicketFlag.PROXY.getOrdinal() ) ) != 0 )
+        {
+            result.append( "PROXY " );
+        }
+
+        if ( ( flags & ( 1 << TicketFlag.MAY_POSTDATE.getOrdinal() ) ) != 0 )
+        {
+            result.append( "MAY_POSTDATE " );
+        }
+
+        if ( ( flags & ( 1 << TicketFlag.POSTDATED.getOrdinal() ) ) != 0 )
+        {
+            result.append( "POSTDATED " );
+        }
+
+        if ( ( flags & ( 1 << TicketFlag.INVALID.getOrdinal() ) ) != 0 )
+        {
+            result.append( "INVALID " );
+        }
+
+        if ( ( flags & ( 1 << TicketFlag.RENEWABLE.getOrdinal() ) ) != 0 )
+        {
+            result.append( "RENEWABLE " );
+        }
+
+        if ( ( flags & ( 1 << TicketFlag.INITIAL.getOrdinal() ) ) != 0 )
+        {
+            result.append( "INITIAL " );
+        }
+
+        if ( ( flags & ( 1 << TicketFlag.PRE_AUTHENT.getOrdinal() ) ) != 0 )
+        {
+            result.append( "PRE_AUTHENT " );
+        }
+
+        if ( ( flags & ( 1 << TicketFlag.HW_AUTHENT.getOrdinal() ) ) != 0 )
+        {
+            result.append( "HW_AUTHENT " );
+        }
+
+        if ( ( flags & ( 1 << TicketFlag.TRANSITED_POLICY_CHECKED.getOrdinal() ) ) != 0 )
+        {
+            result.append( "TRANSITED_POLICY_CHECKED " );
+        }
+
+        if ( ( flags & ( 1 << TicketFlag.OK_AS_DELEGATE.getOrdinal() ) ) != 0 )
+        {
+            result.append( "OPTS_OK_AS_DELEGATE " );
+        }
+
+        return result.toString().trim();
+    }*/
+
+    
+    /**
+     * Converts the object to a printable string.
+     */
+    public String toString()
+    {
+        StringBuilder result = new StringBuilder();
+
+        if ( isFlagSet( TicketFlag.RESERVED ) )
+        {
+            result.append( "RESERVED(0) " );
+        }
+
+        if ( isFlagSet( TicketFlag.FORWARDABLE ) )
+        {
+            result.append( "FORWARDABLE(1) " );
+        }
+
+        if ( isFlagSet( TicketFlag.FORWARDED ) )
+        {
+            result.append( "FORWARDED(2) " );
+        }
+
+        if ( isFlagSet( TicketFlag.PROXIABLE ) )
+        {
+            result.append( "PROXIABLE(3) " );
+        }
+
+        if ( isFlagSet( TicketFlag.PROXY ) )
+        {
+            result.append( "PROXY(4) " );
+        }
+
+        if ( isFlagSet( TicketFlag.MAY_POSTDATE ) )
+        {
+            result.append( "MAY_POSTDATE(5) " );
+        }
+
+        if ( isFlagSet( TicketFlag.POSTDATED ) )
+        {
+            result.append( "POSTDATED(6) " );
+        }
+
+        if ( isFlagSet( TicketFlag.INVALID ) )
+        {
+            result.append( "INVALID(7) " );
+        }
+
+        if ( isFlagSet( TicketFlag.RENEWABLE ) )
+        {
+            result.append( "RENEWABLE(8) " );
+        }
+
+        if ( isFlagSet( TicketFlag.INITIAL ) )
+        {
+            result.append( "INITIAL(9) " );
+        }
+
+        if ( isFlagSet( TicketFlag.PRE_AUTHENT ) )
+        {
+            result.append( "PRE_AUTHENT(10) " );
+        }
+
+        if ( isFlagSet( TicketFlag.HW_AUTHENT ) )
+        {
+            result.append( "HW_AUTHENT(11) " );
+        }
+
+        if ( isFlagSet( TicketFlag.TRANSITED_POLICY_CHECKED ) )
+        {
+            result.append( "TRANSITED_POLICY_CHECKED(12) " );
+        }
+
+        if ( isFlagSet( TicketFlag.OK_AS_DELEGATE ) )
+        {
+            result.append( "OK_AS_DELEGATE(13) " );
+        }
+
+        return result.toString().trim();
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/package-info.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/package-info.java
new file mode 100644
index 0000000..5f3d858
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/package-info.java
@@ -0,0 +1,28 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+/**
+ * Provides value object components of Kerberos messages.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+
+package org.apache.directory.server.kerberos.shared.messages.value;
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/types/AuthorizationType.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/types/AuthorizationType.java
new file mode 100644
index 0000000..6e46d13
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/types/AuthorizationType.java
@@ -0,0 +1,229 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.value.types;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 540371 $, $Date: 2007-05-22 02:00:43 +0200 (Tue, 22 May 2007) $
+ */
+public enum AuthorizationType
+{
+    /**
+     * Constant for the "null" authorization type.
+     */
+    NULL( 0 ),
+
+    /**
+     * Constant for the "if relevant" authorization type.
+     * 
+     * RFC 4120
+     */
+    AD_IF_RELEVANT( 1 ),
+
+    /**
+     * Constant for the "intended for server" authorization type.
+     * 
+     * RFC 4120
+     */
+    AD_INTENDED_FOR_SERVER( 2 ),
+
+    /**
+     * Constant for the  "intended for application class" authorization type.
+     * 
+     * RFC 4120
+     */
+    AD_INTENDED_FOR_APPLICATION_CLASS( 3 ),
+
+    /**
+     * Constant for the "kdc issued" authorization type.
+     * 
+     * RFC 4120
+     */
+    AD_KDC_ISSUED( 4 ),
+
+    /**
+     * Constant for the "or" authorization type.
+     * 
+     * RFC 4120
+     */
+    AD_OR( 5 ),
+
+    /**
+     * Constant for the "mandatory ticket extensions" authorization type.
+     * 
+     * RFC 4120
+     */
+    AD_MANDATORY_TICKET_EXTENSIONS( 6 ),
+
+    /**
+     * Constant for the "in ticket extensions" authorization type.
+     * 
+     * RFC 4120
+     */
+    AD_IN_TICKET_EXTENSIONS( 7 ),
+
+    /**
+     * Constant for the "mandatory-for-kdc" authorization type.
+     * 
+     * RFC 4120
+     */
+    AD_MANDATORY_FOR_KDC( 8 ),
+
+    /**
+     * Constant for the "OSF DCE" authorization type.
+     * 
+     * RFC 1510
+     */
+    OSF_DCE( 64 ),
+
+    /**
+     * Constant for the "sesame" authorization type.
+     * 
+     * RFC 1510
+     */
+    SESAME( 65 ),
+
+    /**
+     * Constant for the "OSF-DCE pki certid" authorization type.
+     * 
+     * RFC 1510
+     */
+    AD_OSF_DCE_PKI_CERTID( 66 ),
+
+    /**
+     * Constant for the "sesame" authorization type.
+     * 
+     * RFC 1510
+     */
+    AD_WIN2K_PAC( 128 ),
+
+    /**
+     * Constant for the "sesame" authorization type.
+     * 
+     * RFC 1510
+     */
+    AD_ETYPE_NEGOTIATION( 129 );
+
+
+    /**
+     * The value/code for the authorization type.
+     */
+    private final int ordinal;
+
+
+    /**
+     * Private constructor prevents construction outside of this class.
+     */
+    private AuthorizationType( int ordinal )
+    {
+        this.ordinal = ordinal;
+    }
+
+
+    /**
+     * Returns the authorization type when specified by its ordinal.
+     *
+     * @param type
+     * @return The authorization type.
+     */
+    public static AuthorizationType getTypeByOrdinal( int type )
+    {
+        switch ( type )
+        {
+            case 1 :    return AD_IF_RELEVANT;
+            case 2 :    return AD_INTENDED_FOR_SERVER;
+            case 3 :    return AD_INTENDED_FOR_APPLICATION_CLASS;
+            case 4 :    return AD_KDC_ISSUED;
+            case 5 :    return AD_OR;
+            case 6 :    return AD_MANDATORY_TICKET_EXTENSIONS;
+            case 7 :    return AD_IN_TICKET_EXTENSIONS;
+            case 8 :    return AD_MANDATORY_FOR_KDC;
+            case 64 :   return OSF_DCE;
+            case 65 :   return SESAME;
+            case 66 :   return AD_OSF_DCE_PKI_CERTID;
+            case 128 :  return AD_WIN2K_PAC;
+            case 129 :  return AD_ETYPE_NEGOTIATION;
+            default :   return NULL;
+        }
+    }
+
+
+    /**
+     * Returns the number associated with this authorization type.
+     *
+     * @return The authorization type ordinal.
+     */
+    public int getOrdinal()
+    {
+        return ordinal;
+    }
+    
+    /**
+     * @see Object#toString()
+     */
+    public String toString()
+    {
+        switch ( this )
+        {
+            case AD_IF_RELEVANT                     : 
+                return "if relevant" + "(" + ordinal + ")";
+            
+            case AD_INTENDED_FOR_SERVER : 
+                return "intended for server" + "(" + ordinal + ")";
+            
+            case AD_INTENDED_FOR_APPLICATION_CLASS : 
+                return "intended for application class" + "(" + ordinal + ")";
+            
+            case AD_KDC_ISSUED : 
+                return "kdc issued" + "(" + ordinal + ")";
+            
+            case AD_OR : 
+                return "or" + "(" + ordinal + ")";
+            
+            case AD_MANDATORY_TICKET_EXTENSIONS : 
+                return "mandatory ticket extensions" + "(" + ordinal + ")";
+            
+            case AD_IN_TICKET_EXTENSIONS : 
+                return "in ticket extensions" + "(" + ordinal + ")";
+            
+            case AD_MANDATORY_FOR_KDC : 
+                return "mandatory-for-kdc" + "(" + ordinal + ")";
+            
+            case OSF_DCE : 
+                return "OSF DCE" + "(" + ordinal + ")";
+            
+            case SESAME : 
+                return "sesame" + "(" + ordinal + ")";
+                
+            case AD_OSF_DCE_PKI_CERTID :
+                return "OSF DCE pki certid" + "(" + ordinal + ")";
+            
+            case AD_WIN2K_PAC :
+                return "win 2000 PAC" + "(" + ordinal + ")";
+            
+            case AD_ETYPE_NEGOTIATION :
+                return "etype negociation" + "(" + ordinal + ")";
+            
+            default : 
+                return "null" + "(" + ordinal + ")";
+        }
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/types/HostAddrType.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/types/HostAddrType.java
new file mode 100644
index 0000000..6761e71
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/types/HostAddrType.java
@@ -0,0 +1,294 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.value.types;
+
+
+/**
+ * Host Address type. Theyare described in RFC 4120, chap. 7.5.3.
+ * 
+ * Only a few of them are declared :
+ * 
+ *   Address Type                   Value
+ *
+ *   IPv4                             2
+ *   Directional                      3
+ *   ChaosNet                         5
+ *   XNS                              6
+ *   ISO                              7
+ *   DECNET Phase IV                 12
+ *   AppleTalk DDP                   16
+ *   NetBios                         20
+ *   IPv6                            24
+ *
+ * The other address types are simply ignored. They are part of a Unix
+ * include file. 
+ * 
+ * @todo find the original include where those ignored values come from 
+ * 
+ * To be realistic, we may encounter IPv4, IPv6 and NetBios addresses in the real world...
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 540371 $, $Date: 2007-05-22 02:00:43 +0200 (Tue, 22 May 2007) $
+ */
+public enum HostAddrType
+{
+    /**
+     * Constant for the "null" host address type.
+     */
+    NULL( 0 ),
+
+    /**
+     * Constant for the "Unix" host address type.
+     * 
+     * Not in RFC
+     */
+    // ADDRTYPE_UNIX( 1 ),
+
+    /**
+     * Constant for the "Internet" host address type.
+     */
+    ADDRTYPE_INET( 2 ),
+
+    /**
+     * Constant for the "Arpanet" host address type.
+     */
+    ADDRTYPE_IMPLINK( 3 ),
+
+    /**
+     * Constant for the "PUP" host address type.
+     * 
+     * Not in RFC
+     */
+    //ADDRTYPE_PUP( 4 ),
+
+    /**
+     * Constant for the "CHAOS" host address type.
+     */
+    ADDRTYPE_CHAOS( 5 ),
+
+    /**
+     * Constant for the "XEROX Network Services" host address type.
+     */
+    ADDRTYPE_XNS( 6 ),
+
+    /**
+     * Constant for the "IPX" host address type.
+     * 
+     * Not in RFC
+     */
+    // ADDRTYPE_IPX( 6 ),
+
+    /**
+     * Constant for the "OSI" host address type.
+     */
+    ADDRTYPE_OSI( 7 ),
+
+    /**
+     * Constant for the "European Computer Manufacturers" host address type.
+     * 
+     * Not in RFC
+     */
+    //ADDRTYPE_ECMA( 8 ),
+
+    /**
+     * Constant for the "Datakit" host address type.
+     * 
+     * Not in RFC
+     */
+    //ADDRTYPE_DATAKIT( 9 ),
+
+    /**
+     * Constant for the "CCITT" host address type.
+     * 
+     * Not in RFC
+     */
+    //pADDRTYPE_CCITT( 10 ),
+
+    /**
+     * Constant for the "SNA" host address type.
+     * 
+     * Not in RFC
+     */
+    //ADDRTYPE_SNA( 11 ),
+
+    /**
+     * Constant for the "DECnet" host address type.
+     */
+    ADDRTYPE_DECNET( 12 ),
+
+    /**
+     * Constant for the "Direct Data Link Interface" host address type.
+     * 
+     * Not in RFC
+     */
+    //ADDRTYPE_DLI( 13 ),
+
+    /**
+     * Constant for the "LAT" host address type.
+     * 
+     * Not in RFC
+     */
+    //ADDRTYPE_LAT( 14 ),
+
+    /**
+     * Constant for the "NSC Hyperchannel" host address type.
+     * 
+     * Not in RFC
+     */
+    //ADDRTYPE_HYLINK( 15 ),
+
+    /**
+     * Constant for the "AppleTalk" host address type.
+     */
+    ADDRTYPE_APPLETALK( 16 ),
+
+    /**
+     * Constant for the "VoiceView" host address type.
+     * 
+     * Not in RFC
+     */
+    //ADDRTYPE_VOICEVIEW( 18 ),
+
+    /**
+     * Constant for the "Firefox" host address type.
+     * 
+     * Not in RFC
+     */
+    //ADDRTYPE_FIREFOX( 19 ),
+
+    /**
+     * Constant for the "NetBios" host address type.
+     * 
+     * Not in RFC
+     */
+    ADDRTYPE_NETBIOS( 20 ),
+
+    /**
+     * Constant for the "Banyan" host address type.
+     * 
+     * Not in RFC
+     */
+    //ADDRTYPE_BAN( 21 ),
+
+    /**
+     * Constant for the "ATM" host address type.
+     * 
+     * Not in RFC
+     */
+    //ADDRTYPE_ATM( 22 ),
+
+    /**
+     * Constant for the "Internet Protocol V6" host address type.
+     */
+    ADDRTYPE_INET6( 24 );
+
+    /**
+     * The value/code for the host address type.
+     */
+    private final int ordinal;
+
+
+    /**
+     * Private constructor prevents construction outside of this class.
+     */
+    private HostAddrType( int ordinal )
+    {
+        this.ordinal = ordinal;
+    }
+
+
+    /**
+     * Returns the host address type when specified by its ordinal.
+     *
+     * @param type
+     * @return The host address type.
+     */
+    public static HostAddrType getTypeByOrdinal( int type )
+    {
+        switch ( type )
+        {
+            case 0 : return NULL;
+            //case 1 : return ADDRTYPE_UNIX;
+            case 2 : return ADDRTYPE_INET;
+            case 3 : return ADDRTYPE_IMPLINK;
+            //case 4 : return ADDRTYPE_PUP;
+            case 5 : return ADDRTYPE_CHAOS;
+            case 6 : return ADDRTYPE_XNS;
+            case 7 : return ADDRTYPE_OSI;
+            //case 8 : return ADDRTYPE_ECMA;
+            //case 9 : return ADDRTYPE_DATAKIT;
+            //case 10 : return pADDRTYPE_CCITT;
+            //case 11 : return ADDRTYPE_SNA;
+            case 12 : return ADDRTYPE_DECNET;
+            //case 13 : return ADDRTYPE_DLI;
+            //case 14 : return ADDRTYPE_LAT;
+            //case 15 : return ADDRTYPE_HYLINK;
+            case 16 : return ADDRTYPE_APPLETALK;
+            //case 18 : return ADDRTYPE_VOICEVIEW;
+            //case 19 : return ADDRTYPE_FIREFOX;
+            case 20 : return ADDRTYPE_NETBIOS;
+            //case 21 : return ADDRTYPE_BAN;
+            //case 22 : return ADDRTYPE_ATM;
+            case 24 : return ADDRTYPE_INET6;
+            default : return NULL;
+        }
+    }
+
+
+    /**
+     * Returns the number associated with this host address type.
+     *
+     * @return The host address type ordinal.
+     */
+    public int getOrdinal()
+    {
+        return ordinal;
+    }
+
+    public String toString()
+    {
+        switch ( ordinal )
+        {
+            //case 1 : return "Unix" + "(" + ordinal + ")"  ;
+            case 2 : return "Internet" + "(" + ordinal + ")"  ;
+            case 3 : return "Arpanet" + "(" + ordinal + ")"  ;
+            //case 4 : return "PUP" + "(" + ordinal + ")"  ;
+            case 5 : return "CHAOS" + "(" + ordinal + ")"  ;
+            case 6 : return "XEROX Network Services" + "(" + ordinal + ")"  ;
+            case 7 : return "OSI" + "(" + ordinal + ")"  ;
+            //case 8 : return "European Computer Manufacturers" + "(" + ordinal + ")"  ;
+            //case 9 : return "Datakit" + "(" + ordinal + ")"  ;
+            //case 10 : return "CCITT" + "(" + ordinal + ")"  ;
+            //case 11 : return "SNA" + "(" + ordinal + ")"  ;
+            case 12 : return "DECnet" + "(" + ordinal + ")"  ;
+            //case 13 : return "Direct Data Link Interface" + "(" + ordinal + ")"  ;
+            //case 14 : return "LAT" + "(" + ordinal + ")"  ;
+            //case 15 : return "NSC Hyperchannel" + "(" + ordinal + ")"  ;
+            //case 16 : return "AppleTalk" + "(" + ordinal + ")"  ;
+            //case 18 : return "VoiceView" + "(" + ordinal + ")"  ;
+            //case 19 : return "Firefox" + "(" + ordinal + ")"  ;
+            case 20 : return "NetBios" + "(" + ordinal + ")"  ;
+            //case 21 : return "Banyan" + "(" + ordinal + ")"  ;
+            //case 22 : return "ATM" + "(" + ordinal + ")"  ;
+            case 24 : return "Internet Protocol V6" + "(" + ordinal + ")" ;             
+            default : return "null" + "(" + ordinal + ")" ;
+        }
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/types/PaDataType.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/types/PaDataType.java
new file mode 100644
index 0000000..a0558be
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/types/PaDataType.java
@@ -0,0 +1,238 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.value.types;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 540371 $, $Date: 2007-05-22 02:00:43 +0200 (Tue, 22 May 2007) $
+ */
+public enum PaDataType
+{
+    /**
+     * Constant for the "null" pre-authentication data type.
+     */
+    NULL( 0 ),
+
+    /**
+     * Constant for the "TGS request" pre-authentication data type.
+     */
+    PA_TGS_REQ( 1 ),
+
+    /**
+     * Constant for the "encrypted timestamp" pre-authentication data type.
+     */
+    PA_ENC_TIMESTAMP( 2 ),
+
+    /**
+     * Constant for the "password salt" pre-authentication data type.
+     */
+    PA_PW_SALT( 3 ),
+
+    /**
+     * Constant for the "enc unix time" pre-authentication data type.
+     */
+    PA_ENC_UNIX_TIME( 5 ),
+
+    /**
+     * Constant for the "sandia secureid" pre-authentication data type.
+     */
+    PA_SANDIA_SECUREID( 6 ),
+
+    /**
+     * Constant for the "sesame" pre-authentication data type.
+     */
+    PA_SESAME( 7 ),
+
+    /**
+     * Constant for the "OSF DCE" pre-authentication data type.
+     */
+    PA_OSF_DCE( 8 ),
+
+    /**
+     * Constant for the "cybersafe secureid" pre-authentication data type.
+     */
+    PA_CYBERSAFE_SECUREID( 9 ),
+
+    /**
+     * Constant for the "ASF3 salt" pre-authentication data type.
+     */
+    PA_ASF3_SALT( 10 ),
+
+    /**
+     * Constant for the "encryption info" pre-authentication data type.
+     */
+    PA_ENCTYPE_INFO( 11 ),
+
+    /**
+     * Constant for the "SAM challenge" pre-authentication data type.
+     */
+    SAM_CHALLENGE( 12 ),
+
+    /**
+     * Constant for the "SAM response" pre-authentication data type.
+     */
+    SAM_RESPONSE( 13 ),
+
+    /**
+     * Constant for the "PK as request" pre-authentication data type.
+     */
+    PA_PK_AS_REQ( 14 ),
+
+    /**
+     * Constant for the "PK as response" pre-authentication data type.
+     */
+    PA_PK_AS_REP( 15 ),
+
+    /**
+     * Constant for the "use specified key version" pre-authentication data type.
+     */
+    PA_USE_SPECIFIED_KVNO( 20 ),
+
+    /**
+     * Constant for the "SAM redirect" pre-authentication data type.
+     */
+    SAM_REDIRECT( 21 ),
+
+    /**
+     * Constant for the "get from typed data" pre-authentication data type.
+     */
+    PA_GET_FROM_TYPED_DATA( 22 );
+
+
+    /**
+     * The value/code for the pre-authentication type.
+     */
+    private final int ordinal;
+
+
+    /**
+     * Private constructor prevents construction outside of this class.
+     */
+    private PaDataType( int ordinal )
+    {
+        this.ordinal = ordinal;
+    }
+
+
+    /**
+     * Returns the number associated with this pre-authentication type.
+     *
+     * @return The pre-authentication type ordinal.
+     */
+    public int getOrdinal()
+    {
+        return ordinal;
+    }
+    
+    /**
+     * Returns the pre authentication data type when specified by its ordinal.
+     *
+     * @param type The ordinal
+     * @return The pre authentication type.
+     */
+    public static PaDataType getTypeByOrdinal( int type )
+    {
+        switch ( type )
+        {
+            case 1 :    return PA_TGS_REQ;
+            case 2 :    return PA_ENC_TIMESTAMP;
+            case 3 :    return PA_PW_SALT;
+            case 5 :    return PA_ENC_UNIX_TIME;
+            case 6 :    return PA_SANDIA_SECUREID;
+            case 7 :    return PA_SESAME;
+            case 8 :    return PA_OSF_DCE;
+            case 9 :    return PA_CYBERSAFE_SECUREID;
+            case 10 :   return PA_ASF3_SALT;
+            case 11 :   return PA_ENCTYPE_INFO;
+            case 12 :   return SAM_CHALLENGE;
+            case 13 :   return SAM_RESPONSE;
+            case 14 :   return PA_PK_AS_REQ;
+            case 15 :   return PA_PK_AS_REQ;
+            case 20 :   return PA_USE_SPECIFIED_KVNO;
+            case 21 :   return SAM_REDIRECT;
+            case 22 :   return PA_GET_FROM_TYPED_DATA;
+            default :   return NULL;
+        }
+    }
+
+    /**
+     * @see Object#toString()
+     */
+    public String toString()
+    {
+        switch ( this )
+        {
+            case PA_TGS_REQ                     : 
+                return "TGS request." + "(" + ordinal + ")";
+            
+            case PA_ENC_TIMESTAMP : 
+                return "Encrypted timestamp." + "(" + ordinal + ")";
+            
+            case PA_PW_SALT : 
+                return "password salt" + "(" + ordinal + ")";
+            
+            case PA_ENC_UNIX_TIME : 
+                return "enc unix time" + "(" + ordinal + ")";
+            
+            case PA_SANDIA_SECUREID : 
+                return "sandia secureid" + "(" + ordinal + ")";
+            
+            case PA_SESAME : 
+                return "sesame" + "(" + ordinal + ")";
+            
+            case PA_OSF_DCE : 
+                return "OSF DCE" + "(" + ordinal + ")";
+            
+            case PA_CYBERSAFE_SECUREID : 
+                return "cybersafe secureid" + "(" + ordinal + ")";
+            
+            case PA_ASF3_SALT : 
+                return "ASF3 salt" + "(" + ordinal + ")";
+            
+            case PA_ENCTYPE_INFO : 
+                return "Encryption info." + "(" + ordinal + ")";
+            
+            case SAM_CHALLENGE : 
+                return "SAM challenge." + "(" + ordinal + ")";
+            
+            case SAM_RESPONSE : 
+                return "SAM response." + "(" + ordinal + ")";
+            
+            case PA_PK_AS_REQ : 
+                return "PK as request" + "(" + ordinal + ")";
+            
+            case PA_PK_AS_REP : 
+                return "PK as response" + "(" + ordinal + ")";
+                
+            case PA_USE_SPECIFIED_KVNO :
+                return "use specified key version" + "(" + ordinal + ")";
+            
+            case SAM_REDIRECT :
+                return "SAM redirect." + "(" + ordinal + ")";
+            
+            case PA_GET_FROM_TYPED_DATA :
+                return "Get from typed data" + "(" + ordinal + ")";
+            
+            default : 
+                return "null" + "(" + ordinal + ")";
+        }
+    }
+}
\ No newline at end of file
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/types/PrincipalNameType.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/types/PrincipalNameType.java
new file mode 100644
index 0000000..48885b4
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/types/PrincipalNameType.java
@@ -0,0 +1,178 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.value.types;
+
+
+/**
+ * An enum describing the differnet types of Principal.
+ * 
+ * Here is the list, taken from RFC 4120 :
+ *  NT-UNKNOWN        0    Name type not known
+ *  NT-PRINCIPAL      1    Just the name of the principal as in DCE,
+ *                           or for users
+ *  NT-SRV-INST       2    Service and other unique instance (krbtgt)
+ *  NT-SRV-HST        3    Service with host name as instance
+ *                           (telnet, rcommands)
+ *  NT-SRV-XHST       4    Service with host as remaining components
+ *  NT-UID            5    Unique ID
+ *  NT-X500-PRINCIPAL 6    Encoded X.509 Distinguished name [RFC2253]
+ *  NT-SMTP-NAME      7    Name in form of SMTP email name
+ *                           (e.g., user@example.com)
+ *  NT-ENTERPRISE    10    Enterprise name - may be mapped to principal
+ *                           name
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 540371 $, $Date: 2007-05-22 02:00:43 +0200 (Tue, 22 May 2007) $
+ */
+public enum PrincipalNameType
+{
+    /**
+     * Constant for the "Name type not known" principal name type.
+     */
+    KRB_NT_UNKNOWN( 0 ),
+
+    /**
+     * Constant for the "Just the name of the principal as in DCE, or for users" principal name type.
+     */
+    KRB_NT_PRINCIPAL( 1 ),
+
+    /**
+     * Constant for the "Service and other unique instance (krbtgt)" principal name type.
+     */
+    KRB_NT_SRV_INST( 2 ),
+
+    /**
+     * Constant for the "Service with host name as instance (telnet, rcommands)" principal name type.
+     */
+    KRB_NT_SRV_HST( 3 ),
+
+    /**
+     * Constant for the "Service with host as remaining components" principal name type.
+     */
+    KRB_NT_SRV_XHST( 4 ),
+
+    /**
+     * Constant for the "Unique ID" principal name type.
+     */
+    KRB_NT_UID( 5 ),
+
+    /**
+     * Constant for the "Encoded X.509 Distinguished name [RFC2253]" principal name type.
+     */
+    KRB_NT_X500_PRINCIPAL( 6 ),
+
+    /**
+     * Constant for the "Name in form of SMTP email name (e.g., user@example.com)" principal name type.
+     */
+    KRB_NT_SMTP_NAME( 7 ),
+
+    /**
+     * Constant for the "Enterprise name; may be mapped to principal name" principal name type.
+     */
+    KRB_NT_ENTERPRISE( 10 );
+
+    /**
+     * The value/code for the principal name type.
+     */
+    private final int ordinal;
+
+
+    /**
+     * Private constructor prevents construction outside of this class.
+     */
+    private PrincipalNameType( int ordinal )
+    {
+        this.ordinal = ordinal;
+    }
+
+
+    /**
+     * Returns the principal name type when specified by its ordinal.
+     *
+     * @param type
+     * @return The principal name type.
+     */
+    public static PrincipalNameType getTypeByOrdinal( int type )
+    {
+        switch ( type )
+        {
+            case 0 : return KRB_NT_UNKNOWN;
+            case 1 : return KRB_NT_PRINCIPAL;
+            case 2 : return KRB_NT_SRV_INST;
+            case 3 : return KRB_NT_SRV_HST;
+            case 4 : return KRB_NT_SRV_XHST;
+            case 5 : return KRB_NT_UID;
+            case 6 : return KRB_NT_X500_PRINCIPAL;
+            case 7 : return KRB_NT_SMTP_NAME;
+            case 10 : return KRB_NT_ENTERPRISE;
+            default : return KRB_NT_UNKNOWN;
+        }
+    }
+
+
+    /**
+     * Returns the number associated with this principal name type.
+     *
+     * @return The principal name type ordinal.
+     */
+    public int getOrdinal()
+    {
+        return ordinal;
+    }
+
+    /**
+     * @see Object#toString()
+     */
+    public String toString()
+    {
+        switch ( this )
+        {
+            case KRB_NT_UNKNOWN         : 
+                return "Name type not known" + "(" + ordinal + ")";
+                
+            case KRB_NT_PRINCIPAL       : 
+                return "Just the name of the principal as in DCE, or for users" + "(" + ordinal + ")";
+                
+            case KRB_NT_SRV_INST        : 
+                return "Service and other unique instance (krbtgt)" + "(" + ordinal + ")";
+            
+            case KRB_NT_SRV_HST         : 
+                return "Service with host name as instance (telnet, rcommands)" + "(" + ordinal + ")";
+            
+            case KRB_NT_SRV_XHST        : 
+                return "Service with host as remaining components" + "(" + ordinal + ")";
+            
+            case KRB_NT_UID             : 
+                return "Unique ID" + "(" + ordinal + ")";
+            
+            case KRB_NT_X500_PRINCIPAL  : 
+                return "Encoded X.509 Distinguished name [RFC2253]" + "(" + ordinal + ")";
+            
+            case KRB_NT_SMTP_NAME       : 
+                return "Name in form of SMTP email name (e.g., user@example.com)" + "(" + ordinal + ")";
+            
+            case KRB_NT_ENTERPRISE      : 
+                return "Enterprise name; may be mapped to principal name" + "(" + ordinal + ")";
+            
+            default                     : 
+                return "unknown name type" + "(" + ordinal + ")";
+        }
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/types/TransitedEncodingType.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/types/TransitedEncodingType.java
new file mode 100644
index 0000000..787102d
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/messages/value/types/TransitedEncodingType.java
@@ -0,0 +1,92 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.value.types;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 540371 $, $Date: 2007-05-22 02:00:43 +0200 (Tue, 22 May 2007) $
+ */
+public enum TransitedEncodingType
+{
+    /**
+     * Constant for the "null" transited encoding type.
+     */
+    NULL( 0 ),
+
+    /**
+     * Constant for the "Domain X500 compress" transited encoding type.
+     */
+    DOMAIN_X500_COMPRESS( 1 );
+
+
+    /**
+     * The value/code for the transited encoding type.
+     */
+    private final int ordinal;
+
+
+    /**
+     * Private constructor prevents construction outside of this class.
+     */
+    private TransitedEncodingType( int ordinal )
+    {
+        this.ordinal = ordinal;
+    }
+
+
+    /**
+     * Returns the transited encoding type when specified by its ordinal.
+     *
+     * @param type
+     * @return The transited encoding type.
+     */
+    public static TransitedEncodingType getTypeByOrdinal( int type )
+    {
+        switch ( type )
+        {
+            case 1  : return DOMAIN_X500_COMPRESS;
+            default : return NULL;
+        }
+    }
+
+
+    /**
+     * Returns the number associated with this transited encoding type.
+     *
+     * @return The transited encoding type ordinal.
+     */
+    public int getOrdinal()
+    {
+        return ordinal;
+    }
+
+    /**
+     * @see Object#toString()
+     */
+    public String toString()
+    {
+        switch ( this )
+        {
+            case DOMAIN_X500_COMPRESS : return "Domain X500 compress (1)";
+            default :                   return "null (0)";
+        }
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/replay/InMemoryReplayCache.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/replay/InMemoryReplayCache.java
new file mode 100644
index 0000000..2faa760
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/replay/InMemoryReplayCache.java
@@ -0,0 +1,315 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.replay;
+
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
+
+
+/**
+ * "The replay cache will store at least the server name, along with the client name,
+ * time, and microsecond fields from the recently-seen authenticators, and if a
+ * matching tuple is found, the KRB_AP_ERR_REPEAT error is returned."
+ * 
+ * We will store the entries using an HashMap which key will be the client
+ * principal, and we will store a list of entries for each client principal.
+ * 
+ * A thread will run every N seconds to clean the cache from entries out of the 
+ * clockSkew
+ *    
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class InMemoryReplayCache extends Thread implements ReplayCache
+{
+    /** Stores the entries in memory */
+    private Map<KerberosPrincipal, List<ReplayCacheEntry>> cache = new HashMap<KerberosPrincipal, List<ReplayCacheEntry>>();
+
+    /** default clock skew */
+    private static final long DEFAULT_CLOCK_SKEW = 5 * KerberosTime.MINUTE;
+    
+    /** The clock skew */
+    private long clockSkew = DEFAULT_CLOCK_SKEW;
+
+    /** The default delay between each run of the cleaning process : 5 s */
+    private static long DEFAULT_DELAY = 5 * 1000;  
+    
+    /** The delay to wait between each cache cleaning */
+    private long delay;
+
+    /**
+     * A structure to hold an entry
+     */
+    public class ReplayCacheEntry
+    {
+        private KerberosPrincipal serverPrincipal;
+        private KerberosPrincipal clientPrincipal;
+        private KerberosTime clientTime;
+        private int clientMicroSeconds;
+
+
+        /**
+         * Creates a new instance of ReplayCacheEntry.
+         * 
+         * @param serverPrincipal 
+         * @param clientPrincipal 
+         * @param clientTime 
+         * @param clientMicroSeconds 
+         */
+        public ReplayCacheEntry( KerberosPrincipal serverPrincipal, KerberosPrincipal clientPrincipal,
+            KerberosTime clientTime, int clientMicroSeconds )
+        {
+            this.serverPrincipal = serverPrincipal;
+            this.clientPrincipal = clientPrincipal;
+            this.clientTime = clientTime;
+            this.clientMicroSeconds = clientMicroSeconds;
+        }
+
+
+        /**
+         * Returns whether this {@link ReplayCacheEntry} is equal to another {@link ReplayCacheEntry}.
+         * {@link ReplayCacheEntry}'s are equal when the server name, client name, client time, and
+         * the client microseconds are equal.
+         *
+         * @param that
+         * @return true if the ReplayCacheEntry's are equal.
+         */
+        public boolean equals( ReplayCacheEntry that )
+        {
+            return serverPrincipal.equals( that.serverPrincipal ) && clientPrincipal.equals( that.clientPrincipal )
+                && clientTime.equals( that.clientTime ) && clientMicroSeconds == that.clientMicroSeconds;
+        }
+
+
+        /**
+         * Returns whether this {@link ReplayCacheEntry} is older than a given time.
+         *
+         * @param clockSkew
+         * @return true if the {@link ReplayCacheEntry}'s client time is outside the clock skew time.
+         */
+        public boolean isOutsideClockSkew( long clockSkew )
+        {
+            return !clientTime.isInClockSkew( clockSkew );
+        }
+    }
+
+    
+    /**
+     * Creates a new instance of InMemoryReplayCache. Sets the
+     * delay between each cleaning run to 5 seconds.
+     */
+    public InMemoryReplayCache()
+    {
+        cache = new HashMap<KerberosPrincipal, List<ReplayCacheEntry>>();
+        delay = DEFAULT_DELAY;
+        this.start();
+    }
+    
+    
+    /**
+     * Creates a new instance of InMemoryReplayCache. Sets the
+     * delay between each cleaning run to 5 seconds. Sets the
+     * clockSkew to the given value
+     * 
+     * @param clockSkew the allowed skew (milliseconds)
+     */
+    public InMemoryReplayCache( long clockSkew )
+    {
+        cache = new HashMap<KerberosPrincipal, List<ReplayCacheEntry>>();
+        delay = DEFAULT_DELAY;
+        this.clockSkew = clockSkew;
+        this.start();
+    }
+    
+    
+    /**
+     * Creates a new instance of InMemoryReplayCache. Sets the
+     * clockSkew to the given value, and set the cleaning thread 
+     * kick off delay
+     * 
+     * @param clockSkew the allowed skew (milliseconds)
+     * @param delay the interval between each run of the cache 
+     * cleaning thread (milliseconds)
+     */
+    public InMemoryReplayCache( long clockSkew, int delay  )
+    {
+        cache = new HashMap<KerberosPrincipal, List<ReplayCacheEntry>>();
+        this.delay = (long)delay;
+        this.clockSkew = clockSkew;
+        this.start();
+    }
+    
+    
+    /**
+     * Creates a new instance of InMemoryReplayCache. Sets the
+     * delay between each cleaning run to 5 seconds. Sets the 
+     * cleaning thread kick off delay
+     * 
+     * @param delay the interval between each run of the cache 
+     * cleaning thread (milliseconds).
+     */
+    public InMemoryReplayCache( int delay )
+    {
+        cache = new HashMap<KerberosPrincipal, List<ReplayCacheEntry>>();
+        this.delay = (long)delay;
+        this.clockSkew = DEFAULT_CLOCK_SKEW;
+    }
+    
+    
+    /**
+     * Sets the clock skew.
+     *
+     * @param clockSkew
+     */
+    public void setClockSkew( long clockSkew )
+    {
+        this.clockSkew = clockSkew;
+    }
+
+    
+    /**
+     * Set the delay between each cleaning thread run.
+     *
+     * @param delay delay in milliseconds
+     */
+    public void setDelay( long delay )
+    {
+        this.delay = delay;
+    }
+
+    /**
+     * Check if an entry is a replay or not.
+     */
+    public synchronized boolean isReplay( KerberosPrincipal serverPrincipal, KerberosPrincipal clientPrincipal,
+        KerberosTime clientTime, int clientMicroSeconds )
+    {
+        List<ReplayCacheEntry> entries = cache.get( clientPrincipal );
+        
+        if ( ( entries == null ) || ( entries.size() == 0 ) )
+        {
+            return false;
+        }
+        
+        for ( ReplayCacheEntry entry:entries )
+        {
+            if ( serverPrincipal.equals( entry.serverPrincipal ) && 
+                 clientTime.equals( entry.clientTime ) && 
+                 (clientMicroSeconds == entry.clientMicroSeconds ) )
+            {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+
+    /**
+     * Add a new entry into the cache. A thread will clean all the timed out
+     * entries.
+     */
+    public synchronized void save( KerberosPrincipal serverPrincipal, KerberosPrincipal clientPrincipal,
+        KerberosTime clientTime, int clientMicroSeconds )
+    {
+        List<ReplayCacheEntry> entries = cache.get( clientPrincipal );
+        
+        if ( entries == null )
+        {
+            entries = new ArrayList<ReplayCacheEntry>();
+        }
+        
+        entries.add( new ReplayCacheEntry( serverPrincipal, clientPrincipal, clientTime, clientMicroSeconds ) );
+        
+        cache.put( clientPrincipal, entries );
+    }
+
+    
+    public Map<KerberosPrincipal, List<ReplayCacheEntry>> getCache()
+    {
+        return cache;
+    }
+    
+    /**
+     * A method to remove all the expired entries from the cache.
+     */
+    private synchronized void cleanCache()
+    {
+        Collection<List<ReplayCacheEntry>> entryList = cache.values();
+        
+        if ( ( entryList == null ) || ( entryList.size() == 0 ) )
+        {
+            return;
+        }
+        
+        for ( List<ReplayCacheEntry> entries:entryList )
+        {
+            if ( ( entries == null ) || ( entries.size() == 0 ) )
+            {
+                continue;
+            }
+            
+            Iterator<ReplayCacheEntry> iterator = entries.iterator();
+            
+            while ( iterator.hasNext() )
+            {
+                ReplayCacheEntry entry = iterator.next();
+                
+                if ( entry.isOutsideClockSkew( clockSkew ) )
+                {
+                    iterator.remove();
+                }
+                else
+                {
+                    break;
+                }
+            }
+        }
+    }
+    
+    
+    /** 
+     * The cleaning thread. It runs every N seconds.
+     */
+    public void run()
+    {
+        while ( true )
+        {
+            try
+            {
+                Thread.sleep( delay );
+                
+                cleanCache();
+            }
+            catch ( InterruptedException ie )
+            {
+                return;
+            }
+        }
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/replay/ReplayCache.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/replay/ReplayCache.java
new file mode 100644
index 0000000..3effd0b
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/replay/ReplayCache.java
@@ -0,0 +1,63 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.replay;
+
+
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
+
+
+/**
+ * "The replay cache will store at least the server name, along with the client name,
+ * time, and microsecond fields from the recently-seen authenticators, and if a
+ * matching tuple is found, the KRB_AP_ERR_REPEAT error is returned."
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public interface ReplayCache
+{
+    /**
+     * Returns whether a request is a replay, based on the server principal, client
+     * principal, time, and microseconds.
+     * 
+     * @param serverPrincipal The server principal 
+     * @param clientPrincipal The client principal
+     * @param clientTime The client time
+     * @param clientMicroSeconds The client microsecond
+     * @return true if the request is a replay.
+     */
+    boolean isReplay( KerberosPrincipal serverPrincipal, KerberosPrincipal clientPrincipal, KerberosTime clientTime,
+        int clientMicroSeconds );
+
+
+    /**
+     * Saves the server principal, client principal, time, and microseconds to
+     * the replay cache.
+     *
+     * @param serverPrincipal The server principal 
+     * @param clientPrincipal The client principal
+     * @param clientTime The client time
+     * @param clientMicroSeconds The client microsecond
+     */
+    void save( KerberosPrincipal serverPrincipal, KerberosPrincipal clientPrincipal, KerberosTime clientTime,
+        int clientMicroSeconds );
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/replay/package-info.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/replay/package-info.java
new file mode 100644
index 0000000..796e55d
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/replay/package-info.java
@@ -0,0 +1,29 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+/**
+ * Provides the interface and in-memory implementation of
+ * a Kerberos replay cache.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+
+package org.apache.directory.server.kerberos.shared.replay;
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/service/package-info.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/service/package-info.java
new file mode 100644
index 0000000..fe4dc6f
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/service/package-info.java
@@ -0,0 +1,29 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+/**
+ * Provides reusable {@link org.apache.mina.handler.chain.IoHandlerCommand}s
+ * for the Kerberos and Change Password protocols.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+
+package org.apache.directory.server.kerberos.shared.service;
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/store/JndiPrincipalStoreImpl.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/store/JndiPrincipalStoreImpl.java
new file mode 100644
index 0000000..a036312
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/store/JndiPrincipalStoreImpl.java
@@ -0,0 +1,97 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.store;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+
+/**
+ * A JNDI-backed implementation of the PrincipalStore interface.  This PrincipalStore uses
+ * the Strategy pattern to either serve principals based on a single base DN or to lookup
+ * catalog mappings from configuration in the DIT.  The strategy is chosen based on the
+ * presence of a catalog base DN.  If the catalog base DN is not present, the single
+ * entry base DN is searched, instead.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class JndiPrincipalStoreImpl implements PrincipalStore
+{
+    /** a handle on the search strategy */
+    private final PrincipalStore store;
+
+
+    /**
+     * Creates a new instance of JndiPrincipalStoreImpl.
+     *
+     * @param catalogBaseDn dn for a catalog of search dns.
+     * @param searchBaseDn if no catalog, single search dn.
+     * @param directoryService the core service
+     */
+    public JndiPrincipalStoreImpl( String catalogBaseDn, String searchBaseDn, DirectoryService directoryService )
+    {
+        store = getStore( catalogBaseDn, searchBaseDn, directoryService );
+    }
+
+
+    public String addPrincipal( PrincipalStoreEntry entry ) throws Exception
+    {
+        return store.addPrincipal( entry );
+    }
+
+
+    public String deletePrincipal( KerberosPrincipal principal ) throws Exception
+    {
+        return store.deletePrincipal( principal );
+    }
+
+
+    public PrincipalStoreEntry[] getAllPrincipals( String realm ) throws Exception
+    {
+        return store.getAllPrincipals( realm );
+    }
+
+
+    public PrincipalStoreEntry getPrincipal( KerberosPrincipal principal ) throws Exception
+    {
+        return store.getPrincipal( principal );
+    }
+
+
+    public String changePassword( KerberosPrincipal principal, String newPassword ) throws Exception
+    {
+        return store.changePassword( principal, newPassword );
+    }
+
+
+    private static PrincipalStore getStore( String catalogBaseDn, String searchBaseDn, DirectoryService directoryService )
+    {
+        if ( catalogBaseDn != null )
+        {
+            // build a catalog from the backing store
+            return new MultiBaseSearch( catalogBaseDn, directoryService );
+        }
+
+        // search only the configured entry baseDN
+        return new SingleBaseSearch( searchBaseDn, directoryService );
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/store/KerberosAttribute.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/store/KerberosAttribute.java
new file mode 100644
index 0000000..20089c2
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/store/KerberosAttribute.java
@@ -0,0 +1,61 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.store;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class KerberosAttribute
+{
+    // ------------------------------------------------------------------------
+    // Krb5 Schema Attributes
+    // ------------------------------------------------------------------------
+    /** the krb5kdc schema principal name for a krb5KDCEntry */
+    public static final String KRB5_PRINCIPAL_NAME_AT = "krb5PrincipalName";
+    public static final String KRB5_PRINCIPAL_NAME_AT_OID = "1.3.6.1.4.1.5322.10.1.1";
+    
+    /** the krb5kdc schema key for a krb5KDCEntry */
+    public static final String KRB5_KEY_AT = "krb5Key";
+    public static final String KRB5_KEY_AT_OID = "1.3.6.1.4.1.5322.10.1.10";
+    
+    /** the krb5kdc schema key version identifier for a krb5KDCEntry */
+    public static final String KRB5_KEY_VERSION_NUMBER_AT = "krb5KeyVersionNumber";
+    public static final String KRB5_KEY_VERSION_NUMBER_AT_OID = "1.3.6.1.4.1.5322.10.1.2";
+    
+    /** the disabled boolean LDAP attribute for a Kerberos account */
+    public static final String KRB5_ACCOUNT_DISABLED_AT = "krb5AccountDisabled";
+    public static final String KRB5_ACCOUNT_DISABLED_AT_OID = "1.3.6.1.4.1.5322.10.1.13";
+    
+    /** the lockedout boolean LDAP attribute for a Kerberos account */
+    public static final String KRB5_ACCOUNT_LOCKEDOUT_AT = "krb5AccountLockedOut";
+    public static final String KRB5_ACCOUNT_LOCKEDOUT_AT_OID = "1.3.6.1.4.1.5322.10.1.14";
+    
+    /** the expiration time attribute LDAP attribute for a Kerberos account */
+    public static final String KRB5_ACCOUNT_EXPIRATION_TIME_AT = "krb5AccountExpirationTime";
+    public static final String KRB5_ACCOUNT_EXPIRATION_TIME_AT_OID = "1.3.6.1.4.1.5322.10.1.15";
+
+
+    /** the Apache specific SAM type attribute */
+    public static final String APACHE_SAM_TYPE_AT = "apacheSamType";
+    public static final String APACHE_SAM_TYPE_AT_OID = "1.3.6.1.4.1.18060.0.4.1.2.9";
+    
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/store/KerberosCatalog.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/store/KerberosCatalog.java
new file mode 100644
index 0000000..5e1d3b6
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/store/KerberosCatalog.java
@@ -0,0 +1,83 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.kerberos.shared.store;
+
+
+import java.util.Map;
+
+import org.apache.directory.server.protocol.shared.catalog.Catalog;
+
+
+/**
+ * A catalog for mapping Kerberos realms to search base DN's. 
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+class KerberosCatalog implements Catalog
+{
+    private Map map;
+
+
+    /**
+     * Creates a new instance of KerberosCatalog.
+     *
+     * @param map
+     */
+    public KerberosCatalog( Map map )
+    {
+        this.map = map;
+    }
+
+
+    public String getBaseDn( String name )
+    {
+        name = name.toLowerCase();
+
+        if ( name.endsWith( "." ) )
+        {
+            int last = name.lastIndexOf( "." );
+            name = name.substring( 0, last );
+        }
+
+        while ( !name.equals( "" ) && name != null )
+        {
+            String candidate = ( String ) map.get( name );
+            if ( candidate != null )
+            {
+                return candidate;
+            }
+
+            int period = name.indexOf( "." );
+
+            if ( period > -1 )
+            {
+                name = name.substring( period + 1 );
+            }
+            else
+            {
+                return "";
+            }
+        }
+
+        return "";
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/store/MultiBaseSearch.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/store/MultiBaseSearch.java
new file mode 100644
index 0000000..16e9344
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/store/MultiBaseSearch.java
@@ -0,0 +1,153 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.kerberos.shared.store;
+
+
+import java.util.Map;
+
+import javax.naming.NamingException;
+import javax.naming.directory.DirContext;
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.kerberos.shared.store.operations.AddPrincipal;
+import org.apache.directory.server.kerberos.shared.store.operations.ChangePassword;
+import org.apache.directory.server.kerberos.shared.store.operations.DeletePrincipal;
+import org.apache.directory.server.kerberos.shared.store.operations.GetAllPrincipals;
+import org.apache.directory.server.kerberos.shared.store.operations.GetPrincipal;
+import org.apache.directory.server.protocol.shared.ServiceConfigurationException;
+import org.apache.directory.server.protocol.shared.catalog.Catalog;
+import org.apache.directory.server.protocol.shared.catalog.GetCatalog;
+import org.apache.directory.server.protocol.shared.store.ContextOperation;
+
+
+/**
+ * A JNDI-backed search strategy implementation.  This search strategy builds a
+ * catalog from configuration in the DIT to determine where realms are to search
+ * for Kerberos principals.
+ *
+ * TODO are exception messages reasonable? I changed them to use the catalog key rather than the catalog value.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+class MultiBaseSearch implements PrincipalStore
+{
+    private final Catalog catalog;
+    private final DirectoryService directoryService;
+
+
+    MultiBaseSearch( String catalogBaseDn, DirectoryService directoryService )
+    {
+        this.directoryService = directoryService;
+        try
+        {
+            DirContext ctx = directoryService.getJndiContext(catalogBaseDn);
+            catalog = new KerberosCatalog( ( Map ) execute( ctx, new GetCatalog() ) );
+        }
+        catch ( Exception e )
+        {
+            String message = "Failed to get catalog context " + catalogBaseDn;
+            throw new ServiceConfigurationException( message, e );
+        }
+    }
+
+
+    public String addPrincipal( PrincipalStoreEntry entry ) throws Exception
+    {
+        try
+        {
+            return ( String ) execute( getDirContext( entry.getRealmName() ), new AddPrincipal( entry ) );
+        }
+        catch ( NamingException ne )
+        {
+            String message = "Failed to get initial context " + entry.getRealmName();
+            throw new ServiceConfigurationException( message, ne );
+        }
+    }
+
+    public String deletePrincipal( KerberosPrincipal principal ) throws Exception
+    {
+        try
+        {
+            return ( String ) execute( getDirContext( principal.getRealm() ), new DeletePrincipal( principal ) );
+        }
+        catch ( NamingException ne )
+        {
+            String message = "Failed to get initial context " + principal.getRealm();
+            throw new ServiceConfigurationException( message, ne );
+        }
+    }
+
+
+    public PrincipalStoreEntry[] getAllPrincipals( String realm ) throws Exception
+    {
+        try
+        {
+            return ( PrincipalStoreEntry[] ) execute( getDirContext( realm ), new GetAllPrincipals() );
+        }
+        catch ( NamingException ne )
+        {
+            String message = "Failed to get initial context " + realm;
+            throw new ServiceConfigurationException( message, ne );
+        }
+    }
+
+
+    public PrincipalStoreEntry getPrincipal( KerberosPrincipal principal ) throws Exception
+    {
+        try
+        {
+            return ( PrincipalStoreEntry ) execute( getDirContext( principal.getRealm() ), new GetPrincipal( principal ) );
+        }
+        catch ( NamingException ne )
+        {
+            String message = "Failed to get initial context " + principal.getRealm();
+            throw new ServiceConfigurationException( message, ne );
+        }
+    }
+
+
+    public String changePassword( KerberosPrincipal principal, String newPassword ) throws Exception
+    {
+        try
+        {
+            return ( String ) execute( getDirContext( principal.getRealm() ), new ChangePassword( principal, newPassword ) );
+        }
+        catch ( NamingException ne )
+        {
+            String message = "Failed to get initial context " + principal.getRealm();
+            throw new ServiceConfigurationException( message, ne );
+        }
+    }
+
+
+    private Object execute( DirContext ctx, ContextOperation operation ) throws Exception
+    {
+        return operation.execute( ctx, null );
+    }
+
+    private DirContext getDirContext( String name ) throws NamingException
+    {
+        return directoryService.getJndiContext(catalog.getBaseDn( name ));
+    }
+
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/store/PrincipalStore.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/store/PrincipalStore.java
new file mode 100644
index 0000000..10d1056
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/store/PrincipalStore.java
@@ -0,0 +1,83 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.store;
+
+
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+
+/**
+ * The store interface used by Kerberos services.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev:330489 $, $Date$
+ */
+public interface PrincipalStore
+{
+    /**
+     * Add a principal.
+     *
+     * @param entry
+     * @return The name of the principal being added.
+     * @throws Exception
+     */
+    public String addPrincipal( PrincipalStoreEntry entry ) throws Exception;
+
+
+    /**
+     * Change a principal's password.
+     *
+     * @param principal
+     * @param newPassword
+     * @return The name of the principal whose password is being changed.
+     * @throws Exception
+     */
+    public String changePassword( KerberosPrincipal principal, String newPassword ) throws Exception;
+
+
+    /**
+     * Delete a principal.
+     *
+     * @param principal
+     * @return The name of the principal being deleted.
+     * @throws Exception
+     */
+    public String deletePrincipal( KerberosPrincipal principal ) throws Exception;
+
+
+    /**
+     * Get all principals for a given realm.
+     *
+     * @param realm
+     * @return An array of {@link PrincipalStoreEntry}'s.
+     * @throws Exception
+     */
+    public PrincipalStoreEntry[] getAllPrincipals( String realm ) throws Exception;
+
+
+    /**
+     * Get a {@link PrincipalStoreEntry} given a Kerberos principal.
+     *
+     * @param principal
+     * @return The {@link PrincipalStoreEntry} for the given Kerberos principal.
+     * @throws Exception
+     */
+    public PrincipalStoreEntry getPrincipal( KerberosPrincipal principal ) throws Exception;
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/store/PrincipalStoreEntry.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/store/PrincipalStoreEntry.java
new file mode 100644
index 0000000..33478f8
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/store/PrincipalStoreEntry.java
@@ -0,0 +1,275 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.store;
+
+
+import java.util.Map;
+
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
+import org.apache.directory.server.kerberos.shared.messages.value.SamType;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class PrincipalStoreEntry
+{
+    // principal
+    private String distinguishedName;
+    private String commonName;
+    private KerberosPrincipal principal;
+    private String realmName;
+
+    // uidObject
+    private String userId;
+
+    // KDCEntry
+    private KerberosTime validStart;
+    private KerberosTime validEnd;
+    private KerberosTime passwordEnd;
+    private int keyVersionNumber;
+    private int maxLife;
+    private int maxRenew;
+    private int kdcFlags;
+    private SamType samType;
+
+    private boolean disabled;
+    private boolean lockedOut;
+    private KerberosTime expiration;
+
+    private Map<EncryptionType, EncryptionKey> keyMap;
+
+
+    PrincipalStoreEntry( String distinguishedName, String commonName, String userId, KerberosPrincipal principal,
+        int keyVersionNumber, KerberosTime validStart, KerberosTime validEnd, KerberosTime passwordEnd, int maxLife,
+        int maxRenew, int kdcFlags, Map<EncryptionType, EncryptionKey> keyMap, String realmName, SamType samType,
+        boolean disabled, boolean lockedOut, KerberosTime expiration )
+    {
+        this.distinguishedName = distinguishedName;
+        this.commonName = commonName;
+        this.userId = userId;
+        this.principal = principal;
+        this.validStart = validStart;
+        this.validEnd = validEnd;
+        this.passwordEnd = passwordEnd;
+        this.keyVersionNumber = keyVersionNumber;
+        this.maxLife = maxLife;
+        this.maxRenew = maxRenew;
+        this.kdcFlags = kdcFlags;
+        this.realmName = realmName;
+        this.disabled = disabled;
+        this.lockedOut = lockedOut;
+        this.expiration = expiration;
+        this.samType = samType;
+        this.keyMap = keyMap;
+    }
+
+
+    /**
+     * Returns whether this account is disabled.
+     *
+     * @return Whether this account is disabled.
+     */
+    public boolean isDisabled()
+    {
+        return disabled;
+    }
+
+
+    /**
+     * Returns whether this account is locked-out.
+     *
+     * @return Whether this account is locked-out.
+     */
+    public boolean isLockedOut()
+    {
+        return lockedOut;
+    }
+
+
+    /**
+     * Returns the expiration time.
+     *
+     * @return The expiration time.
+     */
+    public KerberosTime getExpiration()
+    {
+        return expiration;
+    }
+
+
+    /**
+     * Returns the distinguished name.
+     *
+     * @return The distinguished name.
+     */
+    public String getDistinguishedName()
+    {
+        return distinguishedName;
+    }
+
+
+    /**
+     * Returns the common name.
+     *
+     * @return The common name.
+     */
+    public String getCommonName()
+    {
+        return commonName;
+    }
+
+
+    /**
+     * Returns the user ID.
+     *
+     * @return The user ID.
+     */
+    public String getUserId()
+    {
+        return userId;
+    }
+
+
+    /**
+     * Returns the key map.
+     *
+     * @return The key map.
+     */
+    public Map<EncryptionType, EncryptionKey> getKeyMap()
+    {
+        return keyMap;
+    }
+
+
+    /**
+     * Returns the KDC flags.
+     *
+     * @return The KDC flags.
+     */
+    public int getKDCFlags()
+    {
+        return kdcFlags;
+    }
+
+
+    /**
+     * Returns the key version number (kvno).
+     *
+     * @return The key version number (kvno).
+     */
+    public int getKeyVersionNumber()
+    {
+        return keyVersionNumber;
+    }
+
+
+    /**
+     * Returns the max life.
+     *
+     * @return The max life.
+     */
+    public int getMaxLife()
+    {
+        return maxLife;
+    }
+
+
+    /**
+     * Returns the maximum renew time.
+     *
+     * @return The maximum renew time.
+     */
+    public int getMaxRenew()
+    {
+        return maxRenew;
+    }
+
+
+    /**
+     * Returns the expiration time for the password.
+     *
+     * @return The expiration time for the password.
+     */
+    public KerberosTime getPasswordEnd()
+    {
+        return passwordEnd;
+    }
+
+
+    /**
+     * Returns the principal.
+     *
+     * @return The principal.
+     */
+    public KerberosPrincipal getPrincipal()
+    {
+        return principal;
+    }
+
+
+    /**
+     * Returns the realm name.
+     *
+     * @return The realm name.
+     */
+    public String getRealmName()
+    {
+        return realmName;
+    }
+
+
+    /**
+     * Returns the end of validity.
+     *
+     * @return The end of validity.
+     */
+    public KerberosTime getValidEnd()
+    {
+        return validEnd;
+    }
+
+
+    /**
+     * Returns the start of validity.
+     *
+     * @return The start of validity.
+     */
+    public KerberosTime getValidStart()
+    {
+        return validStart;
+    }
+
+
+    /**
+     * Returns the single-use authentication (SAM) type.
+     *
+     * @return The single-use authentication (SAM) type.
+     */
+    public SamType getSamType()
+    {
+        return samType;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/store/PrincipalStoreEntryModifier.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/store/PrincipalStoreEntryModifier.java
new file mode 100644
index 0000000..8b4dbe7
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/store/PrincipalStoreEntryModifier.java
@@ -0,0 +1,302 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.store;
+
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
+import org.apache.directory.server.kerberos.shared.io.decoder.EncryptionKeyDecoder;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
+import org.apache.directory.server.kerberos.shared.messages.value.SamType;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class PrincipalStoreEntryModifier
+{
+    // principal
+    private String distinguishedName;
+    private String commonName;
+    private KerberosPrincipal principal;
+    private String realmName;
+
+    // uidObject
+    private String userId;
+
+    // KDCEntry
+    // must
+    private int keyVersionNumber;
+    // may
+    private KerberosTime validStart;
+    private KerberosTime validEnd;
+    private KerberosTime passwordEnd;
+    private int maxLife;
+    private int maxRenew;
+    private int kdcFlags;
+    private SamType samType;
+
+    private boolean disabled = false;
+    private boolean lockedOut = false;
+    private KerberosTime expiration = KerberosTime.INFINITY;
+
+    private Map<EncryptionType, EncryptionKey> keyMap;
+
+
+    /**
+     * Returns the {@link PrincipalStoreEntry}.
+     *
+     * @return The {@link PrincipalStoreEntry}.
+     */
+    public PrincipalStoreEntry getEntry()
+    {
+        return new PrincipalStoreEntry( distinguishedName, commonName, userId, principal, keyVersionNumber, validStart,
+            validEnd, passwordEnd, maxLife, maxRenew, kdcFlags, keyMap, realmName, samType, disabled, lockedOut,
+            expiration );
+    }
+
+
+    /**
+     * Sets whether the account is disabled.
+     *
+     * @param disabled
+     */
+    public void setDisabled( boolean disabled )
+    {
+        this.disabled = disabled;
+    }
+
+
+    /**
+     * Sets whether the account is locked-out.
+     *
+     * @param lockedOut
+     */
+    public void setLockedOut( boolean lockedOut )
+    {
+        this.lockedOut = lockedOut;
+    }
+
+
+    /**
+     * Sets the expiration time.
+     *
+     * @param expiration
+     */
+    public void setExpiration( KerberosTime expiration )
+    {
+        this.expiration = expiration;
+    }
+
+
+    /**
+     * Sets the distinguished name (DN).
+     *
+     * @param distinguishedName
+     */
+    public void setDistinguishedName( String distinguishedName )
+    {
+        this.distinguishedName = distinguishedName;
+    }
+
+
+    /**
+     * Sets the common name (cn).
+     *
+     * @param commonName
+     */
+    public void setCommonName( String commonName )
+    {
+        this.commonName = commonName;
+    }
+
+
+    /**
+     * Sets the user ID.
+     *
+     * @param userId
+     */
+    public void setUserId( String userId )
+    {
+        this.userId = userId;
+    }
+
+
+    /**
+     * Sets the KDC flags.
+     *
+     * @param kdcFlags
+     */
+    public void setKDCFlags( int kdcFlags )
+    {
+        this.kdcFlags = kdcFlags;
+    }
+
+
+    /**
+     * Sets the key map.
+     *
+     * @param keyMap
+     */
+    public void setKeyMap( Map<EncryptionType, EncryptionKey> keyMap )
+    {
+        this.keyMap = keyMap;
+    }
+
+
+    /**
+     * Sets the key version number.
+     *
+     * @param keyVersionNumber
+     */
+    public void setKeyVersionNumber( int keyVersionNumber )
+    {
+        this.keyVersionNumber = keyVersionNumber;
+    }
+
+
+    /**
+     * Sets the ticket maximum life time.
+     *
+     * @param maxLife
+     */
+    public void setMaxLife( int maxLife )
+    {
+        this.maxLife = maxLife;
+    }
+
+
+    /**
+     * Sets the ticket maximum renew time.
+     *
+     * @param maxRenew
+     */
+    public void setMaxRenew( int maxRenew )
+    {
+        this.maxRenew = maxRenew;
+    }
+
+
+    /**
+     * Sets the end-of-life for the password.
+     *
+     * @param passwordEnd
+     */
+    public void setPasswordEnd( KerberosTime passwordEnd )
+    {
+        this.passwordEnd = passwordEnd;
+    }
+
+
+    /**
+     * Sets the principal.
+     *
+     * @param principal
+     */
+    public void setPrincipal( KerberosPrincipal principal )
+    {
+        this.principal = principal;
+    }
+
+
+    /**
+     * Sets the realm.
+     *
+     * @param realmName
+     */
+    public void setRealmName( String realmName )
+    {
+        this.realmName = realmName;
+    }
+
+
+    /**
+     * Sets the end of validity.
+     *
+     * @param validEnd
+     */
+    public void setValidEnd( KerberosTime validEnd )
+    {
+        this.validEnd = validEnd;
+    }
+
+
+    /**
+     * Sets the start of validity.
+     *
+     * @param validStart
+     */
+    public void setValidStart( KerberosTime validStart )
+    {
+        this.validStart = validStart;
+    }
+
+
+    /**
+     * Sets the single-use authentication (SAM) type.
+     *
+     * @param samType
+     */
+    public void setSamType( SamType samType )
+    {
+        this.samType = samType;
+    }
+
+
+    /**
+     * Converts the ASN.1 encoded key set to a map of encryption types to encryption keys.
+     *
+     * @param krb5key
+     * @return The map of encryption types to encryption keys.
+     * @throws NamingException
+     * @throws IOException
+     */
+    public Map<EncryptionType, EncryptionKey> reconstituteKeyMap( Attribute krb5key ) throws NamingException,
+        IOException
+    {
+        Map<EncryptionType, EncryptionKey> map = new HashMap<EncryptionType, EncryptionKey>();
+
+        for ( int ii = 0; ii < krb5key.size(); ii++ )
+        {
+            Object key = krb5key.get( ii );
+
+            if ( key instanceof String )
+            {
+                throw new NamingException(
+                    "JNDI should not return a string for the Kerberos key: JNDI property java.naming.ldap.attributes.binary must include the krb5key attribute." );
+            }
+
+            byte[] encryptionKeyBytes = ( byte[] ) key;
+            EncryptionKey encryptionKey = EncryptionKeyDecoder.decode( encryptionKeyBytes );
+            map.put( encryptionKey.getKeyType(), encryptionKey );
+        }
+
+        return map;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/store/SingleBaseSearch.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/store/SingleBaseSearch.java
new file mode 100644
index 0000000..a11937d
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/store/SingleBaseSearch.java
@@ -0,0 +1,92 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.kerberos.shared.store;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.kerberos.shared.store.operations.AddPrincipal;
+import org.apache.directory.server.kerberos.shared.store.operations.ChangePassword;
+import org.apache.directory.server.kerberos.shared.store.operations.DeletePrincipal;
+import org.apache.directory.server.kerberos.shared.store.operations.GetAllPrincipals;
+import org.apache.directory.server.kerberos.shared.store.operations.GetPrincipal;
+import org.apache.directory.server.protocol.shared.ServiceConfigurationException;
+
+import javax.naming.directory.DirContext;
+import javax.naming.NamingException;
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+
+/**
+ * A JNDI-backed search strategy implementation.  This search strategy searches a
+ * single base DN for Kerberos principals.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+class SingleBaseSearch implements PrincipalStore
+{
+    private final DirContext ctx;
+
+
+    SingleBaseSearch( String searchBaseDn, DirectoryService directoryService )
+    {
+        try
+        {
+            ctx = directoryService.getJndiContext(searchBaseDn);
+        } catch ( NamingException e )
+        {
+            throw new ServiceConfigurationException("Can't get context at" + searchBaseDn, e);
+        }
+
+    }
+
+
+    public String addPrincipal( PrincipalStoreEntry entry ) throws Exception
+    {
+        return ( String ) new AddPrincipal( entry ).execute( ctx, null );
+    }
+
+
+    public String deletePrincipal( KerberosPrincipal principal ) throws Exception
+    {
+        return ( String ) new DeletePrincipal( principal ).execute( ctx, null );
+    }
+
+
+    public PrincipalStoreEntry[] getAllPrincipals( String realm ) throws Exception
+    {
+        return ( PrincipalStoreEntry[] ) new GetAllPrincipals().execute( ctx, null );
+    }
+
+
+    public PrincipalStoreEntry getPrincipal( KerberosPrincipal principal ) throws Exception
+    {
+        return ( PrincipalStoreEntry ) new GetPrincipal( principal ).execute( ctx, null );
+    }
+
+
+    public String changePassword( KerberosPrincipal principal, String newPassword ) throws Exception
+    {
+        return ( String ) new ChangePassword( principal, newPassword ).execute( ctx, null );
+    }
+
+
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/store/TicketFactory.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/store/TicketFactory.java
new file mode 100644
index 0000000..47d2d76
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/store/TicketFactory.java
@@ -0,0 +1,173 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.store;
+
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.util.Date;
+
+import javax.security.auth.kerberos.KerberosKey;
+import javax.security.auth.kerberos.KerberosPrincipal;
+import javax.security.auth.kerberos.KerberosTicket;
+
+import org.apache.directory.server.kerberos.shared.KerberosConstants;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.CipherTextHandler;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.KeyUsage;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.RandomKeyFactory;
+import org.apache.directory.server.kerberos.shared.exceptions.KerberosException;
+import org.apache.directory.server.kerberos.shared.io.encoder.TicketEncoder;
+import org.apache.directory.server.kerberos.shared.messages.components.EncTicketPart;
+import org.apache.directory.server.kerberos.shared.messages.components.EncTicketPartModifier;
+import org.apache.directory.server.kerberos.shared.messages.components.Ticket;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptedData;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
+import org.apache.directory.server.kerberos.shared.messages.value.TransitedEncoding;
+import org.apache.directory.server.kerberos.shared.messages.value.flags.TicketFlag;
+import org.apache.directory.server.kerberos.shared.messages.value.flags.TicketFlags;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class TicketFactory
+{
+    /** One day in milliseconds, used for default end time. */
+    private static final int ONE_DAY = 86400000;
+
+    /** One week in milliseconds, used for default renewal period. */
+    private static final int ONE_WEEK = 86400000 * 7;
+
+    private CipherTextHandler cipherTextHandler = new CipherTextHandler();
+
+
+    /**
+     * Returns a server key derived from a server principal and server password.
+     *
+     * @param serverPrincipal
+     * @param serverPassword
+     * @return The server's {@link EncryptionKey}.
+     */
+    public EncryptionKey getServerKey( KerberosPrincipal serverPrincipal, String serverPassword )
+    {
+        KerberosKey serverKerberosKey = new KerberosKey( serverPrincipal, serverPassword.toCharArray(), "DES" );
+        byte[] serverKeyBytes = serverKerberosKey.getEncoded();
+        EncryptionKey serverKey = new EncryptionKey( EncryptionType.DES_CBC_MD5, serverKeyBytes );
+
+        return serverKey;
+    }
+
+
+    /**
+     * Build the service ticket.  The service ticket contains the session key generated
+     * by the KDC for the client and service to use.  The service will unlock the
+     * authenticator with the session key from the ticket.  The principal in the ticket
+     * must equal the authenticator client principal.
+     * 
+     * If set in the AP Options, the Ticket can also be sealed with the session key.
+     * 
+     * @param clientPrincipal
+     * @param serverPrincipal
+     * @param serverKey 
+     * @return The {@link Ticket}.
+     * @throws KerberosException
+     */
+    public Ticket getTicket( KerberosPrincipal clientPrincipal, KerberosPrincipal serverPrincipal,
+        EncryptionKey serverKey ) throws KerberosException
+    {
+        EncTicketPartModifier encTicketModifier = new EncTicketPartModifier();
+
+        TicketFlags ticketFlags = new TicketFlags();
+        ticketFlags.setFlag( TicketFlag.RENEWABLE );
+        encTicketModifier.setFlags( ticketFlags );
+
+        EncryptionKey sessionKey = RandomKeyFactory.getRandomKey( EncryptionType.DES_CBC_MD5 );
+
+        encTicketModifier.setSessionKey( sessionKey );
+        encTicketModifier.setClientPrincipal( clientPrincipal );
+        encTicketModifier.setTransitedEncoding( new TransitedEncoding() );
+        encTicketModifier.setAuthTime( new KerberosTime() );
+
+        long now = System.currentTimeMillis();
+        KerberosTime endTime = new KerberosTime( now + ONE_DAY );
+        encTicketModifier.setEndTime( endTime );
+
+        KerberosTime renewTill = new KerberosTime( now + ONE_WEEK );
+        encTicketModifier.setRenewTill( renewTill );
+
+        EncTicketPart encTicketPart = encTicketModifier.getEncTicketPart();
+
+        EncryptedData encryptedTicketPart = cipherTextHandler.seal( serverKey, encTicketPart, KeyUsage.NUMBER2 );
+
+        Ticket ticket = new Ticket();
+        ticket.setTktVno( KerberosConstants.KERBEROS_V5 );
+        ticket.setServerPrincipal( serverPrincipal );
+        ticket.setEncPart( encryptedTicketPart );
+
+        ticket.setEncTicketPart( encTicketPart );
+
+        return ticket;
+    }
+
+
+    /**
+     * Convert an Apache Directory Kerberos {@link Ticket} into a {@link KerberosTicket}.
+     *
+     * @param ticket
+     * @return The {@link KerberosTicket}.
+     * @throws IOException 
+     */
+    public KerberosTicket getKerberosTicket( Ticket ticket ) throws IOException
+    {
+        byte[] asn1Encoding = TicketEncoder.encodeTicket( ticket );
+
+        KerberosPrincipal client = ticket.getEncTicketPart().getClientPrincipal();
+        KerberosPrincipal server = ticket.getServerPrincipal();
+        byte[] sessionKey = ticket.getEncTicketPart().getSessionKey().getKeyValue();
+        int keyType = ticket.getEncTicketPart().getSessionKey().getKeyType().getOrdinal();
+
+        boolean[] flags = new boolean[32];
+
+        for ( int ii = 0; ii < flags.length; ii++ )
+        {
+            flags[ii] = ticket.getEncTicketPart().getFlags().isFlagSet( ii );
+        }
+
+        Date authTime = ticket.getEncTicketPart().getAuthTime().toDate();
+        Date endTime = ticket.getEncTicketPart().getEndTime().toDate();
+
+        Date startTime = ( ticket.getEncTicketPart().getStartTime() != null ? ticket.getEncTicketPart().getStartTime().toDate() : null );
+
+        Date renewTill = null;
+
+        if ( ticket.getEncTicketPart().getFlags().isRenewable() )
+        {
+            renewTill = ( ticket.getEncTicketPart().getRenewTill() != null ? ticket.getEncTicketPart().getRenewTill().toDate() : null );
+        }
+
+        InetAddress[] clientAddresses = new InetAddress[0];
+
+        return new KerberosTicket( asn1Encoding, client, server, sessionKey, keyType, flags, authTime, startTime,
+            endTime, renewTill, clientAddresses );
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/store/operations/AddPrincipal.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/store/operations/AddPrincipal.java
new file mode 100644
index 0000000..d010ec2
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/store/operations/AddPrincipal.java
@@ -0,0 +1,84 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.kerberos.shared.store.operations;
+
+
+import javax.naming.Name;
+import javax.naming.NamingException;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.spi.DirStateFactory;
+import javax.naming.spi.DirStateFactory.Result;
+
+import org.apache.directory.server.kerberos.shared.store.PrincipalStoreEntry;
+import org.apache.directory.server.protocol.shared.store.ContextOperation;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+
+/**
+ * Command for adding a principal to a JNDI context.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class AddPrincipal implements ContextOperation
+{
+    private static final long serialVersionUID = -1032737167622217786L;
+
+    /** The Kerberos principal who is to be added. */
+    protected PrincipalStoreEntry entry;
+
+
+    /**
+     * Creates the action to be used against the embedded ApacheDS DIT.
+     * 
+     * @param entry The {@link PrincipalStoreEntry} to add.
+     */
+    public AddPrincipal( PrincipalStoreEntry entry )
+    {
+        this.entry = entry;
+    }
+
+
+    public Object execute( DirContext ctx, Name searchBaseDn )
+    {
+        if ( entry == null )
+        {
+            return null;
+        }
+
+        try
+        {
+            DirStateFactory factory = new PrincipalStateFactory();
+            Result result = factory.getStateToBind( entry, null, null, null, null );
+            Attributes attrs = result.getAttributes();
+            LdapDN name = new LdapDN( "uid=" + entry.getUserId() + ",ou=Users" );
+            ctx.rebind( name, null, attrs );
+            return name.toString();
+        }
+        catch ( NamingException ne )
+        {
+            ne.printStackTrace();
+        }
+
+        return null;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/store/operations/ChangePassword.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/store/operations/ChangePassword.java
new file mode 100644
index 0000000..59fd478
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/store/operations/ChangePassword.java
@@ -0,0 +1,143 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.store.operations;
+
+
+import java.util.Properties;
+
+import javax.naming.CompoundName;
+import javax.naming.Name;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.SearchResult;
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.apache.directory.server.kerberos.shared.store.KerberosAttribute;
+import org.apache.directory.server.protocol.shared.store.ContextOperation;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.message.ModificationItemImpl;
+
+
+/**
+ * Command for changing a principal's password in a JNDI context.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ChangePassword implements ContextOperation
+{
+    private static final long serialVersionUID = -7147685183641418353L;
+
+    /** The Kerberos principal who's password is to be changed. */
+    protected KerberosPrincipal principal;
+    /** The new password for the update. */
+    protected String newPassword;
+
+
+    /**
+     * Creates the action to be used against the embedded ApacheDS DIT.
+     * 
+     * @param principal The principal to change the password for.
+     * @param newPassword The password to change.
+     */
+    public ChangePassword( KerberosPrincipal principal, String newPassword )
+    {
+        this.principal = principal;
+        this.newPassword = newPassword;
+    }
+
+
+    public Object execute( DirContext ctx, Name searchBaseDn ) throws NamingException
+    {
+        if ( principal == null )
+        {
+            return null;
+        }
+
+        ModificationItemImpl[] mods = new ModificationItemImpl[2];
+        Attribute newPasswordAttribute = new AttributeImpl( SchemaConstants.USER_PASSWORD_AT, newPassword );
+        mods[0] = new ModificationItemImpl( DirContext.REPLACE_ATTRIBUTE, newPasswordAttribute );
+        Attribute principalAttribute = new AttributeImpl( "krb5PrincipalName", principal.getName() );
+        mods[1] = new ModificationItemImpl( DirContext.REPLACE_ATTRIBUTE, principalAttribute );
+
+        String dn = null;
+
+        dn = search( ctx, principal.getName() );
+        Name rdn = getRelativeName( ctx.getNameInNamespace(), dn );
+        ctx.modifyAttributes( rdn, mods );
+
+        return dn;
+    }
+
+
+    private String search( DirContext ctx, String principal ) throws NamingException
+    {
+        String[] attrIDs =
+            { KerberosAttribute.KRB5_PRINCIPAL_NAME_AT, KerberosAttribute.KRB5_KEY_VERSION_NUMBER_AT, KerberosAttribute.KRB5_KEY_AT };
+
+        Attributes matchAttrs = new AttributesImpl( true );
+        matchAttrs.put( new AttributeImpl( KerberosAttribute.KRB5_PRINCIPAL_NAME_AT, principal ) );
+
+        NamingEnumeration<SearchResult> answer = ctx.search( "", matchAttrs, attrIDs );
+
+        if ( answer.hasMore() )
+        {
+            SearchResult sr = answer.next();
+            if ( sr != null )
+            {
+                return sr.getName();
+            }
+        }
+
+        return null;
+    }
+
+
+    private Name getRelativeName( String nameInNamespace, String baseDn ) throws NamingException
+    {
+        Properties props = new Properties();
+        props.setProperty( "jndi.syntax.direction", "right_to_left" );
+        props.setProperty( "jndi.syntax.separator", "," );
+        props.setProperty( "jndi.syntax.ignorecase", "true" );
+        props.setProperty( "jndi.syntax.trimblanks", "true" );
+
+        Name searchBaseDn = null;
+
+        Name ctxRoot = new CompoundName( nameInNamespace, props );
+        searchBaseDn = new CompoundName( baseDn, props );
+
+        if ( !searchBaseDn.startsWith( ctxRoot ) )
+        {
+            throw new NamingException( "Invalid search base " + baseDn );
+        }
+
+        for ( int ii = 0; ii < ctxRoot.size(); ii++ )
+        {
+            searchBaseDn.remove( 0 );
+        }
+
+        return searchBaseDn;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/store/operations/DeletePrincipal.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/store/operations/DeletePrincipal.java
new file mode 100644
index 0000000..9bc6343
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/store/operations/DeletePrincipal.java
@@ -0,0 +1,147 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.kerberos.shared.store.operations;
+
+
+import java.util.Properties;
+
+import javax.naming.CompoundName;
+import javax.naming.Name;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.SearchResult;
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.apache.directory.server.kerberos.shared.store.KerberosAttribute;
+import org.apache.directory.server.protocol.shared.store.ContextOperation;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+
+
+/**
+ * Command for deleting a principal from a JNDI context.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class DeletePrincipal implements ContextOperation
+{
+    private static final long serialVersionUID = -6970986279811261983L;
+
+    /** The Kerberos principal who is to be deleted. */
+    protected KerberosPrincipal principal;
+
+
+    /**
+     * Creates the action to be used against the embedded ApacheDS DIT.
+     * 
+     * @param principal The principal to delete.
+     */
+    public DeletePrincipal( KerberosPrincipal principal )
+    {
+        this.principal = principal;
+    }
+
+
+    public Object execute( DirContext ctx, Name searchBaseDn )
+    {
+        if ( principal == null )
+        {
+            return null;
+        }
+
+        String dn = null;
+
+        try
+        {
+            dn = search( ctx, searchBaseDn, principal.getName() );
+            Name rdn = getRelativeName( ctx, dn );
+            ctx.destroySubcontext( rdn );
+        }
+        catch ( NamingException e )
+        {
+            e.printStackTrace();
+            return null;
+        }
+
+        return dn;
+    }
+
+
+    private String search( DirContext ctx, Name searchBaseDn, String principal ) throws NamingException
+    {
+        String[] attrIDs =
+            { KerberosAttribute.KRB5_PRINCIPAL_NAME_AT, KerberosAttribute.KRB5_KEY_VERSION_NUMBER_AT, KerberosAttribute.KRB5_KEY_AT };
+
+        Attributes matchAttrs = new AttributesImpl( true );
+        matchAttrs.put( new AttributeImpl( KerberosAttribute.KRB5_PRINCIPAL_NAME_AT, principal ) );
+
+        // Search for objects that have those matching attributes
+        NamingEnumeration<SearchResult> answer = ctx.search( searchBaseDn, matchAttrs, attrIDs );
+
+        if ( answer.hasMore() )
+        {
+            SearchResult sr = answer.next();
+            if ( sr != null )
+            {
+                return sr.getName();
+            }
+        }
+
+        return null;
+    }
+
+
+    private Name getRelativeName( DirContext ctx, String baseDn ) throws NamingException
+    {
+        Properties props = new Properties();
+        props.setProperty( "jndi.syntax.direction", "right_to_left" );
+        props.setProperty( "jndi.syntax.separator", "," );
+        props.setProperty( "jndi.syntax.ignorecase", "true" );
+        props.setProperty( "jndi.syntax.trimblanks", "true" );
+
+        Name searchBaseDn;
+
+        try
+        {
+            Name ctxRoot = new CompoundName( ctx.getNameInNamespace(), props );
+            searchBaseDn = new CompoundName( baseDn, props );
+
+            if ( !searchBaseDn.startsWith( ctxRoot ) )
+            {
+                throw new NamingException( "Invalid search base " + baseDn );
+            }
+
+            for ( int ii = 0; ii < ctxRoot.size(); ii++ )
+            {
+                searchBaseDn.remove( 0 );
+            }
+        }
+        catch ( NamingException e )
+        {
+            throw new NamingException( "Failed to initialize search base " + baseDn );
+        }
+
+        return searchBaseDn;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/store/operations/GetAllPrincipals.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/store/operations/GetAllPrincipals.java
new file mode 100644
index 0000000..f292b8e
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/store/operations/GetAllPrincipals.java
@@ -0,0 +1,142 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.kerberos.shared.store.operations;
+
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import javax.naming.Name;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.InvalidAttributeValueException;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+import org.apache.directory.server.kerberos.shared.messages.value.SamType;
+import org.apache.directory.server.kerberos.shared.store.KerberosAttribute;
+import org.apache.directory.server.kerberos.shared.store.PrincipalStoreEntry;
+import org.apache.directory.server.kerberos.shared.store.PrincipalStoreEntryModifier;
+import org.apache.directory.server.protocol.shared.store.ContextOperation;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+
+
+/**
+ * Command for getting all principals in a JNDI context.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class GetAllPrincipals implements ContextOperation
+{
+    private static final long serialVersionUID = -1214321426487445132L;
+
+    private static final String filter = "(objectClass=krb5Principal)";
+
+
+    public Object execute( DirContext ctx, Name searchBaseDn )
+    {
+        SearchControls controls = new SearchControls();
+
+        List<PrincipalStoreEntry> answers = new ArrayList<PrincipalStoreEntry>();
+
+        try
+        {
+            Attributes attrs = null;
+
+            NamingEnumeration<SearchResult> answer = ctx.search( searchBaseDn, filter, controls );
+
+            while ( answer.hasMore() )
+            {
+                SearchResult result = answer.next();
+                attrs = result.getAttributes();
+                PrincipalStoreEntry entry = getEntry( attrs );
+                answers.add( entry );
+            }
+
+            answer.close();
+
+            PrincipalStoreEntry[] entries = new PrincipalStoreEntry[answers.size()];
+
+            return answers.toArray( entries );
+        }
+        catch ( NamingException e )
+        {
+            e.printStackTrace();
+
+            return null;
+        }
+    }
+
+
+    /**
+     * Marshals an a PrincipalStoreEntry from an Attributes object.
+     *
+     * @param attrs the attributes of the Kerberos principal
+     * @return the entry for the principal
+     * @throws NamingException if there are any access problems
+     */
+    private PrincipalStoreEntry getEntry( Attributes attrs ) throws NamingException
+    {
+        PrincipalStoreEntryModifier modifier = new PrincipalStoreEntryModifier();
+
+        String principal = ( String ) attrs.get( KerberosAttribute.KRB5_PRINCIPAL_NAME_AT ).get();
+        String keyVersionNumber = ( String ) attrs.get( KerberosAttribute.KRB5_KEY_VERSION_NUMBER_AT ).get();
+
+        String commonName = ( String ) attrs.get( SchemaConstants.CN_AT ).get();
+
+        if ( attrs.get( KerberosAttribute.APACHE_SAM_TYPE_AT ) != null )
+        {
+            String samType = ( String ) attrs.get( KerberosAttribute.APACHE_SAM_TYPE_AT ).get();
+
+            modifier.setSamType( SamType.getTypeByOrdinal( Integer.parseInt( samType ) ) );
+        }
+
+        if ( attrs.get( KerberosAttribute.KRB5_KEY_AT ) != null )
+        {
+            Attribute krb5key = attrs.get( KerberosAttribute.KRB5_KEY_AT );
+            try
+            {
+                Map<EncryptionType, EncryptionKey> keyMap = modifier.reconstituteKeyMap( krb5key );
+                modifier.setKeyMap( keyMap );
+            }
+            catch ( IOException ioe )
+            {
+                throw new InvalidAttributeValueException( "Account Kerberos key attribute '" + KerberosAttribute.KRB5_KEY_AT
+                    + "' contained an invalid value for krb5key." );
+            }
+        }
+
+        modifier.setCommonName( commonName );
+        modifier.setPrincipal( new KerberosPrincipal( principal ) );
+        modifier.setKeyVersionNumber( Integer.parseInt( keyVersionNumber ) );
+
+        return modifier.getEntry();
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/store/operations/GetPrincipal.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/store/operations/GetPrincipal.java
new file mode 100644
index 0000000..5094fcc
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/store/operations/GetPrincipal.java
@@ -0,0 +1,197 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.store.operations;
+
+
+import java.io.IOException;
+import java.text.ParseException;
+import java.util.Map;
+
+import javax.naming.Name;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.InvalidAttributeValueException;
+import javax.naming.directory.SearchResult;
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
+import org.apache.directory.server.kerberos.shared.messages.value.SamType;
+import org.apache.directory.server.kerberos.shared.store.KerberosAttribute;
+import org.apache.directory.server.kerberos.shared.store.PrincipalStoreEntry;
+import org.apache.directory.server.kerberos.shared.store.PrincipalStoreEntryModifier;
+import org.apache.directory.server.protocol.shared.store.ContextOperation;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+
+
+/**
+ * Encapsulates the action of looking up a principal in an embedded ApacheDS DIT.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class GetPrincipal implements ContextOperation
+{
+    private static final long serialVersionUID = 4598007518413451945L;
+
+    /** The name of the principal to get. */
+    private final KerberosPrincipal principal;
+
+
+    /**
+     * Creates the action to be used against the embedded ApacheDS DIT.
+     * 
+     * @param principal The principal to search for in the directory.
+     */
+    public GetPrincipal( KerberosPrincipal principal )
+    {
+        this.principal = principal;
+    }
+
+
+    /**
+     * Note that the base is a relative path from the existing context.
+     * It is not a DN.
+     */
+    public Object execute( DirContext ctx, Name base )
+    {
+        if ( principal == null )
+        {
+            return null;
+        }
+
+        String[] attrIDs =
+            {   KerberosAttribute.KRB5_PRINCIPAL_NAME_AT, 
+                KerberosAttribute.KRB5_KEY_VERSION_NUMBER_AT, 
+                KerberosAttribute.KRB5_KEY_AT,
+                KerberosAttribute.APACHE_SAM_TYPE_AT, 
+                KerberosAttribute.KRB5_ACCOUNT_DISABLED_AT,
+                KerberosAttribute.KRB5_ACCOUNT_EXPIRATION_TIME_AT, 
+                KerberosAttribute.KRB5_ACCOUNT_LOCKEDOUT_AT };
+
+        Attributes matchAttrs = new AttributesImpl( true );
+        matchAttrs.put( new AttributeImpl( KerberosAttribute.KRB5_PRINCIPAL_NAME_AT, principal.getName() ) );
+
+        PrincipalStoreEntry entry = null;
+
+        try
+        {
+            NamingEnumeration<SearchResult> answer = ctx.search( "", matchAttrs, attrIDs );
+
+            if ( answer.hasMore() )
+            {
+                SearchResult result = answer.next();
+
+                Attributes attrs = result.getAttributes();
+
+                if ( attrs == null )
+                {
+                    return null;
+                }
+
+                String distinguishedName = result.getName();
+                entry = getEntry( distinguishedName, attrs );
+            }
+        }
+        catch ( NamingException e )
+        {
+            return null;
+        }
+
+        return entry;
+    }
+
+
+    /**
+     * Marshals an a PrincipalStoreEntry from an Attributes object.
+     *
+     * @param dn the distinguished name of the Kerberos principal
+     * @param attrs the attributes of the Kerberos principal
+     * @return the entry for the principal
+     * @throws NamingException if there are any access problems
+     */
+    private PrincipalStoreEntry getEntry( String distinguishedName, Attributes attrs ) throws NamingException
+    {
+        PrincipalStoreEntryModifier modifier = new PrincipalStoreEntryModifier();
+
+        modifier.setDistinguishedName( distinguishedName );
+
+        String principal = ( String ) attrs.get( KerberosAttribute.KRB5_PRINCIPAL_NAME_AT ).get();
+        modifier.setPrincipal( new KerberosPrincipal( principal ) );
+
+        String keyVersionNumber = ( String ) attrs.get( KerberosAttribute.KRB5_KEY_VERSION_NUMBER_AT ).get();
+        modifier.setKeyVersionNumber( Integer.parseInt( keyVersionNumber ) );
+
+        if ( attrs.get( KerberosAttribute.KRB5_ACCOUNT_DISABLED_AT ) != null )
+        {
+            String val = ( String ) attrs.get( KerberosAttribute.KRB5_ACCOUNT_DISABLED_AT ).get();
+            modifier.setDisabled( "true".equalsIgnoreCase( val ) );
+        }
+
+        if ( attrs.get( KerberosAttribute.KRB5_ACCOUNT_LOCKEDOUT_AT ) != null )
+        {
+            String val = ( String ) attrs.get( KerberosAttribute.KRB5_ACCOUNT_LOCKEDOUT_AT ).get();
+            modifier.setLockedOut( "true".equalsIgnoreCase( val ) );
+        }
+
+        if ( attrs.get( KerberosAttribute.KRB5_ACCOUNT_EXPIRATION_TIME_AT ) != null )
+        {
+            String val = ( String ) attrs.get( KerberosAttribute.KRB5_ACCOUNT_EXPIRATION_TIME_AT ).get();
+            try
+            {
+                modifier.setExpiration( KerberosTime.getTime( val ) );
+            }
+            catch ( ParseException e )
+            {
+                throw new InvalidAttributeValueException( "Account expiration attribute "
+                    + KerberosAttribute.KRB5_ACCOUNT_EXPIRATION_TIME_AT + " contained an invalid value for generalizedTime: "
+                    + val );
+            }
+        }
+
+        if ( attrs.get( KerberosAttribute.APACHE_SAM_TYPE_AT ) != null )
+        {
+            String samType = ( String ) attrs.get( KerberosAttribute.APACHE_SAM_TYPE_AT ).get();
+            modifier.setSamType( SamType.getTypeByOrdinal( Integer.parseInt( samType ) ) );
+        }
+
+        if ( attrs.get( KerberosAttribute.KRB5_KEY_AT ) != null )
+        {
+            Attribute krb5key = attrs.get( KerberosAttribute.KRB5_KEY_AT );
+            try
+            {
+                Map<EncryptionType, EncryptionKey> keyMap = modifier.reconstituteKeyMap( krb5key );
+                modifier.setKeyMap( keyMap );
+            }
+            catch ( IOException ioe )
+            {
+                throw new InvalidAttributeValueException( "Account Kerberos key attribute '" + KerberosAttribute.KRB5_KEY_AT
+                    + "' contained an invalid value for krb5key." );
+            }
+        }
+
+        return modifier.getEntry();
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/store/operations/PrincipalObjectFactory.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/store/operations/PrincipalObjectFactory.java
new file mode 100644
index 0000000..d53ccaa
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/store/operations/PrincipalObjectFactory.java
@@ -0,0 +1,93 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.kerberos.shared.store.operations;
+
+
+import java.io.IOException;
+import java.util.Hashtable;
+import java.util.Map;
+
+import javax.naming.Context;
+import javax.naming.Name;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.InvalidAttributeValueException;
+import javax.naming.spi.DirObjectFactory;
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+import org.apache.directory.server.kerberos.shared.store.KerberosAttribute;
+import org.apache.directory.server.kerberos.shared.store.PrincipalStoreEntryModifier;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+
+
+/**
+ * An ObjectFactory that resusitates objects from directory attributes.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class PrincipalObjectFactory implements DirObjectFactory
+{
+    public Object getObjectInstance( Object obj, Name name, Context nameCtx, Hashtable environment, Attributes attrs )
+        throws Exception
+    {
+        if ( attrs == null || attrs.get( SchemaConstants.OBJECT_CLASS_AT ) == null
+            || !attrs.get( SchemaConstants.OBJECT_CLASS_AT ).contains( "krb5KDCEntry" ) )
+        {
+            return null;
+        }
+
+        PrincipalStoreEntryModifier modifier = new PrincipalStoreEntryModifier();
+
+        modifier.setUserId( ( String ) attrs.get( SchemaConstants.UID_AT ).get() );
+        modifier.setCommonName( ( String ) attrs.get( SchemaConstants.CN_AT ).get() );
+
+        KerberosPrincipal principal = new KerberosPrincipal( ( String ) attrs.get( KerberosAttribute.KRB5_PRINCIPAL_NAME_AT ).get() );
+        modifier.setPrincipal( principal );
+
+        if ( attrs.get( KerberosAttribute.KRB5_KEY_AT ) != null )
+        {
+            Attribute krb5key = attrs.get( KerberosAttribute.KRB5_KEY_AT );
+            try
+            {
+                Map<EncryptionType, EncryptionKey> keyMap = modifier.reconstituteKeyMap( krb5key );
+                modifier.setKeyMap( keyMap );
+            }
+            catch ( IOException ioe )
+            {
+                throw new InvalidAttributeValueException( "Account Kerberos key attribute '" + KerberosAttribute.KRB5_KEY_AT
+                    + "' contained an invalid value for krb5key." );
+            }
+        }
+
+        modifier.setKeyVersionNumber( Integer.parseInt( ( String ) attrs.get( KerberosAttribute.KRB5_KEY_VERSION_NUMBER_AT ).get() ) );
+
+        return modifier.getEntry();
+    }
+
+
+    public Object getObjectInstance( Object obj, Name name, Context nameCtx, Hashtable environment ) throws Exception
+    {
+        throw new UnsupportedOperationException( "Attributes are required to add an entry." );
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/store/operations/PrincipalStateFactory.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/store/operations/PrincipalStateFactory.java
new file mode 100644
index 0000000..ce01a95
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/store/operations/PrincipalStateFactory.java
@@ -0,0 +1,171 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.kerberos.shared.store.operations;
+
+
+import java.io.IOException;
+import java.util.Hashtable;
+
+import javax.naming.Context;
+import javax.naming.Name;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.InvalidAttributeValueException;
+import javax.naming.directory.SchemaViolationException;
+import javax.naming.spi.DirStateFactory;
+
+import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
+import org.apache.directory.server.kerberos.shared.io.encoder.EncryptionKeyEncoder;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+import org.apache.directory.server.kerberos.shared.store.KerberosAttribute;
+import org.apache.directory.server.kerberos.shared.store.PrincipalStoreEntry;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.util.AttributeUtils;
+
+
+/**
+ * A StateFactory for a server profile.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class PrincipalStateFactory implements DirStateFactory
+{
+    public Result getStateToBind( Object obj, Name name, Context nameCtx, Hashtable environment, Attributes inAttrs )
+        throws NamingException
+    {
+        // Only interested in PrincipalStoreEntry objects
+        if ( obj instanceof PrincipalStoreEntry )
+        {
+            Attributes outAttrs;
+            if ( inAttrs == null )
+            {
+                outAttrs = new AttributesImpl( true );
+            }
+            else
+            {
+                outAttrs = ( Attributes ) inAttrs.clone();
+            }
+
+            // process the objectClass attribute
+            Attribute oc = outAttrs.get( SchemaConstants.OBJECT_CLASS_AT );
+
+            if ( oc == null )
+            {
+                oc = new AttributeImpl( SchemaConstants.OBJECT_CLASS_AT );
+                outAttrs.put( oc );
+            }
+
+            if ( !AttributeUtils.containsValueCaseIgnore( oc, SchemaConstants.TOP_OC ) )
+            {
+                oc.add( SchemaConstants.TOP_OC );
+            }
+
+            PrincipalStoreEntry p = ( PrincipalStoreEntry ) obj;
+
+            if ( !AttributeUtils.containsValueCaseIgnore( oc, SchemaConstants.UID_OBJECT_AT ) )
+            {
+                oc.add( SchemaConstants.UID_OBJECT_AT );
+
+                if ( p.getUserId() != null )
+                {
+                    outAttrs.put( SchemaConstants.UID_AT, p.getUserId() );
+                }
+                else
+                {
+                    throw new SchemaViolationException( "Person must have uid." );
+                }
+            }
+
+            if ( !AttributeUtils.containsValueCaseIgnore( oc, SchemaConstants.EXTENSIBLE_OBJECT_OC ) )
+            {
+                oc.add( SchemaConstants.EXTENSIBLE_OBJECT_OC );
+                outAttrs.put( KerberosAttribute.APACHE_SAM_TYPE_AT, "7" );
+            }
+
+            if ( !( AttributeUtils.containsValueCaseIgnore( oc, SchemaConstants.PERSON_OC ) || oc
+                .contains( SchemaConstants.PERSON_OC_OID ) ) )
+            {
+                oc.add( SchemaConstants.PERSON_OC );
+
+                // TODO - look into adding sn, gn, and cn to ServerProfiles
+                outAttrs.put( SchemaConstants.SN_AT, p.getUserId() );
+                outAttrs.put( SchemaConstants.CN_AT, p.getCommonName() );
+            }
+
+            if ( !( AttributeUtils.containsValueCaseIgnore( oc, SchemaConstants.ORGANIZATIONAL_PERSON_OC ) || oc
+                .contains( SchemaConstants.ORGANIZATIONAL_PERSON_OC_OID ) ) )
+            {
+                oc.add( SchemaConstants.ORGANIZATIONAL_PERSON_OC );
+            }
+
+            if ( !( AttributeUtils.containsValueCaseIgnore( oc, SchemaConstants.INET_ORG_PERSON_OC ) || oc
+                .contains( SchemaConstants.INET_ORG_PERSON_OC_OID ) ) )
+            {
+                oc.add( SchemaConstants.INET_ORG_PERSON_OC );
+            }
+
+            if ( !oc.contains( SchemaConstants.KRB5_PRINCIPAL_OC ) )
+            {
+                oc.add( SchemaConstants.KRB5_PRINCIPAL_OC );
+            }
+
+            if ( !oc.contains( "krb5KDCEntry" ) )
+            {
+                oc.add( "krb5KDCEntry" );
+
+                String principal = p.getPrincipal().getName();
+
+                EncryptionKey encryptionKey = p.getKeyMap().get( EncryptionType.DES_CBC_MD5 );
+
+                try
+                {
+                    outAttrs.put( KerberosAttribute.KRB5_KEY_AT, EncryptionKeyEncoder.encode( encryptionKey ) );
+                }
+                catch ( IOException ioe )
+                {
+                    throw new InvalidAttributeValueException( "Unable to encode Kerberos key." );
+                }
+
+                int keyVersion = encryptionKey.getKeyVersion();
+
+                outAttrs.put( KerberosAttribute.KRB5_PRINCIPAL_NAME_AT, principal );
+                outAttrs.put( KerberosAttribute.KRB5_KEY_VERSION_NUMBER_AT, Integer.toString( keyVersion ) );
+            }
+
+            Result r = new Result( obj, outAttrs );
+
+            return r;
+        }
+
+        return null;
+    }
+
+
+    public Object getStateToBind( Object obj, Name name, Context nameCtx, Hashtable environment )
+        throws NamingException
+    {
+        throw new UnsupportedOperationException( "Structural objectClass needed with additional attributes!" );
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/store/operations/package-info.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/store/operations/package-info.java
new file mode 100644
index 0000000..86e1b14
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/store/operations/package-info.java
@@ -0,0 +1,28 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+/**
+ * Provides Command pattern objects for working with the JNDI backing store.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+
+package org.apache.directory.server.kerberos.shared.store.operations;
diff --git a/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/store/package-info.java b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/store/package-info.java
new file mode 100644
index 0000000..17d0749
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/store/package-info.java
@@ -0,0 +1,29 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+/**
+ * Provides the implementation for storing Kerberos principals
+ * and symmetric keys backed by JNDI.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+
+package org.apache.directory.server.kerberos.shared.store;
diff --git a/old_trunk/kerberos-shared/src/site/site.xml b/old_trunk/kerberos-shared/src/site/site.xml
new file mode 100644
index 0000000..9ec7c16
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/site/site.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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.
+-->
+<!--
+  @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+-->
+<project name="${project.name}">
+    <bannerLeft>
+        <name>${project.name}</name>
+    </bannerLeft>
+    <publishDate position="navigation-bottom" format="dd-MM-yyyy HH:mm" />
+    <body>
+        <menu name="Parent">
+            <item name="Apache Directory ApacheDS" href="../index.html" />
+        </menu>
+        <menu ref="reports" />
+    </body>
+</project>
diff --git a/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/KerberosUtilsTest.java b/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/KerberosUtilsTest.java
new file mode 100644
index 0000000..930cf03
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/KerberosUtilsTest.java
@@ -0,0 +1,138 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared;
+
+import java.util.List;
+
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.apache.directory.server.kerberos.shared.KerberosUtils;
+
+import junit.framework.TestCase;
+
+/**
+ * Test the KerberosUtils class
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class KerberosUtilsTest extends TestCase
+{
+    public void setUp()
+    {
+        // First setup a default realm
+        System.setProperty( "java.security.krb5.realm", "APACHE.ORG" );
+        System.setProperty( "java.security.krb5.kdc", "localhost" );
+    }
+
+    
+    public void testKerberosNameSimple() throws Exception
+    {
+        KerberosPrincipal kp = new KerberosPrincipal( "abc" );
+        List<String> names = KerberosUtils.getNames( kp );
+     
+        assertEquals( 1, names.size() );
+        assertEquals( "abc", names.get( 0 ) );
+    }
+
+    
+    /**
+    public void testKerberosNameEscaped() throws Exception
+    {
+        KerberosPrincipal kp = new KerberosPrincipal( "abc\\//d\\@f/g\\\\hi" );
+        List<String> names = KerberosUtils.getNames( kp );
+     
+        assertEquals( 3, names.size() );
+        assertEquals( "abc\\/", names.get( 0 ) );
+        assertEquals( "d\\@g", names.get( 1 ) );
+        assertEquals( "g\\\\hi", names.get( 2 ) );
+    }
+    */
+
+
+    public void testKerberosNameSimpleWithRealm() throws Exception
+    {
+        KerberosPrincipal kp = new KerberosPrincipal( "abc@APACHE.ORG" );
+        List<String> names = KerberosUtils.getNames( kp );
+     
+        assertEquals( 1, names.size() );
+        assertEquals( "abc", names.get( 0 ) );
+    }
+    
+    public void testKerberosNameThree() throws Exception
+    {
+        KerberosPrincipal kp = new KerberosPrincipal( "abc/def/ghi" );
+        List<String> names = KerberosUtils.getNames( kp );
+     
+        assertEquals( 3, names.size() );
+        assertEquals( "abc", names.get( 0 ) );
+        assertEquals( "def", names.get( 1 ) );
+        assertEquals( "ghi", names.get( 2 ) );
+    }
+
+    public void testKerberosNameThreeWithRealm() throws Exception
+    {
+        KerberosPrincipal kp = new KerberosPrincipal( "abc/def/ghi@APACHE.ORG" );
+        List<String> names = KerberosUtils.getNames( kp );
+     
+        assertEquals( 3, names.size() );
+        assertEquals( "abc", names.get( 0 ) );
+        assertEquals( "def", names.get( 1 ) );
+        assertEquals( "ghi", names.get( 2 ) );
+    }
+
+
+    
+    /*
+    public void testKerberosEndingSlash()
+    {
+        try
+        {
+            KerberosPrincipal kp = new KerberosPrincipal( "abc/def/ghi/" );
+            KerberosUtils.getNames( kp );
+            
+            // Should not reach this point
+            fail();
+        }
+        catch ( ParseException pe )
+        {
+            assertTrue( true );
+        }
+    }
+    */
+    
+    
+    /*
+    public void testKerberosEndingSlashWithRealm()
+    {
+        try
+        {
+            KerberosPrincipal kp = new KerberosPrincipal( "abc/def/ghi/@APACHE.ORG" );
+            KerberosUtils.getNames( kp );
+            
+            // Should not reach this point
+            fail();
+        }
+        catch ( ParseException pe )
+        {
+            assertTrue( true );
+        }
+    }
+    */
+}
diff --git a/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/crypto/encryption/AesEncryptionTest.java b/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/crypto/encryption/AesEncryptionTest.java
new file mode 100644
index 0000000..ca6cc69
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/crypto/encryption/AesEncryptionTest.java
@@ -0,0 +1,145 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.crypto.encryption;
+
+
+import java.security.GeneralSecurityException;
+import java.security.spec.AlgorithmParameterSpec;
+import java.util.Arrays;
+
+import javax.crypto.Cipher;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+import junit.framework.TestCase;
+
+
+/**
+ * Tests the use of AES for Kerberos, using test vectors from RFC 3962,
+ * "Advanced Encryption Standard (AES) Encryption for Kerberos 5."
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class AesEncryptionTest extends TestCase
+{
+    private byte[] keyBytes =
+        { ( byte ) 0x63, ( byte ) 0x68, ( byte ) 0x69, ( byte ) 0x63, ( byte ) 0x6b, ( byte ) 0x65, ( byte ) 0x6e,
+            ( byte ) 0x20, ( byte ) 0x74, ( byte ) 0x65, ( byte ) 0x72, ( byte ) 0x69, ( byte ) 0x79, ( byte ) 0x61,
+            ( byte ) 0x6b, ( byte ) 0x69 };
+
+    private SecretKey key = new SecretKeySpec( keyBytes, "AES" );
+
+    private byte[] iv =
+        { ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00,
+            ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00,
+            ( byte ) 0x00, ( byte ) 0x00, };
+
+    private AlgorithmParameterSpec paramSpec = new IvParameterSpec( iv );
+
+
+    /**
+     * Tests the first test vector from RFC 3962,
+     * "Advanced Encryption Standard (AES) Encryption for Kerberos 5."
+     */
+    public void testFirstAesVector()
+    {
+        if ( !VendorHelper.isCtsSupported() )
+        {
+            return;
+        }
+
+        byte[] input =
+            { ( byte ) 0x49, ( byte ) 0x20, ( byte ) 0x77, ( byte ) 0x6f, ( byte ) 0x75, ( byte ) 0x6c, ( byte ) 0x64,
+                ( byte ) 0x20, ( byte ) 0x6c, ( byte ) 0x69, ( byte ) 0x6b, ( byte ) 0x65, ( byte ) 0x20,
+                ( byte ) 0x74, ( byte ) 0x68, ( byte ) 0x65, ( byte ) 0x20 };
+
+        byte[] output =
+            { ( byte ) 0xc6, ( byte ) 0x35, ( byte ) 0x35, ( byte ) 0x68, ( byte ) 0xf2, ( byte ) 0xbf, ( byte ) 0x8c,
+                ( byte ) 0xb4, ( byte ) 0xd8, ( byte ) 0xa5, ( byte ) 0x80, ( byte ) 0x36, ( byte ) 0x2d,
+                ( byte ) 0xa7, ( byte ) 0xff, ( byte ) 0x7f, ( byte ) 0x97 };
+
+        byte[] result = aesCipher( key, input );
+
+        assertEquals( "Length", input.length, result.length );
+        assertTrue( Arrays.equals( output, result ) );
+    }
+
+
+    /**
+     * Tests the last test vector from RFC 3962,
+     * "Advanced Encryption Standard (AES) Encryption for Kerberos 5."
+     */
+    public void testLastAesVector()
+    {
+        if ( !VendorHelper.isCtsSupported() )
+        {
+            return;
+        }
+
+        byte[] input =
+            { ( byte ) 0x49, ( byte ) 0x20, ( byte ) 0x77, ( byte ) 0x6f, ( byte ) 0x75, ( byte ) 0x6c, ( byte ) 0x64,
+                ( byte ) 0x20, ( byte ) 0x6c, ( byte ) 0x69, ( byte ) 0x6b, ( byte ) 0x65, ( byte ) 0x20,
+                ( byte ) 0x74, ( byte ) 0x68, ( byte ) 0x65, ( byte ) 0x20, ( byte ) 0x47, ( byte ) 0x65,
+                ( byte ) 0x6e, ( byte ) 0x65, ( byte ) 0x72, ( byte ) 0x61, ( byte ) 0x6c, ( byte ) 0x20,
+                ( byte ) 0x47, ( byte ) 0x61, ( byte ) 0x75, ( byte ) 0x27, ( byte ) 0x73, ( byte ) 0x20,
+                ( byte ) 0x43, ( byte ) 0x68, ( byte ) 0x69, ( byte ) 0x63, ( byte ) 0x6b, ( byte ) 0x65,
+                ( byte ) 0x6e, ( byte ) 0x2c, ( byte ) 0x20, ( byte ) 0x70, ( byte ) 0x6c, ( byte ) 0x65,
+                ( byte ) 0x61, ( byte ) 0x73, ( byte ) 0x65, ( byte ) 0x2c, ( byte ) 0x20, ( byte ) 0x61,
+                ( byte ) 0x6e, ( byte ) 0x64, ( byte ) 0x20, ( byte ) 0x77, ( byte ) 0x6f, ( byte ) 0x6e,
+                ( byte ) 0x74, ( byte ) 0x6f, ( byte ) 0x6e, ( byte ) 0x20, ( byte ) 0x73, ( byte ) 0x6f,
+                ( byte ) 0x75, ( byte ) 0x70, ( byte ) 0x2e };
+
+        byte[] output =
+            { ( byte ) 0x97, ( byte ) 0x68, ( byte ) 0x72, ( byte ) 0x68, ( byte ) 0xd6, ( byte ) 0xec, ( byte ) 0xcc,
+                ( byte ) 0xc0, ( byte ) 0xc0, ( byte ) 0x7b, ( byte ) 0x25, ( byte ) 0xe2, ( byte ) 0x5e,
+                ( byte ) 0xcf, ( byte ) 0xe5, ( byte ) 0x84, ( byte ) 0x39, ( byte ) 0x31, ( byte ) 0x25,
+                ( byte ) 0x23, ( byte ) 0xa7, ( byte ) 0x86, ( byte ) 0x62, ( byte ) 0xd5, ( byte ) 0xbe,
+                ( byte ) 0x7f, ( byte ) 0xcb, ( byte ) 0xcc, ( byte ) 0x98, ( byte ) 0xeb, ( byte ) 0xf5,
+                ( byte ) 0xa8, ( byte ) 0x48, ( byte ) 0x07, ( byte ) 0xef, ( byte ) 0xe8, ( byte ) 0x36,
+                ( byte ) 0xee, ( byte ) 0x89, ( byte ) 0xa5, ( byte ) 0x26, ( byte ) 0x73, ( byte ) 0x0d,
+                ( byte ) 0xbc, ( byte ) 0x2f, ( byte ) 0x7b, ( byte ) 0xc8, ( byte ) 0x40, ( byte ) 0x9d,
+                ( byte ) 0xad, ( byte ) 0x8b, ( byte ) 0xbb, ( byte ) 0x96, ( byte ) 0xc4, ( byte ) 0xcd,
+                ( byte ) 0xc0, ( byte ) 0x3b, ( byte ) 0xc1, ( byte ) 0x03, ( byte ) 0xe1, ( byte ) 0xa1,
+                ( byte ) 0x94, ( byte ) 0xbb, ( byte ) 0xd8 };
+
+        byte[] result = aesCipher( key, input );
+
+        assertEquals( "Length", input.length, result.length );
+        assertTrue( Arrays.equals( output, result ) );
+    }
+
+
+    private byte[] aesCipher( SecretKey key, byte[] input )
+    {
+        try
+        {
+            Cipher ecipher = Cipher.getInstance( "AES/CTS/NoPadding" );
+            ecipher.init( Cipher.ENCRYPT_MODE, key, paramSpec );
+            return ecipher.doFinal( input );
+        }
+        catch ( GeneralSecurityException gse )
+        {
+            return new byte[]
+                { 0x00 };
+        }
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/crypto/encryption/CipherTextHandlerTest.java b/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/crypto/encryption/CipherTextHandlerTest.java
new file mode 100644
index 0000000..c05061a
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/crypto/encryption/CipherTextHandlerTest.java
@@ -0,0 +1,472 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.crypto.encryption;
+
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.TimeZone;
+
+import javax.security.auth.kerberos.KerberosKey;
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import junit.framework.TestCase;
+
+import org.apache.directory.server.kerberos.shared.exceptions.KerberosException;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptedData;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptedTimeStamp;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
+
+
+/**
+ * Test case for sealing and unsealing Kerberos CipherText.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class CipherTextHandlerTest extends TestCase
+{
+    private byte[] desEncryptedTimeStamp =
+        { ( byte ) 0x97, ( byte ) 0x21, ( byte ) 0x58, ( byte ) 0x5f, ( byte ) 0x81, ( byte ) 0x46, ( byte ) 0x17,
+            ( byte ) 0xa6, ( byte ) 0x4e, ( byte ) 0x8a, ( byte ) 0x5d, ( byte ) 0xe2, ( byte ) 0xf3, ( byte ) 0xd1,
+            ( byte ) 0x40, ( byte ) 0x30, ( byte ) 0x38, ( byte ) 0x5e, ( byte ) 0xb8, ( byte ) 0xf6, ( byte ) 0xad,
+            ( byte ) 0xd8, ( byte ) 0x7c, ( byte ) 0x30, ( byte ) 0xb0, ( byte ) 0x0d, ( byte ) 0x69, ( byte ) 0x71,
+            ( byte ) 0x08, ( byte ) 0xd5, ( byte ) 0x6a, ( byte ) 0x61, ( byte ) 0x1f, ( byte ) 0xee, ( byte ) 0x38,
+            ( byte ) 0xad, ( byte ) 0x43, ( byte ) 0x99, ( byte ) 0xae, ( byte ) 0xc2, ( byte ) 0xd2, ( byte ) 0xf5,
+            ( byte ) 0xb2, ( byte ) 0xb7, ( byte ) 0x95, ( byte ) 0x22, ( byte ) 0x93, ( byte ) 0x12, ( byte ) 0x63,
+            ( byte ) 0xd5, ( byte ) 0xf4, ( byte ) 0x39, ( byte ) 0xfa, ( byte ) 0x27, ( byte ) 0x6e, ( byte ) 0x8e };
+
+    private byte[] tripleDesEncryptedTimeStamp =
+        { ( byte ) 0x96, ( byte ) 0xcb, ( byte ) 0x38, ( byte ) 0xb3, ( byte ) 0xc9, ( byte ) 0xb5, ( byte ) 0x78,
+            ( byte ) 0x17, ( byte ) 0xba, ( byte ) 0x0a, ( byte ) 0x64, ( byte ) 0x49, ( byte ) 0x18, ( byte ) 0x39,
+            ( byte ) 0x57, ( byte ) 0x1e, ( byte ) 0xcf, ( byte ) 0xfc, ( byte ) 0x6e, ( byte ) 0x0f, ( byte ) 0x53,
+            ( byte ) 0xe2, ( byte ) 0x9c, ( byte ) 0x96, ( byte ) 0xfd, ( byte ) 0xbc, ( byte ) 0xc6, ( byte ) 0x1e,
+            ( byte ) 0x10, ( byte ) 0x35, ( byte ) 0xe0, ( byte ) 0x8f, ( byte ) 0xc1, ( byte ) 0x7f, ( byte ) 0xbd,
+            ( byte ) 0x86, ( byte ) 0x55, ( byte ) 0xf2, ( byte ) 0x22, ( byte ) 0x48, ( byte ) 0x86, ( byte ) 0xfb,
+            ( byte ) 0x92, ( byte ) 0x22, ( byte ) 0xe7, ( byte ) 0xbe, ( byte ) 0xd1, ( byte ) 0xec, ( byte ) 0x2e,
+            ( byte ) 0x37, ( byte ) 0xd8, ( byte ) 0x47, ( byte ) 0x1e, ( byte ) 0xa0, ( byte ) 0x16, ( byte ) 0x70,
+            ( byte ) 0x5f, ( byte ) 0x6b, ( byte ) 0x18, ( byte ) 0xf3 };
+
+    private byte[] aes128EncryptedTimeStamp =
+        { ( byte ) 0x4f, ( byte ) 0x1e, ( byte ) 0x52, ( byte ) 0xf5, ( byte ) 0xe0, ( byte ) 0xee, ( byte ) 0xe5,
+            ( byte ) 0xe2, ( byte ) 0x2c, ( byte ) 0x9b, ( byte ) 0xf4, ( byte ) 0xdc, ( byte ) 0x58, ( byte ) 0x5f,
+            ( byte ) 0x00, ( byte ) 0x96, ( byte ) 0x31, ( byte ) 0xfe, ( byte ) 0xc7, ( byte ) 0xf7, ( byte ) 0x89,
+            ( byte ) 0x38, ( byte ) 0x88, ( byte ) 0xf5, ( byte ) 0x25, ( byte ) 0xaf, ( byte ) 0x09, ( byte ) 0x9f,
+            ( byte ) 0xfd, ( byte ) 0x78, ( byte ) 0x68, ( byte ) 0x3b, ( byte ) 0xb4, ( byte ) 0x1e, ( byte ) 0xc2,
+            ( byte ) 0xfc, ( byte ) 0x2d, ( byte ) 0xf3, ( byte ) 0x41, ( byte ) 0x88, ( byte ) 0x92, ( byte ) 0x7e,
+            ( byte ) 0xd7, ( byte ) 0xed, ( byte ) 0xe1, ( byte ) 0xe0, ( byte ) 0x0c, ( byte ) 0xad, ( byte ) 0xe5,
+            ( byte ) 0x06, ( byte ) 0xbf, ( byte ) 0x30, ( byte ) 0x1e, ( byte ) 0xbf, ( byte ) 0xf2, ( byte ) 0xec };
+
+    private byte[] aes256EncryptedTimeStamp =
+        { ( byte ) 0xa8, ( byte ) 0x40, ( byte ) 0x73, ( byte ) 0xfc, ( byte ) 0xe5, ( byte ) 0x45, ( byte ) 0x66,
+            ( byte ) 0xd6, ( byte ) 0x83, ( byte ) 0xb4, ( byte ) 0xed, ( byte ) 0xb6, ( byte ) 0x18, ( byte ) 0x5a,
+            ( byte ) 0xd2, ( byte ) 0x24, ( byte ) 0xd6, ( byte ) 0xef, ( byte ) 0x38, ( byte ) 0xac, ( byte ) 0xdf,
+            ( byte ) 0xcd, ( byte ) 0xed, ( byte ) 0x6d, ( byte ) 0x32, ( byte ) 0xf6, ( byte ) 0x00, ( byte ) 0xd1,
+            ( byte ) 0xc0, ( byte ) 0xb0, ( byte ) 0x1e, ( byte ) 0x70, ( byte ) 0x13, ( byte ) 0x48, ( byte ) 0x0a,
+            ( byte ) 0x5a, ( byte ) 0xbb, ( byte ) 0xd2, ( byte ) 0x2a, ( byte ) 0x6b, ( byte ) 0x16, ( byte ) 0x29,
+            ( byte ) 0x63, ( byte ) 0xba, ( byte ) 0xea, ( byte ) 0xb7, ( byte ) 0x1a, ( byte ) 0x90, ( byte ) 0x7b,
+            ( byte ) 0xf4, ( byte ) 0x89, ( byte ) 0x94, ( byte ) 0x7a, ( byte ) 0x2d, ( byte ) 0x6a, ( byte ) 0xf1 };
+
+    private byte[] arcfourEncryptedTimeStamp =
+        { ( byte ) 0xa2, ( byte ) 0x4f, ( byte ) 0x04, ( byte ) 0x6d, ( byte ) 0x93, ( byte ) 0x31, ( byte ) 0x19,
+            ( byte ) 0x77, ( byte ) 0x3f, ( byte ) 0x9d, ( byte ) 0xf9, ( byte ) 0x6f, ( byte ) 0x7e, ( byte ) 0x86,
+            ( byte ) 0x2c, ( byte ) 0x99, ( byte ) 0x63, ( byte ) 0xc5, ( byte ) 0xcf, ( byte ) 0xe2, ( byte ) 0xf1,
+            ( byte ) 0x54, ( byte ) 0x05, ( byte ) 0x6a, ( byte ) 0xea, ( byte ) 0x20, ( byte ) 0x37, ( byte ) 0x31,
+            ( byte ) 0xa2, ( byte ) 0xdc, ( byte ) 0xe8, ( byte ) 0x79, ( byte ) 0xaa, ( byte ) 0xae, ( byte ) 0x1c,
+            ( byte ) 0xfa, ( byte ) 0x93, ( byte ) 0x02, ( byte ) 0xbe, ( byte ) 0x11, ( byte ) 0x14, ( byte ) 0x22,
+            ( byte ) 0x65, ( byte ) 0x92, ( byte ) 0xbd, ( byte ) 0xf5, ( byte ) 0x52, ( byte ) 0x9f, ( byte ) 0x94,
+            ( byte ) 0x67, ( byte ) 0x10, ( byte ) 0xd2 };
+
+    private static final TimeZone UTC_TIME_ZONE = TimeZone.getTimeZone( "UTC" );
+
+    private static final SimpleDateFormat dateFormat = new SimpleDateFormat( "yyyyMMddHHmmss'Z'" );
+
+    static
+    {
+        dateFormat.setTimeZone( UTC_TIME_ZONE );
+    }
+
+
+    /**
+     * Tests the lengths of the test vectors for encrypted timestamps for each
+     * of the supported encryption types.  The length of the Kerberos Cipher Text
+     * is relevant to the structure of the underlying plaintext.
+     */
+    public void testTestVectorLengths()
+    {
+        assertEquals( "DES length", 56, desEncryptedTimeStamp.length );
+        assertEquals( "DES3 length", 60, tripleDesEncryptedTimeStamp.length );
+        assertEquals( "AES128 length", 56, aes128EncryptedTimeStamp.length );
+        assertEquals( "AES256 length", 56, aes256EncryptedTimeStamp.length );
+        assertEquals( "RC4-HMAC length", 52, arcfourEncryptedTimeStamp.length );
+    }
+
+
+    /**
+     * Tests the unsealing of Kerberos CipherText with a good password.  After decryption and
+     * an integrity check, an attempt is made to decode the bytes as an EncryptedTimestamp.  The
+     * result is timestamp data.
+     */
+    public void testDesGoodPasswordDecrypt()
+    {
+        CipherTextHandler lockBox = new CipherTextHandler();
+        Class hint = EncryptedTimeStamp.class;
+        KerberosPrincipal principal = new KerberosPrincipal( "erodriguez@EXAMPLE.COM" );
+        KerberosKey kerberosKey = new KerberosKey( principal, "kerby".toCharArray(), "DES" );
+        EncryptionKey key = new EncryptionKey( EncryptionType.DES_CBC_MD5, kerberosKey.getEncoded() );
+        EncryptedData data = new EncryptedData( EncryptionType.DES_CBC_MD5, 0, desEncryptedTimeStamp );
+
+        try
+        {
+            EncryptedTimeStamp object = ( EncryptedTimeStamp ) lockBox.unseal( hint, key, data, KeyUsage.NUMBER1 );
+            assertEquals( "TimeStamp", "20070322233107Z", object.getTimeStamp().toString() );
+            assertEquals( "MicroSeconds", 291067, object.getMicroSeconds() );
+        }
+        catch ( KerberosException ke )
+        {
+            fail( "Should not have caught exception." );
+        }
+    }
+
+
+    /**
+     * Tests the unsealing of Kerberos CipherText with a bad password.  After decryption, the
+     * checksum is tested and should fail on comparison, resulting in an integrity check error.
+     */
+    public void testDesBadPasswordDecrypt()
+    {
+        CipherTextHandler lockBox = new CipherTextHandler();
+        Class hint = EncryptedTimeStamp.class;
+        KerberosPrincipal principal = new KerberosPrincipal( "erodriguez@EXAMPLE.COM" );
+        KerberosKey kerberosKey = new KerberosKey( principal, "badpassword".toCharArray(), "DES" );
+        EncryptionKey key = new EncryptionKey( EncryptionType.DES_CBC_MD5, kerberosKey.getEncoded() );
+        EncryptedData data = new EncryptedData( EncryptionType.DES_CBC_MD5, 0, desEncryptedTimeStamp );
+
+        try
+        {
+            lockBox.unseal( hint, key, data, KeyUsage.NUMBER1 );
+            fail( "Should have thrown exception." );
+        }
+        catch ( KerberosException ke )
+        {
+            assertEquals( "ErrorCode", 31, ke.getErrorCode() );
+        }
+    }
+
+
+    /**
+     * Tests the unsealing of Kerberos CipherText with a good password.  After decryption and
+     * an integrity check, an attempt is made to decode the bytes as an EncryptedTimestamp.  The
+     * result is timestamp data.
+     */
+    public void testTripleDesGoodPasswordDecrypt()
+    {
+        CipherTextHandler lockBox = new CipherTextHandler();
+        Class hint = EncryptedTimeStamp.class;
+        KerberosPrincipal principal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        String algorithm = VendorHelper.getTripleDesAlgorithm();
+        KerberosKey kerberosKey = new KerberosKey( principal, "secret".toCharArray(), algorithm );
+        EncryptionKey key = new EncryptionKey( EncryptionType.DES3_CBC_SHA1_KD, kerberosKey.getEncoded() );
+        EncryptedData data = new EncryptedData( EncryptionType.DES3_CBC_SHA1_KD, 0, tripleDesEncryptedTimeStamp );
+
+        try
+        {
+            EncryptedTimeStamp object = ( EncryptedTimeStamp ) lockBox.unseal( hint, key, data, KeyUsage.NUMBER1 );
+            assertEquals( "TimeStamp", "20070410190400Z", object.getTimeStamp().toString() );
+            assertEquals( "MicroSeconds", 460450, object.getMicroSeconds() );
+        }
+        catch ( KerberosException ke )
+        {
+            fail( "Should not have caught exception." );
+        }
+    }
+
+
+    /**
+     * Tests the encryption and subsequent unsealing of an ASN.1 encoded timestamp with a
+     * good password.  After encryption, an attempt is made to unseal the encrypted bytes
+     * as an EncryptedTimestamp.  The result is timestamp data.
+     * 
+     * @throws ParseException 
+     */
+    public void testTripleDesGoodPasswordEncrypt() throws ParseException
+    {
+        CipherTextHandler lockBox = new CipherTextHandler();
+        KerberosPrincipal principal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        String algorithm = VendorHelper.getTripleDesAlgorithm();
+        KerberosKey kerberosKey = new KerberosKey( principal, "secret".toCharArray(), algorithm );
+        EncryptionKey key = new EncryptionKey( EncryptionType.DES3_CBC_SHA1_KD, kerberosKey.getEncoded() );
+
+        String zuluTime = "20070410190400Z";
+        int microSeconds = 460450;
+        EncryptedTimeStamp encryptedTimeStamp = getEncryptedTimeStamp( zuluTime, microSeconds );
+
+        EncryptedData encryptedData = null;
+
+        try
+        {
+            encryptedData = lockBox.seal( key, encryptedTimeStamp, KeyUsage.NUMBER1 );
+        }
+        catch ( KerberosException ke )
+        {
+            fail( "Should not have caught exception." );
+        }
+
+        Class hint = EncryptedTimeStamp.class;
+
+        try
+        {
+            EncryptedTimeStamp object = ( EncryptedTimeStamp ) lockBox.unseal( hint, key, encryptedData,
+                KeyUsage.NUMBER1 );
+            assertEquals( "TimeStamp", zuluTime, object.getTimeStamp().toString() );
+            assertEquals( "MicroSeconds", microSeconds, object.getMicroSeconds() );
+        }
+        catch ( KerberosException ke )
+        {
+            fail( "Should not have caught exception." );
+        }
+    }
+
+
+    /**
+     * Tests the unsealing of Kerberos CipherText with a good password.  After decryption and
+     * an integrity check, an attempt is made to decode the bytes as an EncryptedTimestamp.  The
+     * result is timestamp data.
+     */
+    public void testAes128GoodPasswordDecrypt()
+    {
+        if ( !VendorHelper.isCtsSupported() )
+        {
+            return;
+        }
+
+        CipherTextHandler lockBox = new CipherTextHandler();
+        Class hint = EncryptedTimeStamp.class;
+        KerberosPrincipal principal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        KerberosKey kerberosKey = new KerberosKey( principal, "secret".toCharArray(), "AES128" );
+        EncryptionKey key = new EncryptionKey( EncryptionType.AES128_CTS_HMAC_SHA1_96, kerberosKey.getEncoded() );
+        EncryptedData data = new EncryptedData( EncryptionType.AES128_CTS_HMAC_SHA1_96, 0, aes128EncryptedTimeStamp );
+
+        try
+        {
+            EncryptedTimeStamp object = ( EncryptedTimeStamp ) lockBox.unseal( hint, key, data, KeyUsage.NUMBER1 );
+            assertEquals( "TimeStamp", "20070410212557Z", object.getTimeStamp().toString() );
+            assertEquals( "MicroSeconds", 379386, object.getMicroSeconds() );
+        }
+        catch ( KerberosException ke )
+        {
+            fail( "Should not have caught exception." );
+        }
+    }
+
+
+    /**
+     * Tests the encryption and subsequent unsealing of an ASN.1 encoded timestamp with a
+     * good password.  After encryption, an attempt is made to unseal the encrypted bytes
+     * as an EncryptedTimestamp.  The result is timestamp data.
+     * 
+     * @throws ParseException 
+     */
+    public void testAes128GoodPasswordEncrypt() throws ParseException
+    {
+        if ( !VendorHelper.isCtsSupported() )
+        {
+            return;
+        }
+
+        CipherTextHandler lockBox = new CipherTextHandler();
+        KerberosPrincipal principal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        KerberosKey kerberosKey = new KerberosKey( principal, "secret".toCharArray(), "AES128" );
+        EncryptionKey key = new EncryptionKey( EncryptionType.AES128_CTS_HMAC_SHA1_96, kerberosKey.getEncoded() );
+
+        String zuluTime = "20070410190400Z";
+        int microSeconds = 460450;
+        EncryptedTimeStamp encryptedTimeStamp = getEncryptedTimeStamp( zuluTime, microSeconds );
+
+        EncryptedData encryptedData = null;
+
+        try
+        {
+            encryptedData = lockBox.seal( key, encryptedTimeStamp, KeyUsage.NUMBER1 );
+        }
+        catch ( KerberosException ke )
+        {
+            fail( "Should not have caught exception." );
+        }
+
+        Class hint = EncryptedTimeStamp.class;
+
+        try
+        {
+            EncryptedTimeStamp object = ( EncryptedTimeStamp ) lockBox.unseal( hint, key, encryptedData,
+                KeyUsage.NUMBER1 );
+            assertEquals( "TimeStamp", "20070410190400Z", object.getTimeStamp().toString() );
+            assertEquals( "MicroSeconds", 460450, object.getMicroSeconds() );
+        }
+        catch ( KerberosException ke )
+        {
+            fail( "Should not have caught exception." );
+        }
+    }
+
+
+    /**
+     * Tests the unsealing of Kerberos CipherText with a good password.  After decryption and
+     * an integrity check, an attempt is made to decode the bytes as an EncryptedTimestamp.  The
+     * result is timestamp data.
+     */
+    public void testAes256GoodPasswordDecrypt()
+    {
+        if ( !VendorHelper.isCtsSupported() )
+        {
+            return;
+        }
+
+        CipherTextHandler lockBox = new CipherTextHandler();
+        Class hint = EncryptedTimeStamp.class;
+
+        KerberosKey kerberosKey;
+
+        try
+        {
+            KerberosPrincipal principal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+            kerberosKey = new KerberosKey( principal, "secret".toCharArray(), "AES256" );
+        }
+        catch ( IllegalArgumentException iae )
+        {
+            // Algorithm AES256 not enabled
+            return;
+        }
+
+        EncryptionKey key = new EncryptionKey( EncryptionType.AES256_CTS_HMAC_SHA1_96, kerberosKey.getEncoded() );
+        EncryptedData data = new EncryptedData( EncryptionType.AES256_CTS_HMAC_SHA1_96, 0, aes256EncryptedTimeStamp );
+
+        try
+        {
+            EncryptedTimeStamp object = ( EncryptedTimeStamp ) lockBox.unseal( hint, key, data, KeyUsage.NUMBER1 );
+            assertEquals( "TimeStamp", "20070410212809Z", object.getTimeStamp().toString() );
+            assertEquals( "MicroSeconds", 298294, object.getMicroSeconds() );
+        }
+        catch ( KerberosException ke )
+        {
+            fail( "Should not have caught exception." );
+        }
+    }
+
+
+    /**
+     * Tests the encryption and subsequent unsealing of an ASN.1 encoded timestamp with a
+     * good password.  After encryption, an attempt is made to unseal the encrypted bytes
+     * as an EncryptedTimestamp.  The result is timestamp data.
+     * 
+     * @throws ParseException 
+     */
+    public void testAes256GoodPasswordEncrypt() throws ParseException
+    {
+        if ( !VendorHelper.isCtsSupported() )
+        {
+            return;
+        }
+
+        CipherTextHandler lockBox = new CipherTextHandler();
+
+        KerberosKey kerberosKey;
+
+        try
+        {
+            KerberosPrincipal principal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+            kerberosKey = new KerberosKey( principal, "secret".toCharArray(), "AES256" );
+        }
+        catch ( IllegalArgumentException iae )
+        {
+            // Algorithm AES256 not enabled
+            return;
+        }
+
+        EncryptionKey key = new EncryptionKey( EncryptionType.AES256_CTS_HMAC_SHA1_96, kerberosKey.getEncoded() );
+
+        String zuluTime = "20070410190400Z";
+        int microSeconds = 460450;
+        EncryptedTimeStamp encryptedTimeStamp = getEncryptedTimeStamp( zuluTime, microSeconds );
+
+        EncryptedData encryptedData = null;
+
+        try
+        {
+            encryptedData = lockBox.seal( key, encryptedTimeStamp, KeyUsage.NUMBER1 );
+        }
+        catch ( KerberosException ke )
+        {
+            fail( "Should not have caught exception." );
+        }
+
+        Class hint = EncryptedTimeStamp.class;
+
+        try
+        {
+            EncryptedTimeStamp object = ( EncryptedTimeStamp ) lockBox.unseal( hint, key, encryptedData,
+                KeyUsage.NUMBER1 );
+            assertEquals( "TimeStamp", "20070410190400Z", object.getTimeStamp().toString() );
+            assertEquals( "MicroSeconds", 460450, object.getMicroSeconds() );
+        }
+        catch ( KerberosException ke )
+        {
+            fail( "Should not have caught exception." );
+        }
+    }
+
+
+    protected EncryptedTimeStamp getEncryptedTimeStamp( String zuluTime, int microSeconds ) throws ParseException
+    {
+        Date date = null;
+        synchronized ( dateFormat )
+        {
+            date = dateFormat.parse( zuluTime );
+        }
+
+        KerberosTime timeStamp = new KerberosTime( date );
+
+        return new EncryptedTimeStamp( timeStamp, microSeconds );
+    }
+
+    /*
+     public void testArcFourGoodPassword()
+     {
+     LockBox lockBox = new LockBox();
+     Class hint = EncryptedTimeStamp.class;
+     KerberosPrincipal principal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+     KerberosKey kerberosKey = new KerberosKey( principal, "secret".toCharArray(), "ArcFourHmac" );
+     EncryptionKey key = new EncryptionKey( EncryptionType.RC4_HMAC, kerberosKey.getEncoded() );
+     EncryptedData data = new EncryptedData( EncryptionType.RC4_HMAC, 0, arcfourEncryptedTimeStamp );
+
+     try
+     {
+     EncryptedTimeStamp object = ( EncryptedTimeStamp ) lockBox.unseal( hint, key, data );
+     assertEquals( "TimeStamp", "20070322233107Z", object.getTimeStamp().toString() );
+     assertEquals( "MicroSeconds", 291067, object.getMicroSeconds() );
+     }
+     catch ( KerberosException ke )
+     {
+     fail( "Should not have caught exception." );
+     }
+     }*/
+}
diff --git a/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/crypto/encryption/Des3CbcSha1KdEncryptionTest.java b/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/crypto/encryption/Des3CbcSha1KdEncryptionTest.java
new file mode 100644
index 0000000..f0c1c3c
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/crypto/encryption/Des3CbcSha1KdEncryptionTest.java
@@ -0,0 +1,236 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.crypto.encryption;
+
+
+import java.util.Arrays;
+
+import javax.security.auth.kerberos.KerberosKey;
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import junit.framework.TestCase;
+
+
+/**
+ * Tests the use of Triple DES for Kerberos, using test vectors from RFC 3961,
+ * "Encryption and Checksum Specifications for Kerberos 5."
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class Des3CbcSha1KdEncryptionTest extends TestCase
+{
+    private Des3CbcSha1KdEncryption keyDerivationFunction = new Des3CbcSha1KdEncryption();
+
+
+    /**
+     * Tests setting parity as defined in RFC 3961.
+     */
+    public void testParity()
+    {
+        byte[] test =
+            { ( byte ) 0x93, ( byte ) 0x50, ( byte ) 0x79, ( byte ) 0xd1, ( byte ) 0x44, ( byte ) 0x90, ( byte ) 0xa7 };
+        byte[] expected =
+            { ( byte ) 0x92, ( byte ) 0x51, ( byte ) 0x79, ( byte ) 0xd0, ( byte ) 0x45, ( byte ) 0x91, ( byte ) 0xa7,
+                ( byte ) 0x9b };
+
+        byte[] result = keyDerivationFunction.setParity( test );
+
+        assertTrue( Arrays.equals( expected, result ) );
+    }
+
+
+    /**
+     * Tests 'deriveRandom' and 'randomToKey' functions. 
+     */
+    public void testDerivedKey()
+    {
+        byte[] key =
+            { ( byte ) 0xdc, ( byte ) 0xe0, ( byte ) 0x6b, ( byte ) 0x1f, ( byte ) 0x64, ( byte ) 0xc8, ( byte ) 0x57,
+                ( byte ) 0xa1, ( byte ) 0x1c, ( byte ) 0x3d, ( byte ) 0xb5, ( byte ) 0x7c, ( byte ) 0x51,
+                ( byte ) 0x89, ( byte ) 0x9b, ( byte ) 0x2c, ( byte ) 0xc1, ( byte ) 0x79, ( byte ) 0x10,
+                ( byte ) 0x08, ( byte ) 0xce, ( byte ) 0x97, ( byte ) 0x3b, ( byte ) 0x92 };
+
+        byte[] usage =
+            { ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x01, ( byte ) 0x55 };
+
+        byte[] DR =
+            { ( byte ) 0x93, ( byte ) 0x50, ( byte ) 0x79, ( byte ) 0xd1, ( byte ) 0x44, ( byte ) 0x90, ( byte ) 0xa7,
+                ( byte ) 0x5c, ( byte ) 0x30, ( byte ) 0x93, ( byte ) 0xc4, ( byte ) 0xa6, ( byte ) 0xe8,
+                ( byte ) 0xc3, ( byte ) 0xb0, ( byte ) 0x49, ( byte ) 0xc7, ( byte ) 0x1e, ( byte ) 0x6e,
+                ( byte ) 0xe7, ( byte ) 0x05 };
+
+        byte[] DK =
+            { ( byte ) 0x92, ( byte ) 0x51, ( byte ) 0x79, ( byte ) 0xd0, ( byte ) 0x45, ( byte ) 0x91, ( byte ) 0xa7,
+                ( byte ) 0x9b, ( byte ) 0x5d, ( byte ) 0x31, ( byte ) 0x92, ( byte ) 0xc4, ( byte ) 0xa7,
+                ( byte ) 0xe9, ( byte ) 0xc2, ( byte ) 0x89, ( byte ) 0xb0, ( byte ) 0x49, ( byte ) 0xc7,
+                ( byte ) 0x1f, ( byte ) 0x6e, ( byte ) 0xe6, ( byte ) 0x04, ( byte ) 0xcd };
+
+        byte[] result = keyDerivationFunction.deriveRandom( key, usage, 64, 168 );
+        assertTrue( Arrays.equals( DR, result ) );
+
+        result = keyDerivationFunction.randomToKey( result );
+        assertTrue( Arrays.equals( DK, result ) );
+    }
+
+
+    /**
+     * Tests 'deriveRandom' and 'randomToKey' functions. 
+     */
+    public void testDerivedKey2()
+    {
+        byte[] key =
+            { ( byte ) 0x5e, ( byte ) 0x13, ( byte ) 0xd3, ( byte ) 0x1c, ( byte ) 0x70, ( byte ) 0xef, ( byte ) 0x76,
+                ( byte ) 0x57, ( byte ) 0x46, ( byte ) 0x57, ( byte ) 0x85, ( byte ) 0x31, ( byte ) 0xcb,
+                ( byte ) 0x51, ( byte ) 0xc1, ( byte ) 0x5b, ( byte ) 0xf1, ( byte ) 0x1c, ( byte ) 0xa8,
+                ( byte ) 0x2c, ( byte ) 0x97, ( byte ) 0xce, ( byte ) 0xe9, ( byte ) 0xf2 };
+
+        byte[] usage =
+            { ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x01, ( byte ) 0xaa };
+
+        byte[] DR =
+            { ( byte ) 0x9f, ( byte ) 0x58, ( byte ) 0xe5, ( byte ) 0xa0, ( byte ) 0x47, ( byte ) 0xd8, ( byte ) 0x94,
+                ( byte ) 0x10, ( byte ) 0x1c, ( byte ) 0x46, ( byte ) 0x98, ( byte ) 0x45, ( byte ) 0xd6,
+                ( byte ) 0x7a, ( byte ) 0xe3, ( byte ) 0xc5, ( byte ) 0x24, ( byte ) 0x9e, ( byte ) 0xd8,
+                ( byte ) 0x12, ( byte ) 0xf2 };
+
+        byte[] DK =
+            { ( byte ) 0x9e, ( byte ) 0x58, ( byte ) 0xe5, ( byte ) 0xa1, ( byte ) 0x46, ( byte ) 0xd9, ( byte ) 0x94,
+                ( byte ) 0x2a, ( byte ) 0x10, ( byte ) 0x1c, ( byte ) 0x46, ( byte ) 0x98, ( byte ) 0x45,
+                ( byte ) 0xd6, ( byte ) 0x7a, ( byte ) 0x20, ( byte ) 0xe3, ( byte ) 0xc4, ( byte ) 0x25,
+                ( byte ) 0x9e, ( byte ) 0xd9, ( byte ) 0x13, ( byte ) 0xf2, ( byte ) 0x07 };
+
+        byte[] result = keyDerivationFunction.deriveRandom( key, usage, 64, 168 );
+        assertTrue( Arrays.equals( DR, result ) );
+
+        result = keyDerivationFunction.randomToKey( result );
+        assertTrue( Arrays.equals( DK, result ) );
+    }
+
+
+    /**
+     * Tests that key derivation can be performed for a Triple-DES key.
+     */
+    public void testTestVectorsTripleDesKerberosKey1()
+    {
+        byte[] expectedKey =
+            { ( byte ) 0x85, ( byte ) 0x0B, ( byte ) 0xB5, ( byte ) 0x13, ( byte ) 0x58, ( byte ) 0x54, ( byte ) 0x8C,
+                ( byte ) 0xD0, ( byte ) 0x5E, ( byte ) 0x86, ( byte ) 0x76, ( byte ) 0x8C, ( byte ) 0x31,
+                ( byte ) 0x3E, ( byte ) 0x3B, ( byte ) 0xFE, ( byte ) 0xF7, ( byte ) 0x51, ( byte ) 0x19,
+                ( byte ) 0x37, ( byte ) 0xDC, ( byte ) 0xF7, ( byte ) 0x2C, ( byte ) 0x3E };
+
+        KerberosPrincipal principal = new KerberosPrincipal( "raeburn@ATHENA.MIT.EDU" );
+        String algorithm = VendorHelper.getTripleDesAlgorithm();
+        KerberosKey key = new KerberosKey( principal, "password".toCharArray(), algorithm );
+
+        assertEquals( "DESede key length", 24, key.getEncoded().length );
+        assertTrue( "Key match", Arrays.equals( expectedKey, key.getEncoded() ) );
+    }
+
+
+    /**
+     * Tests that key derivation can be performed for a Triple-DES key.
+     */
+    public void testTestVectorsTripleDesKerberosKey2()
+    {
+        byte[] expectedKey =
+            { ( byte ) 0xDF, ( byte ) 0xCD, ( byte ) 0x23, ( byte ) 0x3D, ( byte ) 0xD0, ( byte ) 0xA4, ( byte ) 0x32,
+                ( byte ) 0x04, ( byte ) 0xEA, ( byte ) 0x6D, ( byte ) 0xC4, ( byte ) 0x37, ( byte ) 0xFB,
+                ( byte ) 0x15, ( byte ) 0xE0, ( byte ) 0x61, ( byte ) 0xB0, ( byte ) 0x29, ( byte ) 0x79,
+                ( byte ) 0xC1, ( byte ) 0xF7, ( byte ) 0x4F, ( byte ) 0x37, ( byte ) 0x7A };
+
+        KerberosPrincipal principal = new KerberosPrincipal( "danny@WHITEHOUSE.GOV" );
+        String algorithm = VendorHelper.getTripleDesAlgorithm();
+        KerberosKey key = new KerberosKey( principal, "potatoe".toCharArray(), algorithm );
+
+        assertEquals( "DESede key length", 24, key.getEncoded().length );
+        assertTrue( "Key match", Arrays.equals( expectedKey, key.getEncoded() ) );
+    }
+
+
+    /**
+     * Tests that key derivation can be performed for a Triple-DES key.
+     */
+    public void testTestVectorsTripleDesKerberosKey3()
+    {
+        byte[] expectedKey =
+            { ( byte ) 0x6D, ( byte ) 0x2F, ( byte ) 0xCD, ( byte ) 0xF2, ( byte ) 0xD6, ( byte ) 0xFB, ( byte ) 0xBC,
+                ( byte ) 0x3D, ( byte ) 0xDC, ( byte ) 0xAD, ( byte ) 0xB5, ( byte ) 0xDA, ( byte ) 0x57,
+                ( byte ) 0x10, ( byte ) 0xA2, ( byte ) 0x34, ( byte ) 0x89, ( byte ) 0xB0, ( byte ) 0xD3,
+                ( byte ) 0xB6, ( byte ) 0x9D, ( byte ) 0x5D, ( byte ) 0x9D, ( byte ) 0x4A };
+
+        KerberosPrincipal principal = new KerberosPrincipal( "buckaroo@EXAMPLE.COM" );
+        String algorithm = VendorHelper.getTripleDesAlgorithm();
+        KerberosKey key = new KerberosKey( principal, "penny".toCharArray(), algorithm );
+
+        assertEquals( "DESede key length", 24, key.getEncoded().length );
+        assertTrue( "Key match", Arrays.equals( expectedKey, key.getEncoded() ) );
+    }
+
+
+    /**
+     * Tests that key derivation can be performed for a Triple-DES key.
+     */
+    public void testTestVectorsTripleDesKerberosKey4()
+    {
+        if ( VendorHelper.isIbm() )
+        {
+            return;
+        }
+
+        byte[] expectedKey =
+            { ( byte ) 0x16, ( byte ) 0xD5, ( byte ) 0xA4, ( byte ) 0x0E, ( byte ) 0x1C, ( byte ) 0xE3, ( byte ) 0xBA,
+                ( byte ) 0xCB, ( byte ) 0x61, ( byte ) 0xB9, ( byte ) 0xDC, ( byte ) 0xE0, ( byte ) 0x04,
+                ( byte ) 0x70, ( byte ) 0x32, ( byte ) 0x4C, ( byte ) 0x83, ( byte ) 0x19, ( byte ) 0x73,
+                ( byte ) 0xA7, ( byte ) 0xB9, ( byte ) 0x52, ( byte ) 0xFE, ( byte ) 0xB0 };
+
+        KerberosPrincipal principal = new KerberosPrincipal( "Juri\u0161i\u0107@ATHENA.MIT.EDU" );
+        String algorithm = VendorHelper.getTripleDesAlgorithm();
+        KerberosKey key = new KerberosKey( principal, "\u00DF".toCharArray(), algorithm );
+
+        assertEquals( "DESede key length", 24, key.getEncoded().length );
+        assertTrue( "Key match", Arrays.equals( expectedKey, key.getEncoded() ) );
+    }
+
+
+    /**
+     * Tests that key derivation can be performed for a Triple-DES key.
+     */
+    public void testTestVectorsTripleDesKerberosKey5()
+    {
+        if ( VendorHelper.isIbm() )
+        {
+            return;
+        }
+
+        byte[] expectedKey =
+            { ( byte ) 0x85, ( byte ) 0x76, ( byte ) 0x37, ( byte ) 0x26, ( byte ) 0x58, ( byte ) 0x5D, ( byte ) 0xBC,
+                ( byte ) 0x1C, ( byte ) 0xCE, ( byte ) 0x6E, ( byte ) 0xC4, ( byte ) 0x3E, ( byte ) 0x1F,
+                ( byte ) 0x75, ( byte ) 0x1F, ( byte ) 0x07, ( byte ) 0xF1, ( byte ) 0xC4, ( byte ) 0xCB,
+                ( byte ) 0xB0, ( byte ) 0x98, ( byte ) 0xF4, ( byte ) 0x0B, ( byte ) 0x19 };
+
+        KerberosPrincipal principal = new KerberosPrincipal( "pianist@EXAMPLE.COM" );
+        String algorithm = VendorHelper.getTripleDesAlgorithm();
+        KerberosKey key = new KerberosKey( principal, "\uD834\uDD1E".toCharArray(), algorithm );
+
+        assertEquals( "DESede key length", 24, key.getEncoded().length );
+        assertTrue( "Key match", Arrays.equals( expectedKey, key.getEncoded() ) );
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/crypto/encryption/DesCbcCrcEncryptionTest.java b/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/crypto/encryption/DesCbcCrcEncryptionTest.java
new file mode 100644
index 0000000..fb2d956
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/crypto/encryption/DesCbcCrcEncryptionTest.java
@@ -0,0 +1,124 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.crypto.encryption;
+
+
+import javax.security.auth.kerberos.KerberosKey;
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import junit.framework.TestCase;
+
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptedData;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+
+
+/**
+ * Test case for the DES-CBC-CRC encryption type.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class DesCbcCrcEncryptionTest extends TestCase
+{
+    private static final char[] PASSWORD = "password".toCharArray();
+
+
+    /**
+     * Test successful encryption and decryption when the plaintext size is less than the block size.
+     *
+     * @throws Exception
+     */
+    public void testPlainTextSizeLessThanBlockSize() throws Exception
+    {
+        KerberosKey key = new KerberosKey( new KerberosPrincipal( "hnelson@EXAMPLE.COM" ), PASSWORD, "DES" );
+        byte[] keyBytes = key.getEncoded();
+        EncryptionKey encryptionKey = new EncryptionKey( EncryptionType.DES_CBC_CRC, keyBytes );
+
+        byte[] plainText =
+            { 1, 2, 3, 4, 5, 6, 7 };
+
+        DesCbcCrcEncryption encryption = new DesCbcCrcEncryption();
+        EncryptedData encryptedData = encryption.getEncryptedData( encryptionKey, plainText, null );
+
+        byte[] recoveredText = encryption.getDecryptedData( encryptionKey, encryptedData, null );
+
+        assertTrue( beginsWith( plainText, recoveredText ) );
+    }
+
+
+    /**
+     * Test successful encryption and decryption when the plaintext size equals the block size.
+     *
+     * @throws Exception
+     */
+    public void testPlainTextSizeEqualsBlockSize() throws Exception
+    {
+        KerberosKey key = new KerberosKey( new KerberosPrincipal( "hnelson@EXAMPLE.COM" ), PASSWORD, "DES" );
+        byte[] keyBytes = key.getEncoded();
+        EncryptionKey encryptionKey = new EncryptionKey( EncryptionType.DES_CBC_CRC, keyBytes );
+
+        byte[] plainText =
+            { 1, 2, 3, 4, 5, 6, 7, 8 };
+
+        DesCbcCrcEncryption encryption = new DesCbcCrcEncryption();
+        EncryptedData encryptedData = encryption.getEncryptedData( encryptionKey, plainText, null );
+
+        byte[] recoveredText = encryption.getDecryptedData( encryptionKey, encryptedData, null );
+
+        assertTrue( beginsWith( plainText, recoveredText ) );
+    }
+
+
+    /**
+     * Test successful encryption and decryption when the plaintext size is greater than the block size.
+     *
+     * @throws Exception
+     */
+    public void testPlainTextSizeGreaterThanBlockSize() throws Exception
+    {
+        KerberosKey key = new KerberosKey( new KerberosPrincipal( "hnelson@EXAMPLE.COM" ), PASSWORD, "DES" );
+        byte[] keyBytes = key.getEncoded();
+        EncryptionKey encryptionKey = new EncryptionKey( EncryptionType.DES_CBC_CRC, keyBytes );
+
+        byte[] plainText =
+            { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+
+        DesCbcCrcEncryption encryption = new DesCbcCrcEncryption();
+        EncryptedData encryptedData = encryption.getEncryptedData( encryptionKey, plainText, null );
+
+        byte[] recoveredText = encryption.getDecryptedData( encryptionKey, encryptedData, null );
+
+        assertTrue( beginsWith( plainText, recoveredText ) );
+    }
+
+
+    private boolean beginsWith( byte[] plainText, byte[] recoveredText )
+    {
+        for ( int i = 0; i < plainText.length; i++ )
+        {
+            if ( plainText[i] != recoveredText[i] )
+            {
+                return false;
+            }
+        }
+
+        return true;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/crypto/encryption/DesCbcMd5EncryptionTest.java b/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/crypto/encryption/DesCbcMd5EncryptionTest.java
new file mode 100644
index 0000000..cdcb614
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/crypto/encryption/DesCbcMd5EncryptionTest.java
@@ -0,0 +1,124 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.crypto.encryption;
+
+
+import javax.security.auth.kerberos.KerberosKey;
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import junit.framework.TestCase;
+
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptedData;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+
+
+/**
+ * Test case for the DES-CBC-MD5 encryption type.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class DesCbcMd5EncryptionTest extends TestCase
+{
+    private static final char[] PASSWORD = "password".toCharArray();
+
+
+    /**
+     * Test successful encryption and decryption when the plaintext size is less than the block size.
+     *
+     * @throws Exception
+     */
+    public void testPlainTextSizeLessThanBlockSize() throws Exception
+    {
+        KerberosKey key = new KerberosKey( new KerberosPrincipal( "hnelson@EXAMPLE.COM" ), PASSWORD, "DES" );
+        byte[] keyBytes = key.getEncoded();
+        EncryptionKey encryptionKey = new EncryptionKey( EncryptionType.DES_CBC_MD5, keyBytes );
+
+        byte[] plainText =
+            { 1, 2, 3, 4, 5, 6, 7 };
+
+        DesCbcMd5Encryption encryption = new DesCbcMd5Encryption();
+        EncryptedData encryptedData = encryption.getEncryptedData( encryptionKey, plainText, null );
+
+        byte[] recoveredText = encryption.getDecryptedData( encryptionKey, encryptedData, null );
+
+        assertTrue( beginsWith( plainText, recoveredText ) );
+    }
+
+
+    /**
+     * Test successful encryption and decryption when the plaintext size equals the block size.
+     *
+     * @throws Exception
+     */
+    public void testPlainTextSizeEqualsBlockSize() throws Exception
+    {
+        KerberosKey key = new KerberosKey( new KerberosPrincipal( "hnelson@EXAMPLE.COM" ), PASSWORD, "DES" );
+        byte[] keyBytes = key.getEncoded();
+        EncryptionKey encryptionKey = new EncryptionKey( EncryptionType.DES_CBC_MD5, keyBytes );
+
+        byte[] plainText =
+            { 1, 2, 3, 4, 5, 6, 7, 8 };
+
+        DesCbcMd5Encryption encryption = new DesCbcMd5Encryption();
+        EncryptedData encryptedData = encryption.getEncryptedData( encryptionKey, plainText, null );
+
+        byte[] recoveredText = encryption.getDecryptedData( encryptionKey, encryptedData, null );
+
+        assertTrue( beginsWith( plainText, recoveredText ) );
+    }
+
+
+    /**
+     * Test successful encryption and decryption when the plaintext size is greater than the block size.
+     *
+     * @throws Exception
+     */
+    public void testPlainTextSizeGreaterThanBlockSize() throws Exception
+    {
+        KerberosKey key = new KerberosKey( new KerberosPrincipal( "hnelson@EXAMPLE.COM" ), PASSWORD, "DES" );
+        byte[] keyBytes = key.getEncoded();
+        EncryptionKey encryptionKey = new EncryptionKey( EncryptionType.DES_CBC_MD5, keyBytes );
+
+        byte[] plainText =
+            { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+
+        DesCbcMd5Encryption encryption = new DesCbcMd5Encryption();
+        EncryptedData encryptedData = encryption.getEncryptedData( encryptionKey, plainText, null );
+
+        byte[] recoveredText = encryption.getDecryptedData( encryptionKey, encryptedData, null );
+
+        assertTrue( beginsWith( plainText, recoveredText ) );
+    }
+
+
+    private boolean beginsWith( byte[] plainText, byte[] recoveredText )
+    {
+        for ( int i = 0; i < plainText.length; i++ )
+        {
+            if ( plainText[i] != recoveredText[i] )
+            {
+                return false;
+            }
+        }
+
+        return true;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/crypto/encryption/DesStringToKeyTest.java b/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/crypto/encryption/DesStringToKeyTest.java
new file mode 100644
index 0000000..e112ff3
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/crypto/encryption/DesStringToKeyTest.java
@@ -0,0 +1,158 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.crypto.encryption;
+
+
+import java.security.InvalidKeyException;
+import java.util.Arrays;
+
+import javax.crypto.spec.DESKeySpec;
+
+import junit.framework.TestCase;
+
+
+/**
+ * Test cases for the DES string-to-key function as described in RFC 3961,
+ * "Encryption and Checksum Specifications for Kerberos 5."
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class DesStringToKeyTest extends TestCase
+{
+    private byte[] fanFold1 =
+        { ( byte ) 0xC0, ( byte ) 0x1E, ( byte ) 0x38, ( byte ) 0x68, ( byte ) 0x8A, ( byte ) 0xC8, ( byte ) 0x6C,
+            ( byte ) 0x2E };
+    private byte[] intermediateKey1 =
+        { ( byte ) 0xC1, ( byte ) 0x1F, ( byte ) 0x38, ( byte ) 0x68, ( byte ) 0x8A, ( byte ) 0xC8, ( byte ) 0x6D,
+            ( byte ) 0x2F };
+    private byte[] desKey1 =
+        { ( byte ) 0xCB, ( byte ) 0xC2, ( byte ) 0x2F, ( byte ) 0xAE, ( byte ) 0x23, ( byte ) 0x52, ( byte ) 0x98,
+            ( byte ) 0xE3 };
+
+    private byte[] fanFold2 =
+        { ( byte ) 0xA0, ( byte ) 0x28, ( byte ) 0x94, ( byte ) 0x4E, ( byte ) 0xE6, ( byte ) 0x3C, ( byte ) 0x04,
+            ( byte ) 0x16 };
+    private byte[] intermediateKey2 =
+        { ( byte ) 0xA1, ( byte ) 0x29, ( byte ) 0x94, ( byte ) 0x4F, ( byte ) 0xE6, ( byte ) 0x3D, ( byte ) 0x04,
+            ( byte ) 0x16 };
+    private byte[] desKey2 =
+        { ( byte ) 0xDF, ( byte ) 0x3D, ( byte ) 0x32, ( byte ) 0xA7, ( byte ) 0x4F, ( byte ) 0xD9, ( byte ) 0x2A,
+            ( byte ) 0x01 };
+
+    private DesStringToKey stringToKey = new DesStringToKey();
+
+
+    /**
+     * Tests DES StringToKey test vector 1 from RFC 3961.
+     */
+    public void testDesStringToKeyVector1()
+    {
+        byte[] key = stringToKey.getKey( "password", "ATHENA.MIT.EDU", "raeburn" );
+
+        assertTrue( "Key match", Arrays.equals( desKey1, key ) );
+    }
+
+
+    /**
+     * Tests DES StringToKey test vector 2 from RFC 3961.
+     */
+    public void testDesStringToKeyVector2()
+    {
+        byte[] key = stringToKey.getKey( "potatoe", "WHITEHOUSE.GOV", "danny" );
+
+        assertTrue( "Key match", Arrays.equals( desKey2, key ) );
+    }
+
+
+    /**
+     * Tests DES StringToKey test vector 1 from RFC 3961 with intermediate step checks.
+     *
+     * @throws InvalidKeyException
+     */
+    public void testIntermediateDesStringToKeyVector1() throws InvalidKeyException
+    {
+        String passPhrase = "passwordATHENA.MIT.EDUraeburn";
+
+        byte[] encodedByteArray = stringToKey.characterEncodeString( passPhrase );
+        byte[] paddedByteArray = stringToKey.padString( encodedByteArray );
+        byte[] fanFold = stringToKey.fanFold( paddedByteArray );
+
+        assertTrue( "Key match", Arrays.equals( fanFold1, fanFold ) );
+
+        fanFold = stringToKey.setParity( fanFold );
+        assertTrue( "Key match", Arrays.equals( intermediateKey1, fanFold ) );
+
+        byte[] secretKey = getDesKey( paddedByteArray, fanFold );
+        assertTrue( "Key match", Arrays.equals( desKey1, secretKey ) );
+    }
+
+
+    /**
+     * Tests DES StringToKey test vector 2 from RFC 3961 with intermediate step checks.
+     * 
+     * @throws InvalidKeyException
+     */
+    public void testIntermediateDesStringToKeyVector2() throws InvalidKeyException
+    {
+        String passPhrase = "potatoeWHITEHOUSE.GOVdanny";
+
+        byte[] encodedByteArray = stringToKey.characterEncodeString( passPhrase );
+        byte[] paddedByteArray = stringToKey.padString( encodedByteArray );
+        byte[] fanFold = stringToKey.fanFold( paddedByteArray );
+
+        assertTrue( "Key match", Arrays.equals( fanFold2, fanFold ) );
+
+        fanFold = stringToKey.setParity( fanFold );
+        assertTrue( "Key match", Arrays.equals( intermediateKey2, fanFold ) );
+
+        byte[] secretKey = getDesKey( paddedByteArray, fanFold );
+        assertTrue( "Key match", Arrays.equals( desKey2, secretKey ) );
+    }
+
+
+    /**
+     * Test harness method for checking intermediate key state, which is not
+     * exposed from {@link DesStringToKey}.
+     *
+     * @param paddedByteArray The input passphrase.
+     * @param intermediateKey The intermediate key generated by fan-folding and parity-adjustment.
+     * @return The final DES key.
+     * @throws InvalidKeyException
+     */
+    private byte[] getDesKey( byte[] paddedByteArray, byte[] intermediateKey ) throws InvalidKeyException
+    {
+        if ( DESKeySpec.isWeak( intermediateKey, 0 ) )
+        {
+            intermediateKey = stringToKey.getStrongKey( intermediateKey );
+        }
+
+        byte[] secretKey = stringToKey.calculateChecksum( paddedByteArray, intermediateKey );
+
+        secretKey = stringToKey.setParity( secretKey );
+
+        if ( DESKeySpec.isWeak( secretKey, 0 ) )
+        {
+            secretKey = stringToKey.getStrongKey( secretKey );
+        }
+
+        return secretKey;
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/crypto/encryption/KerberosKeyFactoryTest.java b/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/crypto/encryption/KerberosKeyFactoryTest.java
new file mode 100644
index 0000000..fe19b42
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/crypto/encryption/KerberosKeyFactoryTest.java
@@ -0,0 +1,394 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.crypto.encryption;
+
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import javax.security.auth.kerberos.KerberosKey;
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import junit.framework.TestCase;
+
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+
+
+/**
+ * Test cases for string-to-key functions for DES-, DES3-, AES-, and RC4-based
+ * encryption types.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class KerberosKeyFactoryTest extends TestCase
+{
+    /**
+     * Tests that key derivation can be performed for a DES key.
+     */
+    public void testDesKerberosKey()
+    {
+        KerberosPrincipal principal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        KerberosKey key = new KerberosKey( principal, "secret".toCharArray(), "DES" );
+
+        assertEquals( "DES key length", 8, key.getEncoded().length );
+    }
+
+
+    /**
+     * Tests that key derivation can be performed for a Triple-DES key.
+     */
+    public void testTripleDesKerberosKey()
+    {
+        KerberosPrincipal principal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        String algorithm = VendorHelper.getTripleDesAlgorithm();
+        KerberosKey key = new KerberosKey( principal, "secret".toCharArray(), algorithm );
+
+        assertEquals( "DESede key length", 24, key.getEncoded().length );
+    }
+
+
+    /**
+     * Tests that key derivation can be performed for an RC4-HMAC key.
+     */
+    public void testArcFourHmacKerberosKey()
+    {
+        if ( !VendorHelper.isArcFourHmacSupported() )
+        {
+            return;
+        }
+
+        KerberosPrincipal principal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        KerberosKey key = new KerberosKey( principal, "secret".toCharArray(), "ArcFourHmac" );
+
+        assertEquals( "ArcFourHmac key length", 16, key.getEncoded().length );
+    }
+
+
+    /**
+     * Tests that key derivation can be performed for an AES-128 key.
+     *
+     * @throws Exception
+     */
+    public void testAes128KerberosKey() throws Exception
+    {
+        KerberosPrincipal principal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        KerberosKey key = new KerberosKey( principal, "secret".toCharArray(), "AES128" );
+
+        assertEquals( "AES128 key length", 16, key.getEncoded().length );
+    }
+
+
+    /**
+     * Tests that key derivation can be performed for an AES-256 key.
+     */
+    public void testAes256KerberosKey()
+    {
+        try
+        {
+            KerberosPrincipal principal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+            KerberosKey kerberosKey = new KerberosKey( principal, "secret".toCharArray(), "AES256" );
+            assertEquals( "AES256 key length", 32, kerberosKey.getEncoded().length );
+        }
+        catch ( IllegalArgumentException iae )
+        {
+            // Algorithm AES256 not enabled
+        }
+    }
+
+
+    /**
+     * Tests that key derivation can be performed by the factory for the des-cbc-md5 encryption type.
+     */
+    public void testKerberosKeyFactoryOnlyDes()
+    {
+        String principalName = "hnelson@EXAMPLE.COM";
+        String passPhrase = "secret";
+
+        Set<EncryptionType> encryptionTypes = new HashSet<EncryptionType>();
+        encryptionTypes.add( EncryptionType.DES_CBC_MD5 );
+
+        Map<EncryptionType, EncryptionKey> map = KerberosKeyFactory.getKerberosKeys( principalName, passPhrase,
+            encryptionTypes );
+
+        assertEquals( "List length", 1, map.values().size() );
+
+        EncryptionKey kerberosKey = map.get( EncryptionType.DES_CBC_MD5 );
+
+        EncryptionType keyType = kerberosKey.getKeyType();
+        int keyLength = kerberosKey.getKeyValue().length;
+        byte[] keyBytes = kerberosKey.getKeyValue();
+
+        assertEquals( keyType, EncryptionType.DES_CBC_MD5 );
+        assertEquals( keyLength, 8 );
+        byte[] expectedBytes = new byte[]
+            { ( byte ) 0xF4, ( byte ) 0xA7, ( byte ) 0x13, ( byte ) 0x64, ( byte ) 0x8A, ( byte ) 0x61, ( byte ) 0xCE,
+                ( byte ) 0x5B };
+        assertTrue( Arrays.equals( expectedBytes, keyBytes ) );
+    }
+
+
+    /**
+     * Tests that key derivation can be performed by the factory for the des3-cbc-sha1-kd encryption type.
+     */
+    public void testKerberosKeyFactoryOnlyTripleDes()
+    {
+        if ( !VendorHelper.isTripleDesSupported() )
+        {
+            return;
+        }
+
+        String principalName = "hnelson@EXAMPLE.COM";
+        String passPhrase = "secret";
+
+        Set<EncryptionType> encryptionTypes = new HashSet<EncryptionType>();
+        encryptionTypes.add( EncryptionType.DES3_CBC_SHA1_KD );
+
+        Map<EncryptionType, EncryptionKey> map = KerberosKeyFactory.getKerberosKeys( principalName, passPhrase,
+            encryptionTypes );
+
+        assertEquals( "List length", 1, map.values().size() );
+
+        EncryptionKey kerberosKey = map.get( EncryptionType.DES3_CBC_SHA1_KD );
+
+        EncryptionType keyType = kerberosKey.getKeyType();
+        int keyLength = kerberosKey.getKeyValue().length;
+        byte[] keyBytes = kerberosKey.getKeyValue();
+
+        assertEquals( keyType, EncryptionType.DES3_CBC_SHA1_KD );
+        assertEquals( keyLength, 24 );
+        byte[] expectedBytes = new byte[]
+            { ( byte ) 0x57, ( byte ) 0x07, ( byte ) 0xCE, ( byte ) 0x29, ( byte ) 0x52, ( byte ) 0x92, ( byte ) 0x2C,
+                ( byte ) 0x1C, ( byte ) 0x8C, ( byte ) 0xBF, ( byte ) 0x43, ( byte ) 0xC2, ( byte ) 0x3D,
+                ( byte ) 0x8F, ( byte ) 0x8C, ( byte ) 0x5E, ( byte ) 0x9E, ( byte ) 0x8C, ( byte ) 0xF7,
+                ( byte ) 0x5D, ( byte ) 0x3E, ( byte ) 0x4A, ( byte ) 0x5E, ( byte ) 0x25 };
+        assertTrue( Arrays.equals( expectedBytes, keyBytes ) );
+    }
+
+
+    /**
+     * Tests that key derivation can be performed by the factory for the rc4-hmac encryption type.
+     */
+    public void testKerberosKeyFactoryOnlyArcFourHmac()
+    {
+        if ( !VendorHelper.isArcFourHmacSupported() )
+        {
+            return;
+        }
+
+        String principalName = "hnelson@EXAMPLE.COM";
+        String passPhrase = "secret";
+
+        Set<EncryptionType> encryptionTypes = new HashSet<EncryptionType>();
+        encryptionTypes.add( EncryptionType.RC4_HMAC );
+
+        Map<EncryptionType, EncryptionKey> map = KerberosKeyFactory.getKerberosKeys( principalName, passPhrase,
+            encryptionTypes );
+
+        assertEquals( "List length", 1, map.values().size() );
+
+        EncryptionKey kerberosKey = map.get( EncryptionType.RC4_HMAC );
+
+        EncryptionType keyType = kerberosKey.getKeyType();
+        int keyLength = kerberosKey.getKeyValue().length;
+        byte[] keyBytes = kerberosKey.getKeyValue();
+
+        assertEquals( keyType, EncryptionType.RC4_HMAC );
+        assertEquals( keyLength, 16 );
+        byte[] expectedBytes = new byte[]
+            { ( byte ) 0x87, ( byte ) 0x8D, ( byte ) 0x80, ( byte ) 0x14, ( byte ) 0x60, ( byte ) 0x6C, ( byte ) 0xDA,
+                ( byte ) 0x29, ( byte ) 0x67, ( byte ) 0x7A, ( byte ) 0x44, ( byte ) 0xEF, ( byte ) 0xA1,
+                ( byte ) 0x35, ( byte ) 0x3F, ( byte ) 0xC7 };
+        assertTrue( Arrays.equals( expectedBytes, keyBytes ) );
+    }
+
+
+    /**
+     * Tests that key derivation can be performed by the factory for the aes128-cts-hmac-sha1-96 encryption type.
+     */
+    public void testKerberosKeyFactoryOnlyAes128()
+    {
+        if ( VendorHelper.isIbm() )
+        {
+            return;
+        }
+
+        String principalName = "hnelson@EXAMPLE.COM";
+        String passPhrase = "secret";
+
+        Set<EncryptionType> encryptionTypes = new HashSet<EncryptionType>();
+        encryptionTypes.add( EncryptionType.AES128_CTS_HMAC_SHA1_96 );
+
+        Map<EncryptionType, EncryptionKey> map = KerberosKeyFactory.getKerberosKeys( principalName, passPhrase,
+            encryptionTypes );
+
+        assertEquals( "List length", 1, map.values().size() );
+
+        EncryptionKey kerberosKey = map.get( EncryptionType.AES128_CTS_HMAC_SHA1_96 );
+
+        EncryptionType keyType = kerberosKey.getKeyType();
+        int keyLength = kerberosKey.getKeyValue().length;
+        byte[] keyBytes = kerberosKey.getKeyValue();
+
+        assertEquals( keyType, EncryptionType.AES128_CTS_HMAC_SHA1_96 );
+        assertEquals( keyLength, 16 );
+        byte[] expectedBytes = new byte[]
+            { ( byte ) 0xAD, ( byte ) 0x21, ( byte ) 0x4B, ( byte ) 0x38, ( byte ) 0xB6, ( byte ) 0x9D, ( byte ) 0xFC,
+                ( byte ) 0xCA, ( byte ) 0xAC, ( byte ) 0xF1, ( byte ) 0x5F, ( byte ) 0x34, ( byte ) 0x6D,
+                ( byte ) 0x41, ( byte ) 0x7B, ( byte ) 0x90 };
+
+        assertTrue( Arrays.equals( expectedBytes, keyBytes ) );
+    }
+
+
+    /**
+     * Tests that key derivation can be performed by the factory for the aes256-cts-hmac-sha1-96 encryption type.
+     */
+    public void testKerberosKeyFactoryOnlyAes256()
+    {
+        if ( VendorHelper.isIbm() )
+        {
+            return;
+        }
+
+        String principalName = "hnelson@EXAMPLE.COM";
+        String passPhrase = "secret";
+
+        Set<EncryptionType> encryptionTypes = new HashSet<EncryptionType>();
+        encryptionTypes.add( EncryptionType.AES256_CTS_HMAC_SHA1_96 );
+
+        Map<EncryptionType, EncryptionKey> map = KerberosKeyFactory.getKerberosKeys( principalName, passPhrase,
+            encryptionTypes );
+
+        EncryptionKey kerberosKey = map.get( EncryptionType.AES256_CTS_HMAC_SHA1_96 );
+
+        if ( kerberosKey != null )
+        {
+            assertEquals( "List length", 1, map.values().size() );
+
+            EncryptionType keyType = kerberosKey.getKeyType();
+            int keyLength = kerberosKey.getKeyValue().length;
+            byte[] keyBytes = kerberosKey.getKeyValue();
+
+            assertEquals( keyType, EncryptionType.AES256_CTS_HMAC_SHA1_96 );
+            assertEquals( keyLength, 32 );
+            byte[] expectedBytes = new byte[]
+                { ( byte ) 0x3D, ( byte ) 0x33, ( byte ) 0x31, ( byte ) 0x8F, ( byte ) 0xBE, ( byte ) 0x47,
+                    ( byte ) 0xE5, ( byte ) 0x2A, ( byte ) 0x21, ( byte ) 0x50, ( byte ) 0x77, ( byte ) 0xA4,
+                    ( byte ) 0x15, ( byte ) 0x58, ( byte ) 0xCA, ( byte ) 0xE7, ( byte ) 0x36, ( byte ) 0x50,
+                    ( byte ) 0x1F, ( byte ) 0xA7, ( byte ) 0xA4, ( byte ) 0x85, ( byte ) 0x82, ( byte ) 0x05,
+                    ( byte ) 0xF6, ( byte ) 0x8F, ( byte ) 0x67, ( byte ) 0xA2, ( byte ) 0xB5, ( byte ) 0xEA,
+                    ( byte ) 0x0E, ( byte ) 0xBF };
+            assertTrue( Arrays.equals( expectedBytes, keyBytes ) );
+        }
+    }
+
+
+    /**
+     * Tests that key derivation can be performed by the factory for multiple encryption types.
+     */
+    public void testKerberosKeyFactory()
+    {
+        if ( VendorHelper.isIbm() )
+        {
+            return;
+        }
+
+        String principalName = "hnelson@EXAMPLE.COM";
+        String passPhrase = "secret";
+
+        Map<EncryptionType, EncryptionKey> map = KerberosKeyFactory.getKerberosKeys( principalName, passPhrase );
+
+        EncryptionKey kerberosKey = map.get( EncryptionType.DES_CBC_MD5 );
+
+        EncryptionType keyType = kerberosKey.getKeyType();
+        int keyLength = kerberosKey.getKeyValue().length;
+        byte[] keyBytes = kerberosKey.getKeyValue();
+
+        assertEquals( keyType, EncryptionType.DES_CBC_MD5 );
+        assertEquals( keyLength, 8 );
+        byte[] expectedBytes = new byte[]
+            { ( byte ) 0xF4, ( byte ) 0xA7, ( byte ) 0x13, ( byte ) 0x64, ( byte ) 0x8A, ( byte ) 0x61, ( byte ) 0xCE,
+                ( byte ) 0x5B };
+        assertTrue( Arrays.equals( expectedBytes, keyBytes ) );
+
+        kerberosKey = map.get( EncryptionType.DES3_CBC_SHA1_KD );
+        keyType = kerberosKey.getKeyType();
+        keyLength = kerberosKey.getKeyValue().length;
+        keyBytes = kerberosKey.getKeyValue();
+
+        assertEquals( keyType, EncryptionType.DES3_CBC_SHA1_KD );
+        assertEquals( keyLength, 24 );
+        expectedBytes = new byte[]
+            { ( byte ) 0x57, ( byte ) 0x07, ( byte ) 0xCE, ( byte ) 0x29, ( byte ) 0x52, ( byte ) 0x92, ( byte ) 0x2C,
+                ( byte ) 0x1C, ( byte ) 0x8C, ( byte ) 0xBF, ( byte ) 0x43, ( byte ) 0xC2, ( byte ) 0x3D,
+                ( byte ) 0x8F, ( byte ) 0x8C, ( byte ) 0x5E, ( byte ) 0x9E, ( byte ) 0x8C, ( byte ) 0xF7,
+                ( byte ) 0x5D, ( byte ) 0x3E, ( byte ) 0x4A, ( byte ) 0x5E, ( byte ) 0x25 };
+        assertTrue( Arrays.equals( expectedBytes, keyBytes ) );
+
+        kerberosKey = map.get( EncryptionType.RC4_HMAC );
+        keyType = kerberosKey.getKeyType();
+        keyLength = kerberosKey.getKeyValue().length;
+        keyBytes = kerberosKey.getKeyValue();
+
+        assertEquals( keyType, EncryptionType.RC4_HMAC );
+        assertEquals( keyLength, 16 );
+        expectedBytes = new byte[]
+            { ( byte ) 0x87, ( byte ) 0x8D, ( byte ) 0x80, ( byte ) 0x14, ( byte ) 0x60, ( byte ) 0x6C, ( byte ) 0xDA,
+                ( byte ) 0x29, ( byte ) 0x67, ( byte ) 0x7A, ( byte ) 0x44, ( byte ) 0xEF, ( byte ) 0xA1,
+                ( byte ) 0x35, ( byte ) 0x3F, ( byte ) 0xC7 };
+        assertTrue( Arrays.equals( expectedBytes, keyBytes ) );
+
+        kerberosKey = map.get( EncryptionType.AES128_CTS_HMAC_SHA1_96 );
+        keyType = kerberosKey.getKeyType();
+        keyLength = kerberosKey.getKeyValue().length;
+        keyBytes = kerberosKey.getKeyValue();
+
+        assertEquals( keyType, EncryptionType.AES128_CTS_HMAC_SHA1_96 );
+        assertEquals( keyLength, 16 );
+        expectedBytes = new byte[]
+            { ( byte ) 0xAD, ( byte ) 0x21, ( byte ) 0x4B, ( byte ) 0x38, ( byte ) 0xB6, ( byte ) 0x9D, ( byte ) 0xFC,
+                ( byte ) 0xCA, ( byte ) 0xAC, ( byte ) 0xF1, ( byte ) 0x5F, ( byte ) 0x34, ( byte ) 0x6D,
+                ( byte ) 0x41, ( byte ) 0x7B, ( byte ) 0x90 };
+        assertTrue( Arrays.equals( expectedBytes, keyBytes ) );
+
+        kerberosKey = map.get( EncryptionType.AES256_CTS_HMAC_SHA1_96 );
+
+        if ( kerberosKey != null )
+        {
+            keyType = kerberosKey.getKeyType();
+            keyLength = kerberosKey.getKeyValue().length;
+            keyBytes = kerberosKey.getKeyValue();
+
+            assertEquals( keyType, EncryptionType.AES256_CTS_HMAC_SHA1_96 );
+            assertEquals( keyLength, 32 );
+            expectedBytes = new byte[]
+                { ( byte ) 0x3D, ( byte ) 0x33, ( byte ) 0x31, ( byte ) 0x8F, ( byte ) 0xBE, ( byte ) 0x47,
+                    ( byte ) 0xE5, ( byte ) 0x2A, ( byte ) 0x21, ( byte ) 0x50, ( byte ) 0x77, ( byte ) 0xA4,
+                    ( byte ) 0x15, ( byte ) 0x58, ( byte ) 0xCA, ( byte ) 0xE7, ( byte ) 0x36, ( byte ) 0x50,
+                    ( byte ) 0x1F, ( byte ) 0xA7, ( byte ) 0xA4, ( byte ) 0x85, ( byte ) 0x82, ( byte ) 0x05,
+                    ( byte ) 0xF6, ( byte ) 0x8F, ( byte ) 0x67, ( byte ) 0xA2, ( byte ) 0xB5, ( byte ) 0xEA,
+                    ( byte ) 0x0E, ( byte ) 0xBF };
+            assertTrue( Arrays.equals( expectedBytes, keyBytes ) );
+        }
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/crypto/encryption/KeyTypeTest.java b/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/crypto/encryption/KeyTypeTest.java
new file mode 100644
index 0000000..c6e8570
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/crypto/encryption/KeyTypeTest.java
@@ -0,0 +1,301 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.crypto.encryption;
+
+
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.Provider;
+import java.security.Security;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import javax.crypto.Cipher;
+import javax.crypto.KeyGenerator;
+import javax.crypto.Mac;
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.DESKeySpec;
+
+import junit.framework.TestCase;
+
+
+/**
+ * Test cases for the encryption types used by Kerberos "5.2" per RFC 4120,
+ * "The Kerberos Network Authentication Service (V5)."
+ * 
+ * We MUST support:
+ * Encryption: AES256-CTS-HMAC-SHA1-96 [RFC3962]
+ * Checksums: HMAC-SHA1-96-AES256 [RFC3962]
+ * 
+ * We SHOULD support:
+ * Encryption: AES128-CTS-HMAC-SHA1-96, DES-CBC-MD5, DES3-CBC-SHA1-KD
+ * Checksums: DES-MD5, HMAC-SHA1-DES3-KD, HMAC-SHA1-96-AES128
+ * 
+ * Also important for interoperability is:
+ * ArcFour with HMAC/md5, DES-CBC-CRC
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class KeyTypeTest extends TestCase
+{
+    /**
+     * Tests that the cipher types used by Kerberos exist, namely
+     * DES, DESede, RC4, and AES.
+     */
+    public void testKeyTypes()
+    {
+        String[] names = getCryptoImpls( "Cipher" );
+        List ciphers = Arrays.asList( names );
+
+        assertTrue( ciphers.contains( "DES" ) );
+        assertTrue( ciphers.contains( "DESede" ) );
+        assertTrue( ciphers.contains( "TripleDES" ) );
+        assertTrue( ciphers.contains( "ARCFOUR" ) );
+        assertTrue( ciphers.contains( "RC4" ) );
+        assertTrue( ciphers.contains( "AES" ) );
+    }
+
+
+    /**
+     * Tests that the message digest types used by Kerberos exist, namely
+     * SHA1 and MD5.
+     */
+    public void testMessageDigestTypes()
+    {
+        String[] names = getCryptoImpls( "MessageDigest" );
+        List ciphers = Arrays.asList( names );
+
+        assertTrue( ciphers.contains( "MD5" ) );
+        assertTrue( ciphers.contains( "SHA1" ) );
+    }
+
+
+    /**
+     * Tests that the MAC types used by Kerberos exist, namely
+     * HmacMD5 and HmacSHA1.
+     */
+    public void testMacTypes()
+    {
+        String[] names = getCryptoImpls( "Mac" );
+        List ciphers = Arrays.asList( names );
+
+        assertTrue( ciphers.contains( "HmacMD5" ) );
+        assertTrue( ciphers.contains( "HmacSHA1" ) );
+    }
+
+
+    /**
+     * Tests that DES keys can be generated from bytes.
+     *
+     * @throws Exception
+     */
+    public void generateDes() throws Exception
+    {
+        byte[] desKeyData =
+            { ( byte ) 0x01, ( byte ) 0x02, ( byte ) 0x03, ( byte ) 0x04, ( byte ) 0x05, ( byte ) 0x06, ( byte ) 0x07,
+                ( byte ) 0x08 };
+        DESKeySpec desKeySpec = new DESKeySpec( desKeyData );
+        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance( "DES" );
+        SecretKey desKey = keyFactory.generateSecret( desKeySpec );
+        assertEquals( "DES key size", 8, desKey.getEncoded().length );
+        assertTrue( DESKeySpec.isParityAdjusted( desKey.getEncoded(), 0 ) );
+    }
+
+
+    /**
+     * Tests that a CBC-mode DES cipher can be initialized.
+     *
+     * @throws Exception
+     */
+    public void testDesCipher() throws Exception
+    {
+        KeyGenerator keygen = KeyGenerator.getInstance( "DES" );
+        SecretKey desKey = keygen.generateKey();
+
+        Cipher ecipher = Cipher.getInstance( "DES/CBC/NoPadding" );
+        ecipher.init( Cipher.ENCRYPT_MODE, desKey );
+        assertEquals( "Block size", 8, ecipher.getBlockSize() );
+    }
+
+
+    /**
+     * Tests that a CBC-mode Triple-DES cipher can be initialized.
+     *
+     * @throws Exception
+     */
+    public void testTripleDesCipher() throws Exception
+    {
+        KeyGenerator keygen = KeyGenerator.getInstance( "DESede" );
+        SecretKey desKey = keygen.generateKey();
+
+        Cipher ecipher = Cipher.getInstance( "DESede/CBC/NoPadding" );
+        ecipher.init( Cipher.ENCRYPT_MODE, desKey );
+        assertEquals( "Block size", 8, ecipher.getBlockSize() );
+    }
+
+
+    /**
+     * Tests that a CBC-mode Triple-DES cipher can be initialized.
+     *
+     * @throws Exception
+     */
+    public void testArcFourCipher() throws Exception
+    {
+        KeyGenerator keygen = KeyGenerator.getInstance( "ARCFOUR" );
+        SecretKey desKey = keygen.generateKey();
+
+        Cipher ecipher = Cipher.getInstance( "ARCFOUR" );
+        ecipher.init( Cipher.ENCRYPT_MODE, desKey );
+        assertEquals( "Block size", 0, ecipher.getBlockSize() );
+    }
+
+
+    /**
+     * Tests that a CTS-mode AES cipher can be initialized
+     * with an AES-128 key.
+     *
+     * @throws Exception
+     */
+    public void testAes128Cipher() throws Exception
+    {
+        KeyGenerator keyGenerator = KeyGenerator.getInstance( "AES" );
+        keyGenerator.init( 128 );
+
+        SecretKey key = keyGenerator.generateKey();
+
+        try
+        {
+            Cipher ecipher = Cipher.getInstance( "AES/CTS/NoPadding" );
+            ecipher.init( Cipher.ENCRYPT_MODE, key );
+            assertEquals( "Block size", 16, ecipher.getBlockSize() );
+        }
+        catch ( NoSuchAlgorithmException nsae )
+        {
+            // Without CTS mode this will throw an Exception.
+        }
+    }
+
+
+    /**
+     * Tests that a CTS-mode AES cipher can be initialized
+     * with an AES-256 key.
+     *
+     * @throws Exception
+     */
+    public void testAes256Cipher() throws Exception
+    {
+        KeyGenerator keyGenerator = KeyGenerator.getInstance( "AES" );
+        keyGenerator.init( 256 );
+
+        SecretKey key = keyGenerator.generateKey();
+
+        try
+        {
+            Cipher ecipher = Cipher.getInstance( "AES/CTS/NoPadding" );
+            ecipher.init( Cipher.ENCRYPT_MODE, key );
+            assertEquals( "Block size", 16, ecipher.getBlockSize() );
+        }
+        catch ( InvalidKeyException ike )
+        {
+            // Without unlimited-strength crypto this will throw an exception.
+        }
+        catch ( NoSuchAlgorithmException nsae )
+        {
+            // Without CTS mode this will throw an Exception.
+        }
+    }
+
+
+    /**
+     * Tests the generation of an HMAC-MD5 MAC.
+     * 
+     * @throws Exception
+     */
+    public void testGenerateHmacMd5() throws Exception
+    {
+        KeyGenerator kg = KeyGenerator.getInstance( "HmacMD5" );
+        SecretKey sk = kg.generateKey();
+
+        Mac mac = Mac.getInstance( "HmacMD5" );
+        mac.init( sk );
+        byte[] result = mac.doFinal( "Hello world!".getBytes() );
+
+        assertEquals( "HmacMD5 size", 16, result.length );
+    }
+
+
+    /**
+     * Tests the generation of an HMAC-SHA1 MAC.
+     * 
+     * @throws Exception
+     */
+    public void testGenerateHmacSha1() throws Exception
+    {
+        KeyGenerator kg = KeyGenerator.getInstance( "HmacSHA1" );
+        SecretKey sk = kg.generateKey();
+
+        Mac mac = Mac.getInstance( "HmacSHA1" );
+        mac.init( sk );
+        byte[] result = mac.doFinal( "Hi There".getBytes() );
+
+        assertEquals( "HmacSHA1 size", 20, result.length );
+    }
+
+
+    /**
+     * This method returns the available implementations for a service type.
+     * 
+     * @param serviceType The type of the service.
+     * @return Array of the service types as Strings.
+     */
+    private static String[] getCryptoImpls( String serviceType )
+    {
+        Set<String> result = new HashSet<String>();
+
+        Provider[] providers = Security.getProviders();
+        for ( int i = 0; i < providers.length; i++ )
+        {
+            // Get services provided by each provider
+            Set keys = providers[i].keySet();
+            for ( Iterator it = keys.iterator(); it.hasNext(); )
+            {
+                String key = ( String ) it.next();
+                key = key.split( " " )[0];
+
+                if ( key.startsWith( serviceType + "." ) )
+                {
+                    result.add( key.substring( serviceType.length() + 1 ) );
+                }
+                else if ( key.startsWith( "Alg.Alias." + serviceType + "." ) )
+                {
+                    // This is an alias
+                    result.add( key.substring( serviceType.length() + 11 ) );
+                }
+            }
+        }
+
+        return result.toArray( new String[result.size()] );
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/crypto/encryption/NFoldTest.java b/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/crypto/encryption/NFoldTest.java
new file mode 100644
index 0000000..aae9e88
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/crypto/encryption/NFoldTest.java
@@ -0,0 +1,306 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.crypto.encryption;
+
+
+import java.util.Arrays;
+
+import junit.framework.TestCase;
+
+
+/**
+ * Tests the use of "n-folding" using test vectors from RFC 3961,
+ * "Encryption and Checksum Specifications for Kerberos 5."
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class NFoldTest extends TestCase
+{
+    /**
+     * Tests an n-fold test vector from RFC 3961.
+     */
+    public void testNFold1()
+    {
+        int n = 64;
+        String passPhrase = "012345";
+
+        int k = passPhrase.getBytes().length * 8;
+        int lcm = NFold.getLcm( n, k );
+        assertEquals( "LCM", 192, lcm );
+
+        byte[] nFoldValue = NFold.nFold( n, passPhrase.getBytes() );
+
+        byte[] testVector =
+            { ( byte ) 0xbe, ( byte ) 0x07, ( byte ) 0x26, ( byte ) 0x31, ( byte ) 0x27, ( byte ) 0x6b, ( byte ) 0x19,
+                ( byte ) 0x55 };
+        assertTrue( Arrays.equals( nFoldValue, testVector ) );
+    }
+
+
+    /**
+     * Tests an n-fold test vector from RFC 3961.
+     */
+    public void testNFold2()
+    {
+        int n = 56;
+        String passPhrase = "password";
+
+        int k = passPhrase.getBytes().length * 8;
+        int lcm = NFold.getLcm( n, k );
+        assertEquals( "LCM", 448, lcm );
+
+        byte[] nFoldValue = NFold.nFold( n, passPhrase.getBytes() );
+
+        byte[] testVector =
+            { ( byte ) 0x78, ( byte ) 0xa0, ( byte ) 0x7b, ( byte ) 0x6c, ( byte ) 0xaf, ( byte ) 0x85, ( byte ) 0xfa };
+        assertTrue( Arrays.equals( nFoldValue, testVector ) );
+    }
+
+
+    /**
+     * Tests an n-fold test vector from RFC 3961.
+     */
+    public void testNFold3()
+    {
+        int n = 64;
+        String passPhrase = "Rough Consensus, and Running Code";
+
+        int k = passPhrase.getBytes().length * 8;
+        int lcm = NFold.getLcm( n, k );
+        assertEquals( "LCM", 2112, lcm );
+
+        byte[] nFoldValue = NFold.nFold( n, passPhrase.getBytes() );
+
+        byte[] testVector =
+            { ( byte ) 0xbb, ( byte ) 0x6e, ( byte ) 0xd3, ( byte ) 0x08, ( byte ) 0x70, ( byte ) 0xb7, ( byte ) 0xf0,
+                ( byte ) 0xe0 };
+        assertTrue( Arrays.equals( nFoldValue, testVector ) );
+    }
+
+
+    /**
+     * Tests an n-fold test vector from RFC 3961.
+     */
+    public void testNFold4()
+    {
+        int n = 168;
+        String passPhrase = "password";
+
+        int k = passPhrase.getBytes().length * 8;
+        int lcm = NFold.getLcm( n, k );
+        assertEquals( "LCM", 1344, lcm );
+
+        byte[] nFoldValue = NFold.nFold( n, passPhrase.getBytes() );
+
+        byte[] testVector =
+            { ( byte ) 0x59, ( byte ) 0xe4, ( byte ) 0xa8, ( byte ) 0xca, ( byte ) 0x7c, ( byte ) 0x03, ( byte ) 0x85,
+                ( byte ) 0xc3, ( byte ) 0xc3, ( byte ) 0x7b, ( byte ) 0x3f, ( byte ) 0x6d, ( byte ) 0x20,
+                ( byte ) 0x00, ( byte ) 0x24, ( byte ) 0x7c, ( byte ) 0xb6, ( byte ) 0xe6, ( byte ) 0xbd,
+                ( byte ) 0x5b, ( byte ) 0x3e };
+        assertTrue( Arrays.equals( nFoldValue, testVector ) );
+    }
+
+
+    /**
+     * Tests an n-fold test vector from RFC 3961.
+     */
+    public void testNFold5()
+    {
+        int n = 192;
+        String passPhrase = "MASSACHVSETTS INSTITVTE OF TECHNOLOGY";
+
+        int k = passPhrase.getBytes().length * 8;
+        int lcm = NFold.getLcm( n, k );
+        assertEquals( "LCM", 7104, lcm );
+
+        byte[] nFoldValue = NFold.nFold( n, passPhrase.getBytes() );
+
+        byte[] testVector =
+            { ( byte ) 0xdb, ( byte ) 0x3b, ( byte ) 0x0d, ( byte ) 0x8f, ( byte ) 0x0b, ( byte ) 0x06, ( byte ) 0x1e,
+                ( byte ) 0x60, ( byte ) 0x32, ( byte ) 0x82, ( byte ) 0xb3, ( byte ) 0x08, ( byte ) 0xa5,
+                ( byte ) 0x08, ( byte ) 0x41, ( byte ) 0x22, ( byte ) 0x9a, ( byte ) 0xd7, ( byte ) 0x98,
+                ( byte ) 0xfa, ( byte ) 0xb9, ( byte ) 0x54, ( byte ) 0x0c, ( byte ) 0x1b };
+        assertTrue( Arrays.equals( nFoldValue, testVector ) );
+
+    }
+
+
+    /**
+     * Tests an n-fold test vector from RFC 3961.
+     */
+    public void testNFold6()
+    {
+        int n = 168;
+        String passPhrase = "Q";
+
+        int k = passPhrase.getBytes().length * 8;
+        int lcm = NFold.getLcm( n, k );
+        assertEquals( "LCM", 168, lcm );
+
+        byte[] nFoldValue = NFold.nFold( n, passPhrase.getBytes() );
+
+        byte[] testVector =
+            { ( byte ) 0x51, ( byte ) 0x8a, ( byte ) 0x54, ( byte ) 0xa2, ( byte ) 0x15, ( byte ) 0xa8, ( byte ) 0x45,
+                ( byte ) 0x2a, ( byte ) 0x51, ( byte ) 0x8a, ( byte ) 0x54, ( byte ) 0xa2, ( byte ) 0x15,
+                ( byte ) 0xa8, ( byte ) 0x45, ( byte ) 0x2a, ( byte ) 0x51, ( byte ) 0x8a, ( byte ) 0x54,
+                ( byte ) 0xa2, ( byte ) 0x15 };
+        assertTrue( Arrays.equals( nFoldValue, testVector ) );
+    }
+
+
+    /**
+     * Tests an n-fold test vector from RFC 3961.
+     */
+    public void testNFold7()
+    {
+        int n = 168;
+        String passPhrase = "ba";
+
+        int k = passPhrase.getBytes().length * 8;
+        int lcm = NFold.getLcm( n, k );
+        assertEquals( "LCM", 336, lcm );
+
+        byte[] nFoldValue = NFold.nFold( n, passPhrase.getBytes() );
+
+        byte[] testVector =
+            { ( byte ) 0xfb, ( byte ) 0x25, ( byte ) 0xd5, ( byte ) 0x31, ( byte ) 0xae, ( byte ) 0x89, ( byte ) 0x74,
+                ( byte ) 0x49, ( byte ) 0x9f, ( byte ) 0x52, ( byte ) 0xfd, ( byte ) 0x92, ( byte ) 0xea,
+                ( byte ) 0x98, ( byte ) 0x57, ( byte ) 0xc4, ( byte ) 0xba, ( byte ) 0x24, ( byte ) 0xcf,
+                ( byte ) 0x29, ( byte ) 0x7e };
+        assertTrue( Arrays.equals( nFoldValue, testVector ) );
+    }
+
+
+    /**
+     * Tests an n-fold test vector from RFC 3961.
+     */
+    public void testNFoldKerberos64()
+    {
+        int n = 64;
+        String passPhrase = "kerberos";
+
+        int k = passPhrase.getBytes().length * 8;
+        int lcm = NFold.getLcm( n, k );
+        assertEquals( "LCM", 64, lcm );
+
+        byte[] nFoldValue = NFold.nFold( n, passPhrase.getBytes() );
+
+        byte[] testVector =
+            { ( byte ) 0x6b, ( byte ) 0x65, ( byte ) 0x72, ( byte ) 0x62, ( byte ) 0x65, ( byte ) 0x72, ( byte ) 0x6f,
+                ( byte ) 0x73 };
+        assertTrue( Arrays.equals( nFoldValue, testVector ) );
+    }
+
+
+    /**
+     * Tests an n-fold test vector from RFC 3961.
+     */
+    public void testNFoldKerberos128()
+    {
+        int n = 128;
+        String passPhrase = "kerberos";
+
+        int k = passPhrase.getBytes().length * 8;
+        int lcm = NFold.getLcm( n, k );
+        assertEquals( "LCM", 128, lcm );
+
+        byte[] nFoldValue = NFold.nFold( n, passPhrase.getBytes() );
+
+        byte[] testVector =
+            { ( byte ) 0x6b, ( byte ) 0x65, ( byte ) 0x72, ( byte ) 0x62, ( byte ) 0x65, ( byte ) 0x72, ( byte ) 0x6f,
+                ( byte ) 0x73, ( byte ) 0x7b, ( byte ) 0x9b, ( byte ) 0x5b, ( byte ) 0x2b, ( byte ) 0x93,
+                ( byte ) 0x13, ( byte ) 0x2b, ( byte ) 0x93 };
+        assertTrue( Arrays.equals( nFoldValue, testVector ) );
+    }
+
+
+    /**
+     * Tests an n-fold test vector from RFC 3961.
+     */
+    public void testNFoldKerberos168()
+    {
+        int n = 168;
+        String passPhrase = "kerberos";
+
+        int k = passPhrase.getBytes().length * 8;
+        int lcm = NFold.getLcm( n, k );
+        assertEquals( "LCM", 1344, lcm );
+
+        byte[] nFoldValue = NFold.nFold( n, passPhrase.getBytes() );
+
+        byte[] testVector =
+            { ( byte ) 0x83, ( byte ) 0x72, ( byte ) 0xc2, ( byte ) 0x36, ( byte ) 0x34, ( byte ) 0x4e, ( byte ) 0x5f,
+                ( byte ) 0x15, ( byte ) 0x50, ( byte ) 0xcd, ( byte ) 0x07, ( byte ) 0x47, ( byte ) 0xe1,
+                ( byte ) 0x5d, ( byte ) 0x62, ( byte ) 0xca, ( byte ) 0x7a, ( byte ) 0x5a, ( byte ) 0x3b,
+                ( byte ) 0xce, ( byte ) 0xa4 };
+        assertTrue( Arrays.equals( nFoldValue, testVector ) );
+    }
+
+
+    /**
+     * Tests an n-fold test vector from RFC 3961.
+     */
+    public void testNFoldKerberos256()
+    {
+        int n = 256;
+        String passPhrase = "kerberos";
+
+        int k = passPhrase.getBytes().length * 8;
+        int lcm = NFold.getLcm( n, k );
+        assertEquals( "LCM", 256, lcm );
+
+        byte[] nFoldValue = NFold.nFold( n, passPhrase.getBytes() );
+
+        byte[] testVector =
+            { ( byte ) 0x6b, ( byte ) 0x65, ( byte ) 0x72, ( byte ) 0x62, ( byte ) 0x65, ( byte ) 0x72, ( byte ) 0x6f,
+                ( byte ) 0x73, ( byte ) 0x7b, ( byte ) 0x9b, ( byte ) 0x5b, ( byte ) 0x2b, ( byte ) 0x93,
+                ( byte ) 0x13, ( byte ) 0x2b, ( byte ) 0x93, ( byte ) 0x5c, ( byte ) 0x9b, ( byte ) 0xdc,
+                ( byte ) 0xda, ( byte ) 0xd9, ( byte ) 0x5c, ( byte ) 0x98, ( byte ) 0x99, ( byte ) 0xc4,
+                ( byte ) 0xca, ( byte ) 0xe4, ( byte ) 0xde, ( byte ) 0xe6, ( byte ) 0xd6, ( byte ) 0xca, ( byte ) 0xe4 };
+        assertTrue( Arrays.equals( nFoldValue, testVector ) );
+    }
+
+
+    /**
+     * Test one's complement addition (addition with end-around carry).  Note
+     * that for purposes of n-folding, we do not actually complement the
+     * result of the addition.
+     */
+    public void testSum()
+    {
+        byte[] n1 =
+            { ( byte ) 0x86, ( byte ) 0x5E };
+        byte[] n2 =
+            { ( byte ) 0xAC, ( byte ) 0x60 };
+        byte[] n3 =
+            { ( byte ) 0x71, ( byte ) 0x2A };
+        byte[] n4 =
+            { ( byte ) 0x81, ( byte ) 0xB5 };
+
+        byte[] sum = NFold.sum( n1, n2, n1.length * 8 );
+        sum = NFold.sum( sum, n3, sum.length * 8 );
+        sum = NFold.sum( sum, n4, sum.length * 8 );
+
+        byte[] result = new byte[]
+            { ( byte ) 0x25, ( byte ) 0x9F };
+        assertTrue( Arrays.equals( sum, result ) );
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/crypto/encryption/RandomKeyFactoryTest.java b/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/crypto/encryption/RandomKeyFactoryTest.java
new file mode 100644
index 0000000..dee2742
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/crypto/encryption/RandomKeyFactoryTest.java
@@ -0,0 +1,208 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.crypto.encryption;
+
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.DESKeySpec;
+
+import junit.framework.TestCase;
+
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+
+
+/**
+ * Test cases for random-to-key functions for DES-, DES3-, AES-, and RC4-based
+ * encryption types.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class RandomKeyFactoryTest extends TestCase
+{
+    /**
+     * Tests that random DES keys can be generated.
+     *
+     * @throws Exception
+     */
+    public void testGenerateDesKey() throws Exception
+    {
+        KeyGenerator keygen = KeyGenerator.getInstance( "DES" );
+        SecretKey key = keygen.generateKey();
+        assertEquals( "DES key size", 8, key.getEncoded().length );
+        assertTrue( DESKeySpec.isParityAdjusted( key.getEncoded(), 0 ) );
+    }
+
+
+    /**
+     * Tests that random Triple-DES keys can be generated.
+     *
+     * @throws Exception
+     */
+    public void testGenerateTripleDesKey() throws Exception
+    {
+        KeyGenerator keygen = KeyGenerator.getInstance( "DESede" );
+        SecretKey key = keygen.generateKey();
+        assertEquals( "DESede key size", 24, key.getEncoded().length );
+    }
+
+
+    /**
+     * Tests that random AES128 keys can be generated.
+     *
+     * @throws Exception
+     */
+    public void testGenerateAes128Key() throws Exception
+    {
+        KeyGenerator keygen = KeyGenerator.getInstance( "AES" );
+        keygen.init( 128 );
+        SecretKey key = keygen.generateKey();
+        assertEquals( "AES key size", 16, key.getEncoded().length );
+    }
+
+
+    /**
+     * Tests that random AES256 keys can be generated.
+     *
+     * @throws Exception
+     */
+    public void testGenerateAes256Key() throws Exception
+    {
+        KeyGenerator keygen = KeyGenerator.getInstance( "AES" );
+        keygen.init( 256 );
+        SecretKey key = keygen.generateKey();
+        assertEquals( "AES key size", 32, key.getEncoded().length );
+    }
+
+
+    /**
+     * Tests that random ARCFOUR keys can be generated.
+     *
+     * @throws Exception
+     */
+    public void testGenerateArcFourKey() throws Exception
+    {
+        if ( !VendorHelper.isArcFourHmacSupported() )
+        {
+            return;
+        }
+
+        KeyGenerator keygen = KeyGenerator.getInstance( "ARCFOUR" );
+        SecretKey key = keygen.generateKey();
+        assertEquals( "ARCFOUR key size", 16, key.getEncoded().length );
+    }
+
+
+    /**
+     * Tests that random RC4 keys can be generated.
+     *
+     * @throws Exception
+     */
+    public void testGenerateRc4Key() throws Exception
+    {
+        if ( !VendorHelper.isArcFourHmacSupported() )
+        {
+            return;
+        }
+
+        KeyGenerator keygen = KeyGenerator.getInstance( "RC4" );
+        SecretKey key = keygen.generateKey();
+        assertEquals( "RC4 key size", 16, key.getEncoded().length );
+    }
+
+
+    /**
+     * Tests that random key generation can be performed by the factory for multiple cipher types.
+     * 
+     * @throws Exception
+     */
+    public void testRandomKeyFactory() throws Exception
+    {
+        Map<EncryptionType, EncryptionKey> map = RandomKeyFactory.getRandomKeys();
+
+        EncryptionKey kerberosKey = map.get( EncryptionType.DES_CBC_MD5 );
+
+        EncryptionType keyType = kerberosKey.getKeyType();
+        int keyLength = kerberosKey.getKeyValue().length;
+
+        assertEquals( keyType, EncryptionType.DES_CBC_MD5 );
+        assertEquals( keyLength, 8 );
+
+        kerberosKey = map.get( EncryptionType.DES3_CBC_SHA1_KD );
+        keyType = kerberosKey.getKeyType();
+        keyLength = kerberosKey.getKeyValue().length;
+
+        assertEquals( keyType, EncryptionType.DES3_CBC_SHA1_KD );
+        assertEquals( keyLength, 24 );
+
+        kerberosKey = map.get( EncryptionType.RC4_HMAC );
+        keyType = kerberosKey.getKeyType();
+        keyLength = kerberosKey.getKeyValue().length;
+
+        if ( VendorHelper.isArcFourHmacSupported() )
+        {
+            assertEquals( keyType, EncryptionType.RC4_HMAC );
+            assertEquals( keyLength, 16 );
+        }
+
+        kerberosKey = map.get( EncryptionType.AES128_CTS_HMAC_SHA1_96 );
+        keyType = kerberosKey.getKeyType();
+        keyLength = kerberosKey.getKeyValue().length;
+
+        assertEquals( keyType, EncryptionType.AES128_CTS_HMAC_SHA1_96 );
+        assertEquals( keyLength, 16 );
+
+        kerberosKey = map.get( EncryptionType.AES256_CTS_HMAC_SHA1_96 );
+        keyType = kerberosKey.getKeyType();
+        keyLength = kerberosKey.getKeyValue().length;
+
+        assertEquals( keyType, EncryptionType.AES256_CTS_HMAC_SHA1_96 );
+        assertEquals( keyLength, 32 );
+    }
+
+
+    /**
+     * Tests that random key generation can be performed by the factory for a specified cipher type.
+     * 
+     * @throws Exception
+     */
+    public void testRandomKeyFactoryOnlyDes() throws Exception
+    {
+        Set<EncryptionType> encryptionTypes = new HashSet<EncryptionType>();
+        encryptionTypes.add( EncryptionType.DES_CBC_MD5 );
+
+        Map<EncryptionType, EncryptionKey> map = RandomKeyFactory.getRandomKeys( encryptionTypes );
+
+        assertEquals( "List length", 1, map.values().size() );
+
+        EncryptionKey kerberosKey = map.get( EncryptionType.DES_CBC_MD5 );
+
+        EncryptionType keyType = kerberosKey.getKeyType();
+        int keyLength = kerberosKey.getKeyValue().length;
+
+        assertEquals( keyType, EncryptionType.DES_CBC_MD5 );
+        assertEquals( keyLength, 8 );
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/crypto/encryption/VendorHelper.java b/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/crypto/encryption/VendorHelper.java
new file mode 100644
index 0000000..b68d89c
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/crypto/encryption/VendorHelper.java
@@ -0,0 +1,70 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.crypto.encryption;
+
+
+/**
+ * Helper for determining whether various ciphers are supported by the JRE.  For now
+ * determinations are based solely on JRE vendor.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class VendorHelper
+{
+    private static final String vendor = System.getProperty( "java.vendor" ).toLowerCase();
+
+
+    static String getTripleDesAlgorithm()
+    {
+        if ( isIbm() )
+        {
+            return "3DES";
+        }
+        else
+        {
+            return "DESede";
+        }
+    }
+
+
+    static boolean isCtsSupported()
+    {
+        return vendor.contains( "sun" );
+    }
+
+
+    static boolean isArcFourHmacSupported()
+    {
+        return vendor.contains( "sun" );
+    }
+
+
+    static boolean isTripleDesSupported()
+    {
+        return vendor.contains( "sun" );
+    }
+
+
+    static boolean isIbm()
+    {
+        return vendor.contains( "ibm" );
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/keytab/KeytabTest.java b/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/keytab/KeytabTest.java
new file mode 100644
index 0000000..d1c369d
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/keytab/KeytabTest.java
@@ -0,0 +1,180 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.keytab;
+
+
+import java.security.InvalidKeyException;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.TimeZone;
+
+import javax.crypto.spec.DESKeySpec;
+
+import junit.framework.TestCase;
+
+import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.KerberosKeyFactory;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
+import org.apache.mina.common.ByteBuffer;
+
+
+/**
+ * Tests 'keytab' formatted files.
+ * 
+ * All values are in network byte order.  All text is ASCII.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class KeytabTest extends TestCase
+{
+    private static final byte[] keytab1 = new byte[]
+        { ( byte ) 0x05, ( byte ) 0x02, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x3C, ( byte ) 0x00,
+            ( byte ) 0x02, ( byte ) 0x00, ( byte ) 0x0B, ( byte ) 0x45, ( byte ) 0x58, ( byte ) 0x41, ( byte ) 0x4D,
+            ( byte ) 0x50, ( byte ) 0x4C, ( byte ) 0x45, ( byte ) 0x2E, ( byte ) 0x43, ( byte ) 0x4F, ( byte ) 0x4D,
+            ( byte ) 0x00, ( byte ) 0x04, ( byte ) 0x6C, ( byte ) 0x64, ( byte ) 0x61, ( byte ) 0x70, ( byte ) 0x00,
+            ( byte ) 0x10, ( byte ) 0x77, ( byte ) 0x77, ( byte ) 0x77, ( byte ) 0x2E, ( byte ) 0x76, ( byte ) 0x65,
+            ( byte ) 0x72, ( byte ) 0x69, ( byte ) 0x73, ( byte ) 0x69, ( byte ) 0x67, ( byte ) 0x6E, ( byte ) 0x2E,
+            ( byte ) 0x63, ( byte ) 0x6F, ( byte ) 0x6D, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x01,
+            ( byte ) 0x45, ( byte ) 0xD9, ( byte ) 0x60, ( byte ) 0xBE, ( byte ) 0x01, ( byte ) 0x00, ( byte ) 0x03,
+            ( byte ) 0x00, ( byte ) 0x08, ( byte ) 0xD5, ( byte ) 0xE6, ( byte ) 0xC4, ( byte ) 0xD0, ( byte ) 0xFE,
+            ( byte ) 0x25, ( byte ) 0x07, ( byte ) 0x0D };
+
+    private static final byte[] keytab2 = new byte[]
+        { ( byte ) 0x05, ( byte ) 0x02, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x3C, ( byte ) 0x00,
+            ( byte ) 0x02, ( byte ) 0x00, ( byte ) 0x0B, ( byte ) 0x45, ( byte ) 0x58, ( byte ) 0x41, ( byte ) 0x4D,
+            ( byte ) 0x50, ( byte ) 0x4C, ( byte ) 0x45, ( byte ) 0x2E, ( byte ) 0x43, ( byte ) 0x4F, ( byte ) 0x4D,
+            ( byte ) 0x00, ( byte ) 0x04, ( byte ) 0x48, ( byte ) 0x54, ( byte ) 0x54, ( byte ) 0x50, ( byte ) 0x00,
+            ( byte ) 0x10, ( byte ) 0x77, ( byte ) 0x77, ( byte ) 0x77, ( byte ) 0x2E, ( byte ) 0x76, ( byte ) 0x65,
+            ( byte ) 0x72, ( byte ) 0x69, ( byte ) 0x73, ( byte ) 0x69, ( byte ) 0x67, ( byte ) 0x6E, ( byte ) 0x2E,
+            ( byte ) 0x63, ( byte ) 0x6F, ( byte ) 0x6D, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x01,
+            ( byte ) 0x45, ( byte ) 0xD7, ( byte ) 0x96, ( byte ) 0x79, ( byte ) 0x04, ( byte ) 0x00, ( byte ) 0x03,
+            ( byte ) 0x00, ( byte ) 0x08, ( byte ) 0x13, ( byte ) 0xD9, ( byte ) 0x19, ( byte ) 0x98, ( byte ) 0x23,
+            ( byte ) 0x8F, ( byte ) 0x9E, ( byte ) 0x31 };
+
+    private static final TimeZone UTC_TIME_ZONE = TimeZone.getTimeZone( "UTC" );
+
+    private static final SimpleDateFormat dateFormat = new SimpleDateFormat( "yyyyMMddHHmmss'Z'" );
+
+    static
+    {
+        dateFormat.setTimeZone( UTC_TIME_ZONE );
+    }
+
+
+    /**
+     * Read the first keytab test bytes and check for the presence of a valid DES key.
+     *
+     * @throws Exception
+     */
+    public void testReadKeytab1() throws Exception
+    {
+        Keytab keytab = Keytab.read( keytab1 );
+
+        assertTrue( "Keytab version", Arrays.equals( Keytab.VERSION_52, keytab.getKeytabVersion() ) );
+        assertEquals( "Entries size", 1, keytab.getEntries().size() );
+
+        KeytabEntry entry = keytab.getEntries().get( 0 );
+        EncryptionKey key = entry.getKey();
+
+        try
+        {
+            assertTrue( DESKeySpec.isParityAdjusted( key.getKeyValue(), 0 ) );
+        }
+        catch ( InvalidKeyException ike )
+        {
+            fail( "Key is invalid." );
+        }
+    }
+
+
+    /**
+     * Read the second keytab test bytes and check for the presence of a valid DES key.
+     *
+     * @throws Exception
+     */
+    public void testReadKeytab2() throws Exception
+    {
+        Keytab keytab = Keytab.read( keytab2 );
+
+        assertTrue( "Keytab version", Arrays.equals( Keytab.VERSION_52, keytab.getKeytabVersion() ) );
+        assertEquals( "Entries size", 1, keytab.getEntries().size() );
+
+        KeytabEntry entry = keytab.getEntries().get( 0 );
+        EncryptionKey key = entry.getKey();
+
+        try
+        {
+            assertTrue( DESKeySpec.isParityAdjusted( key.getKeyValue(), 0 ) );
+        }
+        catch ( InvalidKeyException ike )
+        {
+            fail( "Key is invalid." );
+        }
+    }
+
+
+    /**
+     * Test the writing of a keytab file.
+     *
+     * @throws Exception
+     */
+    public void testWriteKeytab() throws Exception
+    {
+        List<KeytabEntry> entries = new ArrayList<KeytabEntry>();
+
+        entries.add( getEntry1() );
+        entries.add( getEntry1() );
+
+        Keytab writer = Keytab.getInstance();
+        writer.setEntries( entries );
+        ByteBuffer buffer = writer.write();
+        assertEquals( "Expected file size.", 130, buffer.limit() );
+    }
+
+
+    private KeytabEntry getEntry1() throws ParseException
+    {
+        String principalName = "HTTP/www.verisign.com@EXAMPLE.COM";
+        long principalType = 1;
+
+        String zuluTime = "20070217235745Z";
+        Date date = null;
+        synchronized ( dateFormat )
+        {
+            date = dateFormat.parse( zuluTime );
+        }
+
+        KerberosTime timeStamp = new KerberosTime( date );
+
+        byte keyVersion = 1;
+        String passPhrase = "secret";
+        Map<EncryptionType, EncryptionKey> keys = KerberosKeyFactory.getKerberosKeys( principalName, passPhrase );
+        EncryptionKey key = keys.get( EncryptionType.DES_CBC_MD5 );
+
+        return new KeytabEntry( principalName, principalType, timeStamp, keyVersion, key );
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/messages/components/TicketTest.java b/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/messages/components/TicketTest.java
new file mode 100644
index 0000000..5f3d074
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/messages/components/TicketTest.java
@@ -0,0 +1,133 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.components;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.apache.directory.server.kerberos.shared.io.encoder.TicketEncoder;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+import org.apache.directory.server.kerberos.shared.store.TicketFactory;
+import org.apache.directory.shared.ldap.util.StringTools;
+
+import junit.framework.TestCase;
+
+/**
+ * Test the Ticket encoding and decoding
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 542147 $, $Date: 2007-05-28 10:14:21 +0200 (Mon, 28 May 2007) $
+ */
+public class TicketTest extends TestCase
+{
+    public void testTicket() throws Exception
+    {
+        TicketFactory ticketFactory = new TicketFactory();
+
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        KerberosPrincipal serverPrincipal = new KerberosPrincipal( "kadmin/changepw@EXAMPLE.COM" );
+        String serverPassword = "s3crEt";
+
+        EncryptionKey serverKey = ticketFactory.getServerKey( serverPrincipal, serverPassword );
+
+        Ticket serviceTicket = ticketFactory.getTicket( clientPrincipal, serverPrincipal, serverKey );
+
+        byte[] encodedTicket = TicketEncoder.encodeTicket( serviceTicket );
+        
+        ByteBuffer encoded = serviceTicket.encode( null );
+        
+        byte[] expectedResult = new byte[]
+            {
+              0x61, (byte)0x81, (byte)0xEF,
+                0x30,  (byte)0x81, (byte)0xEC, 
+                  (byte)0xA0, 0x03,
+                    0x02, 0x01, 0x05,
+                  (byte)0xA1, 0x0D,
+                    0x1B, 0x0B, 
+                      'E', 'X', 'A', 'M', 'P', 'L', 'E', '.', 'C', 'O', 'M',
+                  (byte)0xA2, 0x1D,
+                    0x30, 0x1B,
+                      (byte)0xA0, 0x03, 
+                        0x02, 0x01, 0x01, 
+                      (byte)0xA1, 0x14, 
+                        0x30, 0x12, 
+                          0x1B, 0x06, 
+                            'k', 'a', 'd', 'm', 'i', 'n',
+                          0x1B, 0x08,
+                            'c', 'h', 'a', 'n', 'g', 'e', 'p', 'w',
+                  (byte)0xA3, (byte)0x81, (byte)0xB6, 
+                    0x30, (byte)0x81, (byte)0xB3,
+                      (byte)0xA0, 0x03,
+                        0x02, 0x01, 0x03,
+                      (byte)0xA2, (byte)0x81, (byte)0xAB,
+                        0x04, (byte)0x81, (byte)0xA8
+            };
+
+        // We will just compared the first bytes (everyting before the encrypted data)
+        String expectedResultString = StringTools.dumpBytes( expectedResult );
+        String resultString = StringTools.dumpBytes( encoded.array() ).substring( 0,  expectedResultString.length() );
+        
+        assertEquals( expectedResultString, resultString );
+        assertTrue( Arrays.equals( encodedTicket, encodedTicket ) );
+    }
+
+    /*
+    public void testTicketPerf() throws Exception
+    {
+        TicketFactory ticketFactory = new TicketFactory();
+
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        KerberosPrincipal serverPrincipal = new KerberosPrincipal( "kadmin/changepw@EXAMPLE.COM" );
+        String serverPassword = "s3crEt";
+
+        EncryptionKey serverKey = ticketFactory.getServerKey( serverPrincipal, serverPassword );
+
+        Ticket serviceTicket = ticketFactory.getTicket( clientPrincipal, serverPrincipal, serverKey );
+
+        byte[] encodedTicket = TicketEncoder.encodeTicket( serviceTicket );
+        
+        long t0 = System.currentTimeMillis();
+        
+        for ( int i=0; i < 1000000; i++ )
+        {
+            TicketEncoder.encodeTicket( serviceTicket );
+        }
+        
+        long t1 = System.currentTimeMillis();
+        
+        System.out.println( "Delta slow = " + ( t1 - t0 ) );
+
+        long t2 = System.currentTimeMillis();
+        
+        for ( int i=0; i < 1000000; i++ )
+        {
+            serviceTicket.encode( null );
+        }
+        
+        long t3 = System.currentTimeMillis();
+        
+        System.out.println( "Delta slow = " + ( t3 - t2 ) );
+
+        assertTrue( Arrays.equals( encodedTicket, encodedTicket ) );
+    }
+    */
+}
diff --git a/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/messages/value/AuthorizationDataTest.java b/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/messages/value/AuthorizationDataTest.java
new file mode 100644
index 0000000..ee2244c
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/messages/value/AuthorizationDataTest.java
@@ -0,0 +1,102 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.value;
+
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+import org.apache.directory.server.kerberos.shared.messages.value.types.AuthorizationType;
+
+import junit.framework.TestCase;
+
+
+/**
+ * Test the AuthorizationData encoding and decoding
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 542147 $, $Date: 2007-05-28 10:14:21 +0200 (Mon, 28 May 2007) $
+ */
+public class AuthorizationDataTest extends TestCase
+{
+    public void testAuthorizationDataOneAD() throws Exception
+    {
+        AuthorizationData ad = new AuthorizationData();
+        ad.add( new AuthorizationDataEntry( AuthorizationType.AD_KDC_ISSUED, new byte[]
+            { 0x01, 0x02, 0x03, 0x04 } ) );
+
+        ByteBuffer encoded = ByteBuffer.allocate( ad.computeLength() );
+
+        ad.encode( encoded );
+
+        byte[] expectedResult = new byte[]
+            { 
+              0x30, 0x0F, 
+                0x30, 0x0d, 
+                  ( byte ) 0xA0, 0x03, 
+                    0x02, 0x01, 0x04, 
+                  ( byte ) 0xA1, 0x06, 
+                    0x04, 0x04, 0x01, 0x02, 0x03, 0x04 
+            };
+
+        assertTrue( Arrays.equals( expectedResult, encoded.array() ) );
+    }
+
+
+    public void testAuthorizationDataThreeAD() throws Exception
+    {
+        AuthorizationData ad = new AuthorizationData();
+        ad.add( new AuthorizationDataEntry( AuthorizationType.AD_KDC_ISSUED, new byte[]
+            { 0x01, 0x02, 0x03, 0x04 } ) );
+        ad.add( new AuthorizationDataEntry( AuthorizationType.AD_IF_RELEVANT, new byte[]
+            { 0x05, 0x06, 0x07, 0x08 } ) );
+        ad.add( new AuthorizationDataEntry( AuthorizationType.AD_MANDATORY_TICKET_EXTENSIONS, new byte[]
+            { 0x09, 0x0A, 0x0B, 0x0C } ) );
+
+        ByteBuffer encoded = ByteBuffer.allocate( ad.computeLength() );
+
+        ad.encode( encoded );
+
+        byte[] expectedResult = new byte[]
+            { 
+              0x30, 0x2D, 
+                0x30, 0x0d, 
+                  ( byte ) 0xA0, 0x03, 
+                    0x02, 0x01, 0x04, 
+                  ( byte ) 0xA1, 0x06, 
+                    0x04, 0x04, 
+                      0x01, 0x02, 0x03, 0x04, 
+                0x30, 0x0d, 
+                  ( byte ) 0xA0, 0x03, 
+                    0x02, 0x01, 0x01, 
+                  ( byte ) 0xA1, 0x06, 
+                    0x04, 0x04,
+                      0x05, 0x06, 0x07, 0x08, 
+                0x30, 0x0d, 
+                  ( byte ) 0xA0, 0x03, 
+                    0x02, 0x01, 0x06, 
+                  ( byte ) 0xA1, 0x06, 
+                    0x04, 0x04, 
+                      0x09, 0x0A, 0x0B, 0x0C 
+            };
+
+        assertTrue( Arrays.equals( expectedResult, encoded.array() ) );
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/messages/value/ChecksumTest.java b/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/messages/value/ChecksumTest.java
new file mode 100644
index 0000000..ac872dc
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/messages/value/ChecksumTest.java
@@ -0,0 +1,97 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.value;
+
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+import org.apache.directory.server.kerberos.shared.crypto.checksum.ChecksumType;
+
+import junit.framework.TestCase;
+
+
+/**
+ * Test the Checksum encoding and decoding
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 542147 $, $Date: 2007-05-28 10:14:21 +0200 (Mon, 28 May 2007) $
+ */
+public class ChecksumTest extends TestCase
+{
+    public void testEncodingChecksum() throws Exception
+    {
+        Checksum chk = new Checksum( ChecksumType.CRC32, new byte[]
+            { 0x01, 0x02, 0x03 } );
+
+        ByteBuffer encoded = ByteBuffer.allocate( chk.computeLength() );
+
+        chk.encode( encoded );
+
+        byte[] expectedResult = new byte[]
+            { 
+            0x30, 0x0c, 
+              (byte)0xA0, 0x03, 
+                0x02, 0x01, 0x01, 
+              (byte)0xA1, 0x05, 
+                0x04, 0x03, 
+                  0x01, 0x02, 0x03 
+            };
+
+        assertTrue( Arrays.equals( expectedResult, encoded.array() ) );
+    }
+
+
+    public void testEncodingNullChecksum() throws Exception
+    {
+        Checksum chk = new Checksum( ChecksumType.CRC32, null );
+
+        ByteBuffer encoded = ByteBuffer.allocate( chk.computeLength() );
+
+        chk.encode( encoded );
+
+        byte[] expectedResult = new byte[]
+            { 
+                0x30, 0x09, 
+                  ( byte ) 0xA0, 
+                    0x03, 0x02, 0x01, 0x01, 
+                  ( byte ) 0xA1, 0x02, 
+                    0x04, 0x00 
+            };
+
+        assertTrue( Arrays.equals( expectedResult, encoded.array() ) );
+    }
+
+
+    /**
+     * Tests that two Checksums are equal if both their type and value are equal.
+     */
+    public void testEquality()
+    {
+        byte[] checksumValue =
+            { ( byte ) 0x30, ( byte ) 0x1A, ( byte ) 0xA0, ( byte ) 0x11, ( byte ) 0x18, ( byte ) 0x0F, ( byte ) 0x32,
+                ( byte ) 0x30 };
+
+        Checksum expected = new Checksum( ChecksumType.RSA_MD5, checksumValue );
+        Checksum provided = new Checksum( ChecksumType.RSA_MD5, checksumValue );
+
+        assertTrue( "Checksum equality", expected.equals( provided ) );
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/messages/value/EncryptedDataTest.java b/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/messages/value/EncryptedDataTest.java
new file mode 100644
index 0000000..a93c04d
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/messages/value/EncryptedDataTest.java
@@ -0,0 +1,128 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.value;
+
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
+import org.apache.directory.shared.ldap.util.StringTools;
+
+import junit.framework.TestCase;
+
+
+/**
+ * Test the EncryptedData encoding and decoding
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 542147 $, $Date: 2007-05-28 10:14:21 +0200 (Mon, 28 May 2007) $
+ */
+public class EncryptedDataTest extends TestCase
+{
+    public void testEncodingEncryptedData() throws Exception
+    {
+        EncryptedData ed = new EncryptedData( EncryptionType.AES128_CTS_HMAC_SHA1_96, 1, new byte[]
+            { 0x01, 0x02, 0x03, 0x04 } );
+
+        ByteBuffer encoded = ByteBuffer.allocate( ed.computeLength() );
+
+        ed.encode( encoded );
+
+        byte[] expectedResult = new byte[]
+            { 
+                0x30, 0x12, 
+                  ( byte ) 0xA0, 0x03, 
+                    0x02, 0x01, 0x11, 
+                  ( byte ) 0xA1, 0x03, 
+                    0x02, 0x01, 0x01, 
+                  ( byte ) 0xA2, 0x06, 
+                    0x04, 0x04, 0x01, 0x02, 0x03, 0x04 
+            };
+
+        assertEquals( StringTools.dumpBytes( expectedResult ), StringTools.dumpBytes( encoded.array() ) );
+    }
+
+
+    public void testEncodingEncryptedDataNullCipher() throws Exception
+    {
+        EncryptedData ed = new EncryptedData( EncryptionType.AES128_CTS_HMAC_SHA1_96, 1, null );
+
+        ByteBuffer encoded = ByteBuffer.allocate( ed.computeLength() );
+
+        ed.encode( encoded );
+
+        byte[] expectedResult = new byte[]
+            { 
+              0x30, 0x0E, 
+                ( byte ) 0xA0, 0x03, 
+                  0x02, 0x01, 0x11, 
+                ( byte ) 0xA1, 0x03, 
+                  0x02, 0x01, 0x01, 
+                ( byte ) 0xA2, 0x02, 
+                  0x04, 0x00 
+            };
+
+        assertTrue( Arrays.equals( expectedResult, encoded.array() ) );
+    }
+
+
+    public void testEncodingEncryptedDataNoKvno() throws Exception
+    {
+        EncryptedData ed = new EncryptedData( EncryptionType.AES128_CTS_HMAC_SHA1_96, new byte[]
+            { 0x01, 0x02, 0x03, 0x04 } );
+
+        ByteBuffer encoded = ByteBuffer.allocate( ed.computeLength() );
+
+        ed.encode( encoded );
+
+        byte[] expectedResult = new byte[]
+            { 
+              0x30, 0x0D, 
+                ( byte ) 0xA0, 0x03, 
+                  0x02, 0x01, 0x11, 
+                ( byte ) 0xA2, 0x06, 
+                  0x04, 0x04, 0x01, 0x02, 0x03, 0x04 
+            };
+
+        assertTrue( Arrays.equals( expectedResult, encoded.array() ) );
+    }
+
+
+    public void testEncodingEncryptedDataNoKvnoNullCipher() throws Exception
+    {
+        EncryptedData ed = new EncryptedData( EncryptionType.AES128_CTS_HMAC_SHA1_96, null );
+
+        ByteBuffer encoded = ByteBuffer.allocate( ed.computeLength() );
+
+        ed.encode( encoded );
+
+        byte[] expectedResult = new byte[]
+            { 
+              0x30, 0x09, 
+                ( byte ) 0xA0, 0x03, 
+                  0x02, 0x01, 0x11, 
+                ( byte ) 0xA2, 0x02, 
+                  0x04, 0x00 
+            };
+
+        assertTrue( Arrays.equals( expectedResult, encoded.array() ) );
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/messages/value/EncryptionKeyTest.java b/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/messages/value/EncryptionKeyTest.java
new file mode 100644
index 0000000..106162a
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/messages/value/EncryptionKeyTest.java
@@ -0,0 +1,164 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.value;
+
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
+import org.apache.directory.server.kerberos.shared.io.encoder.EncryptionKeyEncoder;
+import org.apache.directory.shared.asn1.codec.EncoderException;
+
+import junit.framework.TestCase;
+
+
+/**
+ * Test the EncryptionKey encoding and decoding
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 542147 $, $Date: 2007-05-28 10:14:21 +0200 (Mon, 28 May 2007) $
+ */
+public class EncryptionKeyTest extends TestCase
+{
+    public void testEncodingFast() throws Exception
+    {
+        EncryptionKey ec = new EncryptionKey( EncryptionType.AES128_CTS_HMAC_SHA1_96, new byte[]
+            { 0x01, 0x02, 0x03 } );
+
+        ByteBuffer encoded = ByteBuffer.allocate( ec.computeLength() );
+
+        ec.encode( encoded );
+
+        byte[] expectedResult = new byte[]
+            { 
+              0x30, 0x0c, 
+                ( byte ) 0xA0, 0x03, 
+                  0x02, 0x01, 0x11, 
+                ( byte ) 0xA1, 0x05, 
+                  0x04, 0x03, 0x01, 0x02, 0x03 
+            };
+
+        assertTrue( Arrays.equals( expectedResult, encoded.array() ) );
+    }
+
+
+    public void testEncodingNoStructureFast() throws Exception
+    {
+        EncryptionKey ec = new EncryptionKey( EncryptionType.AES128_CTS_HMAC_SHA1_96, null );
+
+        ByteBuffer encoded = ByteBuffer.allocate( ec.computeLength() );
+
+        ec.encode( encoded );
+
+        byte[] expectedResult = new byte[]
+            { 
+              0x30, 0x09, 
+                ( byte ) 0xA0, 0x03, 
+                  0x02, 0x01, 0x11, 
+                ( byte ) 0xA1, 0x02, 
+                  0x04, 0x00 
+            };
+
+        assertTrue( Arrays.equals( expectedResult, encoded.array() ) );
+    }
+
+
+    /*
+     public void testEncodingNoStructureSlow() throws Exception
+     {
+     EncryptionKey ec = new EncryptionKey( EncryptionType.AES128_CTS_HMAC_SHA1_96, null );
+     
+     byte[] encoded = EncryptionKeyEncoder.encode( ec );
+     
+     byte[] expectedResult = new byte[]
+     {
+     0x30, 0x09, 
+     (byte)0xA0, 0x03,
+     0x02, 0x01, 0x11,
+     (byte)0xA1, 0x02,
+     0x04, 0x00
+     };
+
+     assertTrue( Arrays.equals( expectedResult, encoded ) );
+     }
+     */
+
+    public void testEncodingSlow() throws Exception
+    {
+        EncryptionKey ec = new EncryptionKey( EncryptionType.AES128_CTS_HMAC_SHA1_96, new byte[]
+            { 0x01, 0x02, 0x03 } );
+
+        byte[] encoded = EncryptionKeyEncoder.encode( ec );
+
+        byte[] expectedResult = new byte[]
+            { 
+              0x30, 0x0c, 
+                ( byte ) 0xA0, 0x03, 
+                  0x02, 0x01, 0x11, 
+                ( byte ) 0xA1, 0x05, 
+                  0x04, 0x03, 0x01, 0x02, 0x03 
+            };
+
+        assertTrue( Arrays.equals( expectedResult, encoded ) );
+    }
+
+
+    public void testPerfSlow() throws IOException
+    {
+        EncryptionKey ec = new EncryptionKey( EncryptionType.AES128_CTS_HMAC_SHA1_96, new byte[]
+            { 0x01, 0x02, 0x03 } );
+        EncryptionKeyEncoder.encode( ec );
+
+        long t0 = System.currentTimeMillis();
+
+        //for ( int i = 0; i < 10000000; i++ )
+        {
+            EncryptionKeyEncoder.encode( ec );
+        }
+
+        long t1 = System.currentTimeMillis();
+
+        System.out.println( "Delta = " + ( t1 - t0 ) );
+    }
+
+
+    public void testPerfFast() throws EncoderException
+    {
+        EncryptionKey ec = new EncryptionKey( EncryptionType.AES128_CTS_HMAC_SHA1_96, new byte[]
+            { 0x01, 0x02, 0x03 } );
+        ByteBuffer encoded = ByteBuffer.allocate( ec.computeLength() );
+        ec.encode( encoded );
+
+        long t0 = System.currentTimeMillis();
+
+        //for ( int i = 0; i < 40000000; i++ )
+        {
+            encoded = ByteBuffer.allocate( ec.computeLength() );
+
+            ec.encode( encoded );
+        }
+
+        long t1 = System.currentTimeMillis();
+
+        System.out.println( "Delta2 = " + ( t1 - t0 ) );
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/messages/value/HostAddressTest.java b/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/messages/value/HostAddressTest.java
new file mode 100644
index 0000000..051cdea
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/messages/value/HostAddressTest.java
@@ -0,0 +1,81 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.value;
+
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+import org.apache.directory.server.kerberos.shared.messages.value.types.HostAddrType;
+
+import junit.framework.TestCase;
+
+
+/**
+ * Test the HostAddress encoding and decoding
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 542147 $, $Date: 2007-05-28 10:14:21 +0200 (Mon, 28 May 2007) $
+ */
+public class HostAddressTest extends TestCase
+{
+    public void testEncodingHostAddressIP() throws Exception
+    {
+        HostAddress ha = new HostAddress( HostAddrType.ADDRTYPE_INET, new byte[]
+            { 0x01, 0x02, 0x03, 0x04 } );
+
+        ByteBuffer encoded = ByteBuffer.allocate( ha.computeLength() );
+
+        ha.encode( encoded );
+
+        byte[] expectedResult = new byte[]
+            { 
+                0x30, 0x0d, 
+                  (byte)0xA0, 0x03, 
+                    0x02, 0x01, 0x02, 
+                  (byte)0xA1, 0x06, 
+                    0x04, 0x04, 
+                      0x01, 0x02, 0x03, 0x04 
+            };
+
+        assertTrue( Arrays.equals( expectedResult, encoded.array() ) );
+    }
+
+
+    public void testEncodingHostAddressIPNullAddress() throws Exception
+    {
+        HostAddress ha = new HostAddress( HostAddrType.ADDRTYPE_INET, null );
+
+        ByteBuffer encoded = ByteBuffer.allocate( ha.computeLength() );
+
+        ha.encode( encoded );
+
+        byte[] expectedResult = new byte[]
+            { 
+              0x30, 0x09, 
+                (byte)0xA0, 0x03, 
+                  0x02, 0x01, 0x02, 
+                (byte)0xA1, 0x02, 
+                  0x04, 0x00 
+            };
+
+        assertTrue( Arrays.equals( expectedResult, encoded.array() ) );
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/messages/value/HostAddressesTest.java b/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/messages/value/HostAddressesTest.java
new file mode 100644
index 0000000..bc3ed0b
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/messages/value/HostAddressesTest.java
@@ -0,0 +1,120 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.value;
+
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+import org.apache.directory.server.kerberos.shared.messages.value.types.HostAddrType;
+
+import junit.framework.TestCase;
+
+
+/**
+ * Test the HostAddresses encoding and decoding
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 542147 $, $Date: 2007-05-28 10:14:21 +0200 (Mon, 28 May 2007) $
+ */
+public class HostAddressesTest extends TestCase
+{
+    public void testEncodingHostAddressesIPOneAddresses() throws Exception
+    {
+        HostAddress[] ha = new HostAddress[]
+            { new HostAddress( HostAddrType.ADDRTYPE_INET, new byte[]
+                { 0x01, 0x02, 0x03, 0x04 } ), };
+
+        HostAddresses has = new HostAddresses( ha );
+
+        ByteBuffer encoded = ByteBuffer.allocate( has.computeLength() );
+
+        has.encode( encoded );
+
+        byte[] expectedResult = new byte[]
+            { 
+              0x30, 0x0F, 
+                0x30, 0x0d, 
+                  (byte)0xA0, 0x03, 
+                    0x02, 0x01, 0x02, 
+                  (byte)0xA1, 0x06, 
+                    0x04, 0x04, 
+                      0x01, 0x02, 0x03, 0x04
+            };
+
+        assertTrue( Arrays.equals( expectedResult, encoded.array() ) );
+    }
+
+
+    public void testEncodingHostAddressesIP3Addresses() throws Exception
+    {
+        HostAddress[] ha = new HostAddress[]
+            { new HostAddress( HostAddrType.ADDRTYPE_INET, new byte[]
+                { 0x01, 0x02, 0x03, 0x04 } ), new HostAddress( HostAddrType.ADDRTYPE_INET, new byte[]
+                { 0x05, 0x06, 0x07, 0x08 } ), new HostAddress( HostAddrType.ADDRTYPE_INET, new byte[]
+                { 0x09, 0x0A, 0x0B, 0x0C } ) };
+
+        HostAddresses has = new HostAddresses( ha );
+
+        ByteBuffer encoded = ByteBuffer.allocate( has.computeLength() );
+
+        has.encode( encoded );
+
+        byte[] expectedResult = new byte[]
+            { 
+                0x30, 0x2D, 
+                  0x30, 0x0d, 
+                    (byte)0xA0, 0x03, 
+                      0x02, 0x01, 0x02, 
+                    (byte)0xA1, 0x06, 
+                      0x04, 0x04, 
+                        0x01, 0x02, 0x03, 0x04, 
+                  0x30, 0x0d, 
+                    (byte)0xA0, 0x03, 
+                      0x02, 0x01, 0x02, 
+                    (byte)0xA1, 0x06, 
+                      0x04, 0x04,
+                        0x05, 0x06, 0x07, 0x08, 
+                  0x30, 0x0d, 
+                    (byte)0xA0, 0x03, 
+                      0x02, 0x01, 0x02, 
+                    (byte)0xA1, 0x06, 
+                      0x04, 0x04, 
+                        0x09, 0x0A, 0x0B, 0x0C 
+            };
+
+        assertTrue( Arrays.equals( expectedResult, encoded.array() ) );
+    }
+
+
+    public void testEncodingHostAddressIPNullAddress() throws Exception
+    {
+        HostAddresses has = new HostAddresses( null );
+
+        ByteBuffer encoded = ByteBuffer.allocate( has.computeLength() );
+
+        has.encode( encoded );
+
+        byte[] expectedResult = new byte[]
+            { 0x30, 0x00 };
+
+        assertTrue( Arrays.equals( expectedResult, encoded.array() ) );
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/messages/value/PaDataTest.java b/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/messages/value/PaDataTest.java
new file mode 100644
index 0000000..1583a7f
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/messages/value/PaDataTest.java
@@ -0,0 +1,81 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.value;
+
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+import org.apache.directory.server.kerberos.shared.messages.value.types.PaDataType;
+
+import junit.framework.TestCase;
+
+
+/**
+ * Test the PaData encoding and decoding
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 542147 $, $Date: 2007-05-28 10:14:21 +0200 (Mon, 28 May 2007) $
+ */
+public class PaDataTest extends TestCase
+{
+    public void testEncodingPreAuthenticationData() throws Exception
+    {
+        PaData pad = new PaData( PaDataType.PA_ASF3_SALT, new byte[]
+            { 0x01, 0x02, 0x03 } );
+
+        ByteBuffer encoded = ByteBuffer.allocate( pad.computeLength() );
+
+        pad.encode( encoded );
+
+        byte[] expectedResult = new byte[]
+            { 
+            0x30, 0x0c, 
+              ( byte ) 0xA1, 0x03, 
+                0x02, 0x01, 0x0A, 
+              ( byte ) 0xA2, 0x05, 
+                0x04, 0x03, 
+                  0x01, 0x02, 0x03 
+            };
+
+        assertTrue( Arrays.equals( expectedResult, encoded.array() ) );
+    }
+
+
+    public void testEncodingNullPreAuthenticationData() throws Exception
+    {
+        PaData pad = new PaData( PaDataType.PA_ASF3_SALT, null );
+
+        ByteBuffer encoded = ByteBuffer.allocate( pad.computeLength() );
+
+        pad.encode( encoded );
+
+        byte[] expectedResult = new byte[]
+            { 
+                0x30, 0x09, 
+                  ( byte ) 0xA1, 0x03, 
+                    0x02, 0x01, 0x0A, 
+                  ( byte ) 0xA2, 0x02, 
+                    0x04, 0x00 
+            };
+
+        assertTrue( Arrays.equals( expectedResult, encoded.array() ) );
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/messages/value/PrincipalNameTest.java b/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/messages/value/PrincipalNameTest.java
new file mode 100644
index 0000000..e15e552
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/messages/value/PrincipalNameTest.java
@@ -0,0 +1,110 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.value;
+
+
+import java.nio.ByteBuffer;
+
+import org.apache.directory.server.kerberos.shared.messages.value.types.PrincipalNameType;
+import org.apache.directory.shared.ldap.util.StringTools;
+
+import junit.framework.TestCase;
+
+
+/**
+ * Test the PrincipalName encoding and decoding
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 542147 $, $Date: 2007-05-28 10:14:21 +0200 (Mon, 28 May 2007) $
+ */
+public class PrincipalNameTest extends TestCase
+{
+    public void testEncodingPrincipalNameOneName() throws Exception
+    {
+        PrincipalName principal = new PrincipalName( "Test@APACHE", PrincipalNameType.KRB_NT_PRINCIPAL );
+
+        ByteBuffer encoded = ByteBuffer.allocate( principal.computeLength() );
+
+        principal.encode( encoded );
+
+        byte[] expectedResult = new byte[]
+            { 
+              0x30, 0x0F, 
+                (byte) 0xA0, 0x03, 
+                  0x02, 0x01, 0x01, 
+                (byte) 0xA1, 0x08, 
+                  0x30, 0x06, 
+                    0x1B, 0x04, 
+                      'T', 'e', 's', 't' 
+            };
+
+        assertEquals( StringTools.dumpBytes( expectedResult ), StringTools.dumpBytes( encoded.array() ) );
+    }
+
+
+    public void testEncodingPrincipalName3names() throws Exception
+    {
+        PrincipalName principal = new PrincipalName( "Test1@APACHE", PrincipalNameType.KRB_NT_PRINCIPAL );
+        principal.addName( "Test2" );
+        principal.addName( "Test3" );
+
+        ByteBuffer encoded = ByteBuffer.allocate( principal.computeLength() );
+
+        principal.encode( encoded );
+
+        byte[] expectedResult = new byte[]
+            { 
+              0x30, 0x1e, 
+                (byte) 0xA0, 0x03, 
+                  0x02, 0x01, 0x01, 
+                (byte) 0xA1, 0x17, 
+                  0x30, 0x15, 
+                    0x1B, 0x05, 
+                      'T', 'e', 's', 't', '1', 
+                    0x1B, 0x05, 
+                      'T', 'e', 's', 't', '2', 
+                    0x1B, 0x05, 
+                      'T', 'e', 's', 't', '3' 
+            };
+
+        assertEquals( StringTools.dumpBytes( expectedResult ), StringTools.dumpBytes( encoded.array() ) );
+    }
+
+
+    public void testEncodingPrincipalNameNullName() throws Exception
+    {
+        PrincipalName principal = new PrincipalName( null, PrincipalNameType.KRB_NT_PRINCIPAL );
+
+        ByteBuffer encoded = ByteBuffer.allocate( principal.computeLength() );
+
+        principal.encode( encoded );
+
+        byte[] expectedResult = new byte[]
+            { 
+              0x30, 0x09, 
+                ( byte ) 0xA0, 0x03, 
+                  0x02, 0x01, 0x01, 
+                ( byte ) 0xA1, 0x02, 
+                  0x30, 0x00 
+            };
+
+        assertEquals( StringTools.dumpBytes( expectedResult ), StringTools.dumpBytes( encoded.array() ) );
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/messages/value/TransitedEncodingTest.java b/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/messages/value/TransitedEncodingTest.java
new file mode 100644
index 0000000..a849413
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/messages/value/TransitedEncodingTest.java
@@ -0,0 +1,80 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.messages.value;
+
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+import org.apache.directory.server.kerberos.shared.messages.value.types.TransitedEncodingType;
+
+import junit.framework.TestCase;
+
+
+/**
+ * Test the TransitedEncoding encoding and decoding
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 542147 $, $Date: 2007-05-28 10:14:21 +0200 (Mon, 28 May 2007) $
+ */
+public class TransitedEncodingTest extends TestCase
+{
+    public void testEncodingFast() throws Exception
+    {
+        TransitedEncoding te = new TransitedEncoding( TransitedEncodingType.DOMAIN_X500_COMPRESS, new byte[]
+            { 0x01, 0x02, 0x03 } );
+
+        ByteBuffer encoded = ByteBuffer.allocate( te.computeLength() );
+
+        te.encode( encoded );
+
+        byte[] expectedResult = new byte[]
+            { 
+              0x30, 0x0c, 
+                ( byte ) 0xA0, 0x03, 
+                  0x02, 0x01, 0x01, 
+                ( byte ) 0xA1, 0x05, 
+                  0x04, 0x03, 0x01, 0x02, 0x03 
+            };
+
+        assertTrue( Arrays.equals( expectedResult, encoded.array() ) );
+    }
+
+
+    public void testEncodingNoStructureFast() throws Exception
+    {
+        TransitedEncoding te = new TransitedEncoding( TransitedEncodingType.DOMAIN_X500_COMPRESS, null );
+
+        ByteBuffer encoded = ByteBuffer.allocate( te.computeLength() );
+
+        te.encode( encoded );
+
+        byte[] expectedResult = new byte[]
+            { 
+              0x30, 0x09, 
+                ( byte ) 0xA0, 0x03, 
+                  0x02, 0x01, 0x01, 
+                ( byte ) 0xA1, 0x02, 
+                  0x04, 0x00 
+            };
+
+        assertTrue( Arrays.equals( expectedResult, encoded.array() ) );
+    }
+}
diff --git a/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/replay/InMemoryReplayCacheTest.java b/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/replay/InMemoryReplayCacheTest.java
new file mode 100644
index 0000000..a6be824
--- /dev/null
+++ b/old_trunk/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/replay/InMemoryReplayCacheTest.java
@@ -0,0 +1,131 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.shared.replay;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
+import org.apache.directory.server.kerberos.shared.messages.value.types.PrincipalNameType;
+import org.apache.directory.server.kerberos.shared.replay.InMemoryReplayCache.ReplayCacheEntry;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+/**
+ * Test the InMemory replay cache
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 542147 $, $Date: 2007-05-28 10:14:21 +0200 (Mon, 28 May 2007) $
+ */
+public class InMemoryReplayCacheTest
+{
+    /**
+     * Test that the cache is working well. We will create a new entry
+     * every 20 ms, with 10 different serverPrincipals.
+     * 
+     * After this period of time, we should only have 25 entries in the cache
+     */
+    @Test
+    public void testCacheSetting() throws Exception
+    {
+        int delay = 500;
+        long clockSkew = 100;
+        
+        // Set a delay of 500 ms and a clock skew of 100 ms
+        InMemoryReplayCache cache = new InMemoryReplayCache( clockSkew, delay );
+        
+        // Loop for 2 seconds, then check that the cache is clean
+        int i = 0;
+        int nbClient = 20;
+        int nbServer = 10;
+        
+        // Inject 100 entries, one every 20 ms
+        while ( i < 100 )
+        {
+            KerberosPrincipal serverPrincipal = new KerberosPrincipal( "server" + i%nbServer + "@APACHE.ORG", PrincipalNameType.KRB_NT_PRINCIPAL.getOrdinal() );
+            KerberosPrincipal clientPrincipal = new KerberosPrincipal( "client" + i%nbClient + "@APACHE.ORG", PrincipalNameType.KRB_NT_PRINCIPAL.getOrdinal() );
+            
+            cache.save( serverPrincipal, clientPrincipal, new KerberosTime( System.currentTimeMillis() ), 0 );
+            
+            Thread.sleep( 20 );
+            i++;
+        }
+        
+        Map<KerberosPrincipal, List<ReplayCacheEntry>> map = cache.getCache();
+
+        // We should have 20 List of entries, as we have injected 20 different
+        // clientPrincipals
+        assertEquals( nbClient, map.size() );
+        
+        int nbEntries = 0;
+        
+        // Loop into the cache to see how many entries we have
+        Collection<List<ReplayCacheEntry>> entryList = map.values();
+        
+        for ( List<ReplayCacheEntry> entries:entryList )
+        {
+            if ( ( entries == null ) || ( entries.size() == 0 ) )
+            {
+                continue;
+            }
+            
+            Iterator<ReplayCacheEntry> iterator = entries.iterator();
+            
+            while ( iterator.hasNext() )
+            {
+                iterator.next();
+                nbEntries ++;
+            }
+        }
+
+        // We should have some
+        assertNotNull( nbEntries );
+        
+        // Wait another delay, so that the cleaning thread will be kicked off
+        Thread.sleep( delay + 50 );
+        
+        nbEntries = 0;
+        
+        for ( List<ReplayCacheEntry> entries:entryList )
+        {
+            if ( ( entries == null ) || ( entries.size() == 0 ) )
+            {
+                continue;
+            }
+            
+            Iterator<ReplayCacheEntry> iterator = entries.iterator();
+            
+            while ( iterator.hasNext() )
+            {
+                iterator.next();
+                nbEntries ++;
+            }
+        }
+
+        // We should not have anymore entry in the cache
+        assertEquals( 0, nbEntries );
+    }
+}
diff --git a/old_trunk/mina-xbean-spring/pom.xml b/old_trunk/mina-xbean-spring/pom.xml
new file mode 100644
index 0000000..e137b50
--- /dev/null
+++ b/old_trunk/mina-xbean-spring/pom.xml
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you 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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+  <parent>
+    <artifactId>apacheds-parent</artifactId>
+    <groupId>org.apache.directory.server</groupId>
+    <version>1.5.3-SNAPSHOT</version>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>org.apache.directory.mina</groupId>
+  <artifactId>mina-xbean-spring</artifactId>
+  <name>Mina Xbean Spring</name>
+
+  <url>http://maven.apache.org</url>
+  <dependencies>
+      <dependency>
+        <groupId>org.apache.mina</groupId>
+        <artifactId>mina-core</artifactId>
+        <version>1.1.6</version>
+        <classifier>sources</classifier>
+      </dependency>
+
+      <dependency>
+        <groupId>org.apache.mina</groupId>
+        <artifactId>mina-filter-ssl</artifactId>
+        <version>1.1.6</version>
+        <classifier>sources</classifier>
+      </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.xbean</groupId>
+        <artifactId>maven-xbean-plugin</artifactId>
+        <executions>
+          <execution>
+            <configuration>
+              <namespace>http://mina.org/config/1.0</namespace>
+              <schema>target/xbean/${pom.artifactId}.xsd</schema>
+            </configuration>
+            <goals>
+              <goal>mapping</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+
+      <!--  lets ensure that the XSD gets deployed  -->
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>build-helper-maven-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>attach-artifacts</id>
+            <phase>package</phase>
+            <goals>
+              <goal>attach-artifact</goal>
+            </goals>
+            <configuration>
+              <artifacts>
+                <artifact>
+                  <file>${basedir}/target/xbean/${pom.artifactId}.xsd</file>
+                  <type>xsd</type>
+                </artifact>
+                <artifact>
+                  <file>${basedir}/target/xbean/${pom.artifactId}.xsd.html</file>
+                  <type>xsd.html</type>
+                </artifact>
+              </artifacts>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/old_trunk/mina-xbean-spring/src/site/site.xml b/old_trunk/mina-xbean-spring/src/site/site.xml
new file mode 100644
index 0000000..9ec7c16
--- /dev/null
+++ b/old_trunk/mina-xbean-spring/src/site/site.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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.
+-->
+<!--
+  @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+-->
+<project name="${project.name}">
+    <bannerLeft>
+        <name>${project.name}</name>
+    </bannerLeft>
+    <publishDate position="navigation-bottom" format="dd-MM-yyyy HH:mm" />
+    <body>
+        <menu name="Parent">
+            <item name="Apache Directory ApacheDS" href="../index.html" />
+        </menu>
+        <menu ref="reports" />
+    </body>
+</project>
diff --git a/old_trunk/mitosis/pom.xml b/old_trunk/mitosis/pom.xml
new file mode 100644
index 0000000..024a18d
--- /dev/null
+++ b/old_trunk/mitosis/pom.xml
@@ -0,0 +1,279 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you 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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.directory.server</groupId>
+    <artifactId>apacheds-parent</artifactId>
+    <version>1.5.4-SNAPSHOT</version>
+  </parent>
+  <artifactId>mitosis</artifactId>
+  <name>ApacheDS replication</name>
+  <packaging>jar</packaging>
+
+  <description>
+     Mitosis is the multi-master replications service included into Apache
+     Directory Server
+  </description>
+  
+  <repositories>
+    <repository>
+      <id>safehauS-m1-repository</id>
+      <name>SafeHaus.org Maven 1.x Repository</name>
+      <url>http://maven.safehaus.org/</url>
+      <layout>legacy</layout>
+    </repository>
+  </repositories>
+
+  <!-- =======  Dependencies are declared here  ======= -->
+  <dependencies>
+
+    <dependency>
+      <groupId>commons-collections</groupId>
+      <artifactId>commons-collections</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>commons-dbcp</groupId>
+      <artifactId>commons-dbcp</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>commons-pool</groupId>
+      <artifactId>commons-pool</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-io</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>commons-lang</groupId>
+      <artifactId>commons-lang</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>jug</groupId>
+      <artifactId>jug-asl</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.mina</groupId>
+      <artifactId>mina-core</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-core</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-core-shared</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-bootstrap-partition</artifactId>
+      <version>${pom.version}</version>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>opensymphony</groupId>
+      <artifactId>quartz</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.derby</groupId>
+      <artifactId>derby</artifactId>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-source-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>attach-sources</id>
+            <phase>verify</phase>
+            <goals>
+              <goal>jar</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+
+  <profiles>
+    <profile>
+    <id>no-integration-or-perf-tests</id>
+    <activation>
+      <activeByDefault>true</activeByDefault>
+    </activation>
+    <build>
+      <plugins>
+        <plugin>
+          <artifactId>maven-surefire-plugin</artifactId>
+          <configuration>
+            <excludes>
+              <exclude>**/*ITest.java</exclude>
+              <exclude>**/*PTest.java</exclude>
+            </excludes>
+            <argLine>-Xmx1024m</argLine>
+          </configuration>
+        </plugin>
+
+        <plugin>
+          <artifactId>maven-antrun-plugin</artifactId>
+            <executions>
+              <execution>
+                <phase>validate</phase>
+                <configuration>
+                  <tasks>
+                    <echo>
+=================================================================
+                          W A R N I N G
+                          -------------
+
+Integration and performance tests have been disabled.  To enable
+integration tests run maven with the -Dintegration switch.  To
+enable performance tests run maven with the -Dperformance switch.
+=================================================================
+                    </echo>
+                  </tasks>
+                </configuration>
+                <goals>
+                  <goal>run</goal>
+                </goals>
+              </execution>
+            </executions>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+
+    <profile>
+      <id>integration</id>
+      <activation>
+        <property><name>integration</name></property>
+      </activation>
+      <build>
+        <plugins>
+          <plugin>
+            <artifactId>maven-surefire-plugin</artifactId>
+            <configuration>
+              <forkMode>pertest</forkMode>
+              <excludes>
+                <exclude>**/*PTest.java</exclude>
+              </excludes>
+              <argLine>-Xmx1024m</argLine>
+            </configuration>
+          </plugin>
+
+          <plugin>
+            <artifactId>maven-antrun-plugin</artifactId>
+            <executions>
+              <execution>
+                <phase>validate</phase>
+                <configuration>
+                  <tasks>
+                    <echo>
+=================================================================
+                   I N T E G R A T I O N
+                   ---------------------
+
+Performance tests have been disabled.  To enable
+performance tests run maven with the -Dperformance switch.
+=================================================================
+                    </echo>
+                  </tasks>
+                </configuration>
+                <goals>
+                  <goal>run</goal>
+                </goals>
+              </execution>
+            </executions>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+ 
+    <profile>
+      <id>performance</id>
+      <activation>
+        <property><name>performance</name></property>
+      </activation>
+      <build>
+        <plugins>
+          <plugin>
+            <artifactId>maven-surefire-plugin</artifactId>
+            <configuration>
+              <systemProperties>
+                <property>
+                  <name>outputDirectory</name>
+                  <value>${basedir}/target</value>
+                </property>
+              </systemProperties>
+              <excludes>
+                <exclude>**/*ITest.java</exclude>
+              </excludes>
+              <argLine>-Xmx1024m</argLine>
+            </configuration>
+          </plugin>
+
+          <plugin>
+            <artifactId>maven-antrun-plugin</artifactId>
+            <executions>
+              <execution>
+                <phase>validate</phase>
+                <configuration>
+                  <tasks>
+                    <echo>
+=================================================================
+                   P E R F O R M A N C E
+                   ---------------------
+
+Integration tests have been disabled.  To enable integration
+tests run maven with the -Dintegration switch.
+=================================================================
+                    </echo>
+                  </tasks>
+                </configuration>
+                <goals>
+                  <goal>run</goal>
+                </goals>
+              </execution>
+            </executions>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+  </profiles>
+</project>
+
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/common/CSN.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/common/CSN.java
new file mode 100644
index 0000000..f279841
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/common/CSN.java
@@ -0,0 +1,68 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.common;
+
+
+import java.io.Serializable;
+
+
+/**
+ * Represents 'Change Sequence Number' in LDUP specification.
+ * 
+ * A CSN is a composition of a timestamp, a replica ID and a 
+ * operation sequence number.
+ * 
+ * It distinguishes a change made on an object on a server,
+ * and if two operations take place during the same timeStamp,
+ * the operation sequence number makes those operations distinct.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public interface CSN extends Serializable, Comparable
+{
+    /**
+     * Returns GMT timestamp of modification.
+     */
+    long getTimestamp();
+
+
+    /**
+     * Returns replica ID.
+     */
+    ReplicaId getReplicaId();
+
+
+    /**
+     * Returns sequence number of modification.
+     */
+    int getOperationSequence();
+
+
+    /**
+     * Returns octet-string representation of this CSN. 
+     */
+    String toOctetString();
+
+
+    /**
+     * Returns a byte array representing the CSN
+     */
+    byte[] toBytes();
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/common/CSNFactory.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/common/CSNFactory.java
new file mode 100644
index 0000000..7c798d1
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/common/CSNFactory.java
@@ -0,0 +1,50 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.common;
+
+
+/**
+ * Generates a new {@link CSN}.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public interface CSNFactory
+{
+    /**
+     * Returns a new {@link CSN}.
+     * Generated CSN can be duplicate if user generates CSNs more than 2G 
+     * times a milliseconds.
+     * 
+     * @param replicaId Replica ID.  ReplicaID must be 1-8 digit alphanumeric
+     *        string.
+     */
+    CSN newInstance( String replicaId );
+
+
+    /**
+     * Returns a new {@link CSN}.
+     * Generated CSN can be duplicate if user generates CSNs more than 2G 
+     * times a milliseconds.
+     * 
+     * @param replicaId Replica ID.  ReplicaID must be 1-8 digit alphanumeric
+     *        string.
+     */
+    CSN newInstance( ReplicaId replicaId );
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/common/CSNVector.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/common/CSNVector.java
new file mode 100644
index 0000000..ea10f80
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/common/CSNVector.java
@@ -0,0 +1,174 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.common;
+
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.directory.mitosis.service.protocol.handler.ReplicationClientContextHandler;
+import org.apache.directory.shared.ldap.util.EqualsBuilder;
+import org.apache.directory.shared.ldap.util.HashCodeBuilder;
+
+
+/**
+ * Creates a set of {@link CSN}s, which is defined in LDUP specification.
+ * Each {@link CSN} in the same {@link CSNVector} has different
+ * {@link ReplicaId} component from each other.  Its data structure is 
+ * similar to a {@link Map} whose key is {@link ReplicaId}.
+ * <p>
+ * {@link CSNVector} is usually used to represent 'Update Vector (UV)' and
+ * 'Purge Vector (PV)'.  Please refer to the LDUP specification and other 
+ * Mitosis classes such as {@link ReplicationClientContextHandler}.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class CSNVector
+{
+    /**
+     * Declares the Serial Version Uid.
+     *
+     * @see <a
+     *      href="http://c2.com/cgi/wiki?AlwaysDeclareSerialVersionUid">Always
+     *      Declare Serial Version Uid</a>
+     */
+    private static final long serialVersionUID = 1L;
+
+    private final Map<ReplicaId,CSN> csns = new HashMap<ReplicaId,CSN>();
+
+    /**
+     * Creates a new empty instance.
+     */
+    public CSNVector()
+    {
+    }
+
+    /**
+     * Adds the specified <tt>csn</tt> to this vector.  If there's a
+     * {@link CSN} with the same {@link ReplicaId}, it is replaced by
+     * the specified <tt>csn</tt>.
+     */
+    public void setCSN( CSN csn )
+    {
+        csns.put( csn.getReplicaId(), csn );
+    }
+
+
+    /**
+     * Adds all {@link CSN}s that the specified <tt>vector</tt> contains to
+     * this vector.  If there's a {@link CSN} with the same {@link ReplicaId}
+     * in this vector, it is replaced by the {@link CSN} in the specified
+     * <tt>vector</tt>.
+     */
+    public void setAllCSN( CSNVector vector )
+    {
+        Iterator<CSN> i = vector.csns.values().iterator();
+        while ( i.hasNext() )
+        {
+            setCSN( i.next() );
+        }
+    }
+
+    /**
+     * Returns the {@link CSN} whith the specified <tt>replicaId</tt> from
+     * this vector.
+     * 
+     * @return <tt>null</tt> if there's no match
+     */
+    public CSN getCSN( ReplicaId replicaId )
+    {
+        return csns.get( replicaId );
+    }
+
+
+    /**
+     * Removed the {@link CSN} whith the specified <tt>replicaId</tt> from
+     * this vector and returns the removed {@link CSN}.
+     * 
+     * @return <tt>null</tt> if there's no match
+     */
+    public CSN removeCSN( ReplicaId replicaId )
+    {
+        return csns.remove( replicaId );
+    }
+
+
+    /**
+     * Returns the {@link Set} of the {@link ReplicaId}s extracted from
+     * the {@link CSN}s in this vector.
+     */
+    public Set<ReplicaId> getReplicaIds()
+    {
+        return csns.keySet();
+    }
+
+    /**
+     * Returns the number of {@link CSN}s that this vector contains.
+     */
+    public int size()
+    {
+        return csns.size();
+    }
+
+    /**
+     * Returns <tt>true</tt> if and if only the specified <tt>object</tt> is
+     * a {@link CSNVector} and contains the {@link CSN}s with the same values.
+     */
+    public boolean equals( Object object )
+    {
+        if ( object == this )
+        {
+            return true;
+        }
+        if ( !( object instanceof CSNVector ) )
+        {
+            return false;
+        }
+        CSNVector rhs = ( CSNVector ) object;
+        return new EqualsBuilder().append( this.csns, rhs.csns ).isEquals();
+    }
+
+    /**
+     * Returns the hash code of this vector, calculated from each {@link CSN}
+     * element. 
+     * @return the instance's hashcode 
+     */
+    public int hashCode()
+    {
+        return new HashCodeBuilder( -33446267, -459427867 ).append( this.csns ).toHashCode();
+    }
+
+    /**
+     * Creates a deep copy of this vector and returns it.
+     */
+    public CSNVector clone()
+    {
+        CSNVector result = new CSNVector();
+        result.csns.putAll( this.csns );
+        return result;
+    }
+
+    public String toString()
+    {
+        return csns.toString();
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/common/Constants.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/common/Constants.java
new file mode 100644
index 0000000..7242ff8
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/common/Constants.java
@@ -0,0 +1,78 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.common;
+
+
+import javax.naming.NamingException;
+import javax.naming.directory.SearchControls;
+
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerSearchResult;
+import org.apache.directory.server.core.enumeration.SearchResultFilter;
+import org.apache.directory.server.core.invocation.Invocation;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+
+
+/**
+ * Defines constant values used by Mitosis.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class Constants
+{
+    /**
+     * The name of the attribute that represents the {@link UUID} of an
+     * LDAP entry.
+     */
+    public static final String ENTRY_UUID = "entryUUID";
+    
+    /**
+     * The name of the attribute that represents the {@link CSN} of an LDAP
+     * entry.
+     */
+    public static final String ENTRY_CSN = "entryCSN";
+    
+    /**
+     * The name of the attribute that determines if an entry is actually
+     * deleted or not (even if it exists in a DIT.)
+     */
+    public static final String ENTRY_DELETED = "entryDeleted";
+    
+    /**
+     * A {@link SearchResultFilter} that filters out the entries whose
+     * {@link #ENTRY_DELETED} attribute is <tt>TRUE</tt>.
+     */
+    public static final SearchResultFilter DELETED_ENTRIES_FILTER = new SearchResultFilter()
+    {
+        public boolean accept( Invocation invocation, ServerSearchResult result, SearchControls controls )
+            throws NamingException
+        {
+            ServerEntry entry = result.getServerEntry();
+            EntryAttribute deleted = entry.get( ENTRY_DELETED );
+            Object value = deleted == null ? null : deleted.get();
+            return ( value == null || !"TRUE".equalsIgnoreCase( value.toString() ) );
+        }
+    };
+
+
+    private Constants()
+    {
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/common/DefaultCSN.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/common/DefaultCSN.java
new file mode 100644
index 0000000..1d06bc2
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/common/DefaultCSN.java
@@ -0,0 +1,340 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.common;
+
+
+import java.io.Serializable;
+
+import org.apache.directory.mitosis.util.OctetString;
+
+
+/**
+ * A default implementation of {@link CSN}.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class DefaultCSN implements CSN, Serializable, Comparable
+{
+    /**
+     * Declares the Serial Version Uid.
+     *
+     * @see <a
+     *      href="http://c2.com/cgi/wiki?AlwaysDeclareSerialVersionUid">Always
+     *      Declare Serial Version Uid</a>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /** The timeStamp of this operation */
+    private final long timestamp;
+
+    /** The server identification */
+    private final ReplicaId replicaId;
+
+    /** The operation number in the same timestamp */
+    private final int operationSequence;
+
+    /** Stores the String representation of the CSN */
+    private transient String octetString;
+
+    /** Stores the byte array representation of the CSN */
+    private transient byte[] bytes;
+
+
+    /**
+     * Creates a new instance.
+     * 
+     * @param timestamp GMT timestamp of modification
+     * @param replicaId Replica ID where modification occurred (<tt>[-_A-Za-z0-9]{1,16}</tt>)
+     * @param operationSequence Operation sequence
+     */
+    public DefaultCSN( long timestamp, ReplicaId replicaId, int operationSequence )
+    {
+        this.timestamp = timestamp;
+        this.replicaId = replicaId;
+        this.operationSequence = operationSequence;
+    }
+
+
+    /**
+     * Creates a new instance of SimpleCSN from a String.
+     * 
+     * The string format must be :
+     * &lt;timestamp> : &lt;replica ID> : &lt;operation sequence>
+     *
+     * @param value The String containing the CSN
+     */
+    public DefaultCSN( String value ) throws InvalidCSNException
+    {
+        assert value != null;
+
+        int sepTS = value.indexOf( ':' );
+
+        assert sepTS > 0;
+
+        int sepID = value.lastIndexOf( ':' );
+
+        if ( ( sepID == -1 ) || ( sepID == sepTS ) | ( sepID - sepTS < 2 ) )
+        {
+            throw new InvalidCSNException();
+        }
+
+        try
+        {
+            timestamp = Long.parseLong( value.substring( 0, sepTS ), 16 );
+        }
+        catch ( NumberFormatException ife )
+        {
+            throw new InvalidCSNException();
+        }
+
+        try
+        {
+            replicaId = new ReplicaId( value.substring( sepTS + 1, sepID ) );
+        }
+        catch ( IllegalArgumentException iae )
+        {
+            throw new InvalidCSNException();
+        }
+
+        try
+        {
+            operationSequence = Integer.parseInt( value.substring( sepID + 1 ), 16 );
+        }
+        catch ( NumberFormatException ife )
+        {
+            throw new InvalidCSNException();
+        }
+    }
+
+
+    /**
+     * Creates a new instance of SimpleCSN from the serialized data
+     *
+     * @param value The byte array which contains the serialized CSN
+     */
+    public DefaultCSN( byte[] value )
+    {
+        timestamp = ( ( long ) ( value[0] & 0x00FF ) << 56 ) | ( ( long ) ( value[1] & 0x00FF ) << 48 )
+            | ( ( long ) ( value[2] & 0x00FF ) << 40 ) | ( ( long ) ( value[3] & 0x00FF ) << 32 )
+            | ( ( value[4] << 24 ) & 0x00000000FF000000L ) | ( ( value[5] << 16 ) & 0x0000000000FF0000L )
+            | ( ( value[6] << 8 ) & 0x000000000000FF00L ) | ( value[7] & 0x00000000000000FFL );
+
+        operationSequence = ( ( value[8] & 0x00FF ) << 24 ) + ( ( value[9] & 0x00FF ) << 16 )
+            + ( ( value[10] & 0x00FF ) << 8 ) + ( value[11] & 0x00FF );
+
+        char[] chars = new char[value.length - 12];
+
+        for ( int i = 12; i < value.length; i++ )
+        {
+            chars[i - 12] = ( char ) ( value[i] & 0x00FF );
+        }
+
+        replicaId = new ReplicaId( new String( chars ) );
+        bytes = value;
+    }
+
+
+    /**
+     * Return the CSN as a formated string. The used format is :
+     * &lt;timestamp> ':' &lt;replicaId> ':' &lt;operation sequence>
+     * 
+     * @return The CSN as a String
+     */
+    public String toOctetString()
+    {
+        if ( octetString == null )
+        {
+            StringBuffer buf = new StringBuffer( 40 );
+            OctetString.append( buf, timestamp );
+            buf.append( ':' );
+            buf.append( replicaId );
+            buf.append( ':' );
+            OctetString.append( buf, operationSequence );
+            octetString = buf.toString();
+        }
+
+        return octetString;
+    }
+
+
+    /**
+     * Get the CSN as a byte array. The data are stored as :
+     * bytes 1 to 8  : timestamp, big-endian
+     * bytes 9 to 12 : operation sequence, big-endian
+     * bytes 13 to ... : ReplicaId 
+     * 
+     * @return A byte array representing theCSN
+     */
+    public byte[] toBytes()
+    {
+        if ( bytes == null )
+        {
+            String id = replicaId.getId();
+            byte[] bb = new byte[8 + id.length() + 4];
+
+            bb[0] = ( byte ) ( timestamp >> 56 );
+            bb[1] = ( byte ) ( timestamp >> 48 );
+            bb[2] = ( byte ) ( timestamp >> 40 );
+            bb[3] = ( byte ) ( timestamp >> 32 );
+            bb[4] = ( byte ) ( timestamp >> 24 );
+            bb[5] = ( byte ) ( timestamp >> 16 );
+            bb[6] = ( byte ) ( timestamp >> 8 );
+            bb[7] = ( byte ) timestamp;
+            bb[8] = ( byte ) ( ( operationSequence >> 24 ) );
+            bb[9] = ( byte ) ( ( operationSequence >> 16 ) );
+            bb[10] = ( byte ) ( ( operationSequence >> 8 ) );
+            bb[11] = ( byte ) ( operationSequence );
+
+            for ( int i = 0; i < id.length(); i++ )
+            {
+                bb[12 + i] = ( byte ) id.charAt( i );
+            }
+
+            bytes = bb;
+        }
+
+        return bytes;
+    }
+
+
+    /**
+     * @return The timestamp
+     */
+    public long getTimestamp()
+    {
+        return timestamp;
+    }
+
+
+    /**
+     * @return The replicaId
+     */
+    public ReplicaId getReplicaId()
+    {
+        return replicaId;
+    }
+
+
+    /**
+     * @return The operation sequence
+     */
+    public int getOperationSequence()
+    {
+        return operationSequence;
+    }
+
+
+    /**
+     * @return The CSN as a String
+     */
+    public String toString()
+    {
+        return toOctetString();
+    }
+
+
+    /**
+     * Returns a hash code value for the object.
+     * 
+     * @return a hash code value for this object.
+     */
+    public int hashCode()
+    {
+        return replicaId.hashCode() ^ ( int ) timestamp ^ operationSequence;
+    }
+
+
+    /**
+     * Indicates whether some other object is "equal to" this one
+     * 
+     * @param o the reference object with which to compare.
+     * @return <code>true</code> if this object is the same as the obj argument; 
+     * <code>false</code> otherwise.
+     */
+    public boolean equals( Object o )
+    {
+        if ( o == null )
+        {
+            return false;
+        }
+
+        if ( this == o )
+        {
+            return true;
+        }
+
+        if ( !( o instanceof CSN ) )
+        {
+            return false;
+        }
+
+        CSN that = ( CSN ) o;
+
+        return timestamp == that.getTimestamp() && replicaId.equals( that.getReplicaId() )
+            && operationSequence == that.getOperationSequence();
+    }
+
+
+    /**
+     * Compares this object with the specified object for order.  Returns a
+     * negative integer, zero, or a positive integer as this object is less
+     * than, equal to, or greater than the specified object.<p>
+     * 
+     * @param   o the Object to be compared.
+     * @return  a negative integer, zero, or a positive integer as this object
+     *      is less than, equal to, or greater than the specified object.
+     */
+    public int compareTo( Object o )
+    {
+        CSN that = ( CSN ) o;
+        long thatTimestamp = that.getTimestamp();
+
+        if ( this.timestamp < thatTimestamp )
+        {
+            return -1;
+        }
+        else if ( this.timestamp > thatTimestamp )
+        {
+            return 1;
+        }
+
+        int replicaIdCompareResult = this.replicaId.compareTo( that.getReplicaId() );
+
+        if ( replicaIdCompareResult != 0 )
+        {
+            return replicaIdCompareResult;
+        }
+
+        int thatSequence = that.getOperationSequence();
+
+        if ( this.operationSequence < thatSequence )
+        {
+            return -1;
+        }
+        else if ( this.operationSequence > thatSequence )
+        {
+            return 1;
+        }
+        else
+        {
+            return 0;
+        }
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/common/DefaultCSNFactory.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/common/DefaultCSNFactory.java
new file mode 100644
index 0000000..e7f57b7
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/common/DefaultCSNFactory.java
@@ -0,0 +1,63 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.common;
+
+
+/**
+ * A default {@link CSNFactory} implementation.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class DefaultCSNFactory implements CSNFactory
+{
+    private static int operationSequence;
+
+
+    public DefaultCSNFactory()
+    {
+    }
+
+
+    /**
+     * Returns a new {@link CSN}.
+     * Generated CSN can be duplicate if user generates CSNs more than 2G 
+     * times a milliseconds.
+     * 
+     * @param replicaId Replica ID.  ReplicaID must be 1-8 digit alphanumeric
+     *        string.
+     */
+    public CSN newInstance( String replicaId )
+    {
+        return newInstance( new ReplicaId( replicaId ) );
+    }
+
+
+    public synchronized CSN newInstance( ReplicaId replicaId )
+    {
+        long newTimestamp = System.currentTimeMillis();
+        if ( operationSequence == Integer.MAX_VALUE )
+        {
+            operationSequence = 0;
+        }
+
+        CSN newCSN = new DefaultCSN( newTimestamp, replicaId, operationSequence++ );
+        return newCSN;
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/common/DefaultUUID.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/common/DefaultUUID.java
new file mode 100644
index 0000000..e4e4e99
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/common/DefaultUUID.java
@@ -0,0 +1,103 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.common;
+
+
+import org.apache.directory.mitosis.util.OctetString;
+
+
+/**
+ * A default {@link UUID} implementation.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class DefaultUUID implements UUID
+{
+    private static final long serialVersionUID = 3256721788405953846L;
+
+    private final String uuid;
+
+
+    public DefaultUUID( byte[] uuid )
+    {
+        if ( uuid.length != 16 )
+        {
+            throw new IllegalArgumentException( "UUID must be 128-bits long." );
+        }
+
+        this.uuid = OctetString.toString( uuid );
+    }
+
+
+    public DefaultUUID( String uuid )
+    {
+        String newUUID = uuid.replaceAll( "[^0-9A-Za-z]", "" );
+
+        if ( newUUID.length() != 32 )
+        {
+            throw new IllegalArgumentException( "UUID: " + uuid );
+        }
+
+        this.uuid = newUUID;
+    }
+
+
+    /**
+     *Compute the instance's hash code 
+     * @return the instance's hashcode 
+     */
+    public int hashCode()
+    {
+        return uuid.hashCode();
+    }
+
+
+    public boolean equals( Object o )
+    {
+        if ( o == null )
+        {
+            return false;
+        }
+
+        if ( this == o )
+        {
+            return true;
+        }
+
+        if ( !( o instanceof UUID ) )
+        {
+            return false;
+        }
+
+        return uuid.equals( ( ( UUID ) o ).toOctetString() );
+    }
+
+
+    public String toOctetString()
+    {
+        return uuid;
+    }
+
+
+    public int compareTo( Object o )
+    {
+        return uuid.compareTo( ( ( UUID ) o ).toOctetString() );
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/common/DefaultUUIDFactory.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/common/DefaultUUIDFactory.java
new file mode 100644
index 0000000..e3c11b7
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/common/DefaultUUIDFactory.java
@@ -0,0 +1,49 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.common;
+
+
+import org.safehaus.uuid.UUIDGenerator;
+
+
+/**
+ * A default {@link UUIDFactory} implementation.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class DefaultUUIDFactory implements UUIDFactory
+{
+    private static final UUIDGenerator generator = UUIDGenerator.getInstance();
+
+
+    public DefaultUUIDFactory()
+    {
+    }
+
+
+    /**
+     * Generates a new type-1 (time-based) {@link UUID}.
+     */
+    public UUID newInstance()
+    {
+        org.safehaus.uuid.UUID uuid = generator.generateTimeBasedUUID();
+        return new DefaultUUID( uuid.asByteArray() );
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/common/InvalidCSNException.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/common/InvalidCSNException.java
new file mode 100644
index 0000000..46bf3b8
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/common/InvalidCSNException.java
@@ -0,0 +1,73 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.common;
+
+
+/**
+ * A {@link RuntimeException} which is thrown when a wrong string
+ * representation is specified to create a new {@link CSN}.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class InvalidCSNException extends RuntimeException
+{
+    /**
+     * Declares the Serial Version Uid.
+     *
+     * @see <a
+     *      href="http://c2.com/cgi/wiki?AlwaysDeclareSerialVersionUid">Always
+     *      Declare Serial Version Uid</a>
+     */
+    private static final long serialVersionUID = 1L;
+
+
+    /**
+     * Creates a new instance.
+     */
+    public InvalidCSNException()
+    {
+        super();
+    }
+
+    /**
+     * Creates a new instance with the specified <tt>message</tt> and
+     * <tt>cause</tt>.
+     */
+    public InvalidCSNException( String message, Throwable cause )
+    {
+        super( message, cause );
+    }
+
+    /**
+     * Creates a new instance with the specified <tt>message</tt>.
+     */
+    public InvalidCSNException( String message )
+    {
+        super( message );
+    }
+
+    /**
+     * Creates a new instance with the specified <tt>cause</tt>.
+     */
+    public InvalidCSNException( Throwable cause )
+    {
+        super( cause );
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/common/Replica.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/common/Replica.java
new file mode 100644
index 0000000..3deeec4
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/common/Replica.java
@@ -0,0 +1,191 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.common;
+
+
+import java.net.InetSocketAddress;
+
+import org.apache.directory.shared.ldap.util.StringTools;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * The class stores a Replica, which is composed of an Id, a server and a port.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class Replica
+{
+    /** The logger */
+    private static Logger log = LoggerFactory.getLogger( Replica.class );
+
+    /** A speedup for logger */
+    private static final boolean IS_DEBUG = log.isDebugEnabled();
+
+    /** The replicaId */
+    private final ReplicaId id;
+
+    /** The server address */
+    private final InetSocketAddress address;
+
+
+    /**
+     * Creates a new instance of Replica, from a String.
+     * 
+     * The String format is the following :
+     * 
+     * <replicaId>@<server>:<port>
+     * 
+     * @param replica The replica to create
+     */
+    public Replica( String replica )
+    {
+        if ( StringTools.isEmpty( replica ) )
+        {
+            log.error( "Null or empty replica are not allowed" );
+            throw new IllegalArgumentException( "Null or empty Replica " );
+        }
+
+        replica = replica.trim();
+
+        int atPos = replica.indexOf( '@' );
+
+        if ( atPos <= 0 )
+        {
+            log.error( "The ReplicaId '@' element is missing in {}", replica );
+            throw new IllegalArgumentException( "Replica ID not found: " + replica );
+        }
+
+        int colonPos = replica.indexOf( ':', atPos );
+
+        if ( colonPos < 0 )
+        {
+            log.error( "Replica port not found in {}", replica );
+            throw new IllegalArgumentException( "Port number not found in replica : " + replica );
+        }
+
+        id = new ReplicaId( replica.substring( 0, atPos ) );
+        String server = replica.substring( atPos + 1, colonPos );
+        int port = -1;
+
+        try
+        {
+            port = Integer.parseInt( replica.substring( colonPos + 1 ) );
+        }
+        catch ( NumberFormatException nfe )
+        {
+            log.error( "The port value should be a value between 1 and 65535, port  : {}", new Integer( port ) );
+            throw new IllegalArgumentException( "Bad port number : " + port );
+        }
+
+        try
+        {
+            address = new InetSocketAddress( server, port );
+        }
+        catch ( IllegalArgumentException iae )
+        {
+            log.error( "The server address/name is invalid ({}) in replica {}", server, replica );
+            throw new IllegalArgumentException( "The server address/name is invalid in replica " + replica
+                + ", error : " + iae.getMessage() );
+        }
+
+        if ( IS_DEBUG )
+        {
+            log.debug( "Created a replica {} on server {}", id, server + ':' + port );
+        }
+    }
+
+
+    /**
+     * Creates a new instance of Replica, from a valid Id and a valid address.
+     *
+     * @param id The Replica Id
+     * @param address The server address.
+     */
+    public Replica( ReplicaId id, InetSocketAddress address )
+    {
+        assert id != null;
+        assert address != null;
+
+        this.id = id;
+        this.address = address;
+    }
+
+
+    /**
+     * @return the replica address
+     */
+    public InetSocketAddress getAddress()
+    {
+        return address;
+    }
+
+
+    /**
+     * @return the replica Id
+     */
+    public ReplicaId getId()
+    {
+        return id;
+    }
+
+
+    /**
+     * Compute the instance's hash code
+     * @return the instance's hashcode 
+     */
+    public int hashCode()
+    {
+        return id.hashCode();
+    }
+
+
+    public boolean equals( Object o )
+    {
+        if ( o == null )
+        {
+            return false;
+        }
+
+        if ( o == this )
+        {
+            return true;
+        }
+
+        if ( o instanceof Replica )
+        {
+            return this.id.equals( ( ( Replica ) o ).id );
+        }
+        else
+        {
+            return false;
+        }
+    }
+
+
+    /**
+     * @return The replica. The format is &lt;replica id> '@' &lt;server> ':' &lt;port>
+     */
+    public String toString()
+    {
+        return getId().toString() + '@' + getAddress().getAddress().getHostAddress() + ':' + getAddress().getPort();
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/common/ReplicaId.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/common/ReplicaId.java
new file mode 100644
index 0000000..91a2c00
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/common/ReplicaId.java
@@ -0,0 +1,158 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.common;
+
+
+import java.io.Serializable;
+import java.util.regex.Pattern;
+
+import org.apache.directory.shared.ldap.util.StringTools;
+
+
+/**
+ * Store a replica ID after having normalized it.
+ * 
+ *  The normalization proces checks that the submitted id is valid, ie
+ *  contains only this char set : { '-', '_', 'a..z', 'A..Z', '0..9' }
+ *  and its length is between 1 and 16. 
+ *
+ * @org.apache.xbean.XBean
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class ReplicaId implements Comparable, Serializable
+{
+    /**
+     * Declares the Serial Version Uid.
+     *
+     * @see <a
+     *      href="http://c2.com/cgi/wiki?AlwaysDeclareSerialVersionUid">Always
+     *      Declare Serial Version Uid</a>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /** The replica pattern. */
+    private static final Pattern REPLICA_ID_PATTERN = Pattern.compile( "[-_A-Z0-9]{1,16}" );
+
+    /** The formated replicaId */
+    private final String id;
+
+
+    /**
+     * Creates a new instance of ReplicaId. The id must be a String 
+     * which respect the pattern :
+     * 
+     * [-_a-zA-Z0-9]*
+     * 
+     * and must be between 1 and 16 chars length
+     *
+     * @param id The replica pattern
+     */
+    public ReplicaId( String id )
+    {
+        if ( StringTools.isEmpty( id ) )
+        {
+            throw new IllegalArgumentException( "Empty ID: " + id );
+        }
+
+        String tmpId = id.trim().toUpperCase();
+
+        if ( !REPLICA_ID_PATTERN.matcher( tmpId ).matches() )
+        {
+            throw new IllegalArgumentException( "Invalid replica ID: " + id );
+        }
+
+        this.id = id;
+    }
+
+
+    /**
+     * @return The replicaId
+     */
+    public String getId()
+    {
+        return id;
+    }
+
+
+    /**
+     * Returns a hash code value for the object.
+     * 
+     * @return a hash code value for this object.
+     */
+    public int hashCode()
+    {
+        return id.hashCode();
+    }
+
+
+    /**
+     * Indicates whether some other object is "equal to" this one
+     * 
+     * @param o the reference object with which to compare.
+     * @return <code>true</code> if this object is the same as the obj argument; 
+     * <code>false</code> otherwise.
+     */
+    public boolean equals( Object o )
+    {
+        if ( o == null )
+        {
+            return false;
+        }
+
+        if ( o == this )
+        {
+            return true;
+        }
+
+        if ( o instanceof ReplicaId )
+        {
+            return this.id.equals( ( ( ReplicaId ) o ).id );
+        }
+        else
+        {
+            return false;
+        }
+    }
+
+
+    /**
+     * Compares this object with the specified object for order.  Returns a
+     * negative integer, zero, or a positive integer as this object is less
+     * than, equal to, or greater than the specified object.<p>
+     * 
+     * @param   o the Object to be compared.
+     * @return  a negative integer, zero, or a positive integer as this object
+     *      is less than, equal to, or greater than the specified object.
+     */
+    public int compareTo( Object o )
+    {
+        return this.id.compareTo( ( ( ReplicaId ) o ).id );
+    }
+
+
+    /**
+     * @return the Replica Id
+     */
+    public String toString()
+    {
+        return id;
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/common/ReplicationException.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/common/ReplicationException.java
new file mode 100644
index 0000000..f63c1be
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/common/ReplicationException.java
@@ -0,0 +1,65 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.common;
+
+
+/**
+ * A {@link RuntimeException} which if thrown when a problem occurred during
+ * the replication process.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class ReplicationException extends RuntimeException
+{
+    private static final long serialVersionUID = -347196060295426926L;
+
+    /**
+     * Creates a new instance.
+     */
+    public ReplicationException()
+    {
+        super();
+    }
+
+    /**
+     * Creates a new instance with the specified <tt>message</tt> and
+     * <tt>cause</tt>.
+     */
+    public ReplicationException( String message, Throwable cause )
+    {
+        super( message, cause );
+    }
+
+    /**
+     * Creates a new instance with the specified <tt>message</tt>.
+     */
+    public ReplicationException( String message )
+    {
+        super( message );
+    }
+
+    /**
+     * Creates a new instance with the specified <tt>cause</tt>.
+     */
+    public ReplicationException( Throwable cause )
+    {
+        super( cause );
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/common/UUID.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/common/UUID.java
new file mode 100644
index 0000000..31fa6f9
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/common/UUID.java
@@ -0,0 +1,37 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.common;
+
+
+import java.io.Serializable;
+
+
+/**
+ * Represents UUID (Unversally Unique IDentifier).
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public interface UUID extends Serializable, Comparable
+{
+    /**
+     * Returns octet-string representation of this UUID.
+     */
+    String toOctetString();
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/common/UUIDFactory.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/common/UUIDFactory.java
new file mode 100644
index 0000000..f23a5f7
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/common/UUIDFactory.java
@@ -0,0 +1,37 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.common;
+
+
+/**
+ * Generates {@link UUID}.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public interface UUIDFactory
+{
+    /**
+     * 
+     * Create a new UUID
+     *
+     * @return The created UUID
+     */
+    UUID newInstance();
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/configuration/ReplicaIdPropertyEditor.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/configuration/ReplicaIdPropertyEditor.java
new file mode 100644
index 0000000..58851cd
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/configuration/ReplicaIdPropertyEditor.java
@@ -0,0 +1,76 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.configuration;
+
+
+import java.beans.PropertyEditor;
+import java.beans.PropertyEditorSupport;
+
+import org.apache.directory.mitosis.common.ReplicaId;
+
+
+/**
+ * A {@link PropertyEditor} that converts strings into {@link ReplicaId}s
+ * and vice versa.
+ *
+ * @author The Apache Directory Project (dev@directory.apache.org)
+ * @version $Rev: 95 $, $Date: 2006-09-16 13:04:28 +0200 (Sat, 16 Sep 2006) $
+ */
+public class ReplicaIdPropertyEditor extends PropertyEditorSupport
+{
+    public ReplicaIdPropertyEditor()
+    {
+        super();
+    }
+
+
+    public ReplicaIdPropertyEditor( Object source )
+    {
+        super( source );
+    }
+
+
+    public String getAsText()
+    {
+        Object val = getValue();
+        if ( val == null )
+        {
+            return "";
+        }
+        else
+        {
+            return val.toString();
+        }
+    }
+
+
+    public void setAsText( String text ) throws IllegalArgumentException
+    {
+        text = text.trim();
+        if ( text.length() == 0 )
+        {
+            setValue( null );
+        }
+        else
+        {
+            setValue( new ReplicaId( text ) );
+        }
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/configuration/ReplicaPropertyEditor.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/configuration/ReplicaPropertyEditor.java
new file mode 100644
index 0000000..3256d7e
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/configuration/ReplicaPropertyEditor.java
@@ -0,0 +1,68 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.configuration;
+
+
+import java.beans.PropertyEditor;
+import java.beans.PropertyEditorSupport;
+
+import org.apache.directory.mitosis.common.Replica;
+
+
+/**
+ * A {@link PropertyEditor} that converts strings into {@link Replica}s
+ * and vice versa.
+ *
+ * @author The Apache Directory Project (dev@directory.apache.org)
+ * @version $Rev: 95 $, $Date: 2006-09-16 13:04:28 +0200 (Sat, 16 Sep 2006) $
+ */
+public class ReplicaPropertyEditor extends PropertyEditorSupport
+{
+    public ReplicaPropertyEditor()
+    {
+        super();
+    }
+
+
+    public ReplicaPropertyEditor( Object source )
+    {
+        super( source );
+    }
+
+
+    public String getAsText()
+    {
+        Object value = getValue();
+        if ( value == null )
+        {
+            return "";
+        }
+
+        Replica replica = ( Replica ) value;
+        return replica.getId().toString() + '@' + replica.getAddress().getAddress().getHostAddress() + ':'
+            + replica.getAddress().getPort();
+    }
+
+
+    public void setAsText( String text ) throws IllegalArgumentException
+    {
+        setValue( new Replica( text ) );
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/configuration/ReplicationConfiguration.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/configuration/ReplicationConfiguration.java
new file mode 100644
index 0000000..248c2ac
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/configuration/ReplicationConfiguration.java
@@ -0,0 +1,445 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.configuration;
+
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.apache.directory.mitosis.common.CSN;
+import org.apache.directory.mitosis.common.CSNFactory;
+import org.apache.directory.mitosis.common.Replica;
+import org.apache.directory.mitosis.common.ReplicaId;
+import org.apache.directory.mitosis.common.DefaultCSNFactory;
+import org.apache.directory.mitosis.common.DefaultUUIDFactory;
+import org.apache.directory.mitosis.common.UUID;
+import org.apache.directory.mitosis.common.UUIDFactory;
+import org.apache.directory.mitosis.service.ReplicationInterceptor;
+import org.apache.directory.mitosis.store.ReplicationStore;
+import org.apache.directory.mitosis.store.derby.DerbyReplicationStore;
+import org.apache.directory.shared.ldap.util.StringTools;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A configuration for {@link ReplicationInterceptor}.  This configuration can be
+ * used by calling {@link ReplicationInterceptor#setConfiguration(ReplicationConfiguration)}.
+ * 
+ * @org.apache.xbean.XBean
+ *
+ * @author The Apache Directory Project Team
+ */
+public class ReplicationConfiguration
+{
+    public static final int DEFAULT_LOG_MAX_AGE = 7;
+    public static final int DEFAULT_REPLICATION_INTERVAL = 5;
+    public static final int DEFAULT_RESPONSE_TIMEOUT = 60;
+    public static final int DEFAULT_SERVER_PORT = 7846;
+
+    /** The logger */
+    private static Logger log = LoggerFactory.getLogger( ReplicationConfiguration.class );
+
+    /** The server identifier */ 
+    private ReplicaId replicaId;
+    
+    /** Default values for the communication part */
+    private int serverPort = DEFAULT_SERVER_PORT;
+    private int responseTimeout = DEFAULT_RESPONSE_TIMEOUT;
+    private int replicationInterval = DEFAULT_REPLICATION_INTERVAL;
+
+    /** List of connected replicas */
+    private final Set<Replica> peerReplicas = new HashSet<Replica>();
+    
+    /** Factories */
+    private UUIDFactory uuidFactory = new DefaultUUIDFactory();
+    private CSNFactory csnFactory = new DefaultCSNFactory();
+    private ReplicationStore store = new DerbyReplicationStore();
+    
+    /** The longest period of time before a stored entry is removed from storage */
+    private int logMaxAge = DEFAULT_LOG_MAX_AGE; // a week (days)
+
+
+    /**
+     * Creates a new instance with default properties except for
+     * the {@link ReplicaId} of the service and the  the list of peer
+     * {@link Replica}s.  You can set these properties by calling
+     * {@link #setReplicaId(ReplicaId)} and {@link #setPeerReplicas(Set)}
+     * respectively.
+     */
+    public ReplicationConfiguration()
+    {
+    }
+
+    /**
+     * Returns the TCP/IP port number that a {@link ReplicationInterceptor}
+     * listens to.  The default value is {@link #DEFAULT_SERVER_PORT}. 
+     */
+    public int getServerPort()
+    {
+        return serverPort;
+    }
+
+
+    /**
+     * Sets the TCP/IP port number that a {@link ReplicationInterceptor}
+     * listens to.  The default value is {@link #DEFAULT_SERVER_PORT}.
+     */
+    public void setServerPort( int serverPort )
+    {
+        this.serverPort = serverPort;
+    }
+
+    /**
+     * Returns the response timeout value (seconds) for each sent message
+     * during the communication between replicas.  If any response message
+     * is not received within this timeout, the connection is closed and
+     * reestablished.  The default value is {@link #DEFAULT_RESPONSE_TIMEOUT}.
+     */
+    public int getResponseTimeout()
+    {
+        return responseTimeout;
+    }
+
+    /**
+     * Sets the response timeout value (seconds) for each sent message
+     * during the communication between replicas.  If any response message
+     * is not received within this timeout, the connection is closed and
+     * reestablished.  The default value is {@link #DEFAULT_RESPONSE_TIMEOUT}.
+     */
+    public void setResponseTimeout( int responseTimeout )
+    {
+        this.responseTimeout = responseTimeout;
+    }
+
+    /**
+     * Returns the replication data exchange interval (seconds) between two
+     * replicas. The default value is {@link #DEFAULT_REPLICATION_INTERVAL}.
+     * 
+     * @return <tt>0</tt> if automatic replication is disabled
+     */
+    public int getReplicationInterval() {
+        return replicationInterval;
+    }
+
+
+    /**
+     * Sets the replication data exchange interval (seconds) between two
+     * replicas. The default value is {@link #DEFAULT_REPLICATION_INTERVAL}.
+     * 
+     * @param replicationInterval <tt>0</tt> or below to disable automatic replication.
+     */
+    public void setReplicationInterval( int replicationInterval )
+    {
+        if( replicationInterval < 0 )
+        {
+            replicationInterval = 0;
+        }
+        this.replicationInterval = replicationInterval;
+    }
+
+
+    /**
+     * Returns the {@link CSNFactory} for generating {@link CSN}s.
+     * The default factory is {@link DefaultCSNFactory}.
+     */
+    public CSNFactory getCsnFactory()
+    {
+        return csnFactory;
+    }
+
+
+    /**
+     * Sets the {@link CSNFactory} for generating {@link CSN}s.
+     * The default factory is {@link DefaultCSNFactory}.
+     */
+    public void setCsnFactory( CSNFactory csnFactory )
+    {
+        this.csnFactory = csnFactory;
+    }
+
+    /**
+     * Adds the specified {@link Replica} to the remote peer replica list.
+     */
+    public void addPeerReplica( Replica peer )
+    {
+        assert peer != null;
+        peerReplicas.add( peer );
+    }
+
+    /**
+     * Removed the specified {@link Replica} from the remote peer replica list.
+     */
+    public void removePeerReplica( Replica peer )
+    {
+        assert peer != null;
+        peerReplicas.remove( peer );
+    }
+
+    /**
+     * Clears the remote peer replica list.
+     */
+    public void removeAllPeerReplicas()
+    {
+        peerReplicas.clear();
+    }
+
+    /**
+     * Returns the remote peer replica list.
+     */
+    public Set<Replica> getPeerReplicas()
+    {
+        Set<Replica> result = new HashSet<Replica>();
+        result.addAll( peerReplicas );
+        return result;
+    }
+
+
+    /**
+     * Sets the remote peer replica list.
+     */
+    public void setPeerReplicas( Set replicas )
+    {
+        assert replicas != null;
+
+        Set<Replica> normalizedReplicas = new HashSet<Replica>();
+        Iterator i = replicas.iterator();
+        
+        while ( i.hasNext() )
+        {
+            Object o = i.next();
+            
+            if ( o instanceof Replica )
+            {
+                normalizedReplicas.add( ( Replica ) o );
+            }
+            else
+            {
+                normalizedReplicas.add( new Replica( o.toString() ) );
+            }
+        }
+        
+        peerReplicas.clear();
+        peerReplicas.addAll( normalizedReplicas );
+    }
+
+    /**
+     * Returns the ID of the replica this configuration is configuring.
+     */
+    public ReplicaId getReplicaId()
+    {
+        return replicaId;
+    }
+
+    /**
+     * Sets the ID of the replica this configuration is configuring.
+     */
+    public void setReplicaId( ReplicaId replicaId )
+    {
+        assert replicaId != null;
+        this.replicaId = replicaId;
+    }
+
+    /**
+     * Returns the {@link ReplicationStore} which stores the change log
+     * of the replica this configuration is configuring.  The default
+     * implementation is {@link DerbyReplicationStore}.
+     */
+    public ReplicationStore getStore()
+    {
+        return store;
+    }
+
+    /**
+     * Sets the {@link ReplicationStore} which stores the change log
+     * of the replica this configuration is configuring.  The default
+     * implementation is {@link DerbyReplicationStore}.
+     */
+    public void setStore( ReplicationStore store )
+    {
+        this.store = store;
+    }
+
+    /**
+     * Returns the {@link UUIDFactory} which generates {@link UUID}s for
+     * new directory entries.  The default implementation is
+     * {@link DefaultUUIDFactory}.
+     */
+    public UUIDFactory getUuidFactory()
+    {
+        return uuidFactory;
+    }
+
+    /**
+     * Sets the {@link UUIDFactory} which generates {@link UUID}s for
+     * new directory entries.  The default implementation is
+     * {@link DefaultUUIDFactory}.
+     */
+    public void setUuidFactory( UUIDFactory uuidFactory )
+    {
+        this.uuidFactory = uuidFactory;
+    }
+
+    /**
+     * Returns the maximum age (days) of change logs stored in
+     * {@link ReplicationStore}.  Any change logs and deleted entries
+     * older than this value will be purged periodically.  The default value
+     * is {@link #DEFAULT_LOG_MAX_AGE}.
+     */
+    public int getLogMaxAge()
+    {
+        return logMaxAge;
+    }
+
+    /**
+     * Sets the maximum age (days) of change logs stored in
+     * {@link ReplicationStore}.  Any change logs and deleted entries
+     * older than this value will be purged periodically.  The default value
+     * is {@link #DEFAULT_LOG_MAX_AGE}.
+     */
+    public void setLogMaxAge( int logMaxAge )
+    {
+        if ( logMaxAge <= 0 )
+        {
+            throw new ReplicationConfigurationException( "logMaxAge: " + logMaxAge );
+        }
+
+        this.logMaxAge = logMaxAge;
+    }
+
+
+    /**
+     * Validate Mitosis configuration.
+     * 
+     * We check that the configuration file contains valid
+     * parameters :<br/>
+     *  - a replicaId<br/>
+     *  - a valid server port (between 0 and 65535)<br/>
+     *  - a valid response timeout ( > 0 )<br/>
+     *  - a uuidFactory<br/>
+     *  - a CSN factory<br/>
+     *  - a store (derby)<br/>
+     *  - a list of valid replica, none of them being equal
+     *  to the replicaId 
+     *
+     * @throws ReplicationConfigurationException If the configuration file is invalid
+     */
+    public void validate() throws ReplicationConfigurationException
+    {
+        if ( replicaId == null )
+        {
+            log.error( "The replicaId is missing" );
+            throw new ReplicationConfigurationException( "Replica ID is not specified." );
+        }
+
+        if ( ( serverPort < 0 ) || ( serverPort > 65535 ) )
+        {
+            log.error( "The replica port is not between 0 and 65535" );
+            throw new ReplicationConfigurationException( "Server port is invalid: " + serverPort );
+        }
+
+        if ( responseTimeout <= 0 )
+        {
+            log.error( "The replica responsetimeout is negative" );
+            throw new ReplicationConfigurationException( "Invalid response timeout: " + responseTimeout );
+        }
+
+        if ( uuidFactory == null )
+        {
+            log.error( "The UUID factory has not been declared" );
+            throw new ReplicationConfigurationException( "UUID factory is not specified." );
+        }
+
+        if ( csnFactory == null )
+        {
+            log.error( "The CSN factory has not been declared" );
+            throw new ReplicationConfigurationException( "CSN factory is not specified." );
+        }
+
+        if ( store == null )
+        {
+            log.error( "The store has not been declared" );
+            throw new ReplicationConfigurationException( "Replication store is not specified." );
+        }
+
+        if ( peerReplicas.size() == 0 )
+        {
+            log.error( "The replicas peer list is empty" );
+            throw new ReplicationConfigurationException( "No peer replicas" );
+        }
+
+        // Check the peer replicas.
+        // We should check that no replica has the same Id, and that we don't
+        // have two replicas on the same server with the same port
+        Set<String> ids = new TreeSet<String>();
+        Map<String, Integer> servers = new HashMap<String, Integer>();
+
+        // Initialize the set with this server replicaId
+        ids.add( replicaId.getId() );
+
+        // And store the local inetadress
+        servers.put( "localhost", serverPort );
+        servers.put( "127.0.0.1", serverPort );
+
+        try
+        {
+            servers.put( StringTools.lowerCase( InetAddress.getByName( "127.0.0.1" ).getHostName() ), serverPort );
+        }
+        catch ( UnknownHostException uhe )
+        {
+            // Should never occurs with 127.0.0.1
+            throw new ReplicationConfigurationException( "Unknown host name" );
+        }
+
+        for ( Replica peer:peerReplicas )
+        {
+            if ( ids.contains( peer.getId().getId() ) )
+            {
+                log.error( "Peer replica ID '{}' has already been declared.", peer.getId() );
+                throw new ReplicationConfigurationException( "Peer replica ID '" + peer.getId()
+                    + "' has already been declared." );
+            }
+
+            // Now check that we don't already have a replica on a server with the same port 
+            String replicaServer = StringTools.lowerCase( peer.getAddress().getHostName() );
+            int replicaPort = peer.getAddress().getPort();
+
+            if ( servers.containsKey( replicaServer ) )
+            {
+                int peerPort = servers.get( replicaServer );
+
+                if ( replicaPort == peerPort )
+                {
+                    log.error(
+                        "The replica in the peer list has already been declared on the server {} with the port {}",
+                        replicaServer, peerPort );
+                    throw new ReplicationConfigurationException( "Replication store is not specified." );
+                }
+            }
+
+            servers.put( replicaServer, replicaPort );
+        }
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/configuration/ReplicationConfigurationException.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/configuration/ReplicationConfigurationException.java
new file mode 100644
index 0000000..a013923
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/configuration/ReplicationConfigurationException.java
@@ -0,0 +1,66 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.configuration;
+
+
+import org.apache.directory.mitosis.common.ReplicationException;
+
+
+/**
+ * A {@link ReplicationException} which is thrown when there's an error
+ * in {@link ReplicationConfiguration}.
+ * 
+ * @author The Apache Directory Project Team
+ */
+public class ReplicationConfigurationException extends ReplicationException
+{
+    /**
+     * Declares the Serial Version Uid.
+     *
+     * @see <a
+     *      href="http://c2.com/cgi/wiki?AlwaysDeclareSerialVersionUid">Always
+     *      Declare Serial Version Uid</a>
+     */
+    private static final long serialVersionUID = 1L;
+
+
+    public ReplicationConfigurationException()
+    {
+        super();
+    }
+
+
+    public ReplicationConfigurationException( String message, Throwable cause )
+    {
+        super( message, cause );
+    }
+
+
+    public ReplicationConfigurationException( String message )
+    {
+        super( message );
+    }
+
+
+    public ReplicationConfigurationException( Throwable cause )
+    {
+        super( cause );
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/operation/AddAttributeOperation.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/operation/AddAttributeOperation.java
new file mode 100644
index 0000000..67f5e99
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/operation/AddAttributeOperation.java
@@ -0,0 +1,73 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.operation;
+
+
+import org.apache.directory.mitosis.common.CSN;
+import org.apache.directory.server.core.entry.DefaultServerEntry;
+import org.apache.directory.server.core.entry.ServerAttribute;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
+import org.apache.directory.server.core.partition.PartitionNexus;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.entry.Modification;
+import org.apache.directory.shared.ldap.entry.ModificationOperation;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+import javax.naming.NamingException;
+import java.util.List;
+
+
+/**
+ * An {@link Operation} that adds an attribute to an entry.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class AddAttributeOperation extends AttributeOperation
+{
+    private static final long serialVersionUID = 7373124294791982297L;
+
+
+    /**
+     * Creates a new operation that adds the specified attribute.
+     * 
+     * @param attribute an attribute to add
+     */
+    public AddAttributeOperation( CSN csn, LdapDN name, ServerAttribute attribute )
+    {
+        super( csn, name, attribute );
+    }
+
+
+    public String toString()
+    {
+        return super.toString() + ".add( " + getAttributeString() + " )";
+    }
+
+
+    protected void execute1( PartitionNexus nexus, Registries registries ) throws NamingException
+    {
+        ServerEntry serverEntry = new DefaultServerEntry( registries, LdapDN.EMPTY_LDAPDN );
+        ServerAttribute attribute = getAttribute( registries.getAttributeTypeRegistry() );
+        serverEntry.put( attribute );
+        List<Modification> items = ModifyOperationContext.createModItems( serverEntry, ModificationOperation.ADD_ATTRIBUTE );
+        nexus.modify( new ModifyOperationContext( registries, getName(), items ) );
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/operation/AddEntryOperation.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/operation/AddEntryOperation.java
new file mode 100644
index 0000000..f71078e
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/operation/AddEntryOperation.java
@@ -0,0 +1,122 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.operation;
+
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attributes;
+
+import org.apache.directory.mitosis.common.CSN;
+import org.apache.directory.mitosis.operation.support.EntryUtil;
+import org.apache.directory.mitosis.store.ReplicationStore;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerEntryUtils;
+import org.apache.directory.server.core.entry.ServerSearchResult;
+import org.apache.directory.server.core.interceptor.context.AddOperationContext;
+import org.apache.directory.server.core.interceptor.context.DeleteOperationContext;
+import org.apache.directory.server.core.interceptor.context.ListOperationContext;
+import org.apache.directory.server.core.interceptor.context.LookupOperationContext;
+import org.apache.directory.server.core.partition.PartitionNexus;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+
+/**
+ * An {@link Operation} that adds a new entry.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class AddEntryOperation extends Operation
+{
+    private static final long serialVersionUID = 2294492811671880570L;
+
+    /** The entry to add */
+    private final Attributes entry;
+    
+    /** The entry's dn */
+    private final LdapDN dn;
+
+
+    /**
+     * Creates a new instance.
+     * 
+     * @param entry an entry
+     */
+    public AddEntryOperation( CSN csn, ServerEntry entry )
+    {
+        super( csn );
+
+        assert entry != null;
+
+        this.entry = ServerEntryUtils.toAttributesImpl( entry );
+        this.dn = entry.getDn();
+    }
+
+
+    protected void execute0( PartitionNexus nexus, ReplicationStore store, Registries registries )
+        throws NamingException
+    {
+        if ( !EntryUtil.isEntryUpdatable( registries, nexus, dn, getCSN() ) )
+        {
+            return;
+        }
+        
+        EntryUtil.createGlueEntries( registries, nexus, dn, false );
+
+        // Replace the entry if an entry with the same name exists.
+        if ( nexus.lookup( new LookupOperationContext( registries, dn ) ) != null )
+        {
+            recursiveDelete( nexus, dn, registries );
+        }
+
+        nexus.add( new AddOperationContext( registries, ServerEntryUtils.toServerEntry( entry, dn, registries ) ) );
+    }
+
+
+    @SuppressWarnings("unchecked")
+    private void recursiveDelete( PartitionNexus nexus, LdapDN normalizedName, Registries registries )
+        throws NamingException
+    {
+        NamingEnumeration<ServerSearchResult> ne = nexus.list( new ListOperationContext( registries, normalizedName ) );
+        
+        if ( !ne.hasMore() )
+        {
+            nexus.delete( new DeleteOperationContext( registries, normalizedName ) );
+            return;
+        }
+
+        while ( ne.hasMore() )
+        {
+            ServerSearchResult sr = ne.next();
+            LdapDN dn = sr.getDn();
+            dn.normalize( registries.getAttributeTypeRegistry().getNormalizerMapping() );
+            recursiveDelete( nexus, dn, registries );
+        }
+        
+        nexus.delete( new DeleteOperationContext( registries, normalizedName ) );
+    }
+
+
+    public String toString()
+    {
+        return super.toString() + ": [" + dn + "].new( " + entry + " )";
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/operation/AttributeOperation.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/operation/AttributeOperation.java
new file mode 100644
index 0000000..28e7c0a
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/operation/AttributeOperation.java
@@ -0,0 +1,129 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.operation;
+
+
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.InvalidAttributeIdentifierException;
+
+import org.apache.directory.server.core.entry.ServerAttribute;
+import org.apache.directory.server.core.entry.ServerEntryUtils;
+import org.apache.directory.server.core.partition.PartitionNexus;
+import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.mitosis.common.CSN;
+import org.apache.directory.mitosis.operation.support.EntryUtil;
+import org.apache.directory.mitosis.store.ReplicationStore;
+
+
+/**
+ * An {@link Operation} that adds an attribute to an entry.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public abstract class AttributeOperation extends Operation
+{
+    private final LdapDN name;
+    private final Attribute attribute;
+    private transient ServerAttribute serverAttribute;
+
+
+    /**
+     * Create a new operation that affects an entry with the specified name.
+     * 
+     * @param csn
+     * @param name the normalized name of an entry 
+     * @param serverAttribute an attribute to modify
+     */
+    public AttributeOperation( CSN csn, LdapDN name, ServerAttribute serverAttribute )
+    {
+        super( csn );
+
+        assert name != null;
+        assert serverAttribute != null;
+
+        this.name = name;
+        this.serverAttribute = (ServerAttribute)serverAttribute.clone();
+        this.attribute = ServerEntryUtils.toAttributeImpl( this.serverAttribute );
+    }
+
+
+    /**
+     * Returns the attribute to modify.
+     */
+    public ServerAttribute getAttribute( AttributeTypeRegistry atRegistry ) throws InvalidAttributeIdentifierException, NamingException
+    {
+        if ( serverAttribute != null )
+        {
+            return ( ServerAttribute ) serverAttribute.clone();
+        }
+        else
+        {
+            Attribute attr = (Attribute)attribute.clone();
+            
+            serverAttribute = ServerEntryUtils.toServerAttribute( attr, atRegistry.lookup( attr.getID() ) );
+            return (ServerAttribute)serverAttribute.clone();
+        }
+    }
+
+
+    /**
+     * Returns the name of an entry this operation will affect.
+     */
+    public LdapDN getName()
+    {
+        return ( LdapDN ) name.clone();
+    }
+
+
+    protected final void execute0( PartitionNexus nexus, ReplicationStore store, Registries registries ) 
+        throws NamingException
+    {
+        if ( !EntryUtil.isEntryUpdatable( registries, nexus, name, getCSN() ) )
+        {
+            return;
+        }
+        EntryUtil.createGlueEntries( registries, nexus, name, true );
+
+        execute1( nexus, registries );
+    }
+
+
+    protected abstract void execute1( PartitionNexus nexus, Registries registries ) throws NamingException;
+
+
+    /**
+     * Returns the attribute to modify.
+     */
+    public String getAttributeString()
+    {
+        return attribute.toString();
+    }
+
+    /**
+     * Returns string representation of this operation.
+     */
+    public String toString()
+    {
+        return super.toString() + ": [" + name.toString() + ']';
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/operation/CompositeOperation.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/operation/CompositeOperation.java
new file mode 100644
index 0000000..ae0a726
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/operation/CompositeOperation.java
@@ -0,0 +1,177 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.operation;
+
+
+import org.apache.directory.mitosis.common.CSN;
+import org.apache.directory.mitosis.common.CSNVector;
+import org.apache.directory.mitosis.common.ReplicaId;
+import org.apache.directory.mitosis.common.UUID;
+import org.apache.directory.mitosis.configuration.ReplicationConfiguration;
+import org.apache.directory.mitosis.store.ReplicationLogIterator;
+import org.apache.directory.mitosis.store.ReplicationStore;
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.partition.PartitionNexus;
+import org.apache.directory.server.schema.registries.Registries;
+
+import javax.naming.Name;
+import javax.naming.NamingException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+
+/**
+ * An {@link Operation} that contains other {@link Operation}s.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class CompositeOperation extends Operation
+{
+    private static final long serialVersionUID = 6252675003841951356L;
+
+    private static final ReplicationStore DUMMY_STORE = new ReplicationStore()
+    {
+
+        public void open( DirectoryService directoryService, ReplicationConfiguration cfg )
+        {
+        }
+
+
+        public void close()
+        {
+        }
+
+
+        public ReplicaId getReplicaId()
+        {
+            return null;
+        }
+
+
+        public Set<ReplicaId> getKnownReplicaIds()
+        {
+            return null;
+        }
+
+
+        public Name getDN( UUID uuid )
+        {
+            return null;
+        }
+
+
+        public boolean putUUID( UUID uuid, Name dn )
+        {
+            return false;
+        }
+
+
+        public boolean removeUUID( UUID uuid )
+        {
+            return false;
+        }
+
+
+        public void putLog( Operation operation )
+        {
+        }
+
+
+        public ReplicationLogIterator getLogs( CSN fromCSN, boolean inclusive )
+        {
+            return null;
+        }
+
+
+        public ReplicationLogIterator getLogs( CSNVector updateVector, boolean inclusive )
+        {
+            return null;
+        }
+
+
+        public int removeLogs( CSN toCSN, boolean inclusive )
+        {
+            return 0;
+        }
+
+
+        public int getLogSize()
+        {
+            return 0;
+        }
+
+
+        public int getLogSize( ReplicaId replicaId )
+        {
+            return 0;
+        }
+
+
+        public CSNVector getUpdateVector()
+        {
+            return null;
+        }
+
+
+        public CSNVector getPurgeVector()
+        {
+            return null;
+        }
+    };
+
+    private final List<Operation> children = new ArrayList<Operation>();
+
+
+    public CompositeOperation( CSN csn )
+    {
+        super( csn );
+    }
+
+
+    public void add( Operation op )
+    {
+        assert op != null;
+        assert op.getCSN().equals( this.getCSN() );
+        children.add( op );
+    }
+
+
+    public void clear()
+    {
+        children.clear();
+    }
+
+
+    protected void execute0( PartitionNexus nexus, ReplicationStore store, Registries registries ) 
+        throws NamingException
+    {
+        for ( Operation op : children )
+        {
+            op.execute( nexus, DUMMY_STORE, registries );
+        }
+    }
+
+
+    public String toString()
+    {
+        return children.toString();
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/operation/DeleteAttributeOperation.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/operation/DeleteAttributeOperation.java
new file mode 100644
index 0000000..e3016c2
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/operation/DeleteAttributeOperation.java
@@ -0,0 +1,74 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.operation;
+
+
+import org.apache.directory.mitosis.common.CSN;
+import org.apache.directory.server.core.entry.DefaultServerEntry;
+import org.apache.directory.server.core.entry.ServerAttribute;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
+import org.apache.directory.server.core.partition.PartitionNexus;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.entry.Modification;
+import org.apache.directory.shared.ldap.entry.ModificationOperation;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+import javax.naming.NamingException;
+import java.util.List;
+
+
+/**
+ * An {@link Operation} that deletes an attribute from an entry.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class DeleteAttributeOperation extends AttributeOperation
+{
+    private static final long serialVersionUID = -131557844165710365L;
+
+
+    /**
+     * Creates a new operation that deletes the specified attribute.
+     * 
+     * @param attribute an attribute to delete
+     */
+    public DeleteAttributeOperation( CSN csn, LdapDN name, ServerAttribute attribute )
+    {
+        super( csn, name, attribute );
+    }
+
+
+    public String toString()
+    {
+        return super.toString() + ".delete( " + getAttributeString() + " )";
+    }
+
+
+    protected void execute1( PartitionNexus nexus, Registries registries ) throws NamingException
+    {
+        ServerEntry serverEntry = new DefaultServerEntry( registries, LdapDN.EMPTY_LDAPDN );
+        ServerAttribute attribute = getAttribute( registries.getAttributeTypeRegistry() );
+        serverEntry.put( attribute );
+        List<Modification> items = ModifyOperationContext.createModItems( serverEntry, ModificationOperation.REMOVE_ATTRIBUTE );
+
+        nexus.modify( new ModifyOperationContext( registries, getName(), items ) );
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/operation/Operation.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/operation/Operation.java
new file mode 100644
index 0000000..b799643
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/operation/Operation.java
@@ -0,0 +1,119 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.operation;
+
+
+import java.io.Serializable;
+
+import javax.naming.NamingException;
+import javax.naming.OperationNotSupportedException;
+
+import org.apache.directory.mitosis.common.CSN;
+import org.apache.directory.mitosis.common.Constants;
+import org.apache.directory.mitosis.store.ReplicationStore;
+import org.apache.directory.server.core.partition.Partition;
+import org.apache.directory.server.core.partition.PartitionNexus;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+
+/**
+ * Represents an operation performed on one or more entries in replicated
+ * {@link Partition}.  Each {@link Operation} has its own {@link CSN} which
+ * identifies itself.
+ * <p>
+ * An {@link Operation} is usually created by calling factory methods in
+ * {@link OperationFactory}, which produces a {@link CompositeOperation} of
+ * smaller multiple operations.  For example,
+ * {@link OperationFactory#newDelete(LdapDN)} returns a
+ * {@link CompositeOperation} which consists of two
+ * {@link ReplaceAttributeOperation}s; one updates {@link Constants#ENTRY_CSN}
+ * attribute and the other updates {@link Constants#ENTRY_DELETED}.  Refer
+ * to {@link OperationFactory} to find out what LDAP/JNDI operation is 
+ * translated into what {@link Operation} instance.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class Operation implements Serializable
+{
+    /**
+     * Declares the Serial Version Uid.
+     *
+     * @see <a
+     *      href="http://c2.com/cgi/wiki?AlwaysDeclareSerialVersionUid">Always
+     *      Declare Serial Version Uid</a>
+     */
+    private static final long serialVersionUID = 1L;
+
+    /** The entry CSN */
+    private CSN csn;
+
+
+    /**
+     * Creates a new instance of Operation, for the entry which
+     * csn is given as a parameter.
+     *
+     * @param csn The entry's csn.
+     */
+    public Operation( CSN csn )
+    {
+        assert csn != null;
+        this.csn = csn;
+    }
+
+
+    /**
+     * @return Returns {@link CSN} for this operation.
+     */
+    public CSN getCSN()
+    {
+        return csn;
+    }
+
+
+    /**
+     * @return the CSN for this operation
+     */
+    public String toString()
+    {
+        return csn.toString();
+    }
+
+
+    /**
+     * Executes this operation on the specified nexus.
+     */
+    public final void execute( PartitionNexus nexus, ReplicationStore store, Registries registries ) 
+        throws NamingException
+    {
+        synchronized ( nexus )
+        {
+            execute0( nexus, store, registries );
+            store.putLog( this );
+        }
+    }
+
+    @SuppressWarnings("unused")
+    protected void execute0( PartitionNexus nexus, ReplicationStore store, Registries registries ) 
+        throws NamingException
+    {
+        throw new OperationNotSupportedException( nexus.getSuffixDn().toString() );
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/operation/OperationCodec.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/operation/OperationCodec.java
new file mode 100644
index 0000000..f16b9c8
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/operation/OperationCodec.java
@@ -0,0 +1,87 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.operation;
+
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamConstants;
+
+
+/**
+ * Encodes {@link Operation}s to <tt>byte[]</tt> and vice versa so an
+ * {@link Operation} can be transferred via TCP/IP communication.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class OperationCodec
+{
+    public OperationCodec()
+    {
+    }
+
+
+    /**
+     * Transforms the specified {@link Operation} into a byte array.
+     */
+    public byte[] encode( Operation op )
+    {
+        ByteArrayOutputStream bout = new ByteArrayOutputStream();
+        try
+        {
+            ObjectOutputStream out = new ObjectOutputStream( bout );
+            out.useProtocolVersion( ObjectStreamConstants.PROTOCOL_VERSION_2 );
+            out.writeObject( op );
+            out.flush();
+            out.close();
+        }
+        catch ( IOException e )
+        {
+            throw ( InternalError ) new InternalError().initCause( e );
+        }
+        return bout.toByteArray();
+    }
+
+
+    /**
+     * Transforms the specified byte array into an {@link Operation}.
+     */
+    public Operation decode( byte[] data )
+    {
+        ObjectInputStream in;
+        
+        try
+        {
+            in = new ObjectInputStream( new ByteArrayInputStream( data ) );
+            return ( Operation ) in.readObject();
+        }
+        catch ( IOException e )
+        {
+            throw ( InternalError ) new InternalError().initCause( e );
+        }
+        catch ( ClassNotFoundException e )
+        {
+            throw ( InternalError ) new InternalError().initCause( e );
+        }
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/operation/OperationFactory.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/operation/OperationFactory.java
new file mode 100644
index 0000000..63f5202
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/operation/OperationFactory.java
@@ -0,0 +1,416 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.operation;
+
+import org.apache.directory.mitosis.common.CSN;
+import org.apache.directory.mitosis.common.CSNFactory;
+import org.apache.directory.mitosis.common.Constants;
+import org.apache.directory.mitosis.common.ReplicaId;
+import org.apache.directory.mitosis.common.UUIDFactory;
+import org.apache.directory.mitosis.configuration.ReplicationConfiguration;
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.entry.DefaultServerAttribute;
+import org.apache.directory.server.core.entry.ServerAttribute;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerSearchResult;
+import org.apache.directory.server.core.interceptor.context.EntryOperationContext;
+import org.apache.directory.server.core.interceptor.context.LookupOperationContext;
+import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
+import org.apache.directory.server.core.interceptor.context.SearchOperationContext;
+import org.apache.directory.server.core.partition.Partition;
+import org.apache.directory.server.core.partition.PartitionNexus;
+import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.entry.Modification;
+import org.apache.directory.shared.ldap.entry.ModificationOperation;
+import org.apache.directory.shared.ldap.filter.PresenceNode;
+import org.apache.directory.shared.ldap.message.AliasDerefMode;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.name.Rdn;
+
+import javax.naming.NameAlreadyBoundException;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.SearchControls;
+
+import java.util.List;
+
+
+/**
+ * Creates an {@link Operation} instance for a JNDI operation.  The
+ * {@link Operation} instance returned by the provided factory methods are
+ * mostly a {@link CompositeOperation}, which consists smaller JNDI
+ * operations. The elements of the {@link CompositeOperation} differs from
+ * the original JNDI operation to make the operation more robust to
+ * replication conflict.  All {@link Operation}s created by
+ * {@link OperationFactory} whould be robust to the replication conflict and
+ * should be able to recover from the conflict.
+ * <p>
+ * "Add" (or "bind") is the only operation that doesn't return a
+ * {@link CompositeOperation} but returns an {@link AddEntryOperation}.
+ * It is because all other operations needs to update its related entry's
+ * {@link Constants#ENTRY_CSN} or {@link Constants#ENTRY_DELETED} attribute
+ * with additional sub-operations.  In contrast, "add" operation doesn't need
+ * to create a {@link CompositeOperation} because those attributes can be
+ * added just modifying an {@link AddEntryOperation} rather than creating
+ * a parent operation and add sub-operations there.
+ * <p>
+ * Please note that all operations update {@link Constants#ENTRY_CSN} and
+ * documentation for each method won't explain this behavior.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class OperationFactory
+{
+    private final ReplicaId replicaId;
+    private final PartitionNexus nexus;
+    private final UUIDFactory uuidFactory;
+    private final CSNFactory csnFactory;
+    
+    /** The attributeType registry */
+    private final AttributeTypeRegistry attributeRegistry;
+
+    /** The global registries */
+    private Registries registries;
+
+    public OperationFactory( DirectoryService directoryService, ReplicationConfiguration cfg )
+    {
+        replicaId = cfg.getReplicaId();
+        nexus = directoryService.getPartitionNexus();
+        uuidFactory = cfg.getUuidFactory();
+        csnFactory = cfg.getCsnFactory();
+        registries = directoryService.getRegistries();
+        attributeRegistry = registries.getAttributeTypeRegistry();
+    }
+
+
+    /**
+     * Creates a new {@link Operation} that performs LDAP "add" operation
+     * with a newly generated {@link CSN}.
+     */
+    public Operation newAdd( LdapDN normalizedName, ServerEntry entry ) throws NamingException
+    {
+        return newAdd( newCSN(), normalizedName, entry );
+    }
+
+
+    /**
+     * Creates a new {@link Operation} that performs LDAP "add" operation
+     * with the specified {@link CSN}.  The new entry will have three
+     * additional attributes; {@link Constants#ENTRY_CSN} ({@link CSN}),
+     * {@link Constants#ENTRY_UUID}, and {@link Constants#ENTRY_DELETED}.
+     */
+    private Operation newAdd( CSN csn, LdapDN normalizedName, ServerEntry entry ) throws NamingException
+    {
+        // Check an entry already exists.
+        checkBeforeAdd( normalizedName );
+
+        // Insert 'entryUUID' and 'entryDeleted'.
+        ServerEntry cloneEntry = ( ServerEntry ) entry.clone();
+        cloneEntry.removeAttributes( Constants.ENTRY_UUID );
+        cloneEntry.removeAttributes( Constants.ENTRY_DELETED );
+        cloneEntry.put( Constants.ENTRY_UUID, uuidFactory.newInstance().toOctetString() );
+        cloneEntry.put( Constants.ENTRY_DELETED, "FALSE" );
+
+        // NOTE: We inlined addDefaultOperations() because ApacheDS currently
+        // creates an index entry only for ADD operation (and not for
+        // MODIFY operation)
+        cloneEntry.put( Constants.ENTRY_CSN, csn.toOctetString() );
+
+        return new AddEntryOperation( csn, cloneEntry );
+    }
+
+
+    /**
+     * Creates a new {@link Operation} that performs "delete" operation.
+     * The created {@link Operation} doesn't actually delete the entry.
+     * Instead, it sets {@link Constants#ENTRY_DELETED} to "TRUE". 
+     */
+    public Operation newDelete( LdapDN normalizedName ) throws NamingException
+    {
+        CSN csn = newCSN();
+        CompositeOperation result = new CompositeOperation( csn );
+
+        // Transform into replace operation.
+        result.add( new ReplaceAttributeOperation( csn, normalizedName, 
+            new DefaultServerAttribute( 
+                Constants.ENTRY_DELETED, 
+                attributeRegistry.lookup( Constants.ENTRY_DELETED ),
+                "TRUE" ) ) );
+
+        return addDefaultOperations( result, csn, normalizedName );
+    }
+
+
+    /**
+     * Returns a new {@link Operation} that performs "modify" operation.
+     * 
+     * @return a {@link CompositeOperation} that consists of one or more
+     * {@link AttributeOperation}s and one additional operation that
+     * sets {@link Constants#ENTRY_DELETED} to "FALSE" to resurrect the
+     * entry the modified attributes belong to.
+     */
+    public Operation newModify( ModifyOperationContext opContext ) throws NamingException
+    {
+        List<Modification> items = opContext.getModItems();
+        LdapDN normalizedName = opContext.getDn();
+
+        CSN csn = newCSN();
+        CompositeOperation result = new CompositeOperation( csn );
+        
+        // Transform into multiple {@link AttributeOperation}s.
+        for ( Modification item:items )
+        {
+            result.add( 
+                newModify( 
+                    csn, 
+                    normalizedName, 
+                    item.getOperation(), 
+                    (ServerAttribute)item.getAttribute() ) );
+        }
+
+        // Resurrect the entry in case it is deleted.
+        result.add( 
+            new ReplaceAttributeOperation( 
+                csn, 
+                normalizedName, 
+                new DefaultServerAttribute( 
+                    Constants.ENTRY_DELETED,
+                    attributeRegistry.lookup( Constants.ENTRY_DELETED ),
+                    "FALSE" ) ) );
+
+        return addDefaultOperations( result, csn, normalizedName );
+    }
+
+
+    /**
+     * Returns a new {@link AttributeOperation} that performs one 
+     * attribute modification operation.  This method is called by other
+     * methods internally to create an appropriate {@link AttributeOperation}
+     * instance from the specified <tt>modOp</tt> value.
+     */
+    private Operation newModify( CSN csn, LdapDN normalizedName, ModificationOperation modOp, ServerAttribute attribute )
+    {
+        switch ( modOp )
+        {
+            case ADD_ATTRIBUTE:
+                return new AddAttributeOperation( csn, normalizedName, attribute );
+            
+            case REPLACE_ATTRIBUTE:
+                return new ReplaceAttributeOperation( csn, normalizedName, attribute );
+            
+            case REMOVE_ATTRIBUTE:
+                return new DeleteAttributeOperation( csn, normalizedName, attribute );
+            
+            default:
+                throw new IllegalArgumentException( "Unknown modOp: " + modOp );
+        }
+    }
+
+
+    /**
+     * Returns a new {@link Operation} that performs "modifyRN" operation.
+     * This operation is a subset of "move" operation.
+     * Calling this method actually forwards the call to
+     * {@link #newMove(LdapDN, LdapDN, Rdn, boolean)} with unchanged
+     * <tt>newParentName</tt>. 
+     */
+    public Operation newModifyRn( LdapDN oldName, Rdn newRdn, boolean deleteOldRn ) throws NamingException
+    {
+        LdapDN newParentName = ( LdapDN ) oldName.clone();
+        newParentName.remove( oldName.size() - 1 );
+        
+        return newMove( oldName, newParentName, newRdn, deleteOldRn );
+    }
+
+
+    /**
+     * Returns a new {@link Operation} that performs "move" operation.
+     * Calling this method actually forwards the call to
+     * {@link #newMove(LdapDN, LdapDN, Rdn, boolean)} with unchanged
+     * <tt>newRdn</tt> and '<tt>true</tt>' <tt>deleteOldRn</tt>. 
+     */
+    public Operation newMove( LdapDN oldName, LdapDN newParentName ) throws NamingException
+    {
+        return newMove( oldName, newParentName, oldName.getRdn(), true );
+    }
+
+
+    /**
+     * Returns a new {@link Operation} that performs "move" operation.
+     * Please note this operation is the most fragile operation I've written
+     * so it should be reviewed completely again.
+     */
+    public Operation newMove( LdapDN oldName, LdapDN newParentName, Rdn newRdn, boolean deleteOldRn )
+        throws NamingException
+    {
+        // Prepare to create composite operations
+        CSN csn = newCSN();
+        CompositeOperation result = new CompositeOperation( csn );
+
+        // Retrieve all subtree including the base entry
+        SearchControls ctrl = new SearchControls();
+        ctrl.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        
+        NamingEnumeration<ServerSearchResult> e = nexus.search( 
+            new SearchOperationContext( registries, oldName, AliasDerefMode.DEREF_ALWAYS,
+                    new PresenceNode( SchemaConstants.OBJECT_CLASS_AT_OID ), ctrl ) );
+
+        while ( e.hasMore() )
+        {
+            ServerSearchResult sr = e.next();
+
+            // Get the name of the old entry
+            LdapDN oldEntryName = sr.getDn();
+            oldEntryName.normalize( attributeRegistry.getNormalizerMapping() );
+
+            // Delete the old entry
+            result.add( 
+                new ReplaceAttributeOperation( 
+                    csn, 
+                    oldEntryName, 
+                    new DefaultServerAttribute( 
+                        Constants.ENTRY_DELETED,
+                        attributeRegistry.lookup( Constants.ENTRY_DELETED ),
+                        "TRUE" ) ) );
+
+            // Get the old entry attributes and replace RDN if required
+            ServerEntry entry = sr.getServerEntry();
+            
+            if ( oldEntryName.size() == oldName.size() )
+            {
+                if ( deleteOldRn )
+                {
+                    // Delete the old RDN attribute value
+                    String oldRDNAttributeID = oldName.getRdn().getUpType();
+                    EntryAttribute oldRDNAttribute = entry.get( oldRDNAttributeID );
+                    
+                    if ( oldRDNAttribute != null )
+                    {
+                        boolean removed = oldRDNAttribute.remove( (String)oldName.getRdn().getUpValue() );
+                        
+                        if ( removed && oldRDNAttribute.size() == 0 )
+                        {
+                            // Now an empty attribute, remove it.
+                            entry.removeAttributes( oldRDNAttributeID );
+                        }
+                    }
+                }
+                
+                // Add the new RDN attribute value.
+                String newRDNAttributeID = newRdn.getUpType();
+                String newRDNAttributeValue = ( String ) newRdn.getUpValue();
+                EntryAttribute newRDNAttribute = entry.get( newRDNAttributeID );
+                
+                if ( newRDNAttribute != null )
+                {
+                    newRDNAttribute.add( newRDNAttributeValue );
+                }
+                else
+                {
+                    entry.put( newRDNAttributeID, newRDNAttributeValue );
+                }
+            }
+
+            // Calculate new name from newParentName, oldEntryName, and newRdn.
+            LdapDN newEntryName = ( LdapDN ) newParentName.clone();
+            newEntryName.add( newRdn );
+            
+            for ( int i = oldEntryName.size() - newEntryName.size(); i > 0; i-- )
+            {
+                newEntryName.add( oldEntryName.get( oldEntryName.size() - i ) );
+            }
+            
+            newEntryName.normalize( attributeRegistry.getNormalizerMapping() );
+
+            // Add the new entry
+            result.add( newAdd( csn, newEntryName, entry ) );
+
+            // Add default operations to the old entry.
+            // Please note that newAdd() already added default operations
+            // to the new entry. 
+            addDefaultOperations( result, csn, oldEntryName );
+        }
+
+        return result;
+    }
+
+
+    /**
+     * Make sure the specified <tt>newEntryName</tt> already exists.  It
+     * checked {@link Constants#ENTRY_DELETED} additionally to see if the
+     * entry actually exists in a {@link Partition} but maked as deleted.
+     *
+     * @param newEntryName makes sure an entry already exists.
+     */
+    private void checkBeforeAdd( LdapDN newEntryName ) throws NamingException
+    {
+        if ( nexus.hasEntry( new EntryOperationContext( registries, newEntryName ) ) )
+        {
+            ServerEntry entry = nexus.lookup( new LookupOperationContext( registries, newEntryName ) );
+            EntryAttribute deleted = entry.get( Constants.ENTRY_DELETED );
+            Object value = deleted == null ? null : deleted.get();
+
+            /*
+             * Check first if the entry has been marked as deleted before
+             * throwing an exception and delete the entry if so and return
+             * without throwing an exception.
+             */
+            if ( value != null && "TRUE".equalsIgnoreCase( value.toString() ) )
+            {
+                return;
+            }
+
+            throw new NameAlreadyBoundException( newEntryName.toString() + " already exists." );
+        }
+    }
+
+
+    /**
+     * Adds default {@link Operation}s that should be followed by all
+     * JNDI/LDAP operations except "add/bind" operation.  This method
+     * currently adds only one attribute, {@link Constants#ENTRY_CSN}.
+     * @return what you specified as a parameter to enable invocation chaining
+     */
+    private CompositeOperation addDefaultOperations( CompositeOperation result, CSN csn, LdapDN normalizedName ) throws NamingException
+    {
+        result.add( 
+            new ReplaceAttributeOperation( 
+                csn, 
+                normalizedName, 
+                new DefaultServerAttribute( 
+                    Constants.ENTRY_DELETED,
+                    attributeRegistry.lookup( Constants.ENTRY_CSN ),
+                    csn.toOctetString() ) ) );
+
+        return result;
+    }
+
+    /**
+     * Creates new {@link CSN} from the {@link CSNFactory} which was specified
+     * in the constructor.
+     */
+    private CSN newCSN()
+    {
+        return csnFactory.newInstance( replicaId );
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/operation/ReplaceAttributeOperation.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/operation/ReplaceAttributeOperation.java
new file mode 100644
index 0000000..97f2a21
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/operation/ReplaceAttributeOperation.java
@@ -0,0 +1,76 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.operation;
+
+
+import org.apache.directory.mitosis.common.CSN;
+import org.apache.directory.server.core.entry.DefaultServerEntry;
+import org.apache.directory.server.core.entry.ServerAttribute;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
+import org.apache.directory.server.core.partition.PartitionNexus;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.entry.Modification;
+import org.apache.directory.shared.ldap.entry.ModificationOperation;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+import javax.naming.NamingException;
+import java.util.List;
+
+
+/**
+ * An {@link Operation} that replaces an attribute in an entry.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class ReplaceAttributeOperation extends AttributeOperation
+{
+    private static final long serialVersionUID = -6573196586521610472L;
+
+
+    /**
+     * Creates a new operation that replaces the specified attribute.
+     * 
+     * @param attribute an attribute to replace
+     * @param csn ??
+     * @param name ??
+     */
+    public ReplaceAttributeOperation( CSN csn, LdapDN name, ServerAttribute attribute )
+    {
+        super( csn, name, attribute );
+    }
+
+
+    public String toString()
+    {
+        return super.toString() + ".replace( " + getAttributeString() + " )";
+    }
+
+
+    protected void execute1( PartitionNexus nexus, Registries registries ) throws NamingException
+    {
+        ServerEntry serverEntry = new DefaultServerEntry( registries, LdapDN.EMPTY_LDAPDN );
+        ServerAttribute attribute = getAttribute( registries.getAttributeTypeRegistry() );
+        serverEntry.put( attribute );
+        List<Modification> items = ModifyOperationContext.createModItems( serverEntry, ModificationOperation.REPLACE_ATTRIBUTE );
+
+        nexus.modify( new ModifyOperationContext( registries, getName(), items ) );
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/operation/support/EntryUtil.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/operation/support/EntryUtil.java
new file mode 100644
index 0000000..0478c89
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/operation/support/EntryUtil.java
@@ -0,0 +1,140 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.operation.support;
+
+
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+
+import org.apache.directory.server.core.entry.DefaultServerEntry;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.interceptor.context.AddOperationContext;
+import org.apache.directory.server.core.interceptor.context.EntryOperationContext;
+import org.apache.directory.server.core.interceptor.context.LookupOperationContext;
+import org.apache.directory.server.core.partition.PartitionNexus;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.util.NamespaceTools;
+import org.apache.directory.shared.ldap.util.StringTools;
+import org.apache.directory.mitosis.common.CSN;
+import org.apache.directory.mitosis.common.Constants;
+import org.apache.directory.mitosis.common.DefaultCSN;
+
+
+public class EntryUtil
+{
+    @SuppressWarnings("unchecked")
+    public static boolean isEntryUpdatable( Registries registries, PartitionNexus nexus, LdapDN name, CSN newCSN ) throws NamingException
+    {
+        ServerEntry entry = nexus.lookup( new LookupOperationContext( registries, name ) );
+
+        if ( entry == null )
+        {
+            return true;
+        }
+
+        EntryAttribute entryCSNAttr = entry.get( Constants.ENTRY_CSN );
+
+        if ( entryCSNAttr == null )
+        {
+            return true;
+        }
+        else
+        {
+            CSN oldCSN = null;
+
+            try
+            {
+                Object val = entryCSNAttr.get();
+                
+                if ( val instanceof byte[] )
+                {
+                    oldCSN = new DefaultCSN( StringTools.utf8ToString( (byte[])val ) );
+                }
+                else
+                {
+                    oldCSN = new DefaultCSN( (String)val );
+                }
+            }
+            catch ( IllegalArgumentException e )
+            {
+                return true;
+            }
+
+            return oldCSN.compareTo( newCSN ) < 0;
+        }
+    }
+
+
+    public static void createGlueEntries( Registries registries, PartitionNexus nexus, LdapDN name, boolean includeLeaf )
+        throws NamingException
+    {
+        assert name.size() > 0;
+
+        for ( int i = name.size() - 1; i > 0; i-- )
+        {
+            createGlueEntry( registries, nexus, ( LdapDN ) name.getSuffix( i ) );
+        }
+
+        if ( includeLeaf )
+        {
+            createGlueEntry( registries, nexus, name );
+        }
+    }
+
+
+    private static void createGlueEntry( Registries registries, PartitionNexus nexus, LdapDN name ) throws NamingException
+    {
+        try
+        {
+            if ( nexus.hasEntry( new EntryOperationContext( registries, name ) ) )
+            {
+                return;
+            }
+        }
+        catch ( NameNotFoundException e )
+        {
+            // Skip if there's no backend associated with the name.
+            return;
+        }
+
+        // Create a glue entry.
+        ServerEntry entry = new DefaultServerEntry( registries, name );
+        
+        //// Add RDN attribute. 
+        String rdn = name.get( name.size() - 1 );
+        String rdnAttribute = NamespaceTools.getRdnAttribute( rdn );
+        String rdnValue = NamespaceTools.getRdnValue( rdn );
+        entry.put( rdnAttribute, rdnValue );
+        
+        //// Add objectClass attribute. 
+        entry.put( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC, SchemaConstants.EXTENSIBLE_OBJECT_OC );
+
+        // And add it to the nexus.
+        nexus.add( new AddOperationContext( registries, entry ) );
+    }
+
+
+    private EntryUtil()
+    {
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/ClientConnectionManager.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/ClientConnectionManager.java
new file mode 100644
index 0000000..396f364
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/ClientConnectionManager.java
@@ -0,0 +1,414 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.service;
+
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.directory.mitosis.common.Replica;
+import org.apache.directory.mitosis.common.ReplicaId;
+import org.apache.directory.mitosis.configuration.ReplicationConfiguration;
+import org.apache.directory.mitosis.service.protocol.codec.ReplicationClientProtocolCodecFactory;
+import org.apache.directory.mitosis.service.protocol.handler.ReplicationClientContextHandler;
+import org.apache.directory.mitosis.service.protocol.handler.ReplicationClientProtocolHandler;
+import org.apache.directory.mitosis.service.protocol.handler.ReplicationProtocolHandler;
+import org.apache.mina.common.ConnectFuture;
+import org.apache.mina.common.ExecutorThreadModel;
+import org.apache.mina.common.IoConnector;
+import org.apache.mina.common.IoConnectorConfig;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.common.RuntimeIOException;
+import org.apache.mina.filter.LoggingFilter;
+import org.apache.mina.filter.codec.ProtocolCodecFilter;
+import org.apache.mina.transport.socket.nio.SocketConnector;
+import org.apache.mina.transport.socket.nio.SocketConnectorConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Manages all outgoing connections to remote replicas.
+ * It gets the list of the peer {@link Replica}s from
+ * {@link ReplicationInterceptor} and keeps trying to connect to them.
+ * <p>
+ * When the connection attempt fails, the interval between each connection
+ * attempt doubles up (0, 2, 4, 8, 16, ...) to 60 seconds at maximum.
+ * <p>
+ * Once the connection attempt succeeds, the interval value is reset to
+ * its initial value (0 second) and the established connection is handled
+ * by {@link ReplicationClientProtocolHandler}.
+ * The {@link ReplicationClientProtocolHandler} actually wraps
+ * a {@link ReplicationClientContextHandler} that drives the actual
+ * replication process.
+ *
+ * @author The Apache Directory Project (dev@directory.apache.org)
+ * @version $Rev: 116 $, $Date: 2006-09-18 13:47:53Z $
+ */
+class ClientConnectionManager
+{
+    private static final Logger LOG = LoggerFactory.getLogger( ClientConnectionManager.class );
+
+    private final ReplicationInterceptor interceptor;
+    private final IoConnector connector = new SocketConnector();
+    private final IoConnectorConfig connectorConfig = new SocketConnectorConfig();
+    private final Map<ReplicaId,Connection> sessions = new HashMap<ReplicaId,Connection>();
+    private ReplicationConfiguration configuration;
+    private ConnectionMonitor monitor;
+
+
+    ClientConnectionManager( ReplicationInterceptor interceptor )
+    {
+        this.interceptor = interceptor;
+
+        ExecutorThreadModel threadModel = ExecutorThreadModel.getInstance( "mitosis" );
+        threadModel.setExecutor( new ThreadPoolExecutor( 16, 16, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>() ) );
+        connectorConfig.setThreadModel( threadModel );
+
+        //// add codec
+        connectorConfig.getFilterChain().addLast( "protocol",
+            new ProtocolCodecFilter( new ReplicationClientProtocolCodecFactory() ) );
+
+        //// add logger
+        connectorConfig.getFilterChain().addLast( "logger", new LoggingFilter() );
+    }
+
+
+    public void start( ReplicationConfiguration cfg ) throws Exception
+    {
+        this.configuration = cfg;
+        monitor = new ConnectionMonitor();
+        monitor.start();
+    }
+
+
+    public void stop() throws Exception
+    {
+        // close all connections
+        monitor.shutdown();
+
+        // remove all filters
+        connector.getFilterChain().clear();
+
+        ( ( ExecutorService ) ( ( ExecutorThreadModel ) connectorConfig.getThreadModel() ).getExecutor() ).shutdown();
+        
+        // Remove all status values.
+        sessions.clear();
+    }
+
+
+    public void replicate()
+    {
+        // FIXME Can get ConcurrentModificationException.
+        for ( Connection connection : sessions.values() )
+        {
+            synchronized ( connection )
+            {
+                // Begin replication for the connected replicas.
+                if ( connection.session != null )
+                {
+                    ( ( ReplicationProtocolHandler ) connection.session.getHandler() )
+                            .getContext( connection.session ).replicate();
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Interrupt the unconnected connections to make them attempt to connect immediately.
+     *
+     */
+    public void interruptConnectors()
+    {
+        for ( Connection connection : sessions.values() )
+        {
+            synchronized ( connection )
+            {
+                // Wake up the replicas that are sleeping.
+                if ( connection.inProgress && connection.connector != null )
+                {
+                    connection.connector.interrupt();
+                }
+            }
+        }
+        
+    }
+
+
+    private class ConnectionMonitor extends Thread
+    {
+        private boolean timeToShutdown;
+
+
+        public ConnectionMonitor()
+        {
+            super( "ClientConnectionManager" );
+            
+            // Initialize the status map.
+            for ( Replica replica : configuration.getPeerReplicas() )
+            {
+                Connection con = sessions.get( replica.getId() );
+                if ( con == null )
+                {
+                    con = new Connection();
+                    con.replicaId = replica.getId();
+                    sessions.put( replica.getId(), con );
+                }
+            }
+        }
+
+
+        public void shutdown()
+        {
+            timeToShutdown = true;
+            while ( isAlive() )
+            {
+                try
+                {
+                    join();
+                }
+                catch ( InterruptedException e )
+                {
+                    LOG.warn( "[Replica-{}] Unexpected exception.", configuration.getReplicaId(), e );
+                }
+            }
+        }
+
+
+        public void run()
+        {
+            while ( !timeToShutdown )
+            {
+                connectUnconnected();
+                try
+                {
+                    Thread.sleep( 1000 );
+                }
+                catch ( InterruptedException e )
+                {
+                    LOG.warn( "[Replica-{}] Unexpected exception.", configuration.getReplicaId(), e );
+                }
+            }
+
+            disconnectConnected();
+        }
+
+
+        private void connectUnconnected()
+        {
+            for ( Replica replica : configuration.getPeerReplicas() )
+            {
+                // Someone might have modified the configuration,
+                // and therefore we try to detect newly added replicas.
+                Connection con = sessions.get( replica.getId() );
+                if ( con == null )
+                {
+                    con = new Connection();
+                    con.replicaId = replica.getId();
+                    sessions.put( replica.getId(), con );
+                }
+
+                synchronized ( con )
+                {
+                    if ( con.inProgress )
+                    {
+                        // connection is in progress
+                        continue;
+                    }
+
+                    if ( con.session != null )
+                    {
+                        if ( con.session.isConnected() )
+                        {
+                            continue;
+                        }
+                        con.session = null;
+                    }
+
+                    // put to connectingSession with dummy value to mark
+                    // that connection is in progress
+                    con.inProgress = true;
+
+                    if ( con.delay < 0 )
+                    {
+                        con.delay = 0;
+                    }
+                    else if ( con.delay == 0 )
+                    {
+                        con.delay = 2;
+                    }
+                    else
+                    {
+                        con.delay *= 2;
+                        if ( con.delay > 60 )
+                        {
+                            con.delay = 60;
+                        }
+                    }
+                }
+
+                Connector connector = new Connector( replica, con );
+                synchronized ( con )
+                {
+                    con.connector = connector;
+                }
+                connector.start();
+            }
+        }
+
+
+        private void disconnectConnected()
+        {
+            LOG.info( "[Replica-{}] Closing all connections...", configuration.getReplicaId() );
+            for ( ;; )
+            {
+                Iterator i = sessions.values().iterator();
+                while ( i.hasNext() )
+                {
+                    Connection con = ( Connection ) i.next();
+                    synchronized ( con )
+                    {
+                        if ( con.inProgress )
+                        {
+                            if ( con.connector != null )
+                            {
+                                con.connector.interrupt();
+                            }
+                            continue;
+                        }
+
+                        i.remove();
+
+                        if ( con.session != null )
+                        {
+                            LOG.info( "[Replica-{}] Closed connection to Replica-{}", configuration.getReplicaId(),
+                                    con.replicaId );
+                            con.session.close();
+                        }
+                    }
+                }
+
+                if ( sessions.isEmpty() )
+                {
+                    break;
+                }
+
+                // Sleep 1 second and try again waiting for Connector threads.
+                try
+                {
+                    Thread.sleep( 1000 );
+                }
+                catch ( InterruptedException e )
+                {
+                }
+            }
+        }
+    }
+
+
+    private class Connector extends Thread
+    {
+        private final Replica replica;
+        private final Connection con;
+
+
+        public Connector( Replica replica, Connection con )
+        {
+            super( "ClientConnectionManager-" + replica );
+            this.replica = replica;
+            this.con = con;
+        }
+
+
+        public void run()
+        {
+            if ( con.delay > 0 )
+            {
+                if ( LOG.isInfoEnabled() )
+                {
+                    LOG.info( "[Replica-{}] Waiting for {} seconds to reconnect to replica-" + con.replicaId,
+                            ClientConnectionManager.this.configuration.getReplicaId(), con.delay );
+                }
+                
+                try
+                {
+                    Thread.sleep( con.delay * 1000L );
+                }
+                catch ( InterruptedException e )
+                {
+                }
+            }
+
+            LOG.info( "[Replica-{}] Connecting to replica-{}",
+                    ClientConnectionManager.this.configuration.getReplicaId(), replica.getId() );
+
+            IoSession session;
+            try
+            {
+                connectorConfig.setConnectTimeout( configuration.getResponseTimeout() );
+                ConnectFuture future = connector.connect( replica.getAddress(), new ReplicationClientProtocolHandler(
+                        interceptor ), connectorConfig );
+
+                future.join();
+                session = future.getSession();
+
+                synchronized ( con )
+                {
+                    con.session = session;
+                    con.delay = -1; // reset delay
+                    con.inProgress = false;
+                    con.replicaId = replica.getId();
+                }
+            }
+            catch ( RuntimeIOException e )
+            {
+                LOG.warn( "[Replica-" + ClientConnectionManager.this.configuration.getReplicaId()
+                        + "] Failed to connect to replica-" + replica.getId(), e );
+            }
+            finally
+            {
+                synchronized ( con )
+                {
+                    con.inProgress = false;
+                    con.connector = null;
+                }
+            }
+        }
+    }
+
+    private static class Connection
+    {
+        private IoSession session;
+        private int delay = -1;
+        private boolean inProgress;
+        private Connector connector;
+        private ReplicaId replicaId;
+
+
+        public Connection()
+        {
+        }
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/DefaultReplicationContext.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/DefaultReplicationContext.java
new file mode 100644
index 0000000..566c722
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/DefaultReplicationContext.java
@@ -0,0 +1,217 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.service;
+
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import org.apache.directory.mitosis.common.Replica;
+import org.apache.directory.mitosis.configuration.ReplicationConfiguration;
+import org.apache.directory.mitosis.service.protocol.handler.ReplicationClientContextHandler;
+import org.apache.directory.mitosis.service.protocol.handler.ReplicationClientProtocolHandler;
+import org.apache.directory.mitosis.service.protocol.handler.ReplicationContextHandler;
+import org.apache.directory.mitosis.service.protocol.handler.ReplicationProtocolHandler;
+import org.apache.directory.mitosis.service.protocol.message.BaseMessage;
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.util.SessionLog;
+
+/**
+ * The default implementation of {@link ReplicationContext}
+ * 
+ * @author The Apache Directory Project Team
+ */
+public class DefaultReplicationContext implements ReplicationContext
+{
+    private static final Timer EXPIRATION_TIMER = new Timer( "ReplicationMessageExpirer" );
+
+    private final ReplicationInterceptor interceptor;
+    private final ReplicationConfiguration configuration;
+    private final DirectoryService directoryService;
+    private final IoSession session;
+    private final Map<Integer,ExpirationTask> expirableMessages = new HashMap<Integer,ExpirationTask>();
+    private int nextSequence;
+    private Replica peer;
+    private State state = State.INIT;
+
+
+    public DefaultReplicationContext( ReplicationInterceptor interceptor, DirectoryService directoryService,
+        ReplicationConfiguration configuration, IoSession session )
+    {
+        this.interceptor = interceptor;
+        this.configuration = configuration;
+        this.directoryService = directoryService;
+        this.session = session;
+    }
+
+
+    public ReplicationInterceptor getService()
+    {
+        return interceptor;
+    }
+
+
+    public ReplicationConfiguration getConfiguration()
+    {
+        return configuration;
+    }
+
+
+    public DirectoryService getDirectoryService()
+    {
+        return directoryService;
+    }
+
+
+    public IoSession getSession()
+    {
+        return session;
+    }
+
+
+    public int getNextSequence()
+    {
+        return nextSequence++;
+    }
+
+
+    public Replica getPeer()
+    {
+        return peer;
+    }
+
+
+    public void setPeer( Replica peer )
+    {
+        assert peer != null;
+        this.peer = peer;
+    }
+
+
+    public State getState()
+    {
+        return state;
+    }
+
+
+    public void setState( State state )
+    {
+        this.state = state;
+    }
+
+
+    public void scheduleExpiration( Object message )
+    {
+        BaseMessage bm = ( BaseMessage ) message;
+        ExpirationTask task = new ExpirationTask( bm );
+        synchronized ( expirableMessages )
+        {
+            expirableMessages.put( bm.getSequence(), task );
+        }
+
+        EXPIRATION_TIMER.schedule( task, configuration.getResponseTimeout() * 1000L );
+    }
+
+
+    public Object cancelExpiration( int sequence )
+    {
+        ExpirationTask task = removeTask( sequence );
+        if ( task == null )
+        {
+            return null;
+        }
+
+        task.cancel();
+        return task.message;
+    }
+
+    
+    public boolean replicate()
+    {
+        ReplicationProtocolHandler handler =
+            ( ReplicationProtocolHandler ) this.session.getHandler();
+        if( !( handler instanceof ReplicationClientProtocolHandler ) )
+        {
+            throw new UnsupportedOperationException(
+                    "Only clients can begin replication." );
+        }
+        
+        ReplicationContextHandler contextHandler = handler.getContextHandler();
+        return ( ( ReplicationClientContextHandler ) contextHandler ).beginReplication( this );
+    }
+
+
+    public void cancelAllExpirations()
+    {
+        synchronized ( expirableMessages )
+        {
+            for ( ExpirationTask expirationTask : expirableMessages.values() )
+            {
+                ( expirationTask ).cancel();
+            }
+        }
+    }
+
+
+    public int getScheduledExpirations()
+    {
+        synchronized ( expirableMessages )
+        {
+            return expirableMessages.size();
+        }
+    }
+
+
+    private ExpirationTask removeTask( int sequence )
+    {
+        ExpirationTask task;
+        synchronized ( expirableMessages )
+        {
+            task = expirableMessages.remove( sequence );
+        }
+        return task;
+    }
+
+
+    private class ExpirationTask extends TimerTask
+    {
+        private final BaseMessage message;
+
+
+        private ExpirationTask( Object message )
+        {
+            this.message = ( BaseMessage ) message;
+        }
+
+
+        public void run()
+        {
+            if ( removeTask( message.getSequence() ) == this )
+            {
+                SessionLog.warn( getSession(), "No response within " + configuration.getResponseTimeout()
+                    + " second(s) for message #" + message.getSequence() );
+                getSession().close();
+            }
+        }
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/ReplicationContext.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/ReplicationContext.java
new file mode 100644
index 0000000..67970ec
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/ReplicationContext.java
@@ -0,0 +1,167 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.service;
+
+
+import org.apache.directory.mitosis.common.Replica;
+import org.apache.directory.mitosis.configuration.ReplicationConfiguration;
+import org.apache.directory.mitosis.service.protocol.handler.ReplicationClientContextHandler;
+import org.apache.directory.mitosis.service.protocol.handler.ReplicationContextHandler;
+import org.apache.directory.mitosis.service.protocol.handler.ReplicationServerContextHandler;
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.mina.common.IoSession;
+
+
+/**
+ * A <a href="http://www.corej2eepatterns.com/Patterns2ndEd/ContextObject.htm">context object</a>
+ * that provides the functions required for a client or a server to implement
+ * networking code related with replication communication.  This is provided
+ * to and used by {@link ReplicationClientContextHandler} and
+ * {@link ReplicationServerContextHandler}.
+ *   
+ * @author The Apache Directory Project Team
+ */
+public interface ReplicationContext
+{
+    /**
+     * Returns <a href="http://mina.apache.org/">MINA</a> {@link IoSession}
+     * instance that is associated with the current connection to
+     * the remote {@link Replica}.
+     */
+    IoSession getSession();
+
+    /**
+     * Returns the current {@link ReplicationConfiguration} of the
+     * {@link Replica} which is managing this context.
+     */
+    ReplicationConfiguration getConfiguration();
+
+    /**
+     * Returns the {@link ReplicationInterceptor} which is managing this
+     * context.
+     */
+    ReplicationInterceptor getService();
+
+
+    /**
+     * Returns the {@link DirectoryService} which owns the {@link ReplicationInterceptor}
+     * which is managing this context.
+     */
+    DirectoryService getDirectoryService();
+
+
+    /**
+     * Generates a new and unique sequence number of protocol message.
+     * @return the new sequence number.
+     */
+    int getNextSequence();
+
+
+    /**
+     * Returns the remote peer {@link Replica} that this context is connected
+     * to. 
+     */
+    Replica getPeer();
+
+
+    /**
+     * Sets the remote peer {@link Replica} that this context is connected
+     * to.  A user has authenticate the remote peer first and call this method
+     * manually to prevent unauthorized access.
+     */
+    void setPeer( Replica peer );
+
+    /**
+     * Returns the current state of the {@link Replica} this context is
+     * managing.
+     */
+    State getState();
+
+    /**
+     * Sets the current state of the {@link Replica} this context is
+     * managing.
+     */
+    void setState( State state );
+
+
+    /**
+     * Schedules an expiration of the specified <tt>message</tt>.  A user of
+     * this context could call this method with the message it has written out
+     * to the remote peer.  If {@link #cancelExpiration(int)} method is not
+     * invoked within a certain timeout, an exception will be raised to
+     * {@link ReplicationContextHandler#exceptionCaught(ReplicationContext, Throwable)}.
+     */
+    void scheduleExpiration( Object message );
+
+    /**
+     * Cancels the expiration scheduled by calling
+     * {@link #scheduleExpiration(Object)}.  A user of this context could
+     * call this method when the response message has been received to
+     * stop the expiration for the message with the specified
+     * <tt>sequence</tt> number.
+     * 
+     * @return the request message with the specified <tt>sequence</tt> number
+     */
+    Object cancelExpiration( int sequence );
+
+    /**
+     * Cancells all scheduled expirations.  A user of this context could
+     * call this method when the current connection is closed.
+     */
+    void cancelAllExpirations();
+
+    /**
+     * Returns the number of the scheduled experations.  A user of this
+     * contexst could check this value before sending a new message to the
+     * remote peer to prevent {@link OutOfMemoryError} by limiting the number
+     * of the messages which didn't get their responses.
+     */
+    int getScheduledExpirations();
+    
+    
+    /**
+     * Forces this context to send replication data to the peer replica immediately.
+     * 
+     * @return <tt>true</tt> if the replication has been started,
+     *         <tt>false</tt> if the replication didn't start because
+     *         the replication process is already in progress or
+     *         the client is currently logging in to the server yet.
+     */
+    boolean replicate();
+
+    /**
+     * Represents the state of the connection between two {@link Replica}s.
+     * 
+     * @author The Apache Directory Project Team
+     */
+    public enum State
+    {
+        /**
+         * Connection is established.
+         */
+        INIT,
+
+        /**
+         * Client has logged in and is ready to exchange information.
+         */
+        READY,
+        ;
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/ReplicationInterceptor.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/ReplicationInterceptor.java
new file mode 100644
index 0000000..9c17aef
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/ReplicationInterceptor.java
@@ -0,0 +1,558 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.
+ *
+ */
+package org.apache.directory.mitosis.service;
+
+
+import org.apache.directory.mitosis.common.CSN;
+import org.apache.directory.mitosis.common.Constants;
+import org.apache.directory.mitosis.common.DefaultCSN;
+import org.apache.directory.mitosis.common.ReplicaId;
+import org.apache.directory.mitosis.configuration.ReplicationConfiguration;
+import org.apache.directory.mitosis.operation.Operation;
+import org.apache.directory.mitosis.operation.OperationFactory;
+import org.apache.directory.mitosis.service.protocol.codec.ReplicationServerProtocolCodecFactory;
+import org.apache.directory.mitosis.service.protocol.handler.ReplicationClientContextHandler;
+import org.apache.directory.mitosis.service.protocol.handler.ReplicationServerContextHandler;
+import org.apache.directory.mitosis.service.protocol.handler.ReplicationServerProtocolHandler;
+import org.apache.directory.mitosis.store.ReplicationStore;
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerSearchResult;
+import org.apache.directory.server.core.enumeration.SearchResultFilteringEnumeration;
+import org.apache.directory.server.core.interceptor.BaseInterceptor;
+import org.apache.directory.server.core.interceptor.Interceptor;
+import org.apache.directory.server.core.interceptor.NextInterceptor;
+import org.apache.directory.server.core.interceptor.context.AddOperationContext;
+import org.apache.directory.server.core.interceptor.context.DeleteOperationContext;
+import org.apache.directory.server.core.interceptor.context.EntryOperationContext;
+import org.apache.directory.server.core.interceptor.context.GetMatchedNameOperationContext;
+import org.apache.directory.server.core.interceptor.context.ListOperationContext;
+import org.apache.directory.server.core.interceptor.context.LookupOperationContext;
+import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
+import org.apache.directory.server.core.interceptor.context.MoveAndRenameOperationContext;
+import org.apache.directory.server.core.interceptor.context.MoveOperationContext;
+import org.apache.directory.server.core.interceptor.context.RenameOperationContext;
+import org.apache.directory.server.core.interceptor.context.SearchOperationContext;
+import org.apache.directory.server.core.invocation.InvocationStack;
+import org.apache.directory.server.core.partition.PartitionNexus;
+import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.entry.Value;
+import org.apache.directory.shared.ldap.exception.LdapNameNotFoundException;
+import org.apache.directory.shared.ldap.filter.ExprNode;
+import org.apache.directory.shared.ldap.filter.FilterParser;
+import org.apache.directory.shared.ldap.filter.PresenceNode;
+import org.apache.directory.shared.ldap.message.AliasDerefMode;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.mina.common.IoAcceptor;
+import org.apache.mina.filter.LoggingFilter;
+import org.apache.mina.filter.codec.ProtocolCodecFilter;
+import org.apache.mina.transport.socket.nio.SocketAcceptor;
+import org.apache.mina.transport.socket.nio.SocketAcceptorConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.SearchControls;
+
+import java.net.InetSocketAddress;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * An {@link Interceptor} that intercepts LDAP operations and propagates the
+ * changes occurred by the operations into other {@link ReplicaId}s so the DIT
+ * of each {@link ReplicaId} in the cluster has the same content without any
+ * conflict.
+ * <p>
+ * Once an operation is invoked, this interceptor transforms it into one or
+ * more operations that makes the requested operation more proper and robust
+ * for replication.  The transformation process is actually just calling a
+ * respective factory method in {@link OperationFactory}.  The methods in
+ * {@link OperationFactory} returns a new {@link Operation} instance.
+ * <p>
+ * The newly created {@link Operation} is used for three purposes.
+ * <ul>
+ * <li>To perform the requested operation to the local {@link PartitionNexus}
+ * <li>To store the created {@link Operation} itself to
+ *     {@link ReplicationStore} so that it can be retrieved later by
+ *     {@link ReplicationLogCleanJob} and {@link ReplicationClientContextHandler}
+ * <li>To transfer itself to other {@link ReplicaId}s via TCP/IP communication
+ *     between {@link ReplicationClientContextHandler} and
+ *     {@link ReplicationServerContextHandler}
+ * </ul>
+ * The first two actions (modifying the local DIT and storing the
+ * {@link Operation} to {@link ReplicationStore}) are performed automatically
+ * when
+ * {@link Operation#execute(PartitionNexus, ReplicationStore, Registries)}
+ * method is invoked.  {@link ReplicationInterceptor} always call it instead of
+ * forwarding the requested operation to the next {@link Interceptor}.
+ * <p>
+ * The last action takes place by {@link ReplicationClientContextHandler},
+ * which handles TCP/IP connection managed by {@link ClientConnectionManager}.
+ * <p>
+ * There are two special attributes in the entries to be replicated:
+ * <ul>
+ * <li><tt>entryCSN</tt> - stores {@link CSN} of the entry.  This attribute is
+ *     used to compare the incoming operation from other replica is still
+ *     valid.  If the local <tt>entryCSN</tt> value is bigger then that of the
+ *     incoming operation, it means conflict, and therefore an appropriate
+ *     conflict resolution mechanism should get engaged.</li>
+ * <li><tt>entryDeleted</tt> - is <tt>TRUE</tt> if and only if the entry is
+ *     deleted.  The entry is not deleted immediately by a delete operation
+ *     because <tt>entryCSN</tt> attribute should be retained for certain
+ *     amount of time to determine whether the incoming change LOG, which
+ *     affects an entry with the same DN, is a conflict (modification on a
+ *     deleted entry) or not (creation of a new entry). You can purge old
+ *     deleted entries and related change logs in {@link ReplicationStore} by
+ *     calling {@link #purgeAgedData()}, or they will be purged automatically
+ *     by periodic manner as you configured with {@link ReplicationConfiguration}.
+ *     by calling {@link ReplicationConfiguration#setLogMaxAge(int)}.
+ *     Because of this attribute, <tt>lookup</tt> and <tt>search</tt>
+ *     operations are overrided to ignore entries with <tt>entryDeleted</tt>
+ *     set to <tt>TRUE</tt>.</li>
+ * </ul>
+ *
+ * @org.apache.xbean.XBean
+ *
+ * @author The Apache Directory Project (dev@directory.apache.org)
+ * @version $Rev$, $Date$
+ */
+public class ReplicationInterceptor extends BaseInterceptor
+{
+    private static final Logger LOG = LoggerFactory.getLogger( ReplicationInterceptor.class );
+
+    /** The service name */
+    public static final String NAME = "replicationService";
+
+
+    private static final String ENTRY_CSN_OID = "1.3.6.1.4.1.18060.0.4.1.2.30";
+    private static final String ENTRY_DELETED_OID = "1.3.6.1.4.1.18060.0.4.1.2.31";
+
+    /**
+     * default name is the service name?
+     */
+    private String name = NAME;
+
+    private DirectoryService directoryService;
+    private ReplicationConfiguration configuration;
+    private PartitionNexus nexus;
+    private OperationFactory operationFactory;
+    private ReplicationStore store;
+    private IoAcceptor registry;
+    private final ClientConnectionManager clientConnectionManager = new ClientConnectionManager( this );
+    private Registries registries;
+
+
+    public ReplicationInterceptor()
+    {
+    }
+
+    /**
+     * this interceptor has configuration so it might be useful to allow several instances in a chain.
+     * @return configured name for this interceptor.
+     */
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public ReplicationConfiguration getConfiguration()
+    {
+        return configuration;
+    }
+
+
+    public void setConfiguration(ReplicationConfiguration configuration) {
+        this.configuration = configuration;
+    }
+
+
+    public void init( DirectoryService directoryService ) throws NamingException
+    {
+        configuration.validate();
+        // and then preserve frequently used ones
+        this.directoryService = directoryService;
+        registries = directoryService.getRegistries();
+        nexus = directoryService.getPartitionNexus();
+        store = configuration.getStore();
+        operationFactory = new OperationFactory( directoryService, configuration );
+
+        // Initialize store and service
+        store.open( directoryService, configuration );
+        boolean serviceStarted = false;
+        try
+        {
+            startNetworking();
+            serviceStarted = true;
+        }
+        catch ( Exception e )
+        {
+            throw new ReplicationServiceException( "Failed to initialize MINA ServiceRegistry.", e );
+        }
+        finally
+        {
+            if ( !serviceStarted )
+            {
+                // roll back
+                store.close();
+            }
+        }
+
+        purgeAgedData();
+    }
+
+
+    private void startNetworking() throws Exception
+    {
+        registry = new SocketAcceptor();
+        SocketAcceptorConfig config = new SocketAcceptorConfig();
+        config.setReuseAddress( true );
+
+        config.getFilterChain().addLast( "protocol",
+            new ProtocolCodecFilter( new ReplicationServerProtocolCodecFactory() ) );
+
+        config.getFilterChain().addLast( "logger", new LoggingFilter() );
+
+        // bind server protocol provider
+        registry.bind( new InetSocketAddress( configuration.getServerPort() ), new ReplicationServerProtocolHandler(
+            this ), config );
+
+        clientConnectionManager.start( configuration );
+    }
+
+
+    public void destroy()
+    {
+        stopNetworking();
+        store.close();
+    }
+
+
+    private void stopNetworking()
+    {
+        // close all open connections, deactivate all filters and service registry
+        try
+        {
+            clientConnectionManager.stop();
+        }
+        catch ( Exception e )
+        {
+            LOG.error( "[Replica-{}] Failed to stop the client connection manager.", configuration.getReplicaId() );
+            LOG.error( "Stop failure exception: ", e );
+        }
+        registry.unbindAll();
+    }
+
+
+    /**
+     * Forces this context to send replication data to the peer replica immediately.
+     */
+    public void replicate()
+    {
+        LOG.info( "[Replica-{}] Forcing replication...", configuration.getReplicaId() );
+        this.clientConnectionManager.replicate();
+    }
+
+    /**
+     * Wake the sleeping (unconnected) replicas.
+     */
+    public void interruptConnectors()
+    {
+        LOG.info( "[Replica-{}] Waking sleeping replicas...", configuration.getReplicaId() );
+        this.clientConnectionManager.interruptConnectors();
+    }
+
+
+    /**
+     * Purges old replication logs and the old entries marked as 'deleted'
+     * (i.e. {@link Constants#ENTRY_DELETED} is <tt>TRUE</tt>).  This method
+     * should be called periodically to make sure the size of the DIT and
+     * {@link ReplicationStore} increase limitlessly.
+     *
+     * @see ReplicationConfiguration#setLogMaxAge(int)
+     * @see ReplicationLogCleanJob
+     * @throws javax.naming.NamingException on error
+     */
+    public void purgeAgedData() throws NamingException
+    {
+        ServerEntry rootDSE = nexus.getRootDSE( null );
+        EntryAttribute namingContextsAttr = rootDSE.get( SchemaConstants.NAMING_CONTEXTS_AT );
+        
+        if ( ( namingContextsAttr == null ) || ( namingContextsAttr.size() == 0 ) )
+        {
+            throw new NamingException( "No namingContexts attributes in rootDSE." );
+        }
+
+        CSN purgeCSN = new DefaultCSN( System.currentTimeMillis() - configuration.getLogMaxAge() * 1000L * 60L * 60L
+            * 24L, // convert days to millis
+            new ReplicaId( "ZZZZZZZZZZZZZZZZ" ), Integer.MAX_VALUE );
+        ExprNode filter;
+
+        try
+        {
+            filter = FilterParser.parse( "(&(" + ENTRY_CSN_OID + "<=" + purgeCSN.toOctetString() + ")(" + ENTRY_DELETED_OID
+                + "=TRUE))" );
+        }
+        catch ( ParseException e )
+        {
+            throw ( NamingException ) new NamingException().initCause( e );
+        }
+
+        // Iterate all context partitions to send all entries of them.
+        for ( Value<?> namingContext:namingContextsAttr )
+        {
+            // Convert attribute value to JNDI name.
+            LdapDN contextName;
+            
+            contextName = new LdapDN( (String)namingContext.get() );
+
+            contextName.normalize( registries.getAttributeTypeRegistry().getNormalizerMapping() );
+            LOG.info( "[Replica-{}] Purging aged data under '{}'", configuration.getReplicaId(), contextName );
+            purgeAgedData( contextName, filter );
+        }
+
+        store.removeLogs( purgeCSN, false );
+    }
+
+
+    private void purgeAgedData( LdapDN contextName, ExprNode filter ) throws NamingException
+    {
+        SearchControls ctrl = new SearchControls();
+        ctrl.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        ctrl.setReturningAttributes( new String[] { "entryCSN", "entryDeleted" } );
+
+        NamingEnumeration<ServerSearchResult> e = nexus.search(
+            new SearchOperationContext( registries, contextName, AliasDerefMode.DEREF_ALWAYS, filter, ctrl ) );
+
+        List<LdapDN> names = new ArrayList<LdapDN>();
+        
+        try
+        {
+            while ( e.hasMore() )
+            {
+                ServerSearchResult sr = e.next();
+                LdapDN name = sr.getDn();
+                
+                if ( name.size() > contextName.size() )
+                {
+                    names.add( name );
+                }
+            }
+        }
+        finally
+        {
+            e.close();
+        }
+
+        for ( LdapDN name : names )
+        {
+            try
+            {
+                name.normalize( registries.getAttributeTypeRegistry().getNormalizerMapping() );
+                ServerEntry entry = nexus.lookup( new LookupOperationContext( registries, name ) );
+                LOG.info( "[Replica-{}] Purge: " + name + " (" + entry + ')', configuration.getReplicaId() );
+                nexus.delete( new DeleteOperationContext( registries, name ) );
+            }
+            catch ( NamingException ex )
+            {
+                LOG.error( "[Replica-{}] Failed to fetch/delete: " + name, configuration.getReplicaId(), ex );
+            }
+        }
+    }
+
+
+    public void add( NextInterceptor nextInterceptor, AddOperationContext addContext ) throws NamingException
+    {
+        Operation op = operationFactory.newAdd( 
+            addContext.getDn(), addContext.getEntry() );
+        op.execute( nexus, store, registries );
+    }
+
+
+    @Override
+    public void delete( NextInterceptor next, DeleteOperationContext opContext ) throws NamingException
+    {
+        Operation op = operationFactory.newDelete( opContext.getDn() );
+        op.execute( nexus, store, registries );
+    }
+
+
+    public void modify( NextInterceptor next, ModifyOperationContext modifyContext ) throws NamingException
+    {
+        Operation op = operationFactory.newModify( modifyContext );
+        op.execute( nexus, store, registries );
+    }
+
+
+    @Override
+    public void move( NextInterceptor next, MoveOperationContext moveOpContext ) throws NamingException
+    {
+        Operation op = operationFactory.newMove( moveOpContext.getDn(), moveOpContext.getParent() );
+        op.execute( nexus, store, registries );
+    }
+
+
+    @Override
+    public void moveAndRename( NextInterceptor next, MoveAndRenameOperationContext moveAndRenameOpContext ) throws NamingException
+    {
+        Operation op = operationFactory.newMove( moveAndRenameOpContext.getDn(),
+                moveAndRenameOpContext.getParent(), moveAndRenameOpContext.getNewRdn(),
+                moveAndRenameOpContext.getDelOldDn() );
+        op.execute( nexus, store, registries );
+    }
+
+
+    @Override
+    public void rename( NextInterceptor next, RenameOperationContext renameOpContext ) throws NamingException
+    {
+        Operation op = operationFactory.newModifyRn( renameOpContext.getDn(), renameOpContext.getNewRdn(), renameOpContext.getDelOldDn() );
+        op.execute( nexus, store, registries );
+    }
+
+
+    public boolean hasEntry( NextInterceptor nextInterceptor, EntryOperationContext entryContext ) throws NamingException
+    {
+        // Ask others first.
+        boolean hasEntry = nextInterceptor.hasEntry( entryContext );
+
+        // If the entry exists,
+        if ( hasEntry )
+        {
+            // Check DELETED attribute.
+            try
+            {
+                ServerEntry entry = nextInterceptor.lookup( new LookupOperationContext( registries, entryContext.getDn() ) );
+                hasEntry = !isDeleted( entry );
+            }
+            catch ( NameNotFoundException e )
+            {
+                hasEntry = false;
+            }
+        }
+
+        return hasEntry;
+    }
+
+
+    public ServerEntry lookup( NextInterceptor nextInterceptor, LookupOperationContext lookupContext ) throws NamingException
+    {
+        if ( lookupContext.getAttrsId() != null )
+        {
+            boolean found = false;
+
+            String[] attrIds = lookupContext.getAttrsIdArray();
+
+            // Look for 'entryDeleted' attribute is in attrIds.
+            for ( String attrId:attrIds )
+            {
+                if ( Constants.ENTRY_DELETED.equals( attrId ) )
+                {
+                    found = true;
+                    break;
+                }
+            }
+
+            // If not exists, add one.
+            if ( !found )
+            {
+                String[] newAttrIds = new String[attrIds.length + 1];
+                System.arraycopy( attrIds, 0, newAttrIds, 0, attrIds.length );
+                newAttrIds[attrIds.length] = Constants.ENTRY_DELETED;
+                lookupContext.setAttrsId( newAttrIds );
+            }
+        }
+
+        ServerEntry result = nextInterceptor.lookup( lookupContext );
+        ensureNotDeleted( lookupContext.getDn(), result );
+        return result;
+    }
+
+
+    @Override
+    public NamingEnumeration<ServerSearchResult> list( NextInterceptor nextInterceptor, ListOperationContext opContext ) throws NamingException
+    {
+        NamingEnumeration<ServerSearchResult> result = nextInterceptor.search(
+                new SearchOperationContext(
+                    registries, opContext.getDn(), opContext.getAliasDerefMode(),
+                    new PresenceNode( SchemaConstants.OBJECT_CLASS_AT_OID ),
+                    new SearchControls() ) );
+
+        return new SearchResultFilteringEnumeration( result, new SearchControls(), InvocationStack.getInstance().peek(),
+            Constants.DELETED_ENTRIES_FILTER, "List replication filter" );
+    }
+
+
+    @Override
+    public NamingEnumeration<ServerSearchResult> search( NextInterceptor nextInterceptor, SearchOperationContext opContext ) throws NamingException
+    {
+        SearchControls searchControls = opContext.getSearchControls();
+
+        if ( searchControls.getReturningAttributes() != null )
+        {
+            String[] oldAttrIds = searchControls.getReturningAttributes();
+            String[] newAttrIds = new String[oldAttrIds.length + 1];
+            System.arraycopy( oldAttrIds, 0, newAttrIds, 0, oldAttrIds.length );
+            newAttrIds[oldAttrIds.length] = Constants.ENTRY_DELETED.toLowerCase();
+            searchControls.setReturningAttributes( newAttrIds );
+        }
+
+        NamingEnumeration<ServerSearchResult> result = nextInterceptor.search(
+            new SearchOperationContext( registries, opContext.getDn(), opContext.getAliasDerefMode(), opContext.getFilter(), searchControls ) );
+        return new SearchResultFilteringEnumeration( result, searchControls, InvocationStack.getInstance().peek(),
+            Constants.DELETED_ENTRIES_FILTER, "Search Replication filter" );
+    }
+
+
+    private void ensureNotDeleted( LdapDN name, ServerEntry entry ) throws NamingException {
+        if ( isDeleted( entry ) )
+        {
+            LdapNameNotFoundException e = new LdapNameNotFoundException( "Deleted entry: " + name.getUpName() );
+            e.setResolvedName( nexus.getMatchedName( new GetMatchedNameOperationContext( registries, name ) ) );
+            throw e;
+        }
+    }
+
+
+    private boolean isDeleted( ServerEntry entry ) throws NamingException
+    {
+        if ( entry == null )
+        {
+            return true;
+        }
+
+        return entry.contains( Constants.ENTRY_DELETED, "TRUE" );
+    }
+
+
+    public DirectoryService getDirectoryService()
+    {
+        return directoryService;
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/ReplicationLogCleanJob.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/ReplicationLogCleanJob.java
new file mode 100644
index 0000000..919e554
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/ReplicationLogCleanJob.java
@@ -0,0 +1,93 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.service;
+
+
+import org.apache.directory.mitosis.common.Constants;
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.interceptor.Interceptor;
+import org.quartz.Job;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobExecutionException;
+
+import javax.naming.NamingException;
+import java.util.Map;
+
+
+/**
+ * A <a href="http://www.opensymphony.com/quartz/">OpenSymphony Quartz</a>
+ * {@link Job} that purges old replication logs and the old entries marked as
+ * 'deleted' (i.e. {@link Constants#ENTRY_DELETED} is <tt>TRUE</tt>).  This
+ * {@link Job} just calls {@link ReplicationInterceptor#purgeAgedData()} to
+ * purge old data. 
+ * 
+ * @author The Apache Directory Project Team
+ */
+public class ReplicationLogCleanJob implements Job
+{
+    public static final String INSTANCE_ID = "instanceId";
+    private Map<String,DirectoryService> services;
+
+
+    public ReplicationLogCleanJob( Map<String,DirectoryService> services )
+    {
+        this.services = services;
+    }
+
+
+    public void execute( JobExecutionContext ctx ) throws JobExecutionException
+    {
+        String instanceId = ctx.getJobDetail().getJobDataMap().getString( INSTANCE_ID );
+        if ( instanceId == null )
+        {
+            // Execute for all instances in the VM if instanceId is not specified.
+            for ( DirectoryService service : services.values() )
+            {
+                execute0( service );
+            }
+        }
+        else
+        {
+            // Execute for the instance with the specified instanceId if
+            // it is specified.
+
+            execute0( services.get( instanceId ) );
+        }
+    }
+
+
+    private void execute0( DirectoryService service ) throws JobExecutionException
+    {
+        for ( Interceptor interceptor : service.getInterceptorChain().getAll() )
+        {
+            if ( interceptor instanceof ReplicationInterceptor )
+            {
+                try
+                {
+                    ( ( ReplicationInterceptor ) interceptor ).purgeAgedData();
+                }
+                catch ( NamingException e )
+                {
+                    throw new JobExecutionException( e );
+                }
+            }
+        }
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/ReplicationServiceException.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/ReplicationServiceException.java
new file mode 100644
index 0000000..c0f39b3
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/ReplicationServiceException.java
@@ -0,0 +1,53 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.service;
+
+
+import org.apache.directory.mitosis.common.ReplicationException;
+
+
+public class ReplicationServiceException extends ReplicationException
+{
+    private static final long serialVersionUID = 3906090070204430386L;
+
+
+    public ReplicationServiceException()
+    {
+        super();
+    }
+
+
+    public ReplicationServiceException( String message, Throwable cause )
+    {
+        super( message, cause );
+    }
+
+
+    public ReplicationServiceException( String message )
+    {
+        super( message );
+    }
+
+
+    public ReplicationServiceException( Throwable cause )
+    {
+        super( cause );
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/Constants.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/Constants.java
new file mode 100644
index 0000000..3ab9d79
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/Constants.java
@@ -0,0 +1,43 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.service.protocol;
+
+
+public class Constants
+{
+    public static final int LOGIN = 0x00;
+    public static final int LOGIN_ACK = 0x01;
+    public static final int GET_UPDATE_VECTOR = 0x02;
+    public static final int GET_UPDATE_VECTOR_ACK = 0x03;
+    public static final int LOG_ENTRY = 0x04;
+    public static final int LOG_ENTRY_ACK = 0x05;
+    public static final int BEGIN_LOG_ENTRIES = 0x06;
+    public static final int BEGIN_LOG_ENTRIES_ACK = 0x07;
+    public static final int END_LOG_ENTRIES = 0x08;
+    public static final int END_LOG_ENTRIES_ACK = 0x09;
+
+    public static final int OK = 0;
+    public static final int NOT_OK = -1;
+
+
+    private Constants()
+    {
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/BaseMessageDecoder.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/BaseMessageDecoder.java
new file mode 100644
index 0000000..2a89ef2
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/BaseMessageDecoder.java
@@ -0,0 +1,105 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.service.protocol.codec;
+
+
+import org.apache.directory.mitosis.service.protocol.message.BaseMessage;
+import org.apache.mina.common.ByteBuffer;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.filter.codec.ProtocolDecoderException;
+import org.apache.mina.filter.codec.ProtocolDecoderOutput;
+import org.apache.mina.filter.codec.demux.MessageDecoder;
+import org.apache.mina.filter.codec.demux.MessageDecoderResult;
+
+
+public abstract class BaseMessageDecoder implements MessageDecoder
+{
+    private final int type;
+    private final int minBodyLength;
+    private final int maxBodyLength;
+    private boolean readHeader;
+    private int sequence;
+    private int bodyLength;
+
+
+    protected BaseMessageDecoder( int type, int minBodyLength, int maxBodyLength )
+    {
+        this.type = type;
+        this.minBodyLength = minBodyLength;
+        this.maxBodyLength = maxBodyLength;
+    }
+
+
+    public final MessageDecoderResult decodable( IoSession session, ByteBuffer buf )
+    {
+        return type == buf.get() ? OK : NOT_OK;
+    }
+
+
+    public final MessageDecoderResult decode( IoSession session, ByteBuffer in, ProtocolDecoderOutput out )
+        throws Exception
+    {
+        if ( !readHeader )
+        {
+            if ( in.remaining() < 9 )
+            {
+                return NEED_DATA;
+            }
+
+            in.get(); // skip type field
+            sequence = in.getInt();
+            bodyLength = in.getInt();
+
+            if ( bodyLength < minBodyLength || bodyLength > maxBodyLength )
+            {
+                throw new ProtocolDecoderException( "Wrong bodyLength: " + bodyLength );
+            }
+
+            readHeader = true;
+        }
+
+        if ( readHeader )
+        {
+            if ( in.remaining() < bodyLength )
+            {
+                return NEED_DATA;
+            }
+
+            int oldLimit = in.limit();
+
+            try
+            {
+                in.limit( in.position() + bodyLength );
+                out.write( decodeBody( sequence, bodyLength, in ) );
+                return OK;
+            }
+            finally
+            {
+                readHeader = false;
+                in.limit( oldLimit );
+            }
+        }
+
+        throw new InternalError();
+    }
+
+
+    protected abstract BaseMessage decodeBody( int sequence, int bodyLength, ByteBuffer in ) throws Exception;
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/BaseMessageEncoder.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/BaseMessageEncoder.java
new file mode 100644
index 0000000..eb2d609
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/BaseMessageEncoder.java
@@ -0,0 +1,62 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.service.protocol.codec;
+
+
+import org.apache.directory.mitosis.service.protocol.message.BaseMessage;
+import org.apache.mina.common.ByteBuffer;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.filter.codec.ProtocolEncoderOutput;
+import org.apache.mina.filter.codec.demux.MessageEncoder;
+
+
+public abstract class BaseMessageEncoder implements MessageEncoder
+{
+    public BaseMessageEncoder()
+    {
+    }
+
+
+    public final void encode( IoSession session, Object in, ProtocolEncoderOutput out ) throws Exception
+    {
+        BaseMessage m = ( BaseMessage ) in;
+        ByteBuffer buf = ByteBuffer.allocate( 16 );
+        buf.setAutoExpand( true );
+        buf.put( ( byte ) m.getType() );
+        buf.putInt( m.getSequence() );
+        buf.putInt( 0 ); // placeholder for body length field
+
+        final int bodyStartPos = buf.position();
+        encodeBody( m, buf );
+        final int bodyEndPos = buf.position();
+        final int bodyLength = bodyEndPos - bodyStartPos;
+
+        // fill bodyLength
+        buf.position( bodyStartPos - 4 );
+        buf.putInt( bodyLength );
+        buf.position( bodyEndPos );
+
+        buf.flip();
+        out.write( buf );
+    }
+
+
+    protected abstract void encodeBody( BaseMessage in, ByteBuffer out ) throws Exception;
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/BeginLogEntriesAckMessageDecoder.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/BeginLogEntriesAckMessageDecoder.java
new file mode 100644
index 0000000..646d78d
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/BeginLogEntriesAckMessageDecoder.java
@@ -0,0 +1,96 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.service.protocol.codec;
+
+
+import java.nio.charset.CharacterCodingException;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+
+import org.apache.directory.mitosis.common.CSNVector;
+import org.apache.directory.mitosis.common.ReplicaId;
+import org.apache.directory.mitosis.common.DefaultCSN;
+import org.apache.directory.mitosis.service.protocol.Constants;
+import org.apache.directory.mitosis.service.protocol.message.BaseMessage;
+import org.apache.directory.mitosis.service.protocol.message.BeginLogEntriesAckMessage;
+import org.apache.mina.common.ByteBuffer;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.filter.codec.ProtocolDecoderException;
+import org.apache.mina.filter.codec.ProtocolDecoderOutput;
+
+
+public class BeginLogEntriesAckMessageDecoder extends ResponseMessageDecoder
+{
+    private final CharsetDecoder utf8decoder;
+
+
+    public BeginLogEntriesAckMessageDecoder()
+    {
+        super( Constants.GET_UPDATE_VECTOR_ACK, 0, 3072 );
+        utf8decoder = Charset.forName( "UTF-8" ).newDecoder();
+    }
+
+
+    protected BaseMessage decodeBody( int sequence, int bodyLength, int responseCode, ByteBuffer in ) throws Exception
+    {
+        if ( responseCode != Constants.OK )
+        {
+            return new BeginLogEntriesAckMessage( sequence, responseCode, null, null );
+        }
+
+        CSNVector purgeVector = new CSNVector();
+        CSNVector updateVector = new CSNVector();
+        BeginLogEntriesAckMessage m = new BeginLogEntriesAckMessage( sequence, responseCode, purgeVector, updateVector );
+        readCSNVector( in, purgeVector );
+        readCSNVector( in, updateVector );
+
+        return m;
+    }
+
+
+    private void readCSNVector( ByteBuffer in, CSNVector updateVector ) throws Exception
+    {
+        int nReplicas = in.getInt();
+        if ( nReplicas < 0 )
+        {
+            throw new ProtocolDecoderException( "Wrong nReplicas: " + nReplicas );
+        }
+
+        for ( ; nReplicas > 0; nReplicas-- )
+        {
+            ReplicaId replicaId;
+            try
+            {
+                replicaId = new ReplicaId( in.getString( utf8decoder ) );
+            }
+            catch ( CharacterCodingException e )
+            {
+                throw new ProtocolDecoderException( "Invalid replicaId", e );
+            }
+
+            updateVector.setCSN( new DefaultCSN( in.getLong(), replicaId, in.getInt() ) );
+        }
+    }
+
+
+    public void finishDecode( IoSession session, ProtocolDecoderOutput out ) throws Exception
+    {
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/BeginLogEntriesAckMessageEncoder.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/BeginLogEntriesAckMessageEncoder.java
new file mode 100644
index 0000000..f1adabb
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/BeginLogEntriesAckMessageEncoder.java
@@ -0,0 +1,99 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.service.protocol.codec;
+
+
+import java.nio.charset.CharacterCodingException;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetEncoder;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import org.apache.mina.common.ByteBuffer;
+import org.apache.directory.mitosis.common.CSN;
+import org.apache.directory.mitosis.common.CSNVector;
+import org.apache.directory.mitosis.common.ReplicaId;
+import org.apache.directory.mitosis.service.protocol.Constants;
+import org.apache.directory.mitosis.service.protocol.message.BaseMessage;
+import org.apache.directory.mitosis.service.protocol.message.BeginLogEntriesAckMessage;
+
+
+public class BeginLogEntriesAckMessageEncoder extends ResponseMessageEncoder
+{
+    private final CharsetEncoder utf8encoder;
+
+
+    public BeginLogEntriesAckMessageEncoder()
+    {
+        utf8encoder = Charset.forName( "UTF-8" ).newEncoder();
+    }
+
+
+    protected void encodeBody( BaseMessage in, ByteBuffer out ) throws Exception
+    {
+        // write out response code
+        super.encodeBody( in, out );
+
+        BeginLogEntriesAckMessage m = ( BeginLogEntriesAckMessage ) in;
+        if ( m.getResponseCode() != Constants.OK )
+        {
+            return;
+        }
+
+        writeCSNVector( out, m.getPurgeVector() );
+        writeCSNVector( out, m.getUpdateVector() );
+    }
+
+
+    private void writeCSNVector( ByteBuffer out, CSNVector csns )
+    {
+        Set replicaIds = csns.getReplicaIds();
+
+        int nReplicas = replicaIds.size();
+        out.putInt( nReplicas );
+        Iterator it = replicaIds.iterator();
+        while ( it.hasNext() )
+        {
+            ReplicaId replicaId = ( ReplicaId ) it.next();
+            CSN csn = csns.getCSN( replicaId );
+            try
+            {
+                out.putString( replicaId.getId(), utf8encoder );
+                out.put( ( byte ) 0x00 );
+                out.putLong( csn.getTimestamp() );
+                out.putInt( csn.getOperationSequence() );
+            }
+            catch ( CharacterCodingException e )
+            {
+                throw new RuntimeException( e );
+            }
+        }
+    }
+
+
+    public Set getMessageTypes()
+    {
+        Set<Class> set = new HashSet<Class>();
+        set.add( BeginLogEntriesAckMessage.class );
+        return set;
+    }
+
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/BeginLogEntriesMessageDecoder.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/BeginLogEntriesMessageDecoder.java
new file mode 100644
index 0000000..3260f56
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/BeginLogEntriesMessageDecoder.java
@@ -0,0 +1,49 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.service.protocol.codec;
+
+
+import org.apache.directory.mitosis.service.protocol.Constants;
+import org.apache.directory.mitosis.service.protocol.message.BaseMessage;
+import org.apache.directory.mitosis.service.protocol.message.BeginLogEntriesMessage;
+import org.apache.mina.common.ByteBuffer;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.filter.codec.ProtocolDecoderOutput;
+
+
+public class BeginLogEntriesMessageDecoder extends BaseMessageDecoder
+{
+
+    public BeginLogEntriesMessageDecoder()
+    {
+        super( Constants.BEGIN_LOG_ENTRIES, 0, 0 );
+    }
+
+
+    protected BaseMessage decodeBody( int sequence, int bodyLength, ByteBuffer in ) throws Exception
+    {
+        return new BeginLogEntriesMessage( sequence );
+    }
+
+
+    public void finishDecode( IoSession session, ProtocolDecoderOutput out ) throws Exception
+    {
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/BeginLogEntriesMessageEncoder.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/BeginLogEntriesMessageEncoder.java
new file mode 100644
index 0000000..2f7d416
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/BeginLogEntriesMessageEncoder.java
@@ -0,0 +1,49 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.service.protocol.codec;
+
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.directory.mitosis.service.protocol.message.BaseMessage;
+import org.apache.directory.mitosis.service.protocol.message.BeginLogEntriesMessage;
+import org.apache.mina.common.ByteBuffer;
+
+
+public class BeginLogEntriesMessageEncoder extends BaseMessageEncoder
+{
+    public BeginLogEntriesMessageEncoder()
+    {
+    }
+
+
+    protected void encodeBody( BaseMessage in, ByteBuffer out )
+    {
+    }
+
+
+    public Set getMessageTypes()
+    {
+        Set<Class> set = new HashSet<Class>();
+        set.add( BeginLogEntriesMessage.class );
+        return set;
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/EndLogEntriesAckMessageDecoder.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/EndLogEntriesAckMessageDecoder.java
new file mode 100644
index 0000000..0a787cf
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/EndLogEntriesAckMessageDecoder.java
@@ -0,0 +1,48 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.service.protocol.codec;
+
+
+import org.apache.directory.mitosis.service.protocol.Constants;
+import org.apache.directory.mitosis.service.protocol.message.BaseMessage;
+import org.apache.directory.mitosis.service.protocol.message.EndLogEntriesAckMessage;
+import org.apache.mina.common.ByteBuffer;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.filter.codec.ProtocolDecoderOutput;
+
+
+public class EndLogEntriesAckMessageDecoder extends ResponseMessageDecoder
+{
+    public EndLogEntriesAckMessageDecoder()
+    {
+        super( Constants.END_LOG_ENTRIES_ACK, 0, 0 );
+    }
+
+
+    protected BaseMessage decodeBody( int sequence, int bodyLength, int responseCode, ByteBuffer in ) throws Exception
+    {
+        return new EndLogEntriesAckMessage( sequence, responseCode );
+    }
+
+
+    public void finishDecode( IoSession session, ProtocolDecoderOutput out ) throws Exception
+    {
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/EndLogEntriesAckMessageEncoder.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/EndLogEntriesAckMessageEncoder.java
new file mode 100644
index 0000000..d5c3ff5
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/EndLogEntriesAckMessageEncoder.java
@@ -0,0 +1,44 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.service.protocol.codec;
+
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.directory.mitosis.service.protocol.message.EndLogEntriesAckMessage;
+
+
+public class EndLogEntriesAckMessageEncoder extends ResponseMessageEncoder
+{
+
+    public EndLogEntriesAckMessageEncoder()
+    {
+        super();
+    }
+
+
+    public Set getMessageTypes()
+    {
+        Set<Class> set = new HashSet<Class>();
+        set.add( EndLogEntriesAckMessage.class );
+        return set;
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/EndLogEntriesMessageDecoder.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/EndLogEntriesMessageDecoder.java
new file mode 100644
index 0000000..d2e67f7
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/EndLogEntriesMessageDecoder.java
@@ -0,0 +1,49 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.service.protocol.codec;
+
+
+import org.apache.directory.mitosis.service.protocol.Constants;
+import org.apache.directory.mitosis.service.protocol.message.BaseMessage;
+import org.apache.directory.mitosis.service.protocol.message.EndLogEntriesMessage;
+import org.apache.mina.common.ByteBuffer;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.filter.codec.ProtocolDecoderOutput;
+
+
+public class EndLogEntriesMessageDecoder extends BaseMessageDecoder
+{
+
+    public EndLogEntriesMessageDecoder()
+    {
+        super( Constants.END_LOG_ENTRIES, 0, 0 );
+    }
+
+
+    protected BaseMessage decodeBody( int sequence, int bodyLength, ByteBuffer in ) throws Exception
+    {
+        return new EndLogEntriesMessage( sequence );
+    }
+
+
+    public void finishDecode( IoSession session, ProtocolDecoderOutput out ) throws Exception
+    {
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/EndLogEntriesMessageEncoder.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/EndLogEntriesMessageEncoder.java
new file mode 100644
index 0000000..058df8b
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/EndLogEntriesMessageEncoder.java
@@ -0,0 +1,49 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.service.protocol.codec;
+
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.directory.mitosis.service.protocol.message.BaseMessage;
+import org.apache.directory.mitosis.service.protocol.message.EndLogEntriesMessage;
+import org.apache.mina.common.ByteBuffer;
+
+
+public class EndLogEntriesMessageEncoder extends BaseMessageEncoder
+{
+    public EndLogEntriesMessageEncoder()
+    {
+    }
+
+
+    protected void encodeBody( BaseMessage in, ByteBuffer out )
+    {
+    }
+
+
+    public Set getMessageTypes()
+    {
+        Set<Class> set = new HashSet<Class>();
+        set.add( EndLogEntriesMessage.class );
+        return set;
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/LogEntryAckMessageDecoder.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/LogEntryAckMessageDecoder.java
new file mode 100644
index 0000000..937787d
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/LogEntryAckMessageDecoder.java
@@ -0,0 +1,48 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.service.protocol.codec;
+
+
+import org.apache.directory.mitosis.service.protocol.Constants;
+import org.apache.directory.mitosis.service.protocol.message.BaseMessage;
+import org.apache.directory.mitosis.service.protocol.message.LogEntryAckMessage;
+import org.apache.mina.common.ByteBuffer;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.filter.codec.ProtocolDecoderOutput;
+
+
+public class LogEntryAckMessageDecoder extends ResponseMessageDecoder
+{
+    public LogEntryAckMessageDecoder()
+    {
+        super( Constants.LOG_ENTRY_ACK, 0, 0 );
+    }
+
+
+    protected BaseMessage decodeBody( int sequence, int bodyLength, int responseCode, ByteBuffer in ) throws Exception
+    {
+        return new LogEntryAckMessage( sequence, responseCode );
+    }
+
+
+    public void finishDecode( IoSession session, ProtocolDecoderOutput out ) throws Exception
+    {
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/LogEntryAckMessageEncoder.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/LogEntryAckMessageEncoder.java
new file mode 100644
index 0000000..8ad066a
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/LogEntryAckMessageEncoder.java
@@ -0,0 +1,45 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.service.protocol.codec;
+
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.directory.mitosis.service.protocol.message.LogEntryAckMessage;
+
+
+public class LogEntryAckMessageEncoder extends ResponseMessageEncoder
+{
+
+    public LogEntryAckMessageEncoder()
+    {
+        super();
+    }
+
+
+    public Set getMessageTypes()
+    {
+        Set<Class> set = new HashSet<Class>();
+        set.add( LogEntryAckMessage.class );
+        return set;
+    }
+
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/LogEntryMessageDecoder.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/LogEntryMessageDecoder.java
new file mode 100644
index 0000000..65a82c7
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/LogEntryMessageDecoder.java
@@ -0,0 +1,55 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.service.protocol.codec;
+
+
+import org.apache.directory.mitosis.operation.OperationCodec;
+import org.apache.directory.mitosis.service.protocol.Constants;
+import org.apache.directory.mitosis.service.protocol.message.BaseMessage;
+import org.apache.directory.mitosis.service.protocol.message.LogEntryMessage;
+import org.apache.mina.common.ByteBuffer;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.filter.codec.ProtocolDecoderOutput;
+
+
+public class LogEntryMessageDecoder extends BaseMessageDecoder
+{
+    private final OperationCodec operationCodec = new OperationCodec();
+
+
+    public LogEntryMessageDecoder()
+    {
+        super( Constants.LOG_ENTRY, 1, Integer.MAX_VALUE );
+    }
+
+
+    protected BaseMessage decodeBody( int sequence, int bodyLength, ByteBuffer in ) throws Exception
+    {
+        byte[] src = new byte[in.remaining()];
+        in.get( src );
+
+        return new LogEntryMessage( sequence, operationCodec.decode( src ) );
+    }
+
+
+    public void finishDecode( IoSession session, ProtocolDecoderOutput out ) throws Exception
+    {
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/LogEntryMessageEncoder.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/LogEntryMessageEncoder.java
new file mode 100644
index 0000000..6402e1c
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/LogEntryMessageEncoder.java
@@ -0,0 +1,56 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.service.protocol.codec;
+
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.directory.mitosis.operation.OperationCodec;
+import org.apache.directory.mitosis.service.protocol.message.BaseMessage;
+import org.apache.directory.mitosis.service.protocol.message.LogEntryMessage;
+import org.apache.mina.common.ByteBuffer;
+
+
+public class LogEntryMessageEncoder extends BaseMessageEncoder
+{
+    private final OperationCodec operationCodec = new OperationCodec();
+
+
+    public LogEntryMessageEncoder()
+    {
+    }
+
+
+    protected void encodeBody( BaseMessage in, ByteBuffer out )
+    {
+        LogEntryMessage m = ( LogEntryMessage ) in;
+        out.put( operationCodec.encode( m.getOperation() ) );
+    }
+
+
+    public Set getMessageTypes()
+    {
+        Set<Class> set = new HashSet<Class>();
+        set.add( LogEntryMessage.class );
+        return set;
+    }
+
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/LoginAckMessageDecoder.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/LoginAckMessageDecoder.java
new file mode 100644
index 0000000..4e85df5
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/LoginAckMessageDecoder.java
@@ -0,0 +1,55 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.service.protocol.codec;
+
+
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+
+import org.apache.mina.common.ByteBuffer;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.filter.codec.ProtocolDecoderOutput;
+import org.apache.directory.mitosis.common.ReplicaId;
+import org.apache.directory.mitosis.service.protocol.Constants;
+import org.apache.directory.mitosis.service.protocol.message.BaseMessage;
+import org.apache.directory.mitosis.service.protocol.message.LoginAckMessage;
+
+
+public class LoginAckMessageDecoder extends ResponseMessageDecoder
+{
+    private final CharsetDecoder utf8decoder = Charset.forName( "UTF-8" ).newDecoder();
+
+
+    public LoginAckMessageDecoder()
+    {
+        super( Constants.LOGIN_ACK, 1, 64 );
+    }
+
+
+    protected BaseMessage decodeBody( int sequence, int bodyLength, int responseCode, ByteBuffer in ) throws Exception
+    {
+        return new LoginAckMessage( sequence, responseCode, new ReplicaId( in.getString( utf8decoder ) ) );
+    }
+
+
+    public void finishDecode( IoSession session, ProtocolDecoderOutput out ) throws Exception
+    {
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/LoginAckMessageEncoder.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/LoginAckMessageEncoder.java
new file mode 100644
index 0000000..24f585e
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/LoginAckMessageEncoder.java
@@ -0,0 +1,58 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.service.protocol.codec;
+
+
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetEncoder;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.directory.mitosis.service.protocol.message.BaseMessage;
+import org.apache.directory.mitosis.service.protocol.message.LoginAckMessage;
+import org.apache.mina.common.ByteBuffer;
+
+
+public class LoginAckMessageEncoder extends ResponseMessageEncoder
+{
+    private final CharsetEncoder utf8encoder = Charset.forName( "UTF-8" ).newEncoder();
+
+
+    public LoginAckMessageEncoder()
+    {
+    }
+
+
+    public Set getMessageTypes()
+    {
+        Set<Class> set = new HashSet<Class>();
+        set.add( LoginAckMessage.class );
+        return set;
+    }
+
+
+    protected void encodeBody( BaseMessage in, ByteBuffer out ) throws Exception
+    {
+        LoginAckMessage m = ( LoginAckMessage ) in;
+        super.encodeBody( in, out );
+        out.putString( m.getReplicaId().getId(), utf8encoder );
+    }
+
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/LoginMessageDecoder.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/LoginMessageDecoder.java
new file mode 100644
index 0000000..965ba04
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/LoginMessageDecoder.java
@@ -0,0 +1,56 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.service.protocol.codec;
+
+
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+
+import org.apache.mina.common.ByteBuffer;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.filter.codec.ProtocolDecoderOutput;
+import org.apache.directory.mitosis.common.ReplicaId;
+import org.apache.directory.mitosis.service.protocol.Constants;
+import org.apache.directory.mitosis.service.protocol.message.BaseMessage;
+import org.apache.directory.mitosis.service.protocol.message.LoginMessage;
+
+
+public class LoginMessageDecoder extends BaseMessageDecoder
+{
+    private final CharsetDecoder utf8decoder;
+
+
+    public LoginMessageDecoder()
+    {
+        super( Constants.LOGIN, 0, 32 );
+        utf8decoder = Charset.forName( "UTF-8" ).newDecoder();
+    }
+
+
+    protected BaseMessage decodeBody( int sequence, int bodyLength, ByteBuffer in ) throws Exception
+    {
+        return new LoginMessage( sequence, new ReplicaId( in.getString( utf8decoder ) ) );
+    }
+
+
+    public void finishDecode( IoSession session, ProtocolDecoderOutput out ) throws Exception
+    {
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/LoginMessageEncoder.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/LoginMessageEncoder.java
new file mode 100644
index 0000000..cd8cfbc
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/LoginMessageEncoder.java
@@ -0,0 +1,67 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.service.protocol.codec;
+
+
+import java.nio.charset.CharacterCodingException;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetEncoder;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.directory.mitosis.service.protocol.message.BaseMessage;
+import org.apache.directory.mitosis.service.protocol.message.LoginMessage;
+import org.apache.mina.common.ByteBuffer;
+
+
+public class LoginMessageEncoder extends BaseMessageEncoder
+{
+    private final CharsetEncoder utf8encoder;
+
+
+    public LoginMessageEncoder()
+    {
+        utf8encoder = Charset.forName( "UTF-8" ).newEncoder();
+    }
+
+
+    protected void encodeBody( BaseMessage in, ByteBuffer out )
+    {
+        LoginMessage m = ( LoginMessage ) in;
+
+        try
+        {
+            out.putString( m.getReplicaId().getId(), utf8encoder );
+        }
+        catch ( CharacterCodingException e )
+        {
+            throw new RuntimeException( e );
+        }
+    }
+
+
+    public Set getMessageTypes()
+    {
+        Set<Class> set = new HashSet<Class>();
+        set.add( LoginMessage.class );
+
+        return set;
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/ReplicationClientProtocolCodecFactory.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/ReplicationClientProtocolCodecFactory.java
new file mode 100644
index 0000000..4d99616
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/ReplicationClientProtocolCodecFactory.java
@@ -0,0 +1,41 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.service.protocol.codec;
+
+
+import org.apache.mina.filter.codec.demux.DemuxingProtocolCodecFactory;
+
+
+public class ReplicationClientProtocolCodecFactory extends DemuxingProtocolCodecFactory
+{
+
+    public ReplicationClientProtocolCodecFactory()
+    {
+        register( LogEntryMessageEncoder.class );
+        register( LoginMessageEncoder.class );
+        register( BeginLogEntriesMessageEncoder.class );
+        register( EndLogEntriesMessageEncoder.class );
+
+        register( LogEntryAckMessageDecoder.class );
+        register( LoginAckMessageDecoder.class );
+        register( BeginLogEntriesAckMessageDecoder.class );
+        register( EndLogEntriesAckMessageDecoder.class );
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/ReplicationServerProtocolCodecFactory.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/ReplicationServerProtocolCodecFactory.java
new file mode 100644
index 0000000..7106008
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/ReplicationServerProtocolCodecFactory.java
@@ -0,0 +1,41 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.service.protocol.codec;
+
+
+import org.apache.mina.filter.codec.demux.DemuxingProtocolCodecFactory;
+
+
+public class ReplicationServerProtocolCodecFactory extends DemuxingProtocolCodecFactory
+{
+
+    public ReplicationServerProtocolCodecFactory()
+    {
+        register( LogEntryAckMessageEncoder.class );
+        register( LoginAckMessageEncoder.class );
+        register( BeginLogEntriesAckMessageEncoder.class );
+        register( EndLogEntriesAckMessageEncoder.class );
+
+        register( LogEntryMessageDecoder.class );
+        register( LoginMessageDecoder.class );
+        register( BeginLogEntriesMessageDecoder.class );
+        register( EndLogEntriesMessageDecoder.class );
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/ResponseMessageDecoder.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/ResponseMessageDecoder.java
new file mode 100644
index 0000000..1566411
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/ResponseMessageDecoder.java
@@ -0,0 +1,45 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.service.protocol.codec;
+
+
+import org.apache.directory.mitosis.service.protocol.message.BaseMessage;
+import org.apache.mina.common.ByteBuffer;
+
+
+public abstract class ResponseMessageDecoder extends BaseMessageDecoder
+{
+
+    protected ResponseMessageDecoder( int type, int minBodyLength, int maxBodyLength )
+    {
+        super( type, minBodyLength + 4, maxBodyLength + 4 );
+    }
+
+
+    protected final BaseMessage decodeBody( int sequence, int bodyLength, ByteBuffer in ) throws Exception
+    {
+        return decodeBody( sequence, bodyLength, in.getInt(), in );
+    }
+
+
+    protected abstract BaseMessage decodeBody( int sequence, int bodyLength, int responseCode, ByteBuffer in )
+        throws Exception;
+
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/ResponseMessageEncoder.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/ResponseMessageEncoder.java
new file mode 100644
index 0000000..1b8d747
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/codec/ResponseMessageEncoder.java
@@ -0,0 +1,40 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.service.protocol.codec;
+
+
+import org.apache.directory.mitosis.service.protocol.message.BaseMessage;
+import org.apache.directory.mitosis.service.protocol.message.ResponseMessage;
+import org.apache.mina.common.ByteBuffer;
+
+
+public abstract class ResponseMessageEncoder extends BaseMessageEncoder
+{
+    public ResponseMessageEncoder()
+    {
+    }
+
+
+    protected void encodeBody( BaseMessage in, ByteBuffer out ) throws Exception
+    {
+        ResponseMessage m = ( ResponseMessage ) in;
+        out.putInt( m.getResponseCode() );
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/handler/ReplicationClientContextHandler.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/handler/ReplicationClientContextHandler.java
new file mode 100644
index 0000000..ddb7a24
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/handler/ReplicationClientContextHandler.java
@@ -0,0 +1,478 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.service.protocol.handler;
+
+
+import org.apache.directory.mitosis.common.CSN;
+import org.apache.directory.mitosis.common.CSNVector;
+import org.apache.directory.mitosis.common.DefaultCSN;
+import org.apache.directory.mitosis.common.Replica;
+import org.apache.directory.mitosis.common.ReplicaId;
+import org.apache.directory.mitosis.configuration.ReplicationConfiguration;
+import org.apache.directory.mitosis.operation.AddEntryOperation;
+import org.apache.directory.mitosis.operation.Operation;
+import org.apache.directory.mitosis.service.ReplicationContext;
+import org.apache.directory.mitosis.service.ReplicationContext.State;
+import org.apache.directory.mitosis.service.protocol.Constants;
+import org.apache.directory.mitosis.service.protocol.message.BaseMessage;
+import org.apache.directory.mitosis.service.protocol.message.BeginLogEntriesAckMessage;
+import org.apache.directory.mitosis.service.protocol.message.BeginLogEntriesMessage;
+import org.apache.directory.mitosis.service.protocol.message.EndLogEntriesAckMessage;
+import org.apache.directory.mitosis.service.protocol.message.EndLogEntriesMessage;
+import org.apache.directory.mitosis.service.protocol.message.LogEntryAckMessage;
+import org.apache.directory.mitosis.service.protocol.message.LogEntryMessage;
+import org.apache.directory.mitosis.service.protocol.message.LoginAckMessage;
+import org.apache.directory.mitosis.service.protocol.message.LoginMessage;
+import org.apache.directory.mitosis.store.ReplicationLogIterator;
+import org.apache.directory.mitosis.store.ReplicationStore;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerSearchResult;
+import org.apache.directory.server.core.interceptor.context.SearchOperationContext;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.entry.Value;
+import org.apache.directory.shared.ldap.filter.PresenceNode;
+import org.apache.directory.shared.ldap.message.AliasDerefMode;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.schema.OidNormalizer;
+import org.apache.directory.shared.ldap.util.StringTools;
+import org.apache.mina.common.IdleStatus;
+import org.apache.mina.common.WriteFuture;
+import org.apache.mina.util.SessionLog;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.SearchControls;
+import java.net.InetSocketAddress;
+import java.util.Map;
+
+
+/**
+ * {@link ReplicationContextHandler} that implements client-side replication
+ * logic which sends any changes out-of-date to server.  The following is
+ * the detailed protocol flow and the description of the replication logic
+ * execution.
+ * <ul>
+ * <li><tt>ClientConnectionManager</tt> connects the client to the server.</li>
+ * <li>The client sends {@link LoginMessage} to the server.</li>
+ * <li>The server responds with {@link LoginAckMessage} to the client
+ *     <ul>
+ *     <li>Unless the response code is {@link Constants#OK}, disconnect.
+ *         Next connection attempt is performed by
+ *         <tt>ClientConnectionManager</tt> later.</li>
+ *     <li>Otherwise, the state of the {@link ReplicationContext} changes to
+ *         {@link State#READY}, and proceed.</li>
+ *     </ul></li>
+ * <li>The client tries to transfer the data that server needs from
+ *     in {@link ReplicationStore} periodically using
+ *     {@link #contextIdle(ReplicationContext, IdleStatus)} event,
+ *     which is implemented using <tt>sessionIdle</tt> event in MINA. 
+ *     <ul>
+ *     <li>The client sends a {@link BeginLogEntriesMessage} to the server.</li>
+ *     <li>The server responds with {@link BeginLogEntriesAckMessage}.
+ *         <ul>
+ *         <li>If the response code is {@link Constants#OK},
+ *             <ul>
+ *             <li>{@link BeginLogEntriesAckMessage} contains a
+ *                 Update Vector (UV) of the server. The client compares
+ *                 the received UV and the client's Purge Vector (PV).
+ *                 <ul>
+ *                 <li>If the PV is greater than the UV, this means the client
+ *                     can't send all operation logs that server needs to get
+ *                     synchronized.  This usually means that the server has
+ *                     been offline for too long time and got out-of-sync
+ *                     finally due to the log-purging process of the client
+ *                     side (see {@link ReplicationConfiguration#getLogMaxAge()}).
+ *                     The clients sends all entries in the DIT to the server,
+ *                     and the server overwrites its current DIT with the
+ *                     received entries.</li>
+ *                 <li>Otherwise, the client sends only the changed part since
+ *                     the last synchronization by querying its
+ *                     {@link ReplicationStore} by calling
+ *                     {@link ReplicationStore#getLogs(CSNVector, boolean)}.</li>
+ *                 <li>The data transfer is very simple.  It's asynchronous
+ *                     request-response exchange.  The client sends {@link LogEntryMessage},
+ *                     and then the server responds with {@link LogEntryAckMessage}.</li>
+ *             </ul></li>
+ *         <li>If the response code is not {@link Constants#OK}, retry later.</li>
+ *         </ul></li>
+ *     </ul></li>
+ * </ul>
+ *
+ * @author The Apache Directory Project (dev@directory.apache.org)
+ * @version $Rev: 116 $, $Date: 2006-09-18 13:47:53Z $
+ */
+public class ReplicationClientContextHandler implements ReplicationContextHandler
+{
+    public void contextBegin( ReplicationContext ctx ) throws Exception
+    {
+        // Send a login message.
+        LoginMessage m = new LoginMessage( ctx.getNextSequence(), ctx.getService().getConfiguration().getReplicaId() );
+        writeTimeLimitedMessage( ctx, m );
+
+        // Set write timeout
+        ctx.getSession().setWriteTimeout( ctx.getConfiguration().getResponseTimeout() );
+
+        // Check update vector of the remote peer periodically.
+        ctx.getSession().setIdleTime( IdleStatus.BOTH_IDLE, ctx.getConfiguration().getReplicationInterval() );
+    }
+
+
+    public void contextEnd( ReplicationContext ctx ) throws Exception
+    {
+    }
+
+
+    public void messageReceived( ReplicationContext ctx, Object message ) throws Exception
+    {
+        ctx.cancelExpiration( ( ( BaseMessage ) message ).getSequence() );
+
+        if ( ctx.getState() == State.READY )
+        {
+            if ( message instanceof LogEntryAckMessage )
+            {
+                onLogEntryAck( ctx, ( LogEntryAckMessage ) message );
+            }
+            else if ( message instanceof BeginLogEntriesAckMessage )
+            {
+                onBeginLogEntriesAck( ctx, ( BeginLogEntriesAckMessage ) message );
+            }
+            else if ( message instanceof EndLogEntriesAckMessage )
+            {
+                // Do nothing
+            }
+            else
+            {
+                onUnexpectedMessage( ctx, message );
+            }
+        }
+        else
+        {
+            if ( message instanceof LoginAckMessage )
+            {
+                onLoginAck( ctx, ( LoginAckMessage ) message );
+            }
+            else
+            {
+                onUnexpectedMessage( ctx, message );
+            }
+        }
+    }
+
+
+    public void messageSent( ReplicationContext ctx, Object message ) throws Exception
+    {
+    }
+
+
+    /**
+     * A helper to write a message and schedule that message for expiration.
+     *
+     * @param ctx the replication context
+     * @param message the message to replicate
+     * @return the write future to block on this replication message transmission
+     */
+    public WriteFuture writeTimeLimitedMessage( ReplicationContext ctx, Object message )
+    {
+        ctx.scheduleExpiration( message );
+        return ctx.getSession().write( message );
+    }
+
+
+    public void exceptionCaught( ReplicationContext ctx, Throwable cause ) throws Exception
+    {
+        if ( SessionLog.isWarnEnabled( ctx.getSession() ) )
+        {
+            SessionLog.warn( ctx.getSession(), "[Replica-" + ctx.getConfiguration().getReplicaId()
+                + "] Unexpected exception.", cause );
+        }
+        ctx.getSession().close();
+    }
+
+
+    public void contextIdle( ReplicationContext ctx, IdleStatus status ) throws Exception
+    {
+        beginReplication( ctx );
+    }
+
+
+    private void onLoginAck( ReplicationContext ctx, LoginAckMessage message )
+    {
+        if ( message.getResponseCode() != Constants.OK )
+        {
+            SessionLog.warn( ctx.getSession(), "[Replica-" + ctx.getConfiguration().getReplicaId()
+                + "] Login attempt failed: " + message.getResponseCode() );
+            ctx.getSession().close();
+            return;
+        }
+
+        for ( Replica replica : ctx.getConfiguration().getPeerReplicas() )
+        {
+            if ( replica.getId().equals( message.getReplicaId() ) )
+            {
+                if ( replica.getAddress().getAddress().equals(
+                    ( ( InetSocketAddress ) ctx.getSession().getRemoteAddress() ).getAddress() ) )
+                {
+                    ctx.setPeer( replica );
+                    ctx.setState( State.READY );
+                    return;
+                }
+                else
+                {
+                    SessionLog.warn( ctx.getSession(), "[Replica-" + ctx.getConfiguration().getReplicaId()
+                        + "] Peer address mismatches: " + ctx.getSession().getRemoteAddress() + " (expected: "
+                        + replica.getAddress() );
+                    ctx.getSession().close();
+                    return;
+                }
+            }
+        }
+
+        SessionLog.warn( ctx.getSession(), "[Replica-" + ctx.getConfiguration().getReplicaId()
+            + "] Unknown peer replica ID: " + message.getReplicaId() );
+        ctx.getSession().close();
+    }
+
+
+    public boolean beginReplication( ReplicationContext ctx )
+    {
+        // If this cilent is logged in, all responses for sent messages
+        // (LogEntryMessages) is received, and no write request is pending,
+        // it means previous replication process ended or this is the
+        // first replication attempt.
+        if ( ctx.getState() == State.READY && ctx.getScheduledExpirations() <= 0
+            && ctx.getSession().getScheduledWriteRequests() <= 0 )
+        {
+            // Initiate replication process asking update vector.
+            if ( SessionLog.isDebugEnabled( ctx.getSession() ) )
+            {
+                SessionLog.debug( ctx.getSession(), "(" + ctx.getConfiguration().getReplicaId().getId() + "->"
+                    + ( ctx.getPeer() != null ? ctx.getPeer().getId().getId() : "null" ) + ") Beginning replication. " );
+            }
+            ctx.getSession().write( new BeginLogEntriesMessage( ctx.getNextSequence() ) );
+            return true;
+        }
+        else
+        {
+            if ( SessionLog.isDebugEnabled( ctx.getSession() ) )
+            {
+                SessionLog.debug( ctx.getSession(), "(" + ctx.getConfiguration().getReplicaId().getId() + "->"
+                    + ( ctx.getPeer() != null ? ctx.getPeer().getId().getId() : "null" )
+                    + ") Couldn't begin replication.  State:" + ctx.getState() + ", scheduledExpirations:"
+                    + ctx.getScheduledExpirations() + ", scheduledWriteRequests:"
+                    + ctx.getSession().getScheduledWriteRequests() );
+            }
+            return false;
+        }
+    }
+
+
+    private void onLogEntryAck( ReplicationContext ctx, LogEntryAckMessage message ) throws Exception
+    {
+        if ( message.getResponseCode() != Constants.OK )
+        {
+            SessionLog.warn( ctx.getSession(), "[Replica-" + ctx.getConfiguration().getReplicaId()
+                + "] Remote peer failed to execute a log entry." );
+            ctx.getSession().close();
+        }
+    }
+
+
+    private void onBeginLogEntriesAck( ReplicationContext ctx, BeginLogEntriesAckMessage message )
+        throws NamingException
+    {
+        // Start transaction only when the server says OK.
+        if ( message.getResponseCode() != Constants.OK )
+        {
+            return;
+        }
+
+        ReplicationStore store = ctx.getConfiguration().getStore();
+        CSNVector yourUV = message.getUpdateVector();
+        CSNVector myPV;
+        try
+        {
+            myPV = store.getPurgeVector();
+        }
+        catch ( Exception e )
+        {
+            SessionLog.warn( ctx.getSession(), "[Replica-" + ctx.getConfiguration().getReplicaId()
+                + "] Failed to get update vector.", e );
+            ctx.getSession().close();
+            return;
+        }
+
+        // Do full-DIT transfer if the peer is new and I'm not new.
+        try
+        {
+            if ( myPV.size() > 0 && yourUV.size() == 0 )
+            {
+                SessionLog.warn( ctx.getSession(), "[Replica-" + ctx.getConfiguration().getReplicaId()
+                    + "] Starting a whole DIT transfer." );
+                sendAllEntries( ctx );
+            }
+            else
+            {
+                SessionLog.warn( ctx.getSession(), "[Replica-" + ctx.getConfiguration().getReplicaId()
+                    + "] Starting a partial replication log transfer." );
+                sendReplicationLogs( ctx, myPV, yourUV );
+            }
+        }
+        finally
+        {
+            // Send EngLogEntries message to release the remote peer resources.
+            ctx.getSession().write( new EndLogEntriesMessage( ctx.getNextSequence() ) );
+        }
+    }
+
+
+    private void sendAllEntries( ReplicationContext ctx ) throws NamingException
+    {
+        ServerEntry rootDSE = ctx.getDirectoryService().getPartitionNexus().getRootDSE( null );
+
+        EntryAttribute namingContextsAttr = rootDSE.get( SchemaConstants.NAMING_CONTEXTS_AT );
+
+        if ( namingContextsAttr == null || namingContextsAttr.size() == 0 )
+        {
+            SessionLog.warn( ctx.getSession(), "[Replica-" + ctx.getConfiguration().getReplicaId()
+                + "] No namingContexts attributes in rootDSE." );
+            return;
+        }
+
+        // Iterate all context partitions to send all entries of them.
+        for ( Value<?> namingContext : namingContextsAttr )
+        {
+            // Convert attribute value to JNDI name.
+            LdapDN contextName;
+
+            contextName = new LdapDN( ( String ) namingContext.get() );
+
+            SessionLog.info( ctx.getSession(), "[Replica-" + ctx.getConfiguration().getReplicaId()
+                + "] Sending entries under '" + contextName + '\'' );
+
+            Map<String, OidNormalizer> mapping = ctx.getDirectoryService().getRegistries().getAttributeTypeRegistry()
+                .getNormalizerMapping();
+            contextName.normalize( mapping );
+            sendAllEntries( ctx, contextName );
+        }
+    }
+
+
+    private void sendAllEntries( ReplicationContext ctx, LdapDN contextName ) throws NamingException
+    {
+        // Retrieve all subtree including the base entry
+        SearchControls ctrl = new SearchControls();
+        ctrl.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        NamingEnumeration<ServerSearchResult> e = ctx.getDirectoryService().getPartitionNexus().search(
+            new SearchOperationContext( ctx.getDirectoryService().getRegistries(), contextName,
+                AliasDerefMode.DEREF_ALWAYS, new PresenceNode( SchemaConstants.OBJECT_CLASS_AT_OID ), ctrl ) );
+
+        try
+        {
+            while ( e.hasMore() )
+            {
+                ServerSearchResult sr = e.next();
+                ServerEntry attrs = sr.getServerEntry();
+
+                // Skip entries without entryCSN attribute.
+                EntryAttribute entryCSNAttr = attrs.get( org.apache.directory.mitosis.common.Constants.ENTRY_CSN );
+
+                if ( entryCSNAttr == null )
+                {
+                    continue;
+                }
+
+                // Get entryCSN of the entry.  Skip if entryCSN value is invalid. 
+                CSN csn;
+
+                try
+                {
+                    Object val = entryCSNAttr.get();
+
+                    if ( val instanceof byte[] )
+                    {
+                        csn = new DefaultCSN( StringTools.utf8ToString( ( byte[] ) val ) );
+                    }
+                    else
+                    {
+                        csn = new DefaultCSN( ( String ) val );
+                    }
+                }
+                catch ( IllegalArgumentException ex )
+                {
+                    SessionLog.warn( ctx.getSession(), "An entry with improper entryCSN: " + sr.getDn() );
+                    continue;
+                }
+
+                // Convert the entry into AddEntryOperation log.
+                LdapDN dn = sr.getDn();
+                dn.normalize( ctx.getDirectoryService().getRegistries().getAttributeTypeRegistry()
+                    .getNormalizerMapping() );
+                Operation op = new AddEntryOperation( csn, attrs );
+
+                // Send a LogEntry message for the entry.
+                writeTimeLimitedMessage( ctx, new LogEntryMessage( ctx.getNextSequence(), op ) );
+            }
+        }
+        finally
+        {
+            e.close();
+        }
+    }
+
+
+    @SuppressWarnings("unchecked")
+    private void sendReplicationLogs( ReplicationContext ctx, CSNVector myPV, CSNVector yourUV )
+    {
+        for ( ReplicaId replicaId : myPV.getReplicaIds() )
+        {
+            CSN myCSN = myPV.getCSN( replicaId );
+            CSN yourCSN = yourUV.getCSN( replicaId );
+            if ( yourCSN != null && ( myCSN == null || yourCSN.compareTo( myCSN ) < 0 ) )
+            {
+                SessionLog.warn( ctx.getSession(), "Remote update vector (" + yourUV
+                    + ") is out-of-date.  Full replication is required." );
+                ctx.getSession().close();
+                return;
+            }
+        }
+
+        ReplicationLogIterator logIt = ctx.getConfiguration().getStore().getLogs( yourUV, false );
+        try
+        {
+            while ( logIt.next() )
+            {
+                Operation op = logIt.getOperation();
+                writeTimeLimitedMessage( ctx, new LogEntryMessage( ctx.getNextSequence(), op ) );
+            }
+        }
+        finally
+        {
+            logIt.close();
+        }
+    }
+
+
+    private void onUnexpectedMessage( ReplicationContext ctx, Object message )
+    {
+        SessionLog.warn( ctx.getSession(), "Unexpected message: " + message );
+        ctx.getSession().close();
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/handler/ReplicationClientProtocolHandler.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/handler/ReplicationClientProtocolHandler.java
new file mode 100644
index 0000000..19522b4
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/handler/ReplicationClientProtocolHandler.java
@@ -0,0 +1,40 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.service.protocol.handler;
+
+
+import org.apache.directory.mitosis.service.ReplicationInterceptor;
+
+/**
+ * A {@link ReplicationProtocolHandler} for client-side, which uses
+ * {@link ReplicationClientContextHandler}.  This class is actually 
+ * a shortcut to creating a {@link ReplicationProtocolHandler} with a
+ * new {@link ReplicationClientContextHandler}.
+ * 
+ * @author The Apache Directory Project (dev@directory.apache.org)
+ * @version $Rev$, $Date$
+ */
+public class ReplicationClientProtocolHandler extends ReplicationProtocolHandler
+{
+    public ReplicationClientProtocolHandler( ReplicationInterceptor interceptor )
+    {
+        super( interceptor, new ReplicationClientContextHandler() );
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/handler/ReplicationContextHandler.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/handler/ReplicationContextHandler.java
new file mode 100644
index 0000000..dbba670
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/handler/ReplicationContextHandler.java
@@ -0,0 +1,71 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.service.protocol.handler;
+
+
+import org.apache.directory.mitosis.service.ReplicationContext;
+import org.apache.mina.common.IdleStatus;
+import org.apache.mina.common.IoHandler;
+
+/**
+ * An interface that provides handler methods for events which occurs
+ * when a two replicas communicate with each other.  This interface is
+ * very similar to MINA {@link IoHandler}, but there's a difference
+ * in that this interface provide a {@link ReplicationContext} instead of
+ * an {@link IoHandler}.  It's usually wrapped by
+ * {@link ReplicationProtocolHandler} to work with MINA.
+ * 
+ * @author The Apache Directory Project (dev@directory.apache.org)
+ * @version $Rev$, $Date$
+ */
+public interface ReplicationContextHandler
+{
+    /**
+     * Invoked when a connection is established between two replicas.
+     */
+    void contextBegin( ReplicationContext ctx ) throws Exception;
+
+    /**
+     * Invoked when a connection is closed between two replicas.
+     */
+    void contextEnd( ReplicationContext ctx ) throws Exception;
+
+    /**
+     * Invoked when a message is received from a peer replica.
+     */
+    void messageReceived( ReplicationContext ctx, Object message ) throws Exception;
+
+    /**
+     * Invoked when a message is received from a peer replica.
+     */
+    void messageSent( ReplicationContext ctx, Object message ) throws Exception;
+
+    /**
+     * Invoked when an exception is raised during the communication or
+     * executing replication logic.
+     */
+    void exceptionCaught( ReplicationContext ctx, Throwable cause ) throws Exception;
+
+    /**
+     * Invoked when two replicas are not exchanging any data for certain
+     * amount of time.
+     */
+    void contextIdle( ReplicationContext ctx, IdleStatus status ) throws Exception;
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/handler/ReplicationProtocolHandler.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/handler/ReplicationProtocolHandler.java
new file mode 100644
index 0000000..af99152
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/handler/ReplicationProtocolHandler.java
@@ -0,0 +1,114 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.service.protocol.handler;
+
+
+import org.apache.directory.mitosis.configuration.ReplicationConfiguration;
+import org.apache.directory.mitosis.service.DefaultReplicationContext;
+import org.apache.directory.mitosis.service.ReplicationContext;
+import org.apache.directory.mitosis.service.ReplicationInterceptor;
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.mina.common.IdleStatus;
+import org.apache.mina.common.IoHandler;
+import org.apache.mina.common.IoSession;
+
+/**
+ * A MINA {@link IoHandler} that wraps a {@link ReplicationContextHandler}
+ * and provides a default implementation of {@link ReplicationContext}.
+ *  
+ * @author The Apache Directory Project (dev@directory.apache.org)
+ * @version $Rev$, $Date$
+ */
+public class ReplicationProtocolHandler implements IoHandler
+{
+    private static final String CONTEXT = "context";
+
+    private final ReplicationInterceptor interceptor;
+    private final ReplicationConfiguration configuration;
+    private final DirectoryService directoryService;
+    private final ReplicationContextHandler contextHandler;
+
+
+    public ReplicationProtocolHandler( ReplicationInterceptor interceptor, ReplicationContextHandler contextHandler )
+    {
+        assert interceptor != null;
+        assert contextHandler != null;
+
+        this.interceptor = interceptor;
+        this.configuration = interceptor.getConfiguration();
+        this.directoryService = interceptor.getDirectoryService();
+        this.contextHandler = contextHandler;
+    }
+
+
+    public ReplicationContext getContext( IoSession session )
+    {
+        return ( ReplicationContext ) session.getAttribute( CONTEXT );
+    }
+    
+    public ReplicationContextHandler getContextHandler() {
+        return contextHandler;
+    }
+
+
+    public void sessionCreated( IoSession session ) throws Exception
+    {
+        session.setAttribute( CONTEXT,
+                new DefaultReplicationContext( interceptor, directoryService, configuration, session ) );
+    }
+
+
+    public void exceptionCaught( IoSession session, Throwable cause ) throws Exception
+    {
+        contextHandler.exceptionCaught( getContext( session ), cause );
+    }
+
+
+    public void messageReceived( IoSession session, Object message ) throws Exception
+    {
+        contextHandler.messageReceived( getContext( session ), message );
+    }
+
+
+    public void messageSent( IoSession session, Object message ) throws Exception
+    {
+        contextHandler.messageSent( getContext( session ), message );
+    }
+
+
+    public void sessionClosed( IoSession session ) throws Exception
+    {
+        ReplicationContext ctx = getContext( session );
+        contextHandler.contextEnd( ctx );
+        ctx.cancelAllExpirations();
+    }
+
+
+    public void sessionIdle( IoSession session, IdleStatus status ) throws Exception
+    {
+        contextHandler.contextIdle( getContext( session ), status );
+    }
+
+
+    public void sessionOpened( IoSession session ) throws Exception
+    {
+        contextHandler.contextBegin( getContext( session ) );
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/handler/ReplicationServerContextHandler.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/handler/ReplicationServerContextHandler.java
new file mode 100644
index 0000000..ff6b80c
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/handler/ReplicationServerContextHandler.java
@@ -0,0 +1,258 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.service.protocol.handler;
+
+
+import org.apache.directory.mitosis.common.CSNVector;
+import org.apache.directory.mitosis.common.Replica;
+import org.apache.directory.mitosis.operation.Operation;
+import org.apache.directory.mitosis.service.ReplicationContext;
+import org.apache.directory.mitosis.service.ReplicationContext.State;
+import org.apache.directory.mitosis.service.protocol.Constants;
+import org.apache.directory.mitosis.service.protocol.message.BeginLogEntriesAckMessage;
+import org.apache.directory.mitosis.service.protocol.message.BeginLogEntriesMessage;
+import org.apache.directory.mitosis.service.protocol.message.EndLogEntriesAckMessage;
+import org.apache.directory.mitosis.service.protocol.message.EndLogEntriesMessage;
+import org.apache.directory.mitosis.service.protocol.message.LogEntryAckMessage;
+import org.apache.directory.mitosis.service.protocol.message.LogEntryMessage;
+import org.apache.directory.mitosis.service.protocol.message.LoginAckMessage;
+import org.apache.directory.mitosis.service.protocol.message.LoginMessage;
+import org.apache.directory.mitosis.store.ReplicationStore;
+import org.apache.mina.common.IdleStatus;
+import org.apache.mina.util.SessionLog;
+
+import java.net.InetSocketAddress;
+
+
+/**
+ * {@link ReplicationContextHandler} that implements server-side replication logic
+ * which retrieves any changes occurred in remote replicas.  Please refer to
+ * {@link ReplicationClientContextHandler} for the detailed protocol flow and
+ * the description of the replication logic execution.
+ *
+ * @author The Apache Directory Project (dev@directory.apache.org)
+ * @version $Rev: 116 $, $Date: 2006-09-18 13:47:53Z $
+ */
+public class ReplicationServerContextHandler implements ReplicationContextHandler
+{
+    private Replica replicaInTransaction;
+
+
+    public void contextBegin( ReplicationContext ctx ) throws Exception
+    {
+        // Set login timeout
+        ctx.getSession().setIdleTime( IdleStatus.BOTH_IDLE, ctx.getConfiguration().getResponseTimeout() );
+
+        // Set write timeout
+        ctx.getSession().setWriteTimeout( ctx.getConfiguration().getResponseTimeout() );
+    }
+
+
+    public synchronized void contextEnd( ReplicationContext ctx ) throws Exception
+    {
+        // Reset the mark if the context has the unfinished transaction.
+        if ( ctx.getPeer() != null && ctx.getPeer().equals( replicaInTransaction ) )
+        {
+            replicaInTransaction = null;
+        }
+    }
+
+
+    public void messageReceived( ReplicationContext ctx, Object message ) throws Exception
+    {
+        if ( ctx.getState() == State.READY )
+        {
+            if ( message instanceof LogEntryMessage )
+            {
+                onLogEntry( ctx, ( LogEntryMessage ) message );
+            }
+            else if ( message instanceof BeginLogEntriesMessage )
+            {
+                onBeginLogEntries( ctx, ( BeginLogEntriesMessage ) message );
+            }
+            else if ( message instanceof EndLogEntriesMessage )
+            {
+                onEndLogEntries( ctx, ( EndLogEntriesMessage ) message );
+            }
+            else
+            {
+                onUnexpectedMessage( ctx, message );
+            }
+        }
+        else
+        {
+            if ( message instanceof LoginMessage )
+            {
+                onLogin( ctx, ( LoginMessage ) message );
+            }
+            else
+            {
+                onUnexpectedMessage( ctx, message );
+            }
+        }
+    }
+
+
+    public void messageSent( ReplicationContext ctx, Object message ) throws Exception
+    {
+    }
+
+
+    public void exceptionCaught( ReplicationContext ctx, Throwable cause ) throws Exception
+    {
+        SessionLog.warn( ctx.getSession(), "[Replica-" + ctx.getConfiguration().getReplicaId()
+                + "] Unexpected exception.", cause );
+        ctx.getSession().close();
+    }
+
+
+    public void contextIdle( ReplicationContext ctx, IdleStatus status ) throws Exception
+    {
+        if ( ctx.getState() == State.INIT )
+        {
+            SessionLog.warn( ctx.getSession(), "[Replica-" + ctx.getConfiguration().getReplicaId()
+                + "] No login attempt in " + ctx.getConfiguration().getResponseTimeout()
+                + " second(s)." );
+            ctx.getSession().close();
+        }
+    }
+
+
+    private void onLogin( ReplicationContext ctx, LoginMessage message )
+    {
+        for ( Replica replica : ctx.getConfiguration().getPeerReplicas() )
+        {
+            if ( replica.getId().equals( message.getReplicaId() ) )
+            {
+                if ( replica.getAddress().getAddress().equals(
+                        ( ( InetSocketAddress ) ctx.getSession().getRemoteAddress() ).getAddress() ) )
+                {
+                    ctx.getSession()
+                            .write(
+                                    new LoginAckMessage( message.getSequence(), Constants.OK, ctx.getConfiguration()
+                                            .getReplicaId() ) );
+                    ctx.setPeer( replica );
+                    ctx.setState( State.READY );
+
+                    // Clear login timeout.
+                    ctx.getSession().setIdleTime( IdleStatus.BOTH_IDLE, 0 );
+                    return;
+                }
+                else
+                {
+                    SessionLog.warn( ctx.getSession(), "[Replica-" + ctx.getConfiguration().getReplicaId()
+                            + "] Peer address mismatches: "
+                            + ctx.getSession().getRemoteAddress() + " (expected: " + replica.getAddress() );
+                    ctx.getSession().write(
+                            new LoginAckMessage( message.getSequence(), Constants.NOT_OK, ctx.getConfiguration()
+                                    .getReplicaId() ) );
+                    ctx.getSession().close();
+                    return;
+                }
+            }
+        }
+
+        SessionLog.warn( ctx.getSession(), "[Replica-" + ctx.getConfiguration().getReplicaId()
+                + "] Unknown peer replica ID: " + message.getReplicaId() );
+        ctx.getSession().write(
+            new LoginAckMessage( message.getSequence(), Constants.NOT_OK, ctx.getConfiguration().getReplicaId() ) );
+        ctx.getSession().close();
+    }
+
+
+    private synchronized void onLogEntry( ReplicationContext ctx, LogEntryMessage message ) throws Exception
+    {
+        // Return error if other replica than what is in progress sends
+        // a log entry
+        if ( !ctx.getPeer().equals( replicaInTransaction ) )
+        {
+            ctx.getSession().write( new LogEntryAckMessage( message.getSequence(), Constants.NOT_OK ) );
+            return;
+        }
+
+        Operation op = message.getOperation();
+        LogEntryAckMessage ack = null;
+        try
+        {
+            op.execute( ctx.getDirectoryService().getPartitionNexus(), ctx.getConfiguration().getStore(),
+                ctx.getDirectoryService().getRegistries() );
+            ack = new LogEntryAckMessage( message.getSequence(), Constants.OK );
+        }
+        catch ( Exception e )
+        {
+            ack = new LogEntryAckMessage( message.getSequence(), Constants.NOT_OK );
+            throw e;
+        }
+        finally
+        {
+            ctx.getSession().write( ack );
+        }
+    }
+
+
+    private synchronized void onBeginLogEntries( ReplicationContext ctx, BeginLogEntriesMessage message )
+    {
+        // Return error if the transaction is already in progress.
+        if ( replicaInTransaction != null )
+        {
+            ctx.getSession()
+                .write( new BeginLogEntriesAckMessage( message.getSequence(), Constants.NOT_OK, null, null ) );
+            return;
+        }
+
+        ReplicationStore store = ctx.getConfiguration().getStore();
+        try
+        {
+            CSNVector pv = store.getPurgeVector();
+            CSNVector uv = store.getUpdateVector();
+            replicaInTransaction = ctx.getPeer(); // Mark as replica in transaction
+            ctx.getSession().write( new BeginLogEntriesAckMessage( message.getSequence(), Constants.OK, pv, uv ) );
+        }
+        catch ( Exception e )
+        {
+            SessionLog.warn( ctx.getSession(), "Failed to get update vector.", e );
+            ctx.getSession()
+                .write( new BeginLogEntriesAckMessage( message.getSequence(), Constants.NOT_OK, null, null ) );
+        }
+    }
+
+
+    private synchronized void onEndLogEntries( ReplicationContext ctx, EndLogEntriesMessage message )
+    {
+        // Return error if other replica than what is in progress sends
+        // a flow control message
+        if ( !ctx.getPeer().equals( replicaInTransaction ) )
+        {
+            ctx.getSession().write( new EndLogEntriesAckMessage( message.getSequence(), Constants.NOT_OK ) );
+            return;
+        }
+
+        ctx.getSession().write( new EndLogEntriesAckMessage( message.getSequence(), Constants.OK ) );
+        replicaInTransaction = null; // Reset the mark.
+    }
+
+
+    private void onUnexpectedMessage( ReplicationContext ctx, Object message )
+    {
+        SessionLog.warn( ctx.getSession(), "[Replica-" + ctx.getConfiguration().getReplicaId()
+                + "] Unexpected message: " + message );
+        ctx.getSession().close();
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/handler/ReplicationServerProtocolHandler.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/handler/ReplicationServerProtocolHandler.java
new file mode 100644
index 0000000..f170a5e
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/handler/ReplicationServerProtocolHandler.java
@@ -0,0 +1,40 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.service.protocol.handler;
+
+
+import org.apache.directory.mitosis.service.ReplicationInterceptor;
+
+/**
+ * A {@link ReplicationProtocolHandler} for server-side, which uses
+ * {@link ReplicationServerContextHandler}.  This class is actually 
+ * a shortcut to creating a {@link ReplicationProtocolHandler} with a
+ * new {@link ReplicationServerContextHandler}.
+ * 
+ * @author The Apache Directory Project (dev@directory.apache.org)
+ * @version $Rev$, $Date$
+ */
+public class ReplicationServerProtocolHandler extends ReplicationProtocolHandler
+{
+    public ReplicationServerProtocolHandler( ReplicationInterceptor interceptor )
+    {
+        super( interceptor, new ReplicationServerContextHandler() );
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/message/BaseMessage.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/message/BaseMessage.java
new file mode 100644
index 0000000..7416856
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/message/BaseMessage.java
@@ -0,0 +1,75 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.service.protocol.message;
+
+
+import org.apache.directory.shared.ldap.util.EqualsBuilder;
+import org.apache.directory.shared.ldap.util.HashCodeBuilder;
+
+
+public abstract class BaseMessage
+{
+
+    private final int sequence;
+
+
+    protected BaseMessage( int sequence )
+    {
+        this.sequence = sequence;
+    }
+
+
+    public abstract int getType();
+
+
+    public int getSequence()
+    {
+        return sequence;
+    }
+
+
+    public boolean equals( Object object )
+    {
+        if ( !( object instanceof BaseMessage ) )
+        {
+            return false;
+        }
+
+        BaseMessage rhs = ( BaseMessage ) object;
+
+        return new EqualsBuilder().append( this.sequence, rhs.sequence ).isEquals();
+    }
+
+
+    /**
+     * Compute the instance's hash code
+     * @return the instance's hashcode 
+     */
+    public int hashCode()
+    {
+        return new HashCodeBuilder( -1364566505, -1158072471 ).append( this.sequence ).toHashCode();
+    }
+
+
+    public String toString()
+    {
+        return String.valueOf( sequence );
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/message/BeginLogEntriesAckMessage.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/message/BeginLogEntriesAckMessage.java
new file mode 100644
index 0000000..adbc41b
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/message/BeginLogEntriesAckMessage.java
@@ -0,0 +1,110 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.service.protocol.message;
+
+
+import org.apache.directory.shared.ldap.util.EqualsBuilder;
+import org.apache.directory.shared.ldap.util.HashCodeBuilder;
+import org.apache.directory.mitosis.common.CSNVector;
+import org.apache.directory.mitosis.service.protocol.Constants;
+
+
+public class BeginLogEntriesAckMessage extends ResponseMessage
+{
+    private final CSNVector purgeVector;
+    private final CSNVector updateVector;
+
+
+    public BeginLogEntriesAckMessage( int sequence, int responseCode, CSNVector purgeVector, CSNVector updateVector )
+    {
+        super( sequence, responseCode );
+
+        if ( responseCode == Constants.OK )
+        {
+            assert purgeVector != null;
+            assert updateVector != null;
+
+            this.purgeVector = purgeVector;
+            this.updateVector = updateVector;
+        }
+        else
+        {
+            this.purgeVector = null;
+            this.updateVector = null;
+        }
+    }
+
+
+    public int getType()
+    {
+        return Constants.GET_UPDATE_VECTOR_ACK;
+    }
+
+
+    public CSNVector getPurgeVector()
+    {
+        return purgeVector;
+    }
+
+
+    public CSNVector getUpdateVector()
+    {
+        return updateVector;
+    }
+
+
+    /**
+     * @see java.lang.Object#equals(Object)
+     */
+    public boolean equals( Object object )
+    {
+        if ( object == this )
+        {
+            return true;
+        }
+
+        if ( !( object instanceof BeginLogEntriesAckMessage ) )
+        {
+            return false;
+        }
+
+        BeginLogEntriesAckMessage rhs = ( BeginLogEntriesAckMessage ) object;
+
+        return new EqualsBuilder().appendSuper( super.equals( object ) ).append( this.purgeVector, rhs.purgeVector )
+            .append( this.updateVector, rhs.updateVector ).isEquals();
+    }
+
+
+    /**
+     * @see java.lang.Object#hashCode()
+     * @return the instance's hashcode 
+     */
+    public int hashCode()
+    {
+        return new HashCodeBuilder( 537917217, 1652875233 ).appendSuper( super.hashCode() ).append( this.purgeVector )
+            .append( this.updateVector ).toHashCode();
+    }
+
+
+    public String toString()
+    {
+        return "[BeginLogEntriesAck] " + super.toString() + ", PV: " + purgeVector + ", UV: " + updateVector;
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/message/BeginLogEntriesMessage.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/message/BeginLogEntriesMessage.java
new file mode 100644
index 0000000..d465f02
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/message/BeginLogEntriesMessage.java
@@ -0,0 +1,68 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.service.protocol.message;
+
+
+import org.apache.directory.mitosis.service.protocol.Constants;
+import org.apache.directory.shared.ldap.util.EqualsBuilder;
+import org.apache.directory.shared.ldap.util.HashCodeBuilder;
+
+
+public class BeginLogEntriesMessage extends BaseMessage
+{
+
+    public BeginLogEntriesMessage( int sequence )
+    {
+        super( sequence );
+    }
+
+
+    public int getType()
+    {
+        return Constants.BEGIN_LOG_ENTRIES;
+    }
+
+
+    public boolean equals( final Object other )
+    {
+        if ( !( other instanceof BeginLogEntriesMessage ) )
+        {
+            return false;
+        }
+
+        return new EqualsBuilder().isEquals();
+    }
+
+
+    /**
+     * Compute the instance's hash code
+     * @return the instance's hashcode 
+     */
+    public int hashCode()
+    {
+        return new HashCodeBuilder( -1480829129, 951761287 ).appendSuper( super.hashCode() ).toHashCode();
+    }
+
+
+    public String toString()
+    {
+        return "[BeginLogEntries] " + super.toString();
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/message/EndLogEntriesAckMessage.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/message/EndLogEntriesAckMessage.java
new file mode 100644
index 0000000..6eca9dc
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/message/EndLogEntriesAckMessage.java
@@ -0,0 +1,68 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.service.protocol.message;
+
+
+import org.apache.directory.mitosis.service.protocol.Constants;
+import org.apache.directory.shared.ldap.util.EqualsBuilder;
+import org.apache.directory.shared.ldap.util.HashCodeBuilder;
+
+
+public class EndLogEntriesAckMessage extends ResponseMessage
+{
+
+    public EndLogEntriesAckMessage( int sequence, int responseCode )
+    {
+        super( sequence, responseCode );
+    }
+
+
+    public int getType()
+    {
+        return Constants.END_LOG_ENTRIES_ACK;
+    }
+
+
+    public boolean equals( Object object )
+    {
+        if ( !( object instanceof EndLogEntriesAckMessage ) )
+        {
+            return false;
+        }
+
+        return new EqualsBuilder().appendSuper( super.equals( object ) ).isEquals();
+    }
+
+
+    /**
+     * Compute the instance's hash code
+     * @return the instance's hashcode 
+     */
+    public int hashCode()
+    {
+        return new HashCodeBuilder( 247639103, -470023671 ).appendSuper( super.hashCode() ).toHashCode();
+    }
+
+
+    public String toString()
+    {
+        return "[EndLogEntriesAck] " + super.toString();
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/message/EndLogEntriesMessage.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/message/EndLogEntriesMessage.java
new file mode 100644
index 0000000..58b3b62
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/message/EndLogEntriesMessage.java
@@ -0,0 +1,68 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.service.protocol.message;
+
+
+import org.apache.directory.mitosis.service.protocol.Constants;
+import org.apache.directory.shared.ldap.util.EqualsBuilder;
+import org.apache.directory.shared.ldap.util.HashCodeBuilder;
+
+
+public class EndLogEntriesMessage extends BaseMessage
+{
+
+    public EndLogEntriesMessage( int sequence )
+    {
+        super( sequence );
+    }
+
+
+    public int getType()
+    {
+        return Constants.END_LOG_ENTRIES;
+    }
+
+
+    public boolean equals( final Object other )
+    {
+        if ( !( other instanceof EndLogEntriesMessage ) )
+        {
+            return false;
+        }
+
+        return new EqualsBuilder().isEquals();
+    }
+
+
+    /**
+     * Compute the instance's hash code
+     * @return the instance's hashcode 
+     */
+    public int hashCode()
+    {
+        return new HashCodeBuilder( 1892305163, -1367551105 ).appendSuper( super.hashCode() ).toHashCode();
+    }
+
+
+    public String toString()
+    {
+        return "[EndLogEntries] " + super.toString();
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/message/LogEntryAckMessage.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/message/LogEntryAckMessage.java
new file mode 100644
index 0000000..9e321d8
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/message/LogEntryAckMessage.java
@@ -0,0 +1,75 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.service.protocol.message;
+
+
+import org.apache.directory.mitosis.service.protocol.Constants;
+import org.apache.directory.shared.ldap.util.EqualsBuilder;
+import org.apache.directory.shared.ldap.util.HashCodeBuilder;
+
+
+public class LogEntryAckMessage extends ResponseMessage
+{
+    public LogEntryAckMessage( int sequence, int responseCode )
+    {
+        super( sequence, responseCode );
+    }
+
+
+    public int getType()
+    {
+        return Constants.LOG_ENTRY_ACK;
+    }
+
+
+    /**
+     * @see java.lang.Object#equals(Object)
+     */
+    public boolean equals( Object object )
+    {
+        if ( object == this )
+        {
+            return true;
+        }
+
+        if ( !( object instanceof LogEntryAckMessage ) )
+        {
+            return false;
+        }
+
+        return new EqualsBuilder().appendSuper( super.equals( object ) ).isEquals();
+    }
+
+
+    /**
+     * @see java.lang.Object#hashCode()
+     * @return the instance's hashcode 
+     */
+    public int hashCode()
+    {
+        return new HashCodeBuilder( -873557437, -1464393829 ).appendSuper( super.hashCode() ).toHashCode();
+    }
+
+
+    public String toString()
+    {
+        return "[LogEntryAck] " + super.toString();
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/message/LogEntryMessage.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/message/LogEntryMessage.java
new file mode 100644
index 0000000..febf36b
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/message/LogEntryMessage.java
@@ -0,0 +1,90 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.service.protocol.message;
+
+
+import org.apache.directory.mitosis.operation.Operation;
+import org.apache.directory.mitosis.service.protocol.Constants;
+import org.apache.directory.shared.ldap.util.EqualsBuilder;
+import org.apache.directory.shared.ldap.util.HashCodeBuilder;
+
+
+public class LogEntryMessage extends BaseMessage
+{
+    private final Operation operation;
+
+
+    public LogEntryMessage( int sequence, Operation operation )
+    {
+        super( sequence );
+        this.operation = operation;
+    }
+
+
+    public int getType()
+    {
+        return Constants.LOG_ENTRY;
+    }
+
+
+    public Operation getOperation()
+    {
+        return operation;
+    }
+
+
+    /**
+     * @see java.lang.Object#equals(Object)
+     */
+    public boolean equals( Object object )
+    {
+        if ( object == this )
+        {
+            return true;
+        }
+
+        if ( !( object instanceof LogEntryMessage ) )
+        {
+            return false;
+        }
+
+        LogEntryMessage rhs = ( LogEntryMessage ) object;
+
+        return new EqualsBuilder().appendSuper( super.equals( object ) ).append( this.operation, rhs.operation )
+            .isEquals();
+    }
+
+
+    /**
+     * @see java.lang.Object#hashCode()
+     * @return the instance's hashcode 
+     */
+    public int hashCode()
+    {
+        return new HashCodeBuilder( 633013569, -1063609843 ).appendSuper( super.hashCode() ).append( this.operation )
+            .toHashCode();
+    }
+
+
+    public String toString()
+    {
+        return "[LogEntry] " + super.toString() + ", " + operation;
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/message/LoginAckMessage.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/message/LoginAckMessage.java
new file mode 100644
index 0000000..8de9cea
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/message/LoginAckMessage.java
@@ -0,0 +1,90 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.service.protocol.message;
+
+
+import org.apache.directory.shared.ldap.util.EqualsBuilder;
+import org.apache.directory.shared.ldap.util.HashCodeBuilder;
+import org.apache.directory.mitosis.common.ReplicaId;
+import org.apache.directory.mitosis.service.protocol.Constants;
+
+
+public class LoginAckMessage extends ResponseMessage
+{
+    private ReplicaId replicaId;
+
+
+    public LoginAckMessage( int sequence, int responseCode, ReplicaId replicaId )
+    {
+        super( sequence, responseCode );
+        this.replicaId = replicaId;
+    }
+
+
+    public int getType()
+    {
+        return Constants.LOGIN_ACK;
+    }
+
+
+    public ReplicaId getReplicaId()
+    {
+        return replicaId;
+    }
+
+
+    /**
+     * @see java.lang.Object#equals(Object)
+     */
+    public boolean equals( Object object )
+    {
+        if ( object == this )
+        {
+            return true;
+        }
+
+        if ( !( object instanceof LoginAckMessage ) )
+        {
+            return false;
+        }
+
+        LoginAckMessage rhs = ( LoginAckMessage ) object;
+
+        return new EqualsBuilder().appendSuper( super.equals( object ) ).append( this.replicaId, rhs.replicaId )
+            .isEquals();
+    }
+
+
+    /**
+     * @see java.lang.Object#hashCode()
+     * @return the instance's hashcode 
+     */
+    public int hashCode()
+    {
+        return new HashCodeBuilder( -280394717, -328404193 ).appendSuper( super.hashCode() ).append( this.replicaId )
+            .toHashCode();
+    }
+
+
+    public String toString()
+    {
+        return "[LoginAck] " + super.toString() + ", " + replicaId;
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/message/LoginMessage.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/message/LoginMessage.java
new file mode 100644
index 0000000..507f1dc
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/message/LoginMessage.java
@@ -0,0 +1,86 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.service.protocol.message;
+
+
+import org.apache.directory.shared.ldap.util.EqualsBuilder;
+import org.apache.directory.shared.ldap.util.HashCodeBuilder;
+import org.apache.directory.mitosis.common.ReplicaId;
+import org.apache.directory.mitosis.service.protocol.Constants;
+
+
+public class LoginMessage extends BaseMessage
+{
+    private final ReplicaId replicaId;
+
+
+    public LoginMessage( int sequence, ReplicaId replicaId )
+    {
+        super( sequence );
+
+        this.replicaId = replicaId;
+    }
+
+
+    public int getType()
+    {
+        return Constants.LOGIN;
+    }
+
+
+    public ReplicaId getReplicaId()
+    {
+        return replicaId;
+    }
+
+
+    /**
+     * @see java.lang.Object#equals(Object)
+     */
+    public boolean equals( Object object )
+    {
+        if ( !( object instanceof LoginMessage ) )
+        {
+            return false;
+        }
+
+        LoginMessage rhs = ( LoginMessage ) object;
+
+        return new EqualsBuilder().appendSuper( super.equals( object ) ).append( this.replicaId, rhs.replicaId )
+            .isEquals();
+    }
+
+
+    /**
+     * @see java.lang.Object#hashCode()
+     * @return the instance's hashcode 
+     */
+    public int hashCode()
+    {
+        return new HashCodeBuilder( 1520317245, 1630850531 ).appendSuper( super.hashCode() ).append( this.replicaId )
+            .toHashCode();
+    }
+
+
+    public String toString()
+    {
+        return "[Login] " + super.toString() + ", " + replicaId;
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/message/ResponseMessage.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/message/ResponseMessage.java
new file mode 100644
index 0000000..28bd6de
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/service/protocol/message/ResponseMessage.java
@@ -0,0 +1,83 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.service.protocol.message;
+
+
+import org.apache.directory.shared.ldap.util.EqualsBuilder;
+import org.apache.directory.shared.ldap.util.HashCodeBuilder;
+
+
+public abstract class ResponseMessage extends BaseMessage
+{
+    private final int responseCode;
+
+
+    public ResponseMessage( int sequence, int responseCode )
+    {
+        super( sequence );
+
+        this.responseCode = responseCode;
+    }
+
+
+    public int getResponseCode()
+    {
+        return responseCode;
+    }
+
+
+    /**
+     * @see java.lang.Object#equals(Object)
+     */
+    public boolean equals( Object object )
+    {
+        if ( object == this )
+        {
+            return true;
+        }
+
+        if ( !( object instanceof ResponseMessage ) )
+        {
+            return false;
+        }
+
+        ResponseMessage rhs = ( ResponseMessage ) object;
+
+        return new EqualsBuilder().appendSuper( super.equals( object ) ).append( this.responseCode, rhs.responseCode )
+            .isEquals();
+    }
+
+
+    /**
+     * @see java.lang.Object#hashCode()
+     * @return the instance's hashcode 
+     */
+    public int hashCode()
+    {
+        return new HashCodeBuilder( -710373179, 1116475565 ).appendSuper( super.hashCode() ).append( this.responseCode )
+            .toHashCode();
+    }
+
+
+    public String toString()
+    {
+        return super.toString() + ": " + responseCode;
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/store/ReplicationLogIterator.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/store/ReplicationLogIterator.java
new file mode 100644
index 0000000..b3d91c6
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/store/ReplicationLogIterator.java
@@ -0,0 +1,52 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.store;
+
+
+import java.sql.ResultSet;
+
+import org.apache.directory.mitosis.operation.Operation;
+
+/**
+ * Iterates a set of {@link Operation}s, which is a result of a query on 
+ * {@link ReplicationStore}.  It's usage is similar to that of JDBC
+ * {@link ResultSet}.
+ * 
+ * @author The Apache Directory Project (dev@directory.apache.org)
+ * @version $Rev$, $Date$
+ */
+public interface ReplicationLogIterator
+{
+    /**
+     * Move on to the next item.
+     * @return <tt>true</tt> if and only if it has more item.
+     */
+    boolean next();
+
+    /**
+     * Releases all resources allocated to this iterator.
+     */
+    void close();
+
+    /**
+     * Returns the {@link Operation} on the current iterator position.
+     */
+    Operation getOperation();
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/store/ReplicationStore.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/store/ReplicationStore.java
new file mode 100644
index 0000000..e0655e5
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/store/ReplicationStore.java
@@ -0,0 +1,144 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.store;
+
+
+import org.apache.directory.mitosis.common.CSN;
+import org.apache.directory.mitosis.common.CSNVector;
+import org.apache.directory.mitosis.common.ReplicaId;
+import org.apache.directory.mitosis.common.UUID;
+import org.apache.directory.mitosis.configuration.ReplicationConfiguration;
+import org.apache.directory.mitosis.operation.Operation;
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+import javax.naming.Name;
+import java.util.Set;
+
+/**
+ * Provides an abstract storage that stores data required to perform
+ * replication, such as {@link UUID}-{@link LdapDN} mapping and
+ * LDAP {@link Operation}s.  It also calculates the Update Vector (UV)
+ * and the Purge Vector (PV) of a replica.
+ * 
+ * @author The Apache Directory Project (dev@directory.apache.org)
+ * @version $Rev$, $Date#
+ */
+public interface ReplicationStore
+{
+    /**
+     * Opens this storage.
+     */
+    void open( DirectoryService directoryService, ReplicationConfiguration cfg );
+
+    /**
+     * Closes this storage and releases the resources allocated when it's
+     * opened.
+     */
+    void close();
+
+    /**
+     * Returns the {@link ReplicaId} of the {@link ReplicaId} that this storage
+     * is associated with.
+     */
+    ReplicaId getReplicaId();
+
+    /**
+     * Returns the set of {@link ReplicaId}s of the {@link ReplicaId}s that
+     * belongs to the same cluster.
+     */
+    Set<ReplicaId> getKnownReplicaIds();
+
+
+    // UUID to DN table operations
+
+    /**
+     * Finds the {@link Name} of an entry with the specified {@link UUID}.
+     */
+    Name getDN( UUID uuid );
+
+    /**
+     * Associates the specified name and UUID so a user can
+     * find an entry's name from a UUID.
+     */
+    boolean putUUID( UUID uuid, Name dn );
+
+    /**
+     * Removed the specified UUID mapping from this storage.
+     * @return <tt>true</tt> if and only if the mapping has been removed
+     */
+    boolean removeUUID( UUID uuid );
+
+
+    // Log entry operations
+
+    /**
+     * Puts the specified operation into this storage.
+     */
+    void putLog( Operation operation );
+
+    /**
+     * Queries all operations that is greater than the specified {@link CSN}.
+     * 
+     * @param inclusive <tt>true</tt> if you want to include <tt>fromCSN</tt>
+     *                  itself in the result set.
+     */
+    ReplicationLogIterator getLogs( CSN fromCSN, boolean inclusive );
+
+    /**
+     * Queries all operations that is greater than the specified
+     * {@link CSNVector}.
+     * 
+     * @param inclusive <tt>true</tt> if you want to include
+     *                  <tt>updateVector</tt> itself in the result set.
+     */
+    ReplicationLogIterator getLogs( CSNVector updateVector, boolean inclusive );
+
+    /**
+     * Removes all operations that is less than the specified {@link CSN}.
+     * 
+     * @param inclusive <tt>true</tt> if you want to delete the
+     *                  <tt>toCSN</tt> itself, too.
+     * @return the number of deleted {@link Operation}s
+     */
+    int removeLogs( CSN toCSN, boolean inclusive );
+
+    /**
+     * Returns the number of {@link Operation}s logged in this storage.
+     */
+    int getLogSize();
+
+    /**
+     * Returns the number of {@link Operation}s logged by
+     * the {@link ReplicaId} with the specified {@link ReplicaId}
+     * in this storage .
+     */
+    int getLogSize( ReplicaId replicaId );
+
+    /**
+     * Calculates the Update Vector (UV) from this storage. 
+     */
+    CSNVector getUpdateVector();
+
+    /**
+     * Calculates the Purge Vector (PV) from this storage. 
+     */
+    CSNVector getPurgeVector();
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/store/ReplicationStoreException.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/store/ReplicationStoreException.java
new file mode 100644
index 0000000..ef25044
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/store/ReplicationStoreException.java
@@ -0,0 +1,57 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.store;
+
+/**
+ * A {@link RuntimeException} which is thrown when {@link ReplicationStore}
+ * has a problem in accessing its underlying data source (e.g. a file).
+ * 
+ * @author The Apache Directory Project (dev@directory.apache.org)
+ * @version $Rev$, $Date$
+ */
+public class ReplicationStoreException extends RuntimeException
+{
+
+    private static final long serialVersionUID = 3257289127798913336L;
+
+
+    public ReplicationStoreException()
+    {
+        super();
+    }
+
+
+    public ReplicationStoreException( String message )
+    {
+        super( message );
+    }
+
+
+    public ReplicationStoreException( String message, Throwable cause )
+    {
+        super( message, cause );
+    }
+
+
+    public ReplicationStoreException( Throwable cause )
+    {
+        super( cause );
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/store/derby/DerbyReplicationLogIterator.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/store/derby/DerbyReplicationLogIterator.java
new file mode 100644
index 0000000..4c7e96d
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/store/derby/DerbyReplicationLogIterator.java
@@ -0,0 +1,100 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.store.derby;
+
+
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+import org.apache.directory.mitosis.common.CSN;
+import org.apache.directory.mitosis.common.ReplicaId;
+import org.apache.directory.mitosis.common.DefaultCSN;
+import org.apache.directory.mitosis.operation.Operation;
+import org.apache.directory.mitosis.operation.OperationCodec;
+import org.apache.directory.mitosis.store.ReplicationLogIterator;
+import org.apache.directory.mitosis.store.ReplicationStoreException;
+
+
+class DerbyReplicationLogIterator implements ReplicationLogIterator
+{
+    private final OperationCodec codec;
+    private final Connection con;
+    private final Statement stmt;
+    private final ResultSet rs;
+
+
+    DerbyReplicationLogIterator( OperationCodec codec, Connection con, Statement stmt, ResultSet rs )
+    {
+        this.codec = codec;
+        this.con = con;
+        this.stmt = stmt;
+        this.rs = rs;
+    }
+
+
+    public boolean next()
+    {
+        try
+        {
+            return rs.next();
+        }
+        catch ( SQLException e )
+        {
+            throw new ReplicationStoreException( e );
+        }
+    }
+
+
+    public void close()
+    {
+        SQLUtil.cleanup( con, stmt, rs );
+    }
+
+
+    public CSN getCSN()
+    {
+        try
+        {
+            ReplicaId replicaId = new ReplicaId( rs.getString( 1 ) );
+            long timestamp = rs.getLong( 2 );
+            int operationSequence = rs.getInt( 3 );
+            return new DefaultCSN( timestamp, replicaId, operationSequence );
+        }
+        catch ( Exception e )
+        {
+            throw new ReplicationStoreException( e );
+        }
+    }
+
+
+    public Operation getOperation()
+    {
+        try
+        {
+            return codec.decode( rs.getBytes( 4 ) );
+        }
+        catch ( Exception e )
+        {
+            throw new ReplicationStoreException( e );
+        }
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/store/derby/DerbyReplicationStore.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/store/derby/DerbyReplicationStore.java
new file mode 100644
index 0000000..0b93ef7
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/store/derby/DerbyReplicationStore.java
@@ -0,0 +1,739 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.store.derby;
+
+
+import org.apache.commons.dbcp.BasicDataSource;
+import org.apache.directory.mitosis.common.CSN;
+import org.apache.directory.mitosis.common.CSNVector;
+import org.apache.directory.mitosis.common.DefaultCSN;
+import org.apache.directory.mitosis.common.ReplicaId;
+import org.apache.directory.mitosis.common.UUID;
+import org.apache.directory.mitosis.configuration.ReplicationConfiguration;
+import org.apache.directory.mitosis.operation.Operation;
+import org.apache.directory.mitosis.operation.OperationCodec;
+import org.apache.directory.mitosis.store.ReplicationLogIterator;
+import org.apache.directory.mitosis.store.ReplicationStore;
+import org.apache.directory.mitosis.store.ReplicationStoreException;
+import org.apache.directory.server.core.DirectoryService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.naming.Name;
+import javax.naming.ldap.LdapName;
+import java.io.File;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+
+public class DerbyReplicationStore implements ReplicationStore
+{
+    private static final Logger LOG = LoggerFactory.getLogger( DerbyReplicationStore.class );
+
+    private static final String DEFAULT_TABLE_PREFIX = "REPLICATION_";
+    private static final String KEY_REPLICA_ID = "replicaId";
+
+    private static final String DRIVER_NAME = "org.apache.derby.jdbc.EmbeddedDriver";
+    private static final String DB_URI_PREFIX = "jdbc:derby:";
+
+    private String dbURI;
+    private BasicDataSource dataSource;
+    private ReplicaId replicaId;
+    private String tablePrefix = DEFAULT_TABLE_PREFIX;
+    private String metadataTableName;
+    private String uuidTableName;
+    private String logTableName;
+    private Set<ReplicaId> knownReplicaIds;
+    private final Object knownReplicaIdsLock = new Object();
+    private final OperationCodec operationCodec = new OperationCodec();
+
+
+    public String getTablePrefix()
+    {
+        return tablePrefix;
+    }
+
+
+    public void setTablePrefix( String tablePrefix )
+    {
+        if ( tablePrefix == null )
+        {
+            tablePrefix = DEFAULT_TABLE_PREFIX;
+        }
+
+        tablePrefix = tablePrefix.trim();
+        if ( tablePrefix.length() == 0 )
+        {
+            tablePrefix = DEFAULT_TABLE_PREFIX;
+        }
+
+        this.tablePrefix = tablePrefix;
+    }
+
+
+    public void open( DirectoryService serviceCfg, ReplicationConfiguration cfg )
+    {
+        replicaId = cfg.getReplicaId();
+
+        // Calculate DB URI
+        dbURI = DB_URI_PREFIX + serviceCfg.getWorkingDirectory().getPath() + File.separator
+            + "replication";
+
+        // Create database if not exists.
+        try
+        {
+            Class.forName( DRIVER_NAME );
+            Connection con = DriverManager.getConnection( dbURI + ";create=true" );
+            con.close();
+        }
+        catch ( Exception e )
+        {
+            throw new ReplicationStoreException( "Failed to initialize Derby database.", e );
+        }
+
+        // Initialize DataSource
+        BasicDataSource dataSource = new BasicDataSource();
+        dataSource.setDriverClassName( DRIVER_NAME );
+        dataSource.setUrl( dbURI );
+        dataSource.setUsername( "sa" );
+        dataSource.setPassword( "" );
+        this.dataSource = dataSource;
+
+        // Pre-calculate table names
+        metadataTableName = tablePrefix + "METADATA";
+        uuidTableName = tablePrefix + "UUID";
+        logTableName = tablePrefix + "LOG";
+
+        initSchema();
+        loadMetadata();
+    }
+
+
+    private void initSchema()
+    {
+        Connection con = null;
+        Statement stmt = null;
+        ResultSet rs = null;
+
+        try
+        {
+            con = dataSource.getConnection();
+            con.setAutoCommit( true );
+
+            stmt = con.createStatement();
+
+            try
+            {
+                rs = stmt.executeQuery( "SELECT M_KEY FROM " + metadataTableName + " WHERE M_KEY IS NULL" );
+                rs.close();
+                rs = null;
+            }
+            catch ( SQLException e )
+            {
+                stmt.executeUpdate( "CREATE TABLE " + metadataTableName + " ("
+                    + "    M_KEY VARCHAR(30) NOT NULL PRIMARY KEY," + "    M_VALUE VARCHAR(100) NOT NULL )" );
+            }
+
+            try
+            {
+                rs = stmt.executeQuery( "SELECT UUID FROM " + uuidTableName + " WHERE UUID IS NULL" );
+                rs.close();
+                rs = null;
+            }
+            catch ( SQLException e )
+            {
+                stmt.executeUpdate( "CREATE TABLE " + uuidTableName + " (" + "    UUID CHAR(32) NOT NULL PRIMARY KEY,"
+                    + "    DN CLOB NOT NULL" + ")" );
+            }
+
+            try
+            {
+                rs = stmt.executeQuery( "SELECT CSN_REPLICA_ID FROM " + logTableName + " WHERE CSN_REPLICA_ID IS NULL" );
+                rs.close();
+                rs = null;
+            }
+            catch ( SQLException e )
+            {
+                stmt.executeUpdate( "CREATE TABLE " + logTableName + " (" + "    CSN_REPLICA_ID VARCHAR(16) NOT NULL,"
+                    + "    CSN_TIMESTAMP BIGINT NOT NULL," + "    CSN_OP_SEQ INTEGER NOT NULL,"
+                    + "    OPERATION BLOB NOT NULL," + "CONSTRAINT " + logTableName + "_PK PRIMARY KEY ("
+                    + "    CSN_REPLICA_ID," + "    CSN_TIMESTAMP," + "    CSN_OP_SEQ)" + ")" );
+            }
+        }
+        catch ( SQLException e )
+        {
+            throw new ReplicationStoreException( "Failed to initialize DB schema.", e );
+        }
+        finally
+        {
+            SQLUtil.cleanup( con, stmt, rs );
+        }
+    }
+
+
+    private void loadMetadata()
+    {
+        Connection con = null;
+        PreparedStatement ps = null;
+        ResultSet rs = null;
+
+        try
+        {
+            con = dataSource.getConnection();
+            con.setAutoCommit( true );
+            con.setTransactionIsolation( Connection.TRANSACTION_REPEATABLE_READ );
+            con.setReadOnly( true );
+
+            // Check if replicaId is already registered
+            ps = con.prepareStatement( "SELECT M_VALUE FROM " + metadataTableName + " WHERE M_KEY=?" );
+            ps.setString( 1, KEY_REPLICA_ID );
+            rs = ps.executeQuery();
+            if ( rs.next() )
+            {
+                // If already registered, match it with what user specified.
+                String actualReplicaId = rs.getString( 1 );
+                if ( !replicaId.getId().equalsIgnoreCase( actualReplicaId ) )
+                {
+                    throw new ReplicationStoreException( "Replica ID mismatches: " + actualReplicaId + " (expected: "
+                        + replicaId + ")" );
+                }
+            }
+            else
+            {
+                rs.close();
+                rs = null;
+                ps.close();
+                ps = null;
+
+                con.setReadOnly( false );
+                // If not registered yet, register with what user specified.
+                ps = con.prepareStatement( "INSERT INTO " + metadataTableName + " (M_KEY, M_VALUE) VALUES (?,?)" );
+                ps.setString( 1, KEY_REPLICA_ID );
+                ps.setString( 2, replicaId.getId() );
+                ps.executeUpdate();
+            }
+
+            if ( rs != null )
+            {
+                rs.close();
+                rs = null;
+            }
+            ps.close();
+            ps = null;
+
+            // Get known replica IDs.
+            ps = con.prepareStatement( "SELECT DISTINCT CSN_REPLICA_ID FROM " + logTableName );
+            rs = ps.executeQuery();
+            knownReplicaIds = new HashSet<ReplicaId>();
+            while ( rs.next() )
+            {
+                knownReplicaIds.add( new ReplicaId( rs.getString( 1 ) ) );
+            }
+        }
+        catch ( Exception e )
+        {
+            if ( e instanceof ReplicationStoreException )
+            {
+                throw ( ReplicationStoreException ) e;
+            }
+            throw new ReplicationStoreException( e );
+        }
+        finally
+        {
+            SQLUtil.cleanup( con, ps, rs );
+        }
+    }
+
+
+    public void close()
+    {
+        try
+        {
+            dataSource.close();
+        }
+        catch ( SQLException e )
+        {
+            LOG.warn( "Failed to close the dataSource.", e );
+        }
+        dataSource = null;
+        replicaId = null;
+
+        try
+        {
+            DriverManager.getConnection( dbURI + ";shutdown=true" );
+        }
+        catch ( Exception e )
+        {
+            // An exception is thrown always.
+        }
+    }
+
+
+    public ReplicaId getReplicaId()
+    {
+        return replicaId;
+    }
+
+
+    public Set<ReplicaId> getKnownReplicaIds()
+    {
+        return new HashSet<ReplicaId>( knownReplicaIds );
+    }
+
+
+    public Name getDN( UUID uuid )
+    {
+        Connection con = null;
+        PreparedStatement ps = null;
+        ResultSet rs = null;
+
+        try
+        {
+            con = dataSource.getConnection();
+            con.setTransactionIsolation( Connection.TRANSACTION_READ_COMMITTED );
+            con.setReadOnly( true );
+            ps = con.prepareStatement( "SELECT DN FROM " + uuidTableName + " WHERE UUID=?" );
+            ps.setString( 1, uuid.toOctetString() );
+            rs = ps.executeQuery();
+            if ( rs.next() )
+            {
+                return new LdapName( rs.getString( 1 ) );
+            }
+            else
+            {
+                return null;
+            }
+        }
+        catch ( Exception e )
+        {
+            throw new ReplicationStoreException( e );
+        }
+        finally
+        {
+            SQLUtil.cleanup( con, ps, rs );
+        }
+    }
+
+
+    public boolean putUUID( UUID uuid, Name dn )
+    {
+        String uuidString = uuid.toOctetString();
+        Connection con = null;
+        PreparedStatement ps = null;
+        ResultSet rs = null;
+
+        try
+        {
+            con = dataSource.getConnection();
+            con.setAutoCommit( false );
+            con.setTransactionIsolation( Connection.TRANSACTION_REPEATABLE_READ );
+            con.setReadOnly( true );
+
+            // Check if the specified uuid already exists
+            ps = con.prepareStatement( "SELECT UUID FROM " + uuidTableName + " WHERE UUID=?" );
+            ps.setString( 1, uuidString );
+            rs = ps.executeQuery();
+            if ( rs.next() )
+            {
+                return false;
+            }
+
+            rs.close();
+            rs = null;
+
+            // insert
+            con.setReadOnly( false );
+            ps = con.prepareStatement( "INSERT INTO " + uuidTableName + " (UUID, DN) VALUES(?,?)" );
+            ps.setString( 1, uuidString );
+            ps.setString( 2, dn.toString() );
+
+            int updateCnt = ps.executeUpdate();
+            con.commit();
+            return updateCnt == 1;
+        }
+        catch ( Exception e )
+        {
+            try
+            {
+                con.rollback();
+            }
+            catch ( SQLException e1 )
+            {
+                LOG.error( "Failed to rollback transaction.", e );
+            }
+
+            throw new ReplicationStoreException( e );
+        }
+        finally
+        {
+            SQLUtil.cleanup( con, ps, rs );
+        }
+    }
+
+
+    public boolean removeUUID( UUID uuid )
+    {
+        String uuidString = uuid.toOctetString();
+        Connection con = null;
+        PreparedStatement ps = null;
+
+        try
+        {
+            con = dataSource.getConnection();
+            con.setAutoCommit( true );
+            con.setTransactionIsolation( Connection.TRANSACTION_READ_UNCOMMITTED );
+            con.setReadOnly( false );
+
+            // Check if the specified uuid already exists
+            ps = con.prepareStatement( "DELETE FROM " + uuidTableName + " WHERE UUID=?" );
+            ps.setString( 1, uuidString );
+            return ps.executeUpdate() == 1;
+        }
+        catch ( Exception e )
+        {
+            throw new ReplicationStoreException( e );
+        }
+        finally
+        {
+            SQLUtil.cleanup( con, ps, null );
+        }
+    }
+
+
+    public void putLog( Operation op )
+    {
+        CSN csn = op.getCSN();
+        byte[] encodedOp = operationCodec.encode( op );
+        Connection con = null;
+        PreparedStatement ps = null;
+
+        try
+        {
+            con = dataSource.getConnection();
+            con.setAutoCommit( true );
+            con.setTransactionIsolation( Connection.TRANSACTION_READ_UNCOMMITTED );
+            con.setReadOnly( false );
+
+            // Check if the specified uuid already exists
+            ps = con.prepareStatement( "INSERT INTO " + logTableName
+                + " (CSN_REPLICA_ID, CSN_TIMESTAMP, CSN_OP_SEQ, OPERATION) VALUES(?,?,?,?)" );
+            ps.setString( 1, csn.getReplicaId().getId() );
+            ps.setLong( 2, csn.getTimestamp() );
+            ps.setInt( 3, csn.getOperationSequence() );
+            ps.setBytes( 4, encodedOp );
+            if ( ps.executeUpdate() != 1 )
+            {
+                throw new ReplicationStoreException( "Failed to insert a row." );
+            }
+        }
+        catch ( Exception e )
+        {
+            if ( e instanceof ReplicationStoreException )
+            {
+                throw ( ReplicationStoreException ) e;
+            }
+
+            throw new ReplicationStoreException( e );
+        }
+        finally
+        {
+            SQLUtil.cleanup( con, ps, null );
+        }
+
+        if ( !knownReplicaIds.contains( csn.getReplicaId() ) )
+        {
+            synchronized ( knownReplicaIdsLock )
+            {
+                Set<ReplicaId> newKnownReplicaIds = new HashSet<ReplicaId>( knownReplicaIds );
+                newKnownReplicaIds.add( csn.getReplicaId() );
+                knownReplicaIds = newKnownReplicaIds;
+            }
+        }
+    }
+
+
+    public ReplicationLogIterator getLogs( CSNVector updateVector, boolean inclusive )
+    {
+        Connection con;
+        PreparedStatement ps;
+        ResultSet rs;
+
+        updateVector = getNormalizedUpdateVector( updateVector );
+
+        StringBuffer buf = new StringBuffer( "SELECT CSN_REPLICA_ID, CSN_TIMESTAMP, CSN_OP_SEQ, OPERATION FROM "
+            + logTableName + " " );
+
+        if ( updateVector.size() > 0 )
+        {
+            buf.append( "WHERE " );
+            for ( int i = updateVector.size();; )
+            {
+                buf.append( "( CSN_REPLICA_ID = ? AND (CSN_TIMESTAMP = ? AND CSN_OP_SEQ >" + ( inclusive ? "=" : "" )
+                    + " ? OR CSN_TIMESTAMP > ?) ) " );
+                i--;
+                if ( i == 0 )
+                {
+                    break;
+                }
+                else
+                {
+                    buf.append( "OR " );
+                }
+
+            }
+        }
+        buf.append( "ORDER BY CSN_TIMESTAMP ASC, CSN_OP_SEQ ASC" );
+
+        String query = buf.toString();
+
+        try
+        {
+            con = dataSource.getConnection();
+            con.setAutoCommit( true );
+            con.setTransactionIsolation( Connection.TRANSACTION_READ_UNCOMMITTED );
+            con.setReadOnly( true );
+
+            // Check if the specified uuid already exists
+            ps = con.prepareStatement( query );
+
+            Iterator<ReplicaId> i = updateVector.getReplicaIds().iterator();
+            int paramIdx = 1;
+            
+            while ( i.hasNext() )
+            {
+                ReplicaId replicaId = i.next();
+                CSN csn = updateVector.getCSN( replicaId );
+                ps.setString( paramIdx++, replicaId.getId() );
+                ps.setLong( paramIdx++, csn.getTimestamp() );
+                ps.setInt( paramIdx++, csn.getOperationSequence() );
+                ps.setLong( paramIdx++, csn.getTimestamp() );
+            }
+            rs = ps.executeQuery();
+
+            return new DerbyReplicationLogIterator( operationCodec, con, ps, rs );
+        }
+        catch ( Exception e )
+        {
+            throw new ReplicationStoreException( e );
+        }
+    }
+
+
+    private CSNVector getNormalizedUpdateVector( CSNVector updateVector )
+    {
+        CSNVector newUV = new CSNVector();
+        synchronized ( knownReplicaIds )
+        {
+            for ( ReplicaId knownReplicaId : knownReplicaIds )
+            {
+                newUV.setCSN( new DefaultCSN( 0, knownReplicaId, 0 ) );
+            }
+        }
+
+        newUV.setAllCSN( updateVector );
+        return newUV;
+    }
+
+
+    public ReplicationLogIterator getLogs( CSN fromCSN, boolean inclusive )
+    {
+        Connection con;
+        PreparedStatement ps;
+        ResultSet rs;
+
+        try
+        {
+            con = dataSource.getConnection();
+            con.setAutoCommit( true );
+            con.setTransactionIsolation( Connection.TRANSACTION_READ_UNCOMMITTED );
+            con.setReadOnly( true );
+
+            // Check if the specified uuid already exists
+            ps = con
+                .prepareStatement( "SELECT CSN_REPLICA_ID, CSN_TIMESTAMP, CSN_OP_SEQ, OPERATION FROM " + logTableName
+                    + " " + "WHERE CSN_REPLICA_ID = ? AND (CSN_TIMESTAMP = ? AND CSN_OP_SEQ >"
+                    + ( inclusive ? "=" : "" ) + " ? OR CSN_TIMESTAMP > ?) "
+                    + "ORDER BY CSN_TIMESTAMP ASC, CSN_OP_SEQ ASC" );
+            ps.setString( 1, fromCSN.getReplicaId().getId() );
+            ps.setLong( 2, fromCSN.getTimestamp() );
+            ps.setInt( 3, fromCSN.getOperationSequence() );
+            ps.setLong( 4, fromCSN.getTimestamp() );
+            rs = ps.executeQuery();
+
+            return new DerbyReplicationLogIterator( operationCodec, con, ps, rs );
+        }
+        catch ( Exception e )
+        {
+            throw new ReplicationStoreException( e );
+        }
+    }
+
+
+    public int removeLogs( CSN toCSN, boolean inclusive )
+    {
+        Connection con = null;
+        PreparedStatement ps = null;
+
+        try
+        {
+            con = dataSource.getConnection();
+            con.setAutoCommit( true );
+            con.setTransactionIsolation( Connection.TRANSACTION_READ_UNCOMMITTED );
+            con.setReadOnly( false );
+
+            // Check if the specified uuid already exists
+            ps = con.prepareStatement( "DELETE FROM " + logTableName + " WHERE "
+                + "CSN_REPLICA_ID = ? AND (CSN_TIMESTAMP = ? AND CSN_OP_SEQ <" + ( inclusive ? "=" : "" )
+                + " ? OR CSN_TIMESTAMP < ?)" );
+            ps.setString( 1, toCSN.getReplicaId().getId() );
+            ps.setLong( 2, toCSN.getTimestamp() );
+            ps.setInt( 3, toCSN.getOperationSequence() );
+            ps.setLong( 4, toCSN.getTimestamp() );
+            return ps.executeUpdate();
+        }
+        catch ( Exception e )
+        {
+            throw new ReplicationStoreException( e );
+        }
+        finally
+        {
+            SQLUtil.cleanup( con, ps, null );
+        }
+    }
+
+
+    public int getLogSize()
+    {
+        Connection con = null;
+        Statement stmt = null;
+        ResultSet rs = null;
+
+        try
+        {
+            con = dataSource.getConnection();
+            con.setTransactionIsolation( Connection.TRANSACTION_READ_COMMITTED );
+            con.setReadOnly( true );
+            stmt = con.createStatement();
+            rs = stmt.executeQuery( "SELECT COUNT(*) FROM " + logTableName );
+            rs.next();
+            return rs.getInt( 1 );
+        }
+        catch ( Exception e )
+        {
+            throw new ReplicationStoreException( e );
+        }
+        finally
+        {
+            SQLUtil.cleanup( con, stmt, rs );
+        }
+    }
+
+
+    public int getLogSize( ReplicaId replicaId )
+    {
+        Connection con = null;
+        PreparedStatement ps = null;
+        ResultSet rs = null;
+
+        try
+        {
+            con = dataSource.getConnection();
+            con.setTransactionIsolation( Connection.TRANSACTION_READ_COMMITTED );
+            con.setReadOnly( true );
+            ps = con.prepareStatement( "SELECT COUNT(*) FROM " + logTableName + " WHERE CSN_REPLICA_ID=?" );
+            ps.setString( 1, replicaId.getId() );
+            rs = ps.executeQuery();
+            rs.next();
+            return rs.getInt( 1 );
+        }
+        catch ( Exception e )
+        {
+            throw new ReplicationStoreException( e );
+        }
+        finally
+        {
+            SQLUtil.cleanup( con, ps, rs );
+        }
+    }
+
+
+    public CSNVector getUpdateVector()
+    {
+        return getVector( false );
+    }
+
+
+    public CSNVector getPurgeVector()
+    {
+        return getVector( true );
+    }
+
+
+    private CSNVector getVector( boolean min )
+    {
+        final String ORDER = min ? "ASC" : "DESC";
+
+        Connection con = null;
+        PreparedStatement ps = null;
+        ResultSet rs = null;
+        CSNVector result = new CSNVector();
+
+        try
+        {
+            con = dataSource.getConnection();
+            con.setTransactionIsolation( Connection.TRANSACTION_READ_COMMITTED );
+            con.setReadOnly( true );
+            ps = con.prepareStatement( "SELECT CSN_TIMESTAMP, CSN_OP_SEQ FROM " + logTableName
+                + " WHERE CSN_REPLICA_ID=? ORDER BY CSN_TIMESTAMP " + ORDER + ", CSN_OP_SEQ " + ORDER );
+
+            Iterator<ReplicaId> it = knownReplicaIds.iterator();
+            while ( it.hasNext() )
+            {
+                ReplicaId replicaId = it.next();
+                ps.setString( 1, replicaId.getId() );
+                rs = ps.executeQuery();
+                if ( rs.next() )
+                {
+                    result.setCSN( new DefaultCSN( rs.getLong( 1 ), replicaId, rs.getInt( 2 ) ) );
+                }
+                rs.close();
+                rs = null;
+                ps.clearParameters();
+            }
+
+            return result;
+        }
+        catch ( Exception e )
+        {
+            throw new ReplicationStoreException( e );
+        }
+        finally
+        {
+            SQLUtil.cleanup( con, ps, rs );
+        }
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/store/derby/SQLUtil.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/store/derby/SQLUtil.java
new file mode 100644
index 0000000..994121b
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/store/derby/SQLUtil.java
@@ -0,0 +1,73 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.store.derby;
+
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+
+class SQLUtil
+{
+    private static final Logger LOG = LoggerFactory.getLogger( SQLUtil.class );
+
+
+    static void cleanup( Connection con, Statement stmt, ResultSet rs )
+    {
+        if ( rs != null )
+        {
+            try
+            {
+                rs.close();
+            }
+            catch ( SQLException e )
+            {
+                LOG.error( "Failed to close result set.", e );
+            }
+        }
+        if ( stmt != null )
+        {
+            try
+            {
+                stmt.close();
+            }
+            catch ( SQLException e )
+            {
+                LOG.error( "Failed to close statement.", e );
+            }
+        }
+        if ( con != null )
+        {
+            try
+            {
+                con.close();
+            }
+            catch ( SQLException e )
+            {
+                LOG.error( "Failed to close jdbc connection.", e );
+            }
+        }
+    }
+}
diff --git a/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/util/OctetString.java b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/util/OctetString.java
new file mode 100644
index 0000000..72e39ee
--- /dev/null
+++ b/old_trunk/mitosis/src/main/java/org/apache/directory/mitosis/util/OctetString.java
@@ -0,0 +1,178 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.util;
+
+
+/**
+ * A utuility class that converts an integer to an octet string, and vice
+ * versa.
+ * 
+ * @author The Apache Directory Project (dev@directory.apache.org)
+ * @version $Rev: 118 $, $Date: 2006-09-18 13:48:47Z $
+ */
+public class OctetString
+{
+    private static final char[] highDigits;
+    private static final char[] lowDigits;
+
+    static
+    {
+        final char[] digits =
+            { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+
+        int i;
+        char[] high = new char[256];
+        char[] low = new char[256];
+
+        for ( i = 0; i < 256; i++ )
+        {
+            high[i] = digits[i >>> 4];
+            low[i] = digits[i & 0x0F];
+        }
+
+        highDigits = high;
+        lowDigits = low;
+    }
+
+
+    /**
+     * Converts the specified <tt>value</tt> to an octet string and appends
+     * it to the specified <tt>destination</tt>.
+     */
+    public static void append( StringBuffer destination, long value )
+    {
+        int v;
+        v = ( int ) ( value >>> 56 );
+        destination.append( highDigits[v] );
+        destination.append( lowDigits[v] );
+
+        v = ( int ) ( value >>> 48 ) & 0xff;
+        destination.append( highDigits[v] );
+        destination.append( lowDigits[v] );
+
+        v = ( int ) ( value >>> 40 ) & 0xff;
+        destination.append( highDigits[v] );
+        destination.append( lowDigits[v] );
+
+        v = ( int ) ( value >>> 32 ) & 0xff;
+        destination.append( highDigits[v] );
+        destination.append( lowDigits[v] );
+
+        v = ( int ) ( value >>> 24 ) & 0xff;
+        destination.append( highDigits[v] );
+        destination.append( lowDigits[v] );
+
+        v = ( int ) ( value >>> 16 ) & 0xff;
+        destination.append( highDigits[v] );
+        destination.append( lowDigits[v] );
+
+        v = ( int ) ( value >>> 8 ) & 0xff;
+        destination.append( highDigits[v] );
+        destination.append( lowDigits[v] );
+
+        v = ( int ) value & 0xff;
+        destination.append( highDigits[v] );
+        destination.append( lowDigits[v] );
+    }
+
+
+    /**
+     * Converts the specified <tt>value</tt> to an octet string and appends
+     * it to the specified <tt>destination</tt>.
+     */
+    public static void append( StringBuffer destination, int value )
+    {
+        int v;
+        v = ( value >>> 24 ) & 0xff;
+        destination.append( highDigits[v] );
+        destination.append( lowDigits[v] );
+
+        v = ( value >>> 16 ) & 0xff;
+        destination.append( highDigits[v] );
+        destination.append( lowDigits[v] );
+
+        v = ( value >>> 8 ) & 0xff;
+        destination.append( highDigits[v] );
+        destination.append( lowDigits[v] );
+
+        v = value & 0xff;
+        destination.append( highDigits[v] );
+        destination.append( lowDigits[v] );
+    }
+
+    /**
+     * Converts the specified binary data into an octet string and returns it.
+     */
+    public static String toString( byte[] src )
+    {
+        final int end = src.length;
+        StringBuffer dst = new StringBuffer( src.length << 1 );
+        for ( int i = 0; i < end; i++ )
+        {
+            dst.append( highDigits[src[i] & 0xff] );
+            dst.append( lowDigits[src[i] & 0xff] );
+        }
+
+        return dst.toString();
+    }
+
+    /**
+     * Converts the specified value into an octet string and returns it.
+     */
+    public static String toString( long value )
+    {
+        StringBuffer dst = new StringBuffer( 16 );
+        append( dst, value );
+        return dst.toString();
+    }
+
+    /**
+     * Converts the specified value into an octet string and returns it.
+     */
+    public static String toString( int value )
+    {
+        StringBuffer dst = new StringBuffer( 8 );
+        append( dst, value );
+        return dst.toString();
+    }
+
+    /**
+     * Converts the specified octet string value into an integer and returns
+     * it.
+     */
+    public static int parseInt( String value )
+    {
+        return Integer.parseInt( value, 16 );
+    }
+
+    /**
+     * Converts the specified octet string value into a long integer and
+     * returns it.
+     */
+    public static long parseLong( String value )
+    {
+        return Long.parseLong( value, 16 );
+    }
+
+
+    private OctetString()
+    {
+    }
+}
diff --git a/old_trunk/mitosis/src/site/site.xml b/old_trunk/mitosis/src/site/site.xml
new file mode 100644
index 0000000..9ec7c16
--- /dev/null
+++ b/old_trunk/mitosis/src/site/site.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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.
+-->
+<!--
+  @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+-->
+<project name="${project.name}">
+    <bannerLeft>
+        <name>${project.name}</name>
+    </bannerLeft>
+    <publishDate position="navigation-bottom" format="dd-MM-yyyy HH:mm" />
+    <body>
+        <menu name="Parent">
+            <item name="Apache Directory ApacheDS" href="../index.html" />
+        </menu>
+        <menu ref="reports" />
+    </body>
+</project>
diff --git a/old_trunk/mitosis/src/test/java/org/apache/directory/mitosis/common/DefaultCSNFactoryTest.java b/old_trunk/mitosis/src/test/java/org/apache/directory/mitosis/common/DefaultCSNFactoryTest.java
new file mode 100644
index 0000000..36611a2
--- /dev/null
+++ b/old_trunk/mitosis/src/test/java/org/apache/directory/mitosis/common/DefaultCSNFactoryTest.java
@@ -0,0 +1,61 @@
+/*
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *   or more contributor license agreements.  See the NOTICE file
+ *   distributed with this work for additional information
+ *   regarding copyright ownership.  The ASF licenses this file
+ *   to you 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.
+ *
+ */
+
+package org.apache.directory.mitosis.common;
+
+
+import junit.framework.TestCase;
+
+
+/**
+ * Test case for the DefaultCSNFactory class.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class DefaultCSNFactoryTest extends TestCase
+{
+    private static final int NUM_GENERATES = 10;
+
+
+    /**
+     * Ensure all CSN's generated by a CSNFactory are unique.
+     *
+     */
+    public void testUnique()
+    {
+        String replicaID = "test";
+        DefaultCSNFactory defaultCSNFactory = new DefaultCSNFactory();
+
+        CSN[] csns = new CSN[NUM_GENERATES];
+
+        for ( int i = 0; i < NUM_GENERATES; i++ )
+        {
+            csns[i] = defaultCSNFactory.newInstance( replicaID );
+        }
+
+        for ( int i = 0; i < NUM_GENERATES; i++ )
+        {
+            for ( int j = i + 1; j < NUM_GENERATES; j++ )
+            {
+                assertFalse( csns[i].equals( csns[j] ) );
+            }
+        }
+    }
+}
diff --git a/old_trunk/mitosis/src/test/java/org/apache/directory/mitosis/common/DefaultCSNTest.java b/old_trunk/mitosis/src/test/java/org/apache/directory/mitosis/common/DefaultCSNTest.java
new file mode 100644
index 0000000..9eaa4fb
--- /dev/null
+++ b/old_trunk/mitosis/src/test/java/org/apache/directory/mitosis/common/DefaultCSNTest.java
@@ -0,0 +1,233 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.common;
+
+
+import junit.framework.TestCase;
+
+
+/**
+ * 
+ * Test for the SimpleCSN class
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class DefaultCSNTest extends TestCase
+{
+
+    public void testCSN()
+    {
+        long ts = System.currentTimeMillis();
+
+        CSN csn = new DefaultCSN( Long.toString( ts, 16 ) + ":abcdefghi0123:" + 1 );
+
+        assertEquals( ts, csn.getTimestamp() );
+        assertEquals( 1, csn.getOperationSequence() );
+        assertEquals( "abcdefghi0123", csn.getReplicaId().toString() );
+    }
+
+
+    public void testCSNEmpty()
+    {
+        try
+        {
+            new DefaultCSN( "" );
+            fail();
+        }
+        catch ( AssertionError ae )
+        {
+            assertTrue( true );
+        }
+        catch ( InvalidCSNException ice )
+        {
+            assertTrue( true );
+        }
+    }
+
+
+    public void testCSNTSOnly()
+    {
+        try
+        {
+            new DefaultCSN( "123" );
+            fail();
+        }
+        catch ( AssertionError ae )
+        {
+            assertTrue( true );
+        }
+        catch ( InvalidCSNException ice )
+        {
+            assertTrue( true );
+        }
+    }
+
+
+    public void testCSNInvalidTS()
+    {
+        try
+        {
+            new DefaultCSN( "zzz:abc:1" );
+            fail();
+        }
+        catch ( AssertionError ae )
+        {
+            assertTrue( true );
+        }
+        catch ( InvalidCSNException ice )
+        {
+            assertTrue( true );
+        }
+    }
+
+
+    public void testCSNNoTS()
+    {
+        try
+        {
+            new DefaultCSN( ":abc:1" );
+            fail();
+        }
+        catch ( AssertionError ae )
+        {
+            assertTrue( true );
+        }
+        catch ( InvalidCSNException ice )
+        {
+            assertTrue( true );
+        }
+    }
+
+
+    public void testCSNInavlidReplica()
+    {
+        try
+        {
+            new DefaultCSN( "123:*:1" );
+            fail();
+        }
+        catch ( AssertionError ae )
+        {
+            assertTrue( true );
+        }
+        catch ( InvalidCSNException ice )
+        {
+            assertTrue( true );
+        }
+    }
+
+
+    public void testCSNNoReplica()
+    {
+        try
+        {
+            new DefaultCSN( "123::1" );
+            fail();
+        }
+        catch ( AssertionError ae )
+        {
+            assertTrue( true );
+        }
+        catch ( InvalidCSNException ice )
+        {
+            assertTrue( true );
+        }
+    }
+
+
+    public void testCSNInavlidOpSeq()
+    {
+        try
+        {
+            new DefaultCSN( "123:abc:zzz" );
+            fail();
+        }
+        catch ( AssertionError ae )
+        {
+            assertTrue( true );
+        }
+        catch ( InvalidCSNException ice )
+        {
+            assertTrue( true );
+        }
+    }
+
+
+    public void testCSNEmptyOpSeq()
+    {
+        try
+        {
+            new DefaultCSN( "123:abc:" );
+            fail();
+        }
+        catch ( AssertionError ae )
+        {
+            assertTrue( true );
+        }
+        catch ( InvalidCSNException ice )
+        {
+            assertTrue( true );
+        }
+    }
+
+
+    public void testCSNNoOpSeq()
+    {
+        try
+        {
+            new DefaultCSN( "123:abc" );
+            fail();
+        }
+        catch ( AssertionError ae )
+        {
+            assertTrue( true );
+        }
+        catch ( InvalidCSNException ice )
+        {
+            assertTrue( true );
+        }
+    }
+
+
+    public void testCSNToBytes()
+    {
+        CSN csn = new DefaultCSN( "0123456789abcdef:test:5678cdef" );
+
+        byte[] bytes = csn.toBytes();
+
+        assertEquals( 0x01, bytes[0] );
+        assertEquals( 0x23, bytes[1] );
+        assertEquals( 0x45, bytes[2] );
+        assertEquals( 0x67, bytes[3] );
+        assertEquals( ( byte ) 0x89, bytes[4] );
+        assertEquals( ( byte ) 0xAB, bytes[5] );
+        assertEquals( ( byte ) 0xCD, bytes[6] );
+        assertEquals( ( byte ) 0xEF, bytes[7] );
+        assertEquals( 0x56, bytes[8] );
+        assertEquals( 0x78, bytes[9] );
+        assertEquals( ( byte ) 0xCD, bytes[10] );
+        assertEquals( ( byte ) 0xEF, bytes[11] );
+
+        assertEquals( "test", new String( bytes, 12, bytes.length - 12 ) );
+
+        CSN deserializedCSN = new DefaultCSN( bytes );
+        assertEquals( csn, deserializedCSN );
+    }
+}
diff --git a/old_trunk/mitosis/src/test/java/org/apache/directory/mitosis/service/AbstractReplicationServiceTestCase.java b/old_trunk/mitosis/src/test/java/org/apache/directory/mitosis/service/AbstractReplicationServiceTestCase.java
new file mode 100644
index 0000000..f6e5b9a
--- /dev/null
+++ b/old_trunk/mitosis/src/test/java/org/apache/directory/mitosis/service/AbstractReplicationServiceTestCase.java
@@ -0,0 +1,203 @@
+/*
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *   or more contributor license agreements.  See the NOTICE file
+ *   distributed with this work for additional information
+ *   regarding copyright ownership.  The ASF licenses this file
+ *   to you 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.
+ *
+ */
+
+package org.apache.directory.mitosis.service;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.directory.mitosis.common.Replica;
+import org.apache.directory.mitosis.common.ReplicaId;
+import org.apache.directory.mitosis.configuration.ReplicationConfiguration;
+import org.apache.directory.server.constants.ServerDNConstants;
+import org.apache.directory.server.core.DefaultDirectoryService;
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.interceptor.Interceptor;
+import org.apache.directory.server.core.jndi.CoreContextFactory;
+import org.apache.mina.util.AvailablePortFinder;
+import org.junit.After;
+import org.junit.Before;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.naming.Context;
+import javax.naming.ldap.InitialLdapContext;
+import javax.naming.ldap.LdapContext;
+import java.io.File;
+import java.net.InetSocketAddress;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+
+
+/**
+ * An abstract base class for replication tests.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public abstract class AbstractReplicationServiceTestCase
+{
+    private static final Logger LOG = LoggerFactory.getLogger( AbstractReplicationServiceTestCase.class );
+    protected Map<String, LdapContext> contexts = new HashMap<String, LdapContext>();
+    protected Map<String, DirectoryService> services = new HashMap<String, DirectoryService>();
+    protected Map<String, ReplicationInterceptor> replicationServices = new HashMap<String, ReplicationInterceptor>();
+
+
+    @Before public void setUp() throws Exception
+    {
+        createReplicas( new String[] { "A", "B", "C" } );
+    }
+
+
+    @After public void tearDown() throws Exception
+    {
+        destroyAllReplicas();
+    }
+
+
+    protected void createReplicas( String[] names ) throws Exception
+    {
+        int lastAvailablePort = 1024;
+
+        Replica[] replicas = new Replica[ names.length ];
+        
+        for( int i = 0; i < names.length; i++ )
+        {
+            int replicationPort = AvailablePortFinder.getNextAvailable( lastAvailablePort );
+            lastAvailablePort = replicationPort + 1;
+
+            replicas[ i ] = new Replica( new ReplicaId( names[ i ] ),
+                    new InetSocketAddress( "127.0.0.1", replicationPort ) );
+        }
+
+        Random random = new Random();
+        String homeDirectory = System.getProperty( "java.io.tmpdir" )
+                + File.separator + "mitosis-"
+                + Long.toHexString( random.nextLong() );
+
+        for ( Replica replica : replicas )
+        {
+            String replicaId = replica.getId().getId();
+            DirectoryService service = new DefaultDirectoryService();
+            service.setInstanceId( replicaId );
+            File workDir = new File( homeDirectory + File.separator + service.getInstanceId() );
+            service.setShutdownHookEnabled( false );
+            service.setWorkingDirectory( workDir );
+
+            List<Interceptor> interceptors = service.getInterceptors();
+
+            ReplicationConfiguration replicationCfg = new ReplicationConfiguration();
+            replicationCfg.setReplicaId( replica.getId() );
+            // Disable automatic replication to prevent unexpected behavior
+            replicationCfg.setReplicationInterval( 0 );
+            replicationCfg.setServerPort( replica.getAddress().getPort() );
+
+            for ( Replica replica1 : replicas )
+            {
+                if ( replica1 != replica )
+                {
+                    replicationCfg.addPeerReplica( replica1 );
+                }
+            }
+
+            ReplicationInterceptor replicationInterceptor = new ReplicationInterceptor();
+            replicationInterceptor.setConfiguration( replicationCfg );
+            interceptors.add( replicationInterceptor );
+
+            service.setInterceptors( interceptors );
+
+            if ( workDir.exists() )
+            {
+                FileUtils.deleteDirectory( workDir );
+            }
+
+            service.startup();
+            
+            Hashtable<String,Object> env = new Hashtable<String,Object>();
+            env.put( DirectoryService.JNDI_KEY, service );
+            env.put( Context.SECURITY_PRINCIPAL, ServerDNConstants.ADMIN_SYSTEM_DN );
+            env.put( Context.SECURITY_CREDENTIALS, "secret" );
+            env.put( Context.SECURITY_AUTHENTICATION, "simple" );
+            env.put( Context.PROVIDER_URL, "" );
+            env.put( Context.INITIAL_CONTEXT_FACTORY, CoreContextFactory.class.getName() );
+
+            // Initialize the server instance.
+            LdapContext context = new InitialLdapContext( env, null );
+            contexts.put( replicaId, context );
+            services.put( replicaId, service );
+            replicationServices.put( replicaId, replicationInterceptor );
+        }
+
+        // Ensure all replicas have had a chance to connect to each other since the last one started.
+        for ( ReplicationInterceptor replicationInterceptor : replicationServices.values() )
+        {
+            replicationInterceptor.interruptConnectors();
+        }
+        Thread.sleep( 5000 );
+    }
+
+
+    protected LdapContext getReplicaContext( String name ) throws Exception
+    {
+        LdapContext context = contexts.get( name );
+        if( context == null )
+        {
+            throw new IllegalArgumentException( "No such replica: " + name );
+        }
+
+        return ( LdapContext ) context.lookup( "" );
+    }
+
+
+    @SuppressWarnings("unchecked")
+    protected void destroyAllReplicas() throws Exception
+    {
+        for( Iterator<DirectoryService> i = services.values().iterator(); i.hasNext(); )
+        {
+            DirectoryService replica = i.next();
+            File workDir = replica.getWorkingDirectory();
+
+            try
+            {
+                replica.shutdown();
+            }
+            catch( Exception e )
+            {
+                LOG.error( "Encountered error while shutting down replica {}", replica.getInstanceId(), e );
+                throw e;
+            }
+
+            try
+            {
+                FileUtils.deleteDirectory( workDir );
+            }
+            catch( Exception e )
+            {
+                e.printStackTrace();
+            }
+
+            workDir.getParentFile().delete();
+
+            i.remove();
+        }
+    }
+}
diff --git a/old_trunk/mitosis/src/test/java/org/apache/directory/mitosis/service/DIRSERVER1013ITest.java b/old_trunk/mitosis/src/test/java/org/apache/directory/mitosis/service/DIRSERVER1013ITest.java
new file mode 100644
index 0000000..a2d9e6d
--- /dev/null
+++ b/old_trunk/mitosis/src/test/java/org/apache/directory/mitosis/service/DIRSERVER1013ITest.java
@@ -0,0 +1,67 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.service;
+
+import javax.naming.directory.Attributes;
+import javax.naming.ldap.LdapContext;
+
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertNull;
+
+
+/**
+ * A test case for DIRSERVER-1013: "Extra RDN attribute created".
+ * 
+ * When mitosis is enabled and an entry is added the OID of the RDN
+ * attribute is added as an extra attribute. As an example, if an
+ * entry ou=test,ou=system is added then it will have attributes
+ * ou=test and 2.5.4.11=test.
+ * 
+ * @author The Apache Directory Project Team (dev@directory.apache.org)
+ * @version $Rev$, $Date$
+ */
+public class DIRSERVER1013ITest extends AbstractReplicationServiceTestCase
+{
+    @Before public void setUp() throws Exception
+    {
+        // Create two replicas as we currently can't have the
+        // replication service enabled without more than one.
+        createReplicas( new String[] { "A", "B" } );
+    }
+    
+    @Test public void testNoRDNOID () throws Exception
+    {
+        LdapContext ctxA = getReplicaContext( "A" );
+        
+        Attributes entry = new AttributesImpl( true );
+        entry.put( "cn", "test" );
+        
+        // We add the 'room' OC to have at least a STRUCTURAL OC
+        entry.put( "objectClass", "top" ).add( "room" );
+        ctxA.bind( "cn=test,ou=system", null, entry );
+        
+        Attributes attributes = ctxA.getAttributes( "cn=test,ou=system" );
+        assertNull( attributes.get( SchemaConstants.CN_AT_OID ) );
+    }
+}
diff --git a/old_trunk/mitosis/src/test/java/org/apache/directory/mitosis/service/ReplicationServiceITest.java b/old_trunk/mitosis/src/test/java/org/apache/directory/mitosis/service/ReplicationServiceITest.java
new file mode 100644
index 0000000..db14704
--- /dev/null
+++ b/old_trunk/mitosis/src/test/java/org/apache/directory/mitosis/service/ReplicationServiceITest.java
@@ -0,0 +1,276 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.service;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.ModificationItem;
+import javax.naming.ldap.LdapContext;
+
+import junit.framework.Assert;
+
+import org.apache.directory.shared.ldap.exception.LdapNameNotFoundException;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.message.ModificationItemImpl;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.schema.DeepTrimToLowerNormalizer;
+import org.apache.directory.shared.ldap.schema.OidNormalizer;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+
+/**
+ * A test case for {@link ReplicationServiceITest}
+ * 
+ * @author The Apache Directory Project Team (dev@directory.apache.org)
+ * @version $Rev$, $Date$
+ */
+public class ReplicationServiceITest extends AbstractReplicationServiceTestCase
+{
+    private Map<String, OidNormalizer> oids;
+    
+    @Before public void setUp() throws Exception
+    {
+        createReplicas( new String[] { "A", "B", "C" } );
+
+        
+        // Initialize OIDs maps for normalization
+        oids = new HashMap<String, OidNormalizer>();
+
+        oids.put( "ou", new OidNormalizer( "ou", new DeepTrimToLowerNormalizer() ) );
+        oids.put( "organizationalUnitName", new OidNormalizer( "ou", new DeepTrimToLowerNormalizer() ) );
+        oids.put( "2.5.4.11", new OidNormalizer( "ou", new DeepTrimToLowerNormalizer() ) );
+        oids.put( "cn", new OidNormalizer( "cn", new DeepTrimToLowerNormalizer() ) );
+        oids.put( "commonName", new OidNormalizer( "cn", new DeepTrimToLowerNormalizer() ) );
+        oids.put( "2.5.4.3", new OidNormalizer( "cn", new DeepTrimToLowerNormalizer() ) );
+    }
+
+    @Ignore
+    @Test public void testOneWay() throws Exception
+    {
+        String dn1 = "cn=test,ou=system";
+        String dn2 = "cn=test2,ou=system";
+        testOneWayBind( dn1 );
+        testOneWayModify( dn1 );
+        testOneWayRename( dn1, dn2, true );
+        testOneWayRename( dn2, dn1, false );
+        testOneWayUnbind( dn1 );
+    }
+    
+    /**
+     * Test that the entry created last will win in the case of a conflict.
+     * 
+     * NOTE: This test is DISABLED as there is an occasional problem when a message is acknowledged
+     * too quickly, meaning no further messages can be sent until it has timed out (DIRSERVER-998).
+     *
+     * @throws Exception on failure
+     */
+    public void disabled_testTwoWayBind() throws Exception
+    {
+        LdapContext ctxA = getReplicaContext( "A" );
+        LdapContext ctxB = getReplicaContext( "B" );
+        LdapContext ctxC = getReplicaContext( "C" );
+
+        Attributes entryA = new AttributesImpl( true );
+        entryA.put( "cn", "test" );
+        entryA.put( "sn", "test" );
+        entryA.put( "ou", "A" );
+        
+        Attribute oc = new AttributeImpl( "objectClass" );
+        oc.add( "top" );
+        oc.add( "person" );
+        oc.add( "organizationalPerson" );
+
+        entryA.put( oc );
+        
+        ctxA.bind( "cn=test,ou=system", null, entryA );
+        
+        // Ensure the second bind is undebatebly the second.
+        Thread.sleep( 100 );
+
+        Attributes entryB = new AttributesImpl( true );
+        entryB.put( "cn", "test" );
+        entryB.put( "sn", "test" );
+        entryB.put( "ou", "B" );
+        entryB.put( oc );
+        ctxB.bind( "cn=test,ou=system", null, entryB );
+
+        // Let both replicas replicate.  Note that a replica can only receive
+        // logs from one peer at a time so we must delay between replications.
+        replicationServices.get( "A" ).replicate();
+        
+        Thread.sleep( 5000 );
+        
+        replicationServices.get( "B" ).replicate();
+        
+        Thread.sleep( 5000 );
+
+        Assert.assertEquals( "B", getAttributeValue( ctxA, "cn=test,ou=system", "ou" ) );
+        Assert.assertEquals( "B", getAttributeValue( ctxB, "cn=test,ou=system", "ou" ) );
+        Assert.assertEquals( "B", getAttributeValue( ctxC, "cn=test,ou=system", "ou" ) );
+    }
+    
+    private void testOneWayBind( String dn ) throws Exception
+    {
+        LdapContext ctxA = getReplicaContext( "A" );
+        LdapContext ctxB = getReplicaContext( "B" );
+        LdapContext ctxC = getReplicaContext( "C" );
+        
+        Attributes entry = new AttributesImpl( true );
+        entry.put( "cn", "test" );
+        entry.put( "sn", "test" );
+        
+        Attribute oc = new AttributeImpl( "objectClass" );
+        oc.add( "top" );
+        oc.add( "person" );
+        oc.add( "organizationalPerson" );
+        
+        entry.put( oc );
+        
+        ctxA.bind( dn, null, entry );
+
+        replicationServices.get( "A" ).replicate();
+        
+        Thread.sleep( 5000 );
+
+        Assert.assertNotNull( ctxA.lookup( dn ) );
+        Assert.assertNotNull( ctxB.lookup( dn ) );
+        Assert.assertNotNull( ctxC.lookup( dn ) );
+    }
+
+    private void testOneWayModify( String dn ) throws Exception
+    {
+        LdapContext ctxA = getReplicaContext( "A" );
+        LdapContext ctxB = getReplicaContext( "B" );
+        LdapContext ctxC = getReplicaContext( "C" );
+        
+        String newValue = "anything";
+        
+        ctxA.modifyAttributes( dn, new ModificationItem[] {
+            new ModificationItemImpl( DirContext.REPLACE_ATTRIBUTE, new AttributeImpl( "ou", newValue ))} );
+
+        replicationServices.get( "A" ).replicate();
+        
+        Thread.sleep( 5000 );
+
+        Assert.assertEquals( newValue, getAttributeValue( ctxB, dn, "ou" ) );
+        Assert.assertEquals( newValue, getAttributeValue( ctxC, dn, "ou" ) );
+    }
+
+    private void testOneWayRename( String dn1, String dn2, boolean deleteRDN ) throws Exception
+    {
+        LdapContext ctxA = getReplicaContext( "A" );
+        LdapContext ctxB = getReplicaContext( "B" );
+        LdapContext ctxC = getReplicaContext( "C" );
+        
+        String oldRDNValue = (String) new LdapDN(dn1).getRdn().getUpValue();
+        
+        ctxA.addToEnvironment( "java.naming.ldap.deleteRDN", Boolean.toString( deleteRDN ) );
+        ctxA.rename( dn1, dn2 );
+        
+        replicationServices.get( "A" ).replicate();
+
+        Thread.sleep( 5000 );
+        
+        assertNotExists( ctxA, dn1 );
+        assertNotExists( ctxB, dn1 );
+        assertNotExists( ctxC, dn1 );
+        Assert.assertNotNull( ctxA.lookup( dn2 ) );
+        Assert.assertNotNull( ctxB.lookup( dn2 ) );
+        Assert.assertNotNull( ctxC.lookup( dn2 ) );
+
+        Attribute oldRDNAttributeA = ctxA.getAttributes( dn2 ).get( new LdapDN(dn1).getRdn().getUpType() );
+        Attribute oldRDNAttributeB = ctxB.getAttributes( dn2 ).get( new LdapDN(dn1).getRdn().getUpType() );
+        Attribute oldRDNAttributeC = ctxC.getAttributes( dn2 ).get( new LdapDN(dn1).getRdn().getUpType() );
+        boolean oldRDNExistsA = attributeContainsValue( oldRDNAttributeA, oldRDNValue );
+        boolean oldRDNExistsB = attributeContainsValue( oldRDNAttributeB, oldRDNValue );
+        boolean oldRDNExistsC = attributeContainsValue( oldRDNAttributeC, oldRDNValue );
+        
+        if ( deleteRDN )
+        {
+            Assert.assertFalse( oldRDNExistsA );
+            Assert.assertFalse( oldRDNExistsB );
+            Assert.assertFalse( oldRDNExistsC );
+        }
+        else
+        {
+            Assert.assertTrue( oldRDNExistsA );
+            Assert.assertTrue( oldRDNExistsB );
+            Assert.assertTrue( oldRDNExistsC );
+        }
+    }
+    
+    private void testOneWayUnbind( String dn ) throws Exception
+    {
+        LdapContext ctxA = getReplicaContext( "A" );
+        LdapContext ctxB = getReplicaContext( "B" );
+        LdapContext ctxC = getReplicaContext( "C" );
+        
+        ctxA.unbind( dn );
+        
+        replicationServices.get( "A" ).replicate();
+
+        Thread.sleep( 5000 );
+        
+        assertNotExists( ctxA, dn );
+        assertNotExists( ctxB, dn );
+        assertNotExists( ctxC, dn );
+    }
+    
+    private void assertNotExists( LdapContext ctx, String dn ) throws NamingException
+    {
+        try
+        {
+            ctx.lookup( dn );
+        }
+        catch ( LdapNameNotFoundException e )
+        {
+            // This is expected so return immediately.
+            return;
+        }
+        throw new AssertionError( "The entry exists" );
+    }
+    
+    private String getAttributeValue( LdapContext ctx, String name, String attrName ) throws Exception
+    {
+        Attribute attr = ctx.getAttributes( name ).get( attrName );
+        return ( String ) attr.get();
+    }
+    
+    private boolean attributeContainsValue( Attribute attribute, Object value ) throws NamingException
+    {
+        boolean foundValue = false;
+        for ( NamingEnumeration ne = attribute.getAll(); ne.hasMore(); )
+        {
+            if ( value.equals( ne.next() ) )
+            {
+                foundValue = true;
+            }
+        }
+        return foundValue;
+    }
+}
diff --git a/old_trunk/mitosis/src/test/java/org/apache/directory/mitosis/service/protocol/codec/AbstractMessageCodecTest.java b/old_trunk/mitosis/src/test/java/org/apache/directory/mitosis/service/protocol/codec/AbstractMessageCodecTest.java
new file mode 100644
index 0000000..45cde99
--- /dev/null
+++ b/old_trunk/mitosis/src/test/java/org/apache/directory/mitosis/service/protocol/codec/AbstractMessageCodecTest.java
@@ -0,0 +1,109 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.service.protocol.codec;
+
+
+import junit.framework.Assert;
+
+import org.apache.directory.mitosis.service.protocol.message.BaseMessage;
+import org.apache.mina.common.ByteBuffer;
+import org.apache.mina.common.WriteFuture;
+import org.apache.mina.filter.codec.ProtocolDecoderOutput;
+import org.apache.mina.filter.codec.demux.MessageDecoder;
+import org.apache.mina.filter.codec.demux.MessageEncoder;
+import org.apache.mina.filter.codec.support.SimpleProtocolEncoderOutput;
+import org.junit.Test;
+
+import sun.misc.Queue;
+
+
+public abstract class AbstractMessageCodecTest
+{
+    private final BaseMessage message;
+    private final MessageEncoder encoder;
+    private final MessageDecoder decoder;
+
+
+    protected AbstractMessageCodecTest( BaseMessage message, MessageEncoder encoder, MessageDecoder decoder )
+    {
+        if ( message == null )
+        {
+            throw new NullPointerException( "message" );
+        }
+        if ( encoder == null )
+        {
+            throw new NullPointerException( "encoder" );
+        }
+        if ( decoder == null )
+        {
+            throw new NullPointerException( "decoder" );
+        }
+
+        this.message = message;
+        this.encoder = encoder;
+        this.decoder = decoder;
+    }
+
+
+    @Test public void testMessageCodec() throws Exception
+    {
+        SimpleProtocolEncoderOutput encoderOut = new SimpleProtocolEncoderOutput()
+        {
+            protected WriteFuture doFlush( ByteBuffer buf )
+            {
+                return null;
+            }
+
+        };
+        encoder.encode( null, message, encoderOut );
+        ByteBuffer buf = encoderOut.getBufferQueue().poll();
+
+        buf.mark();
+        Assert.assertTrue( decoder.decodable( null, buf ) == MessageDecoder.OK );
+        buf.reset();
+
+        ProtocolDecoderOutputImpl decoderOut = new ProtocolDecoderOutputImpl();
+        decoder.decode( null, buf, decoderOut );
+
+        Assert.assertTrue( compare( message, ( BaseMessage ) decoderOut.messages.dequeue() ) );
+    }
+
+
+    protected boolean compare( BaseMessage expected, BaseMessage actual )
+    {
+        return expected.equals( actual );
+    }
+
+    private class ProtocolDecoderOutputImpl implements ProtocolDecoderOutput
+    {
+        private final Queue messages = new Queue();
+
+
+        public void flush()
+        {
+        }
+
+
+        public void write( Object message )
+        {
+            messages.enqueue( message );
+        }
+    }
+}
diff --git a/old_trunk/mitosis/src/test/java/org/apache/directory/mitosis/service/protocol/codec/BeginLogEntriesAckMessageCodecTest.java b/old_trunk/mitosis/src/test/java/org/apache/directory/mitosis/service/protocol/codec/BeginLogEntriesAckMessageCodecTest.java
new file mode 100644
index 0000000..470fd30
--- /dev/null
+++ b/old_trunk/mitosis/src/test/java/org/apache/directory/mitosis/service/protocol/codec/BeginLogEntriesAckMessageCodecTest.java
@@ -0,0 +1,57 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.service.protocol.codec;
+
+
+import org.apache.directory.mitosis.common.CSNVector;
+import org.apache.directory.mitosis.common.ReplicaId;
+import org.apache.directory.mitosis.common.DefaultCSN;
+import org.apache.directory.mitosis.service.protocol.Constants;
+import org.apache.directory.mitosis.service.protocol.codec.BeginLogEntriesAckMessageDecoder;
+import org.apache.directory.mitosis.service.protocol.codec.BeginLogEntriesAckMessageEncoder;
+import org.apache.directory.mitosis.service.protocol.message.BeginLogEntriesAckMessage;
+
+
+public class BeginLogEntriesAckMessageCodecTest extends AbstractMessageCodecTest
+{
+
+    private static final CSNVector PURGE_VECTOR = new CSNVector();
+    private static final CSNVector UPDATE_VECTOR = new CSNVector();
+
+    static
+    {
+        PURGE_VECTOR.setCSN( new DefaultCSN( System.currentTimeMillis() - 400, new ReplicaId( "replica0" ), 3456 ) );
+        PURGE_VECTOR.setCSN( new DefaultCSN( System.currentTimeMillis() - 300, new ReplicaId( "replica1" ), 9012 ) );
+        PURGE_VECTOR.setCSN( new DefaultCSN( System.currentTimeMillis() - 200, new ReplicaId( "replica2" ), 5678 ) );
+        PURGE_VECTOR.setCSN( new DefaultCSN( System.currentTimeMillis() - 100, new ReplicaId( "replica3" ), 1234 ) );
+
+        UPDATE_VECTOR.setCSN( new DefaultCSN( System.currentTimeMillis() + 000, new ReplicaId( "replica0" ), 1234 ) );
+        UPDATE_VECTOR.setCSN( new DefaultCSN( System.currentTimeMillis() + 100, new ReplicaId( "replica1" ), 5678 ) );
+        UPDATE_VECTOR.setCSN( new DefaultCSN( System.currentTimeMillis() + 200, new ReplicaId( "replica2" ), 9012 ) );
+        UPDATE_VECTOR.setCSN( new DefaultCSN( System.currentTimeMillis() + 300, new ReplicaId( "replica3" ), 3456 ) );
+    }
+
+
+    public BeginLogEntriesAckMessageCodecTest()
+    {
+        super( new BeginLogEntriesAckMessage( 1234, Constants.OK, PURGE_VECTOR, UPDATE_VECTOR ),
+            new BeginLogEntriesAckMessageEncoder(), new BeginLogEntriesAckMessageDecoder() );
+    }
+}
diff --git a/old_trunk/mitosis/src/test/java/org/apache/directory/mitosis/service/protocol/codec/BeginLogEntriesAckMessageCodecTest2.java b/old_trunk/mitosis/src/test/java/org/apache/directory/mitosis/service/protocol/codec/BeginLogEntriesAckMessageCodecTest2.java
new file mode 100644
index 0000000..2c74a04
--- /dev/null
+++ b/old_trunk/mitosis/src/test/java/org/apache/directory/mitosis/service/protocol/codec/BeginLogEntriesAckMessageCodecTest2.java
@@ -0,0 +1,37 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.service.protocol.codec;
+
+
+import org.apache.directory.mitosis.service.protocol.Constants;
+import org.apache.directory.mitosis.service.protocol.codec.BeginLogEntriesAckMessageDecoder;
+import org.apache.directory.mitosis.service.protocol.codec.BeginLogEntriesAckMessageEncoder;
+import org.apache.directory.mitosis.service.protocol.message.BeginLogEntriesAckMessage;
+
+
+public class BeginLogEntriesAckMessageCodecTest2 extends AbstractMessageCodecTest
+{
+
+    public BeginLogEntriesAckMessageCodecTest2()
+    {
+        super( new BeginLogEntriesAckMessage( 1234, Constants.NOT_OK, null, null ),
+            new BeginLogEntriesAckMessageEncoder(), new BeginLogEntriesAckMessageDecoder() );
+    }
+}
diff --git a/old_trunk/mitosis/src/test/java/org/apache/directory/mitosis/service/protocol/codec/BeginLogEntriesMessageCodecTest.java b/old_trunk/mitosis/src/test/java/org/apache/directory/mitosis/service/protocol/codec/BeginLogEntriesMessageCodecTest.java
new file mode 100644
index 0000000..3a95df4
--- /dev/null
+++ b/old_trunk/mitosis/src/test/java/org/apache/directory/mitosis/service/protocol/codec/BeginLogEntriesMessageCodecTest.java
@@ -0,0 +1,36 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.service.protocol.codec;
+
+
+import org.apache.directory.mitosis.service.protocol.codec.BeginLogEntriesMessageDecoder;
+import org.apache.directory.mitosis.service.protocol.codec.BeginLogEntriesMessageEncoder;
+import org.apache.directory.mitosis.service.protocol.message.BeginLogEntriesMessage;
+
+
+public class BeginLogEntriesMessageCodecTest extends AbstractMessageCodecTest
+{
+
+    public BeginLogEntriesMessageCodecTest()
+    {
+        super( new BeginLogEntriesMessage( 1234 ), new BeginLogEntriesMessageEncoder(),
+            new BeginLogEntriesMessageDecoder() );
+    }
+}
diff --git a/old_trunk/mitosis/src/test/java/org/apache/directory/mitosis/service/protocol/codec/EndLogEntriesAckMessageCodecTest.java b/old_trunk/mitosis/src/test/java/org/apache/directory/mitosis/service/protocol/codec/EndLogEntriesAckMessageCodecTest.java
new file mode 100644
index 0000000..e6371a8
--- /dev/null
+++ b/old_trunk/mitosis/src/test/java/org/apache/directory/mitosis/service/protocol/codec/EndLogEntriesAckMessageCodecTest.java
@@ -0,0 +1,36 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.service.protocol.codec;
+
+
+import org.apache.directory.mitosis.service.protocol.codec.EndLogEntriesAckMessageDecoder;
+import org.apache.directory.mitosis.service.protocol.codec.EndLogEntriesAckMessageEncoder;
+import org.apache.directory.mitosis.service.protocol.message.EndLogEntriesAckMessage;
+
+
+public class EndLogEntriesAckMessageCodecTest extends AbstractMessageCodecTest
+{
+
+    public EndLogEntriesAckMessageCodecTest()
+    {
+        super( new EndLogEntriesAckMessage( 1234, 5678 ), new EndLogEntriesAckMessageEncoder(),
+            new EndLogEntriesAckMessageDecoder() );
+    }
+}
diff --git a/old_trunk/mitosis/src/test/java/org/apache/directory/mitosis/service/protocol/codec/EndLogEntriesMessageCodecTest.java b/old_trunk/mitosis/src/test/java/org/apache/directory/mitosis/service/protocol/codec/EndLogEntriesMessageCodecTest.java
new file mode 100644
index 0000000..aa21a85
--- /dev/null
+++ b/old_trunk/mitosis/src/test/java/org/apache/directory/mitosis/service/protocol/codec/EndLogEntriesMessageCodecTest.java
@@ -0,0 +1,35 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.service.protocol.codec;
+
+
+import org.apache.directory.mitosis.service.protocol.codec.EndLogEntriesMessageDecoder;
+import org.apache.directory.mitosis.service.protocol.codec.EndLogEntriesMessageEncoder;
+import org.apache.directory.mitosis.service.protocol.message.EndLogEntriesMessage;
+
+
+public class EndLogEntriesMessageCodecTest extends AbstractMessageCodecTest
+{
+
+    public EndLogEntriesMessageCodecTest()
+    {
+        super( new EndLogEntriesMessage( 1234 ), new EndLogEntriesMessageEncoder(), new EndLogEntriesMessageDecoder() );
+    }
+}
diff --git a/old_trunk/mitosis/src/test/java/org/apache/directory/mitosis/service/protocol/codec/LogEntryAckMessageCodecTest.java b/old_trunk/mitosis/src/test/java/org/apache/directory/mitosis/service/protocol/codec/LogEntryAckMessageCodecTest.java
new file mode 100644
index 0000000..b238285
--- /dev/null
+++ b/old_trunk/mitosis/src/test/java/org/apache/directory/mitosis/service/protocol/codec/LogEntryAckMessageCodecTest.java
@@ -0,0 +1,35 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.service.protocol.codec;
+
+
+import org.apache.directory.mitosis.service.protocol.codec.LogEntryAckMessageDecoder;
+import org.apache.directory.mitosis.service.protocol.codec.LogEntryAckMessageEncoder;
+import org.apache.directory.mitosis.service.protocol.message.LogEntryAckMessage;
+
+
+public class LogEntryAckMessageCodecTest extends AbstractMessageCodecTest
+{
+
+    public LogEntryAckMessageCodecTest()
+    {
+        super( new LogEntryAckMessage( 1234, 5678 ), new LogEntryAckMessageEncoder(), new LogEntryAckMessageDecoder() );
+    }
+}
diff --git a/old_trunk/mitosis/src/test/java/org/apache/directory/mitosis/service/protocol/codec/LogEntryMessageCodecTest.java b/old_trunk/mitosis/src/test/java/org/apache/directory/mitosis/service/protocol/codec/LogEntryMessageCodecTest.java
new file mode 100644
index 0000000..84df081
--- /dev/null
+++ b/old_trunk/mitosis/src/test/java/org/apache/directory/mitosis/service/protocol/codec/LogEntryMessageCodecTest.java
@@ -0,0 +1,93 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.service.protocol.codec;
+
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.naming.InvalidNameException;
+import javax.naming.NamingException;
+
+import org.apache.directory.mitosis.common.ReplicaId;
+import org.apache.directory.mitosis.common.DefaultCSN;
+import org.apache.directory.mitosis.operation.AddAttributeOperation;
+import org.apache.directory.mitosis.service.protocol.codec.LogEntryMessageDecoder;
+import org.apache.directory.mitosis.service.protocol.codec.LogEntryMessageEncoder;
+import org.apache.directory.mitosis.service.protocol.message.BaseMessage;
+import org.apache.directory.mitosis.service.protocol.message.LogEntryMessage;
+import org.apache.directory.server.core.DefaultDirectoryService;
+import org.apache.directory.server.core.entry.DefaultServerAttribute;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.schema.DeepTrimToLowerNormalizer;
+import org.apache.directory.shared.ldap.schema.OidNormalizer;
+
+
+public class LogEntryMessageCodecTest extends AbstractMessageCodecTest
+{
+    private static Map<String, OidNormalizer> oids = new HashMap<String, OidNormalizer>();
+
+    private static DefaultDirectoryService service;
+    
+    static 
+    {
+        oids.put( "ou", new OidNormalizer( "ou", new DeepTrimToLowerNormalizer() ) );
+        oids.put( "organizationalUnitName", new OidNormalizer( "ou", new DeepTrimToLowerNormalizer() ) );
+        oids.put( "2.5.4.11", new OidNormalizer( "ou", new DeepTrimToLowerNormalizer() ) );
+        service = new DefaultDirectoryService();        
+    }
+    
+    
+
+    public LogEntryMessageCodecTest() throws InvalidNameException, NamingException
+    {
+        // Initialize OIDs maps for normalization
+        /*Map<String, OidNormalizer> oids = new HashMap<String, OidNormalizer>();
+
+        oids.put( "ou", new OidNormalizer( "ou", new DeepTrimToLowerNormalizer() ) );
+        oids.put( "organizationalUnitName", new OidNormalizer( "ou", new DeepTrimToLowerNormalizer() ) );
+        oids.put( "2.5.4.11", new OidNormalizer( "ou", new DeepTrimToLowerNormalizer() ) );
+         */
+        super(
+            new LogEntryMessage( 
+                1234, 
+                new AddAttributeOperation( 
+                    new DefaultCSN( System.currentTimeMillis(),
+                        new ReplicaId( "testReplica0" ), 1234 ), 
+                    new LdapDN( "ou=system" ).normalize( oids ),
+                    new DefaultServerAttribute( "ou", 
+                        service.getRegistries().getAttributeTypeRegistry().lookup( "ou" ), "Test" ) ) ), 
+            new LogEntryMessageEncoder(), 
+            new LogEntryMessageDecoder() );
+    }
+
+
+    protected boolean compare( BaseMessage expected0, BaseMessage actual0 )
+    {
+        LogEntryMessage expected = ( LogEntryMessage ) expected0;
+        LogEntryMessage actual = ( LogEntryMessage ) actual0;
+
+        // We don't compare operation here because it is {@link OperationCodec}'s
+        // duty to serialize and deserialize Invocations.
+        return expected.getType() == actual.getType() && expected.getSequence() == actual.getSequence()
+            && expected.getOperation().getCSN().equals( actual.getOperation().getCSN() )
+            && expected.getOperation().getClass() == actual.getOperation().getClass();
+    }
+}
diff --git a/old_trunk/mitosis/src/test/java/org/apache/directory/mitosis/service/protocol/codec/LoginAckMessageCodecTest.java b/old_trunk/mitosis/src/test/java/org/apache/directory/mitosis/service/protocol/codec/LoginAckMessageCodecTest.java
new file mode 100644
index 0000000..b693947
--- /dev/null
+++ b/old_trunk/mitosis/src/test/java/org/apache/directory/mitosis/service/protocol/codec/LoginAckMessageCodecTest.java
@@ -0,0 +1,37 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.service.protocol.codec;
+
+
+import org.apache.directory.mitosis.common.ReplicaId;
+import org.apache.directory.mitosis.service.protocol.codec.LoginAckMessageDecoder;
+import org.apache.directory.mitosis.service.protocol.codec.LoginAckMessageEncoder;
+import org.apache.directory.mitosis.service.protocol.message.LoginAckMessage;
+
+
+public class LoginAckMessageCodecTest extends AbstractMessageCodecTest
+{
+
+    public LoginAckMessageCodecTest()
+    {
+        super( new LoginAckMessage( 1234, 5678, new ReplicaId( "ReplicaABCD" ) ), new LoginAckMessageEncoder(),
+            new LoginAckMessageDecoder() );
+    }
+}
diff --git a/old_trunk/mitosis/src/test/java/org/apache/directory/mitosis/service/protocol/codec/LoginMessageCodecTest.java b/old_trunk/mitosis/src/test/java/org/apache/directory/mitosis/service/protocol/codec/LoginMessageCodecTest.java
new file mode 100644
index 0000000..45bd84f
--- /dev/null
+++ b/old_trunk/mitosis/src/test/java/org/apache/directory/mitosis/service/protocol/codec/LoginMessageCodecTest.java
@@ -0,0 +1,37 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.service.protocol.codec;
+
+
+import org.apache.directory.mitosis.common.ReplicaId;
+import org.apache.directory.mitosis.service.protocol.codec.LoginMessageDecoder;
+import org.apache.directory.mitosis.service.protocol.codec.LoginMessageEncoder;
+import org.apache.directory.mitosis.service.protocol.message.LoginMessage;
+
+
+public class LoginMessageCodecTest extends AbstractMessageCodecTest
+{
+
+    public LoginMessageCodecTest()
+    {
+        super( new LoginMessage( 1234, new ReplicaId( "myReplica0" ) ), new LoginMessageEncoder(),
+            new LoginMessageDecoder() );
+    }
+}
diff --git a/old_trunk/mitosis/src/test/java/org/apache/directory/mitosis/store/derby/DerbyReplicationStoreTest.java b/old_trunk/mitosis/src/test/java/org/apache/directory/mitosis/store/derby/DerbyReplicationStoreTest.java
new file mode 100644
index 0000000..820140e
--- /dev/null
+++ b/old_trunk/mitosis/src/test/java/org/apache/directory/mitosis/store/derby/DerbyReplicationStoreTest.java
@@ -0,0 +1,402 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.mitosis.store.derby;
+ 
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.naming.Name;
+import javax.naming.ldap.LdapName;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.directory.server.core.DefaultDirectoryService;
+import org.apache.directory.server.core.entry.DefaultServerAttribute;
+import org.apache.directory.server.core.entry.DefaultServerEntry;
+import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.schema.DeepTrimToLowerNormalizer;
+import org.apache.directory.shared.ldap.schema.OidNormalizer;
+import org.apache.directory.mitosis.common.CSN;
+import org.apache.directory.mitosis.common.CSNFactory;
+import org.apache.directory.mitosis.common.CSNVector;
+import org.apache.directory.mitosis.common.ReplicaId;
+import org.apache.directory.mitosis.common.DefaultCSN;
+import org.apache.directory.mitosis.common.DefaultCSNFactory;
+import org.apache.directory.mitosis.common.DefaultUUIDFactory;
+import org.apache.directory.mitosis.common.UUID;
+import org.apache.directory.mitosis.common.UUIDFactory;
+import org.apache.directory.mitosis.configuration.ReplicationConfiguration;
+import org.apache.directory.mitosis.operation.AddAttributeOperation;
+import org.apache.directory.mitosis.operation.AddEntryOperation;
+import org.apache.directory.mitosis.operation.CompositeOperation;
+import org.apache.directory.mitosis.operation.DeleteAttributeOperation;
+import org.apache.directory.mitosis.operation.Operation;
+import org.apache.directory.mitosis.operation.ReplaceAttributeOperation;
+import org.apache.directory.mitosis.store.ReplicationLogIterator;
+import org.apache.directory.mitosis.store.ReplicationStoreException;
+
+
+public class DerbyReplicationStoreTest extends TestCase
+{
+    private static final ReplicaId REPLICA_ID = new ReplicaId( "TEST_REPLICA" );
+    private static final ReplicaId OTHER_REPLICA_ID = new ReplicaId( "OTHER_REPLICA" );
+    private static final ReplicaId OTHER_REPLICA_ID_2 = new ReplicaId( "OTHER_REPLICA_2" );
+    private static final File DB_PATH = new File( "target/testDB" );
+
+    private final UUIDFactory uuidFactory = new DefaultUUIDFactory();
+    private final CSNFactory csnFactory = new DefaultCSNFactory();
+    private DerbyReplicationStore store;
+    private int testCount;
+    private long startTime;
+    private DefaultDirectoryService service;
+
+
+    public void setUp() throws Exception
+    {
+        dropDatabase();
+        startupDatabase( REPLICA_ID );
+        initStopWatch();
+    }
+
+
+    private void startupDatabase( ReplicaId replicaId ) throws Exception
+    {
+        // Prepare configuration
+        ReplicationConfiguration cfg = new ReplicationConfiguration();
+        cfg.setReplicaId( replicaId );
+
+        // Open store
+        store = new DerbyReplicationStore();
+        store.setTablePrefix( "TEST_" );
+        service = new DefaultDirectoryService();
+        service.setWorkingDirectory( DB_PATH );
+        store.open( service, cfg );
+    }
+
+
+    public void tearDown() throws Exception
+    {
+        store.close();
+        dropDatabase();
+    }
+
+
+    private void dropDatabase() throws IOException
+    {
+        FileUtils.deleteDirectory( DB_PATH );
+        File logFile = new File( "derby.log" );
+        if ( !logFile.delete() )
+        {
+            logFile.deleteOnExit();
+        }
+    }
+
+
+    public void testOperations() throws Exception
+    {
+        subTestReopen();
+        printElapsedTime( "Reopen" );
+        subTestUUID();
+        printElapsedTime( "UUID" );
+        subTestEmptyLog();
+        printElapsedTime( "EmptyLog" );
+        subTestWriteLog();
+        printElapsedTime( "WriteLog" );
+        subTestRemoveLogs();
+        printElapsedTime( "RemoveLogs" );
+        subTestVectors();
+        printElapsedTime( "Vectors" );
+    }
+
+
+    private void subTestReopen() throws Exception
+    {
+        store.close();
+        try
+        {
+            startupDatabase( OTHER_REPLICA_ID );
+            Assert.fail( "Store cannot start up with wrong replica ID." );
+        }
+        catch ( ReplicationStoreException e )
+        {
+        }
+        startupDatabase( REPLICA_ID );
+    }
+
+
+    private void subTestUUID() throws Exception
+    {
+        UUID uuid = uuidFactory.newInstance();
+        Name name = new LdapName( "ou=a, ou=b" );
+        Assert.assertTrue( store.putUUID( uuid, name ) );
+        Assert.assertEquals( name, store.getDN( uuid ) );
+        Assert.assertTrue( store.removeUUID( uuid ) );
+        Assert.assertFalse( store.removeUUID( uuid ) );
+        Assert.assertNull( store.getDN( uuid ) );
+    }
+
+
+    private void subTestEmptyLog() throws Exception
+    {
+        ReplicationLogIterator it;
+
+        it = store.getLogs( csnFactory.newInstance( REPLICA_ID ), true );
+        Assert.assertFalse( it.next() );
+        it.close();
+        it = store.getLogs( csnFactory.newInstance( REPLICA_ID ), false );
+        Assert.assertFalse( it.next() );
+        it.close();
+        it = store.getLogs( csnFactory.newInstance( OTHER_REPLICA_ID ), true );
+        Assert.assertFalse( it.next() );
+        it.close();
+        it = store.getLogs( csnFactory.newInstance( OTHER_REPLICA_ID ), false );
+        Assert.assertFalse( it.next() );
+        it.close();
+
+        Assert.assertEquals( 0, store.getLogSize() );
+    }
+
+
+    private void subTestWriteLog() throws Exception
+    {
+        Map<String, OidNormalizer> oids = new HashMap<String, OidNormalizer>();
+
+        oids.put( "ou", new OidNormalizer( "ou", new DeepTrimToLowerNormalizer() ) );
+        oids.put( "organizationalUnitName", new OidNormalizer( "ou", new DeepTrimToLowerNormalizer() ) );
+        oids.put( "2.5.4.11", new OidNormalizer( "ou", new DeepTrimToLowerNormalizer() ) );
+
+        AttributeTypeRegistry atRegistry = service.getRegistries().getAttributeTypeRegistry();
+
+        CSN csn = csnFactory.newInstance( REPLICA_ID );
+        CompositeOperation op1 = new CompositeOperation( csn );
+        LdapDN ouA =  new LdapDN( "ou=a" ).normalize( oids );
+        op1.add( new AddEntryOperation( csn, 
+            new DefaultServerEntry( service.getRegistries(), ouA ) ) );
+        
+        op1.add( new AddAttributeOperation( csn, ouA, 
+            new DefaultServerAttribute( "ou", atRegistry.lookup( "ou" ), "valie" ) ) );
+        
+        op1.add( new ReplaceAttributeOperation( csn, ouA, 
+            new DefaultServerAttribute( "ou", atRegistry.lookup( "ou" ), "valie" ) ) );
+        
+        op1.add( new DeleteAttributeOperation( csn, ouA, 
+            new DefaultServerAttribute( "ou", atRegistry.lookup( "ou" ), "valie" ) ) );
+
+        store.putLog( op1 );
+        testGetLogs( csn, op1 );
+
+        csn = csnFactory.newInstance( OTHER_REPLICA_ID );
+        CompositeOperation op2 = new CompositeOperation( csn );
+        op2.add( new AddEntryOperation( csn, 
+            new DefaultServerEntry( service.getRegistries(), ouA ) ) );
+        
+        op2.add( new AddAttributeOperation( csn, ouA, 
+            new DefaultServerAttribute( "ou", atRegistry.lookup( "ou" ), "valie" ) ) );
+        
+        op2.add( new ReplaceAttributeOperation( csn, ouA, 
+            new DefaultServerAttribute( "ou", atRegistry.lookup( "ou" ), "valie" ) ) );
+        
+        op2.add( new DeleteAttributeOperation( csn, ouA, 
+            new DefaultServerAttribute( "ou", atRegistry.lookup( "ou" ), "valie" ) ) );
+
+        store.putLog( op2 );
+        testGetLogs( csn, op2 );
+
+        Assert.assertEquals( 2, store.getLogSize() );
+        Assert.assertEquals( 1, store.getLogSize( REPLICA_ID ) );
+        Assert.assertEquals( 1, store.getLogSize( OTHER_REPLICA_ID ) );
+
+        // Test getLogs(CSNVector, true)
+        List<Operation> expected = new ArrayList<Operation>();
+        expected.add( op1 );
+        expected.add( op2 );
+        CSNVector updateVector = new CSNVector();
+        testGetLogs( updateVector, true, expected );
+
+        updateVector = new CSNVector();
+        updateVector.setCSN( op1.getCSN() );
+        testGetLogs( updateVector, true, expected );
+        
+        updateVector = new CSNVector();
+        updateVector.setCSN( op2.getCSN() );
+        testGetLogs( updateVector, true, expected );
+        
+        updateVector = new CSNVector();
+        updateVector.setCSN( op1.getCSN() );
+        updateVector.setCSN( op2.getCSN() );
+        testGetLogs( updateVector, true, expected );
+
+        // Test getLogs(CSNVector, false)
+        expected = new ArrayList<Operation>();
+        expected.add( op1 );
+        expected.add( op2 );
+        updateVector = new CSNVector();
+        testGetLogs( updateVector, false, expected );
+        expected = new ArrayList<Operation>();
+        expected.add( op2 );
+        updateVector = new CSNVector();
+        updateVector.setCSN( op1.getCSN() );
+        testGetLogs( updateVector, false, expected );
+        expected = new ArrayList<Operation>();
+        expected.add( op1 );
+        updateVector = new CSNVector();
+        updateVector.setCSN( op2.getCSN() );
+        testGetLogs( updateVector, false, expected );
+        expected = new ArrayList<Operation>();
+        updateVector = new CSNVector();
+        updateVector.setCSN( op1.getCSN() );
+        updateVector.setCSN( op2.getCSN() );
+        testGetLogs( updateVector, false, expected );
+    }
+
+
+    private void subTestRemoveLogs()
+    {
+        CSN csn;
+        ReplicationLogIterator it;
+
+        it = store.getLogs( new DefaultCSN( 0, REPLICA_ID, 0 ), false );
+        it.next();
+        csn = it.getOperation().getCSN();
+        it.close();
+
+        Assert.assertEquals( 0, store.removeLogs( csn, false ) );
+        Assert.assertEquals( 1, store.removeLogs( csn, true ) );
+        Assert.assertEquals( 0, store.getLogSize( REPLICA_ID ) );
+
+        it = store.getLogs( new DefaultCSN( 0, OTHER_REPLICA_ID, 0 ), false );
+        Assert.assertTrue( it.next() );
+        csn = it.getOperation().getCSN();
+        it.close();
+
+        Assert.assertEquals( 0, store.removeLogs( csn, false ) );
+        Assert.assertEquals( 1, store.removeLogs( csn, true ) );
+        Assert.assertEquals( 0, store.getLogSize( OTHER_REPLICA_ID ) );
+
+        Assert.assertEquals( 0, store.getLogSize() );
+    }
+
+
+    private void subTestVectors() throws Exception
+    {
+        CSN csnA = new DefaultCSN( 0, REPLICA_ID, 0 );
+        CSN csnB = new DefaultCSN( 1, REPLICA_ID, 0 );
+        CSN csnC = new DefaultCSN( 0, OTHER_REPLICA_ID_2, 0 );
+        CSN csnD = new DefaultCSN( 0, OTHER_REPLICA_ID_2, 1 );
+        store.putLog( new Operation( csnA ) );
+        store.putLog( new Operation( csnB ) );
+        store.putLog( new Operation( csnC ) );
+        store.putLog( new Operation( csnD ) );
+
+        Set<ReplicaId> expectedKnownReplicaIds = new HashSet<ReplicaId>();
+        expectedKnownReplicaIds.add( REPLICA_ID );
+        expectedKnownReplicaIds.add( OTHER_REPLICA_ID );
+        expectedKnownReplicaIds.add( OTHER_REPLICA_ID_2 );
+
+        Assert.assertEquals( expectedKnownReplicaIds, store.getKnownReplicaIds() );
+
+        CSNVector expectedUpdateVector = new CSNVector();
+        expectedUpdateVector.setCSN( csnB );
+        expectedUpdateVector.setCSN( csnD );
+
+        Assert.assertEquals( expectedUpdateVector, store.getUpdateVector() );
+
+        CSNVector expectedPurgeVector = new CSNVector();
+        expectedPurgeVector.setCSN( csnA );
+        expectedPurgeVector.setCSN( csnC );
+
+        Assert.assertEquals( expectedPurgeVector, store.getPurgeVector() );
+    }
+
+
+    private void testGetLogs( CSN csn, Operation operation )
+    {
+        List<Operation> operations = new ArrayList<Operation>();
+        operations.add( operation );
+        testGetLogs( csn, operations );
+    }
+
+
+    private void testGetLogs( CSN csn, List<Operation> operations )
+    {
+        Iterator<Operation> it = operations.iterator();
+        ReplicationLogIterator rit = store.getLogs( csn, true );
+        testGetLogs( it, rit );
+
+        rit = store.getLogs( csn, false );
+        Assert.assertFalse( rit.next() );
+        rit.close();
+    }
+
+
+    private void testGetLogs( CSNVector updateVector, boolean inclusive, List<Operation> operations )
+    {
+        Iterator<Operation> it = operations.iterator();
+        ReplicationLogIterator rit = store.getLogs( updateVector, inclusive );
+        testGetLogs( it, rit );
+    }
+
+
+    private void testGetLogs( Iterator<Operation> expectedIt, ReplicationLogIterator actualIt )
+    {
+        while ( expectedIt.hasNext() )
+        {
+            Operation expected = expectedIt.next();
+            Assert.assertTrue( actualIt.next() );
+
+            Operation actual = actualIt.getOperation();
+            Assert.assertEquals( expected.getCSN(), actual.getCSN() );
+            assertEquals( expected, actual );
+        }
+        Assert.assertFalse( actualIt.next() );
+        actualIt.close();
+    }
+
+
+    private void initStopWatch()
+    {
+        startTime = System.currentTimeMillis();
+    }
+
+
+    private void printElapsedTime( String testName )
+    {
+        long endTime = System.currentTimeMillis();
+        System.out.println( "Subtest #" + ( ++testCount ) + " [" + testName + "]: " + ( endTime - startTime ) + " ms" );
+        startTime = System.currentTimeMillis();
+    }
+
+
+    private static void assertEquals( Operation expected, Operation actual )
+    {
+        Assert.assertEquals( expected.toString(), actual.toString() );
+    }
+}
\ No newline at end of file
diff --git a/old_trunk/mitosis/src/test/resources/log4j.properties b/old_trunk/mitosis/src/test/resources/log4j.properties
new file mode 100644
index 0000000..492ae34
--- /dev/null
+++ b/old_trunk/mitosis/src/test/resources/log4j.properties
@@ -0,0 +1,23 @@
+#############################################################################
+#    Licensed to the Apache Software Foundation (ASF) under one or more
+#    contributor license agreements.  See the NOTICE file distributed with
+#    this work for additional information regarding copyright ownership.
+#    The ASF licenses this file to You 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.
+#############################################################################
+log4j.rootLogger=OFF, stdout
+log4j.logger.org.apache.directory.mitosis=OFF
+log4j.logger.org.apache.directory.mitosis.service.ClientConnectionManager=OFF
+
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=[%d{HH:mm:ss}] %5p [%-26.26c{1}] %m%n
diff --git a/old_trunk/pom.xml b/old_trunk/pom.xml
new file mode 100644
index 0000000..e0530fb
--- /dev/null
+++ b/old_trunk/pom.xml
@@ -0,0 +1,546 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you 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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.directory.project</groupId>
+    <artifactId>project</artifactId>
+    <version>10</version>
+  </parent>
+
+  <groupId>org.apache.directory.server</groupId>
+  <artifactId>apacheds-parent</artifactId>
+  <version>1.5.4-SNAPSHOT</version>
+  <name>ApacheDS</name>
+  <packaging>pom</packaging>
+
+  <properties>
+    <projectName>Apache Directory Server</projectName>
+    <!--<siteId>apacheds</siteId>-->
+  </properties>
+
+  <url>http://directory.apache.org/apacheds/1.5</url>
+
+  <dependencyManagement>
+    <dependencies>
+
+      <dependency>
+        <groupId>org.apache.directory.shared</groupId>
+        <artifactId>shared-asn1-codec</artifactId>
+        <version>0.9.11</version>
+      </dependency>
+
+      <dependency>
+        <groupId>org.apache.directory.shared</groupId>
+        <artifactId>shared-ldap</artifactId>
+        <version>0.9.11</version>
+      </dependency>
+
+      <dependency>
+        <groupId>org.apache.directory.shared</groupId>
+        <artifactId>shared-ldap-constants</artifactId>
+        <version>0.9.11</version>
+      </dependency>
+
+      <dependency>
+        <groupId>org.apache.directory.shared</groupId>
+        <artifactId>shared-bouncycastle-reduced</artifactId>
+        <version>0.9.11</version>
+      </dependency>
+
+      <dependency>
+        <groupId>org.apache.directory.daemon</groupId>
+        <artifactId>daemon-bootstrappers</artifactId>
+        <version>1.1.4</version>
+      </dependency>
+
+      <dependency>
+        <groupId>org.apache.mina</groupId>
+        <artifactId>mina-core</artifactId>
+        <version>1.1.6</version>
+      </dependency>
+
+      <dependency>
+        <groupId>org.apache.mina</groupId>
+        <artifactId>mina-filter-ssl</artifactId>
+        <version>1.1.6</version>
+      </dependency>
+
+      <dependency>
+        <groupId>org.apache.commons</groupId>
+        <artifactId>commons-io</artifactId>
+        <version>1.3.2</version>
+      </dependency>
+
+      <dependency>
+        <groupId>commons-collections</groupId>
+        <artifactId>commons-collections</artifactId>
+        <version>3.2</version>
+      </dependency>
+
+      <dependency>
+        <groupId>commons-daemon</groupId>
+        <artifactId>commons-daemon</artifactId>
+        <version>1.0.1</version>
+      </dependency>
+
+      <dependency>
+        <groupId>commons-lang</groupId>
+        <artifactId>commons-lang</artifactId>
+        <version>2.3</version>
+      </dependency>
+
+      <dependency>
+        <groupId>commons-cli</groupId>
+        <artifactId>commons-cli</artifactId>
+        <version>1.1</version>
+      </dependency>
+
+      <dependency>
+        <groupId>commons-dbcp</groupId>
+        <artifactId>commons-dbcp</artifactId>
+        <version>1.2.2</version>
+      </dependency>
+
+      <dependency>
+        <groupId>commons-net</groupId>
+        <artifactId>commons-net</artifactId>
+        <version>1.4.1</version>
+      </dependency>
+
+      <dependency>
+        <groupId>commons-pool</groupId>
+        <artifactId>commons-pool</artifactId>
+        <version>1.3</version>
+      </dependency>
+
+      <dependency>
+        <groupId>org.slf4j</groupId>
+        <artifactId>slf4j-api</artifactId>
+        <version>1.4.3</version>
+      </dependency>
+
+      <dependency>
+        <groupId>org.slf4j</groupId>
+        <artifactId>jcl104-over-slf4j</artifactId>
+        <version>1.4.3</version>
+      </dependency>
+
+      <dependency>
+        <groupId>org.slf4j</groupId>
+        <artifactId>slf4j-log4j12</artifactId>
+        <version>1.4.3</version>
+      </dependency>
+
+      <dependency>
+        <groupId>log4j</groupId>
+        <artifactId>log4j</artifactId>
+        <version>1.2.14</version>
+      </dependency>
+
+      <!--dependency>
+        <groupId>jdbm</groupId>
+        <artifactId>jdbm</artifactId>
+        <version>1.0</version>
+      </dependency-->
+
+      <dependency>
+        <groupId>org.apache.velocity</groupId>
+        <artifactId>velocity</artifactId>
+        <version>1.5</version>
+      </dependency>
+
+      <dependency>
+        <groupId>antlr</groupId>
+        <artifactId>antlr</artifactId>
+        <version>2.7.7</version>
+      </dependency>
+
+      <dependency>
+        <groupId>junit</groupId>
+        <artifactId>junit</artifactId>
+        <version>4.4</version>
+      </dependency>
+
+      <dependency>
+        <groupId>opensymphony</groupId>
+        <artifactId>quartz</artifactId>
+        <version>1.6.0</version>
+      </dependency>
+
+      <dependency>
+        <groupId>jug</groupId>
+        <artifactId>jug-asl</artifactId>
+        <version>2.0.0</version>
+      </dependency>
+
+      <dependency>
+        <groupId>org.apache.derby</groupId>
+        <artifactId>derby</artifactId>
+        <version>10.3.1.4</version>
+      </dependency>
+
+      <dependency>
+        <groupId>org.apache.xbean</groupId>
+        <artifactId>xbean-spring</artifactId>
+        <version>3.3</version>
+      </dependency>
+
+      <dependency>
+        <groupId>org.springframework</groupId>
+        <artifactId>spring-core</artifactId>
+        <version>2.0.6</version>
+      </dependency>
+
+      <dependency>
+        <groupId>org.springframework</groupId>
+        <artifactId>spring-beans</artifactId>
+        <version>2.0.6</version>
+      </dependency>
+
+      <dependency>
+        <groupId>org.springframework</groupId>
+        <artifactId>spring-context</artifactId>
+        <version>2.0.6</version>
+      </dependency>
+
+      <dependency>
+        <groupId>org.apache.maven</groupId>
+        <artifactId>maven-plugin-api</artifactId>
+        <version>2.0.7</version>
+      </dependency>
+
+      <dependency>
+        <groupId>org.apache.maven</groupId>
+        <artifactId>maven-project</artifactId>
+        <version>2.0.7</version>
+      </dependency>
+
+      <dependency>
+        <groupId>org.apache.maven</groupId>
+        <artifactId>maven-archiver</artifactId>
+        <version>2.2</version>
+      </dependency>
+
+      <dependency>
+        <groupId>org.codehaus.plexus</groupId>
+        <artifactId>plexus-utils</artifactId>
+        <version>1.4.5</version>
+      </dependency>
+
+      <dependency>
+        <groupId>org.apache.maven</groupId>
+        <artifactId>maven-artifact</artifactId>
+        <version>2.0.7</version>
+      </dependency>
+
+      <dependency>
+        <groupId>ldapsdk</groupId>
+        <artifactId>ldapsdk</artifactId>
+        <version>4.1</version>
+      </dependency>
+
+      <dependency>
+        <groupId>dnsjava</groupId>
+        <artifactId>dnsjava</artifactId>
+        <version>2.0.1</version>
+      </dependency>
+
+      <dependency>
+        <groupId>tanukisoft</groupId>
+        <artifactId>wrapper</artifactId>
+        <version>3.2.3</version>
+      </dependency>
+
+      <dependency>
+        <groupId>bouncycastle</groupId>
+        <artifactId>bcprov-jdk15</artifactId>
+        <version>136</version>
+      </dependency>
+    </dependencies>
+  </dependencyManagement>
+
+  <issueManagement>
+    <system>JIRA</system>
+    <url>http://issues.apache.org/jira/browse/DIRSERVER</url>
+  </issueManagement>
+
+  <distributionManagement>
+    <site>
+      <id>apache.directory.apacheds</id>
+      <url>scpexe://vm094.oxylos.org/var/www/html/projects/apacheds/</url>
+    </site>
+  </distributionManagement>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.directory.shared</groupId>
+      <artifactId>shared-ldap</artifactId>
+    </dependency>
+
+    <!-- apacheds standard logging interface -->
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+    </dependency>
+
+    <!-- not sure what this is used for -->
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>jcl104-over-slf4j</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+    <!-- standard logging implementation for tests -->
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-log4j12</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>log4j</groupId>
+      <artifactId>log4j</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <modules>
+    <module>apacheds-jdbm</module>
+    <module>bootstrap-extract</module>
+    <module>bootstrap-partition</module>
+    <module>bootstrap-plugin</module>
+    <module>schema-extras</module>
+    <module>schema-bootstrap</module>
+    <module>utils</module>
+    <module>schema-registries</module>
+    <module>jdbm-store</module>
+    <module>btree-base</module>
+    <module>core</module>
+    <!--module>core-jndi</module-->
+    <module>core-constants</module>
+    <module>core-shared</module>
+    <module>core-plugin</module>
+    <module>core-unit</module>
+    <module>core-integ</module>
+    <module>core-entry</module>
+    <module>protocol-shared</module>
+    <module>protocol-ntp</module>
+    <module>protocol-ldap</module>
+    <module>protocol-kerberos</module>
+    <module>protocol-dhcp</module>
+    <module>protocol-dns</module>
+    <module>protocol-changepw</module>
+    <module>server-tools</module>
+
+    <!-- breaks the build on first try
+      <module>sar-plugin</module>
+      <module>server-sar</module>
+    -->
+
+    <module>server-xml</module>
+    <module>server-unit</module>
+    <module>server-jndi</module>
+    <module>kerberos-shared</module>
+    <!--module>kerberos-unit</module-->
+    <module>interceptor-kerberos</module>
+    <module>mitosis</module>
+    <module>server-replication</module>
+    <module>xbean-spring</module>
+  </modules>
+
+  <build>
+    <pluginManagement>
+      <plugins>
+        <plugin>
+          <groupId>org.codehaus.mojo</groupId>
+          <artifactId>dependency-maven-plugin</artifactId>
+          <version>1.0</version>
+        </plugin>
+
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-antlr-plugin</artifactId>
+          <version>2.0-beta-1</version>
+          <executions>
+            <execution>
+              <goals>
+                <goal>generate</goal>
+              </goals>
+            </execution>
+          </executions>
+        </plugin>
+
+
+        <plugin>
+          <groupId>org.apache.xbean</groupId>
+          <artifactId>maven-xbean-plugin</artifactId>
+          <version>3.2</version>
+        </plugin>
+
+        <!--  lets ensure that the XSD gets deployed  -->
+        <plugin>
+          <groupId>org.codehaus.mojo</groupId>
+          <artifactId>build-helper-maven-plugin</artifactId>
+          <version>1.0</version>
+        </plugin>
+
+      </plugins>
+    </pluginManagement>
+
+  </build>
+
+  <reporting>
+    <excludeDefaults>true</excludeDefaults>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-report-plugin</artifactId>
+        <configuration>
+          <aggregate>true</aggregate>
+        </configuration>
+      </plugin>
+      <plugin>
+        <artifactId>maven-jxr-plugin</artifactId>
+        <configuration>
+          <aggregate>true</aggregate>
+        </configuration>
+      </plugin>
+      <plugin>
+        <artifactId>maven-pmd-plugin</artifactId>
+        <configuration>
+          <linkXref>true</linkXref>
+          <sourceEncoding>utf-8</sourceEncoding>
+          <minimumTokens>100</minimumTokens>
+          <targetJdk>1.5</targetJdk>
+          <aggregate>true</aggregate>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>taglist-maven-plugin</artifactId>
+        <configuration>
+          <tags>
+            <tag>TODO</tag>
+            <tag>@todo</tag>
+            <tag>@deprecated</tag>
+            <tag>FIXME</tag>
+          </tags>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-javadoc-plugin</artifactId>
+        <configuration>
+          <aggregate>true</aggregate>
+          <tags>
+            <tag>
+              <name>org.apache.xbean.XBean</name>
+              <placement>a</placement>
+              <head>XBean</head>
+            </tag>
+            <tag>
+              <name>org.apache.xbean.Property</name>
+              <placement>a</placement>
+              <head>XBean Property</head>
+            </tag>
+            <tag>
+              <name>org.apache.xbean.DestroyMethod</name>
+              <placement>a</placement>
+              <head>XBean DestroyMethod</head>
+            </tag>
+            <tag>
+              <name>note</name>
+              <placement>a</placement>
+              <head>NOTE</head>
+            </tag>
+            <tag>
+              <name>todo</name>
+              <placement>a</placement>
+              <head>TODO</head>
+            </tag>
+            <tag>
+              <name>warning</name>
+              <placement>a</placement>
+              <head>WARNING</head>
+            </tag>
+          </tags>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>findbugs-maven-plugin</artifactId>
+        <version>1.1.1</version>
+        <configuration>
+          <xmlOutput>false</xmlOutput>
+          <!--
+            <xmlOutput>true|false</xmlOutput>
+            <xmlOutputDirectory>directory location of xml findbugs report</xmlOutputDirectory>
+            <threshold>High|Normal|Low|Exp|Ignore</threshold>
+            <effort>Min|Default|Max</effort>
+            <excludeFilterFile>findbugs-exclude.xml</excludeFilterFile>
+            <includeFilterFile>findbugs-include.xml</includeFilterFile>
+            <visitors>FindDeadLocalStores,UnreadFields</visitors>
+            <omitVisitors>FindDeadLocalStores,UnreadFields</omitVisitors>
+            <onlyAnalyze>org.codehaus.mojo.findbugs.*</onlyAnalyze>
+            <pluginList>/libs/fb-contrib/fb-contrib-2.8.0.jar</pluginList>
+            <debug>true|false</debug>
+            <relaxed>true|false</relaxed>
+          -->
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>cobertura-maven-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+      </plugin>
+    </plugins>
+  </reporting>
+
+  <scm>
+    <connection>scm:svn:http://svn.apache.org/repos/asf/directory/apacheds/trunk</connection>
+    <developerConnection>scm:svn:https://svn.apache.org/repos/asf/directory/apacheds/trunk</developerConnection>
+    <url>http://svn.apache.org/viewvc/directory/apacheds/trunk</url>
+  </scm>
+
+  <repositories>
+    <repository>
+      <id>apache.directory.snapshot.repo</id>
+      <name>Snapshot repository for the Apache Directory project</name>
+      <url>http://vm094.oxylos.org/mirror-maven2/</url>
+      <releases>
+        <enabled>false</enabled>
+      </releases>
+    </repository>
+  </repositories>
+  
+</project>
diff --git a/old_trunk/protocol-changepw/pom.xml b/old_trunk/protocol-changepw/pom.xml
new file mode 100644
index 0000000..2be8651
--- /dev/null
+++ b/old_trunk/protocol-changepw/pom.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you 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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.directory.server</groupId>
+    <artifactId>apacheds-parent</artifactId>
+    <version>1.5.4-SNAPSHOT</version>
+  </parent>
+  <artifactId>apacheds-protocol-changepw</artifactId>
+  <name>ApacheDS Protocol Change Password</name>
+
+  <description>
+    The Change Password protocol provider for ApacheDS
+  </description>
+
+  <packaging>jar</packaging>  
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-kerberos-shared</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-source-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>attach-sources</id>
+            <phase>verify</phase>
+            <goals>
+              <goal>jar</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+
+</project>
+
diff --git a/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/ChangePasswordServer.java b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/ChangePasswordServer.java
new file mode 100644
index 0000000..33f3482
--- /dev/null
+++ b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/ChangePasswordServer.java
@@ -0,0 +1,348 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.changepw;
+
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.apache.directory.server.changepw.protocol.ChangePasswordProtocolHandler;
+import org.apache.directory.server.constants.ServerDNConstants;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
+import org.apache.directory.server.kerberos.shared.store.JndiPrincipalStoreImpl;
+import org.apache.directory.server.kerberos.shared.store.PrincipalStore;
+import org.apache.directory.server.protocol.shared.DirectoryBackedService;
+import org.apache.mina.transport.socket.nio.DatagramAcceptorConfig;
+import org.apache.mina.transport.socket.nio.SocketAcceptorConfig;
+
+
+/**
+ * Contains the configuration parameters for the Change Password protocol provider.
+ *
+ * @org.apache.xbean.XBean
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ChangePasswordServer extends DirectoryBackedService
+{
+    private static final long serialVersionUID = 3509208713288140629L;
+
+    /** The default change password principal name. */
+    private static final String SERVICE_PRINCIPAL_DEFAULT = "kadmin/changepw@EXAMPLE.COM";
+
+    /** The default change password realm. */
+    private static final String REALM_DEFAULT = "EXAMPLE.COM";
+
+    /** The default change password port. */
+    private static final int IP_PORT_DEFAULT = 464;
+
+    /** The default encryption types. */
+    public static final String[] ENCRYPTION_TYPES_DEFAULT = new String[]
+        { "des-cbc-md5" };
+
+    /** The default changepw buffer size. */
+    private static final long DEFAULT_ALLOWABLE_CLOCKSKEW = 5 * 60000;
+
+    /** The default empty addresses. */
+    private static final boolean DEFAULT_EMPTY_ADDRESSES_ALLOWED = true;
+
+    /** The default change password password policy for password length. */
+    public static final int DEFAULT_PASSWORD_LENGTH = 6;
+
+    /** The default change password password policy for category count. */
+    public static final int DEFAULT_CATEGORY_COUNT = 3;
+
+    /** The default change password password policy for token size. */
+    public static final int DEFAULT_TOKEN_SIZE = 3;
+
+    /** The default service PID. */
+    private static final String SERVICE_PID_DEFAULT = "org.apache.directory.server.changepw";
+
+    /** The default service name. */
+    private static final String SERVICE_NAME_DEFAULT = "ApacheDS Change Password Service";
+
+    /** The encryption types. */
+    private EncryptionType[] encryptionTypes;
+
+    /** The primary realm. */
+    private String primaryRealm = REALM_DEFAULT;
+
+    /** The service principal name. */
+    private String servicePrincipal = SERVICE_PRINCIPAL_DEFAULT;
+
+    /** The allowable clock skew. */
+    private long allowableClockSkew = DEFAULT_ALLOWABLE_CLOCKSKEW;
+
+    /** Whether empty addresses are allowed. */
+    private boolean isEmptyAddressesAllowed = DEFAULT_EMPTY_ADDRESSES_ALLOWED;
+
+    /** The policy for password length. */
+    private int policyPasswordLength;
+
+    /** The policy for category count. */
+    private int policyCategoryCount;
+
+    /** The policy for token size. */
+    private int policyTokenSize;
+
+
+    /**
+     * Creates a new instance of ChangePasswordConfiguration.
+     */
+    public ChangePasswordServer()
+    {
+        super.setServiceName( SERVICE_NAME_DEFAULT );
+        super.setIpPort( IP_PORT_DEFAULT );
+        super.setServiceId( SERVICE_PID_DEFAULT );
+        super.setSearchBaseDn( ServerDNConstants.USER_EXAMPLE_COM_DN );
+
+        prepareEncryptionTypes();
+    }
+
+
+    /**
+     * Returns the primary realm.
+     *
+     * @return The primary realm.
+     */
+    public String getPrimaryRealm()
+    {
+        return primaryRealm;
+    }
+
+
+    /**
+     * @param primaryRealm The primaryRealm to set.
+     */
+    public void setPrimaryRealm( String primaryRealm )
+    {
+        this.primaryRealm = primaryRealm;
+    }
+
+
+    /**
+     * Returns the encryption types.
+     *
+     * @return The encryption types.
+     */
+    public EncryptionType[] getEncryptionTypes()
+    {
+        return encryptionTypes;
+    }
+
+
+    /**
+     * @param encryptionTypes The encryptionTypes to set.
+     */
+    public void setEncryptionTypes( EncryptionType[] encryptionTypes )
+    {
+        this.encryptionTypes = encryptionTypes;
+    }
+
+
+    /**
+     * Returns the allowable clock skew.
+     *
+     * @return The allowable clock skew.
+     */
+    public long getAllowableClockSkew()
+    {
+        return allowableClockSkew;
+    }
+
+
+    /**
+     * @param allowableClockSkew The allowableClockSkew to set.
+     */
+    public void setAllowableClockSkew( long allowableClockSkew )
+    {
+        this.allowableClockSkew = allowableClockSkew;
+    }
+
+
+    /**
+     * Returns the Change Password service principal.
+     *
+     * @return The Change Password service principal.
+     */
+    public KerberosPrincipal getServicePrincipal()
+    {
+        return new KerberosPrincipal( servicePrincipal );
+    }
+
+
+    /**
+     * @param servicePrincipal The Change Password service principal to set.
+     */
+    public void setServicePrincipal( String servicePrincipal )
+    {
+        this.servicePrincipal = servicePrincipal;
+    }
+
+
+    /**
+     * Returns whether empty addresses are allowed.
+     *
+     * @return Whether empty addresses are allowed.
+     */
+    public boolean isEmptyAddressesAllowed()
+    {
+        return isEmptyAddressesAllowed;
+    }
+
+
+    /**
+     * @param isEmptyAddressesAllowed The isEmptyAddressesAllowed to set.
+     */
+    public void setEmptyAddressesAllowed( boolean isEmptyAddressesAllowed )
+    {
+        this.isEmptyAddressesAllowed = isEmptyAddressesAllowed;
+    }
+
+
+    /**
+     * Returns the password length.
+     *
+     * @return The password length.
+     */
+    public int getPasswordLengthPolicy()
+    {
+        return policyPasswordLength;
+    }
+
+
+    /**
+     * Returns the category count.
+     *
+     * @return The category count.
+     */
+    public int getCategoryCountPolicy()
+    {
+        return policyCategoryCount;
+    }
+
+
+    /**
+     * Returns the token size.
+     *
+     * @return The token size.
+     */
+    public int getTokenSizePolicy()
+    {
+        return policyTokenSize;
+    }
+
+
+    /**
+     * @throws IOException if we cannot bind to the specified ports
+     */
+    public void start() throws IOException
+    {
+        PrincipalStore store = new JndiPrincipalStoreImpl( getSearchBaseDn(),
+                getSearchBaseDn(), getDirectoryService() );
+
+        if ( getDatagramAcceptor() != null )
+        {
+            DatagramAcceptorConfig udpConfig = new DatagramAcceptorConfig();
+            getDatagramAcceptor().bind( new InetSocketAddress( getIpPort() ),
+                    new ChangePasswordProtocolHandler( this, store ), udpConfig );
+        }
+
+        if ( getSocketAcceptor() != null )
+        {
+            SocketAcceptorConfig tcpConfig = new SocketAcceptorConfig();
+            tcpConfig.setDisconnectOnUnbind( false );
+            tcpConfig.setReuseAddress( true );
+            getSocketAcceptor().bind( new InetSocketAddress( getIpPort() ),
+                    new ChangePasswordProtocolHandler( this, store ), tcpConfig );
+        }
+    }
+
+
+    public void stop()
+    {
+        if ( getDatagramAcceptor() != null )
+        {
+            getDatagramAcceptor().unbind( new InetSocketAddress( getIpPort() ));
+        }
+        if ( getSocketAcceptor() != null )
+        {
+            getSocketAcceptor().unbind( new InetSocketAddress( getIpPort() ));
+        }
+    }
+
+
+    private void prepareEncryptionTypes()
+    {
+        String[] encryptionTypeStrings = ENCRYPTION_TYPES_DEFAULT;
+        List<EncryptionType> encTypes = new ArrayList<EncryptionType>();
+
+        for ( String enc : encryptionTypeStrings )
+        {
+            for ( EncryptionType type : EncryptionType.getEncryptionTypes() )
+            {
+                if ( type.toString().equalsIgnoreCase( enc ) )
+                {
+                    encTypes.add( type );
+                }
+            }
+        }
+
+        encryptionTypes = encTypes.toArray( new EncryptionType[encTypes.size()] );
+    }
+
+
+    /**
+     * Sets the policy's minimum?? password length.
+     *
+     * @param policyPasswordLength the minimum password length requirement
+     */
+    public void setPolicyPasswordLength( int policyPasswordLength )
+    {
+        this.policyPasswordLength = policyPasswordLength;
+    }
+
+
+    /**
+     * Sets the policy category count - what's this?
+     *
+     * @param policyCategoryCount the policy category count
+     */
+    public void setPolicyCategoryCount( int policyCategoryCount )
+    {
+        this.policyCategoryCount = policyCategoryCount;
+    }
+
+
+    /**
+     * Sets the policy token size - what's this?
+     *
+     * @param policyTokenSize the policy token size
+     */
+    public void setPolicyTokenSize( int policyTokenSize )
+    {
+        this.policyTokenSize = policyTokenSize;
+    }
+}
diff --git a/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/exceptions/ChangePasswordException.java b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/exceptions/ChangePasswordException.java
new file mode 100644
index 0000000..d2700fb
--- /dev/null
+++ b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/exceptions/ChangePasswordException.java
@@ -0,0 +1,87 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.changepw.exceptions;
+
+
+import org.apache.directory.server.kerberos.shared.exceptions.KerberosException;
+
+
+/**
+ * The root of the Change Password exception hierarchy.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ChangePasswordException extends KerberosException
+{
+    private static final long serialVersionUID = 4880242751298831543L;
+
+
+    /**
+     * Creates a ChangePasswordException with an {@link ErrorType}.
+     *
+     * @param errorType The {@link ErrorType} associated with this ChangePasswordException.
+     */
+    public ChangePasswordException( ErrorType errorType )
+    {
+        super( errorType.getOrdinal(), errorType.getMessage() );
+    }
+
+
+    /**
+     * Creates a ChangePasswordException with an {@link ErrorType} and an
+     * underlying throwable that caused this fault.
+     *
+     * @param errorType The {@link ErrorType} associated with this ChangePasswordException.
+     * @param cause The underlying failure, if any.
+     */
+    public ChangePasswordException( ErrorType errorType, Throwable cause )
+    {
+        super( errorType.getOrdinal(), errorType.getMessage(), cause );
+    }
+
+
+    /**
+     * Creates a ChangePasswordException with an {@link ErrorType} and
+     * data helping to explain what caused this fault.
+     *
+     * @param errorType The {@link ErrorType} associated with this ChangePasswordException.
+     * @param explanatoryData Data helping to explain this fault, if any.
+     */
+    public ChangePasswordException( ErrorType errorType, byte[] explanatoryData )
+    {
+        super( errorType.getOrdinal(), errorType.getMessage(), explanatoryData );
+    }
+
+
+    /**
+     * Creates a ChangePasswordException with an {@link ErrorType}, data helping to explain
+     * what caused this fault, and an underlying throwable that caused this fault.
+     *
+     * @param errorType The error type associated with this ChangePasswordException.
+     * @param explanatoryData Data helping to explain this fault, if any.
+     * @param cause The underlying failure, if any.
+     */
+    public ChangePasswordException( ErrorType errorType, byte[] explanatoryData, Throwable cause )
+    {
+        super( errorType.getOrdinal(), errorType.getMessage(), explanatoryData, cause );
+    }
+}
diff --git a/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/exceptions/ErrorType.java b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/exceptions/ErrorType.java
new file mode 100644
index 0000000..bdc51f7
--- /dev/null
+++ b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/exceptions/ErrorType.java
@@ -0,0 +1,179 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.changepw.exceptions;
+
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+
+/**
+ * Type safe enumeration of Change Password error types
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public final class ErrorType implements Comparable<ErrorType>
+{
+    /**
+     * Constant for the "Request failed due to being malformed" error type.
+     */
+    public static final ErrorType KRB5_KPASSWD_MALFORMED = new ErrorType( 1, "Request failed due to being malformed." );
+
+    /**
+     * Constant for the "Request failed due to a hard error in processing the request" error type.
+     */
+    public static final ErrorType KRB5_KPASSWD_HARDERROR = new ErrorType( 2,
+        "Request failed due to a hard error in processing the request." );
+
+    /**
+     * Constant for the "Request failed due to an error in authentication processing" error type.
+     */
+    public static final ErrorType KRB5_KPASSWD_AUTHERROR = new ErrorType( 3,
+        "Request failed due to an error in authentication processing." );
+
+    /**
+     * Constant for the "Request failed due to a soft error in processing the request" error type.
+     */
+    public static final ErrorType KRB5_KPASSWD_SOFTERROR = new ErrorType( 4,
+        "Request failed due to a soft error in processing the request." );
+
+    /**
+     * Constant for the "Requestor not authorized" error type.
+     */
+    public static final ErrorType KRB5_KPASSWD_ACCESSDENIED = new ErrorType( 5, "Requestor not authorized." );
+
+    /**
+     * Constant for the "Protocol version unsupported" error type.
+     */
+    public static final ErrorType KRB5_KPASSWD_BAD_VERSION = new ErrorType( 6, "Protocol version unsupported." );
+
+    /**
+     * Constant for the "Initial flag required" error type.
+     */
+    public static final ErrorType KRB5_KPASSWD_INITIAL_FLAG_NEEDED = new ErrorType( 7, "Initial flag required." );
+
+    /**
+     * Constant for the "Request failed for an unknown reason" error type.
+     */
+    public static final ErrorType KRB5_KPASSWD_UNKNOWN_ERROR = new ErrorType( 8,
+        "Request failed for an unknown reason." );
+
+    /**
+     * Array for building a List of VALUES.
+     */
+    private static final ErrorType[] values =
+        { KRB5_KPASSWD_MALFORMED, KRB5_KPASSWD_HARDERROR, KRB5_KPASSWD_AUTHERROR, KRB5_KPASSWD_SOFTERROR,
+            KRB5_KPASSWD_ACCESSDENIED, KRB5_KPASSWD_BAD_VERSION, KRB5_KPASSWD_INITIAL_FLAG_NEEDED,
+            KRB5_KPASSWD_UNKNOWN_ERROR };
+
+    /**
+     * A list of all the error type constants.
+     */
+    public static final List<ErrorType> VALUES = Collections.unmodifiableList( Arrays.asList( values ) );
+
+    /**
+     * The name of the error type.
+     */
+    private final String name;
+
+    /**
+     * The value/code for the error type.
+     */
+    private final int ordinal;
+
+
+    /**
+     * Private constructor prevents construction outside of this class.
+     */
+    private ErrorType( int ordinal, String name )
+    {
+        this.ordinal = ordinal;
+        this.name = name;
+    }
+
+
+    /**
+     * Returns the message for this Change Password error.
+     *
+     * @return the message for this Change Password error.
+     */
+    public String getMessage()
+    {
+        return name;
+    }
+
+
+    /**
+     * Returns the message for this Change Password error.
+     *
+     * @return the message for this Change Password error.
+     */
+    public String toString()
+    {
+        return name;
+    }
+
+
+    /**
+     * Compares this type to another object hopefully one that is of the same
+     * type.
+     *
+     * @param that the object to compare this ErrorType to
+     * @return ordinal - that.ordinal;
+     */
+    public int compareTo( ErrorType that )
+    {
+        return this.ordinal - that.ordinal;
+    }
+
+
+    /**
+     * Gets the ordinal by its ordinal value.
+     *
+     * @param ordinal the ordinal value of the ordinal
+     * @return the type corresponding to the ordinal value
+     */
+    public static ErrorType getTypeByOrdinal( int ordinal )
+    {
+        for ( int ii = 0; ii < values.length; ii++ )
+        {
+            if ( values[ii].ordinal == ordinal )
+            {
+                return values[ii];
+            }
+        }
+
+        return KRB5_KPASSWD_UNKNOWN_ERROR;
+    }
+
+
+    /**
+     * Gets the ordinal value associated with this Change Password error.
+     *
+     * @return the ordinal value associated with this Change Password error
+     */
+    public int getOrdinal()
+    {
+        return ordinal;
+    }
+}
diff --git a/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/exceptions/package-info.java b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/exceptions/package-info.java
new file mode 100644
index 0000000..f248d36
--- /dev/null
+++ b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/exceptions/package-info.java
@@ -0,0 +1,29 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+/**
+ * Provides the root of the exception hierarchy, as well as the
+ * enumerator for mapping Change Password errors to error codes.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+
+package org.apache.directory.server.changepw.exceptions;
diff --git a/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/io/ChangePasswordDataDecoder.java b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/io/ChangePasswordDataDecoder.java
new file mode 100644
index 0000000..0c91819
--- /dev/null
+++ b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/io/ChangePasswordDataDecoder.java
@@ -0,0 +1,91 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.changepw.io;
+
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+import org.apache.directory.server.changepw.value.ChangePasswordData;
+import org.apache.directory.server.changepw.value.ChangePasswordDataModifier;
+import org.apache.directory.server.kerberos.shared.io.decoder.PrincipalNameDecoder;
+import org.apache.directory.shared.asn1.der.ASN1InputStream;
+import org.apache.directory.shared.asn1.der.DEREncodable;
+import org.apache.directory.shared.asn1.der.DERGeneralString;
+import org.apache.directory.shared.asn1.der.DEROctetString;
+import org.apache.directory.shared.asn1.der.DERSequence;
+import org.apache.directory.shared.asn1.der.DERTaggedObject;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ChangePasswordDataDecoder
+{
+    /**
+     * Decodes bytes into a ChangePasswordData.
+     *
+     * @param encodedChangePasswdData
+     * @return The {@link ChangePasswordData}.
+     * @throws IOException
+     */
+    public ChangePasswordData decodeChangePasswordData( byte[] encodedChangePasswdData ) throws IOException
+    {
+        ASN1InputStream ais = new ASN1InputStream( encodedChangePasswdData );
+
+        DERSequence sequence = ( DERSequence ) ais.readObject();
+
+        return decodeChangePasswdData( sequence );
+    }
+
+
+    protected ChangePasswordData decodeChangePasswdData( DERSequence sequence )
+    {
+        ChangePasswordDataModifier modifier = new ChangePasswordDataModifier();
+
+        for ( Enumeration e = sequence.getObjects(); e.hasMoreElements(); )
+        {
+            DERTaggedObject object = ( ( DERTaggedObject ) e.nextElement() );
+            int tag = object.getTagNo();
+            DEREncodable derObject = object.getObject();
+            switch ( tag )
+            {
+                case 0:
+                    DEROctetString tag0 = ( DEROctetString ) derObject;
+                    modifier.setNewPassword( tag0.getOctets() );
+                    break;
+                case 1:
+                    DERSequence tag1 = ( DERSequence ) derObject;
+                    modifier.setTargetName( PrincipalNameDecoder.decode( tag1 ) );
+                    break;
+                case 2:
+                    DERGeneralString tag2 = ( DERGeneralString ) derObject;
+                    modifier.setTargetRealm( tag2.getString() );
+                    break;
+                default:
+                    break;
+            }
+        }
+
+        return modifier.getChangePasswdData();
+    }
+}
diff --git a/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/io/ChangePasswordDataEncoder.java b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/io/ChangePasswordDataEncoder.java
new file mode 100644
index 0000000..a35e403
--- /dev/null
+++ b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/io/ChangePasswordDataEncoder.java
@@ -0,0 +1,100 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.changepw.io;
+
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+import org.apache.directory.server.changepw.value.ChangePasswordData;
+import org.apache.directory.server.kerberos.shared.io.encoder.PrincipalNameEncoder;
+import org.apache.directory.shared.asn1.der.ASN1OutputStream;
+import org.apache.directory.shared.asn1.der.DERGeneralString;
+import org.apache.directory.shared.asn1.der.DEROctetString;
+import org.apache.directory.shared.asn1.der.DERSequence;
+import org.apache.directory.shared.asn1.der.DERTaggedObject;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ChangePasswordDataEncoder
+{
+    /**
+     * Encodes a {@link ChangePasswordData} into a byte array.
+     *
+     * @param data
+     * @return The byte array.
+     * @throws IOException
+     */
+    public byte[] encode( ChangePasswordData data ) throws IOException
+    {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        ASN1OutputStream aos = new ASN1OutputStream( baos );
+
+        DERSequence dataSequence = encodeDataSequence( data );
+        aos.writeObject( dataSequence );
+
+        aos.close();
+
+        return baos.toByteArray();
+    }
+
+
+    /**
+     * Encodes a {@link ChangePasswordData} into a {@link ByteBuffer}.
+     *
+     * @param data
+     * @param out
+     * @throws IOException
+     */
+    public void encode( ChangePasswordData data, ByteBuffer out ) throws IOException
+    {
+        ASN1OutputStream aos = new ASN1OutputStream( out );
+
+        DERSequence sequence = encodeDataSequence( data );
+        aos.writeObject( sequence );
+
+        aos.close();
+    }
+
+
+    private DERSequence encodeDataSequence( ChangePasswordData data )
+    {
+        DERSequence sequence = new DERSequence();
+        sequence.add( new DERTaggedObject( 0, new DEROctetString( data.getPassword() ) ) );
+
+        // OPTIONAL
+        if ( data.getPrincipalName() != null )
+        {
+            sequence.add( new DERTaggedObject( 1, PrincipalNameEncoder.encode( data.getPrincipalName() ) ) );
+        }
+
+        // OPTIONAL
+        if ( data.getRealm() != null )
+        {
+            sequence.add( new DERTaggedObject( 2, DERGeneralString.valueOf( data.getRealm() ) ) );
+        }
+
+        return sequence;
+    }
+}
diff --git a/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/io/ChangePasswordErrorDecoder.java b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/io/ChangePasswordErrorDecoder.java
new file mode 100644
index 0000000..8122112
--- /dev/null
+++ b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/io/ChangePasswordErrorDecoder.java
@@ -0,0 +1,73 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.changepw.io;
+
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+import org.apache.directory.server.changepw.messages.ChangePasswordError;
+import org.apache.directory.server.changepw.messages.ChangePasswordErrorModifier;
+import org.apache.directory.server.kerberos.shared.io.decoder.ErrorMessageDecoder;
+import org.apache.directory.server.kerberos.shared.messages.ErrorMessage;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ChangePasswordErrorDecoder
+{
+    private static final int HEADER_LENGTH = 6;
+
+
+    /**
+     * Decodes a {@link ByteBuffer} into a {@link ChangePasswordError}.
+     *
+     * @param buf
+     * @return The {@link ChangePasswordError}.
+     * @throws IOException
+     */
+    public ChangePasswordError decode( ByteBuffer buf ) throws IOException
+    {
+        ChangePasswordErrorModifier modifier = new ChangePasswordErrorModifier();
+
+        short messageLength = buf.getShort();
+
+        modifier.setProtocolVersionNumber( buf.getShort() );
+
+        // AP_REQ length will be 0 for error messages
+        buf.getShort(); // authHeader length
+
+        int errorLength = messageLength - HEADER_LENGTH;
+
+        byte[] errorBytes = new byte[errorLength];
+
+        buf.get( errorBytes );
+        ByteBuffer errorBuffer = ByteBuffer.wrap( errorBytes );
+
+        ErrorMessageDecoder errorDecoder = new ErrorMessageDecoder();
+        ErrorMessage errorMessage = errorDecoder.decode( errorBuffer );
+
+        modifier.setErrorMessage( errorMessage );
+
+        return modifier.getChangePasswordError();
+    }
+}
diff --git a/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/io/ChangePasswordErrorEncoder.java b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/io/ChangePasswordErrorEncoder.java
new file mode 100644
index 0000000..f94406f
--- /dev/null
+++ b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/io/ChangePasswordErrorEncoder.java
@@ -0,0 +1,66 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.changepw.io;
+
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+import org.apache.directory.server.changepw.messages.ChangePasswordError;
+import org.apache.directory.server.kerberos.shared.io.encoder.ErrorMessageEncoder;
+import org.apache.directory.server.kerberos.shared.messages.ErrorMessage;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ChangePasswordErrorEncoder
+{
+    private static final int HEADER_LENGTH = 6;
+
+
+    /**
+     * Encodes a {@link ChangePasswordError} into a {@link ByteBuffer}.
+     *
+     * @param buf
+     * @param message
+     * @throws IOException
+     */
+    public void encode( ByteBuffer buf, ChangePasswordError message ) throws IOException
+    {
+        // Build error message bytes
+        ErrorMessage errorMessage = message.getErrorMessage();
+        ErrorMessageEncoder errorEncoder = new ErrorMessageEncoder();
+        byte[] errorBytes = errorEncoder.encode( errorMessage );
+
+        short messageLength = ( short ) ( HEADER_LENGTH + errorBytes.length );
+        buf.putShort( messageLength );
+
+        short protocolVersion = 1;
+        buf.putShort( protocolVersion );
+
+        short zeroIndicatesError = 0;
+        buf.putShort( zeroIndicatesError );
+
+        buf.put( errorBytes );
+    }
+}
diff --git a/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/io/ChangePasswordReplyDecoder.java b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/io/ChangePasswordReplyDecoder.java
new file mode 100644
index 0000000..5eeb50d
--- /dev/null
+++ b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/io/ChangePasswordReplyDecoder.java
@@ -0,0 +1,77 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.changepw.io;
+
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+import org.apache.directory.server.changepw.messages.ChangePasswordReply;
+import org.apache.directory.server.changepw.messages.ChangePasswordReplyModifier;
+import org.apache.directory.server.kerberos.shared.io.decoder.ApplicationReplyDecoder;
+import org.apache.directory.server.kerberos.shared.io.decoder.PrivateMessageDecoder;
+import org.apache.directory.server.kerberos.shared.messages.application.ApplicationReply;
+import org.apache.directory.server.kerberos.shared.messages.application.PrivateMessage;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ChangePasswordReplyDecoder
+{
+    private static final int HEADER_LENGTH = 6;
+
+
+    /**
+     * Decodes a {@link ByteBuffer} into a {@link ChangePasswordReply}.
+     *
+     * @param buf
+     * @return The {@link ChangePasswordReply}.
+     * @throws IOException
+     */
+    public ChangePasswordReply decode( ByteBuffer buf ) throws IOException
+    {
+        ChangePasswordReplyModifier modifier = new ChangePasswordReplyModifier();
+
+        short messageLength = buf.getShort();
+        short protocolVersion = buf.getShort();
+        short encodedAppReplyLength = buf.getShort();
+
+        modifier.setProtocolVersionNumber( protocolVersion );
+
+        byte[] encodedAppReply = new byte[encodedAppReplyLength];
+        buf.get( encodedAppReply );
+
+        ApplicationReplyDecoder appDecoder = new ApplicationReplyDecoder();
+        ApplicationReply applicationReply = appDecoder.decode( encodedAppReply );
+        modifier.setApplicationReply( applicationReply );
+
+        int privateBytesLength = messageLength - HEADER_LENGTH - encodedAppReplyLength;
+        byte[] encodedPrivateMessage = new byte[privateBytesLength];
+        buf.get( encodedPrivateMessage );
+
+        PrivateMessageDecoder privateDecoder = new PrivateMessageDecoder();
+        PrivateMessage privateMessage = privateDecoder.decode( encodedPrivateMessage );
+        modifier.setPrivateMessage( privateMessage );
+
+        return modifier.getChangePasswordReply();
+    }
+}
diff --git a/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/io/ChangePasswordReplyEncoder.java b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/io/ChangePasswordReplyEncoder.java
new file mode 100644
index 0000000..ec6e1b9
--- /dev/null
+++ b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/io/ChangePasswordReplyEncoder.java
@@ -0,0 +1,73 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.changepw.io;
+
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+import org.apache.directory.server.changepw.messages.ChangePasswordReply;
+import org.apache.directory.server.kerberos.shared.io.encoder.ApplicationReplyEncoder;
+import org.apache.directory.server.kerberos.shared.io.encoder.PrivateMessageEncoder;
+import org.apache.directory.server.kerberos.shared.messages.application.ApplicationReply;
+import org.apache.directory.server.kerberos.shared.messages.application.PrivateMessage;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ChangePasswordReplyEncoder
+{
+    private static final int HEADER_LENGTH = 6;
+
+
+    /**
+     * Encodes a {@link ChangePasswordReply} into a {@link ByteBuffer}.
+     *
+     * @param buf
+     * @param message
+     * @throws IOException
+     */
+    public void encode( ByteBuffer buf, ChangePasswordReply message ) throws IOException
+    {
+        // Build application reply bytes
+        ApplicationReply appReply = message.getApplicationReply();
+        ApplicationReplyEncoder appEncoder = new ApplicationReplyEncoder();
+        byte[] encodedAppReply = appEncoder.encode( appReply );
+
+        // Build private message bytes
+        PrivateMessage privateMessage = message.getPrivateMessage();
+        PrivateMessageEncoder privateEncoder = new PrivateMessageEncoder();
+        byte[] privateBytes = privateEncoder.encode( privateMessage );
+
+        short messageLength = ( short ) ( HEADER_LENGTH + encodedAppReply.length + privateBytes.length );
+
+        short protocolVersion = 1;
+
+        buf.putShort( messageLength );
+        buf.putShort( protocolVersion );
+        buf.putShort( ( short ) encodedAppReply.length );
+
+        buf.put( encodedAppReply );
+        buf.put( privateBytes );
+    }
+}
diff --git a/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/io/ChangePasswordRequestDecoder.java b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/io/ChangePasswordRequestDecoder.java
new file mode 100644
index 0000000..67d43da
--- /dev/null
+++ b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/io/ChangePasswordRequestDecoder.java
@@ -0,0 +1,75 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.changepw.io;
+
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+import org.apache.directory.server.changepw.messages.ChangePasswordRequest;
+import org.apache.directory.server.changepw.messages.ChangePasswordRequestModifier;
+import org.apache.directory.server.kerberos.shared.io.decoder.ApplicationRequestDecoder;
+import org.apache.directory.server.kerberos.shared.io.decoder.PrivateMessageDecoder;
+import org.apache.directory.server.kerberos.shared.messages.ApplicationRequest;
+import org.apache.directory.server.kerberos.shared.messages.application.PrivateMessage;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ChangePasswordRequestDecoder
+{
+    /**
+     * Decodes a {@link ByteBuffer} into a {@link ChangePasswordRequest}.
+     *
+     * @param buf
+     * @return The {@link ChangePasswordRequest}.
+     * @throws IOException
+     */
+    public ChangePasswordRequest decode( ByteBuffer buf ) throws IOException
+    {
+        ChangePasswordRequestModifier modifier = new ChangePasswordRequestModifier();
+
+        buf.getShort(); // message length
+
+        modifier.setProtocolVersionNumber( buf.getShort() );
+
+        short authHeaderLength = buf.getShort();
+
+        byte[] undecodedAuthHeader = new byte[authHeaderLength];
+        buf.get( undecodedAuthHeader, 0, authHeaderLength );
+
+        ApplicationRequestDecoder decoder = new ApplicationRequestDecoder();
+        ApplicationRequest authHeader = decoder.decode( undecodedAuthHeader );
+
+        modifier.setAuthHeader( authHeader );
+
+        byte[] encodedPrivate = new byte[buf.remaining()];
+        buf.get( encodedPrivate, 0, buf.remaining() );
+
+        PrivateMessageDecoder privateDecoder = new PrivateMessageDecoder();
+        PrivateMessage privMessage = privateDecoder.decode( encodedPrivate );
+
+        modifier.setPrivateMessage( privMessage );
+
+        return modifier.getChangePasswordMessage();
+    }
+}
diff --git a/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/io/ChangePasswordRequestEncoder.java b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/io/ChangePasswordRequestEncoder.java
new file mode 100644
index 0000000..2419608
--- /dev/null
+++ b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/io/ChangePasswordRequestEncoder.java
@@ -0,0 +1,72 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.changepw.io;
+
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+import org.apache.directory.server.changepw.messages.ChangePasswordRequest;
+import org.apache.directory.server.kerberos.shared.io.encoder.ApplicationRequestEncoder;
+import org.apache.directory.server.kerberos.shared.io.encoder.PrivateMessageEncoder;
+import org.apache.directory.server.kerberos.shared.messages.ApplicationRequest;
+import org.apache.directory.server.kerberos.shared.messages.application.PrivateMessage;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ChangePasswordRequestEncoder
+{
+    private static final int HEADER_LENGTH = 6;
+
+
+    /**
+     * Encodes a {@link ChangePasswordRequest} into a {@link ByteBuffer}.
+     *
+     * @param buf
+     * @param message
+     * @throws IOException
+     */
+    public void encode( ByteBuffer buf, ChangePasswordRequest message ) throws IOException
+    {
+        // Build application request bytes
+        ApplicationRequest appRequest = message.getAuthHeader();
+        ApplicationRequestEncoder appEncoder = new ApplicationRequestEncoder();
+        byte[] encodedAppRequest = appEncoder.encode( appRequest );
+
+        // Build private message bytes
+        PrivateMessage privateMessage = message.getPrivateMessage();
+        PrivateMessageEncoder privateEncoder = new PrivateMessageEncoder();
+        byte[] privateBytes = privateEncoder.encode( privateMessage );
+
+        short messageLength = ( short ) ( HEADER_LENGTH + encodedAppRequest.length + privateBytes.length );
+
+        short protocolVersion = 1;
+
+        buf.putShort( messageLength );
+        buf.putShort( protocolVersion );
+        buf.putShort( ( short ) encodedAppRequest.length );
+
+        buf.put( encodedAppRequest );
+        buf.put( privateBytes );
+    }
+}
diff --git a/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/io/package-info.java b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/io/package-info.java
new file mode 100644
index 0000000..dbce845
--- /dev/null
+++ b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/io/package-info.java
@@ -0,0 +1,29 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+/**
+ * Provides encoders and decoders for request, reply, error, and data
+ * payload conversions.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+
+package org.apache.directory.server.changepw.io;
diff --git a/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/messages/AbstractPasswordMessage.java b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/messages/AbstractPasswordMessage.java
new file mode 100644
index 0000000..34ba670
--- /dev/null
+++ b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/messages/AbstractPasswordMessage.java
@@ -0,0 +1,52 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.changepw.messages;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+abstract public class AbstractPasswordMessage
+{
+    /**
+     * ChangePassword protocol version number.
+     */
+    public static final int PVNO = 1;
+
+    private short versionNumber;
+
+
+    protected AbstractPasswordMessage( short versionNumber )
+    {
+        this.versionNumber = versionNumber;
+    }
+
+
+    /**
+     * Returns the version number.
+     *
+     * @return The version number.
+     */
+    public short getVersionNumber()
+    {
+        return versionNumber;
+    }
+}
diff --git a/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/messages/AbstractPasswordMessageModifier.java b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/messages/AbstractPasswordMessageModifier.java
new file mode 100644
index 0000000..e36a696
--- /dev/null
+++ b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/messages/AbstractPasswordMessageModifier.java
@@ -0,0 +1,41 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.changepw.messages;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+abstract public class AbstractPasswordMessageModifier
+{
+    protected short versionNumber;
+
+
+    /**
+     * Sets the protocol version number.
+     *
+     * @param versionNumber
+     */
+    public void setProtocolVersionNumber( short versionNumber )
+    {
+        this.versionNumber = versionNumber;
+    }
+}
diff --git a/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/messages/ChangePasswordError.java b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/messages/ChangePasswordError.java
new file mode 100644
index 0000000..32e9530
--- /dev/null
+++ b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/messages/ChangePasswordError.java
@@ -0,0 +1,58 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.changepw.messages;
+
+
+import org.apache.directory.server.kerberos.shared.messages.ErrorMessage;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ChangePasswordError extends AbstractPasswordMessage
+{
+    private ErrorMessage errorMessage;
+
+
+    /**
+     * Creates a new instance of ChangePasswordError.
+     *
+     * @param versionNumber
+     * @param errorMessage
+     */
+    public ChangePasswordError( short versionNumber, ErrorMessage errorMessage )
+    {
+        super( versionNumber );
+
+        this.errorMessage = errorMessage;
+    }
+
+
+    /**
+     * Returns the {@link ErrorMessage}.
+     *
+     * @return The {@link ErrorMessage}.
+     */
+    public ErrorMessage getErrorMessage()
+    {
+        return errorMessage;
+    }
+}
diff --git a/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/messages/ChangePasswordErrorModifier.java b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/messages/ChangePasswordErrorModifier.java
new file mode 100644
index 0000000..6f36612
--- /dev/null
+++ b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/messages/ChangePasswordErrorModifier.java
@@ -0,0 +1,55 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.changepw.messages;
+
+
+import org.apache.directory.server.kerberos.shared.messages.ErrorMessage;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ChangePasswordErrorModifier extends AbstractPasswordMessageModifier
+{
+    private ErrorMessage errorMessage;
+
+
+    /**
+     * Returns the {@link ChangePasswordError}.
+     *
+     * @return The {@link ChangePasswordError}.
+     */
+    public ChangePasswordError getChangePasswordError()
+    {
+        return new ChangePasswordError( versionNumber, errorMessage );
+    }
+
+
+    /**
+     * Sets the {@link ErrorMessage}.
+     *
+     * @param errorMessage
+     */
+    public void setErrorMessage( ErrorMessage errorMessage )
+    {
+        this.errorMessage = errorMessage;
+    }
+}
diff --git a/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/messages/ChangePasswordReply.java b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/messages/ChangePasswordReply.java
new file mode 100644
index 0000000..b856359
--- /dev/null
+++ b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/messages/ChangePasswordReply.java
@@ -0,0 +1,73 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.changepw.messages;
+
+
+import org.apache.directory.server.kerberos.shared.messages.application.ApplicationReply;
+import org.apache.directory.server.kerberos.shared.messages.application.PrivateMessage;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ChangePasswordReply extends AbstractPasswordMessage
+{
+    private ApplicationReply applicationReply;
+    private PrivateMessage privateMessage;
+
+
+    /**
+     * Creates a new instance of ChangePasswordReply.
+     *
+     * @param versionNumber
+     * @param applicationReply
+     * @param privateMessage
+     */
+    public ChangePasswordReply( short versionNumber, ApplicationReply applicationReply, PrivateMessage privateMessage )
+    {
+        super( versionNumber );
+
+        this.applicationReply = applicationReply;
+        this.privateMessage = privateMessage;
+    }
+
+
+    /**
+     * Returns the {@link ApplicationReply}.
+     *
+     * @return The {@link ApplicationReply}.
+     */
+    public ApplicationReply getApplicationReply()
+    {
+        return applicationReply;
+    }
+
+
+    /**
+     * Returns the {@link PrivateMessage}.
+     *
+     * @return The {@link PrivateMessage}.
+     */
+    public PrivateMessage getPrivateMessage()
+    {
+        return privateMessage;
+    }
+}
diff --git a/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/messages/ChangePasswordReplyModifier.java b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/messages/ChangePasswordReplyModifier.java
new file mode 100644
index 0000000..0b4f933
--- /dev/null
+++ b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/messages/ChangePasswordReplyModifier.java
@@ -0,0 +1,68 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.changepw.messages;
+
+
+import org.apache.directory.server.kerberos.shared.messages.application.ApplicationReply;
+import org.apache.directory.server.kerberos.shared.messages.application.PrivateMessage;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ChangePasswordReplyModifier extends AbstractPasswordMessageModifier
+{
+    private ApplicationReply applicationReply;
+    private PrivateMessage privateMessage;
+
+
+    /**
+     * Returns the {@link ChangePasswordReply}.
+     *
+     * @return The {@link ChangePasswordReply}.
+     */
+    public ChangePasswordReply getChangePasswordReply()
+    {
+        return new ChangePasswordReply( versionNumber, applicationReply, privateMessage );
+    }
+
+
+    /**
+     * Sets the {@link ApplicationReply}.
+     *
+     * @param applicationReply
+     */
+    public void setApplicationReply( ApplicationReply applicationReply )
+    {
+        this.applicationReply = applicationReply;
+    }
+
+
+    /**
+     * Sets the {@link PrivateMessage}.
+     *
+     * @param privateMessage
+     */
+    public void setPrivateMessage( PrivateMessage privateMessage )
+    {
+        this.privateMessage = privateMessage;
+    }
+}
diff --git a/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/messages/ChangePasswordRequest.java b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/messages/ChangePasswordRequest.java
new file mode 100644
index 0000000..ea0c671
--- /dev/null
+++ b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/messages/ChangePasswordRequest.java
@@ -0,0 +1,73 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.changepw.messages;
+
+
+import org.apache.directory.server.kerberos.shared.messages.ApplicationRequest;
+import org.apache.directory.server.kerberos.shared.messages.application.PrivateMessage;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ChangePasswordRequest extends AbstractPasswordMessage
+{
+    private ApplicationRequest authHeader;
+    private PrivateMessage privateMessage;
+
+
+    /**
+     * Creates a new instance of ChangePasswordRequest.
+     *
+     * @param versionNumber
+     * @param authHeader
+     * @param privateMessage
+     */
+    public ChangePasswordRequest( short versionNumber, ApplicationRequest authHeader, PrivateMessage privateMessage )
+    {
+        super( versionNumber );
+
+        this.authHeader = authHeader;
+        this.privateMessage = privateMessage;
+    }
+
+
+    /**
+     * Returns the {@link ApplicationRequest}.
+     *
+     * @return The {@link ApplicationRequest}.
+     */
+    public ApplicationRequest getAuthHeader()
+    {
+        return authHeader;
+    }
+
+
+    /**
+     * Returns the {@link PrivateMessage}.
+     *
+     * @return The {@link PrivateMessage}.
+     */
+    public PrivateMessage getPrivateMessage()
+    {
+        return privateMessage;
+    }
+}
diff --git a/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/messages/ChangePasswordRequestModifier.java b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/messages/ChangePasswordRequestModifier.java
new file mode 100644
index 0000000..5c302c3
--- /dev/null
+++ b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/messages/ChangePasswordRequestModifier.java
@@ -0,0 +1,68 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.changepw.messages;
+
+
+import org.apache.directory.server.kerberos.shared.messages.ApplicationRequest;
+import org.apache.directory.server.kerberos.shared.messages.application.PrivateMessage;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ChangePasswordRequestModifier extends AbstractPasswordMessageModifier
+{
+    private ApplicationRequest authHeader;
+    private PrivateMessage privateMessage;
+
+
+    /**
+     * Returns the {@link ChangePasswordRequest}.
+     *
+     * @return The {@link ChangePasswordRequest}.
+     */
+    public ChangePasswordRequest getChangePasswordMessage()
+    {
+        return new ChangePasswordRequest( versionNumber, authHeader, privateMessage );
+    }
+
+
+    /**
+     * Sets the AuthHeader.
+     *
+     * @param authHeader
+     */
+    public void setAuthHeader( ApplicationRequest authHeader )
+    {
+        this.authHeader = authHeader;
+    }
+
+
+    /**
+     * Sets the {@link PrivateMessage}.
+     *
+     * @param privateMessage
+     */
+    public void setPrivateMessage( PrivateMessage privateMessage )
+    {
+        this.privateMessage = privateMessage;
+    }
+}
diff --git a/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/messages/package-info.java b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/messages/package-info.java
new file mode 100644
index 0000000..2726faf
--- /dev/null
+++ b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/messages/package-info.java
@@ -0,0 +1,28 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+/**
+ * Provides message objects for request, reply, and error messages.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+
+package org.apache.directory.server.changepw.messages;
diff --git a/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/package-info.java b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/package-info.java
new file mode 100644
index 0000000..0be8aaf
--- /dev/null
+++ b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/package-info.java
@@ -0,0 +1,30 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+/**
+ * Provides the entry point to an instance of the
+ * {@link org.apache.directory.server.changepw.ChangePasswordServer},
+ * as well as support for configuration.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+
+package org.apache.directory.server.changepw;
diff --git a/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/protocol/ChangePasswordProtocolHandler.java b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/protocol/ChangePasswordProtocolHandler.java
new file mode 100644
index 0000000..8499efa
--- /dev/null
+++ b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/protocol/ChangePasswordProtocolHandler.java
@@ -0,0 +1,236 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.changepw.protocol;
+
+
+import java.io.UnsupportedEncodingException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.nio.ByteBuffer;
+
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.apache.directory.server.changepw.ChangePasswordServer;
+import org.apache.directory.server.changepw.exceptions.ChangePasswordException;
+import org.apache.directory.server.changepw.exceptions.ErrorType;
+import org.apache.directory.server.changepw.messages.ChangePasswordErrorModifier;
+import org.apache.directory.server.changepw.messages.ChangePasswordRequest;
+import org.apache.directory.server.changepw.service.ChangePasswordContext;
+import org.apache.directory.server.changepw.service.ChangePasswordService;
+import org.apache.directory.server.kerberos.shared.exceptions.KerberosException;
+import org.apache.directory.server.kerberos.shared.messages.ErrorMessage;
+import org.apache.directory.server.kerberos.shared.messages.ErrorMessageModifier;
+import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
+import org.apache.directory.server.kerberos.shared.store.PrincipalStore;
+import org.apache.mina.common.IdleStatus;
+import org.apache.mina.common.IoHandler;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.common.TransportType;
+import org.apache.mina.filter.codec.ProtocolCodecFilter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ChangePasswordProtocolHandler implements IoHandler
+{
+    private static final Logger log = LoggerFactory.getLogger( ChangePasswordProtocolHandler.class );
+
+    private ChangePasswordServer config;
+    private PrincipalStore store;
+    private String contextKey = "context";
+
+
+    /**
+     * Creates a new instance of ChangePasswordProtocolHandler.
+     *
+     * @param config
+     * @param store
+     */
+    public ChangePasswordProtocolHandler( ChangePasswordServer config, PrincipalStore store )
+    {
+        this.config = config;
+        this.store = store;
+    }
+
+
+    public void sessionCreated( IoSession session ) throws Exception
+    {
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "{} CREATED:  {}", session.getRemoteAddress(), session.getTransportType() );
+        }
+
+        if ( session.getTransportType() == TransportType.DATAGRAM )
+        {
+            session.getFilterChain().addFirst( "codec",
+                new ProtocolCodecFilter( ChangePasswordUdpProtocolCodecFactory.getInstance() ) );
+        }
+        else
+        {
+            session.getFilterChain().addFirst( "codec",
+                new ProtocolCodecFilter( ChangePasswordTcpProtocolCodecFactory.getInstance() ) );
+        }
+    }
+
+
+    public void sessionOpened( IoSession session )
+    {
+        log.debug( "{} OPENED", session.getRemoteAddress() );
+    }
+
+
+    public void sessionClosed( IoSession session )
+    {
+        log.debug( "{} CLOSED", session.getRemoteAddress() );
+    }
+
+
+    public void sessionIdle( IoSession session, IdleStatus status )
+    {
+        log.debug( "{} IDLE ({})", session.getRemoteAddress(), status );
+    }
+
+
+    public void exceptionCaught( IoSession session, Throwable cause )
+    {
+        log.debug( session.getRemoteAddress() + " EXCEPTION", cause );
+        session.close();
+    }
+
+
+    public void messageReceived( IoSession session, Object message )
+    {
+        log.debug( "{} RCVD:  {}", session.getRemoteAddress(), message );
+
+        InetAddress clientAddress = ( ( InetSocketAddress ) session.getRemoteAddress() ).getAddress();
+        ChangePasswordRequest request = ( ChangePasswordRequest ) message;
+
+        try
+        {
+            ChangePasswordContext changepwContext = new ChangePasswordContext();
+            changepwContext.setConfig( config );
+            changepwContext.setStore( store );
+            changepwContext.setClientAddress( clientAddress );
+            changepwContext.setRequest( request );
+            session.setAttribute( getContextKey(), changepwContext );
+
+            ChangePasswordService.execute( session, changepwContext );
+
+            session.write( changepwContext.getReply() );
+        }
+        catch ( KerberosException ke )
+        {
+            if ( log.isDebugEnabled() )
+            {
+                log.warn( ke.getMessage(), ke );
+            }
+            else
+            {
+                log.warn( ke.getMessage() );
+            }
+
+            ErrorMessage errorMessage = getErrorMessage( config.getServicePrincipal(), ke );
+
+            ChangePasswordErrorModifier modifier = new ChangePasswordErrorModifier();
+            modifier.setErrorMessage( errorMessage );
+
+            session.write( modifier.getChangePasswordError() );
+        }
+        catch ( Exception e )
+        {
+            log.error( "Unexpected exception:  " + e.getMessage(), e );
+
+            session.write( getErrorMessage( config.getServicePrincipal(), new ChangePasswordException(
+                ErrorType.KRB5_KPASSWD_UNKNOWN_ERROR ) ) );
+        }
+    }
+
+
+    public void messageSent( IoSession session, Object message )
+    {
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "{} SENT:  {}", session.getRemoteAddress(), message );
+        }
+    }
+
+
+    protected String getContextKey()
+    {
+        return ( this.contextKey );
+    }
+
+
+    private ErrorMessage getErrorMessage( KerberosPrincipal principal, KerberosException exception )
+    {
+        ErrorMessageModifier modifier = new ErrorMessageModifier();
+
+        KerberosTime now = new KerberosTime();
+
+        modifier.setErrorCode( exception.getErrorCode() );
+        modifier.setExplanatoryText( exception.getMessage() );
+        modifier.setServerPrincipal( principal );
+        modifier.setServerTime( now );
+        modifier.setServerMicroSecond( 0 );
+        modifier.setExplanatoryData( buildExplanatoryData( exception ) );
+
+        return modifier.getErrorMessage();
+    }
+
+
+    private byte[] buildExplanatoryData( KerberosException exception )
+    {
+        short resultCode = ( short ) exception.getErrorCode();
+
+        byte[] resultString =
+            { ( byte ) 0x00 };
+
+        if ( exception.getExplanatoryData() == null || exception.getExplanatoryData().length == 0 )
+        {
+            try
+            {
+                resultString = exception.getMessage().getBytes( "UTF-8" );
+            }
+            catch ( UnsupportedEncodingException uee )
+            {
+                log.error( uee.getMessage() );
+            }
+        }
+        else
+        {
+            resultString = exception.getExplanatoryData();
+        }
+
+        ByteBuffer byteBuffer = ByteBuffer.allocate( 256 );
+        byteBuffer.putShort( resultCode );
+        byteBuffer.put( resultString );
+
+        byteBuffer.flip();
+        byte[] explanatoryData = new byte[byteBuffer.remaining()];
+        byteBuffer.get( explanatoryData, 0, explanatoryData.length );
+
+        return explanatoryData;
+    }
+}
diff --git a/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/protocol/ChangePasswordTcpDecoder.java b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/protocol/ChangePasswordTcpDecoder.java
new file mode 100644
index 0000000..28d1e63
--- /dev/null
+++ b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/protocol/ChangePasswordTcpDecoder.java
@@ -0,0 +1,90 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.changepw.protocol;
+
+
+import org.apache.directory.server.changepw.io.ChangePasswordRequestDecoder;
+import org.apache.mina.common.BufferDataException;
+import org.apache.mina.common.ByteBuffer;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.filter.codec.CumulativeProtocolDecoder;
+import org.apache.mina.filter.codec.ProtocolDecoderOutput;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 549315 $, $Date: 2007-06-20 18:13:53 -0700 (Wed, 20 Jun 2007) $
+ */
+public class ChangePasswordTcpDecoder extends CumulativeProtocolDecoder
+{
+    private ChangePasswordRequestDecoder decoder = new ChangePasswordRequestDecoder();
+
+    private int maxObjectSize = 16384; // 16KB
+
+
+    /**
+     * Returns the allowed maximum size of the object to be decoded.
+     * If the size of the object to be decoded exceeds this value, this
+     * decoder will throw a {@link BufferDataException}.  The default
+     * value is <tt>16384</tt> (16KB).
+     * 
+     * @return The max object size.
+     */
+    public int getMaxObjectSize()
+    {
+        return maxObjectSize;
+    }
+
+
+    /**
+     * Sets the allowed maximum size of the object to be decoded.
+     * If the size of the object to be decoded exceeds this value, this
+     * decoder will throw a {@link BufferDataException}.  The default
+     * value is <tt>16384</tt> (16KB).
+     * 
+     * @param maxObjectSize 
+     */
+    public void setMaxObjectSize( int maxObjectSize )
+    {
+        if ( maxObjectSize <= 0 )
+        {
+            throw new IllegalArgumentException( "maxObjectSize: " + maxObjectSize );
+        }
+
+        this.maxObjectSize = maxObjectSize;
+    }
+
+
+    @Override
+    protected boolean doDecode( IoSession session, ByteBuffer in, ProtocolDecoderOutput out ) throws Exception
+    {
+        if ( !in.prefixedDataAvailable( 4, maxObjectSize ) )
+        {
+            return false;
+        }
+
+        in.getInt();
+
+        out.write( decoder.decode( in.buf() ) );
+
+        return true;
+    }
+}
diff --git a/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/protocol/ChangePasswordTcpEncoder.java b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/protocol/ChangePasswordTcpEncoder.java
new file mode 100644
index 0000000..3d7b1a9
--- /dev/null
+++ b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/protocol/ChangePasswordTcpEncoder.java
@@ -0,0 +1,81 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.changepw.protocol;
+
+
+import java.io.IOException;
+
+import org.apache.directory.server.changepw.io.ChangePasswordErrorEncoder;
+import org.apache.directory.server.changepw.io.ChangePasswordReplyEncoder;
+import org.apache.directory.server.changepw.messages.ChangePasswordError;
+import org.apache.directory.server.changepw.messages.ChangePasswordReply;
+import org.apache.mina.common.ByteBuffer;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.filter.codec.ProtocolEncoderAdapter;
+import org.apache.mina.filter.codec.ProtocolEncoderOutput;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 549315 $, $Date: 2007-06-20 18:13:53 -0700 (Wed, 20 Jun 2007) $
+ */
+public class ChangePasswordTcpEncoder extends ProtocolEncoderAdapter
+{
+    ChangePasswordReplyEncoder replyEncoder = new ChangePasswordReplyEncoder();
+    ChangePasswordErrorEncoder errorEncoder = new ChangePasswordErrorEncoder();
+
+
+    public void encode( IoSession session, Object message, ProtocolEncoderOutput out ) throws IOException
+    {
+        ByteBuffer buf = ByteBuffer.allocate( 512 );
+
+        // make space for int length
+        buf.putInt( 0 );
+
+        if ( message instanceof ChangePasswordReply )
+        {
+            replyEncoder.encode( buf.buf(), ( ChangePasswordReply ) message );
+        }
+        else
+        {
+            if ( message instanceof ChangePasswordError )
+            {
+                errorEncoder.encode( buf.buf(), ( ChangePasswordError ) message );
+            }
+        }
+
+        // mark position
+        int pos = buf.position();
+
+        // length is the data minus 4 bytes for the pre-pended length
+        int recordLength = buf.position() - 4;
+
+        // write the length
+        buf.rewind();
+        buf.putInt( recordLength );
+
+        // set the position back before flipping the buffer
+        buf.position( pos );
+        buf.flip();
+
+        out.write( buf );
+    }
+}
diff --git a/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/protocol/ChangePasswordTcpProtocolCodecFactory.java b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/protocol/ChangePasswordTcpProtocolCodecFactory.java
new file mode 100644
index 0000000..4b9d179
--- /dev/null
+++ b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/protocol/ChangePasswordTcpProtocolCodecFactory.java
@@ -0,0 +1,67 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.changepw.protocol;
+
+
+import org.apache.mina.filter.codec.ProtocolCodecFactory;
+import org.apache.mina.filter.codec.ProtocolDecoder;
+import org.apache.mina.filter.codec.ProtocolEncoder;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 549315 $, $Date: 2007-06-20 18:13:53 -0700 (Wed, 20 Jun 2007) $
+ */
+public class ChangePasswordTcpProtocolCodecFactory implements ProtocolCodecFactory
+{
+    private static final ChangePasswordTcpProtocolCodecFactory INSTANCE = new ChangePasswordTcpProtocolCodecFactory();
+
+
+    /**
+     * Returns the singleton instance of {@link ChangePasswordTcpProtocolCodecFactory}.
+     *
+     * @return The singleton instance of {@link ChangePasswordTcpProtocolCodecFactory}.
+     */
+    public static ChangePasswordTcpProtocolCodecFactory getInstance()
+    {
+        return INSTANCE;
+    }
+
+
+    private ChangePasswordTcpProtocolCodecFactory()
+    {
+        // Private constructor prevents instantiation outside this class.
+    }
+
+
+    public ProtocolEncoder getEncoder()
+    {
+        // Create a new encoder.
+        return new ChangePasswordTcpEncoder();
+    }
+
+
+    public ProtocolDecoder getDecoder()
+    {
+        // Create a new decoder.
+        return new ChangePasswordTcpDecoder();
+    }
+}
diff --git a/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/protocol/ChangePasswordUdpDecoder.java b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/protocol/ChangePasswordUdpDecoder.java
new file mode 100644
index 0000000..1be68b8
--- /dev/null
+++ b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/protocol/ChangePasswordUdpDecoder.java
@@ -0,0 +1,44 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.changepw.protocol;
+
+
+import java.io.IOException;
+
+import org.apache.directory.server.changepw.io.ChangePasswordRequestDecoder;
+import org.apache.mina.common.ByteBuffer;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.filter.codec.ProtocolDecoderAdapter;
+import org.apache.mina.filter.codec.ProtocolDecoderOutput;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ChangePasswordUdpDecoder extends ProtocolDecoderAdapter
+{
+    public void decode( IoSession session, ByteBuffer in, ProtocolDecoderOutput out ) throws IOException
+    {
+        ChangePasswordRequestDecoder decoder = new ChangePasswordRequestDecoder();
+        out.write( decoder.decode( in.buf() ) );
+    }
+}
diff --git a/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/protocol/ChangePasswordUdpEncoder.java b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/protocol/ChangePasswordUdpEncoder.java
new file mode 100644
index 0000000..0668957
--- /dev/null
+++ b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/protocol/ChangePasswordUdpEncoder.java
@@ -0,0 +1,78 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.changepw.protocol;
+
+
+import java.io.IOException;
+
+import org.apache.directory.server.changepw.io.ChangePasswordErrorEncoder;
+import org.apache.directory.server.changepw.io.ChangePasswordReplyEncoder;
+import org.apache.directory.server.changepw.messages.ChangePasswordError;
+import org.apache.directory.server.changepw.messages.ChangePasswordReply;
+import org.apache.mina.common.ByteBuffer;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.filter.codec.ProtocolEncoderAdapter;
+import org.apache.mina.filter.codec.ProtocolEncoderOutput;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ChangePasswordUdpEncoder extends ProtocolEncoderAdapter
+{
+    public void encode( IoSession session, Object message, ProtocolEncoderOutput out ) throws IOException
+    {
+        ByteBuffer buf = ByteBuffer.allocate( 512 );
+
+        if ( message instanceof ChangePasswordReply )
+        {
+            encodeReply( ( ChangePasswordReply ) message, buf );
+        }
+        else
+        {
+            if ( message instanceof ChangePasswordError )
+            {
+                encodeError( ( ChangePasswordError ) message, buf );
+            }
+        }
+
+        buf.flip();
+
+        out.write( buf );
+    }
+
+
+    private void encodeReply( ChangePasswordReply reply, ByteBuffer buf ) throws IOException
+    {
+        ChangePasswordReplyEncoder encoder = new ChangePasswordReplyEncoder();
+
+        encoder.encode( buf.buf(), reply );
+    }
+
+
+    private void encodeError( ChangePasswordError error, ByteBuffer buf ) throws IOException
+    {
+        ChangePasswordErrorEncoder encoder = new ChangePasswordErrorEncoder();
+
+        encoder.encode( buf.buf(), error );
+    }
+}
diff --git a/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/protocol/ChangePasswordUdpProtocolCodecFactory.java b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/protocol/ChangePasswordUdpProtocolCodecFactory.java
new file mode 100644
index 0000000..5ce9f4a
--- /dev/null
+++ b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/protocol/ChangePasswordUdpProtocolCodecFactory.java
@@ -0,0 +1,67 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.changepw.protocol;
+
+
+import org.apache.mina.filter.codec.ProtocolCodecFactory;
+import org.apache.mina.filter.codec.ProtocolDecoder;
+import org.apache.mina.filter.codec.ProtocolEncoder;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ChangePasswordUdpProtocolCodecFactory implements ProtocolCodecFactory
+{
+    private static final ChangePasswordUdpProtocolCodecFactory INSTANCE = new ChangePasswordUdpProtocolCodecFactory();
+
+
+    /**
+     * Returns the singleton instance of {@link ChangePasswordUdpProtocolCodecFactory}.
+     *
+     * @return The singleton instance of {@link ChangePasswordUdpProtocolCodecFactory}.
+     */
+    public static ChangePasswordUdpProtocolCodecFactory getInstance()
+    {
+        return INSTANCE;
+    }
+
+
+    private ChangePasswordUdpProtocolCodecFactory()
+    {
+        // Private constructor prevents instantiation outside this class.
+    }
+
+
+    public ProtocolEncoder getEncoder()
+    {
+        // Create a new encoder.
+        return new ChangePasswordUdpEncoder();
+    }
+
+
+    public ProtocolDecoder getDecoder()
+    {
+        // Create a new decoder.
+        return new ChangePasswordUdpDecoder();
+    }
+}
diff --git a/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/protocol/package-info.java b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/protocol/package-info.java
new file mode 100644
index 0000000..aeb0536
--- /dev/null
+++ b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/protocol/package-info.java
@@ -0,0 +1,30 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+/**
+ * Provides the {@link org.apache.mina.common.IoHandler} and associated
+ * {@link org.apache.mina.filter.codec.ProtocolCodecFactory} required
+ * to implement the Change Password Service with the MINA NIO framework.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+
+package org.apache.directory.server.changepw.protocol;
diff --git a/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/service/ChangePasswordContext.java b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/service/ChangePasswordContext.java
new file mode 100644
index 0000000..9f8cbb8
--- /dev/null
+++ b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/service/ChangePasswordContext.java
@@ -0,0 +1,273 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.changepw.service;
+
+
+import java.net.InetAddress;
+
+import org.apache.directory.server.changepw.ChangePasswordServer;
+import org.apache.directory.server.changepw.messages.AbstractPasswordMessage;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.CipherTextHandler;
+import org.apache.directory.server.kerberos.shared.messages.ApplicationRequest;
+import org.apache.directory.server.kerberos.shared.messages.components.Authenticator;
+import org.apache.directory.server.kerberos.shared.messages.components.Ticket;
+import org.apache.directory.server.kerberos.shared.replay.ReplayCache;
+import org.apache.directory.server.kerberos.shared.store.PrincipalStore;
+import org.apache.directory.server.kerberos.shared.store.PrincipalStoreEntry;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ChangePasswordContext
+{
+    private static final long serialVersionUID = -5124209294966799740L;
+
+    private ChangePasswordServer config;
+    private PrincipalStore store;
+    private AbstractPasswordMessage request;
+    private AbstractPasswordMessage reply;
+    private InetAddress clientAddress;
+
+    private ApplicationRequest authHeader;
+    private Ticket ticket;
+    private Authenticator authenticator;
+    private PrincipalStoreEntry serverEntry;
+    private ReplayCache replayCache;
+    private CipherTextHandler cipherTextHandler;
+    private String password;
+
+
+    /**
+     * @return Returns the replayCache.
+     */
+    public ReplayCache getReplayCache()
+    {
+        return replayCache;
+    }
+
+
+    /**
+     * @param replayCache The replayCache to set.
+     */
+    public void setReplayCache( ReplayCache replayCache )
+    {
+        this.replayCache = replayCache;
+    }
+
+
+    /**
+     * @return Returns the serverEntry.
+     */
+    public PrincipalStoreEntry getServerEntry()
+    {
+        return serverEntry;
+    }
+
+
+    /**
+     * @param serverEntry The serverEntry to set.
+     */
+    public void setServerEntry( PrincipalStoreEntry serverEntry )
+    {
+        this.serverEntry = serverEntry;
+    }
+
+
+    /**
+     * @return Returns the config.
+     */
+    public ChangePasswordServer getConfig()
+    {
+        return config;
+    }
+
+
+    /**
+     * @param config The config to set.
+     */
+    public void setConfig( ChangePasswordServer config )
+    {
+        this.config = config;
+    }
+
+
+    /**
+     * @return Returns the reply.
+     */
+    public AbstractPasswordMessage getReply()
+    {
+        return reply;
+    }
+
+
+    /**
+     * @param reply The reply to set.
+     */
+    public void setReply( AbstractPasswordMessage reply )
+    {
+        this.reply = reply;
+    }
+
+
+    /**
+     * @return Returns the request.
+     */
+    public AbstractPasswordMessage getRequest()
+    {
+        return request;
+    }
+
+
+    /**
+     * @param request The request to set.
+     */
+    public void setRequest( AbstractPasswordMessage request )
+    {
+        this.request = request;
+    }
+
+
+    /**
+     * @return Returns the store.
+     */
+    public PrincipalStore getStore()
+    {
+        return store;
+    }
+
+
+    /**
+     * @param store The store to set.
+     */
+    public void setStore( PrincipalStore store )
+    {
+        this.store = store;
+    }
+
+
+    /**
+     * @return Returns the {@link CipherTextHandler}.
+     */
+    public CipherTextHandler getCipherTextHandler()
+    {
+        return cipherTextHandler;
+    }
+
+
+    /**
+     * @param cipherTextHandler The {@link CipherTextHandler} to set.
+     */
+    public void setCipherTextHandler( CipherTextHandler cipherTextHandler )
+    {
+        this.cipherTextHandler = cipherTextHandler;
+    }
+
+
+    /**
+     * @return Returns the authenticator.
+     */
+    public Authenticator getAuthenticator()
+    {
+        return authenticator;
+    }
+
+
+    /**
+     * @param authenticator The authenticator to set.
+     */
+    public void setAuthenticator( Authenticator authenticator )
+    {
+        this.authenticator = authenticator;
+    }
+
+
+    /**
+     * @return Returns the authHeader.
+     */
+    public ApplicationRequest getAuthHeader()
+    {
+        return authHeader;
+    }
+
+
+    /**
+     * @param authHeader The authHeader to set.
+     */
+    public void setAuthHeader( ApplicationRequest authHeader )
+    {
+        this.authHeader = authHeader;
+    }
+
+
+    /**
+     * @return Returns the ticket.
+     */
+    public Ticket getTicket()
+    {
+        return ticket;
+    }
+
+
+    /**
+     * @param ticket The ticket to set.
+     */
+    public void setTicket( Ticket ticket )
+    {
+        this.ticket = ticket;
+    }
+
+
+    /**
+     * @return Returns the clientAddress.
+     */
+    public InetAddress getClientAddress()
+    {
+        return clientAddress;
+    }
+
+
+    /**
+     * @param clientAddress The clientAddress to set.
+     */
+    public void setClientAddress( InetAddress clientAddress )
+    {
+        this.clientAddress = clientAddress;
+    }
+
+
+    /**
+     * @return Returns the password.
+     */
+    public String getPassword()
+    {
+        return password;
+    }
+
+
+    /**
+     * @param password The password to set.
+     */
+    public void setPassword( String password )
+    {
+        this.password = password;
+    }
+}
diff --git a/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/service/ChangePasswordService.java b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/service/ChangePasswordService.java
new file mode 100644
index 0000000..62e71f6
--- /dev/null
+++ b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/service/ChangePasswordService.java
@@ -0,0 +1,436 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.changepw.service;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+import javax.naming.NamingException;
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.apache.directory.server.changepw.ChangePasswordServer;
+import org.apache.directory.server.changepw.exceptions.ChangePasswordException;
+import org.apache.directory.server.changepw.exceptions.ErrorType;
+import org.apache.directory.server.changepw.io.ChangePasswordDataDecoder;
+import org.apache.directory.server.changepw.messages.ChangePasswordReply;
+import org.apache.directory.server.changepw.messages.ChangePasswordReplyModifier;
+import org.apache.directory.server.changepw.messages.ChangePasswordRequest;
+import org.apache.directory.server.changepw.value.ChangePasswordData;
+import org.apache.directory.server.changepw.value.ChangePasswordDataModifier;
+import org.apache.directory.server.kerberos.shared.KerberosUtils;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.CipherTextHandler;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.KeyUsage;
+import org.apache.directory.server.kerberos.shared.exceptions.KerberosException;
+//import org.apache.directory.server.kerberos.shared.exceptions.ErrorType;
+import org.apache.directory.server.kerberos.shared.messages.ApplicationRequest;
+import org.apache.directory.server.kerberos.shared.messages.application.ApplicationReply;
+import org.apache.directory.server.kerberos.shared.messages.application.PrivateMessage;
+import org.apache.directory.server.kerberos.shared.messages.components.Authenticator;
+import org.apache.directory.server.kerberos.shared.messages.components.EncApRepPart;
+import org.apache.directory.server.kerberos.shared.messages.components.EncApRepPartModifier;
+import org.apache.directory.server.kerberos.shared.messages.components.EncKrbPrivPart;
+import org.apache.directory.server.kerberos.shared.messages.components.EncKrbPrivPartModifier;
+import org.apache.directory.server.kerberos.shared.messages.components.Ticket;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptedData;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+import org.apache.directory.server.kerberos.shared.messages.value.HostAddress;
+import org.apache.directory.server.kerberos.shared.messages.value.HostAddresses;
+import org.apache.directory.server.kerberos.shared.replay.InMemoryReplayCache;
+import org.apache.directory.server.kerberos.shared.replay.ReplayCache;
+import org.apache.directory.server.kerberos.shared.store.PrincipalStore;
+import org.apache.directory.server.kerberos.shared.store.PrincipalStoreEntry;
+import org.apache.mina.common.IoSession;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ChangePasswordService
+{
+    /** the logger for this class */
+    private static final Logger LOG = LoggerFactory.getLogger( ChangePasswordService.class );
+
+    private static final ReplayCache replayCache = new InMemoryReplayCache();
+    
+    private static final CipherTextHandler cipherTextHandler = new CipherTextHandler();
+
+    
+    public static void execute( IoSession session, ChangePasswordContext changepwContext ) throws KerberosException, IOException
+    {
+        if ( LOG.isDebugEnabled() )
+        {
+            monitorRequest( changepwContext );
+        }
+        
+        configureChangePassword( changepwContext );
+        getAuthHeader( session, changepwContext );
+        verifyServiceTicket( changepwContext );
+        getServerEntry( changepwContext );
+        verifyServiceTicketAuthHeader( changepwContext );
+        extractPassword( changepwContext );
+        
+        if ( LOG.isDebugEnabled() )
+        {
+            monitorContext( changepwContext );
+        }
+        
+        processPasswordChange( changepwContext );
+        buildReply( changepwContext );
+        
+        if ( LOG.isDebugEnabled() )
+        {
+            monitorReply( changepwContext );
+        }
+    }
+    
+    
+    private static void processPasswordChange( ChangePasswordContext changepwContext ) throws KerberosException
+    {
+        PrincipalStore store = changepwContext.getStore();
+        Authenticator authenticator = changepwContext.getAuthenticator();
+        String newPassword = changepwContext.getPassword();
+        KerberosPrincipal clientPrincipal = authenticator.getClientPrincipal();
+
+        // usec and seq-number must be present per MS but aren't in legacy kpasswd
+        // seq-number must have same value as authenticator
+        // ignore r-address
+
+        try
+        {
+            String principalName = store.changePassword( clientPrincipal, newPassword );
+            LOG.debug( "Successfully modified principal {}.", principalName );
+        }
+        catch ( NamingException ne )
+        {
+            throw new ChangePasswordException( ErrorType.KRB5_KPASSWD_SOFTERROR, ne.getExplanation().getBytes(), ne );
+        }
+        catch ( Exception e )
+        {
+            throw new ChangePasswordException( ErrorType.KRB5_KPASSWD_HARDERROR, e );
+        }
+    }
+    
+    
+    private static void monitorRequest( ChangePasswordContext changepwContext ) throws KerberosException
+    {
+        try
+        {
+            ChangePasswordRequest request = ( ChangePasswordRequest ) changepwContext.getRequest();
+            short versionNumber = request.getVersionNumber();
+
+            StringBuffer sb = new StringBuffer();
+            sb.append( "Responding to change password request:" );
+            sb.append( "\n\t" + "versionNumber    " + versionNumber );
+
+            LOG.debug( sb.toString() );
+        }
+        catch ( Exception e )
+        {
+            // This is a monitor.  No exceptions should bubble up.
+            LOG.error( "Error in request monitor", e );
+        }
+    }
+    
+    
+    private static void configureChangePassword( ChangePasswordContext changepwContext )
+    {
+        changepwContext.setReplayCache( replayCache );
+        changepwContext.setCipherTextHandler( cipherTextHandler );
+    }
+    
+    
+    private static void getAuthHeader( IoSession session, ChangePasswordContext changepwContext ) throws KerberosException
+    {
+        ChangePasswordRequest request = ( ChangePasswordRequest ) changepwContext.getRequest();
+
+        if ( request.getVersionNumber() != 1 )
+        {
+            throw new ChangePasswordException( ErrorType.KRB5_KPASSWD_BAD_VERSION );
+        }
+
+        if ( request.getAuthHeader() == null || request.getAuthHeader().getTicket() == null )
+        {
+            throw new ChangePasswordException( ErrorType.KRB5_KPASSWD_AUTHERROR );
+        }
+
+        ApplicationRequest authHeader = request.getAuthHeader();
+        Ticket ticket = authHeader.getTicket();
+
+        changepwContext.setAuthHeader( authHeader );
+        changepwContext.setTicket( ticket );
+    }
+    
+    
+    private static void verifyServiceTicket( ChangePasswordContext changepwContext ) throws KerberosException
+    {
+        ChangePasswordServer config = changepwContext.getConfig();
+        Ticket ticket = changepwContext.getTicket();
+        String primaryRealm = config.getPrimaryRealm();
+        KerberosPrincipal changepwPrincipal = config.getServicePrincipal();
+        KerberosPrincipal serverPrincipal = ticket.getServerPrincipal(); 
+
+        if ( !ticket.getRealm().equals( primaryRealm ) || !serverPrincipal.equals( changepwPrincipal ) )
+        {
+            throw new KerberosException( org.apache.directory.server.kerberos.shared.exceptions.ErrorType.KRB_AP_ERR_NOT_US );
+        }
+    }
+    
+    
+    private static void getServerEntry( ChangePasswordContext changepwContext ) throws KerberosException
+    {
+        KerberosPrincipal principal =  changepwContext.getTicket().getServerPrincipal();
+        PrincipalStore store = changepwContext.getStore();
+
+        changepwContext.setServerEntry( KerberosUtils.getEntry( principal, store, org.apache.directory.server.kerberos.shared.exceptions.ErrorType.KDC_ERR_S_PRINCIPAL_UNKNOWN ) );
+    }
+    
+    
+    private static void verifyServiceTicketAuthHeader( ChangePasswordContext changepwContext ) throws KerberosException
+    {
+        ApplicationRequest authHeader = changepwContext.getAuthHeader();
+        Ticket ticket = changepwContext.getTicket();
+
+        EncryptionType encryptionType = ticket.getEncPart().getEType();
+        EncryptionKey serverKey = changepwContext.getServerEntry().getKeyMap().get( encryptionType );
+
+        long clockSkew = changepwContext.getConfig().getAllowableClockSkew();
+        ReplayCache replayCache = changepwContext.getReplayCache();
+        boolean emptyAddressesAllowed = changepwContext.getConfig().isEmptyAddressesAllowed();
+        InetAddress clientAddress = changepwContext.getClientAddress();
+        CipherTextHandler cipherTextHandler = changepwContext.getCipherTextHandler();
+
+        Authenticator authenticator = KerberosUtils.verifyAuthHeader( authHeader, ticket, serverKey, clockSkew, replayCache,
+            emptyAddressesAllowed, clientAddress, cipherTextHandler, KeyUsage.NUMBER11, false );
+
+        ChangePasswordRequest request = ( ChangePasswordRequest ) changepwContext.getRequest();
+
+        if ( request.getVersionNumber() == 1 && !ticket.getEncTicketPart().getFlags().isInitial() )
+        {
+            throw new ChangePasswordException( ErrorType.KRB5_KPASSWD_INITIAL_FLAG_NEEDED );
+        }
+
+        changepwContext.setAuthenticator( authenticator );
+    }
+    
+    
+    private static void extractPassword( ChangePasswordContext changepwContext ) throws KerberosException, IOException
+    {
+        ChangePasswordRequest request = ( ChangePasswordRequest ) changepwContext.getRequest();
+        Authenticator authenticator = changepwContext.getAuthenticator();
+        CipherTextHandler cipherTextHandler = changepwContext.getCipherTextHandler();
+
+        // TODO - check ticket is for service authorized to change passwords
+        // ticket.getServerPrincipal().getName().equals(config.getChangepwPrincipal().getName()));
+
+        // TODO - check client principal in ticket is authorized to change password
+
+        // get the subsession key from the Authenticator
+        EncryptionKey subSessionKey = authenticator.getSubSessionKey();
+
+        // decrypt the request's private message with the subsession key
+        EncryptedData encReqPrivPart = request.getPrivateMessage().getEncryptedPart();
+
+        EncKrbPrivPart privatePart;
+
+        try
+        {
+            privatePart = ( EncKrbPrivPart ) cipherTextHandler.unseal( EncKrbPrivPart.class, subSessionKey,
+                encReqPrivPart, KeyUsage.NUMBER13 );
+        }
+        catch ( KerberosException ke )
+        {
+            throw new ChangePasswordException( ErrorType.KRB5_KPASSWD_SOFTERROR, ke );
+        }
+
+        ChangePasswordData passwordData = null;
+
+        if ( request.getVersionNumber() == ( short ) 1 )
+        {
+            // Use protocol version 0x0001, the legacy Kerberos change password protocol
+            ChangePasswordDataModifier modifier = new ChangePasswordDataModifier();
+            modifier.setNewPassword( privatePart.getUserData() );
+            passwordData = modifier.getChangePasswdData();
+        }
+        else
+        {
+            // Use protocol version 0xFF80, the backwards-compatible MS protocol
+            ChangePasswordDataDecoder passwordDecoder = new ChangePasswordDataDecoder();
+            passwordData = passwordDecoder.decodeChangePasswordData( privatePart.getUserData() );
+        }
+
+        try
+        {
+            changepwContext.setPassword( new String( passwordData.getPassword(), "UTF-8" ) );
+        }
+        catch ( UnsupportedEncodingException uee )
+        {
+            throw new ChangePasswordException( ErrorType.KRB5_KPASSWD_SOFTERROR, uee );
+        }
+    }
+
+    
+    private static void monitorContext( ChangePasswordContext changepwContext ) throws KerberosException
+    {
+        try
+        {
+            PrincipalStore store = changepwContext.getStore();
+            ApplicationRequest authHeader = changepwContext.getAuthHeader();
+            Ticket ticket = changepwContext.getTicket();
+            ReplayCache replayCache = changepwContext.getReplayCache();
+            long clockSkew = changepwContext.getConfig().getAllowableClockSkew();
+
+            Authenticator authenticator = changepwContext.getAuthenticator();
+            KerberosPrincipal clientPrincipal = authenticator.getClientPrincipal();
+            String desiredPassword = changepwContext.getPassword();
+
+            InetAddress clientAddress = changepwContext.getClientAddress();
+            HostAddresses clientAddresses = ticket.getEncTicketPart().getClientAddresses();
+
+            boolean caddrContainsSender = false;
+
+            if ( ticket.getEncTicketPart().getClientAddresses() != null )
+            {
+                caddrContainsSender = ticket.getEncTicketPart().getClientAddresses().contains( new HostAddress( clientAddress ) );
+            }
+
+            StringBuffer sb = new StringBuffer();
+            sb.append( "Monitoring context:" );
+            sb.append( "\n\t" + "store                  " + store );
+            sb.append( "\n\t" + "authHeader             " + authHeader );
+            sb.append( "\n\t" + "ticket                 " + ticket );
+            sb.append( "\n\t" + "replayCache            " + replayCache );
+            sb.append( "\n\t" + "clockSkew              " + clockSkew );
+            sb.append( "\n\t" + "clientPrincipal        " + clientPrincipal );
+            sb.append( "\n\t" + "desiredPassword        " + desiredPassword );
+            sb.append( "\n\t" + "clientAddress          " + clientAddress );
+            sb.append( "\n\t" + "clientAddresses        " + clientAddresses );
+            sb.append( "\n\t" + "caddr contains sender  " + caddrContainsSender );
+            sb.append( "\n\t" + "Ticket principal       " + ticket.getServerPrincipal() );
+
+            PrincipalStoreEntry ticketPrincipal = changepwContext.getServerEntry();
+            
+            sb.append( "\n\t" + "cn                     " + ticketPrincipal.getCommonName() );
+            sb.append( "\n\t" + "realm                  " + ticketPrincipal.getRealmName() );
+            sb.append( "\n\t" + "Service principal      " + ticketPrincipal.getPrincipal() );
+            sb.append( "\n\t" + "SAM type               " + ticketPrincipal.getSamType() );
+
+            EncryptionType encryptionType = ticket.getEncPart().getEType();
+            int keyVersion = ticketPrincipal.getKeyMap().get( encryptionType ).getKeyVersion();
+            sb.append( "\n\t" + "Ticket key type        " + encryptionType );
+            sb.append( "\n\t" + "Service key version    " + keyVersion );
+
+            LOG.debug( sb.toString() );
+        }
+        catch ( Exception e )
+        {
+            // This is a monitor.  No exceptions should bubble up.
+            LOG.error( "Error in context monitor", e );
+        }
+    }
+    
+    
+    private static void buildReply( ChangePasswordContext changepwContext ) throws KerberosException, UnknownHostException
+    {
+        Authenticator authenticator = changepwContext.getAuthenticator();
+        Ticket ticket = changepwContext.getTicket();
+        CipherTextHandler cipherTextHandler = changepwContext.getCipherTextHandler();
+
+        // begin building reply
+
+        // create priv message
+        // user-data component is short result code
+        EncKrbPrivPartModifier modifier = new EncKrbPrivPartModifier();
+        byte[] resultCode =
+            { ( byte ) 0x00, ( byte ) 0x00 };
+        modifier.setUserData( resultCode );
+
+        modifier.setSenderAddress( new HostAddress( InetAddress.getLocalHost() ) );
+        EncKrbPrivPart privPart = modifier.getEncKrbPrivPart();
+
+        // get the subsession key from the Authenticator
+        EncryptionKey subSessionKey = authenticator.getSubSessionKey();
+
+        EncryptedData encPrivPart;
+
+        try
+        {
+            encPrivPart = cipherTextHandler.seal( subSessionKey, privPart, KeyUsage.NUMBER13 );
+        }
+        catch ( KerberosException ke )
+        {
+            throw new ChangePasswordException( ErrorType.KRB5_KPASSWD_SOFTERROR, ke );
+        }
+
+        PrivateMessage privateMessage = new PrivateMessage( encPrivPart );
+
+        // Begin AP_REP generation
+        EncApRepPartModifier encApModifier = new EncApRepPartModifier();
+        encApModifier.setClientTime( authenticator.getClientTime() );
+        encApModifier.setClientMicroSecond( authenticator.getClientMicroSecond() );
+        encApModifier.setSequenceNumber( new Integer( authenticator.getSequenceNumber() ) );
+        encApModifier.setSubSessionKey( authenticator.getSubSessionKey() );
+
+        EncApRepPart repPart = encApModifier.getEncApRepPart();
+
+        EncryptedData encRepPart;
+
+        try
+        {
+            encRepPart = cipherTextHandler.seal( ticket.getEncTicketPart().getSessionKey(), repPart, KeyUsage.NUMBER12 );
+        }
+        catch ( KerberosException ke )
+        {
+            throw new ChangePasswordException( ErrorType.KRB5_KPASSWD_SOFTERROR, ke );
+        }
+
+        ApplicationReply appReply = new ApplicationReply( encRepPart );
+
+        // return status message value object
+        ChangePasswordReplyModifier replyModifier = new ChangePasswordReplyModifier();
+        replyModifier.setApplicationReply( appReply );
+        replyModifier.setPrivateMessage( privateMessage );
+
+        changepwContext.setReply( replyModifier.getChangePasswordReply() );
+    }
+
+    
+    private static void monitorReply( ChangePasswordContext changepwContext ) throws KerberosException
+    {
+        try
+        {
+            ChangePasswordReply reply = ( ChangePasswordReply ) changepwContext.getReply();
+            ApplicationReply appReply = reply.getApplicationReply();
+            PrivateMessage priv = reply.getPrivateMessage();
+
+            StringBuilder sb = new StringBuilder();
+            sb.append( "Responding with change password reply:" );
+            sb.append( "\n\t" + "appReply               " + appReply );
+            sb.append( "\n\t" + "priv                   " + priv );
+
+            LOG.debug( sb.toString() );
+        }
+        catch ( Exception e )
+        {
+            // This is a monitor.  No exceptions should bubble up.
+            LOG.error( "Error in reply monitor", e );
+        }
+    }
+}
diff --git a/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/service/CheckPasswordPolicy.java b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/service/CheckPasswordPolicy.java
new file mode 100644
index 0000000..c8e09c8
--- /dev/null
+++ b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/service/CheckPasswordPolicy.java
@@ -0,0 +1,229 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.changepw.service;
+
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.apache.directory.server.changepw.ChangePasswordServer;
+import org.apache.directory.server.changepw.exceptions.ChangePasswordException;
+import org.apache.directory.server.changepw.exceptions.ErrorType;
+import org.apache.directory.server.kerberos.shared.messages.components.Authenticator;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.handler.chain.IoHandlerCommand;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * A basic password policy check using well-established methods.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class CheckPasswordPolicy implements IoHandlerCommand
+{
+    /** the log for this class */
+    private static final Logger log = LoggerFactory.getLogger( CheckPasswordPolicy.class );
+
+    private String contextKey = "context";
+
+
+    public void execute( NextCommand next, IoSession session, Object message ) throws Exception
+    {
+        ChangePasswordContext changepwContext = ( ChangePasswordContext ) session.getAttribute( getContextKey() );
+
+        ChangePasswordServer config = changepwContext.getConfig();
+        Authenticator authenticator = changepwContext.getAuthenticator();
+        KerberosPrincipal clientPrincipal = authenticator.getClientPrincipal();
+
+        String password = changepwContext.getPassword();
+        String username = clientPrincipal.getName();
+
+        int passwordLength = config.getPasswordLengthPolicy();
+        int categoryCount = config.getCategoryCountPolicy();
+        int tokenSize = config.getTokenSizePolicy();
+
+        if ( !isValid( username, password, passwordLength, categoryCount, tokenSize ) )
+        {
+            String explanation = buildErrorMessage( username, password, passwordLength, categoryCount, tokenSize );
+            log.error( explanation );
+
+            byte[] explanatoryData = explanation.getBytes( "UTF-8" );
+
+            throw new ChangePasswordException( ErrorType.KRB5_KPASSWD_SOFTERROR, explanatoryData );
+        }
+
+        next.execute( session, message );
+    }
+
+
+    /**
+     * Tests that:
+     * The password is at least six characters long.
+     * The password contains a mix of characters.
+     * The password does not contain three letter (or more) tokens from the user's account name.
+     */
+    boolean isValid( String username, String password, int passwordLength, int categoryCount, int tokenSize )
+    {
+        return isValidPasswordLength( password, passwordLength ) && isValidCategoryCount( password, categoryCount )
+            && isValidUsernameSubstring( username, password, tokenSize );
+    }
+
+
+    /**
+     * The password is at least six characters long.
+     */
+    boolean isValidPasswordLength( String password, int passwordLength )
+    {
+        return password.length() >= passwordLength;
+    }
+
+
+    /**
+     * The password contains characters from at least three of the following four categories:
+     * English uppercase characters (A - Z)
+     * English lowercase characters (a - z)
+     * Base 10 digits (0 - 9)
+     * Any non-alphanumeric character (for example: !, $, #, or %)
+     */
+    boolean isValidCategoryCount( String password, int categoryCount )
+    {
+        int uppercase = 0;
+        int lowercase = 0;
+        int digit = 0;
+        int nonAlphaNumeric = 0;
+
+        char[] characters = password.toCharArray();
+
+        for ( int ii = 0; ii < characters.length; ii++ )
+        {
+            if ( Character.isLowerCase( characters[ii] ) )
+            {
+                lowercase = 1;
+            }
+            else
+            {
+                if ( Character.isUpperCase( characters[ii] ) )
+                {
+                    uppercase = 1;
+                }
+                else
+                {
+                    if ( Character.isDigit( characters[ii] ) )
+                    {
+                        digit = 1;
+                    }
+                    else
+                    {
+                        if ( !Character.isLetterOrDigit( characters[ii] ) )
+                        {
+                            nonAlphaNumeric = 1;
+                        }
+                    }
+                }
+            }
+        }
+
+        return ( uppercase + lowercase + digit + nonAlphaNumeric ) >= categoryCount;
+    }
+
+
+    /**
+     * The password does not contain three letter (or more) tokens from the user's account name.
+     * 
+     * If the account name is less than three characters long, this check is not performed
+     * because the rate at which passwords would be rejected is too high. For each token that is
+     * three or more characters long, that token is searched for in the password; if it is present,
+     * the password change is rejected. For example, the name "First M. Last" would be split into
+     * three tokens: "First", "M", and "Last". Because the second token is only one character long,
+     * it would be ignored. Therefore, this user could not have a password that included either
+     * "first" or "last" as a substring anywhere in the password. All of these checks are
+     * case-insensitive.
+     */
+    boolean isValidUsernameSubstring( String username, String password, int tokenSize )
+    {
+        String[] tokens = username.split( "[^a-zA-Z]" );
+
+        for ( int ii = 0; ii < tokens.length; ii++ )
+        {
+            if ( tokens[ii].length() >= tokenSize )
+            {
+                if ( password.matches( "(?i).*" + tokens[ii] + ".*" ) )
+                {
+                    return false;
+                }
+            }
+        }
+
+        return true;
+    }
+
+
+    private String buildErrorMessage( String username, String password, int passwordLength, int categoryCount,
+        int tokenSize )
+    {
+        List<String> violations = new ArrayList<String>();
+
+        if ( !isValidPasswordLength( password, passwordLength ) )
+        {
+            violations.add( "length too short" );
+        }
+
+        if ( !isValidCategoryCount( password, categoryCount ) )
+        {
+            violations.add( "insufficient character mix" );
+        }
+
+        if ( !isValidUsernameSubstring( username, password, tokenSize ) )
+        {
+            violations.add( "contains portions of username" );
+        }
+
+        StringBuffer sb = new StringBuffer( "Password violates policy:  " );
+
+        boolean isFirst = true;
+
+        for ( String violation : violations )
+        {
+            if ( isFirst )
+            {
+                isFirst = false;
+            }
+            else
+            {
+                sb.append( ", " );
+            }
+
+            sb.append( violation );
+        }
+
+        return sb.toString();
+    }
+
+
+    protected String getContextKey()
+    {
+        return ( this.contextKey );
+    }
+}
diff --git a/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/service/package-info.java b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/service/package-info.java
new file mode 100644
index 0000000..fd4a827
--- /dev/null
+++ b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/service/package-info.java
@@ -0,0 +1,28 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+/**
+ * Provides the Change Password Service.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+
+package org.apache.directory.server.changepw.service;
diff --git a/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/value/ChangePasswordData.java b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/value/ChangePasswordData.java
new file mode 100644
index 0000000..909f98a
--- /dev/null
+++ b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/value/ChangePasswordData.java
@@ -0,0 +1,83 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.changepw.value;
+
+
+import org.apache.directory.server.kerberos.shared.messages.value.PrincipalName;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ChangePasswordData
+{
+    private byte[] password;
+    private PrincipalName principalName;
+    private String realm;
+
+
+    /**
+     * Creates a new instance of ChangePasswordData.
+     *
+     * @param password
+     * @param principalName
+     * @param realm
+     */
+    public ChangePasswordData( byte[] password, PrincipalName principalName, String realm )
+    {
+        this.password = password;
+        this.principalName = principalName;
+        this.realm = realm;
+    }
+
+
+    /**
+     * Returns the password as bytes.
+     *
+     * @return The password as bytes.
+     */
+    public byte[] getPassword()
+    {
+        return password;
+    }
+
+
+    /**
+     * Returns the principal name.
+     *
+     * @return The principal name.
+     */
+    public PrincipalName getPrincipalName()
+    {
+        return principalName;
+    }
+
+
+    /**
+     * Returns the realm.
+     *
+     * @return The realm.
+     */
+    public String getRealm()
+    {
+        return realm;
+    }
+}
diff --git a/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/value/ChangePasswordDataModifier.java b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/value/ChangePasswordDataModifier.java
new file mode 100644
index 0000000..cfc8728
--- /dev/null
+++ b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/value/ChangePasswordDataModifier.java
@@ -0,0 +1,79 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.changepw.value;
+
+
+import org.apache.directory.server.kerberos.shared.messages.value.PrincipalName;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ChangePasswordDataModifier
+{
+    private byte[] password;
+    private PrincipalName principalName;
+    private String realm;
+
+
+    /**
+     * Returns the {@link ChangePasswordData}.
+     *
+     * @return The {@link ChangePasswordData}.
+     */
+    public ChangePasswordData getChangePasswdData()
+    {
+        return new ChangePasswordData( password, principalName, realm );
+    }
+
+
+    /**
+     * Sets the bytes of the new password.
+     *
+     * @param password
+     */
+    public void setNewPassword( byte[] password )
+    {
+        this.password = password;
+    }
+
+
+    /**
+     * Sets the target principal name whose password is to be changed.
+     *
+     * @param principalName
+     */
+    public void setTargetName( PrincipalName principalName )
+    {
+        this.principalName = principalName;
+    }
+
+
+    /**
+     * Sets the target realm of the principal whose password is to be changed.
+     *
+     * @param realm
+     */
+    public void setTargetRealm( String realm )
+    {
+        this.realm = realm;
+    }
+}
diff --git a/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/value/package-info.java b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/value/package-info.java
new file mode 100644
index 0000000..1a61ea1
--- /dev/null
+++ b/old_trunk/protocol-changepw/src/main/java/org/apache/directory/server/changepw/value/package-info.java
@@ -0,0 +1,28 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+/**
+ * Provides the message object for the Change Password data payload.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+
+package org.apache.directory.server.changepw.value;
diff --git a/old_trunk/protocol-changepw/src/site/site.xml b/old_trunk/protocol-changepw/src/site/site.xml
new file mode 100644
index 0000000..9ec7c16
--- /dev/null
+++ b/old_trunk/protocol-changepw/src/site/site.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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.
+-->
+<!--
+  @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+-->
+<project name="${project.name}">
+    <bannerLeft>
+        <name>${project.name}</name>
+    </bannerLeft>
+    <publishDate position="navigation-bottom" format="dd-MM-yyyy HH:mm" />
+    <body>
+        <menu name="Parent">
+            <item name="Apache Directory ApacheDS" href="../index.html" />
+        </menu>
+        <menu ref="reports" />
+    </body>
+</project>
diff --git a/old_trunk/protocol-changepw/src/test/java/org/apache/directory/server/changepw/protocol/ChangepwProtocolHandlerTest.java b/old_trunk/protocol-changepw/src/test/java/org/apache/directory/server/changepw/protocol/ChangepwProtocolHandlerTest.java
new file mode 100644
index 0000000..132a3cc
--- /dev/null
+++ b/old_trunk/protocol-changepw/src/test/java/org/apache/directory/server/changepw/protocol/ChangepwProtocolHandlerTest.java
@@ -0,0 +1,422 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.changepw.protocol;
+
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.net.UnknownHostException;
+
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import junit.framework.TestCase;
+import org.apache.directory.server.changepw.ChangePasswordServer;
+import org.apache.directory.server.changepw.io.ChangePasswordDataEncoder;
+import org.apache.directory.server.changepw.messages.ChangePasswordError;
+import org.apache.directory.server.changepw.messages.ChangePasswordRequest;
+import org.apache.directory.server.changepw.value.ChangePasswordData;
+import org.apache.directory.server.changepw.value.ChangePasswordDataModifier;
+import org.apache.directory.server.kerberos.shared.KerberosMessageType;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.CipherTextHandler;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.KeyUsage;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.RandomKeyFactory;
+import org.apache.directory.server.kerberos.shared.exceptions.KerberosException;
+import org.apache.directory.server.kerberos.shared.messages.ApplicationRequest;
+import org.apache.directory.server.kerberos.shared.messages.ErrorMessage;
+import org.apache.directory.server.kerberos.shared.messages.application.PrivateMessage;
+import org.apache.directory.server.kerberos.shared.messages.components.AuthenticatorModifier;
+import org.apache.directory.server.kerberos.shared.messages.components.EncKrbPrivPart;
+import org.apache.directory.server.kerberos.shared.messages.components.EncKrbPrivPartModifier;
+import org.apache.directory.server.kerberos.shared.messages.components.Ticket;
+import org.apache.directory.server.kerberos.shared.messages.value.ApOptions;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptedData;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+import org.apache.directory.server.kerberos.shared.messages.value.HostAddress;
+import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
+import org.apache.directory.server.kerberos.shared.messages.value.PrincipalName;
+import org.apache.directory.server.kerberos.shared.messages.value.types.PrincipalNameType;
+import org.apache.directory.server.kerberos.shared.store.PrincipalStore;
+import org.apache.directory.server.kerberos.shared.store.TicketFactory;
+import org.apache.mina.common.IoFilterChain;
+import org.apache.mina.common.IoHandler;
+import org.apache.mina.common.IoService;
+import org.apache.mina.common.IoServiceConfig;
+import org.apache.mina.common.IoSessionConfig;
+import org.apache.mina.common.TransportType;
+import org.apache.mina.common.WriteFuture;
+import org.apache.mina.common.support.BaseIoSession;
+
+
+/**
+ * Tests the ChangePasswordProtocolHandler.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ChangepwProtocolHandlerTest extends TestCase
+{
+    /**
+     * The Change Password SUCCESS result code.
+     */
+    // Never used...
+    //private static final byte[] SUCCESS = new byte[]
+    //    { ( byte ) 0x00, ( byte ) 0x00 };
+
+    private ChangePasswordServer config;
+    private PrincipalStore store;
+    private ChangePasswordProtocolHandler handler;
+    private DummySession session;
+
+    private CipherTextHandler cipherTextHandler = new CipherTextHandler();
+
+
+    /**
+     * Creates a new instance of ChangepwProtocolHandlerTest.
+     */
+    public ChangepwProtocolHandlerTest()
+    {
+        config = new ChangePasswordServer();
+        store = new MapPrincipalStoreImpl();
+        handler = new ChangePasswordProtocolHandler( config, store );
+        session = new DummySession();
+    }
+
+
+    /**
+     * Tests the protocol version number, which must be '1'.
+     */
+    public void testProtocolVersionNumber()
+    {
+        ChangePasswordRequest message = new ChangePasswordRequest( ( short ) 2, null, null );
+
+        handler.messageReceived( session, message );
+
+        ChangePasswordError reply = ( ChangePasswordError ) session.getMessage();
+        ErrorMessage error = reply.getErrorMessage();
+        assertEquals( "Protocol version unsupported", 6, error.getErrorCode() );
+    }
+
+
+    /**
+     * Tests when a service ticket is missing that the request is rejected with
+     * the correct error message.
+     */
+    public void testMissingTicket()
+    {
+        ChangePasswordRequest message = new ChangePasswordRequest( ( short ) 1, null, null );
+
+        handler.messageReceived( session, message );
+
+        ChangePasswordError reply = ( ChangePasswordError ) session.getMessage();
+        ErrorMessage error = reply.getErrorMessage();
+        assertEquals( "Request failed due to an error in authentication processing", 3, error.getErrorCode() );
+    }
+
+
+    /**
+     * Tests when the INITIAL flag is missing that the request is rejected with
+     * the correct error message.
+     *
+     * @throws Exception
+     */
+    public void testInitialFlagRequired() throws Exception
+    {
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+
+        KerberosPrincipal serverPrincipal = new KerberosPrincipal( "kadmin/changepw@EXAMPLE.COM" );
+        String serverPassword = "secret";
+
+        TicketFactory ticketFactory = new TicketFactory();
+        EncryptionKey serverKey = ticketFactory.getServerKey( serverPrincipal, serverPassword );
+        Ticket serviceTicket = ticketFactory.getTicket( clientPrincipal, serverPrincipal, serverKey );
+
+        EncryptionKey subSessionKey = RandomKeyFactory.getRandomKey( EncryptionType.DES_CBC_MD5 );
+
+        ApOptions apOptions = new ApOptions();
+
+        AuthenticatorModifier modifier = new AuthenticatorModifier();
+        modifier.setVersionNumber( 5 );
+        modifier.setClientRealm( "EXAMPLE.COM" );
+        modifier.setClientName( getPrincipalName( "hnelson" ) );
+        modifier.setClientTime( new KerberosTime() );
+        modifier.setClientMicroSecond( 0 );
+
+        modifier.setSubSessionKey( subSessionKey );
+
+        EncryptedData encryptedAuthenticator = cipherTextHandler.seal( serviceTicket.getEncTicketPart().getSessionKey(), modifier
+                .getAuthenticator(), KeyUsage.NUMBER11 );
+
+        ApplicationRequest apReq = new ApplicationRequest( apOptions, serviceTicket, encryptedAuthenticator );
+
+        String newPassword = "secretsecret";
+
+        PrivateMessage priv = getChangePasswordPrivateMessage( newPassword, subSessionKey );
+
+        ChangePasswordRequest message = new ChangePasswordRequest( ( short ) 1, apReq, priv );
+
+        handler.messageReceived( session, message );
+
+        ChangePasswordError reply = ( ChangePasswordError ) session.getMessage();
+        ErrorMessage error = reply.getErrorMessage();
+        assertEquals( "Initial flag required", 7, error.getErrorCode() );
+
+        //ChangePasswordReply reply = ( ChangePasswordReply ) session.getMessage();
+
+        //processChangePasswordReply( reply, serviceTicket.getSessionKey(), subSessionKey );
+    }
+
+    /**
+     * TODO : Check if this method is important or not. It was called in
+     * the testInitialFlagRequired() method above, but this call has been commented
+     * private void processChangePasswordReply( ChangePasswordReply reply, EncryptionKey sessionKey,
+     * EncryptionKey subSessionKey ) throws Exception
+     * {
+     * PrivateMessage privateMessage = reply.getPrivateMessage();
+     * <p/>
+     * EncryptedData encPrivPart = privateMessage.getEncryptedPart();
+     * <p/>
+     * EncKrbPrivPart privPart;
+     * <p/>
+     * try
+     * {
+     * privPart = ( EncKrbPrivPart ) cipherTextHandler.unseal( EncKrbPrivPart.class, subSessionKey, encPrivPart,
+     * KeyUsage.NUMBER13 );
+     * }
+     * catch ( KerberosException ke )
+     * {
+     * return;
+     * }
+     * <p/>
+     * // Verify result code.
+     * byte[] resultCode = privPart.getUserData();
+     * <p/>
+     * assertTrue( "Password change returned SUCCESS (0x00 0x00).", Arrays.equals( SUCCESS, resultCode ) );
+     * }
+     */
+
+
+    public void testSetPassword() throws Exception
+    {
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+
+        KerberosPrincipal serverPrincipal = new KerberosPrincipal( "kadmin/changepw@EXAMPLE.COM" );
+        String serverPassword = "secret";
+
+        TicketFactory ticketFactory = new TicketFactory();
+        EncryptionKey serverKey = ticketFactory.getServerKey( serverPrincipal, serverPassword );
+        Ticket serviceTicket = ticketFactory.getTicket( clientPrincipal, serverPrincipal, serverKey );
+
+        EncryptionKey subSessionKey = RandomKeyFactory.getRandomKey( EncryptionType.DES_CBC_MD5 );
+
+        ApOptions apOptions = new ApOptions();
+
+        AuthenticatorModifier modifier = new AuthenticatorModifier();
+        modifier.setVersionNumber( 5 );
+        modifier.setClientRealm( "EXAMPLE.COM" );
+        modifier.setClientName( getPrincipalName( "hnelson" ) );
+        modifier.setClientTime( new KerberosTime() );
+        modifier.setClientMicroSecond( 0 );
+
+        EncryptedData encryptedAuthenticator = cipherTextHandler.seal( serverKey, modifier.getAuthenticator(),
+                KeyUsage.NUMBER11 );
+
+        ApplicationRequest apReq = new ApplicationRequest( apOptions, serviceTicket, encryptedAuthenticator );
+
+        String newPassword = "secretsecret";
+
+        PrivateMessage priv = getSetPasswordPrivateMessage( newPassword, subSessionKey, getPrincipalName( "hnelson" ) );
+
+        ChangePasswordRequest message = new ChangePasswordRequest( ( short ) 0xFF80, apReq, priv );
+
+        handler.messageReceived( session, message );
+
+        ChangePasswordError reply = ( ChangePasswordError ) session.getMessage();
+        ErrorMessage error = reply.getErrorMessage();
+        assertEquals( "Protocol version unsupported", 6, error.getErrorCode() );
+    }
+
+
+    /*
+     * Legacy kpasswd (Change Password) version.  User data is the password bytes.
+     */
+    private PrivateMessage getChangePasswordPrivateMessage( String newPassword, EncryptionKey subSessionKey )
+            throws UnsupportedEncodingException, KerberosException, UnknownHostException
+    {
+        // Make private message part.
+        EncKrbPrivPartModifier privPartModifier = new EncKrbPrivPartModifier();
+        privPartModifier.setUserData( newPassword.getBytes( "UTF-8" ) );
+        privPartModifier.setSenderAddress( new HostAddress( InetAddress.getLocalHost() ) );
+        EncKrbPrivPart encReqPrivPart = privPartModifier.getEncKrbPrivPart();
+
+        // Seal private message part.
+        EncryptedData encryptedPrivPart = cipherTextHandler.seal( subSessionKey, encReqPrivPart, KeyUsage.NUMBER13 );
+
+        // Make private message with private message part.
+        PrivateMessage privateMessage = new PrivateMessage();
+        privateMessage.setProtocolVersionNumber( 5 );
+        privateMessage.setMessageType( KerberosMessageType.ENC_PRIV_PART );
+        privateMessage.setEncryptedPart( encryptedPrivPart );
+
+        return privateMessage;
+    }
+
+
+    /*
+     * Set/Change Password version.  User data is an encoding of the new password and the target principal.
+     */
+    private PrivateMessage getSetPasswordPrivateMessage( String newPassword, EncryptionKey subSessionKey,
+            PrincipalName targetPrincipalName ) throws UnsupportedEncodingException, KerberosException,
+            UnknownHostException, IOException
+    {
+        // Make private message part.
+        EncKrbPrivPartModifier privPartModifier = new EncKrbPrivPartModifier();
+
+        ChangePasswordDataModifier dataModifier = new ChangePasswordDataModifier();
+        dataModifier.setNewPassword( newPassword.getBytes() );
+        dataModifier.setTargetName( targetPrincipalName );
+        dataModifier.setTargetRealm( "EXAMPLE.COM" );
+        ChangePasswordData data = dataModifier.getChangePasswdData();
+
+        ChangePasswordDataEncoder encoder = new ChangePasswordDataEncoder();
+        byte[] dataBytes = encoder.encode( data );
+
+        privPartModifier.setUserData( dataBytes );
+
+        privPartModifier.setSenderAddress( new HostAddress( InetAddress.getLocalHost() ) );
+        EncKrbPrivPart encReqPrivPart = privPartModifier.getEncKrbPrivPart();
+
+        // Seal private message part.
+        EncryptedData encryptedPrivPart = cipherTextHandler.seal( subSessionKey, encReqPrivPart, KeyUsage.NUMBER13 );
+
+        // Make private message with private message part.
+        PrivateMessage privateMessage = new PrivateMessage();
+        privateMessage.setProtocolVersionNumber( 5 );
+        privateMessage.setMessageType( KerberosMessageType.ENC_PRIV_PART );
+        privateMessage.setEncryptedPart( encryptedPrivPart );
+
+        return privateMessage;
+    }
+
+
+    private PrincipalName getPrincipalName( String name )
+    {
+        PrincipalName principalName = new PrincipalName();
+        principalName.addName( name );
+        principalName.setNameType( PrincipalNameType.KRB_NT_PRINCIPAL );
+
+        return principalName;
+    }
+
+    private static class DummySession extends BaseIoSession
+    {
+        Object message;
+
+
+        @Override
+        public WriteFuture write( Object message )
+        {
+            this.message = message;
+
+            return super.write( message );
+        }
+
+
+        private Object getMessage()
+        {
+            return message;
+        }
+
+
+        protected void updateTrafficMask()
+        {
+            // Do nothing.
+        }
+
+
+        public IoService getService()
+        {
+            return null;
+        }
+
+
+        public IoHandler getHandler()
+        {
+            return null;
+        }
+
+
+        public IoFilterChain getFilterChain()
+        {
+            return null;
+        }
+
+
+        public TransportType getTransportType()
+        {
+            return null;
+        }
+
+
+        public SocketAddress getRemoteAddress()
+        {
+            return new InetSocketAddress( 10464 );
+        }
+
+
+        public SocketAddress getLocalAddress()
+        {
+            return null;
+        }
+
+
+        public IoSessionConfig getConfig()
+        {
+            return null;
+        }
+
+
+        public int getScheduledWriteRequests()
+        {
+            return 0;
+        }
+
+
+        public SocketAddress getServiceAddress()
+        {
+            return null;
+        }
+
+
+        public IoServiceConfig getServiceConfig()
+        {
+            return null;
+        }
+
+
+        public int getScheduledWriteBytes()
+        {
+            return 0;
+        }
+    }
+}
diff --git a/old_trunk/protocol-changepw/src/test/java/org/apache/directory/server/changepw/protocol/MapPrincipalStoreImpl.java b/old_trunk/protocol-changepw/src/test/java/org/apache/directory/server/changepw/protocol/MapPrincipalStoreImpl.java
new file mode 100644
index 0000000..133f313
--- /dev/null
+++ b/old_trunk/protocol-changepw/src/test/java/org/apache/directory/server/changepw/protocol/MapPrincipalStoreImpl.java
@@ -0,0 +1,111 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.changepw.protocol;
+
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.KerberosKeyFactory;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+import org.apache.directory.server.kerberos.shared.store.PrincipalStore;
+import org.apache.directory.server.kerberos.shared.store.PrincipalStoreEntry;
+import org.apache.directory.server.kerberos.shared.store.PrincipalStoreEntryModifier;
+
+
+/**
+ * An implementation of {@link PrincipalStore} that is backed by a {@link Map}.  This
+ * store implements both getPrincipal and changePassword, as required by the Set/Change Password
+ * service.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class MapPrincipalStoreImpl implements PrincipalStore
+{
+    private static Map<KerberosPrincipal, PrincipalStoreEntry> store = new HashMap<KerberosPrincipal, PrincipalStoreEntry>();
+
+    static
+    {
+        String principalName = "hnelson@EXAMPLE.COM";
+        String passPhrase = "secret";
+
+        PrincipalStoreEntry entry = getEntry( principalName, passPhrase );
+
+        store.put( entry.getPrincipal(), entry );
+
+        principalName = "kadmin/changepw@EXAMPLE.COM";
+        passPhrase = "secret";
+
+        entry = getEntry( principalName, passPhrase );
+
+        store.put( entry.getPrincipal(), entry );
+    }
+
+
+    public PrincipalStoreEntry getPrincipal( KerberosPrincipal principal ) throws Exception
+    {
+        PrincipalStoreEntry entry = store.get( principal );
+
+        return entry;
+    }
+
+
+    public String changePassword( KerberosPrincipal principal, String newPassword ) throws Exception
+    {
+        return principal.getName();
+    }
+
+
+    public String addPrincipal( PrincipalStoreEntry entry ) throws Exception
+    {
+        return null;
+    }
+
+
+    public String deletePrincipal( KerberosPrincipal principal ) throws Exception
+    {
+        return null;
+    }
+
+
+    public PrincipalStoreEntry[] getAllPrincipals( String realm ) throws Exception
+    {
+        return null;
+    }
+
+
+    private static PrincipalStoreEntry getEntry( String principalName, String passPhrase )
+    {
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( principalName );
+
+        PrincipalStoreEntryModifier modifier = new PrincipalStoreEntryModifier();
+        modifier.setPrincipal( clientPrincipal );
+
+        Map<EncryptionType, EncryptionKey> keyMap = KerberosKeyFactory.getKerberosKeys( principalName, passPhrase );
+
+        modifier.setKeyMap( keyMap );
+
+        return modifier.getEntry();
+    }
+}
diff --git a/old_trunk/protocol-changepw/src/test/java/org/apache/directory/server/changepw/service/CheckPasswordPolicyTest.java b/old_trunk/protocol-changepw/src/test/java/org/apache/directory/server/changepw/service/CheckPasswordPolicyTest.java
new file mode 100644
index 0000000..66611e6
--- /dev/null
+++ b/old_trunk/protocol-changepw/src/test/java/org/apache/directory/server/changepw/service/CheckPasswordPolicyTest.java
@@ -0,0 +1,86 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.changepw.service;
+
+
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import junit.framework.TestCase;
+
+
+/**
+ * Tests {@link CheckPasswordPolicy}.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class CheckPasswordPolicyTest extends TestCase
+{
+    private int passwordLength = 6;
+    private int categoryCount = 3;
+    private int tokenSize = 3;
+
+    private CheckPasswordPolicy policy = new CheckPasswordPolicy();
+
+
+    /**
+     * Tests that a good password is valid according to all policy checks.
+     */
+    public void testGoodPassword()
+    {
+        String username = "Enrique Rodriguez";
+        String password = "d1r3ct0rY";
+        assertTrue( policy.isValidPasswordLength( password, passwordLength ) );
+        assertTrue( policy.isValidCategoryCount( password, categoryCount ) );
+        assertTrue( policy.isValidUsernameSubstring( username, password, tokenSize ) );
+        assertTrue( policy.isValid( username, password, passwordLength, categoryCount, tokenSize ) );
+    }
+
+
+    /**
+     * Tests that a bad password fails all validity checks.
+     */
+    public void testBadPassword()
+    {
+        String username = "Erin Randall";
+        String password = "erin1";
+        assertFalse( policy.isValidPasswordLength( password, passwordLength ) );
+        assertFalse( policy.isValidCategoryCount( password, categoryCount ) );
+        assertFalse( policy.isValidUsernameSubstring( username, password, tokenSize ) );
+        assertFalse( policy.isValid( username, password, passwordLength, categoryCount, tokenSize ) );
+    }
+
+
+    /**
+     * Tests variations of a password where the password includes tokens of the username.
+     */
+    public void testPrincipalAsUsername()
+    {
+        String username = new KerberosPrincipal( "erodriguez@EXAMPLE.COM" ).getName();
+        String password1 = "d1r3ct0rY";
+        String password2 = "ERodriguez@d1r3ct0rY";
+        String password3 = "Example@d1r3ct0rY";
+
+        assertTrue( policy.isValidUsernameSubstring( username, password1, tokenSize ) );
+
+        assertFalse( policy.isValidUsernameSubstring( username, password2, tokenSize ) );
+        assertFalse( policy.isValidUsernameSubstring( username, password3, tokenSize ) );
+    }
+}
diff --git a/old_trunk/protocol-dhcp/pom.xml b/old_trunk/protocol-dhcp/pom.xml
new file mode 100644
index 0000000..7e070ef
--- /dev/null
+++ b/old_trunk/protocol-dhcp/pom.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you 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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.directory.server</groupId>
+    <artifactId>apacheds-parent</artifactId>
+    <version>1.5.4-SNAPSHOT</version>
+  </parent>
+  <artifactId>apacheds-protocol-dhcp</artifactId>
+  <name>ApacheDS Protocol Dhcp</name>
+
+  <description>
+    The DHCP protocol provider for ApacheDS
+  </description>
+
+  <packaging>jar</packaging>  
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.mina</groupId>
+      <artifactId>mina-core</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-protocol-shared</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
+  </dependencies>
+</project>
+
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/DhcpException.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/DhcpException.java
new file mode 100644
index 0000000..cac2144
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/DhcpException.java
@@ -0,0 +1,67 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class DhcpException extends Exception
+{
+    private static final long serialVersionUID = 3985748516732135317L;
+
+
+    /**
+     * This empty constructor is used if no 
+     * explanation of the DHCP exception is required.
+     */
+    public DhcpException()
+    {
+        super();
+    }
+
+
+    /**
+     * This constructor is used if a description of the event
+     * that caused the exception is required.
+     * 
+     * @param description this is a description of the exception
+     */
+    public DhcpException(String description)
+    {
+        super( description );
+    }
+
+
+    /**
+     * This constructor is used if a description of the event that caused the
+     * exception is required.
+     * 
+     * @param description
+     *            this is a description of the exception
+     * @param e
+     */
+    public DhcpException(String description, Exception e)
+    {
+        super( description, e );
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/io/DhcpMessageDecoder.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/io/DhcpMessageDecoder.java
new file mode 100644
index 0000000..f4d4a7d
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/io/DhcpMessageDecoder.java
@@ -0,0 +1,200 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ * 
+ */
+
+package org.apache.directory.server.dhcp.io;
+
+
+import java.io.UnsupportedEncodingException;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+import org.apache.directory.server.dhcp.DhcpException;
+import org.apache.directory.server.dhcp.messages.DhcpMessage;
+import org.apache.directory.server.dhcp.messages.HardwareAddress;
+import org.apache.directory.server.dhcp.options.DhcpOption;
+import org.apache.directory.server.dhcp.options.OptionsField;
+import org.apache.directory.server.dhcp.options.dhcp.DhcpMessageType;
+import org.apache.directory.server.dhcp.options.dhcp.UnrecognizedOption;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class DhcpMessageDecoder
+{
+
+    /**
+     * Convert a byte buffer into a DhcpMessage.
+     * 
+     * @return a DhcpMessage.
+     * @param buffer ByteBuffer to convert to a DhcpMessage object
+     * @throws DhcpException 
+     */
+    public DhcpMessage decode( ByteBuffer buffer ) throws DhcpException
+    {
+        byte op = buffer.get();
+
+        short htype = ( short ) ( buffer.get() & 0xff );
+        short hlen = ( short ) ( buffer.get() & 0xff );
+        short hops = ( short ) ( buffer.get() & 0xff );
+        int xid = buffer.getInt();
+        int secs = buffer.getShort() & 0xffff;
+        short flags = buffer.getShort();
+
+        InetAddress ciaddr = decodeAddress( buffer );
+        InetAddress yiaddr = decodeAddress( buffer );
+        InetAddress siaddr = decodeAddress( buffer );
+        InetAddress giaddr = decodeAddress( buffer );
+
+        byte[] chaddr = decodeBytes( buffer, 16 );
+
+        String sname = decodeString( buffer, 64 );
+        String file = decodeString( buffer, 128 );
+
+        OptionsField options = decodeOptions( buffer );
+
+        // message type option: may be null if option isn't set (BOOTP)
+        DhcpMessageType mto = ( DhcpMessageType ) options.get( DhcpMessageType.class );
+
+        return new DhcpMessage( null != mto ? mto.getType() : null, op, new HardwareAddress( htype, hlen, chaddr ),
+            hops, xid, secs, flags, ciaddr, yiaddr, siaddr, giaddr, sname, file, options );
+    }
+
+
+    /**
+     * @param buffer
+     * @param len
+     * @return
+     */
+    private static byte[] decodeBytes( ByteBuffer buffer, int len )
+    {
+        byte[] bytes = new byte[len];
+        buffer.get( bytes );
+        return bytes;
+    }
+
+
+    /**
+     * @param buffer
+     * @return
+     */
+    private static String decodeString( ByteBuffer buffer, int len )
+    {
+        byte[] bytes = new byte[len];
+        buffer.get( bytes );
+
+        // find zero-terminator
+        int slen = 0;
+        while ( bytes[slen] != 0 )
+            slen++;
+
+        try
+        {
+            return new String( bytes, 0, slen, "ASCII" );
+        }
+        catch ( UnsupportedEncodingException e )
+        {
+            throw new RuntimeException( "No ASCII encoding", e );
+        }
+    }
+
+
+    /**
+     * Read a 4-byte inet address from the buffer.
+     * 
+     * @param buffer
+     * @return
+     * @throws UnknownHostException
+     */
+    private static InetAddress decodeAddress( ByteBuffer buffer )
+    {
+        byte[] addr = new byte[4];
+        buffer.get( addr );
+        try
+        {
+            return InetAddress.getByAddress( addr );
+        }
+        catch ( UnknownHostException e )
+        {
+            // should not happen
+            return null;
+        }
+    }
+
+    private static final byte[] VENDOR_MAGIC_COOKIE =
+        { ( byte ) 99, ( byte ) 130, ( byte ) 83, ( byte ) 99 };
+
+
+    public OptionsField decodeOptions( ByteBuffer message ) throws DhcpException
+    {
+        byte[] magicCookie = new byte[4];
+        message.get( magicCookie );
+
+        if ( !Arrays.equals( VENDOR_MAGIC_COOKIE, magicCookie ) )
+        {
+            throw new DhcpException( "Parse exception." );
+        }
+
+        byte code;
+        byte length;
+        byte value[];
+
+        OptionsField options = new OptionsField();
+
+        while ( true )
+        {
+            code = message.get();
+            if ( code == 0 ) // pad option
+                continue;
+
+            if ( code == -1 ) // end option
+                break;
+
+            length = message.get();
+            value = new byte[length];
+            message.get( value );
+
+            options.add( getOptionInstance( code, value ) );
+        }
+
+        return options;
+    }
+
+
+    private DhcpOption getOptionInstance( int tag, byte[] value ) throws DhcpException
+    {
+        try
+        {
+            Class c = DhcpOption.getClassByTag( tag );
+
+            DhcpOption o = null != c ? ( DhcpOption ) c.newInstance() : new UnrecognizedOption( ( byte ) tag );
+            o.setData( value );
+
+            return o;
+        }
+        catch ( Exception e )
+        {
+            throw new DhcpException( "Can't set option value: " + e.toString() );
+        }
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/io/DhcpMessageEncoder.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/io/DhcpMessageEncoder.java
new file mode 100644
index 0000000..786fc79
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/io/DhcpMessageEncoder.java
@@ -0,0 +1,171 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ * 
+ */
+
+package org.apache.directory.server.dhcp.io;
+
+
+import java.io.UnsupportedEncodingException;
+import java.net.InetAddress;
+import java.nio.ByteBuffer;
+import java.util.Iterator;
+
+import org.apache.directory.server.dhcp.messages.DhcpMessage;
+import org.apache.directory.server.dhcp.messages.HardwareAddress;
+import org.apache.directory.server.dhcp.options.DhcpOption;
+import org.apache.directory.server.dhcp.options.OptionsField;
+import org.apache.directory.server.dhcp.options.dhcp.DhcpMessageType;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class DhcpMessageEncoder
+{
+    /**
+     * Converts a DhcpMessage object into a byte buffer.
+     * 
+     * @param byteBuffer ByteBuffer to put DhcpMessage into
+     * @param message DhcpMessage to encode into ByteBuffer
+     */
+    public void encode( ByteBuffer byteBuffer, DhcpMessage message )
+    {
+        byteBuffer.put( message.getOp() );
+
+        HardwareAddress hardwareAddress = message.getHardwareAddress();
+
+        byteBuffer.put( ( byte ) ( null != hardwareAddress ? hardwareAddress.getType() : 0 ) );
+        byteBuffer.put( ( byte ) ( null != hardwareAddress ? hardwareAddress.getLength() : 0 ) );
+        byteBuffer.put( ( byte ) message.getHopCount() );
+        byteBuffer.putInt( message.getTransactionId() );
+        byteBuffer.putShort( ( short ) message.getSeconds() );
+        byteBuffer.putShort( message.getFlags() );
+
+        writeAddress( byteBuffer, message.getCurrentClientAddress() );
+        writeAddress( byteBuffer, message.getAssignedClientAddress() );
+        writeAddress( byteBuffer, message.getNextServerAddress() );
+        writeAddress( byteBuffer, message.getRelayAgentAddress() );
+
+        writeBytes( byteBuffer, ( null != hardwareAddress ? hardwareAddress.getAddress() : new byte[]
+            {} ), 16 );
+
+        writeString( byteBuffer, message.getServerHostname(), 64 );
+        writeString( byteBuffer, message.getBootFileName(), 128 );
+
+        OptionsField options = message.getOptions();
+
+        // update message type option (if set)
+        if ( null != message.getMessageType() )
+            options.add( new DhcpMessageType( message.getMessageType() ) );
+
+        encodeOptions( options, byteBuffer );
+    }
+
+
+    /**
+     * Write a zero-terminated string to a field of len bytes.
+     * 
+     * @param byteBuffer
+     * @param serverHostname
+     * @param i
+     */
+    private void writeString( ByteBuffer byteBuffer, String string, int len )
+    {
+        if ( null == string )
+            string = "";
+
+        try
+        {
+            byte sbytes[] = string.getBytes( "ASCII" );
+
+            // writeBytes will automatically zero-pad and thus terminate the
+            // string.
+            writeBytes( byteBuffer, sbytes, len );
+        }
+        catch ( UnsupportedEncodingException e )
+        {
+            // should not happen
+            throw new RuntimeException( "No ASCII encoding", e );
+        }
+    }
+
+
+    /**
+     * Write an InetAddress to the byte buffer.
+     * 
+     * @param byteBuffer
+     * @param currentClientAddress
+     */
+    private void writeAddress( ByteBuffer byteBuffer, InetAddress currentClientAddress )
+    {
+        if ( null == currentClientAddress )
+        {
+            byte emptyAddress[] =
+                { 0, 0, 0, 0 };
+            byteBuffer.put( emptyAddress );
+        }
+        else
+        {
+            byte[] addressBytes = currentClientAddress.getAddress();
+            byteBuffer.put( addressBytes );
+        }
+    }
+
+
+    /**
+     * Write an array of bytes to the buffer. Write exactly len bytes,
+     * truncating if more than len, padding if less than len bytes are
+     * available.
+     * 
+     * @param byteBuffer
+     * @param currentClientAddress
+     */
+    private void writeBytes( ByteBuffer byteBuffer, byte bytes[], int len )
+    {
+        if ( null == bytes )
+            bytes = new byte[]
+                {};
+
+        byteBuffer.put( bytes, 0, Math.min(len, bytes.length) );
+
+        // pad as necessary
+        int remain = len - bytes.length;
+        while ( remain-- > 0 )
+            byteBuffer.put( ( byte ) 0 );
+    }
+
+    private static final byte[] VENDOR_MAGIC_COOKIE =
+        { ( byte ) 99, ( byte ) 130, ( byte ) 83, ( byte ) 99 };
+
+
+    public void encodeOptions( OptionsField options, ByteBuffer message )
+    {
+        message.put( VENDOR_MAGIC_COOKIE );
+
+        for ( Iterator i = options.iterator(); i.hasNext(); )
+        {
+            DhcpOption option = ( DhcpOption ) i.next();
+            option.writeTo( message );
+        }
+
+        // add end option
+        message.put( ( byte ) 0xff );
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/io/package-info.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/io/package-info.java
new file mode 100644
index 0000000..b144a93
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/io/package-info.java
@@ -0,0 +1,29 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+/**
+ * Provides the encoders and decoders for DHCP message
+ * and option conversions.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+
+package org.apache.directory.server.dhcp.io;
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/messages/DhcpMessage.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/messages/DhcpMessage.java
new file mode 100644
index 0000000..4a846d6
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/messages/DhcpMessage.java
@@ -0,0 +1,296 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ * 
+ */
+
+package org.apache.directory.server.dhcp.messages;
+
+import java.net.InetAddress;
+
+import org.apache.directory.server.dhcp.options.OptionsField;
+
+/**
+ * A DHCP (RFC 2131) message. Field descriptions contain the oroginal RFC field
+ * names in brackets.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class DhcpMessage {
+    /**
+     * Flag value: request broadcast answer.
+     */
+    public static final int FLAG_BROADCAST = 0x01;
+
+    /**
+     * [yiaddr] 'your' (client) IP address.
+     */
+    private InetAddress assignedClientAddress;
+
+    /**
+     * [file] Boot file name, null terminated string; "generic" name or null in
+     * DHCPDISCOVER, fully qualified directory-path name in DHCPOFFER.
+     */
+    private String bootFileName;
+
+    /**
+     * [ciaddr] Current client IP address; only filled in if client is in BOUND,
+     * RENEW or REBINDING state and can respond to ARP requests.
+     */
+    private InetAddress currentClientAddress;
+
+    /**
+     * [flags] Flags. (LSB is broadcast flag)
+     */
+    private short flags;
+
+    /**
+     * [hops] Client sets to zero, optionally used by relay agents when booting
+     * via a relay agent.
+     */
+    private short hopCount;
+
+    /**
+     * [op] Message op code. 1 = BOOTREQUEST, 2 = BOOTREPLY, ...
+     */
+    private byte op;
+
+    /**
+     * Operation constant: boot request (client to server).
+     * 
+     * @see #op
+     */
+    public static final byte OP_BOOTREQUEST = 1;
+
+    /**
+     * Operation constant: boot reply (server to client).
+     * 
+     * @see #op
+     */
+    public static final byte OP_BOOTREPLY = 2;
+
+    /**
+     * [siaddr] IP address of next server to use in bootstrap; returned in
+     * DHCPOFFER, DHCPACK by server.
+     */
+    private InetAddress nextServerAddress;
+
+    /**
+     * [options] Optional parameters field. See the options documents for a list
+     * of defined options.
+     */
+    private OptionsField options = new OptionsField();
+
+    /**
+     * [giaddr] Relay agent IP address, used in booting via a relay agent.
+     */
+    private InetAddress relayAgentAddress;
+
+    /**
+     * [secs] Filled in by client, seconds elapsed since client began address
+     * acquisition or renewal process.
+     */
+    private int seconds;
+
+    /**
+     * [sname] Optional server host name, null terminated string.
+     */
+    private String serverHostname;
+
+    /**
+     * [xid] Transaction ID, a random number chosen by the client, used by the
+     * client and server to associate messages and responses between a client and
+     * a server.
+     */
+    private int transactionId;
+
+    /**
+     * The DHCP message type option.
+     */
+    private MessageType messageType;
+
+    private HardwareAddress hardwareAddress;
+
+    /**
+     * Create a default dhcp message.
+     */
+    public DhcpMessage() {
+
+    }
+
+    /**
+     * Create a DHCP message based on the supplied values.
+     * 
+     * @param messageType
+     * @param op
+     * @param hardwareAddress
+     * @param hops
+     * @param transactionId
+     * @param seconds
+     * @param flags
+     * @param currentClientAddress
+     * @param assignedClientAddress
+     * @param nextServerAddress
+     * @param relayAgentAddress
+     * @param serverHostname
+     * @param bootFileName
+     * @param options
+     */
+    public DhcpMessage(MessageType messageType, byte op,
+            HardwareAddress hardwareAddress, short hops, int transactionId,
+            int seconds, short flags, InetAddress currentClientAddress,
+            InetAddress assignedClientAddress, InetAddress nextServerAddress,
+            InetAddress relayAgentAddress, String serverHostname,
+            String bootFileName, OptionsField options) {
+        this.messageType = messageType;
+        this.op = op;
+        this.hardwareAddress = hardwareAddress;
+        this.hopCount = hops;
+        this.transactionId = transactionId;
+        this.seconds = seconds;
+        this.flags = flags;
+        this.currentClientAddress = currentClientAddress;
+        this.assignedClientAddress = assignedClientAddress;
+        this.nextServerAddress = nextServerAddress;
+        this.relayAgentAddress = relayAgentAddress;
+        this.serverHostname = serverHostname;
+        this.bootFileName = bootFileName;
+        this.options = options;
+    }
+
+    public InetAddress getAssignedClientAddress() {
+        return assignedClientAddress;
+    }
+
+    public String getBootFileName() {
+        return bootFileName;
+    }
+
+    public InetAddress getCurrentClientAddress() {
+        return currentClientAddress;
+    }
+
+    public short getFlags() {
+        return flags;
+    }
+
+    public short getHopCount() {
+        return hopCount;
+    }
+
+    public MessageType getMessageType() {
+        return messageType;
+    }
+
+    public InetAddress getNextServerAddress() {
+        return nextServerAddress;
+    }
+
+    public OptionsField getOptions() {
+        return options;
+    }
+
+    public InetAddress getRelayAgentAddress() {
+        return relayAgentAddress;
+    }
+
+    public int getSeconds() {
+        return seconds;
+    }
+
+    public String getServerHostname() {
+        return serverHostname;
+    }
+
+    public int getTransactionId() {
+        return transactionId;
+    }
+
+    public void setAssignedClientAddress(InetAddress assignedClientAddress) {
+        this.assignedClientAddress = assignedClientAddress;
+    }
+
+    public void setBootFileName(String bootFileName) {
+        this.bootFileName = bootFileName;
+    }
+
+    public void setCurrentClientAddress(InetAddress currentClientAddress) {
+        this.currentClientAddress = currentClientAddress;
+    }
+
+    public void setFlags(short flags) {
+        this.flags = flags;
+    }
+
+    public void setHopCount(short hopCount) {
+        this.hopCount = hopCount;
+    }
+
+    public void setMessageType(MessageType messageType) {
+        this.messageType = messageType;
+    }
+
+    public void setNextServerAddress(InetAddress nextServerAddress) {
+        this.nextServerAddress = nextServerAddress;
+    }
+
+    public void setOptions(OptionsField options) {
+        this.options = options;
+    }
+
+    public void setRelayAgentAddress(InetAddress relayAgentAddress) {
+        this.relayAgentAddress = relayAgentAddress;
+    }
+
+    public void setSeconds(int seconds) {
+        this.seconds = seconds;
+    }
+
+    public void setServerHostname(String serverHostname) {
+        this.serverHostname = serverHostname;
+    }
+
+    public void setTransactionId(int transactionId) {
+        this.transactionId = transactionId;
+    }
+
+    public byte getOp() {
+        return op;
+    }
+
+    public void setOp(byte op) {
+        this.op = op;
+    }
+
+    public HardwareAddress getHardwareAddress() {
+        return hardwareAddress;
+    }
+
+    public void setHardwareAddress(HardwareAddress hardwareAddress) {
+        this.hardwareAddress = hardwareAddress;
+    }
+
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append(messageType).append(": hwAddress=").append(hardwareAddress)
+                .append(", tx=").append(transactionId).append(", options=").append(
+                        options);
+
+        return sb.toString();
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/messages/HardwareAddress.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/messages/HardwareAddress.java
new file mode 100644
index 0000000..7c182f7
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/messages/HardwareAddress.java
@@ -0,0 +1,203 @@
+/*

+ *  Licensed to the Apache Software Foundation (ASF) under one

+ *  or more contributor license agreements.  See the NOTICE file

+ *  distributed with this work for additional information

+ *  regarding copyright ownership.  The ASF licenses this file

+ *  to you 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. 

+ * 

+ */

+package org.apache.directory.server.dhcp.messages;

+

+

+import java.text.ParseException;

+import java.util.Arrays;

+import java.util.regex.Matcher;

+import java.util.regex.Pattern;

+

+

+/**

+ * A representation of a DHCP hardware address.

+ * 

+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>

+ * @version $Rev: 551805 $, $Date: 2007-06-29 00:57:04 -0500 (Fr, 29 Jun 2007) $

+ */

+public final class HardwareAddress

+{

+    /**

+     * [htype] Hardware address type, see ARP section in "Assigned Numbers" RFC;

+     * e.g., '1' = 10mb ethernet.

+     */

+    private final short type;

+

+    /**

+     * [hlen] Hardware address length (e.g. '6' for 10mb ethernet).

+     */

+    private final short length;

+

+    /**

+     * [chaddr] Client hardware address.

+     */

+    private final byte[] address;

+

+

+    /**

+     * @param type

+     * @param length

+     * @param address

+     */

+    public HardwareAddress(short type, short length, byte[] address)

+    {

+        this.type = type;

+        this.length = length;

+        this.address = address;

+    }

+

+

+    public byte[] getAddress()

+    {

+        return address;

+    }

+

+

+    public short getLength()

+    {

+        return length;

+    }

+

+

+    public short getType()

+    {

+        return type;

+    }

+

+

+    /**

+     * @see java.lang.Object#hashCode()

+     * @return the instance's hash code 

+     */

+    public int hashCode()

+    {

+        int hashCode = 98643532 ^ type ^ length;

+        for ( int i = 0; i < length; i++ )

+            hashCode ^= address[i];

+

+        return hashCode;

+    }

+

+

+    /*

+     * @see java.lang.Object#equals(java.lang.Object)

+     */

+    public boolean equals( Object obj )

+    {

+        if ( null == obj || !( obj.getClass().equals( HardwareAddress.class ) ) )

+            return false;

+

+        HardwareAddress hw = ( HardwareAddress ) obj;

+

+        return length == hw.length && type == hw.type && Arrays.equals( address, hw.address );

+    }

+

+

+    /**

+     * Create the string representation of the hardware address native to the

+     * corresponding address type. This method currently supports only type

+     * 1==ethernet with the representation <code>a1:a2:a3:a4:a5:a6</code>.<br>

+     * For all other types, this method falls back to the representation created

+     * by toString().

+     * 

+     * @see java.lang.Object#toString()

+     */

+    public String getNativeRepresentation()

+    {

+        StringBuffer sb = new StringBuffer();

+        switch ( type )

+        {

+            case 1:

+                for ( int i = 0; i < length; i++ )

+                {

+                    if ( i > 0 )

+                        sb.append( ":" );

+                    String hex = Integer.toHexString( address[i] & 0xff );

+                    if ( hex.length() < 2 )

+                        sb.append( '0' );

+                    sb.append( hex );

+                }

+                break;

+            default:

+                sb.append( toString() );

+        }

+

+        return sb.toString();

+    }

+

+

+    /**

+     * Create a string representation of the hardware address. The string

+     * representation is in the format<br>

+     * <code>t/a1:a2:a3...</code><br>

+     * Where <code>t</code> represents the address type (decimal) and

+     * <code>a<sub>n</sub></code> represent the address bytes (hexadecimal).

+     * 

+     * @see java.lang.Object#toString()

+     */

+    public String toString()

+    {

+        StringBuffer sb = new StringBuffer();

+        sb.append( type );

+        sb.append( "/" );

+        for ( int i = 0; i < length; i++ )

+        {

+            if ( i > 0 )

+                sb.append( ":" );

+            String hex = Integer.toHexString( address[i] & 0xff );

+            if ( hex.length() < 2 )

+                sb.append( '0' );

+            sb.append( hex );

+        }

+

+        return sb.toString();

+    }

+

+    private static final Pattern PARSE_PATTERN = Pattern

+        .compile( "(\\d+)\\s+(?:(\\p{XDigit}{1,2}):)*(\\p{XDigit}{1,2})?" );

+

+

+    /**

+     * Parses a string representation of a hardware address according to the

+     * specification given in {@link #toString()}.

+     * 

+     * @param s

+     * @return HardwareAddress

+     * @throws ParseException

+     */

+    public static HardwareAddress valueOf( String s )

+    {

+        if ( null == s || s.length() == 0 )

+            return null;

+

+        Matcher m = PARSE_PATTERN.matcher( s );

+        if ( !m.matches() )

+            throw new IllegalArgumentException( "Can't parse string representation: " + s );

+

+        int type = Integer.parseInt( m.group( 1 ) );

+        int len = m.groupCount() - 1;

+

+        byte addr[] = new byte[len];

+        for ( int i = 0; i < addr.length; i++ )

+            addr[i] = ( byte ) Integer.parseInt( m.group( i + 2 ), 16 );

+

+        return new HardwareAddress( ( short ) type, ( short ) len, addr );

+    }

+}

diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/messages/MessageType.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/messages/MessageType.java
new file mode 100644
index 0000000..1f746db
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/messages/MessageType.java
@@ -0,0 +1,126 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ * 
+ */
+
+package org.apache.directory.server.dhcp.messages;
+
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public final class MessageType implements Comparable
+{
+    // FIXME: does this class make a lot of sense in absence of real (1.5)
+    // enums?
+    // The DOCPDISCOVER et. al. constants can't be used conveniently in
+    // switches,
+    // therefore the byte constants fpr the opcodes are duplicated here.
+    public static final byte CODE_DHCPINFORM = 8;
+
+    public static final byte CODE_DHCPRELEASE = 7;
+
+    public static final byte CODE_DHCPNAK = 6;
+
+    public static final byte CODE_DHCPACK = 5;
+
+    public static final byte CODE_DHCPDECLINE = 4;
+
+    public static final byte CODE_DHCPREQUEST = 3;
+
+    public static final byte CODE_DHCPOFFER = 2;
+
+    public static final byte CODE_DHCPDISCOVER = 1;
+
+    /**
+     * Enumeration elements are constructed once upon class loading. Order of
+     * appearance here determines the order of compareTo.
+     */
+    public static final MessageType DHCPDISCOVER = new MessageType( CODE_DHCPDISCOVER, "DHCP Discover" );
+
+    public static final MessageType DHCPOFFER = new MessageType( CODE_DHCPOFFER, "DHCP Offer" );
+
+    public static final MessageType DHCPREQUEST = new MessageType( CODE_DHCPREQUEST, "DHCP Request" );
+
+    public static final MessageType DHCPDECLINE = new MessageType( CODE_DHCPDECLINE, "DHCP Decline" );
+
+    public static final MessageType DHCPACK = new MessageType( CODE_DHCPACK, "DHCP Acknowledge" );
+
+    public static final MessageType DHCPNAK = new MessageType( CODE_DHCPNAK, "DHCP Not Acknowledge" );
+
+    public static final MessageType DHCPRELEASE = new MessageType( CODE_DHCPRELEASE, "DHCP Release" );
+
+    public static final MessageType DHCPINFORM = new MessageType( CODE_DHCPINFORM, "DHCP Inform" );
+
+
+    public String toString()
+    {
+        return name;
+    }
+
+
+    public int compareTo( Object that )
+    {
+        return ordinal - ( ( MessageType ) that ).ordinal;
+    }
+
+
+    public static MessageType getTypeByCode( byte type )
+    {
+        for ( int ii = 0; ii < values.length; ii++ )
+            if ( values[ii].ordinal == type )
+                return values[ii];
+        return new MessageType( type, "Unrecognized" );
+    }
+
+
+    public byte getCode()
+    {
+        return ordinal;
+    }
+
+    // / PRIVATE /////
+    private final String name;
+
+    private final byte ordinal;
+
+
+    /**
+     * Private constructor prevents construction outside of this class.
+     */
+    private MessageType(byte ordinal, String name)
+    {
+        this.ordinal = ordinal;
+        this.name = name;
+    }
+
+    /**
+     * These two lines are all that's necessary to export a List of VALUES.
+     */
+    private static final MessageType[] values =
+        { DHCPDISCOVER, DHCPOFFER, DHCPREQUEST, DHCPDECLINE, DHCPACK, DHCPNAK, DHCPRELEASE, DHCPINFORM };
+
+    // VALUES needs to be located here, otherwise illegal forward reference
+    public static final List VALUES = Collections.unmodifiableList( Arrays.asList( values ) );
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/messages/package-info.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/messages/package-info.java
new file mode 100644
index 0000000..bde50cb
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/messages/package-info.java
@@ -0,0 +1,28 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+/**
+ * Provides message objects for DHCP messages.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+
+package org.apache.directory.server.dhcp.messages;
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/AddressListOption.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/AddressListOption.java
new file mode 100644
index 0000000..085dc97
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/AddressListOption.java
@@ -0,0 +1,38 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *
+ */
+
+package org.apache.directory.server.dhcp.options;
+
+
+/**
+ * The Dynamic Host Configuration Protocol (DHCP) provides a framework
+ * for passing configuration information to hosts on a TCP/IP network.  
+ * Configuration parameters and other control information are carried in
+ * tagged data items that are stored in the 'options' field of the DHCP
+ * message.  The data items themselves are also called "options."
+ *
+ * This abstract base class is for options that carry lists of IP addresses.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public abstract class AddressListOption extends DhcpOption
+{
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/AddressOption.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/AddressOption.java
new file mode 100644
index 0000000..7939553
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/AddressOption.java
@@ -0,0 +1,80 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *
+ */
+
+package org.apache.directory.server.dhcp.options;
+
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+
+/**
+ * The Dynamic Host Configuration Protocol (DHCP) provides a framework
+ * for passing configuration information to hosts on a TCP/IP network.  
+ * Configuration parameters and other control information are carried in
+ * tagged data items that are stored in the 'options' field of the DHCP
+ * message.  The data items themselves are also called "options."
+ *
+ * This abstract base class is for options that carry a single IP address.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public abstract class AddressOption extends DhcpOption
+{
+    private InetAddress address;
+
+
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getData()
+     */
+    public byte[] getData()
+    {
+        return address.getAddress();
+    }
+
+
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#setData(byte[])
+     */
+    public void setData( byte[] data )
+    {
+        try
+        {
+            address = InetAddress.getByAddress( data );
+        }
+        catch ( UnknownHostException e )
+        {
+            throw new IllegalArgumentException( "illegal address", e );
+        }
+    }
+
+
+    public InetAddress getAddress()
+    {
+        return address;
+    }
+
+
+    public void setAddress( InetAddress address )
+    {
+        this.address = address;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/ByteOption.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/ByteOption.java
new file mode 100644
index 0000000..6caf3b8
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/ByteOption.java
@@ -0,0 +1,64 @@
+/*

+ *  Licensed to the Apache Software Foundation (ASF) under one

+ *  or more contributor license agreements.  See the NOTICE file

+ *  distributed with this work for additional information

+ *  regarding copyright ownership.  The ASF licenses this file

+ *  to you 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. 

+ *

+ */

+

+package org.apache.directory.server.dhcp.options;

+

+/**

+ * The Dynamic Host Configuration Protocol (DHCP) provides a framework for

+ * passing configuration information to hosts on a TCP/IP network. Configuration

+ * parameters and other control information are carried in tagged data items

+ * that are stored in the 'options' field of the DHCP message. The data items

+ * themselves are also called "options."

+ * 

+ * This abstract base class is for options that carry an unsigned short value

+ * (16 bit).

+ *  

+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>

+ * @version $Rev: 551805 $, $Date: 2007-06-29 00:57:04 -0500 (Fr, 29 Jun 2007) $

+ * 

+ */

+public abstract class ByteOption extends DhcpOption {

+    /**

+     * The byte value (represented as a short because of the unsignedness).

+     */

+    private short byteValue;

+

+    /*

+     * @see org.apache.directory.server.dhcp.options.DhcpOption#setData(byte[])

+     */

+    public void setData(byte[] data) {

+        byteValue = (short) (data[0] & 0xff);

+    }

+

+    /*

+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getData()

+     */

+    public byte[] getData() {

+        return new byte[]{(byte) (byteValue & 0xff)};

+    }

+

+    public short getByteValue() {

+        return byteValue;

+    }

+

+    public void setShortValue(short shortValue) {

+        this.byteValue = shortValue;

+    }

+}

diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/DhcpOption.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/DhcpOption.java
new file mode 100755
index 0000000..f6148c6
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/DhcpOption.java
@@ -0,0 +1,241 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   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.
+ *
+ */
+
+package org.apache.directory.server.dhcp.options;
+
+
+import java.nio.ByteBuffer;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.directory.server.dhcp.options.dhcp.BootfileName;
+import org.apache.directory.server.dhcp.options.dhcp.ClientIdentifier;
+import org.apache.directory.server.dhcp.options.dhcp.DhcpMessageType;
+import org.apache.directory.server.dhcp.options.dhcp.IpAddressLeaseTime;
+import org.apache.directory.server.dhcp.options.dhcp.MaximumDhcpMessageSize;
+import org.apache.directory.server.dhcp.options.dhcp.OptionOverload;
+import org.apache.directory.server.dhcp.options.dhcp.ParameterRequestList;
+import org.apache.directory.server.dhcp.options.dhcp.RebindingTimeValue;
+import org.apache.directory.server.dhcp.options.dhcp.RenewalTimeValue;
+import org.apache.directory.server.dhcp.options.dhcp.RequestedIpAddress;
+import org.apache.directory.server.dhcp.options.dhcp.ServerIdentifier;
+import org.apache.directory.server.dhcp.options.dhcp.TftpServerName;
+import org.apache.directory.server.dhcp.options.dhcp.UnrecognizedOption;
+import org.apache.directory.server.dhcp.options.dhcp.VendorClassIdentifier;
+import org.apache.directory.server.dhcp.options.misc.DefaultFingerServers;
+import org.apache.directory.server.dhcp.options.misc.DefaultIrcServers;
+import org.apache.directory.server.dhcp.options.misc.DefaultWwwServers;
+import org.apache.directory.server.dhcp.options.misc.MobileIpHomeAgents;
+import org.apache.directory.server.dhcp.options.misc.NbddServers;
+import org.apache.directory.server.dhcp.options.misc.NetbiosNameServers;
+import org.apache.directory.server.dhcp.options.misc.NetbiosNodeType;
+import org.apache.directory.server.dhcp.options.misc.NetbiosScope;
+import org.apache.directory.server.dhcp.options.misc.NisDomain;
+import org.apache.directory.server.dhcp.options.misc.NisPlusDomain;
+import org.apache.directory.server.dhcp.options.misc.NisPlusServers;
+import org.apache.directory.server.dhcp.options.misc.NisServers;
+import org.apache.directory.server.dhcp.options.misc.NntpServers;
+import org.apache.directory.server.dhcp.options.misc.NtpServers;
+import org.apache.directory.server.dhcp.options.misc.Pop3Servers;
+import org.apache.directory.server.dhcp.options.misc.SmtpServers;
+import org.apache.directory.server.dhcp.options.misc.StdaServers;
+import org.apache.directory.server.dhcp.options.misc.StreetTalkServers;
+import org.apache.directory.server.dhcp.options.misc.VendorSpecificInformation;
+import org.apache.directory.server.dhcp.options.misc.XWindowDisplayManagers;
+import org.apache.directory.server.dhcp.options.misc.XWindowFontServers;
+import org.apache.directory.server.dhcp.options.perhost.DefaultIpTimeToLive;
+import org.apache.directory.server.dhcp.options.perhost.IpForwarding;
+import org.apache.directory.server.dhcp.options.perhost.MaximumDatagramSize;
+import org.apache.directory.server.dhcp.options.perhost.NonLocalSourceRouting;
+import org.apache.directory.server.dhcp.options.perhost.PathMtuAgingTimeout;
+import org.apache.directory.server.dhcp.options.perhost.PathMtuPlateauTable;
+import org.apache.directory.server.dhcp.options.perhost.PolicyFilter;
+import org.apache.directory.server.dhcp.options.perinterface.AllSubnetsAreLocal;
+import org.apache.directory.server.dhcp.options.perinterface.BroadcastAddress;
+import org.apache.directory.server.dhcp.options.perinterface.InterfaceMtu;
+import org.apache.directory.server.dhcp.options.perinterface.MaskSupplier;
+import org.apache.directory.server.dhcp.options.perinterface.PerformMaskDiscovery;
+import org.apache.directory.server.dhcp.options.perinterface.PerformRouterDiscovery;
+import org.apache.directory.server.dhcp.options.perinterface.RouterSolicitationAddress;
+import org.apache.directory.server.dhcp.options.perinterface.StaticRoute;
+import org.apache.directory.server.dhcp.options.tcp.TcpDefaultTimeToLive;
+import org.apache.directory.server.dhcp.options.tcp.TcpKeepaliveGarbage;
+import org.apache.directory.server.dhcp.options.tcp.TcpKeepaliveInterval;
+import org.apache.directory.server.dhcp.options.vendor.BootFileSize;
+import org.apache.directory.server.dhcp.options.vendor.CookieServers;
+import org.apache.directory.server.dhcp.options.vendor.DomainName;
+import org.apache.directory.server.dhcp.options.vendor.DomainNameServers;
+import org.apache.directory.server.dhcp.options.vendor.ExtensionsPath;
+import org.apache.directory.server.dhcp.options.vendor.HostName;
+import org.apache.directory.server.dhcp.options.vendor.ImpressServers;
+import org.apache.directory.server.dhcp.options.vendor.LogServers;
+import org.apache.directory.server.dhcp.options.vendor.LprServers;
+import org.apache.directory.server.dhcp.options.vendor.MeritDumpFile;
+import org.apache.directory.server.dhcp.options.vendor.NameServers;
+import org.apache.directory.server.dhcp.options.vendor.ResourceLocationServers;
+import org.apache.directory.server.dhcp.options.vendor.RootPath;
+import org.apache.directory.server.dhcp.options.vendor.Routers;
+import org.apache.directory.server.dhcp.options.vendor.SubnetMask;
+import org.apache.directory.server.dhcp.options.vendor.SwapServer;
+import org.apache.directory.server.dhcp.options.vendor.TimeOffset;
+import org.apache.directory.server.dhcp.options.vendor.TimeServers;
+
+
+/**
+ * The Dynamic Host Configuration Protocol (DHCP) provides a framework
+ * for passing configuration information to hosts on a TCP/IP network.  
+ * Configuration parameters and other control information are carried in
+ * tagged data items that are stored in the 'options' field of the DHCP
+ * message.  The data items themselves are also called "options."
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public abstract class DhcpOption
+{
+    /**
+     * An array of concrete implementations of DhcpOption.
+     */
+    private static Class OPTION_CLASSES[] =
+        { BootfileName.class, ClientIdentifier.class, DhcpMessageType.class, IpAddressLeaseTime.class,
+            MaximumDhcpMessageSize.class, org.apache.directory.server.dhcp.options.dhcp.Message.class,
+            OptionOverload.class, ParameterRequestList.class, RebindingTimeValue.class, RenewalTimeValue.class,
+            RequestedIpAddress.class, ServerIdentifier.class, TftpServerName.class, VendorClassIdentifier.class,
+            ClientIdentifier.class, DhcpMessageType.class, IpAddressLeaseTime.class, MaximumDhcpMessageSize.class,
+            OptionOverload.class, ParameterRequestList.class, RebindingTimeValue.class, RenewalTimeValue.class,
+            RequestedIpAddress.class, ServerIdentifier.class, TftpServerName.class, UnrecognizedOption.class,
+            VendorClassIdentifier.class, DefaultFingerServers.class, DefaultIrcServers.class, DefaultWwwServers.class,
+            MobileIpHomeAgents.class, NbddServers.class, NetbiosNameServers.class, NetbiosNodeType.class,
+            NetbiosScope.class, NisDomain.class, NisPlusDomain.class, NisPlusServers.class, NisServers.class,
+            NntpServers.class, NtpServers.class, Pop3Servers.class, SmtpServers.class, StdaServers.class,
+            StreetTalkServers.class, VendorSpecificInformation.class, XWindowDisplayManagers.class,
+            XWindowFontServers.class, DefaultIpTimeToLive.class, IpForwarding.class, MaximumDatagramSize.class,
+            NonLocalSourceRouting.class, PathMtuAgingTimeout.class, PathMtuPlateauTable.class, PolicyFilter.class,
+            AllSubnetsAreLocal.class, BroadcastAddress.class, InterfaceMtu.class, MaskSupplier.class,
+            PerformMaskDiscovery.class, PerformRouterDiscovery.class, RouterSolicitationAddress.class,
+            StaticRoute.class, TcpDefaultTimeToLive.class, TcpKeepaliveGarbage.class, TcpKeepaliveInterval.class,
+            BootFileSize.class, CookieServers.class, DomainName.class, DomainNameServers.class, ExtensionsPath.class,
+            HostName.class, ImpressServers.class, LogServers.class, LprServers.class, MeritDumpFile.class,
+            NameServers.class, ResourceLocationServers.class, RootPath.class, Routers.class, SubnetMask.class,
+            SwapServer.class, TimeOffset.class, TimeServers.class, };
+
+    /**
+     * A map of concrete implementations of DhcpOption indexed by tag code.
+     */
+    private static Map OPTION_CLASS_BY_CODE;
+
+    /**
+     * A map of tag codes indexed by OptionClass subclass.
+     */
+    private static Map CODE_BY_CLASS;
+
+    static
+    {
+        try
+        {
+            // initialize the tag-to-class and class-to-tag map
+            Map classByCode = new HashMap();
+            Map codeByClass = new HashMap();
+            for ( int i = 0; i < OPTION_CLASSES.length; i++ )
+            {
+                Class c = OPTION_CLASSES[i];
+
+                if ( !DhcpOption.class.isAssignableFrom( c ) )
+                    throw new RuntimeException( "Class " + c + " is not a descendant of DhcpOption" );
+
+                DhcpOption o = ( DhcpOption ) c.newInstance();
+
+                Integer tagInt = new Integer( o.getTag() );
+                classByCode.put( tagInt, c );
+                codeByClass.put( c, tagInt );
+            }
+
+            OPTION_CLASS_BY_CODE = Collections.unmodifiableMap( classByCode );
+            CODE_BY_CLASS = Collections.unmodifiableMap( codeByClass );
+        }
+        catch ( Exception e )
+        {
+            throw new RuntimeException( "Can't initialize option field classes", e );
+        }
+    }
+
+
+    public static Class getClassByTag( int tag )
+    {
+        return ( Class ) OPTION_CLASS_BY_CODE.get( new Integer( tag ) );
+    }
+
+
+    public static int getTagByClass( Class c )
+    {
+        return ( ( Integer ) CODE_BY_CLASS.get( c ) ).intValue();
+    }
+
+    /**
+     * The default data array used for simple (unparsed) options.
+     */
+    private byte[] data;
+
+
+    /**
+     * Get the option's code tag.
+     * 
+     * @return byte
+     */
+    public abstract byte getTag();
+
+
+    /**
+     * Set the data (wire format) from a byte array. The default implementation
+     * just records the data as a byte array. Subclasses may parse the data into
+     * something more meaningful.
+     * 
+     * @param data
+     */
+    public void setData( byte data[] )
+    {
+        this.data = data;
+    }
+
+
+    /**
+     * Get the data (wire format) into a byte array. Subclasses must provide an
+     * implementation which serializes the parsed data back into a byte array if
+     * they override {@link #setData(byte[])}.
+     * 
+     * @return byte[]
+     */
+    public byte[] getData()
+    {
+        return data;
+    }
+
+
+    public final void writeTo( ByteBuffer out )
+    {
+        out.put( getTag() );
+
+        // FIXME: handle continuation, i.e. options longer than 128 bytes?
+        byte data[] = getData();
+        if ( data.length > 255 )
+            throw new IllegalArgumentException( "Max data length: 128 bytes." );
+
+        out.put( ( byte ) data.length );
+        out.put( data );
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/IntOption.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/IntOption.java
new file mode 100644
index 0000000..a4ce2f6
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/IntOption.java
@@ -0,0 +1,63 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one or more

+ * contributor license agreements. See the NOTICE file distributed with this

+ * work for additional information regarding copyright ownership. The ASF

+ * licenses this file to you 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.

+ * 

+ */

+

+package org.apache.directory.server.dhcp.options;

+

+/**

+ * The Dynamic Host Configuration Protocol (DHCP) provides a framework for

+ * passing configuration information to hosts on a TCP/IP network. Configuration

+ * parameters and other control information are carried in tagged data items

+ * that are stored in the 'options' field of the DHCP message. The data items

+ * themselves are also called "options."

+ * 

+ * This abstract base class is for options that carry a short value (16 bit).

+ * 

+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>

+ * @version $Rev: 551805 $, $Date: 2007-06-29 00:57:04 -0500 (Fr, 29 Jun 2007) $

+ */

+public abstract class IntOption extends DhcpOption {

+    /**

+     * The int value (represented as a long because of the unsignedness).

+     */

+    private long intValue;

+

+    /*

+     * @see org.apache.directory.server.dhcp.options.DhcpOption#setData(byte[])

+     */

+    public void setData(byte[] data) {

+        intValue = (data[0] & 0xff) << 24 | (data[1] & 0xff) << 16

+                | (data[2] & 0xff) << 8 | (data[3] & 0xff);

+    }

+

+    /*

+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getData()

+     */

+    public byte[] getData() {

+        return new byte[]{(byte) (intValue >> 24 & 0xff),

+                (byte) (intValue >> 16 & 0xff), (byte) (intValue >> 8 & 0xff),

+                (byte) (intValue & 0xff)};

+    }

+

+    public long getIntValue() {

+        return intValue;

+    }

+

+    public void setIntValue(long intValue) {

+        this.intValue = intValue;

+    }

+}

diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/OptionsField.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/OptionsField.java
new file mode 100644
index 0000000..54fcfa2
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/OptionsField.java
@@ -0,0 +1,142 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *
+ */
+
+package org.apache.directory.server.dhcp.options;
+
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+
+/**
+ * The Dynamic Host Configuration Protocol (DHCP) provides a framework
+ * for passing configuration information to hosts on a TCP/IP network.  
+ * Configuration parameters and other control information are carried in
+ * tagged data items that are stored in the 'options' field of the DHCP
+ * message.  The data items themselves are also called "options."
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class OptionsField
+{
+    /**
+     * A map of option code (Integer)->DhcpOption. FIXME: use IntHashtable from
+     * commons collections
+     */
+    private Map options = new HashMap();
+
+
+    public void add( DhcpOption option )
+    {
+        options.put( new Integer( option.getTag() ), option );
+    }
+
+
+    public boolean isEmpty()
+    {
+        return options.isEmpty();
+    }
+
+
+    public Iterator iterator()
+    {
+        return options.values().iterator();
+    }
+
+
+    /**
+     * Return the (first) DHCP option matching a given option class or
+     * <code>null</code> of the option isn't set.
+     * 
+     * @param optionClass
+     */
+    public DhcpOption get( Class optionClass )
+    {
+        Integer key = new Integer( DhcpOption.getTagByClass( optionClass ) );
+        return ( DhcpOption ) options.get( key );
+    }
+
+
+    /**
+     * Return the (first) DHCP option matching a given tag or <code>null</code>
+     * of the option isn't set.
+     * 
+     * @param tag
+     */
+    public DhcpOption get( int tag )
+    {
+        Integer key = new Integer( tag );
+        return ( DhcpOption ) options.get( key );
+    }
+
+
+    /**
+     * Merge the options from the given options field into my options. Existing
+     * options are replaced by the ones from the supplied options field.
+     * 
+     * @param options
+     */
+    public void merge( OptionsField options )
+    {
+        if ( null == options )
+            return;
+
+        for ( Iterator i = options.iterator(); i.hasNext(); )
+        {
+            DhcpOption option = ( DhcpOption ) i.next();
+            this.options.put( new Integer( option.getTag() ), option );
+        }
+    }
+
+
+    /**
+     * Remove instances of the given option class.
+     * 
+     * @param c
+     */
+    public void remove( Class c )
+    {
+        Integer key = new Integer( DhcpOption.getTagByClass( c ) );
+        options.remove( key );
+    }
+
+
+    /**
+     * Remove options matching the given tag
+     * 
+     * @param tag
+     */
+    public void remove( int tag )
+    {
+        Integer key = new Integer( tag );
+        options.remove( key );
+    }
+
+
+    /**
+     * @see Map#clear()
+     */
+    public void clear()
+    {
+        options.clear();
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/ShortOption.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/ShortOption.java
new file mode 100644
index 0000000..27b0396
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/ShortOption.java
@@ -0,0 +1,64 @@
+/*

+ *  Licensed to the Apache Software Foundation (ASF) under one

+ *  or more contributor license agreements.  See the NOTICE file

+ *  distributed with this work for additional information

+ *  regarding copyright ownership.  The ASF licenses this file

+ *  to you 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. 

+ *

+ */

+

+package org.apache.directory.server.dhcp.options;

+

+/**

+ * The Dynamic Host Configuration Protocol (DHCP) provides a framework for

+ * passing configuration information to hosts on a TCP/IP network. Configuration

+ * parameters and other control information are carried in tagged data items

+ * that are stored in the 'options' field of the DHCP message. The data items

+ * themselves are also called "options."

+ * 

+ * This abstract base class is for options that carry an unsigned short value

+ * (16 bit).

+ * 

+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>

+ * @version $Rev: 551805 $, $Date: 2007-06-29 00:57:04 -0500 (Fr, 29 Jun 2007) $

+ */

+public abstract class ShortOption extends DhcpOption {

+    /**

+     * The short value (represented as an int because of the unsignedness).

+     */

+    private int shortValue;

+

+    /*

+     * @see org.apache.directory.server.dhcp.options.DhcpOption#setData(byte[])

+     */

+    public void setData(byte[] data) {

+        shortValue = (data[0] & 0xff) << 8 | (data[1] & 0xff);

+    }

+

+    /*

+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getData()

+     */

+    public byte[] getData() {

+        return new byte[]{(byte) (shortValue >> 8 & 0xff),

+                (byte) (shortValue & 0xff)};

+    }

+

+    public int getShortValue() {

+        return shortValue;

+    }

+

+    public void setShortValue(int shortValue) {

+        this.shortValue = shortValue;

+    }

+}

diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/StringOption.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/StringOption.java
new file mode 100644
index 0000000..0857041
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/StringOption.java
@@ -0,0 +1,93 @@
+/*

+ *  Licensed to the Apache Software Foundation (ASF) under one

+ *  or more contributor license agreements.  See the NOTICE file

+ *  distributed with this work for additional information

+ *  regarding copyright ownership.  The ASF licenses this file

+ *  to you 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. 

+ *

+ */

+

+package org.apache.directory.server.dhcp.options;

+

+

+import java.io.UnsupportedEncodingException;

+

+

+/**

+ * The Dynamic Host Configuration Protocol (DHCP) provides a framework for

+ * passing configuration information to hosts on a TCP/IP network. Configuration

+ * parameters and other control information are carried in tagged data items

+ * that are stored in the 'options' field of the DHCP message. The data items

+ * themselves are also called "options." 

+ * 

+ * This abstract base class is for options

+ * that carry a string.

+ * 

+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>

+ * @version $Rev: 551805 $, $Date: 2007-06-29 00:57:04 -0500 (Fr, 29 Jun 2007) $

+ */

+public abstract class StringOption extends DhcpOption

+{

+    private String string;

+

+

+    /*

+     * @see org.apache.directory.server.dhcp.options.DhcpOption#setData(byte[])

+     */

+    public void setData( byte[] data )

+    {

+        try

+        {

+            string = new String( data, "ASCII" );

+        }

+        catch ( UnsupportedEncodingException e )

+        {

+            // should not happen

+            throw new RuntimeException( "ASCII encoding unavailable" );

+        }

+    }

+

+

+    /*

+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getData()

+     */

+    public byte[] getData()

+    {

+        if ( null == string )

+            return new byte[]

+                {};

+

+        try

+        {

+            return string.getBytes( "ASCII" );

+        }

+        catch ( UnsupportedEncodingException e )

+        {

+            // should not happen

+            throw new RuntimeException( "ASCII encoding unavailable" );

+        }

+    }

+

+

+    public String getString()

+    {

+        return string;

+    }

+

+

+    public void setString( String string )

+    {

+        this.string = string;

+    }

+}

diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/dhcp/BootfileName.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/dhcp/BootfileName.java
new file mode 100644
index 0000000..f9ce46a
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/dhcp/BootfileName.java
@@ -0,0 +1,45 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *
+ */
+
+package org.apache.directory.server.dhcp.options.dhcp;
+
+
+import org.apache.directory.server.dhcp.options.StringOption;
+
+
+/**
+ * This option is used to identify a bootfile when the 'file' field in
+ * the DHCP header has been used for DHCP options.
+*
+ * The code for this option is 67, and its minimum length is 1.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class BootfileName extends StringOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 67;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/dhcp/ClientIdentifier.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/dhcp/ClientIdentifier.java
new file mode 100644
index 0000000..43a735d
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/dhcp/ClientIdentifier.java
@@ -0,0 +1,63 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.dhcp;
+
+
+import org.apache.directory.server.dhcp.options.DhcpOption;
+
+
+/**
+ * This option is used by DHCP clients to specify their unique
+ * identifier.  DHCP servers use this value to index their database of
+ * address bindings.  This value is expected to be unique for all
+ * clients in an administrative domain.
+ * 
+ * Identifiers SHOULD be treated as opaque objects by DHCP servers.
+ * 
+ * The client identifier MAY consist of type-value pairs similar to the
+ * 'htype'/'chaddr' fields. For instance, it MAY consist
+ * of a hardware type and hardware address. In this case the type field
+ * SHOULD be one of the ARP hardware types defined in STD2.  A
+ * hardware type of 0 (zero) should be used when the value field
+ * contains an identifier other than a hardware address (e.g. a fully
+ * qualified domain name).
+ * 
+ * For correct identification of clients, each client's client-
+ * identifier MUST be unique among the client-identifiers used on the
+ * subnet to which the client is attached.  Vendors and system
+ * administrators are responsible for choosing client-identifiers that
+ * meet this requirement for uniqueness.
+ * 
+ * The code for this option is 61, and its minimum length is 2.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ClientIdentifier extends DhcpOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 61;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/dhcp/DhcpMessageType.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/dhcp/DhcpMessageType.java
new file mode 100644
index 0000000..76b049f
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/dhcp/DhcpMessageType.java
@@ -0,0 +1,89 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.dhcp;
+
+
+import org.apache.directory.server.dhcp.messages.MessageType;
+import org.apache.directory.server.dhcp.options.DhcpOption;
+
+
+/**
+ * This option is used to convey the type of the DHCP message.  The code
+ * for this option is 53, and its length is 1.  Legal values for this
+ * option are:
+ * 
+ *         Value   Message Type
+ *         -----   ------------
+ *           1     DHCPDISCOVER
+ *           2     DHCPOFFER
+ *           3     DHCPREQUEST
+ *           4     DHCPDECLINE
+ *           5     DHCPACK
+ *           6     DHCPNAK
+ *           7     DHCPRELEASE
+ *           8     DHCPINFORM
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class DhcpMessageType extends DhcpOption
+{
+    private MessageType type;
+
+
+    public DhcpMessageType()
+    {
+    }
+
+
+    public DhcpMessageType(MessageType type)
+    {
+        this.type = type;
+    }
+
+
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 53;
+    }
+
+
+    public void setData( byte[] messageType )
+    {
+        type = MessageType.getTypeByCode( messageType[0] );
+    }
+
+
+    public byte[] getData()
+    {
+        return new byte[]
+            { type.getCode() };
+    }
+
+
+    public MessageType getType()
+    {
+        return type;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/dhcp/IpAddressLeaseTime.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/dhcp/IpAddressLeaseTime.java
new file mode 100644
index 0000000..90cae36
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/dhcp/IpAddressLeaseTime.java
@@ -0,0 +1,61 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.dhcp;
+
+
+import org.apache.directory.server.dhcp.options.IntOption;
+
+
+/**
+ * This option is used in a client request (DHCPDISCOVER or DHCPREQUEST)
+ * to allow the client to request a lease time for the IP address.  In a
+ * server reply (DHCPOFFER), a DHCP server uses this option to specify
+ * the lease time it is willing to offer.
+ * 
+ * The time is in units of seconds, and is specified as a 32-bit
+ * unsigned integer.
+ * 
+ * The code for this option is 51, and its length is 4.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class IpAddressLeaseTime extends IntOption
+{
+    public IpAddressLeaseTime()
+    {
+    }
+
+
+    public IpAddressLeaseTime(long leaseTime)
+    {
+        setIntValue( leaseTime );
+    }
+
+
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 51;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/dhcp/MaximumDhcpMessageSize.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/dhcp/MaximumDhcpMessageSize.java
new file mode 100644
index 0000000..1a0cbc4
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/dhcp/MaximumDhcpMessageSize.java
@@ -0,0 +1,49 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.dhcp;
+
+
+import org.apache.directory.server.dhcp.options.ShortOption;
+
+
+/**
+ * This option specifies the maximum length DHCP message that it is
+ * willing to accept.  The length is specified as an unsigned 16-bit
+ * integer.  A client may use the maximum DHCP message size option in
+ * DHCPDISCOVER or DHCPREQUEST messages, but should not use the option
+ * in DHCPDECLINE messages.
+ * 
+ * The code for this option is 57, and its length is 2.  The minimum
+ * legal value is 576 octets.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class MaximumDhcpMessageSize extends ShortOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 57;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/dhcp/Message.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/dhcp/Message.java
new file mode 100644
index 0000000..31e2383
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/dhcp/Message.java
@@ -0,0 +1,49 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.dhcp;
+
+
+import org.apache.directory.server.dhcp.options.StringOption;
+
+
+/**
+ * This option is used by a DHCP server to provide an error message to a
+ * DHCP client in a DHCPNAK message in the event of a failure. A client
+ * may use this option in a DHCPDECLINE message to indicate the why the
+ * client declined the offered parameters.  The message consists of n
+ * octets of NVT ASCII text, which the client may display on an
+ * available output device.
+ * 
+ * The code for this option is 56 and its minimum length is 1.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class Message extends StringOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 56;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/dhcp/OptionOverload.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/dhcp/OptionOverload.java
new file mode 100644
index 0000000..2366827
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/dhcp/OptionOverload.java
@@ -0,0 +1,58 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.dhcp;
+
+
+import org.apache.directory.server.dhcp.options.ByteOption;
+
+
+/**
+ * This option is used to indicate that the DHCP 'sname' or 'file'
+ * fields are being overloaded by using them to carry DHCP options. A
+ * DHCP server inserts this option if the returned parameters will
+ * exceed the usual space allotted for options.
+ * 
+ * If this option is present, the client interprets the specified
+ * additional fields after it concludes interpretation of the standard
+ * option fields.
+ * 
+ * The code for this option is 52, and its length is 1.  Legal values
+ * for this option are:
+ * 
+ *         Value   Meaning
+ *         -----   --------
+ *           1     the 'file' field is used to hold options
+ *           2     the 'sname' field is used to hold options
+ *           3     both fields are used to hold options
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class OptionOverload extends ByteOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 52;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/dhcp/ParameterRequestList.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/dhcp/ParameterRequestList.java
new file mode 100644
index 0000000..c667c42
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/dhcp/ParameterRequestList.java
@@ -0,0 +1,52 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.dhcp;
+
+
+import org.apache.directory.server.dhcp.options.DhcpOption;
+
+
+/**
+ * This option is used by a DHCP client to request values for specified
+ * configuration parameters.  The list of requested parameters is
+ * specified as n octets, where each octet is a valid DHCP option code
+ * as defined in this document.
+ * 
+ * The client MAY list the options in order of preference.  The DHCP
+ * server is not required to return the options in the requested order,
+ * but MUST try to insert the requested options in the order requested
+ * by the client.
+ * 
+ * The code for this option is 55.  Its minimum length is 1.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ParameterRequestList extends DhcpOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 55;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/dhcp/RebindingTimeValue.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/dhcp/RebindingTimeValue.java
new file mode 100644
index 0000000..ab9e8c2
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/dhcp/RebindingTimeValue.java
@@ -0,0 +1,48 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.dhcp;
+
+
+import org.apache.directory.server.dhcp.options.IntOption;
+
+
+/**
+ * This option specifies the time interval from address assignment until
+ * the client transitions to the REBINDING state.
+ * 
+ * The value is in units of seconds, and is specified as a 32-bit
+ * unsigned integer.
+ * 
+ * The code for this option is 59, and its length is 4.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class RebindingTimeValue extends IntOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 59;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/dhcp/RenewalTimeValue.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/dhcp/RenewalTimeValue.java
new file mode 100644
index 0000000..36a0da2
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/dhcp/RenewalTimeValue.java
@@ -0,0 +1,48 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.dhcp;
+
+
+import org.apache.directory.server.dhcp.options.IntOption;
+
+
+/**
+ * This option specifies the time interval from address assignment until
+ * the client transitions to the RENEWING state.
+ * 
+ * The value is in units of seconds, and is specified as a 32-bit
+ * unsigned integer.
+ * 
+ * The code for this option is 58, and its length is 4.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class RenewalTimeValue extends IntOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 58;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/dhcp/RequestedIpAddress.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/dhcp/RequestedIpAddress.java
new file mode 100644
index 0000000..c35fc50
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/dhcp/RequestedIpAddress.java
@@ -0,0 +1,45 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.dhcp;
+
+
+import org.apache.directory.server.dhcp.options.AddressOption;
+
+
+/**
+ * This option is used in a client request (DHCPDISCOVER) to allow the
+ * client to request that a particular IP address be assigned.
+ * 
+ * The code for this option is 50, and its length is 4.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class RequestedIpAddress extends AddressOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 50;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/dhcp/ServerIdentifier.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/dhcp/ServerIdentifier.java
new file mode 100644
index 0000000..ea4ebe6
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/dhcp/ServerIdentifier.java
@@ -0,0 +1,66 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.dhcp;
+
+
+import java.net.InetAddress;
+
+import org.apache.directory.server.dhcp.options.AddressOption;
+
+
+/**
+ * This option is used in DHCPOFFER and DHCPREQUEST messages, and may
+ * optionally be included in the DHCPACK and DHCPNAK messages.  DHCP
+ * servers include this option in the DHCPOFFER in order to allow the
+ * client to distinguish between lease offers.  DHCP clients use the
+ * contents of the 'server identifier' field as the destination address
+ * for any DHCP messages unicast to the DHCP server.  DHCP clients also
+ * indicate which of several lease offers is being accepted by including
+ * this option in a DHCPREQUEST message.
+ * 
+ * The identifier is the IP address of the selected server.
+ * 
+ * The code for this option is 54, and its length is 4.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ServerIdentifier extends AddressOption
+{
+    public ServerIdentifier()
+    {
+    }
+
+
+    public ServerIdentifier(InetAddress localHost)
+    {
+        setAddress( localHost );
+    }
+
+
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 54;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/dhcp/TftpServerName.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/dhcp/TftpServerName.java
new file mode 100644
index 0000000..af56e78
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/dhcp/TftpServerName.java
@@ -0,0 +1,45 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.dhcp;
+
+
+import org.apache.directory.server.dhcp.options.StringOption;
+
+
+/**
+ * This option is used to identify a TFTP server when the 'sname' field
+ * in the DHCP header has been used for DHCP options.
+ * 
+ * The code for this option is 66, and its minimum length is 1.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class TftpServerName extends StringOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 66;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/dhcp/UnrecognizedOption.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/dhcp/UnrecognizedOption.java
new file mode 100644
index 0000000..b7dcbf5
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/dhcp/UnrecognizedOption.java
@@ -0,0 +1,49 @@
+/*

+ *  Licensed to the Apache Software Foundation (ASF) under one

+ *  or more contributor license agreements.  See the NOTICE file

+ *  distributed with this work for additional information

+ *  regarding copyright ownership.  The ASF licenses this file

+ *  to you 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. 

+ *  

+ */

+package org.apache.directory.server.dhcp.options.dhcp;

+

+import org.apache.directory.server.dhcp.options.DhcpOption;

+

+/**

+ * This pseudo option represents all options which have not been recognized and

+ * parsed as specific implementations. No special semantics are associated with

+ * it. Users are therefore required to manually deal with the contained data.

+ * 

+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>

+ * @version $Rev: 552077 $, $Date: 2007-06-29 21:23:39 -0500 (Fr, 29 Jun 2007) $

+ */

+public class UnrecognizedOption extends DhcpOption {

+    private final byte tag;

+

+    public UnrecognizedOption() {

+        tag = -1;

+    }

+

+    /*

+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()

+     */

+    public byte getTag() {

+        return tag;

+    }

+

+    public UnrecognizedOption(byte tag) {

+        this.tag = tag;

+    }

+}

diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/dhcp/VendorClassIdentifier.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/dhcp/VendorClassIdentifier.java
new file mode 100644
index 0000000..0f06fae
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/dhcp/VendorClassIdentifier.java
@@ -0,0 +1,55 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.dhcp;
+
+
+import org.apache.directory.server.dhcp.options.StringOption;
+
+
+/**
+ * This option is used by DHCP clients to optionally identify the vendor
+ * type and configuration of a DHCP client.  The information is a string
+ * of n octets, interpreted by servers.  Vendors may choose to define
+ * specific vendor class identifiers to convey particular configuration
+ * or other identification information about a client.  For example, the
+ * identifier may encode the client's hardware configuration.  Servers
+ * not equipped to interpret the class-specific information sent by a
+ * client MUST ignore it (although it may be reported). Servers that
+ * 
+ * respond SHOULD only use option 43 to return the vendor-specific
+ * information to the client.
+ * 
+ * The code for this option is 60, and its minimum length is 1.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class VendorClassIdentifier extends StringOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 60;
+    }
+
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/dhcp/package-info.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/dhcp/package-info.java
new file mode 100644
index 0000000..09f4e28
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/dhcp/package-info.java
@@ -0,0 +1,28 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+/**
+ * Provides DHCP options for the DHCP protocol.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+
+package org.apache.directory.server.dhcp.options.dhcp;
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/linklayer/ArpCacheTimeout.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/linklayer/ArpCacheTimeout.java
new file mode 100644
index 0000000..6642f7e
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/linklayer/ArpCacheTimeout.java
@@ -0,0 +1,45 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.linklayer;
+
+
+import org.apache.directory.server.dhcp.options.IntOption;
+
+
+/**
+ * This option specifies the timeout in seconds for ARP cache entries.
+ * The time is specified as a 32-bit unsigned integer.
+ * 
+ * The code for this option is 35, and its length is 4.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ArpCacheTimeout extends IntOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 35;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/linklayer/EthernetEncapsulation.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/linklayer/EthernetEncapsulation.java
new file mode 100644
index 0000000..2f419fa
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/linklayer/EthernetEncapsulation.java
@@ -0,0 +1,48 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.linklayer;
+
+
+import org.apache.directory.server.dhcp.options.ByteOption;
+
+
+/**
+ * This option specifies whether or not the client should use Ethernet
+ * Version 2 (RFC 894) or IEEE 802.3 (RFC 1042) encapsulation
+ * if the interface is an Ethernet.  A value of 0 indicates that the
+ * client should use RFC 894 encapsulation.  A value of 1 means that the
+ * client should use RFC 1042 encapsulation.
+ * 
+ * The code for this option is 36, and its length is 1.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class EthernetEncapsulation extends ByteOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 36;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/linklayer/TrailerEncapsulation.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/linklayer/TrailerEncapsulation.java
new file mode 100644
index 0000000..0a00937
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/linklayer/TrailerEncapsulation.java
@@ -0,0 +1,47 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.linklayer;
+
+
+import org.apache.directory.server.dhcp.options.ByteOption;
+
+
+/**
+ * This option specifies whether or not the client should negotiate the
+ * use of trailers (RFC 893) when using the ARP protocol.  A value
+ * of 0 indicates that the client should not attempt to use trailers.  A
+ * value of 1 means that the client should attempt to use trailers.
+ * 
+ * The code for this option is 34, and its length is 1.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class TrailerEncapsulation extends ByteOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 34;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/linklayer/package-info.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/linklayer/package-info.java
new file mode 100644
index 0000000..b2ca29e
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/linklayer/package-info.java
@@ -0,0 +1,28 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+/**
+ * Provides link layer options for the DHCP protocol.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+
+package org.apache.directory.server.dhcp.options.linklayer;
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/DefaultFingerServers.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/DefaultFingerServers.java
new file mode 100644
index 0000000..f522e53
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/DefaultFingerServers.java
@@ -0,0 +1,47 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.misc;
+
+
+import org.apache.directory.server.dhcp.options.AddressListOption;
+
+
+/**
+ * The Finger server option specifies a list of Finger available to the
+ * client.  Servers SHOULD be listed in order of preference.
+ * 
+ * The code for the Finger server option is 73.  The minimum length for
+ * this option is 4 octets, and the length MUST always be a multiple of
+ * 4.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class DefaultFingerServers extends AddressListOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 73;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/DefaultIrcServers.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/DefaultIrcServers.java
new file mode 100644
index 0000000..665b637
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/DefaultIrcServers.java
@@ -0,0 +1,47 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.misc;
+
+
+import org.apache.directory.server.dhcp.options.AddressListOption;
+
+
+/**
+ * The IRC server option specifies a list of IRC available to the
+ * client.  Servers SHOULD be listed in order of preference.
+ * 
+ * The code for the IRC server option is 74.  The minimum length for
+ * this option is 4 octets, and the length MUST always be a multiple of
+ * 4.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class DefaultIrcServers extends AddressListOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 74;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/DefaultWwwServers.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/DefaultWwwServers.java
new file mode 100644
index 0000000..e69e36c
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/DefaultWwwServers.java
@@ -0,0 +1,47 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.misc;
+
+
+import org.apache.directory.server.dhcp.options.AddressListOption;
+
+
+/**
+ * The WWW server option specifies a list of WWW available to the
+ * client.  Servers SHOULD be listed in order of preference.
+ * 
+ * The code for the WWW server option is 72.  The minimum length for
+ * this option is 4 octets, and the length MUST always be a multiple of
+ * 4.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class DefaultWwwServers extends AddressListOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 72;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/MobileIpHomeAgents.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/MobileIpHomeAgents.java
new file mode 100644
index 0000000..393ea7a
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/MobileIpHomeAgents.java
@@ -0,0 +1,49 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.misc;
+
+
+import org.apache.directory.server.dhcp.options.AddressListOption;
+
+
+/**
+ * This option specifies a list of IP addresses indicating mobile IP
+ * home agents available to the client.  Agents SHOULD be listed in
+ * order of preference.
+ * 
+ * The code for this option is 68.  Its minimum length is 0 (indicating
+ * no home agents are available) and the length MUST be a multiple of 4.
+ * It is expected that the usual length will be four octets, containing
+ * a single home agent's address.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class MobileIpHomeAgents extends AddressListOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 68;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/NbddServers.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/NbddServers.java
new file mode 100644
index 0000000..974e83d
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/NbddServers.java
@@ -0,0 +1,45 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.misc;
+
+
+import org.apache.directory.server.dhcp.options.AddressListOption;
+
+
+/**
+ * The NetBIOS datagram distribution server (NBDD) option specifies a
+ * list of RFC 1001/1002 NBDD servers listed in order of preference. The
+ * code for this option is 45.  The minimum length of the option is 4
+ * octets, and the length must always be a multiple of 4.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class NbddServers extends AddressListOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 45;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/NetbiosNameServers.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/NetbiosNameServers.java
new file mode 100644
index 0000000..1b3580d
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/NetbiosNameServers.java
@@ -0,0 +1,46 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.misc;
+
+
+import org.apache.directory.server.dhcp.options.AddressListOption;
+
+
+/**
+ * The NetBIOS name server (NBNS) option specifies a list of RFC
+ * 1001/1002 NBNS name servers listed in order of preference.
+ * 
+ * The code for this option is 44.  The minimum length of the option is
+ * 4 octets, and the length must always be a multiple of 4.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class NetbiosNameServers extends AddressListOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 44;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/NetbiosNodeType.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/NetbiosNodeType.java
new file mode 100644
index 0000000..cfd8d81
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/NetbiosNodeType.java
@@ -0,0 +1,53 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.misc;
+
+
+import org.apache.directory.server.dhcp.options.ByteOption;
+
+
+/**
+ * The NetBIOS node type option allows NetBIOS over TCP/IP clients which
+ * are configurable to be configured as described in RFC 1001/1002.  The
+ * value is specified as a single octet which identifies the client type
+ * as follows:
+ * 
+ *    Value         Node Type
+ *    -----         ---------
+ *    0x1           B-node
+ *    0x2           P-node
+ *    0x4           M-node
+ *    0x8           H-node
+ * 
+ * In the above chart, the notation '0x' indicates a number in base-16
+ * (hexadecimal). The code for this option is 46. The length of this option is
+ * always 1.
+ */
+public class NetbiosNodeType extends ByteOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 46;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/NetbiosScope.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/NetbiosScope.java
new file mode 100644
index 0000000..da3b453
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/NetbiosScope.java
@@ -0,0 +1,46 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.misc;
+
+
+import org.apache.directory.server.dhcp.options.StringOption;
+
+
+/**
+ * The NetBIOS scope option specifies the NetBIOS over TCP/IP scope
+ * parameter for the client as specified in RFC 1001/1002.
+ * 
+ * The code for this option is 47.  The minimum length of this option is
+ * 1.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class NetbiosScope extends StringOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 47;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/NisDomain.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/NisDomain.java
new file mode 100644
index 0000000..31a1460
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/NisDomain.java
@@ -0,0 +1,46 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.misc;
+
+
+import org.apache.directory.server.dhcp.options.StringOption;
+
+
+/**
+ * This option specifies the name of the client's NIS domain.  The
+ * domain is formatted as a character string consisting of characters
+ * from the NVT ASCII character set.
+ * 
+ * The code for this option is 40.  Its minimum length is 1.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class NisDomain extends StringOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 40;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/NisPlusDomain.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/NisPlusDomain.java
new file mode 100644
index 0000000..6ace0e6
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/NisPlusDomain.java
@@ -0,0 +1,46 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.misc;
+
+
+import org.apache.directory.server.dhcp.options.DhcpOption;
+
+
+/**
+ * This option specifies the name of the client's NIS+ domain.  The
+ * domain is formatted as a character string consisting of characters
+ * from the NVT ASCII character set.
+ * 
+ * The code for this option is 64.  Its minimum length is 1.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class NisPlusDomain extends DhcpOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 64;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/NisPlusServers.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/NisPlusServers.java
new file mode 100644
index 0000000..f4b157a
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/NisPlusServers.java
@@ -0,0 +1,47 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.misc;
+
+
+import org.apache.directory.server.dhcp.options.AddressListOption;
+
+
+/**
+ * This option specifies a list of IP addresses indicating NIS+ servers
+ * available to the client.  Servers SHOULD be listed in order of
+ * preference.
+ * 
+ * The code for this option is 65.  Its minimum length is 4, and the
+ * length MUST be a multiple of 4.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class NisPlusServers extends AddressListOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 65;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/NisServers.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/NisServers.java
new file mode 100644
index 0000000..17316a7
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/NisServers.java
@@ -0,0 +1,47 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.misc;
+
+
+import org.apache.directory.server.dhcp.options.AddressListOption;
+
+
+/**
+ * This option specifies a list of IP addresses indicating NIS servers
+ * available to the client.  Servers SHOULD be listed in order of
+ * preference.
+ * 
+ * The code for this option is 41.  Its minimum length is 4, and the
+ * length MUST be a multiple of 4.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class NisServers extends AddressListOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 41;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/NntpServers.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/NntpServers.java
new file mode 100644
index 0000000..bd0ab86
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/NntpServers.java
@@ -0,0 +1,47 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.misc;
+
+
+import org.apache.directory.server.dhcp.options.AddressListOption;
+
+
+/**
+ * The NNTP server option specifies a list of NNTP available to the
+ * client.  Servers SHOULD be listed in order of preference.
+ * 
+ * The code for the NNTP server option is 71. The minimum length for
+ * this option is 4 octets, and the length MUST always be a multiple of
+ * 4.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class NntpServers extends AddressListOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 71;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/NtpServers.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/NtpServers.java
new file mode 100644
index 0000000..a0f8806
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/NtpServers.java
@@ -0,0 +1,47 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.misc;
+
+
+import org.apache.directory.server.dhcp.options.AddressListOption;
+
+
+/**
+ * This option specifies a list of IP addresses indicating NTP
+ * servers available to the client.  Servers SHOULD be listed in order
+ * of preference.
+ * 
+ * The code for this option is 42.  Its minimum length is 4, and the
+ * length MUST be a multiple of 4.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class NtpServers extends AddressListOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 42;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/Pop3Servers.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/Pop3Servers.java
new file mode 100644
index 0000000..b404d74
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/Pop3Servers.java
@@ -0,0 +1,47 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.misc;
+
+
+import org.apache.directory.server.dhcp.options.AddressListOption;
+
+
+/**
+ * The POP3 server option specifies a list of POP3 available to the
+ * client.  Servers SHOULD be listed in order of preference.
+ * 
+ * The code for the POP3 server option is 70.  The minimum length for
+ * this option is 4 octets, and the length MUST always be a multiple of
+ * 4.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class Pop3Servers extends AddressListOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 70;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/SmtpServers.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/SmtpServers.java
new file mode 100644
index 0000000..ebb716a
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/SmtpServers.java
@@ -0,0 +1,47 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.misc;
+
+
+import org.apache.directory.server.dhcp.options.AddressListOption;
+
+
+/**
+ * The SMTP server option specifies a list of SMTP servers available to
+ * the client.  Servers SHOULD be listed in order of preference.
+ * 
+ * The code for the SMTP server option is 69.  The minimum length for
+ * this option is 4 octets, and the length MUST always be a multiple of
+ * 4.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class SmtpServers extends AddressListOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 69;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/StdaServers.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/StdaServers.java
new file mode 100644
index 0000000..20cdae6
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/StdaServers.java
@@ -0,0 +1,48 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.misc;
+
+
+import org.apache.directory.server.dhcp.options.AddressListOption;
+
+
+/**
+ * The StreetTalk Directory Assistance (STDA) server option specifies a
+ * list of STDA servers available to the client.  Servers SHOULD be
+ * listed in order of preference.
+ * 
+ * The code for the StreetTalk Directory Assistance server option is 76.
+ * The minimum length for this option is 4 octets, and the length MUST
+ * always be a multiple of 4.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class StdaServers extends AddressListOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 76;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/StreetTalkServers.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/StreetTalkServers.java
new file mode 100644
index 0000000..6c52c35
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/StreetTalkServers.java
@@ -0,0 +1,48 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.misc;
+
+
+import org.apache.directory.server.dhcp.options.AddressListOption;
+
+
+/**
+ * The StreetTalk server option specifies a list of StreetTalk servers
+ * available to the client.  Servers SHOULD be listed in order of
+ * preference.
+ * 
+ * The code for the StreetTalk server option is 75.  The minimum length
+ * for this option is 4 octets, and the length MUST always be a multiple
+ * of 4.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class StreetTalkServers extends AddressListOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 75;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/VendorSpecificInformation.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/VendorSpecificInformation.java
new file mode 100644
index 0000000..6cdb63d
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/VendorSpecificInformation.java
@@ -0,0 +1,73 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.misc;
+
+
+import org.apache.directory.server.dhcp.options.DhcpOption;
+
+
+/**
+ * This option is used by clients and servers to exchange vendor-
+ * specific information.  The information is an opaque object of n
+ * octets, presumably interpreted by vendor-specific code on the clients
+ * and servers.  The definition of this information is vendor specific.
+ * The vendor is indicated in the vendor class identifier option.
+ * Servers not equipped to interpret the vendor-specific information
+ * sent by a client MUST ignore it (although it may be reported).
+ * Clients which do not receive desired vendor-specific information
+ * SHOULD make an attempt to operate without it, although they may do so
+ * (and announce they are doing so) in a degraded mode.
+ * 
+ * If a vendor potentially encodes more than one item of information in
+ * this option, then the vendor SHOULD encode the option using
+ * "Encapsulated vendor-specific options" as described below:
+ * The Encapsulated vendor-specific options field SHOULD be encoded as a
+ * sequence of code/length/value fields of identical syntax to the DHCP
+ * options field with the following exceptions:
+ * 
+ *    1) There SHOULD NOT be a "magic cookie" field in the encapsulated
+ *       vendor-specific extensions field.
+ * 
+ *    2) Codes other than 0 or 255 MAY be redefined by the vendor within
+ *       the encapsulated vendor-specific extensions field, but SHOULD
+ *       conform to the tag-length-value syntax defined in section 2.
+ * 
+ *    3) Code 255 (END), if present, signifies the end of the
+ *       encapsulated vendor extensions, not the end of the vendor
+ *       extensions field. If no code 255 is present, then the end of
+ *       the enclosing vendor-specific information field is taken as the
+ *       end of the encapsulated vendor-specific extensions field.
+ * 
+ * The code for this option is 43 and its minimum length is 1.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class VendorSpecificInformation extends DhcpOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 43;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/XWindowDisplayManagers.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/XWindowDisplayManagers.java
new file mode 100644
index 0000000..f70dbd1
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/XWindowDisplayManagers.java
@@ -0,0 +1,49 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.misc;
+
+
+import org.apache.directory.server.dhcp.options.AddressListOption;
+
+
+/**
+ * This option specifies a list of IP addresses of systems that are
+ * running the X Window System Display Manager and are available to the
+ * client.
+ * 
+ * Addresses SHOULD be listed in order of preference.
+ * 
+ * The code for the this option is 49. The minimum length of this option
+ * is 4, and the length MUST be a multiple of 4.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class XWindowDisplayManagers extends AddressListOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 49;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/XWindowFontServers.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/XWindowFontServers.java
new file mode 100644
index 0000000..24a88cc
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/XWindowFontServers.java
@@ -0,0 +1,47 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.misc;
+
+
+import org.apache.directory.server.dhcp.options.AddressListOption;
+
+
+/**
+ * This option specifies a list of X Window System Font servers
+ * available to the client. Servers SHOULD be listed in order of
+ * preference.
+ * 
+ * The code for this option is 48.  The minimum length of this option is
+ * 4 octets, and the length MUST be a multiple of 4.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class XWindowFontServers extends AddressListOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 48;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/package-info.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/package-info.java
new file mode 100644
index 0000000..ea728a1
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/misc/package-info.java
@@ -0,0 +1,28 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+/**
+ * Provides miscellaneous options for the DHCP protocol.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+
+package org.apache.directory.server.dhcp.options.misc;
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/package-info.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/package-info.java
new file mode 100644
index 0000000..c196549
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/package-info.java
@@ -0,0 +1,28 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+/**
+ * Provides base message objects for DHCP options.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+
+package org.apache.directory.server.dhcp.options;
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/perhost/DefaultIpTimeToLive.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/perhost/DefaultIpTimeToLive.java
new file mode 100644
index 0000000..c30f52e
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/perhost/DefaultIpTimeToLive.java
@@ -0,0 +1,46 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.perhost;
+
+
+import org.apache.directory.server.dhcp.options.ByteOption;
+
+
+/**
+ * This option specifies the default time-to-live that the client should
+ * use on outgoing datagrams.  The TTL is specified as an octet with a
+ * value between 1 and 255.
+ * 
+ * The code for this option is 23, and its length is 1.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class DefaultIpTimeToLive extends ByteOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 23;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/perhost/IpForwarding.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/perhost/IpForwarding.java
new file mode 100644
index 0000000..c3f3cfb
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/perhost/IpForwarding.java
@@ -0,0 +1,46 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.perhost;
+
+
+import org.apache.directory.server.dhcp.options.ByteOption;
+
+
+/**
+ * This option specifies whether the client should configure its IP
+ * layer for packet forwarding.  A value of 0 means disable IP
+ * forwarding, and a value of 1 means enable IP forwarding.
+ * 
+ * The code for this option is 19, and its length is 1.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class IpForwarding extends ByteOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 19;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/perhost/MaximumDatagramSize.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/perhost/MaximumDatagramSize.java
new file mode 100644
index 0000000..39beecb
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/perhost/MaximumDatagramSize.java
@@ -0,0 +1,46 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.perhost;
+
+
+import org.apache.directory.server.dhcp.options.ShortOption;
+
+
+/**
+ * This option specifies the maximum size datagram that the client
+ * should be prepared to reassemble.  The size is specified as a 16-bit
+ * unsigned integer.  The minimum value legal value is 576.
+ * 
+ * The code for this option is 22, and its length is 2.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class MaximumDatagramSize extends ShortOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 22;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/perhost/NonLocalSourceRouting.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/perhost/NonLocalSourceRouting.java
new file mode 100644
index 0000000..d3d344b
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/perhost/NonLocalSourceRouting.java
@@ -0,0 +1,47 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.perhost;
+
+
+import org.apache.directory.server.dhcp.options.ByteOption;
+
+
+/**
+ * This option specifies whether the client should configure its IP
+ * layer to allow forwarding of datagrams with non-local source routes.
+ * A value of 0 means disallow forwarding of such datagrams, and a value
+ * of 1 means allow forwarding.
+ * 
+ * The code for this option is 20, and its length is 1.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class NonLocalSourceRouting extends ByteOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 20;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/perhost/PathMtuAgingTimeout.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/perhost/PathMtuAgingTimeout.java
new file mode 100644
index 0000000..8645fe8
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/perhost/PathMtuAgingTimeout.java
@@ -0,0 +1,46 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.perhost;
+
+
+import org.apache.directory.server.dhcp.options.IntOption;
+
+
+/**
+ * This option specifies the timeout (in seconds) to use when aging Path
+ * MTU values discovered by the mechanism defined in RFC 1191.  The
+ * timeout is specified as a 32-bit unsigned integer.
+ * 
+ * The code for this option is 24, and its length is 4.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class PathMtuAgingTimeout extends IntOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 24;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/perhost/PathMtuPlateauTable.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/perhost/PathMtuPlateauTable.java
new file mode 100644
index 0000000..69ce169
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/perhost/PathMtuPlateauTable.java
@@ -0,0 +1,48 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.perhost;
+
+
+import org.apache.directory.server.dhcp.options.DhcpOption;
+
+
+/**
+ * This option specifies a table of MTU sizes to use when performing
+ * Path MTU Discovery as defined in RFC 1191.  The table is formatted as
+ * a list of 16-bit unsigned integers, ordered from smallest to largest.
+ * The minimum MTU value cannot be smaller than 68.
+ * 
+ * The code for this option is 25.  Its minimum length is 2, and the
+ * length MUST be a multiple of 2.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class PathMtuPlateauTable extends DhcpOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 25;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/perhost/PolicyFilter.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/perhost/PolicyFilter.java
new file mode 100644
index 0000000..f51d900
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/perhost/PolicyFilter.java
@@ -0,0 +1,50 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.perhost;
+
+
+import org.apache.directory.server.dhcp.options.AddressListOption;
+
+
+/**
+ * This option specifies policy filters for non-local source routing.
+ * The filters consist of a list of IP addresses and masks which specify
+ * destination/mask pairs with which to filter incoming source routes.
+ * 
+ * Any source routed datagram whose next-hop address does not match one
+ * of the filters should be discarded by the client.
+ * 
+ * The code for this option is 21.  The minimum length of this option is
+ * 8, and the length MUST be a multiple of 8.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class PolicyFilter extends AddressListOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 21;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/perhost/package-info.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/perhost/package-info.java
new file mode 100644
index 0000000..25d8cae
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/perhost/package-info.java
@@ -0,0 +1,28 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+/**
+ * Provides per-host options for the DHCP protocol.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+
+package org.apache.directory.server.dhcp.options.perhost;
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/perinterface/AllSubnetsAreLocal.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/perinterface/AllSubnetsAreLocal.java
new file mode 100644
index 0000000..f19d408
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/perinterface/AllSubnetsAreLocal.java
@@ -0,0 +1,49 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.perinterface;
+
+
+import org.apache.directory.server.dhcp.options.ByteOption;
+
+
+/**
+ * This option specifies whether or not the client may assume that all
+ * subnets of the IP network to which the client is connected use the
+ * same MTU as the subnet of that network to which the client is
+ * directly connected.  A value of 1 indicates that all subnets share
+ * the same MTU.  A value of 0 means that the client should assume that
+ * some subnets of the directly connected network may have smaller MTUs.
+ * 
+ * The code for this option is 27, and its length is 1.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class AllSubnetsAreLocal extends ByteOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 27;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/perinterface/BroadcastAddress.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/perinterface/BroadcastAddress.java
new file mode 100644
index 0000000..c26371f
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/perinterface/BroadcastAddress.java
@@ -0,0 +1,45 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.perinterface;
+
+
+import org.apache.directory.server.dhcp.options.AddressOption;
+
+
+/**
+ * This option specifies the broadcast address in use on the client's
+ * subnet.
+ * 
+ * The code for this option is 28, and its length is 4.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class BroadcastAddress extends AddressOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 28;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/perinterface/InterfaceMtu.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/perinterface/InterfaceMtu.java
new file mode 100644
index 0000000..d0ebdd5
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/perinterface/InterfaceMtu.java
@@ -0,0 +1,46 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.perinterface;
+
+
+import org.apache.directory.server.dhcp.options.ShortOption;
+
+
+/**
+ * This option specifies the MTU to use on this interface.  The MTU is
+ * specified as a 16-bit unsigned integer.  The minimum legal value for
+ * the MTU is 68.
+ * 
+ * The code for this option is 26, and its length is 2.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class InterfaceMtu extends ShortOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 26;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/perinterface/MaskSupplier.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/perinterface/MaskSupplier.java
new file mode 100644
index 0000000..f209ca2
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/perinterface/MaskSupplier.java
@@ -0,0 +1,47 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.perinterface;
+
+
+import org.apache.directory.server.dhcp.options.ByteOption;
+
+
+/**
+ * This option specifies whether or not the client should respond to
+ * subnet mask requests using ICMP.  A value of 0 indicates that the
+ * client should not respond.  A value of 1 means that the client should
+ * respond.
+ * 
+ * The code for this option is 30, and its length is 1.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class MaskSupplier extends ByteOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 30;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/perinterface/PerformMaskDiscovery.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/perinterface/PerformMaskDiscovery.java
new file mode 100644
index 0000000..a30e009
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/perinterface/PerformMaskDiscovery.java
@@ -0,0 +1,47 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.perinterface;
+
+
+import org.apache.directory.server.dhcp.options.ByteOption;
+
+
+/**
+ * This option specifies whether or not the client should perform subnet
+ * mask discovery using ICMP.  A value of 0 indicates that the client
+ * should not perform mask discovery.  A value of 1 means that the
+ * client should perform mask discovery.
+ * 
+ * The code for this option is 29, and its length is 1.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class PerformMaskDiscovery extends ByteOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 29;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/perinterface/PerformRouterDiscovery.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/perinterface/PerformRouterDiscovery.java
new file mode 100644
index 0000000..492440b
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/perinterface/PerformRouterDiscovery.java
@@ -0,0 +1,48 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.perinterface;
+
+
+import org.apache.directory.server.dhcp.options.DhcpOption;
+
+
+/**
+ * This option specifies whether or not the client should solicit
+ * routers using the Router Discovery mechanism defined in RFC 1256.
+ * A value of 0 indicates that the client should not perform router
+ * discovery.  A value of 1 means that the client should perform
+ * router discovery.
+ * 
+ * The code for this option is 31, and its length is 1.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class PerformRouterDiscovery extends DhcpOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 31;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/perinterface/RouterSolicitationAddress.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/perinterface/RouterSolicitationAddress.java
new file mode 100644
index 0000000..3d3cc09
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/perinterface/RouterSolicitationAddress.java
@@ -0,0 +1,45 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.perinterface;
+
+
+import org.apache.directory.server.dhcp.options.AddressOption;
+
+
+/**
+ * This option specifies the address to which the client should transmit
+ * router solicitation requests.
+ * 
+ * The code for this option is 32, and its length is 4.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class RouterSolicitationAddress extends AddressOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 32;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/perinterface/StaticRoute.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/perinterface/StaticRoute.java
new file mode 100644
index 0000000..20f9f06
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/perinterface/StaticRoute.java
@@ -0,0 +1,55 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.perinterface;
+
+
+import org.apache.directory.server.dhcp.options.DhcpOption;
+
+
+/**
+ * This option specifies a list of static routes that the client should
+ * install in its routing cache.  If multiple routes to the same
+ * destination are specified, they are listed in descending order of
+ * priority.
+ * 
+ * The routes consist of a list of IP address pairs.  The first address
+ * is the destination address, and the second address is the router for
+ * the destination.
+ * 
+ * The default route (0.0.0.0) is an illegal destination for a static
+ * route.  See section 3.5 for information about the router option.
+ * 
+ * The code for this option is 33.  The minimum length of this option is
+ * 8, and the length MUST be a multiple of 8.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class StaticRoute extends DhcpOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 33;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/perinterface/package-info.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/perinterface/package-info.java
new file mode 100644
index 0000000..a5f1cf0
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/perinterface/package-info.java
@@ -0,0 +1,28 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+/**
+ * Provides per-interface options for the DHCP protocol.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+
+package org.apache.directory.server.dhcp.options.perinterface;
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/tcp/TcpDefaultTimeToLive.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/tcp/TcpDefaultTimeToLive.java
new file mode 100644
index 0000000..757e175
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/tcp/TcpDefaultTimeToLive.java
@@ -0,0 +1,46 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.tcp;
+
+
+import org.apache.directory.server.dhcp.options.ByteOption;
+
+
+/**
+ * This option specifies the default TTL that the client should use when
+ * sending TCP segments.  The value is represented as an 8-bit unsigned
+ * integer.  The minimum value is 1.
+ * 
+ * The code for this option is 37, and its length is 1.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class TcpDefaultTimeToLive extends ByteOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 37;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/tcp/TcpKeepaliveGarbage.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/tcp/TcpKeepaliveGarbage.java
new file mode 100644
index 0000000..416906a
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/tcp/TcpKeepaliveGarbage.java
@@ -0,0 +1,48 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.tcp;
+
+
+import org.apache.directory.server.dhcp.options.IntOption;
+
+
+/**
+ * This option specifies the interval (in seconds) that the client TCP
+ * should wait before sending a keepalive message on a TCP connection.
+ * The time is specified as a 32-bit unsigned integer.  A value of zero
+ * indicates that the client should not generate keepalive messages on
+ * connections unless specifically requested by an application.
+ * 
+ * The code for this option is 38, and its length is 4.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class TcpKeepaliveGarbage extends IntOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 38;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/tcp/TcpKeepaliveInterval.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/tcp/TcpKeepaliveInterval.java
new file mode 100644
index 0000000..3fd072c
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/tcp/TcpKeepaliveInterval.java
@@ -0,0 +1,48 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.tcp;
+
+
+import org.apache.directory.server.dhcp.options.ByteOption;
+
+
+/**
+ * This option specifies the whether or not the client should send TCP
+ * keepalive messages with a octet of garbage for compatibility with
+ * older implementations.  A value of 0 indicates that a garbage octet
+ * should not be sent. A value of 1 indicates that a garbage octet
+ * should be sent.
+ * 
+ * The code for this option is 39, and its length is 1.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class TcpKeepaliveInterval extends ByteOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 39;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/tcp/package-info.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/tcp/package-info.java
new file mode 100644
index 0000000..fe438d9
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/tcp/package-info.java
@@ -0,0 +1,28 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+/**
+ * Provides TCP options for the DHCP protocol.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+
+package org.apache.directory.server.dhcp.options.tcp;
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/vendor/BootFileSize.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/vendor/BootFileSize.java
new file mode 100644
index 0000000..6cb815e
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/vendor/BootFileSize.java
@@ -0,0 +1,46 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.vendor;
+
+
+import org.apache.directory.server.dhcp.options.ShortOption;
+
+
+/**
+ * This option specifies the length in 512-octet blocks of the default
+ * boot image for the client.  The file length is specified as an
+ * unsigned 16-bit integer.
+ * 
+ * The code for this option is 13, and its length is 2.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class BootFileSize extends ShortOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 21;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/vendor/CookieServers.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/vendor/CookieServers.java
new file mode 100644
index 0000000..5342db7
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/vendor/CookieServers.java
@@ -0,0 +1,44 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   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.
+ *
+ */
+
+package org.apache.directory.server.dhcp.options.vendor;
+
+
+import org.apache.directory.server.dhcp.options.AddressListOption;
+
+
+/**
+ * The cookie server option specifies a list of RFC 865 cookie
+ * servers available to the client.  Servers SHOULD be listed in order
+ * of preference.
+ * 
+ * The code for the log server option is 8.  The minimum length for this
+ * option is 4 octets, and the length MUST always be a multiple of 4.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class CookieServers extends AddressListOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 8;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/vendor/DomainName.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/vendor/DomainName.java
new file mode 100644
index 0000000..2475482
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/vendor/DomainName.java
@@ -0,0 +1,46 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.vendor;
+
+
+import org.apache.directory.server.dhcp.options.StringOption;
+
+
+/**
+ * This option specifies the domain name that client should use when
+ * resolving hostnames via the Domain Name System.
+ * 
+ * The code for this option is 15.  Its minimum length is 1.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class DomainName extends StringOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 15;
+    }
+
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/vendor/DomainNameServers.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/vendor/DomainNameServers.java
new file mode 100644
index 0000000..d7510ae
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/vendor/DomainNameServers.java
@@ -0,0 +1,48 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.vendor;
+
+
+import org.apache.directory.server.dhcp.options.AddressListOption;
+
+
+/**
+ * The domain name server option specifies a list of Domain Name System
+ * (STD 13, RFC 1035 [8]) name servers available to the client.  Servers
+ * SHOULD be listed in order of preference.
+ * 
+ * The code for the domain name server option is 6.  The minimum length
+ * for this option is 4 octets, and the length MUST always be a multiple
+ * of 4.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class DomainNameServers extends AddressListOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 6;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/vendor/ExtensionsPath.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/vendor/ExtensionsPath.java
new file mode 100644
index 0000000..2a51baa
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/vendor/ExtensionsPath.java
@@ -0,0 +1,52 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.vendor;
+
+
+import org.apache.directory.server.dhcp.options.StringOption;
+
+
+/**
+ * A string to specify a file, retrievable via TFTP, which contains
+ * information which can be interpreted in the same way as the 64-octet
+ * vendor-extension field within the BOOTP response, with the following
+ * exceptions:
+ * 
+ *        - the length of the file is unconstrained;
+ *        - all references to Tag 18 (i.e., instances of the
+ *          BOOTP Extensions Path field) within the file are
+ *          ignored.
+ * 
+ * The code for this option is 18.  Its minimum length is 1.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ExtensionsPath extends StringOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 18;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/vendor/HostName.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/vendor/HostName.java
new file mode 100644
index 0000000..6236e95
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/vendor/HostName.java
@@ -0,0 +1,60 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.vendor;
+
+
+import org.apache.directory.server.dhcp.options.StringOption;
+
+
+/**
+ * This option specifies the name of the client.  The name may or may
+ * not be qualified with the local domain name (see section 3.17 for the
+ * preferred way to retrieve the domain name).  See RFC 1035 for
+ * character set restrictions.
+ * 
+ * The code for this option is 12, and its minimum length is 1.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class HostName extends StringOption
+{
+    public HostName()
+    {
+    }
+    
+    /**
+     * @param name
+     */
+    public HostName(String name)
+    {
+        setString( name );
+    }
+
+
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 12;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/vendor/ImpressServers.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/vendor/ImpressServers.java
new file mode 100644
index 0000000..b98e545
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/vendor/ImpressServers.java
@@ -0,0 +1,54 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   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 Impress server option specifies a list of Imagen Impress servers
+ * available to the client.  Servers SHOULD be listed in order of
+ * preference.
+ * 
+ * The code for the Impress server option is 10.  The minimum length for
+ * this option is 4 octets, and the length MUST always be a multiple of
+ * 4.
+ */
+package org.apache.directory.server.dhcp.options.vendor;
+
+
+import org.apache.directory.server.dhcp.options.AddressListOption;
+
+
+/**
+ * The Impress server option specifies a list of Imagen Impress servers
+ * available to the client.  Servers SHOULD be listed in order of
+ * preference.
+ * 
+ * The code for the Impress server option is 10.  The minimum length for
+ * this option is 4 octets, and the length MUST always be a multiple of
+ * 4.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ImpressServers extends AddressListOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 10;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/vendor/LogServers.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/vendor/LogServers.java
new file mode 100644
index 0000000..a54a7ea
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/vendor/LogServers.java
@@ -0,0 +1,47 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.vendor;
+
+
+import org.apache.directory.server.dhcp.options.AddressListOption;
+
+
+/**
+ * The log server option specifies a list of MIT-LCS UDP log servers
+ * available to the client.  Servers SHOULD be listed in order of
+ * preference.
+ * 
+ * The code for the log server option is 7.  The minimum length for this
+ * option is 4 octets, and the length MUST always be a multiple of 4.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class LogServers extends AddressListOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 7;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/vendor/LprServers.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/vendor/LprServers.java
new file mode 100644
index 0000000..b0f37cb
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/vendor/LprServers.java
@@ -0,0 +1,47 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.vendor;
+
+
+import org.apache.directory.server.dhcp.options.AddressListOption;
+
+
+/**
+ * The LPR server option specifies a list of RFC 1179 line printer
+ * servers available to the client.  Servers SHOULD be listed in order
+ * of preference.
+ * 
+ * The code for the LPR server option is 9.  The minimum length for this
+ * option is 4 octets, and the length MUST always be a multiple of 4.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class LprServers extends AddressListOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 9;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/vendor/MeritDumpFile.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/vendor/MeritDumpFile.java
new file mode 100644
index 0000000..06386ed
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/vendor/MeritDumpFile.java
@@ -0,0 +1,47 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.vendor;
+
+
+import org.apache.directory.server.dhcp.options.StringOption;
+
+
+/**
+ * This option specifies the path-name of a file to which the client's
+ * core image should be dumped in the event the client crashes.  The
+ * path is formatted as a character string consisting of characters from
+ * the NVT ASCII character set.
+ * 
+ * The code for this option is 14.  Its minimum length is 1.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class MeritDumpFile extends StringOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 14;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/vendor/NameServers.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/vendor/NameServers.java
new file mode 100644
index 0000000..21480a5
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/vendor/NameServers.java
@@ -0,0 +1,48 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.vendor;
+
+
+import org.apache.directory.server.dhcp.options.AddressListOption;
+
+
+/**
+ * The name server option specifies a list of IEN 116 name servers
+ * available to the client.  Servers SHOULD be listed in order of
+ * preference.
+ * 
+ * The code for the name server option is 5.  The minimum length for
+ * this option is 4 octets, and the length MUST always be a multiple of
+ * 4.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class NameServers extends AddressListOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 5;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/vendor/ResourceLocationServers.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/vendor/ResourceLocationServers.java
new file mode 100644
index 0000000..4df623b
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/vendor/ResourceLocationServers.java
@@ -0,0 +1,47 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.vendor;
+
+
+import org.apache.directory.server.dhcp.options.AddressListOption;
+
+
+/**
+ * This option specifies a list of RFC 887 Resource Location
+ * servers available to the client.  Servers SHOULD be listed in order
+ * of preference.
+ * 
+ * The code for this option is 11.  The minimum length for this option
+ * is 4 octets, and the length MUST always be a multiple of 4.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ResourceLocationServers extends AddressListOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 11;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/vendor/RootPath.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/vendor/RootPath.java
new file mode 100644
index 0000000..f737d8a
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/vendor/RootPath.java
@@ -0,0 +1,60 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.vendor;
+
+
+import org.apache.directory.server.dhcp.options.StringOption;
+
+
+/**
+ * This option specifies the path-name that contains the client's root
+ * disk.  The path is formatted as a character string consisting of
+ * characters from the NVT ASCII character set.
+ * 
+ * The code for this option is 17.  Its minimum length is 1.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class RootPath extends StringOption
+{
+    public RootPath()
+    {
+    }
+
+
+    /**
+     * @param path
+     */
+    public RootPath(String path)
+    {
+        setString( path );
+    }
+
+
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 17;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/vendor/Routers.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/vendor/Routers.java
new file mode 100644
index 0000000..7d0e54d
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/vendor/Routers.java
@@ -0,0 +1,47 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.vendor;
+
+
+import org.apache.directory.server.dhcp.options.AddressListOption;
+
+
+/**
+ * The router option specifies a list of IP addresses for routers on the
+ * client's subnet.  Routers SHOULD be listed in order of preference.
+ * 
+ * The code for the router option is 3.  The minimum length for the
+ * router option is 4 octets, and the length MUST always be a multiple
+ * of 4.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class Routers extends AddressListOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 3;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/vendor/SubnetMask.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/vendor/SubnetMask.java
new file mode 100644
index 0000000..7de4b50
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/vendor/SubnetMask.java
@@ -0,0 +1,63 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.vendor;
+
+
+import java.net.InetAddress;
+
+import org.apache.directory.server.dhcp.options.AddressOption;
+
+
+/**
+ * The subnet mask option specifies the client's subnet mask as per RFC
+ * 950.
+ * 
+ * If both the subnet mask and the router option are specified in a DHCP
+ * reply, the subnet mask option MUST be first.
+ * 
+ * The code for the subnet mask option is 1, and its length is 4 octets.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class SubnetMask extends AddressOption
+{
+    public SubnetMask()
+    {
+    }
+    
+    /**
+     * @param netmask
+     */
+    public SubnetMask(InetAddress netmask)
+    {
+        setAddress( netmask );
+    }
+
+
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 1;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/vendor/SwapServer.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/vendor/SwapServer.java
new file mode 100644
index 0000000..b785ba8
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/vendor/SwapServer.java
@@ -0,0 +1,44 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.vendor;
+
+
+import org.apache.directory.server.dhcp.options.AddressOption;
+
+
+/**
+ * This specifies the IP address of the client's swap server.
+ * 
+ * The code for this option is 16 and its length is 4.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class SwapServer extends AddressOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 16;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/vendor/TimeOffset.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/vendor/TimeOffset.java
new file mode 100644
index 0000000..aac304e
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/vendor/TimeOffset.java
@@ -0,0 +1,48 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.vendor;
+
+
+import org.apache.directory.server.dhcp.options.AddressOption;
+
+
+/**
+ * The time offset field specifies the offset of the client's subnet in
+ * seconds from Coordinated Universal Time (UTC).  The offset is
+ * expressed as a two's complement 32-bit integer.  A positive offset
+ * indicates a location east of the zero meridian and a negative offset
+ * indicates a location west of the zero meridian.
+ * 
+ * The code for the time offset option is 2, and its length is 4 octets.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class TimeOffset extends AddressOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 2;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/vendor/TimeServers.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/vendor/TimeServers.java
new file mode 100644
index 0000000..c351cb2
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/vendor/TimeServers.java
@@ -0,0 +1,48 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.options.vendor;
+
+
+import org.apache.directory.server.dhcp.options.AddressListOption;
+
+
+/**
+ * The time server option specifies a list of RFC 868 time servers
+ * available to the client.  Servers SHOULD be listed in order of
+ * preference.
+ * 
+ * The code for the time server option is 4.  The minimum length for
+ * this option is 4 octets, and the length MUST always be a multiple of
+ * 4.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class TimeServers extends AddressListOption
+{
+    /*
+     * @see org.apache.directory.server.dhcp.options.DhcpOption#getTag()
+     */
+    public byte getTag()
+    {
+        return 4;
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/vendor/package-info.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/vendor/package-info.java
new file mode 100644
index 0000000..1ca84c3
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/options/vendor/package-info.java
@@ -0,0 +1,28 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+/**
+ * Provides vendor options for the DHCP protocol.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+
+package org.apache.directory.server.dhcp.options.vendor;
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/package-info.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/package-info.java
new file mode 100644
index 0000000..6c8353b
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/package-info.java
@@ -0,0 +1,30 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+/**
+ * Provides the entry point to an instance of the
+ * {@link org.apache.directory.server.dhcp.service.DhcpService},
+ * as well as the root of the exception hierarchy.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+
+package org.apache.directory.server.dhcp;
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/protocol/DhcpDecoder.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/protocol/DhcpDecoder.java
new file mode 100644
index 0000000..63afe98
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/protocol/DhcpDecoder.java
@@ -0,0 +1,57 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.protocol;
+
+
+import org.apache.directory.server.dhcp.DhcpException;
+import org.apache.directory.server.dhcp.io.DhcpMessageDecoder;
+import org.apache.mina.common.ByteBuffer;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.filter.codec.ProtocolDecoder;
+import org.apache.mina.filter.codec.ProtocolDecoderOutput;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class DhcpDecoder implements ProtocolDecoder
+{
+    public void decode( IoSession session, ByteBuffer in, ProtocolDecoderOutput out ) throws DhcpException
+    {
+        DhcpMessageDecoder decoder = new DhcpMessageDecoder();
+        out.write( decoder.decode( in.buf() ) );
+    }
+
+
+    public void dispose( IoSession arg0 ) throws Exception
+    {
+    }
+
+
+    /* 
+     * @see org.apache.mina.filter.codec.ProtocolDecoder#finishDecode(org.apache.mina.common.IoSession, org.apache.mina.filter.codec.ProtocolDecoderOutput)
+     */
+    public void finishDecode( IoSession session, ProtocolDecoderOutput out ) throws Exception
+    {
+        // TODO Auto-generated method stub
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/protocol/DhcpEncoder.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/protocol/DhcpEncoder.java
new file mode 100644
index 0000000..c11d9b1
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/protocol/DhcpEncoder.java
@@ -0,0 +1,56 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.protocol;
+
+
+import org.apache.directory.server.dhcp.io.DhcpMessageEncoder;
+import org.apache.directory.server.dhcp.messages.DhcpMessage;
+import org.apache.mina.common.ByteBuffer;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.filter.codec.ProtocolEncoder;
+import org.apache.mina.filter.codec.ProtocolEncoderOutput;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class DhcpEncoder implements ProtocolEncoder
+{
+    // FIXME: what's the point of splitting this class from the actual encoder?
+    private DhcpMessageEncoder encoder = new DhcpMessageEncoder();
+
+
+    public void encode( IoSession session, Object message, ProtocolEncoderOutput out )
+    {
+        ByteBuffer buf = ByteBuffer.allocate( 1024 );
+        encoder.encode( buf.buf(), ( DhcpMessage ) message );
+
+        buf.flip();
+
+        out.write( buf );
+    }
+
+
+    public void dispose( IoSession arg0 ) throws Exception
+    {
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/protocol/DhcpProtocolCodecFactory.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/protocol/DhcpProtocolCodecFactory.java
new file mode 100644
index 0000000..628d79a
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/protocol/DhcpProtocolCodecFactory.java
@@ -0,0 +1,61 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.protocol;
+
+
+import org.apache.mina.filter.codec.ProtocolCodecFactory;
+import org.apache.mina.filter.codec.ProtocolDecoder;
+import org.apache.mina.filter.codec.ProtocolEncoder;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class DhcpProtocolCodecFactory implements ProtocolCodecFactory
+{
+    private static DhcpProtocolCodecFactory INSTANCE = new DhcpProtocolCodecFactory();
+
+
+    /**
+     * Returns the singleton instance of {@link DhcpProtocolCodecFactory}.
+     *
+     * @return The singleton instance of {@link DhcpProtocolCodecFactory}.
+     */
+    public static DhcpProtocolCodecFactory getInstance()
+    {
+        return INSTANCE;
+    }
+
+
+    public ProtocolEncoder getEncoder()
+    {
+        // Create a new encoder.
+        return new DhcpEncoder();
+    }
+
+
+    public ProtocolDecoder getDecoder()
+    {
+        // Create a new decoder.
+        return new DhcpDecoder();
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/protocol/DhcpProtocolHandler.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/protocol/DhcpProtocolHandler.java
new file mode 100644
index 0000000..3558b9e
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/protocol/DhcpProtocolHandler.java
@@ -0,0 +1,174 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp.protocol;
+
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+
+import org.apache.directory.server.dhcp.messages.DhcpMessage;
+import org.apache.directory.server.dhcp.messages.MessageType;
+import org.apache.directory.server.dhcp.service.DhcpService;
+import org.apache.mina.common.BroadcastIoSession;
+import org.apache.mina.common.IdleStatus;
+import org.apache.mina.common.IoHandler;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.filter.codec.ProtocolCodecFilter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Implementation of a DHCP protocol handler which delegates the work of
+ * generating replys to a DhcpService implementation.
+ * 
+ * @see org.apache.directory.server.dhcp.service.DhcpService
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class DhcpProtocolHandler implements IoHandler {
+    private static final Logger logger = LoggerFactory
+            .getLogger(DhcpProtocolHandler.class);
+
+    /**
+     * Default DHCP client port
+     */
+    public static final int CLIENT_PORT = 68;
+
+    /**
+     * Default DHCP server port
+     */
+    public static final int SERVER_PORT = 67;
+
+    /**
+     * The DHCP service implementation. The implementation is supposed to be
+     * thread-safe.
+     */
+    private final DhcpService dhcpService;
+
+    /**
+     * 
+     */
+    public DhcpProtocolHandler(DhcpService service) {
+        this.dhcpService = service;
+    }
+
+    public void sessionCreated(IoSession session) throws Exception {
+        logger.debug("{} CREATED", session.getLocalAddress());
+        session.getFilterChain().addFirst("codec",
+                new ProtocolCodecFilter(new DhcpProtocolCodecFactory()));
+    }
+
+    public void sessionOpened(IoSession session) {
+        logger.debug("{} -> {} OPENED", session.getRemoteAddress(), session
+                .getLocalAddress());
+    }
+
+    public void sessionClosed(IoSession session) {
+        logger.debug("{} -> {} CLOSED", session.getRemoteAddress(), session
+                .getLocalAddress());
+    }
+
+    public void sessionIdle(IoSession session, IdleStatus status) {
+        // ignore
+    }
+
+    public void exceptionCaught(IoSession session, Throwable cause) {
+        logger.error("EXCEPTION CAUGHT ", cause);
+        cause.printStackTrace(System.out);
+
+        session.close();
+    }
+
+    public void messageReceived(IoSession session, Object message)
+            throws Exception {
+        if (logger.isDebugEnabled())
+            logger.debug("{} -> {} RCVD: {} " + message, session.getRemoteAddress(),
+                    session.getLocalAddress());
+
+        final DhcpMessage request = (DhcpMessage) message;
+
+        final DhcpMessage reply = dhcpService.getReplyFor(
+                (InetSocketAddress) session.getServiceAddress(),
+                (InetSocketAddress) session.getRemoteAddress(), request);
+
+        if (null != reply) {
+            final InetSocketAddress isa = determineMessageDestination(request, reply);
+            ((BroadcastIoSession) session).write(reply, isa);
+        }
+    }
+
+    /**
+     * Determine where to send the message: <br>
+     * If the 'giaddr' field in a DHCP message from a client is non-zero, the
+     * server sends any return messages to the 'DHCP server' port on the BOOTP
+     * relay agent whose address appears in 'giaddr'. If the 'giaddr' field is
+     * zero and the 'ciaddr' field is nonzero, then the server unicasts DHCPOFFER
+     * and DHCPACK messages to the address in 'ciaddr'. If 'giaddr' is zero and
+     * 'ciaddr' is zero, and the broadcast bit is set, then the server broadcasts
+     * DHCPOFFER and DHCPACK messages to 0xffffffff. If the broadcast bit is not
+     * set and 'giaddr' is zero and 'ciaddr' is zero, then the server unicasts
+     * DHCPOFFER and DHCPACK messages to the client's hardware address and
+     * 'yiaddr' address. In all cases, when 'giaddr' is zero, the server
+     * broadcasts any DHCPNAK messages to 0xffffffff.
+     * 
+     * @param request
+     * @param reply
+     * @return
+     */
+    private InetSocketAddress determineMessageDestination(DhcpMessage request,
+            DhcpMessage reply) {
+
+        final MessageType mt = reply.getMessageType();
+        if (!isNullAddress(request.getRelayAgentAddress()))
+            // send to agent, if received via agent.
+            return new InetSocketAddress(request.getRelayAgentAddress(), SERVER_PORT);
+        else if (null != mt && mt == MessageType.DHCPNAK)
+            // force broadcast for DHCPNAKs
+            return new InetSocketAddress("255.255.255.255", 68);
+        else // not a NAK...
+        if (!isNullAddress(request.getCurrentClientAddress()))
+            // have a current address? unicast to it.
+            return new InetSocketAddress(request.getCurrentClientAddress(),
+                    CLIENT_PORT);
+        else
+            return new InetSocketAddress("255.255.255.255", 68);
+    }
+
+    /**
+     * Determine, whether the given address ist actually the null address
+     * "0.0.0.0".
+     * 
+     * @param relayAgentAddress
+     * @return
+     */
+    private boolean isNullAddress(InetAddress addr) {
+        final byte a[] = addr.getAddress();
+        for (int i = 0; i < a.length; i++)
+            if (a[i] != 0)
+                return false;
+        return true;
+    }
+
+    public void messageSent(IoSession session, Object message) {
+        if (logger.isDebugEnabled())
+            logger.debug("{} -> {} SENT: " + message, session.getRemoteAddress(),
+                    session.getLocalAddress());
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/protocol/package-info.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/protocol/package-info.java
new file mode 100644
index 0000000..ac74443
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/protocol/package-info.java
@@ -0,0 +1,30 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+/**
+ * Provides the {@link org.apache.mina.common.IoHandler} and associated
+ * {@link org.apache.mina.filter.codec.ProtocolCodecFactory} required
+ * to implement the DHCP Service with the MINA NIO framework.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+
+package org.apache.directory.server.dhcp.protocol;
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/service/AbstractDhcpService.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/service/AbstractDhcpService.java
new file mode 100644
index 0000000..7924814
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/service/AbstractDhcpService.java
@@ -0,0 +1,296 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one or more

+ * contributor license agreements. See the NOTICE file distributed with this

+ * work for additional information regarding copyright ownership. The ASF

+ * licenses this file to you 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.

+ * 

+ */

+

+package org.apache.directory.server.dhcp.service;

+

+import java.net.InetAddress;

+import java.net.InetSocketAddress;

+import java.util.Iterator;

+

+import org.apache.directory.server.dhcp.DhcpException;

+import org.apache.directory.server.dhcp.messages.DhcpMessage;

+import org.apache.directory.server.dhcp.messages.MessageType;

+import org.apache.directory.server.dhcp.options.DhcpOption;

+import org.apache.directory.server.dhcp.options.OptionsField;

+import org.apache.directory.server.dhcp.options.dhcp.ParameterRequestList;

+import org.apache.directory.server.dhcp.options.dhcp.ServerIdentifier;

+import org.slf4j.Logger;

+import org.slf4j.LoggerFactory;

+

+/**

+ * Abstract implementation of the server-side DHCP protocol. This class just

+ * provides some utility methods and dispatches server-bound messages to handler

+ * methods which can be overridden to provide the functionality.

+ * <p>

+ * Client-bound messages and BOOTP messages are ignored.

+ * 

+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>

+ * @version $Rev: 545042 $, $Date: 2007-06-06 22:32:01 -0500 (Mi, 06 Jun 2007) $

+ * 

+ */

+public abstract class AbstractDhcpService implements DhcpService {

+    private static final Logger logger = LoggerFactory

+            .getLogger(AbstractDhcpService.class);

+

+    /*

+     * @see org.apache.directory.server.dhcp.DhcpService#getReplyFor(org.apache.directory.server.dhcp.messages.DhcpMessage)

+     */

+    public final DhcpMessage getReplyFor(InetSocketAddress localAddress,

+            InetSocketAddress clientAddress, DhcpMessage request)

+            throws DhcpException {

+        // ignore messages with an op != REQUEST/REPLY

+        if (request.getOp() != DhcpMessage.OP_BOOTREQUEST

+                && request.getOp() != DhcpMessage.OP_BOOTREPLY)

+            return null;

+

+        // message type option MUST be set - we don't support plain BOOTP.

+        if (null == request.getMessageType()) {

+            logger.warn("Missing message type option - plain BOOTP not supported.");

+            return null;

+        }

+

+        // dispatch based on the message type

+        switch (request.getMessageType().getCode()){

+            // client-to-server messages

+            case MessageType.CODE_DHCPDISCOVER :

+                return handleDISCOVER(localAddress, clientAddress, request);

+            case MessageType.CODE_DHCPREQUEST :

+                return handleREQUEST(localAddress, clientAddress, request);

+            case MessageType.CODE_DHCPRELEASE :

+                return handleRELEASE(localAddress, clientAddress, request);

+            case MessageType.CODE_DHCPINFORM :

+                return handleINFORM(localAddress, clientAddress, request);

+

+            case MessageType.CODE_DHCPOFFER :

+                return handleOFFER(localAddress, clientAddress, request);

+

+                // server-to-client messages

+            case MessageType.CODE_DHCPDECLINE :

+            case MessageType.CODE_DHCPACK :

+            case MessageType.CODE_DHCPNAK :

+                return null; // just ignore them

+

+            default :

+                return handleUnknownMessage(clientAddress, request);

+        }

+    }

+

+    /**

+     * Handle unknown DHCP message. The default implementation just logs and

+     * ignores it.

+     * 

+     * @param clientAddress

+     * @param request the request message

+     * @return DhcpMessage response message or <code>null</code> to ignore (don't reply to)

+     *         it.

+     */

+    protected DhcpMessage handleUnknownMessage(InetSocketAddress clientAddress,

+            DhcpMessage request) {

+        if (logger.isWarnEnabled())

+            logger.warn("Got unknkown DHCP message: " + request + " from:  "

+                    + clientAddress);

+        return null;

+    }

+

+    /**

+     * Handle DHCPINFORM message. The default implementation just ignores it.

+     * 

+     * @param localAddress

+     * @param clientAddress

+     * @param request the request message

+     * @return DhcpMessage response message or <code>null</code> to ignore (don't reply to)

+     *         it.

+     */

+    protected DhcpMessage handleINFORM(InetSocketAddress localAddress,

+            InetSocketAddress clientAddress, DhcpMessage request)

+            throws DhcpException {

+        if (logger.isDebugEnabled())

+            logger.debug("Got INFORM message: " + request + " from:  "

+                    + clientAddress);

+        return null;

+    }

+

+    /**

+     * Handle DHCPRELEASE message. The default implementation just ignores it.

+     * 

+     * @param localAddress

+     * @param clientAddress

+     * @param request the request message

+     * @return DhcpMessage response message or <code>null</code> to ignore (don't reply to)

+     *         it.

+     */

+    protected DhcpMessage handleRELEASE(InetSocketAddress localAddress,

+            InetSocketAddress clientAddress, DhcpMessage request)

+            throws DhcpException {

+        if (logger.isDebugEnabled())

+            logger.debug("Got RELEASE message: " + request + " from:  "

+                    + clientAddress);

+        return null;

+    }

+

+    /**

+     * Handle DHCPREQUEST message. The default implementation just ignores it.

+     * 

+     * @param localAddress

+     * @param clientAddress

+     * @param request the request message

+     * @return DhcpMessage response message or <code>null</code> to ignore (don't reply to)

+     *         it.

+     */

+    protected DhcpMessage handleREQUEST(InetSocketAddress localAddress,

+            InetSocketAddress clientAddress, DhcpMessage request)

+            throws DhcpException {

+        if (logger.isDebugEnabled())

+            logger.debug("Got REQUEST message: " + request + " from:  "

+                    + clientAddress);

+        return null;

+    }

+

+    /**

+     * Handle DHCPDISCOVER message. The default implementation just ignores it.

+     * 

+     * @param localAddress

+     * @param clientAddress

+     * @param request the request message

+     * @return DhcpMessage response message or <code>null</code> to ignore (don't reply to)

+     *         it.

+     * @throws DhcpException

+     */

+    protected DhcpMessage handleDISCOVER(InetSocketAddress localAddress,

+            InetSocketAddress clientAddress, DhcpMessage request)

+            throws DhcpException {

+        if (logger.isDebugEnabled())

+            logger.debug("Got DISCOVER message: " + request + " from:  "

+                    + clientAddress);

+        return null;

+    }

+

+    /**

+     * Handle DHCPOFFER message. The default implementation just ignores it.

+     * 

+     * @param localAddress

+     * @param clientAddress

+     * @param request the request message

+     * @return DhcpMessage response message or <code>null</code> to ignore (don't reply to)

+     *         it.

+     * @throws DhcpException

+     */

+    protected DhcpMessage handleOFFER(InetSocketAddress localAddress,

+            InetSocketAddress clientAddress, DhcpMessage request)

+            throws DhcpException {

+        if (logger.isDebugEnabled())

+            logger

+                    .debug("Got OFFER message: " + request + " from:  " + clientAddress);

+        return null;

+    }

+

+    /**

+     * Initialize a general DHCP reply message. Sets:

+     * <ul>

+     * <li>op=BOOTREPLY

+     * <li>htype, hlen, xid, flags, giaddr, chaddr like in request message

+     * <li>hops, secs to 0.

+     * <li>server hostname to the hostname appropriate for the interface the

+     * request was received on

+     * <li>the server identifier set to the address of the interface the request

+     * was received on

+     * </ul>

+     * 

+     * @param localAddress

+     * @param request

+     * @return DhcpMessage

+     */

+    protected final DhcpMessage initGeneralReply(InetSocketAddress localAddress,

+            DhcpMessage request) {

+        final DhcpMessage reply = new DhcpMessage();

+

+        reply.setOp(DhcpMessage.OP_BOOTREPLY);

+

+        reply.setHardwareAddress(request.getHardwareAddress());

+        reply.setTransactionId(request.getTransactionId());

+        reply.setFlags(request.getFlags());

+        reply.setRelayAgentAddress(request.getRelayAgentAddress());

+

+        // set server hostname

+        reply.setServerHostname(localAddress.getHostName());

+

+        // set server identifier based on the IF on which we received the packet

+        reply.getOptions().add(new ServerIdentifier(localAddress.getAddress()));

+

+        return reply;

+    }

+

+    /**

+     * Check if an address is the zero-address

+     * 

+     * @param addr

+     * @return boolean

+     */

+    private boolean isZeroAddress(byte[] addr) {

+        for (int i = 0; i < addr.length; i++)

+            if (addr[i] != 0)

+                return false;

+        return true;

+    }

+

+    /**

+     * Determine address on which to base selection. If the relay agent address is

+     * set, we use the relay agent's address, otherwise we use the address we

+     * received the request from.

+     * 

+     * @param clientAddress

+     * @param request

+     * @return InetAddress

+     */

+    protected final InetAddress determineSelectionBase(

+            InetSocketAddress clientAddress, DhcpMessage request) {

+        // FIXME: do we know

+        // a) the interface address over which we received a message (!)

+        // b) the client address (if specified)

+        // c) the relay agent address?

+

+        // if the relay agent address is set, we use it as the selection base

+        if (!isZeroAddress(request.getRelayAgentAddress().getAddress()))

+            return request.getRelayAgentAddress();

+

+        return clientAddress.getAddress();

+    }

+

+    /**

+     * Strip options that the client doesn't want, if the ParameterRequestList

+     * option is present.

+     * 

+     * @param request

+     * @param options

+     */

+    protected final void stripUnwantedOptions(DhcpMessage request,

+            OptionsField options) {

+        final ParameterRequestList prl = (ParameterRequestList) request

+                .getOptions().get(ParameterRequestList.class);

+        if (null != prl) {

+            final byte[] list = prl.getData();

+            for (final Iterator i = options.iterator(); i.hasNext();) {

+                final DhcpOption o = (DhcpOption) i.next();

+                for (int j = 0; j < list.length; j++)

+                    if (list[j] == o.getTag())

+                        continue;

+                i.remove();

+            }

+        }

+    }

+}

diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/service/DhcpService.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/service/DhcpService.java
new file mode 100644
index 0000000..af0ca1c
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/service/DhcpService.java
@@ -0,0 +1,49 @@
+/*

+ *  Licensed to the Apache Software Foundation (ASF) under one

+ *  or more contributor license agreements.  See the NOTICE file

+ *  distributed with this work for additional information

+ *  regarding copyright ownership.  The ASF licenses this file

+ *  to you 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. 

+ *  

+ */

+

+package org.apache.directory.server.dhcp.service;

+

+

+import java.net.InetSocketAddress;

+

+import org.apache.directory.server.dhcp.DhcpException;

+import org.apache.directory.server.dhcp.messages.DhcpMessage;

+

+

+/**

+ * DHCP Protocol (RFC 2131, RFC 2132). Implementations of the DHCP service must

+ * be thread-safe with respect to concurrent calls to getReplyFor().

+ * 

+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>

+ * @version $Rev: 545042 $, $Date: 2007-06-06 22:32:01 -0500 (Mi, 06 Jun 2007) $

+ */

+public interface DhcpService

+{

+    /**

+     * Retrieve the reply to a given message. The reply may be zero, if the

+     * message should be ignored.

+     * @param localAddress TODO

+     * @param clientAddress 

+     * @param request

+     * @return DhcpMessage

+     * @throws DhcpException 

+     */

+    public DhcpMessage getReplyFor( InetSocketAddress localAddress, InetSocketAddress clientAddress, DhcpMessage request ) throws DhcpException;

+}

diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/service/Lease.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/service/Lease.java
new file mode 100644
index 0000000..7f3fc81
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/service/Lease.java
@@ -0,0 +1,174 @@
+/*

+ *  Licensed to the Apache Software Foundation (ASF) under one

+ *  or more contributor license agreements.  See the NOTICE file

+ *  distributed with this work for additional information

+ *  regarding copyright ownership.  The ASF licenses this file

+ *  to you 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. 

+ *  

+ */

+package org.apache.directory.server.dhcp.service;

+

+

+import java.net.InetAddress;

+

+import org.apache.directory.server.dhcp.messages.HardwareAddress;

+import org.apache.directory.server.dhcp.options.OptionsField;

+

+

+/**

+ * Leases represent a temporary assignment of an IP address to a DHCP client.

+ * 

+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>

+ * @version $Rev: 545042 $, $Date: 2007-06-06 22:32:01 -0500 (Mi, 06 Jun 2007) $

+ */

+public class Lease

+{

+    /** Lease state: newly created */

+    public static final int STATE_NEW = 1;

+

+    /** Lease state: offered to client */

+    public static final int STATE_OFFERED = 2;

+

+    /** Lease state: active - assigned to client */

+    public static final int STATE_ACTIVE = 3;

+

+    /** Lease state: released by client */

+    public static final int STATE_RELEASED = 4;

+

+    /** Lease state: expired */

+    public static final int STATE_EXPIRED = 5;

+

+    /**

+     * The lease's state.

+     * 

+     * @see #STATE_NEW

+     * @see #STATE_OFFERED

+     * @see #STATE_ACTIVE

+     * @see #STATE_RELEASED

+     * @see #STATE_EXPIRED

+     */

+    private int state;

+

+    /**

+     * The assigned client address.

+     */

+    private InetAddress clientAddress;

+

+    /**

+     * The client's hardware address.

+     */

+    private HardwareAddress hardwareAddress;

+

+    /**

+     * The next-server (boot-server) address.

+     */

+    private InetAddress nextServerAddress;

+

+    /**

+     * The DhcpOptions to provide to the client along with the lease.

+     */

+    private OptionsField options = new OptionsField();

+

+    private long acquired = -1;

+

+    private long expires = -1;

+

+

+    /**

+     * @return InetAddress

+     */

+    public InetAddress getClientAddress()

+    {

+        return clientAddress;

+    }

+

+

+    /**

+     * @return InetAddress

+     */

+    public InetAddress getNextServerAddress()

+    {

+        return nextServerAddress;

+    }

+

+

+    /**

+     * @return OptionsField

+     */

+    public OptionsField getOptions()

+    {

+        return options;

+    }

+

+

+    /**

+     * @return int

+     */

+    public int getState()

+    {

+        return state;

+    }

+

+

+    /**

+     * @param state

+     */

+    public void setState( int state )

+    {

+        this.state = state;

+    }

+

+

+    public HardwareAddress getHardwareAddress()

+    {

+        return hardwareAddress;

+    }

+

+

+    public void setHardwareAddress( HardwareAddress hardwareAddress )

+    {

+        this.hardwareAddress = hardwareAddress;

+    }

+

+

+    public long getAcquired()

+    {

+        return acquired;

+    }

+

+

+    public void setAcquired( long acquired )

+    {

+        this.acquired = acquired;

+    }

+

+

+    public long getExpires()

+    {

+        return expires;

+    }

+

+

+    public void setExpires( long expires )

+    {

+        this.expires = expires;

+    }

+

+

+    public void setClientAddress( InetAddress clientAddress )

+    {

+        this.clientAddress = clientAddress;

+    }

+

+}

diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/service/StoreBasedDhcpService.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/service/StoreBasedDhcpService.java
new file mode 100644
index 0000000..8dfbda6
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/service/StoreBasedDhcpService.java
@@ -0,0 +1,290 @@
+/*

+ *  Licensed to the Apache Software Foundation (ASF) under one

+ *  or more contributor license agreements.  See the NOTICE file

+ *  distributed with this work for additional information

+ *  regarding copyright ownership.  The ASF licenses this file

+ *  to you 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. 

+ *  

+ */

+package org.apache.directory.server.dhcp.service;

+

+

+import java.net.InetAddress;

+import java.net.InetSocketAddress;

+

+import org.apache.directory.server.dhcp.DhcpException;

+import org.apache.directory.server.dhcp.messages.DhcpMessage;

+import org.apache.directory.server.dhcp.messages.MessageType;

+import org.apache.directory.server.dhcp.options.AddressOption;

+import org.apache.directory.server.dhcp.options.OptionsField;

+import org.apache.directory.server.dhcp.options.dhcp.ClientIdentifier;

+import org.apache.directory.server.dhcp.options.dhcp.IpAddressLeaseTime;

+import org.apache.directory.server.dhcp.options.dhcp.MaximumDhcpMessageSize;

+import org.apache.directory.server.dhcp.options.dhcp.ParameterRequestList;

+import org.apache.directory.server.dhcp.options.dhcp.RequestedIpAddress;

+import org.apache.directory.server.dhcp.options.dhcp.ServerIdentifier;

+import org.apache.directory.server.dhcp.store.DhcpStore;

+

+

+/**

+ * A default implementation of the DHCP service. Does the tedious low-level

+ * chores of handling DHCP messages, but delegates the lease-handling to a

+ * supplied DhcpStore.

+ * 

+ * @see org.apache.directory.server.dhcp.store.DhcpStore

+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>

+ * @version $Rev: 545042 $, $Date: 2007-06-06 22:32:01 -0500 (Mi, 06 Jun 2007) $

+ */

+public class StoreBasedDhcpService extends AbstractDhcpService

+{

+    private final DhcpStore dhcpStore;

+

+

+    public StoreBasedDhcpService(DhcpStore dhcpStore)

+    {

+        this.dhcpStore = dhcpStore;

+    }

+

+

+    /**

+     * Try to get an existing lease. The lease may have been created during

+     * earlier DHCP negotiations or a recent DHCPDISCOVER.

+     * 

+     * @param clientAddress

+     * @param request

+     * @return

+     * @throws DhcpException

+     */

+    private Lease getExistingLease( InetSocketAddress clientAddress, DhcpMessage request ) throws DhcpException

+    {

+        // determine requested lease time

+        IpAddressLeaseTime requestedLeaseTimeOption = ( IpAddressLeaseTime ) request.getOptions().get(

+            IpAddressLeaseTime.class );

+        long requestedLeaseTime = null != requestedLeaseTimeOption ? requestedLeaseTimeOption.getIntValue() * 1000

+            : -1L;

+

+        // try to get the lease (address) requested by the client

+        InetAddress requestedAddress = null;

+        AddressOption requestedAddressOption = ( AddressOption ) request.getOptions().get( RequestedIpAddress.class );

+        if ( null != requestedAddressOption )

+            requestedAddress = requestedAddressOption.getAddress();

+        if ( null == requestedAddress )

+            requestedAddress = request.getCurrentClientAddress();

+

+        InetAddress selectionBase = determineSelectionBase( clientAddress, request );

+

+        Lease lease = dhcpStore.getExistingLease( request.getHardwareAddress(), requestedAddress, selectionBase,

+            requestedLeaseTime, request.getOptions() );

+

+        if ( null == lease )

+            return null;

+

+        return lease;

+    }

+

+

+    /**

+     * Determine a lease to offer in response to a DHCPDISCOVER message.

+     * <p>

+     * When a server receives a DHCPDISCOVER message from a client, the server

+     * chooses a network address for the requesting client. If no address is

+     * available, the server may choose to report the problem to the system

+     * administrator. If an address is available, the new address SHOULD be

+     * chosen as follows:

+     * <ul>

+     * <li> The client's current address as recorded in the client's current

+     * binding, ELSE

+     * <li> The client's previous address as recorded in the client's (now

+     * expired or released) binding, if that address is in the server's pool of

+     * available addresses and not already allocated, ELSE

+     * <li> The address requested in the 'Requested IP Address' option, if that

+     * address is valid and not already allocated, ELSE

+     * <li> A new address allocated from the server's pool of available

+     * addresses; the address is selected based on the subnet from which the

+     * message was received (if 'giaddr' is 0) or on the address of the relay

+     * agent that forwarded the message ('giaddr' when not 0).

+     * </ul>

+     * 

+     * @param clientAddress

+     * @param request

+     * @return

+     */

+    private Lease getLeaseOffer( InetSocketAddress clientAddress, DhcpMessage request ) throws DhcpException

+    {

+        // determine requested lease time

+        IpAddressLeaseTime requestedLeaseTimeOption = ( IpAddressLeaseTime ) request.getOptions().get(

+            IpAddressLeaseTime.class );

+        long requestedLeaseTime = null != requestedLeaseTimeOption ? requestedLeaseTimeOption.getIntValue() * 1000

+            : -1L;

+

+        // try to get the lease (address) requested by the client

+        InetAddress requestedAddress = null;

+        AddressOption requestedAddressOption = ( AddressOption ) request.getOptions().get( RequestedIpAddress.class );

+        if ( null != requestedAddressOption )

+            requestedAddress = requestedAddressOption.getAddress();

+

+        InetAddress selectionBase = determineSelectionBase( clientAddress, request );

+

+        Lease lease = dhcpStore.getLeaseOffer( request.getHardwareAddress(), requestedAddress, selectionBase,

+            requestedLeaseTime, request.getOptions() );

+

+        return lease;

+    }

+

+

+    /*

+     * @see org.apache.directory.server.dhcp.service.AbstractDhcpService#handleRELEASE(java.net.InetSocketAddress,

+     *      java.net.InetSocketAddress,

+     *      org.apache.directory.server.dhcp.messages.DhcpMessage)

+     */

+    protected DhcpMessage handleRELEASE( InetSocketAddress localAddress, InetSocketAddress clientAddress,

+        DhcpMessage request ) throws DhcpException

+    {

+        // check server ident

+        AddressOption serverIdentOption = ( AddressOption ) request.getOptions().get( ServerIdentifier.class );

+        if ( null != serverIdentOption && serverIdentOption.getAddress().isAnyLocalAddress() )

+            return null; // not me?! FIXME: handle authoritative server case

+

+        Lease lease = getExistingLease( clientAddress, request );

+

+        DhcpMessage reply = initGeneralReply( localAddress, request );

+

+        if ( null == lease )

+        {

+            // null lease? send NAK

+            // FIXME...

+            reply.setMessageType( MessageType.DHCPNAK );

+            reply.setCurrentClientAddress( null );

+            reply.setAssignedClientAddress( null );

+            reply.setNextServerAddress( null );

+        }

+        else

+        {

+            dhcpStore.releaseLease( lease );

+

+            // lease Ok, send ACK

+            // FIXME...

+            reply.getOptions().merge( lease.getOptions() );

+

+            reply.setAssignedClientAddress( lease.getClientAddress() );

+            reply.setNextServerAddress( lease.getNextServerAddress() );

+

+            // fix options

+            OptionsField options = reply.getOptions();

+

+            // these options must not be present

+            options.remove( RequestedIpAddress.class );

+            options.remove( ParameterRequestList.class );

+            options.remove( ClientIdentifier.class );

+            options.remove( MaximumDhcpMessageSize.class );

+

+            // these options must be present

+            options.add( new IpAddressLeaseTime( ( lease.getExpires() - System.currentTimeMillis() ) / 1000L ) );

+

+            stripUnwantedOptions( request, options );

+        }

+        return reply;

+

+    }

+

+

+    /*

+     * @see org.apache.directory.server.dhcp.service.AbstractDhcpService#handleDISCOVER(java.net.InetSocketAddress,

+     *      org.apache.directory.server.dhcp.messages.DhcpMessage)

+     */

+    protected DhcpMessage handleDISCOVER( InetSocketAddress localAddress, InetSocketAddress clientAddress,

+        DhcpMessage request ) throws DhcpException

+    {

+        Lease lease = getLeaseOffer( clientAddress, request );

+

+        // null lease? don't offer one.

+        if ( null == lease )

+            return null;

+

+        DhcpMessage reply = initGeneralReply( localAddress, request );

+

+        reply.getOptions().merge( lease.getOptions() );

+

+        reply.setMessageType( MessageType.DHCPOFFER );

+

+        reply.setAssignedClientAddress( lease.getClientAddress() );

+        reply.setNextServerAddress( lease.getNextServerAddress() );

+

+        // fix options

+        OptionsField options = reply.getOptions();

+

+        // these options must not be present

+        options.remove( RequestedIpAddress.class );

+        options.remove( ParameterRequestList.class );

+        options.remove( ClientIdentifier.class );

+        options.remove( MaximumDhcpMessageSize.class );

+

+        // these options must be present

+        options.add( new IpAddressLeaseTime( ( lease.getExpires() - System.currentTimeMillis() ) / 1000L ) );

+

+        stripUnwantedOptions( request, options );

+

+        return reply;

+    }

+

+

+    /*

+     * @see org.apache.directory.server.dhcp.service.AbstractDhcpService#handleREQUEST(java.net.InetSocketAddress,

+     *      org.apache.directory.server.dhcp.messages.DhcpMessage)

+     */

+    protected DhcpMessage handleREQUEST( InetSocketAddress localAddress, InetSocketAddress clientAddress,

+        DhcpMessage request ) throws DhcpException

+    {

+        // check server ident

+        AddressOption serverIdentOption = ( AddressOption ) request.getOptions().get( ServerIdentifier.class );

+        if ( null != serverIdentOption && serverIdentOption.getAddress().isAnyLocalAddress() )

+            return null; // not me?! FIXME: handle authoritative server case

+

+        Lease lease = getExistingLease( clientAddress, request );

+

+        DhcpMessage reply = initGeneralReply( localAddress, request );

+

+        if ( null == lease )

+        {

+            // null lease? send NAK

+            reply.setMessageType( MessageType.DHCPNAK );

+            reply.setCurrentClientAddress( null );

+            reply.setAssignedClientAddress( null );

+            reply.setNextServerAddress( null );

+        }

+        else

+        {

+            // lease Ok, send ACK

+            reply.getOptions().merge( lease.getOptions() );

+

+            reply.setAssignedClientAddress( lease.getClientAddress() );

+            reply.setNextServerAddress( lease.getNextServerAddress() );

+

+            // fix options

+            OptionsField options = reply.getOptions();

+

+            // these options must not be present

+            options.remove( RequestedIpAddress.class );

+            options.remove( ParameterRequestList.class );

+            options.remove( ClientIdentifier.class );

+            options.remove( MaximumDhcpMessageSize.class );

+

+            // these options must be present

+            options.add( new IpAddressLeaseTime( ( lease.getExpires() - System.currentTimeMillis() ) / 1000L ) );

+

+            stripUnwantedOptions( request, options );

+        }

+        return reply;

+    }

+}

diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/service/package-info.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/service/package-info.java
new file mode 100644
index 0000000..6e5830f
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/service/package-info.java
@@ -0,0 +1,28 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+/**
+ * Provides the implementation of the {@link org.apache.directory.server.dhcp.service.DhcpService}.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+
+package org.apache.directory.server.dhcp.service;
diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/store/AbstractDhcpStore.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/store/AbstractDhcpStore.java
new file mode 100644
index 0000000..d054690
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/store/AbstractDhcpStore.java
@@ -0,0 +1,316 @@
+/*

+ *  Licensed to the Apache Software Foundation (ASF) under one

+ *  or more contributor license agreements.  See the NOTICE file

+ *  distributed with this work for additional information

+ *  regarding copyright ownership.  The ASF licenses this file

+ *  to you 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. 

+ *  

+ */

+package org.apache.directory.server.dhcp.store;

+

+

+import java.net.InetAddress;

+import java.util.Map;

+

+import org.apache.directory.server.dhcp.DhcpException;

+import org.apache.directory.server.dhcp.messages.HardwareAddress;

+import org.apache.directory.server.dhcp.options.OptionsField;

+import org.apache.directory.server.dhcp.options.vendor.HostName;

+import org.apache.directory.server.dhcp.options.vendor.SubnetMask;

+import org.apache.directory.server.dhcp.service.Lease;

+import org.slf4j.Logger;

+import org.slf4j.LoggerFactory;

+

+

+/**

+ * Abstract base implementation of a {@link DhcpStore}.

+ * 

+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>

+ * @version $Rev: 545042 $, $Date: 2007-06-06 22:32:01 -0500 (Mi, 06 Jun 2007) $

+ */

+public abstract class AbstractDhcpStore implements DhcpStore

+{

+    private static final Logger logger = LoggerFactory.getLogger( AbstractDhcpStore.class );

+

+

+    /*

+     * @see org.apache.directory.server.dhcp.service.DhcpStore#getLeaseOffer(org.apache.directory.server.dhcp.messages.HardwareAddress,

+     *      java.net.InetAddress, java.net.InetAddress, long,

+     *      org.apache.directory.server.dhcp.options.OptionsField)

+     */

+    public Lease getLeaseOffer( HardwareAddress hardwareAddress, InetAddress requestedAddress,

+        InetAddress selectionBase, long requestedLeaseTime, OptionsField options ) throws DhcpException

+    {

+        Subnet subnet = findSubnet( selectionBase );

+        if ( null == subnet )

+        {

+            logger.warn( "Don't know anything about the sbnet containing " + selectionBase );

+            return null;

+        }

+

+        // try to find existing lease

+        Lease lease = null;

+        lease = findExistingLease( hardwareAddress, lease );

+        if ( null != lease )

+            return lease;

+

+        Host host = null;

+        host = findDesignatedHost( hardwareAddress );

+        if ( null != host )

+        {

+            // make sure that the host is actually within the subnet. Depending

+            // on the way the DhcpStore configuration is implemented, it is not

+            // possible to violate this condition, but we can't be sure.

+            if ( !subnet.contains( host.getAddress() ) )

+            {

+                logger.warn( "Host " + host + " is not within the subnet for which an address is requested" );

+            }

+            else

+            {

+                // build properties map

+                Map properties = getProperties( subnet );

+                properties.putAll( getProperties( host ) );

+

+                // build lease

+                lease = new Lease();

+                lease.setAcquired( System.currentTimeMillis() );

+

+                long leaseTime = determineLeaseTime( requestedLeaseTime, properties );

+

+                lease.setExpires( System.currentTimeMillis() + leaseTime );

+

+                lease.setHardwareAddress( hardwareAddress );

+                lease.setState( Lease.STATE_NEW );

+                lease.setClientAddress( host.getAddress() );

+

+                // set lease options

+                OptionsField o = lease.getOptions();

+

+                // set (client) host name

+                o.add( new HostName( host.getName() ) );

+

+                // add subnet settings

+                o.add( new SubnetMask( subnet.getNetmask() ) );

+                o.merge( subnet.getOptions() );

+

+                // add the host's options. they override existing

+                // subnet options as they take the precedence.

+                o.merge( host.getOptions() );

+            }

+        }

+

+        if ( null == lease )

+        {

+            // FIXME: use selection base to find a lease in a pool.

+        }

+

+        // update the lease state

+        if ( null != lease && lease.getState() != Lease.STATE_ACTIVE )

+        {

+            lease.setState( Lease.STATE_OFFERED );

+            updateLease( lease );

+        }

+

+        return lease;

+    }

+

+

+    /*

+     * @see org.apache.directory.server.dhcp.store.DhcpStore#getExistingLease(org.apache.directory.server.dhcp.messages.HardwareAddress,

+     *      java.net.InetAddress, java.net.InetAddress, long,

+     *      org.apache.directory.server.dhcp.options.OptionsField)

+     */

+    public Lease getExistingLease( HardwareAddress hardwareAddress, InetAddress requestedAddress,

+        InetAddress selectionBase, long requestedLeaseTime, OptionsField options ) throws DhcpException

+    {

+        // try to find existing lease. if we don't find a lease based on the

+        // client's

+        // hardware address, we send a NAK.

+        Lease lease = null;

+        lease = findExistingLease( hardwareAddress, lease );

+        if ( null == lease )

+            return null;

+

+        // check whether the notions of the client address match

+        if ( !lease.getClientAddress().equals( requestedAddress ) )

+        {

+            logger.warn( "Requested address " + requestedAddress + " for " + hardwareAddress

+                + " doesn't match existing lease " + lease );

+            return null;

+        }

+

+        // check whether addresses and subnet match

+        Subnet subnet = findSubnet( selectionBase );

+        if ( null == subnet )

+        {

+            logger.warn( "No subnet found for existing lease " + lease );

+            return null;

+        }

+        if ( !subnet.contains( lease.getClientAddress() ) )

+        {

+            logger.warn( "Client with existing lease " + lease + " is on wrong subnet " + subnet );

+            return null;

+        }

+        if ( !subnet.isInRange( lease.getClientAddress() ) )

+        {

+            logger.warn( "Client with existing lease " + lease + " is out of valid range for subnet " + subnet );

+            return null;

+        }

+

+        // build properties map

+        Map properties = getProperties( subnet );

+

+        // update lease options

+        OptionsField o = lease.getOptions();

+        o.clear();

+

+        // add subnet settings

+        o.add( new SubnetMask( subnet.getNetmask() ) );

+        o.merge( subnet.getOptions() );

+

+        // check whether there is a designated host.

+        Host host = findDesignatedHost( hardwareAddress );

+        if ( null != host )

+        {

+            // check whether the host matches the address (using a fixed

+            // host address is mandatory).

+            if ( host.getAddress() != null && !host.getAddress().equals( lease.getClientAddress() ) )

+            {

+                logger.warn( "Existing fixed address for " + hardwareAddress + " conflicts with existing lease "

+                    + lease );

+                return null;

+            }

+

+            properties.putAll( getProperties( host ) );

+

+            // set (client) host name

+            o.add( new HostName( host.getName() ) );

+

+            // add the host's options

+            o.merge( host.getOptions() );

+        }

+

+        // update other lease fields

+        long leaseTime = determineLeaseTime( requestedLeaseTime, properties );

+        lease.setExpires( System.currentTimeMillis() + leaseTime );

+        lease.setHardwareAddress( hardwareAddress );

+

+        // update the lease state

+        if ( lease.getState() != Lease.STATE_ACTIVE )

+        {

+            lease.setState( Lease.STATE_ACTIVE );

+            updateLease( lease );

+        }

+

+        // store information about the lease

+        updateLease( lease );

+

+        return lease;

+    }

+

+

+    /**

+     * Determine the lease time based on the time requested by the client, the

+     * properties and a global default.

+     * 

+     * @param requestedLeaseTime

+     * @param properties

+     * @return long

+     */

+    private long determineLeaseTime( long requestedLeaseTime, Map properties )

+    {

+        // built-in default

+        long leaseTime = 1000L * 3600;

+        Integer propMaxLeaseTime = ( Integer ) properties.get( DhcpConfigElement.PROPERTY_MAX_LEASE_TIME );

+        if ( null != propMaxLeaseTime )

+            if ( requestedLeaseTime > 0 )

+                leaseTime = Math.min( propMaxLeaseTime.intValue() * 1000L, requestedLeaseTime );

+            else

+                leaseTime = propMaxLeaseTime.intValue() * 1000L;

+        return leaseTime;

+    }

+

+

+    /*

+     * @see org.apache.directory.server.dhcp.store.DhcpStore#releaseLease(org.apache.directory.server.dhcp.service.Lease)

+     */

+    public void releaseLease( Lease lease )

+    {

+        lease.setState( Lease.STATE_RELEASED );

+        updateLease( lease );

+    }

+

+

+    /**

+     * Update the (possibly changed) lease in the store.

+     * 

+     * @param lease

+     */

+    protected abstract void updateLease( Lease lease );

+

+

+    /**

+     * Return a list of all options applicable to the given config element. List

+     * list must contain the options specified for the element and all parent

+     * elements in an aggregated fashion. For instance, the options for a host

+     * must include the global default options, the options of classes the host

+     * is a member of, the host's group options and the host's options.

+     * 

+     * @param element

+     * @return OptionsField

+     */

+    protected abstract OptionsField getOptions( DhcpConfigElement element );

+

+

+    /**

+     * Return a list of all options applicable to the given config element. List

+     * list must contain the options specified for the element and all parent

+     * elements in an aggregated fashion. For instance, the options for a host

+     * must include the global default options, the options of classes the host

+     * is a member of, the host's group options and the host's options.

+     * 

+     * @param element

+     * @return Map

+     */

+    protected abstract Map getProperties( DhcpConfigElement element );

+

+

+    /**

+     * Find an existing lease in the store.

+     * 

+     * @param hardwareAddress

+     * @param existingLease

+     * @return Map

+     */

+    protected abstract Lease findExistingLease( HardwareAddress hardwareAddress, Lease existingLease );

+

+

+    /**

+     * Find a host to with the explicitely designated hardware address.

+     * 

+     * @param hardwareAddress

+     * @return Host

+     * @throws DhcpException

+     */

+    protected abstract Host findDesignatedHost( HardwareAddress hardwareAddress ) throws DhcpException;

+

+

+    /**

+     * Find the subnet definition matching the given address.

+     * 

+     * @param clientAddress

+     * @return Subnet

+     */

+    protected abstract Subnet findSubnet( InetAddress clientAddress );

+}

diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/store/DhcpConfigElement.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/store/DhcpConfigElement.java
new file mode 100644
index 0000000..7a75a07
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/store/DhcpConfigElement.java
@@ -0,0 +1,55 @@
+/*

+ *  Licensed to the Apache Software Foundation (ASF) under one

+ *  or more contributor license agreements.  See the NOTICE file

+ *  distributed with this work for additional information

+ *  regarding copyright ownership.  The ASF licenses this file

+ *  to you 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. 

+ *  

+ */

+package org.apache.directory.server.dhcp.store;

+

+

+import java.util.HashMap;

+import java.util.Map;

+

+import org.apache.directory.server.dhcp.options.OptionsField;

+

+

+/**

+ * 

+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>

+ * @version $Rev: 545042 $, $Date: 2007-06-06 22:32:01 -0500 (Mi, 06 Jun 2007) $

+ */

+public abstract class DhcpConfigElement

+{

+    public static final String PROPERTY_MAX_LEASE_TIME = "max-lease-time";

+        

+    /** List of DhcpOptions for ths subnet */

+    private OptionsField options = new OptionsField();

+

+    /** Map of properties for this element */

+    private Map properties = new HashMap();

+

+

+    public OptionsField getOptions()

+    {

+        return options;

+    }

+

+

+    public Map getProperties()

+    {

+        return properties;

+    }

+}

diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/store/DhcpStore.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/store/DhcpStore.java
new file mode 100644
index 0000000..e9d682d
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/store/DhcpStore.java
@@ -0,0 +1,103 @@
+/*

+ *  Licensed to the Apache Software Foundation (ASF) under one

+ *  or more contributor license agreements.  See the NOTICE file

+ *  distributed with this work for additional information

+ *  regarding copyright ownership.  The ASF licenses this file

+ *  to you 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. 

+ *  

+ */

+package org.apache.directory.server.dhcp.store;

+

+

+import java.net.InetAddress;

+

+import org.apache.directory.server.dhcp.DhcpException;

+import org.apache.directory.server.dhcp.messages.HardwareAddress;

+import org.apache.directory.server.dhcp.options.OptionsField;

+import org.apache.directory.server.dhcp.service.Lease;

+

+

+/**

+ * 

+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>

+ * @version $Rev: 545042 $, $Date: 2007-06-06 22:32:01 -0500 (Mi, 06 Jun 2007) $

+ */

+public interface DhcpStore

+{

+    /**

+     * Find a lease to offer in response to a DHCPDISCOVER request.

+     * <p>

+     * The lease to offer should be determined by an algorithme like the

+     * following:

+     * <ul>

+     * <li> Try to find an existing lease for the given hardware address. The

+     * lease may be either ACTIVE or EXPIRED.

+     * <li>Try to find a lease which has been explicitely dedicated to the

+     * given hardware address.

+     * <li>Try to get a lease from a pool of leases. If the client requested a

+     * specific address, the request should be honored, if possible. Otherwise

+     * the selection of an address should be based on the selection base address

+     * and may be refined using the supplied options.

+     * </ul>

+     * <p>

+     * If the requestedLeaseTime is >= 0, the validity duration of the returned

+     * lease must be updated, so that the lease is valid for at least the

+     * specified time. The duration may, however, be constrained by a configured

+     * maximum lease time.

+     * 

+     * @param hardwareAddress

+     *            hardwareAddress the hardware address of the client requesting

+     *            the lease.

+     * @param requestedAddress

+     *            the address requested by the client or <code>null</code> if

+     *            the client did not request a specific address.

+     * @param selectionBase

+     *            the address on which to base the selection of a lease from a

+     *            pool, i.e. either the address of the interface on which the

+     *            request was received or the address of a DHCP relay agent.

+     * @param requestedLeaseTime

+     *            the lease time in milliseconds as requested by the client, or

+     *            -1 if the client did not request a specific lease time.

+     * @param options

+     *            the supplied DHCP options. Lease selection may be refined by

+     *            using those options

+     * @return a lease or <code>null</code> if no matching lease was found.

+     * @throws DhcpException

+     */

+    Lease getLeaseOffer( HardwareAddress hardwareAddress, InetAddress requestedAddress, InetAddress selectionBase,

+        long requestedLeaseTime, OptionsField options ) throws DhcpException;

+

+

+    /**

+     * Retrieve an existing lease from the dhcp store.

+     * 

+     * @param hardwareAddress

+     * @param requestedAddress

+     * @param selectionBase

+     * @param requestedLeaseTime

+     * @param options

+     * @return Lease

+     * @throws DhcpException 

+     */

+    Lease getExistingLease( HardwareAddress hardwareAddress, InetAddress requestedAddress, InetAddress selectionBase,

+        long requestedLeaseTime, OptionsField options ) throws DhcpException;

+

+

+    /**

+     * Release the specified lease. 

+     * 

+     * @param lease

+     */

+    void releaseLease( Lease lease );

+}

diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/store/Host.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/store/Host.java
new file mode 100644
index 0000000..395c272
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/store/Host.java
@@ -0,0 +1,70 @@
+/*

+ *  Licensed to the Apache Software Foundation (ASF) under one

+ *  or more contributor license agreements.  See the NOTICE file

+ *  distributed with this work for additional information

+ *  regarding copyright ownership.  The ASF licenses this file

+ *  to you 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. 

+ *  

+ */

+package org.apache.directory.server.dhcp.store;

+

+

+import java.net.InetAddress;

+

+import org.apache.directory.server.dhcp.messages.HardwareAddress;

+

+

+/**

+ * The definition of a host.

+ * 

+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>

+ * @version $Rev: 545042 $, $Date: 2007-06-06 22:32:01 -0500 (Mi, 06 Jun 2007) $

+ */

+public class Host extends DhcpConfigElement

+{

+    private final String name;

+

+    private HardwareAddress hardwareAddress;

+

+    /**

+     * The host's fixed address. May be <code>null</code>.

+     */

+    private InetAddress address;

+

+

+    public Host(String name, InetAddress address, HardwareAddress hardwareAddress)

+    {

+        this.name = name;

+        this.address = address;

+        this.hardwareAddress = hardwareAddress;

+    }

+

+

+    public HardwareAddress getHardwareAddress()

+    {

+        return hardwareAddress;

+    }

+

+

+    public String getName()

+    {

+        return name;

+    }

+

+

+    public InetAddress getAddress()

+    {

+        return address;

+    }

+}

diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/store/SimpleDhcpStore.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/store/SimpleDhcpStore.java
new file mode 100644
index 0000000..e74a3ce
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/store/SimpleDhcpStore.java
@@ -0,0 +1,201 @@
+/*

+ *  Licensed to the Apache Software Foundation (ASF) under one

+ *  or more contributor license agreements.  See the NOTICE file

+ *  distributed with this work for additional information

+ *  regarding copyright ownership.  The ASF licenses this file

+ *  to you 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. 

+ *  

+ */

+package org.apache.directory.server.dhcp.store;

+

+

+import java.net.InetAddress;

+import java.net.UnknownHostException;

+import java.util.ArrayList;

+import java.util.HashMap;

+import java.util.Hashtable;

+import java.util.Iterator;

+import java.util.List;

+import java.util.Map;

+

+import javax.naming.Context;

+import javax.naming.NamingEnumeration;

+import javax.naming.NamingException;

+import javax.naming.directory.Attribute;

+import javax.naming.directory.Attributes;

+import javax.naming.directory.DirContext;

+import javax.naming.directory.InitialDirContext;

+import javax.naming.directory.SearchControls;

+import javax.naming.directory.SearchResult;

+

+import org.apache.directory.server.dhcp.DhcpException;

+import org.apache.directory.server.dhcp.messages.HardwareAddress;

+import org.apache.directory.server.dhcp.options.OptionsField;

+import org.apache.directory.server.dhcp.service.Lease;

+

+

+/**

+ * Very simple dummy/proof-of-concept implementation of a DhcpStore.

+ * 

+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>

+ * @version $Rev: 545042 $, $Date: 2007-06-06 22:32:01 -0500 (Mi, 06 Jun 2007) $

+ */

+public class SimpleDhcpStore extends AbstractDhcpStore

+{

+    // private static final String DEFAULT_INITIAL_CONTEXT_FACTORY =

+    // "org.apache.directory.server.core.jndi.CoreContextFactory";

+

+    // a map of current leases

+    private Map leases = new HashMap();

+

+    private List subnets = new ArrayList();

+

+

+    public SimpleDhcpStore()

+    {

+        try

+        {

+            subnets.add( new Subnet( InetAddress.getByName( "192.168.168.0" ),

+                InetAddress.getByName( "255.255.255.0" ), InetAddress.getByName( "192.168.168.159" ), InetAddress

+                    .getByName( "192.168.168.179" ) ) );

+        }

+        catch ( UnknownHostException e )

+        {

+            throw new RuntimeException( "Can't init", e );

+        }

+    }

+

+

+    protected DirContext getContext() throws NamingException

+    {

+        Hashtable env = new Hashtable();

+        env.put( Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory" );

+        // env.put( Context.INITIAL_CONTEXT_FACTORY,

+        // DEFAULT_INITIAL_CONTEXT_FACTORY );

+        env.put( Context.PROVIDER_URL, "ldap://localhost:389/dc=tcat,dc=test" );

+

+        return new InitialDirContext( env );

+    }

+

+

+    /**

+     * @param hardwareAddress

+     * @param existingLease

+     * @return Lease

+     */

+    protected Lease findExistingLease( HardwareAddress hardwareAddress, Lease existingLease )

+    {

+        if ( leases.containsKey( hardwareAddress ) )

+            existingLease = ( Lease ) leases.get( hardwareAddress );

+        return existingLease;

+    }

+

+

+    /**

+     * @param hardwareAddress

+     * @return Host

+     * @throws DhcpException

+     */

+    protected Host findDesignatedHost( HardwareAddress hardwareAddress ) throws DhcpException

+    {

+        try

+        {

+            DirContext ctx = getContext();

+            try

+            {

+                String filter = "(&(objectclass=ipHost)(objectclass=ieee802Device)(macaddress={0}))";

+                SearchControls sc = new SearchControls();

+                sc.setCountLimit( 1 );

+                sc.setSearchScope( SearchControls.SUBTREE_SCOPE );

+                NamingEnumeration ne = ctx.search( "", filter, new Object[]

+                    { hardwareAddress.toString() }, sc );

+

+                if ( ne.hasMoreElements() )

+                {

+                    SearchResult sr = ( SearchResult ) ne.next();

+                    Attributes att = sr.getAttributes();

+                    Attribute ipHostNumberAttribute = att.get( "iphostnumber" );

+                    if ( ipHostNumberAttribute != null )

+                    {

+                        InetAddress clientAddress = InetAddress.getByName( ( String ) ipHostNumberAttribute.get() );

+                        Attribute cnAttribute = att.get( "cn" );

+                        return new Host( cnAttribute != null ? ( String ) cnAttribute.get() : "unknown", clientAddress,

+                            hardwareAddress );

+                    }

+                }

+            }

+            catch ( Exception e )

+            {

+                throw new DhcpException( "Can't lookup lease", e );

+            }

+            finally

+            {

+                ctx.close();

+            }

+        }

+        catch ( NamingException e )

+        {

+            throw new DhcpException( "Can't lookup lease", e );

+        }

+

+        return null;

+    }

+

+

+    /**

+     * Find the subnet for the given client address.

+     * 

+     * @param clientAddress

+     * @return Subnet

+     */

+    protected Subnet findSubnet( InetAddress clientAddress )

+    {

+        for ( Iterator i = subnets.iterator(); i.hasNext(); )

+        {

+            Subnet subnet = ( Subnet ) i.next();

+            if ( subnet.contains( clientAddress ) )

+                return subnet;

+        }

+        return null;

+    }

+

+

+    /*

+     * @see org.apache.directory.server.dhcp.store.AbstractDhcpStore#updateLease(org.apache.directory.server.dhcp.service.Lease)

+     */

+    public void updateLease( Lease lease )

+    {

+        leases.put( lease.getHardwareAddress(), lease );

+    }

+

+

+    /*

+     * @see org.apache.directory.server.dhcp.store.AbstractDhcpStore#getOptions(org.apache.directory.server.dhcp.store.DhcpConfigElement)

+     */

+    protected OptionsField getOptions( DhcpConfigElement element )

+    {

+        // we don't have groups, classes, etc. yet.

+        return element.getOptions();

+    }

+

+

+    /*

+     * @see org.apache.directory.server.dhcp.store.AbstractDhcpStore#getProperties(org.apache.directory.server.dhcp.store.DhcpConfigElement)

+     */

+    protected Map getProperties( DhcpConfigElement element )

+    {

+        // we don't have groups, classes, etc. yet.

+        return element.getProperties();

+    }

+}

diff --git a/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/store/Subnet.java b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/store/Subnet.java
new file mode 100644
index 0000000..ef90abe
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/main/java/org/apache/directory/server/dhcp/store/Subnet.java
@@ -0,0 +1,167 @@
+/*

+ *  Licensed to the Apache Software Foundation (ASF) under one

+ *  or more contributor license agreements.  See the NOTICE file

+ *  distributed with this work for additional information

+ *  regarding copyright ownership.  The ASF licenses this file

+ *  to you 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. 

+ *  

+ */

+package org.apache.directory.server.dhcp.store;

+

+

+import java.net.InetAddress;

+import java.net.UnknownHostException;

+import java.util.Arrays;

+

+

+/**

+ * The definition of a Subnet.

+ * 

+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>

+ * @version $Rev: 545042 $, $Date: 2007-06-06 22:32:01 -0500 (Mi, 06 Jun 2007) $

+ */

+public class Subnet extends DhcpConfigElement

+{

+    /** the subnet's address */

+    private final InetAddress address;

+

+    /** the subnet's netmask */

+    private final InetAddress netmask;

+

+    /** the subnet's range: minimum address in range */

+    private InetAddress rangeMin;

+

+    /** the subnet's range: maximum address in range */

+    private InetAddress rangeMax;

+

+

+    public Subnet(InetAddress address, InetAddress netmask, InetAddress rangeMin, InetAddress rangeMax)

+    {

+        // mask address to match subnet

+        byte masked[] = netmask.getAddress();

+        byte addrBytes[] = netmask.getAddress();

+        for ( int i = 0; i < addrBytes.length; i++ )

+            masked[i] &= addrBytes[i];

+

+        if ( !Arrays.equals( masked, addrBytes ) )

+            try

+            {

+                address = InetAddress.getByAddress( masked );

+            }

+            catch ( UnknownHostException e )

+            {

+                // ignore - doesn't happen.

+            }

+

+        this.address = address;

+        this.netmask = netmask;

+        this.rangeMin = rangeMin;

+        this.rangeMax = rangeMax;

+    }

+

+

+    public InetAddress getAddress()

+    {

+        return address;

+    }

+

+

+    public InetAddress getNetmask()

+    {

+        return netmask;

+    }

+

+

+    public InetAddress getRangeMax()

+    {

+        return rangeMax;

+    }

+

+

+    public void setRangeMax( InetAddress rangeMax )

+    {

+        this.rangeMax = rangeMax;

+    }

+

+

+    public InetAddress getRangeMin()

+    {

+        return rangeMin;

+    }

+

+

+    public void setRangeMin( InetAddress rangeMin )

+    {

+        this.rangeMin = rangeMin;

+    }

+

+

+    /**

+     * Check whether the given client address resides within this subnet and

+     * possibly range.

+     * 

+     * @param clientAddress

+     * @return boolean

+     */

+    public boolean contains( InetAddress clientAddress )

+    {

+        // check address type

+        if ( !clientAddress.getClass().equals( address.getClass() ) )

+            return false;

+

+        byte client[] = clientAddress.getAddress();

+        byte masked[] = netmask.getAddress();

+        for ( int i = 0; i < masked.length; i++ )

+            masked[i] &= client[i];

+

+        return Arrays.equals( masked, address.getAddress() );

+    }

+

+

+    /**

+     * Check whether the specified address is within the range for this subnet.

+     * 

+     * @param clientAddress

+     * @return boolean

+     */

+    public boolean isInRange( InetAddress clientAddress )

+    {

+        byte client[] = clientAddress.getAddress();

+        byte masked[] = netmask.getAddress();

+        for ( int i = 0; i < masked.length; i++ )

+            masked[i] &= client[i];

+

+        if ( null != rangeMin )

+            if ( arrayComp( masked, rangeMin.getAddress() ) < 0 )

+                return false;

+

+        if ( null != rangeMin )

+            if ( arrayComp( masked, rangeMax.getAddress() ) > 0 )

+                return false;

+

+        return true;

+    }

+

+

+    private static int arrayComp( byte a1[], byte a2[] )

+    {

+        for ( int i = 0; i < a1.length && i < a2.length; i++ )

+        {

+            if ( a1[i] != a2[i] )

+                return ( a1[i] & 0xff ) - ( a2[i] & 0xff );

+        }

+

+        return a1.length - a2.length;

+    }

+}

diff --git a/old_trunk/protocol-dhcp/src/site/site.xml b/old_trunk/protocol-dhcp/src/site/site.xml
new file mode 100644
index 0000000..9ec7c16
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/site/site.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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.
+-->
+<!--
+  @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+-->
+<project name="${project.name}">
+    <bannerLeft>
+        <name>${project.name}</name>
+    </bannerLeft>
+    <publishDate position="navigation-bottom" format="dd-MM-yyyy HH:mm" />
+    <body>
+        <menu name="Parent">
+            <item name="Apache Directory ApacheDS" href="../index.html" />
+        </menu>
+        <menu ref="reports" />
+    </body>
+</project>
diff --git a/old_trunk/protocol-dhcp/src/test/java/org/apache/directory/server/dhcp/AbstractDhcpTestCase.java b/old_trunk/protocol-dhcp/src/test/java/org/apache/directory/server/dhcp/AbstractDhcpTestCase.java
new file mode 100644
index 0000000..2741dfb
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/test/java/org/apache/directory/server/dhcp/AbstractDhcpTestCase.java
@@ -0,0 +1,89 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp;
+
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+
+import junit.framework.TestCase;
+
+import org.apache.directory.server.dhcp.messages.DhcpMessage;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public abstract class AbstractDhcpTestCase extends TestCase
+{
+    protected static final int MINIMUM_DHCP_DATAGRAM_SIZE = 576;
+    protected final Logger log;
+
+
+    public AbstractDhcpTestCase()
+    {
+        log = LoggerFactory.getLogger( AbstractDhcpTestCase.class );
+    }
+
+
+    public AbstractDhcpTestCase(Class subclass)
+    {
+        log = LoggerFactory.getLogger( subclass );
+    }
+
+
+    protected void print( DhcpMessage message )
+    {
+        log.debug( String.valueOf( message.getMessageType() ) );
+        log.debug( String.valueOf( message.getHardwareAddress() ) );
+        log.debug( String.valueOf( message.getTransactionId() ) );
+        log.debug( String.valueOf( message.getSeconds() ) );
+        log.debug( String.valueOf( message.getFlags() ) );
+        log.debug( String.valueOf( message.getCurrentClientAddress() ) );
+        log.debug( String.valueOf( message.getAssignedClientAddress() ) );
+        log.debug( String.valueOf( message.getNextServerAddress() ) );
+        log.debug( String.valueOf( message.getRelayAgentAddress() ) );
+        log.debug( String.valueOf( message.getServerHostname() ) );
+        log.debug( String.valueOf( message.getBootFileName() ) );
+    }
+
+
+    protected ByteBuffer getByteBufferFromFile( String file ) throws IOException
+    {
+        InputStream is = getClass().getResourceAsStream( file );
+
+        byte[] bytes = new byte[MINIMUM_DHCP_DATAGRAM_SIZE];
+
+        int offset = 0;
+        int numRead = 0;
+        while ( offset < bytes.length && ( numRead = is.read( bytes, offset, bytes.length - offset ) ) >= 0 )
+        {
+            offset += numRead;
+        }
+
+        is.close();
+        return ByteBuffer.wrap( bytes );
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/test/java/org/apache/directory/server/dhcp/DhcpMessageDecoderTest.java b/old_trunk/protocol-dhcp/src/test/java/org/apache/directory/server/dhcp/DhcpMessageDecoderTest.java
new file mode 100644
index 0000000..f8cdff1
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/test/java/org/apache/directory/server/dhcp/DhcpMessageDecoderTest.java
@@ -0,0 +1,65 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dhcp;
+
+
+import java.nio.ByteBuffer;
+
+import org.apache.directory.server.dhcp.io.DhcpMessageDecoder;
+import org.apache.directory.server.dhcp.messages.DhcpMessage;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class DhcpMessageDecoderTest extends AbstractDhcpTestCase
+{
+    private ByteBuffer requestByteBuffer;
+
+
+    public DhcpMessageDecoderTest()
+    {
+        super( DhcpMessageDecoderTest.class );
+    }
+
+
+    public void testParseDiscover() throws Exception
+    {
+        requestByteBuffer = getByteBufferFromFile( "DHCPDISCOVER.pdu" );
+
+        DhcpMessageDecoder decoder = new DhcpMessageDecoder();
+        DhcpMessage dhcpRequest = decoder.decode( requestByteBuffer );
+
+        print( dhcpRequest );
+    }
+
+
+    public void testParseOffer() throws Exception
+    {
+        requestByteBuffer = getByteBufferFromFile( "DHCPOFFER.pdu" );
+
+        DhcpMessageDecoder decoder = new DhcpMessageDecoder();
+        DhcpMessage dhcpRequest = decoder.decode( requestByteBuffer );
+
+        print( dhcpRequest );
+    }
+}
diff --git a/old_trunk/protocol-dhcp/src/test/resources/log4j.properties b/old_trunk/protocol-dhcp/src/test/resources/log4j.properties
new file mode 100644
index 0000000..facb3e6
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/test/resources/log4j.properties
@@ -0,0 +1,21 @@
+#############################################################################
+#    Licensed to the Apache Software Foundation (ASF) under one or more
+#    contributor license agreements.  See the NOTICE file distributed with
+#    this work for additional information regarding copyright ownership.
+#    The ASF licenses this file to You 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.
+#############################################################################
+log4j.rootCategory=OFF, stdout
+
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=[%d{HH:mm:ss}] %p [%c] - %m%n
diff --git a/old_trunk/protocol-dhcp/src/test/resources/org/apache/directory/server/dhcp/DHCP-DISCOVER.libpcap b/old_trunk/protocol-dhcp/src/test/resources/org/apache/directory/server/dhcp/DHCP-DISCOVER.libpcap
new file mode 100644
index 0000000..4d2e9e0
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/test/resources/org/apache/directory/server/dhcp/DHCP-DISCOVER.libpcap
Binary files differ
diff --git a/old_trunk/protocol-dhcp/src/test/resources/org/apache/directory/server/dhcp/DHCP-TRAFFIC.libpcap b/old_trunk/protocol-dhcp/src/test/resources/org/apache/directory/server/dhcp/DHCP-TRAFFIC.libpcap
new file mode 100644
index 0000000..e53c401
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/test/resources/org/apache/directory/server/dhcp/DHCP-TRAFFIC.libpcap
Binary files differ
diff --git a/old_trunk/protocol-dhcp/src/test/resources/org/apache/directory/server/dhcp/DHCPDISCOVER.pdu b/old_trunk/protocol-dhcp/src/test/resources/org/apache/directory/server/dhcp/DHCPDISCOVER.pdu
new file mode 100644
index 0000000..3868d6a
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/test/resources/org/apache/directory/server/dhcp/DHCPDISCOVER.pdu
Binary files differ
diff --git a/old_trunk/protocol-dhcp/src/test/resources/org/apache/directory/server/dhcp/DHCPOFFER.pdu b/old_trunk/protocol-dhcp/src/test/resources/org/apache/directory/server/dhcp/DHCPOFFER.pdu
new file mode 100644
index 0000000..5811cbb
--- /dev/null
+++ b/old_trunk/protocol-dhcp/src/test/resources/org/apache/directory/server/dhcp/DHCPOFFER.pdu
Binary files differ
diff --git a/old_trunk/protocol-dns/pom.xml b/old_trunk/protocol-dns/pom.xml
new file mode 100644
index 0000000..7c0c662
--- /dev/null
+++ b/old_trunk/protocol-dns/pom.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you 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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.directory.server</groupId>
+    <artifactId>apacheds-parent</artifactId>
+    <version>1.5.4-SNAPSHOT</version>
+  </parent>
+  <artifactId>apacheds-protocol-dns</artifactId>
+  <name>ApacheDS Protocol Dns</name>
+
+  <description>
+    The DNS protocol provider for ApacheDS
+  </description>
+
+  <packaging>jar</packaging>  
+
+  <dependencies>
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-core</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-protocol-shared</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-source-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>attach-sources</id>
+            <phase>verify</phase>
+            <goals>
+              <goal>jar</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+
+</project>
+
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/DnsException.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/DnsException.java
new file mode 100755
index 0000000..249de3f
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/DnsException.java
@@ -0,0 +1,65 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns;
+
+
+import org.apache.directory.server.dns.messages.ResponseCode;
+
+
+/**
+ * The root of the DNS exception hierarchy.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class DnsException extends Exception
+{
+    private static final long serialVersionUID = 7493573233290707069L;
+
+    /**
+     * The DNS response code associated with this exception
+     */
+    private final int responseCode;
+
+
+    /**
+     * Creates a DnsException with a response code.
+     *
+     * @param responseCode the response code associated with this DnsException
+     */
+    public DnsException(ResponseCode responseCode)
+    {
+        super( responseCode.name() );
+
+        this.responseCode = responseCode.convert();
+    }
+
+
+    /**
+     * Gets the protocol response code associated with this DnsException.
+     *
+     * @return the response code associated with this DnsException
+     */
+    public int getResponseCode()
+    {
+        return this.responseCode;
+    }
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/DnsServer.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/DnsServer.java
new file mode 100644
index 0000000..68be3cf
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/DnsServer.java
@@ -0,0 +1,100 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.dns;
+
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+
+import org.apache.directory.server.dns.protocol.DnsProtocolHandler;
+import org.apache.directory.server.dns.store.RecordStore;
+import org.apache.directory.server.dns.store.jndi.JndiRecordStoreImpl;
+import org.apache.directory.server.protocol.shared.DirectoryBackedService;
+import org.apache.mina.transport.socket.nio.DatagramAcceptorConfig;
+import org.apache.mina.transport.socket.nio.SocketAcceptorConfig;
+
+
+/**
+ * Contains the configuration parameters for the DNS protocol provider.
+ *
+ * @org.apache.xbean.XBean
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class DnsServer extends DirectoryBackedService
+{
+    private static final long serialVersionUID = 6943138644427163149L;
+
+    /** The default IP port. */
+    private static final int IP_PORT_DEFAULT = 53;
+
+    /** The default service pid. */
+    private static final String SERVICE_PID_DEFAULT = "org.apache.directory.server.dns";
+
+    /** The default service name. */
+    private static final String SERVICE_NAME_DEFAULT = "ApacheDS DNS Service";
+
+
+    /**
+     * Creates a new instance of DnsConfiguration.
+     */
+    public DnsServer()
+    {
+        super.setIpPort( IP_PORT_DEFAULT );
+        super.setServiceId( SERVICE_PID_DEFAULT );
+        super.setServiceName( SERVICE_NAME_DEFAULT );
+    }
+
+
+    /**
+     * @throws IOException if we cannot bind to the specified ports
+     */
+    public void start() throws IOException
+    {
+        RecordStore store = new JndiRecordStoreImpl( getSearchBaseDn(), getSearchBaseDn(), getDirectoryService() );
+
+        if ( getDatagramAcceptor() != null )
+        {
+            DatagramAcceptorConfig udpConfig = new DatagramAcceptorConfig();
+            getDatagramAcceptor().bind( new InetSocketAddress( getIpPort() ), new DnsProtocolHandler( this, store ), udpConfig );
+        }
+
+        if ( getSocketAcceptor() != null )
+        {
+            SocketAcceptorConfig tcpConfig = new SocketAcceptorConfig();
+            tcpConfig.setDisconnectOnUnbind( false );
+            tcpConfig.setReuseAddress( true );
+            getSocketAcceptor().bind( new InetSocketAddress( getIpPort() ), new DnsProtocolHandler( this, store ), tcpConfig );
+        }
+    }
+
+
+    public void stop() {
+        if ( getDatagramAcceptor() != null )
+        {
+            getDatagramAcceptor().unbind( new InetSocketAddress( getIpPort() ));
+        }
+        if ( getSocketAcceptor() != null )
+        {
+            getSocketAcceptor().unbind( new InetSocketAddress( getIpPort() ));
+        }
+    }
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/Main.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/Main.java
new file mode 100644
index 0000000..3dca553
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/Main.java
@@ -0,0 +1,83 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.dns;
+
+
+import java.io.IOException;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.core.DefaultDirectoryService;
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.protocol.shared.DatagramAcceptor;
+import org.apache.directory.server.protocol.shared.SocketAcceptor;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class Main
+{
+    /** Logger for this class */
+    private static final Logger LOG = LoggerFactory.getLogger( Main.class );
+
+    private static DnsServer dnsConfiguration;
+
+    /**
+     * Entry point for the DNS server.
+     *
+     * @param args
+     * @throws Exception
+     */
+    public static void main( String[] args ) throws Exception
+    {
+        new Main().go();
+    }
+
+
+    /**
+     * Start an instance of the DNS server.
+     */
+    public void go() throws IOException, NamingException
+    {
+        LOG.debug( "Starting the DNS server" );
+        
+        DatagramAcceptor datagramAcceptor = new DatagramAcceptor( null );
+        SocketAcceptor socketAcceptor = new SocketAcceptor( null );
+        DirectoryService directoryService = new DefaultDirectoryService();
+        dnsConfiguration = new DnsServer();
+        dnsConfiguration.setDatagramAcceptor( datagramAcceptor );
+        dnsConfiguration.setSocketAcceptor( socketAcceptor );
+        dnsConfiguration.setDirectoryService( directoryService );
+        dnsConfiguration.setEnabled( true );
+        dnsConfiguration.setIpPort( 10053 );
+        dnsConfiguration.start();
+    }
+
+
+    protected void shutdown()
+    {
+        LOG.debug( "Stopping the DNS server" );
+        dnsConfiguration.stop();
+    }
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/decoder/AddressRecordDecoder.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/decoder/AddressRecordDecoder.java
new file mode 100644
index 0000000..16d90b3
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/decoder/AddressRecordDecoder.java
@@ -0,0 +1,62 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.io.decoder;
+
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.directory.server.dns.store.DnsAttribute;
+import org.apache.mina.common.ByteBuffer;
+
+
+/**
+ * A decoder for A records. As per RFC 1035
+ * 
+ * <pre>
+ *   3.4.1. A RDATA format
+ *
+ *     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *     |                    ADDRESS                    |
+ *     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *
+ *   where:
+ *
+ *   ADDRESS
+ *       A 32 bit Internet address. 
+ * </pre>
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class AddressRecordDecoder implements RecordDecoder
+{
+    public Map<String, Object> decode( ByteBuffer byteBuffer, short length ) throws IOException
+    {
+        Map<String, Object> attributes = new HashMap<String, Object>();
+        byte[] addressBytes = new byte[length];
+        byteBuffer.get( addressBytes );
+        attributes.put( DnsAttribute.IP_ADDRESS, InetAddress.getByAddress( addressBytes ) );
+        return attributes;
+    }
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/decoder/DnsMessageDecoder.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/decoder/DnsMessageDecoder.java
new file mode 100644
index 0000000..c842978
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/decoder/DnsMessageDecoder.java
@@ -0,0 +1,275 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.io.decoder;
+
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.directory.server.dns.messages.DnsMessage;
+import org.apache.directory.server.dns.messages.DnsMessageModifier;
+import org.apache.directory.server.dns.messages.MessageType;
+import org.apache.directory.server.dns.messages.OpCode;
+import org.apache.directory.server.dns.messages.QuestionRecord;
+import org.apache.directory.server.dns.messages.RecordClass;
+import org.apache.directory.server.dns.messages.RecordType;
+import org.apache.directory.server.dns.messages.ResourceRecord;
+import org.apache.directory.server.dns.messages.ResourceRecordImpl;
+import org.apache.directory.server.dns.messages.ResponseCode;
+import org.apache.mina.common.ByteBuffer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * A decoder for DNS messages.  The primary usage of the DnsMessageDecoder is by
+ * calling the <code>decode(ByteBuffer)</code> method which will read the 
+ * message from the incoming ByteBuffer and build a <code>DnsMessage</code>
+ * from it according to the DnsMessage encoding in RFC-1035.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class DnsMessageDecoder
+{
+
+    private final Logger logger = LoggerFactory.getLogger( DnsMessageDecoder.class );
+
+    /**
+     * A Hashed Adapter mapping record types to their encoders.
+     */
+    private static final Map<RecordType, RecordDecoder> DEFAULT_DECODERS;
+
+    static
+    {
+        Map<RecordType, RecordDecoder> map = new HashMap<RecordType, RecordDecoder>();
+
+        map.put( RecordType.A, new AddressRecordDecoder() );
+        map.put( RecordType.NS, new NameServerRecordDecoder() );
+        map.put( RecordType.MX, new MailExchangeRecordDecoder() );
+        map.put( RecordType.AAAA, new IPv6RecordDecoder() );
+
+        DEFAULT_DECODERS = Collections.unmodifiableMap( map );
+    }
+
+
+    /**
+     * Decode the {@link ByteBuffer} into a {@link DnsMessage}.
+     *
+     * @param in
+     * @return The {@link DnsMessage}.
+     * @throws IOException
+     */
+    public DnsMessage decode( ByteBuffer in ) throws IOException
+    {
+        DnsMessageModifier modifier = new DnsMessageModifier();
+
+        modifier.setTransactionId( in.getUnsignedShort() );
+
+        byte header = in.get();
+        modifier.setMessageType( decodeMessageType( header ) );
+        modifier.setOpCode( decodeOpCode( header ) );
+        modifier.setAuthoritativeAnswer( decodeAuthoritativeAnswer( header ) );
+        modifier.setTruncated( decodeTruncated( header ) );
+        modifier.setRecursionDesired( decodeRecursionDesired( header ) );
+
+        header = in.get();
+        modifier.setRecursionAvailable( decodeRecursionAvailable( header ) );
+        modifier.setResponseCode( decodeResponseCode( header ) );
+
+        short questionCount = in.getShort();
+        short answerCount = in.getShort();
+        short authorityCount = in.getShort();
+        short additionalCount = in.getShort();
+
+        logger.debug( "decoding {} question records", questionCount );
+        modifier.setQuestionRecords( getQuestions( in, questionCount ) );
+
+        logger.debug( "decoding {} answer records", answerCount );
+        modifier.setAnswerRecords( getRecords( in, answerCount ) );
+
+        logger.debug( "decoding {} authority records", authorityCount );
+        modifier.setAuthorityRecords( getRecords( in, authorityCount ) );
+
+        logger.debug( "decoding {} additional records", additionalCount );
+        modifier.setAdditionalRecords( getRecords( in, additionalCount ) );
+
+        return modifier.getDnsMessage();
+    }
+
+
+    private List<ResourceRecord> getRecords( ByteBuffer byteBuffer, short recordCount ) throws IOException
+    {
+        List<ResourceRecord> records = new ArrayList<ResourceRecord>( recordCount );
+
+        for ( int ii = 0; ii < recordCount; ii++ )
+        {
+            String domainName = getDomainName( byteBuffer );
+            RecordType recordType = RecordType.convert( byteBuffer.getShort() );
+            RecordClass recordClass = RecordClass.convert( byteBuffer.getShort() );
+
+            int timeToLive = byteBuffer.getInt();
+            short dataLength = byteBuffer.getShort();
+
+            Map<String, Object> attributes = decode( byteBuffer, recordType, dataLength );
+            records.add( new ResourceRecordImpl( domainName, recordType, recordClass, timeToLive, attributes ) );
+        }
+
+        return records;
+    }
+
+
+    private Map<String, Object> decode( ByteBuffer byteBuffer, RecordType type, short length ) throws IOException
+    {
+        RecordDecoder recordDecoder = DEFAULT_DECODERS.get( type );
+
+        if ( recordDecoder == null )
+        {
+            throw new IllegalArgumentException( "Decoder unavailable for " + type );
+        }
+
+        return recordDecoder.decode( byteBuffer, length );
+    }
+
+
+    private List<QuestionRecord> getQuestions( ByteBuffer byteBuffer, short questionCount )
+    {
+        List<QuestionRecord> questions = new ArrayList<QuestionRecord>( questionCount );
+
+        for ( int ii = 0; ii < questionCount; ii++ )
+        {
+            String domainName = getDomainName( byteBuffer );
+
+            RecordType recordType = RecordType.convert( byteBuffer.getShort() );
+            RecordClass recordClass = RecordClass.convert( byteBuffer.getShort() );
+
+            questions.add( new QuestionRecord( domainName, recordType, recordClass ) );
+        }
+
+        return questions;
+    }
+
+
+    static String getDomainName( ByteBuffer byteBuffer )
+    {
+        StringBuffer domainName = new StringBuffer();
+        recurseDomainName( byteBuffer, domainName );
+
+        return domainName.toString();
+    }
+
+
+    static void recurseDomainName( ByteBuffer byteBuffer, StringBuffer domainName )
+    {
+        int length = byteBuffer.getUnsigned();
+
+        if ( isOffset( length ) )
+        {
+            int position = byteBuffer.getUnsigned();
+            int offset = length & ~( 0xc0 ) << 8;
+            int originalPosition = byteBuffer.position();
+            byteBuffer.position( position + offset );
+
+            recurseDomainName( byteBuffer, domainName );
+
+            byteBuffer.position( originalPosition );
+        }
+        else if ( isLabel( length ) )
+        {
+            int labelLength = length;
+            getLabel( byteBuffer, domainName, labelLength );
+            recurseDomainName( byteBuffer, domainName );
+        }
+    }
+
+
+    static boolean isOffset( int length )
+    {
+        return ( ( length & 0xc0 ) == 0xc0 );
+    }
+
+
+    static boolean isLabel( int length )
+    {
+        return ( length != 0 && ( length & 0xc0 ) == 0 );
+    }
+
+
+    static void getLabel( ByteBuffer byteBuffer, StringBuffer domainName, int labelLength )
+    {
+        for ( int jj = 0; jj < labelLength; jj++ )
+        {
+            char character = ( char ) byteBuffer.get();
+            domainName.append( character );
+        }
+
+        if ( byteBuffer.get( byteBuffer.position() ) != 0 )
+        {
+            domainName.append( "." );
+        }
+    }
+
+
+    private MessageType decodeMessageType( byte header )
+    {
+        return MessageType.convert( ( byte ) ( ( header & 0x80 ) >>> 7 ) );
+    }
+
+
+    private OpCode decodeOpCode( byte header )
+    {
+        return OpCode.convert( ( byte ) ( ( header & 0x78 ) >>> 3 ) );
+    }
+
+
+    private boolean decodeAuthoritativeAnswer( byte header )
+    {
+        return ( ( header & 0x04 ) >>> 2 ) == 1;
+    }
+
+
+    private boolean decodeTruncated( byte header )
+    {
+        return ( ( header & 0x02 ) >>> 1 ) == 1;
+    }
+
+
+    private boolean decodeRecursionDesired( byte header )
+    {
+        return ( ( header & 0x01 ) ) == 1;
+    }
+
+
+    private boolean decodeRecursionAvailable( byte header )
+    {
+        return ( ( header & 0x80 ) >>> 7 ) == 1;
+    }
+
+
+    private ResponseCode decodeResponseCode( byte header )
+    {
+        return ResponseCode.convert( ( byte ) ( header & 0x0F ) );
+    }
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/decoder/IPv6RecordDecoder.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/decoder/IPv6RecordDecoder.java
new file mode 100644
index 0000000..a336072
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/decoder/IPv6RecordDecoder.java
@@ -0,0 +1,58 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.io.decoder;
+
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.directory.server.dns.store.DnsAttribute;
+import org.apache.mina.common.ByteBuffer;
+
+
+/**
+ * A decoder for AAAA records.  AAAA records are encoded as per RFC-3596:
+ * 
+ * <pre>
+ *   2.2. AAAA data format
+ *
+ *     A 128 bit IPv6 address is encoded in the data portion of an AAAA
+ *     resource record in network byte order (high-order byte first).
+ * </pre>
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class IPv6RecordDecoder implements RecordDecoder
+{
+
+    public Map<String, Object> decode( ByteBuffer byteBuffer, short length ) throws IOException
+    {
+        Map<String, Object> attributes = new HashMap<String, Object>();
+        byte[] addressBytes = new byte[length];
+        byteBuffer.get( addressBytes );
+        attributes.put( DnsAttribute.IP_ADDRESS, InetAddress.getByAddress( addressBytes ) );
+        return attributes;
+    }
+
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/decoder/MailExchangeRecordDecoder.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/decoder/MailExchangeRecordDecoder.java
new file mode 100644
index 0000000..9369b6b
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/decoder/MailExchangeRecordDecoder.java
@@ -0,0 +1,68 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.io.decoder;
+
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.directory.server.dns.store.DnsAttribute;
+import org.apache.mina.common.ByteBuffer;
+
+
+/**
+ * A decoder for MX records.  MX records are encoded as per RFC-1035:
+ * 
+ * <pre>
+ *   3.3.9. MX RDATA format
+ *
+ *     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *     |                  PREFERENCE                   |
+ *     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *     /                   EXCHANGE                    /
+ *     /                                               /
+ *     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *
+ *   where:
+ *
+ *   PREFERENCE
+ *     A 16 bit integer which specifies the preference given to this RR among 
+ *     others at the same owner. Lower values are preferred. 
+ *     
+ *   EXCHANGE
+ *     A <domain-name> which specifies a host willing to act as a mail exchange
+ *     for the owner name.
+ * </pre>
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class MailExchangeRecordDecoder implements RecordDecoder
+{
+    public Map<String, Object> decode( ByteBuffer byteBuffer, short length ) throws IOException
+    {
+        Map<String, Object> attributes = new HashMap<String, Object>();
+        attributes.put( DnsAttribute.MX_PREFERENCE, byteBuffer.getShort() );
+        attributes.put( DnsAttribute.DOMAIN_NAME, DnsMessageDecoder.getDomainName( byteBuffer ) );
+        return attributes;
+    }
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/decoder/NameServerRecordDecoder.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/decoder/NameServerRecordDecoder.java
new file mode 100644
index 0000000..4f1788b
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/decoder/NameServerRecordDecoder.java
@@ -0,0 +1,61 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.io.decoder;
+
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.directory.server.dns.store.DnsAttribute;
+import org.apache.mina.common.ByteBuffer;
+
+
+/**
+ * A decoder for NS records.  NS records are encoded as per RFC-1035:
+ * 
+ * <pre>
+ *   3.3.11. NS RDATA format
+ *
+ *     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *     /                   NSDNAME                     /
+ *     /                                               /
+ *     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *
+ *   where:
+ *
+ *   NSDNAME
+ *     A <domain-name> which specifies a host which should be authoritative for
+ *     the specified class and domain. 
+ * </pre>
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class NameServerRecordDecoder implements RecordDecoder
+{
+    public Map<String, Object> decode( ByteBuffer byteBuffer, short length ) throws IOException
+    {
+        Map<String, Object> map = new HashMap<String, Object>();
+        map.put( DnsAttribute.DOMAIN_NAME, DnsMessageDecoder.getDomainName( byteBuffer ) );
+        return map;
+    }
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/decoder/RecordDecoder.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/decoder/RecordDecoder.java
new file mode 100644
index 0000000..55a186a
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/decoder/RecordDecoder.java
@@ -0,0 +1,46 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.io.decoder;
+
+
+import java.io.IOException;
+import java.util.Map;
+
+import org.apache.mina.common.ByteBuffer;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public interface RecordDecoder
+{
+    /**
+     * Decodes the given length of resource record data into attributes.  The
+     * type and number of attributes depends on the type of the resource record.
+     *
+     * @param byteBuffer
+     * @param length
+     * @return The map of attributes.
+     * @throws IOException
+     */
+    public Map<String, Object> decode( ByteBuffer byteBuffer, short length ) throws IOException;
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/decoder/package-info.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/decoder/package-info.java
new file mode 100644
index 0000000..16450f2
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/decoder/package-info.java
@@ -0,0 +1,28 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+/**
+ * Provides the decoders for DNS messages and resource records.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+
+package org.apache.directory.server.dns.io.decoder;
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/A6RecordEncoder.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/A6RecordEncoder.java
new file mode 100755
index 0000000..8808e6e
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/A6RecordEncoder.java
@@ -0,0 +1,61 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.io.encoder;
+
+
+/**
+ * 3.1.1.  Format
+ * 
+ *    The RDATA portion of the A6 record contains two or three fields.
+ * 
+ *            +-----------+------------------+-------------------+
+ *            |Prefix len.|  Address suffix  |    Prefix name    |
+ *            | (1 octet) |  (0..16 octets)  |  (0..255 octets)  |
+ *            +-----------+------------------+-------------------+
+ * 
+ *    o  A prefix length, encoded as an eight-bit unsigned integer with
+ *       value between 0 and 128 inclusive.
+ * 
+ *    o  An IPv6 address suffix, encoded in network order (high-order octet
+ *       first).  There MUST be exactly enough octets in this field to
+ *       contain a number of bits equal to 128 minus prefix length, with 0
+ *       to 7 leading pad bits to make this field an integral number of
+ *       octets.  Pad bits, if present, MUST be set to zero when loading a
+ *       zone file and ignored (other than for SIG [DNSSEC] verification)
+ *       on reception.
+ * 
+ *    o  The name of the prefix, encoded as a domain name.  By the rules of
+ *       [DNSIS], this name MUST NOT be compressed.
+ * 
+ *    The domain name component SHALL NOT be present if the prefix length
+ *    is zero.  The address suffix component SHALL NOT be present if the
+ *    prefix length is 128.
+ * 
+ *    It is SUGGESTED that an A6 record intended for use as a prefix for
+ *    other A6 records have all the insignificant trailing bits in its
+ *    address suffix field set to zero.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class A6RecordEncoder
+{
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/AddressRecordEncoder.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/AddressRecordEncoder.java
new file mode 100755
index 0000000..fd7f0f5
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/AddressRecordEncoder.java
@@ -0,0 +1,68 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.io.encoder;
+
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+import org.apache.directory.server.dns.messages.ResourceRecord;
+import org.apache.directory.server.dns.store.DnsAttribute;
+import org.apache.mina.common.ByteBuffer;
+
+
+/**
+ * 3.4.1. A RDATA format
+ * 
+ *  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *  |                    ADDRESS                    |
+ *  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ * 
+ * where:
+ * 
+ * ADDRESS         A 32 bit Internet address.
+ * 
+ * Hosts that have multiple Internet addresses will have multiple A
+ * records.
+ * 
+ * A records cause no additional section processing.  The RDATA section of
+ * an A line in a master file is an Internet address expressed as four
+ * decimal numbers separated by dots without any imbedded spaces (e.g.,
+ * "10.2.0.52" or "192.0.5.6").
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class AddressRecordEncoder extends ResourceRecordEncoder
+{
+    protected void putResourceRecordData( ByteBuffer byteBuffer, ResourceRecord record )
+    {
+        String ipAddress = record.get( DnsAttribute.IP_ADDRESS );
+
+        try
+        {
+            byteBuffer.put( InetAddress.getByName( ipAddress ).getAddress() );
+        }
+        catch ( UnknownHostException uhe )
+        {
+        }
+    }
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/CanonicalNameRecordEncoder.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/CanonicalNameRecordEncoder.java
new file mode 100644
index 0000000..1e24a21
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/CanonicalNameRecordEncoder.java
@@ -0,0 +1,57 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.io.encoder;
+
+
+import org.apache.directory.server.dns.messages.ResourceRecord;
+import org.apache.directory.server.dns.store.DnsAttribute;
+import org.apache.mina.common.ByteBuffer;
+
+
+/**
+ * 3.3.1. CNAME RDATA format
+ * 
+ *     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *     /                     CNAME                     /
+ *     /                                               /
+ *     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ * 
+ * where:
+ * 
+ * CNAME           A <domain-name> which specifies the canonical or primary
+ *                 name for the owner.  The owner name is an alias.
+ * 
+ * CNAME RRs cause no additional section processing, but name servers may
+ * choose to restart the query at the canonical name in certain cases.  See
+ * the description of name server logic in [RFC-1034] for details.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class CanonicalNameRecordEncoder extends ResourceRecordEncoder
+{
+    protected void putResourceRecordData( ByteBuffer byteBuffer, ResourceRecord record )
+    {
+        String domainName = record.get( DnsAttribute.DOMAIN_NAME );
+
+        putDomainName( byteBuffer, domainName );
+    }
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/CertificateRecordEncoder.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/CertificateRecordEncoder.java
new file mode 100755
index 0000000..122eeef
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/CertificateRecordEncoder.java
@@ -0,0 +1,66 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.io.encoder;
+
+
+/**
+ * 2. The CERT Resource Record
+ * 
+ *    The CERT resource record (RR) has the structure given below.  Its RR
+ *    type code is 37.
+ * 
+ *                          1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ *      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ *     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *     |             type              |             key tag           |
+ *     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *     |   algorithm   |                                               /
+ *     +---------------+            certificate or CRL                 /
+ *     /                                                               /
+ *     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-|
+ * 
+ *    The type field is the certificate type as define in section 2.1
+ *    below.
+ * 
+ *    The algorithm field has the same meaning as the algorithm field in
+ *    KEY and SIG RRs [RFC 2535] except that a zero algorithm field
+ *    indicates the algorithm is unknown to a secure DNS, which may simply
+ *    be the result of the algorithm not having been standardized for
+ *    secure DNS.
+ * 
+ *    The key tag field is the 16 bit value computed for the key embedded
+ *    in the certificate as specified in the DNSSEC Standard [RFC 2535].
+ *    This field is used as an efficiency measure to pick which CERT RRs
+ *    may be applicable to a particular key.  The key tag can be calculated
+ *    for the key in question and then only CERT RRs with the same key tag
+ *    need be examined. However, the key must always be transformed to the
+ *    format it would have as the public key portion of a KEY RR before the
+ *    key tag is computed.  This is only possible if the key is applicable
+ *    to an algorithm (and limits such as key size limits) defined for DNS
+ *    security.  If it is not, the algorithm field MUST BE zero and the tag
+ *    field is meaningless and SHOULD BE zero.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class CertificateRecordEncoder
+{
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/DnameRecordEncoder.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/DnameRecordEncoder.java
new file mode 100755
index 0000000..d9bd49d
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/DnameRecordEncoder.java
@@ -0,0 +1,71 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.io.encoder;
+
+
+/**
+ * 3. The DNAME Resource Record
+ * 
+ *    The DNAME RR has mnemonic DNAME and type code 39 (decimal).
+ * 
+ *    DNAME has the following format:
+ * 
+ *       <owner> <ttl> <class> DNAME <target>
+ * 
+ *    The format is not class-sensitive.  All fields are required.  The
+ *    RDATA field <target> is a <domain-name> [DNSIS].
+ * 
+ *    The DNAME RR causes type NS additional section processing.
+ * 
+ *    The effect of the DNAME record is the substitution of the record's
+ *    <target> for its <owner> as a suffix of a domain name.  A "no-
+ *    descendants" limitation governs the use of DNAMEs in a zone file:
+ * 
+ *       If a DNAME RR is present at a node N, there may be other data at N
+ *       (except a CNAME or another DNAME), but there MUST be no data at
+ *       any descendant of N.  This restriction applies only to records of
+ *       the same class as the DNAME record.
+ * 
+ *    This rule assures predictable results when a DNAME record is cached
+ *    by a server which is not authoritative for the record's zone.  It
+ *    MUST be enforced when authoritative zone data is loaded.  Together
+ *    with the rules for DNS zone authority [DNSCLR] it implies that DNAME
+ *    and NS records can only coexist at the top of a zone which has only
+ *    one node.
+ * 
+ *    The compression scheme of [DNSIS] MUST NOT be applied to the RDATA
+ *    portion of a DNAME record unless the sending server has some way of
+ *    knowing that the receiver understands the DNAME record format.
+ *    Signalling such understanding is expected to be the subject of future
+ *    DNS Extensions.
+ * 
+ *    Naming loops can be created with DNAME records or a combination of
+ *    DNAME and CNAME records, just as they can with CNAME records alone.
+ *    Resolvers, including resolvers embedded in DNS servers, MUST limit
+ *    the resources they devote to any query.  Implementors should note,
+ *    however, that fairly lengthy chains of DNAME records may be valid.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class DnameRecordEncoder
+{
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/DnsMessageEncoder.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/DnsMessageEncoder.java
new file mode 100644
index 0000000..0bbc3d9
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/DnsMessageEncoder.java
@@ -0,0 +1,234 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.io.encoder;
+
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.directory.server.dns.messages.DnsMessage;
+import org.apache.directory.server.dns.messages.MessageType;
+import org.apache.directory.server.dns.messages.OpCode;
+import org.apache.directory.server.dns.messages.QuestionRecord;
+import org.apache.directory.server.dns.messages.RecordType;
+import org.apache.directory.server.dns.messages.ResourceRecord;
+import org.apache.directory.server.dns.messages.ResponseCode;
+import org.apache.mina.common.ByteBuffer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * An encoder for DNS messages.  The primary usage of the DnsMessageEncoder is 
+ * to call the <code>encode(ByteBuffer, DnsMessage)</code> method which will 
+ * write the message to the outgoing ByteBuffer according to the DnsMessage 
+ * encoding in RFC-1035.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class DnsMessageEncoder
+{
+    /** the log for this class */
+    private static final Logger log = LoggerFactory.getLogger( DnsMessageEncoder.class );
+
+    /**
+     * A Hashed Adapter mapping record types to their encoders.
+     */
+    private static final Map<RecordType, RecordEncoder> DEFAULT_ENCODERS;
+
+    static
+    {
+        Map<RecordType, RecordEncoder> map = new HashMap<RecordType, RecordEncoder>();
+
+        map.put( RecordType.SOA, new StartOfAuthorityRecordEncoder() );
+        map.put( RecordType.A, new AddressRecordEncoder() );
+        map.put( RecordType.NS, new NameServerRecordEncoder() );
+        map.put( RecordType.CNAME, new CanonicalNameRecordEncoder() );
+        map.put( RecordType.PTR, new PointerRecordEncoder() );
+        map.put( RecordType.MX, new MailExchangeRecordEncoder() );
+        map.put( RecordType.SRV, new ServerSelectionRecordEncoder() );
+        map.put( RecordType.TXT, new TextRecordEncoder() );
+
+        DEFAULT_ENCODERS = Collections.unmodifiableMap( map );
+    }
+
+
+    /**
+     * Encodes the {@link DnsMessage} into the {@link ByteBuffer}.
+     *
+     * @param byteBuffer
+     * @param message
+     */
+    public void encode( ByteBuffer byteBuffer, DnsMessage message )
+    {
+        byteBuffer.putShort( ( short ) message.getTransactionId() );
+
+        byte header = ( byte ) 0x00;
+        header |= encodeMessageType( message.getMessageType() );
+        header |= encodeOpCode( message.getOpCode() );
+        header |= encodeAuthoritativeAnswer( message.isAuthoritativeAnswer() );
+        header |= encodeTruncated( message.isTruncated() );
+        header |= encodeRecursionDesired( message.isRecursionDesired() );
+        byteBuffer.put( header );
+
+        header = ( byte ) 0x00;
+        header |= encodeRecursionAvailable( message.isRecursionAvailable() );
+        header |= encodeResponseCode( message.getResponseCode() );
+        byteBuffer.put( header );
+
+        byteBuffer
+            .putShort( ( short ) ( message.getQuestionRecords() != null ? message.getQuestionRecords().size() : 0 ) );
+        byteBuffer.putShort( ( short ) ( message.getAnswerRecords() != null ? message.getAnswerRecords().size() : 0 ) );
+        byteBuffer.putShort( ( short ) ( message.getAuthorityRecords() != null ? message.getAuthorityRecords().size()
+            : 0 ) );
+        byteBuffer.putShort( ( short ) ( message.getAdditionalRecords() != null ? message.getAdditionalRecords().size()
+            : 0 ) );
+
+        putQuestionRecords( byteBuffer, message.getQuestionRecords() );
+        putResourceRecords( byteBuffer, message.getAnswerRecords() );
+        putResourceRecords( byteBuffer, message.getAuthorityRecords() );
+        putResourceRecords( byteBuffer, message.getAdditionalRecords() );
+    }
+
+
+    private void putQuestionRecords( ByteBuffer byteBuffer, List<QuestionRecord> questions )
+    {
+        if ( questions == null )
+        {
+            return;
+        }
+
+        QuestionRecordEncoder encoder = new QuestionRecordEncoder();
+
+        Iterator<QuestionRecord> it = questions.iterator();
+
+        while ( it.hasNext() )
+        {
+            QuestionRecord question = it.next();
+            encoder.put( byteBuffer, question );
+        }
+    }
+
+
+    private void putResourceRecords( ByteBuffer byteBuffer, List<ResourceRecord> records )
+    {
+        if ( records == null )
+        {
+            return;
+        }
+
+        Iterator<ResourceRecord> it = records.iterator();
+
+        while ( it.hasNext() )
+        {
+            ResourceRecord record = it.next();
+
+            try
+            {
+                put( byteBuffer, record );
+            }
+            catch ( IOException ioe )
+            {
+                log.error( ioe.getMessage(), ioe );
+            }
+        }
+    }
+
+
+    private void put( ByteBuffer byteBuffer, ResourceRecord record ) throws IOException
+    {
+        RecordType type = record.getRecordType();
+
+        RecordEncoder encoder = DEFAULT_ENCODERS.get( type );
+
+        if ( encoder == null )
+        {
+            throw new IOException( "Encoder unavailable for " + type );
+        }
+
+        encoder.put( byteBuffer, record );
+    }
+
+
+    private byte encodeMessageType( MessageType messageType )
+    {
+        byte oneBit = ( byte ) ( messageType.convert() & 0x01 );
+        return ( byte ) ( oneBit << 7 );
+    }
+
+
+    private byte encodeOpCode( OpCode opCode )
+    {
+        byte fourBits = ( byte ) ( opCode.convert() & 0x0F );
+        return ( byte ) ( fourBits << 3 );
+    }
+
+
+    private byte encodeAuthoritativeAnswer( boolean authoritative )
+    {
+        if ( authoritative )
+        {
+            return ( byte ) ( ( byte ) 0x01 << 2 );
+        }
+        return ( byte ) 0;
+    }
+
+
+    private byte encodeTruncated( boolean truncated )
+    {
+        if ( truncated )
+        {
+            return ( byte ) ( ( byte ) 0x01 << 1 );
+        }
+        return 0;
+    }
+
+
+    private byte encodeRecursionDesired( boolean recursionDesired )
+    {
+        if ( recursionDesired )
+        {
+            return ( byte ) 0x01;
+        }
+        return 0;
+    }
+
+
+    private byte encodeRecursionAvailable( boolean recursionAvailable )
+    {
+        if ( recursionAvailable )
+        {
+            return ( byte ) ( ( byte ) 0x01 << 7 );
+        }
+        return 0;
+    }
+
+
+    private byte encodeResponseCode( ResponseCode responseCode )
+    {
+        return ( byte ) ( responseCode.convert() & 0x0F );
+    }
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/HostInformationRecordEncoder.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/HostInformationRecordEncoder.java
new file mode 100644
index 0000000..1ad4656
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/HostInformationRecordEncoder.java
@@ -0,0 +1,51 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.io.encoder;
+
+
+/**
+ * 3.3.2. HINFO RDATA format
+ * 
+ *     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *     /                      CPU                      /
+ *     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *     /                       OS                      /
+ *     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ * 
+ * where:
+ * 
+ * CPU             A <character-string> which specifies the CPU type.
+ * 
+ * OS              A <character-string> which specifies the operating
+ *                 system type.
+ * 
+ * Standard values for CPU and OS can be found in [RFC-1010].
+ * 
+ * HINFO records are used to acquire general information about a host.  The
+ * main use is for protocols such as FTP that can use special procedures
+ * when talking between machines or operating systems of the same type.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class HostInformationRecordEncoder
+{
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/Inet6AddressRecordEncoder.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/Inet6AddressRecordEncoder.java
new file mode 100755
index 0000000..4f4959b
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/Inet6AddressRecordEncoder.java
@@ -0,0 +1,83 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.io.encoder;
+
+
+/**
+ * 2. New resource record definition and domain
+ * 
+ *    A record type is defined to store a host's IPv6 address.  A host that
+ *    has more than one IPv6 address must have more than one such record.
+ * 
+ * 2.1 AAAA record type
+ * 
+ *    The AAAA resource record type is a record specific to the Internet
+ *    class that stores a single IPv6 address.
+ * 
+ *    The IANA assigned value of the type is 28 (decimal).
+ * 
+ * 2.2 AAAA data format
+ * 
+ *    A 128 bit IPv6 address is encoded in the data portion of an AAAA
+ *    resource record in network byte order (high-order byte first).
+ * 
+ * 2.3 AAAA query
+ * 
+ *    An AAAA query for a specified domain name in the Internet class
+ *    returns all associated AAAA resource records in the answer section of
+ *    a response.
+ * 
+ *    A type AAAA query does not trigger additional section processing.
+ * 
+ * 2.4 Textual format of AAAA records
+ * 
+ *    The textual representation of the data portion of the AAAA resource
+ *    record used in a master database file is the textual representation
+ *    of an IPv6 address as defined in [3].
+ * 
+ * 2.5 IP6.ARPA Domain
+ * 
+ *    A special domain is defined to look up a record given an IPv6
+ *    address.  The intent of this domain is to provide a way of mapping an
+ *    IPv6 address to a host name, although it may be used for other
+ *    purposes as well.  The domain is rooted at IP6.ARPA.
+ * 
+ *    An IPv6 address is represented as a name in the IP6.ARPA domain by a
+ *    sequence of nibbles separated by dots with the suffix ".IP6.ARPA".
+ *    The sequence of nibbles is encoded in reverse order, i.e., the
+ *    low-order nibble is encoded first, followed by the next low-order
+ *    nibble and so on.  Each nibble is represented by a hexadecimal digit.
+ *    For example, the reverse lookup domain name corresponding to the
+ *    address
+ * 
+ *        4321:0:1:2:3:4:567:89ab
+ * 
+ *    would be
+ * 
+ *    b.a.9.8.7.6.5.0.4.0.0.0.3.0.0.0.2.0.0.0.1.0.0.0.0.0.0.0.1.2.3.4.IP6.
+ *                                                                   ARPA.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class Inet6AddressRecordEncoder
+{
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/KeyExchangeRecordEncoder.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/KeyExchangeRecordEncoder.java
new file mode 100755
index 0000000..3f7f1a1
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/KeyExchangeRecordEncoder.java
@@ -0,0 +1,57 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.io.encoder;
+
+
+/**
+ * 3.1 KX RDATA format
+ * 
+ *    The KX DNS record has the following RDATA format:
+ * 
+ *     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *     |                  PREFERENCE                   |
+ *     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *     /                   EXCHANGER                   /
+ *     /                                               /
+ *     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ * 
+ *    where:
+ * 
+ *    PREFERENCE      A 16 bit non-negative integer which specifies the
+ *                    preference given to this RR among other KX records
+ *                    at the same owner.  Lower values are preferred.
+ * 
+ *    EXCHANGER       A <domain-name> which specifies a host willing to
+ *                    act as a mail exchange for the owner name.
+ * 
+ *    KX records MUST cause type A additional section processing for the
+ *    host specified by EXCHANGER.  In the event that the host processing
+ *    the DNS transaction supports IPv6, KX records MUST also cause type
+ *    AAAA additional section processing.
+ * 
+ *    The KX RDATA field MUST NOT be compressed.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class KeyExchangeRecordEncoder
+{
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/KeyRecordEncoder.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/KeyRecordEncoder.java
new file mode 100755
index 0000000..fee59c5
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/KeyRecordEncoder.java
@@ -0,0 +1,64 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.io.encoder;
+
+
+/**
+ * 3. The KEY Resource Record
+ * 
+ *    The KEY resource record (RR) is used to store a public key that is
+ *    associated with a Domain Name System (DNS) name.  This can be the
+ *    public key of a zone, a user, or a host or other end entity. Security
+ *    aware DNS implementations MUST be designed to handle at least two
+ *    simultaneously valid keys of the same type associated with the same
+ *    name.
+ * 
+ *    The type number for the KEY RR is 25.
+ * 
+ *    A KEY RR is, like any other RR, authenticated by a SIG RR.  KEY RRs
+ *    must be signed by a zone level key.
+ * 
+ * 3.1 KEY RDATA format
+ * 
+ *    The RDATA for a KEY RR consists of flags, a protocol octet, the
+ *    algorithm number octet, and the public key itself.  The format is as
+ *    follows:
+ * 
+ *                         1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ *     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *    |             flags             |    protocol   |   algorithm   |
+ *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *    |                                                               /
+ *    /                          public key                           /
+ *    /                                                               /
+ *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-|
+ * 
+ *    The KEY RR is not intended for storage of certificates and a separate
+ *    certificate RR has been developed for that purpose, defined in [RFC
+ *    2538].
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class KeyRecordEncoder
+{
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/LocationRecordEncoder.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/LocationRecordEncoder.java
new file mode 100755
index 0000000..107cb14
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/LocationRecordEncoder.java
@@ -0,0 +1,121 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.io.encoder;
+
+
+/**
+ * 2. RDATA Format
+ * 
+ *        MSB                                           LSB
+ *        +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *       0|        VERSION        |         SIZE          |
+ *        +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *       2|       HORIZ PRE       |       VERT PRE        |
+ *        +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *       4|                   LATITUDE                    |
+ *        +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *       6|                   LATITUDE                    |
+ *        +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *       8|                   LONGITUDE                   |
+ *        +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *      10|                   LONGITUDE                   |
+ *        +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *      12|                   ALTITUDE                    |
+ *        +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *      14|                   ALTITUDE                    |
+ *        +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *    (octet)
+ * 
+ * where:
+ * 
+ * VERSION      Version number of the representation.  This must be zero.
+ *              Implementations are required to check this field and make
+ *              no assumptions about the format of unrecognized versions.
+ * 
+ * SIZE         The diameter of a sphere enclosing the described entity, in
+ *              centimeters, expressed as a pair of four-bit unsigned
+ *              integers, each ranging from zero to nine, with the most
+ *              significant four bits representing the base and the second
+ *              number representing the power of ten by which to multiply
+ *              the base.  This allows sizes from 0e0 (<1cm) to 9e9
+ *              (90,000km) to be expressed.  This representation was chosen
+ *              such that the hexadecimal representation can be read by
+ *              eye; 0x15 = 1e5.  Four-bit values greater than 9 are
+ *              undefined, as are values with a base of zero and a non-zero
+ *              exponent.
+ * 
+ *              Since 20000000m (represented by the value 0x29) is greater
+ *              than the equatorial diameter of the WGS 84 ellipsoid
+ *              (12756274m), it is therefore suitable for use as a
+ *              "worldwide" size.
+ * 
+ * HORIZ PRE    The horizontal precision of the data, in centimeters,
+ *              expressed using the same representation as SIZE.  This is
+ *              the diameter of the horizontal "circle of error", rather
+ *              than a "plus or minus" value.  (This was chosen to match
+ *              the interpretation of SIZE; to get a "plus or minus" value,
+ *              divide by 2.)
+ * 
+ * VERT PRE     The vertical precision of the data, in centimeters,
+ *              expressed using the sane representation as for SIZE.  This
+ *              is the total potential vertical error, rather than a "plus
+ *              or minus" value.  (This was chosen to match the
+ *              interpretation of SIZE; to get a "plus or minus" value,
+ *              divide by 2.)  Note that if altitude above or below sea
+ *              level is used as an approximation for altitude relative to
+ *              the [WGS 84] ellipsoid, the precision value should be
+ *              adjusted.
+ * 
+ * LATITUDE     The latitude of the center of the sphere described by the
+ *              SIZE field, expressed as a 32-bit integer, most significant
+ *              octet first (network standard byte order), in thousandths
+ *              of a second of arc.  2^31 represents the equator; numbers
+ *              above that are north latitude.
+ * 
+ * LONGITUDE    The longitude of the center of the sphere described by the
+ *              SIZE field, expressed as a 32-bit integer, most significant
+ *              octet first (network standard byte order), in thousandths
+ *              of a second of arc, rounded away from the prime meridian.
+ *              2^31 represents the prime meridian; numbers above that are
+ *              east longitude.
+ * 
+ * ALTITUDE     The altitude of the center of the sphere described by the
+ *              SIZE field, expressed as a 32-bit integer, most significant
+ *              octet first (network standard byte order), in centimeters,
+ *              from a base of 100,000m below the [WGS 84] reference
+ *              spheroid used by GPS (semimajor axis a=6378137.0,
+ *              reciprocal flattening rf=298.257223563).  Altitude above
+ *              (or below) sea level may be used as an approximation of
+ *              altitude relative to the the [WGS 84] spheroid, though due
+ *              to the Earth's surface not being a perfect spheroid, there
+ *              will be differences.  (For example, the geoid (which sea
+ *              level approximates) for the continental US ranges from 10
+ *              meters to 50 meters below the [WGS 84] spheroid.
+ *              Adjustments to ALTITUDE and/or VERT PRE will be necessary
+ *              in most cases.  The Defense Mapping Agency publishes geoid
+ *              height values relative to the [WGS 84] ellipsoid.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class LocationRecordEncoder
+{
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/MailExchangeRecordEncoder.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/MailExchangeRecordEncoder.java
new file mode 100644
index 0000000..244420f
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/MailExchangeRecordEncoder.java
@@ -0,0 +1,62 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.io.encoder;
+
+
+import org.apache.directory.server.dns.messages.ResourceRecord;
+import org.apache.directory.server.dns.store.DnsAttribute;
+import org.apache.mina.common.ByteBuffer;
+
+
+/**
+ * 3.3.9. MX RDATA format
+ * 
+ *     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *     |                  PREFERENCE                   |
+ *     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *     /                   EXCHANGE                    /
+ *     /                                               /
+ *     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ * 
+ * where:
+ * 
+ * PREFERENCE      A 16 bit integer which specifies the preference given to
+ *                 this RR among others at the same owner.  Lower values
+ *                 are preferred.
+ * 
+ * EXCHANGE        A <domain-name> which specifies a host willing to act as
+ *                 a mail exchange for the owner name.
+ * 
+ * MX records cause type A additional section processing for the host
+ * specified by EXCHANGE.  The use of MX RRs is explained in detail in
+ * [RFC-974].
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class MailExchangeRecordEncoder extends ResourceRecordEncoder
+{
+    protected void putResourceRecordData( ByteBuffer byteBuffer, ResourceRecord record )
+    {
+        byteBuffer.putShort( Short.parseShort( record.get( DnsAttribute.MX_PREFERENCE ) ) );
+        putDomainName( byteBuffer, record.get( DnsAttribute.DOMAIN_NAME ) );
+    }
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/MailInformationRecordEncoder.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/MailInformationRecordEncoder.java
new file mode 100644
index 0000000..35d3f2a
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/MailInformationRecordEncoder.java
@@ -0,0 +1,59 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.io.encoder;
+
+
+/**
+ * 3.3.7. MINFO RDATA format (EXPERIMENTAL)
+ * 
+ *     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *     /                    RMAILBX                    /
+ *     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *     /                    EMAILBX                    /
+ *     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ * 
+ * where:
+ * 
+ * RMAILBX         A <domain-name> which specifies a mailbox which is
+ *                 responsible for the mailing list or mailbox.  If this
+ *                 domain name names the root, the owner of the MINFO RR is
+ *                 responsible for itself.  Note that many existing mailing
+ *                 lists use a mailbox X-request for the RMAILBX field of
+ *                 mailing list X, e.g., Msgroup-request for Msgroup.  This
+ *                 field provides a more general mechanism.
+ * 
+ * EMAILBX         A <domain-name> which specifies a mailbox which is to
+ *                 receive error messages related to the mailing list or
+ *                 mailbox specified by the owner of the MINFO RR (similar
+ *                 to the ERRORS-TO: field which has been proposed).  If
+ *                 this domain name names the root, errors should be
+ *                 returned to the sender of the message.
+ * 
+ * MINFO records cause no additional section processing.  Although these
+ * records can be associated with a simple mailbox, they are usually used
+ * with a mailing list.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class MailInformationRecordEncoder
+{
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/NameAuthorityPointerEncoder.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/NameAuthorityPointerEncoder.java
new file mode 100755
index 0000000..7f33161
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/NameAuthorityPointerEncoder.java
@@ -0,0 +1,138 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.io.encoder;
+
+
+/**
+ * 4. NAPTR RR Format
+ * 
+ * 4.1 Packet Format
+ * 
+ *    The packet format of the NAPTR RR is given below.  The DNS type code
+ *    for NAPTR is 35.
+ * 
+ *       The packet format for the NAPTR record is as follows
+ *                                        1  1  1  1  1  1
+ *          0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
+ *        +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *        |                     ORDER                     |
+ *        +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *        |                   PREFERENCE                  |
+ *        +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *        /                     FLAGS                     /
+ *        +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *        /                   SERVICES                    /
+ *        +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *        /                    REGEXP                     /
+ *        +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *        /                  REPLACEMENT                  /
+ *        /                                               /
+ *        +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ * 
+ *    <character-string> and <domain-name> as used here are defined in RFC
+ *    1035 [7].
+ * 
+ *    ORDER
+ *       A 16-bit unsigned integer specifying the order in which the NAPTR
+ *       records MUST be processed in order to accurately represent the
+ *       ordered list of Rules.  The ordering is from lowest to highest.
+ *       If two records have the same order value then they are considered
+ *       to be the same rule and should be selected based on the
+ *       combination of the Preference values and Services offered.
+ * 
+ *    PREFERENCE
+ *       Although it is called "preference" in deference to DNS
+ *       terminology, this field is equivalent to the Priority value in the
+ *       DDDS Algorithm.  It is a 16-bit unsigned integer that specifies
+ *       the order in which NAPTR records with equal Order values SHOULD be
+ *       processed, low numbers being processed before high numbers.  This
+ *       is similar to the preference field in an MX record, and is used so
+ *       domain administrators can direct clients towards more capable
+ *       hosts or lighter weight protocols.  A client MAY look at records
+ *       with higher preference values if it has a good reason to do so
+ *       such as not supporting some protocol or service very well.
+ * 
+ *       The important difference between Order and Preference is that once
+ *       a match is found the client MUST NOT consider records with a
+ *       different Order but they MAY process records with the same Order
+ *       but different Preferences.  The only exception to this is noted in
+ *       the second important Note in the DDDS algorithm specification
+ *       concerning allowing clients to use more complex Service
+ *       determination between steps 3 and 4 in the algorithm.  Preference
+ *       is used to give communicate a higher quality of service to rules
+ *       that are considered the same from an authority standpoint but not
+ *       from a simple load balancing standpoint.
+ * 
+ *       It is important to note that DNS contains several load balancing
+ *       mechanisms and if load balancing among otherwise equal services
+ *       should be needed then methods such as SRV records or multiple A
+ *       records should be utilized to accomplish load balancing.
+ * 
+ *    FLAGS
+ *       A <character-string> containing flags to control aspects of the
+ *       rewriting and interpretation of the fields in the record.  Flags
+ *       are single characters from the set A-Z and 0-9.  The case of the
+ *       alphabetic characters is not significant.  The field can be empty.
+ * 
+ *       It is up to the Application specifying how it is using this
+ *       Database to define the Flags in this field.  It must define which
+ *       ones are terminal and which ones are not.
+ * 
+ *    SERVICES
+ *       A <character-string> that specifies the Service Parameters
+ *       applicable to this this delegation path.  It is up to the
+ *       Application Specification to specify the values found in this
+ *       field.
+ * 
+ *    REGEXP
+ *       A <character-string> containing a substitution expression that is
+ *       applied to the original string held by the client in order to
+ *       construct the next domain name to lookup.  See the DDDS Algorithm
+ *       specification for the syntax of this field.
+ * 
+ *       As stated in the DDDS algorithm, The regular expressions MUST NOT
+ *       be used in a cumulative fashion, that is, they should only be
+ *       applied to the original string held by the client, never to the
+ *       domain name produced by a previous NAPTR rewrite.  The latter is
+ *       tempting in some applications but experience has shown such use to
+ *       be extremely fault sensitive, very error prone, and extremely
+ *       difficult to debug.
+ * 
+ *    REPLACEMENT
+ *       A <domain-name> which is the next domain-name to query for
+ *       depending on the potential values found in the flags field.  This
+ *       field is used when the regular expression is a simple replacement
+ *       operation.  Any value in this field MUST be a fully qualified
+ *       domain-name.  Name compression is not to be used for this field.
+ * 
+ *       This field and the REGEXP field together make up the Substitution
+ *       Expression in the DDDS Algorithm.  It is simply a historical
+ *       optimization specifically for DNS compression that this field
+ *       exists.  The fields are also mutually exclusive.  If a record is
+ *       returned that has values for both fields then it is considered to
+ *       be in error and SHOULD be either ignored or an error returned.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class NameAuthorityPointerEncoder
+{
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/NameServerRecordEncoder.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/NameServerRecordEncoder.java
new file mode 100644
index 0000000..b9e3e26
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/NameServerRecordEncoder.java
@@ -0,0 +1,64 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.io.encoder;
+
+
+import org.apache.directory.server.dns.messages.ResourceRecord;
+import org.apache.directory.server.dns.store.DnsAttribute;
+import org.apache.mina.common.ByteBuffer;
+
+
+/**
+ * 3.3.11. NS RDATA format
+ * 
+ *     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *     /                   NSDNAME                     /
+ *     /                                               /
+ *     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ * 
+ * where:
+ * 
+ * NSDNAME         A <domain-name> which specifies a host which should be
+ *                 authoritative for the specified class and domain.
+ * 
+ * NS records cause both the usual additional section processing to locate
+ * a type A record, and, when used in a referral, a special search of the
+ * zone in which they reside for glue information.
+ * 
+ * The NS RR states that the named host should be expected to have a zone
+ * starting at owner name of the specified class.  Note that the class may
+ * not indicate the protocol family which should be used to communicate
+ * with the host, although it is typically a strong hint.  For example,
+ * hosts which are name servers for either Internet (IN) or Hesiod (HS)
+ * class information are normally queried using IN class protocols.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class NameServerRecordEncoder extends ResourceRecordEncoder
+{
+    protected void putResourceRecordData( ByteBuffer byteBuffer, ResourceRecord record )
+    {
+        String domainName = record.get( DnsAttribute.DOMAIN_NAME );
+
+        putDomainName( byteBuffer, domainName );
+    }
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/NextNameRecordEncoder.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/NextNameRecordEncoder.java
new file mode 100755
index 0000000..a2b19d0
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/NextNameRecordEncoder.java
@@ -0,0 +1,60 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.io.encoder;
+
+
+/**
+ * 5.2 NXT RDATA Format
+ * 
+ *    The RDATA for an NXT RR consists simply of a domain name followed by
+ *    a bit map.
+ * 
+ *    The type number for the NXT RR is 30.
+ * 
+ *                            1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ *        0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ *       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *       |         next domain name                                      /
+ *       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *       |                    type bit map                               /
+ *       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 
+ *    The NXT RR type bit map is one bit per RR type present for the owner
+ *    name similar to the WKS socket bit map.  The first bit represents RR
+ *    type zero (an illegal type which should not be present.) A one bit
+ *    indicates that at least one RR of that type is present for the owner
+ *    name.  A zero indicates that no such RR is present.  All bits not
+ *    specified because they are beyond the end of the bit map are assumed
+ *    to be zero.  Note that bit 30, for NXT, will always be on so the
+ *    minimum bit map length is actually four octets.  The NXT bit map
+ *    should be printed as a list of RR type mnemonics or decimal numbers
+ *    similar to the WKS RR.
+ * 
+ *    The domain name may be compressed with standard DNS name compression
+ *    when being transmitted over the network.  The size of the bit map can
+ *    be inferred from the RDLENGTH and the length of the next domain name.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class NextNameRecordEncoder
+{
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/PointerRecordEncoder.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/PointerRecordEncoder.java
new file mode 100644
index 0000000..edde4a5
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/PointerRecordEncoder.java
@@ -0,0 +1,58 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.io.encoder;
+
+
+import org.apache.directory.server.dns.messages.ResourceRecord;
+import org.apache.directory.server.dns.store.DnsAttribute;
+import org.apache.mina.common.ByteBuffer;
+
+
+/**
+ * 3.3.12. PTR RDATA format
+ * 
+ *     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *     /                   PTRDNAME                    /
+ *     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ * 
+ * where:
+ * 
+ * PTRDNAME        A <domain-name> which points to some location in the
+ *                 domain name space.
+ * 
+ * PTR records cause no additional section processing.  These RRs are used
+ * in special domains to point to some other location in the domain space.
+ * These records are simple data, and don't imply any special processing
+ * similar to that performed by CNAME, which identifies aliases.  See the
+ * description of the IN-ADDR.ARPA domain for an example.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class PointerRecordEncoder extends ResourceRecordEncoder
+{
+    protected void putResourceRecordData( ByteBuffer byteBuffer, ResourceRecord record )
+    {
+        String domainName = record.get( DnsAttribute.DOMAIN_NAME );
+
+        putDomainName( byteBuffer, domainName );
+    }
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/QuestionRecordEncoder.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/QuestionRecordEncoder.java
new file mode 100644
index 0000000..4d21e57
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/QuestionRecordEncoder.java
@@ -0,0 +1,79 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.io.encoder;
+
+
+import org.apache.directory.server.dns.messages.QuestionRecord;
+import org.apache.directory.server.dns.messages.RecordClass;
+import org.apache.directory.server.dns.messages.RecordType;
+import org.apache.mina.common.ByteBuffer;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class QuestionRecordEncoder
+{
+    /**
+     * Encodes the {@link QuestionRecord} into the {@link ByteBuffer}.
+     *
+     * @param out
+     * @param question
+     */
+    public void put( ByteBuffer out, QuestionRecord question )
+    {
+        encodeDomainName( out, question.getDomainName() );
+        encodeRecordType( out, question.getRecordType() );
+        encodeRecordClass( out, question.getRecordClass() );
+    }
+
+
+    private void encodeDomainName( ByteBuffer byteBuffer, String domainName )
+    {
+        String[] labels = domainName.split( "\\." );
+
+        for ( int ii = 0; ii < labels.length; ii++ )
+        {
+            byteBuffer.put( ( byte ) labels[ii].length() );
+
+            char[] characters = labels[ii].toCharArray();
+            for ( int jj = 0; jj < characters.length; jj++ )
+            {
+                byteBuffer.put( ( byte ) characters[jj] );
+            }
+        }
+
+        byteBuffer.put( ( byte ) 0x00 );
+    }
+
+
+    private void encodeRecordType( ByteBuffer byteBuffer, RecordType recordType )
+    {
+        byteBuffer.putShort( recordType.convert() );
+    }
+
+
+    private void encodeRecordClass( ByteBuffer byteBuffer, RecordClass recordClass )
+    {
+        byteBuffer.putShort( recordClass.convert() );
+    }
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/RecordEncoder.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/RecordEncoder.java
new file mode 100644
index 0000000..fdb2c48
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/RecordEncoder.java
@@ -0,0 +1,44 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.io.encoder;
+
+
+import java.io.IOException;
+
+import org.apache.directory.server.dns.messages.ResourceRecord;
+import org.apache.mina.common.ByteBuffer;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public interface RecordEncoder
+{
+    /**
+     * Encodes the {@link ResourceRecord} into the {@link ByteBuffer}.
+     *
+     * @param buffer
+     * @param record
+     * @throws IOException
+     */
+    public void put( ByteBuffer buffer, ResourceRecord record ) throws IOException;
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/ResourceRecordEncoder.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/ResourceRecordEncoder.java
new file mode 100644
index 0000000..97066e0
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/ResourceRecordEncoder.java
@@ -0,0 +1,132 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.io.encoder;
+
+
+import java.io.IOException;
+
+import org.apache.directory.server.dns.messages.RecordClass;
+import org.apache.directory.server.dns.messages.RecordType;
+import org.apache.directory.server.dns.messages.ResourceRecord;
+import org.apache.mina.common.ByteBuffer;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public abstract class ResourceRecordEncoder implements RecordEncoder
+{
+    public void put( ByteBuffer byteBuffer, ResourceRecord record ) throws IOException
+    {
+        putDomainName( byteBuffer, record.getDomainName() );
+        putRecordType( byteBuffer, record.getRecordType() );
+        putRecordClass( byteBuffer, record.getRecordClass() );
+
+        byteBuffer.putInt( record.getTimeToLive() );
+
+        putResourceRecord( byteBuffer, record );
+    }
+
+
+    protected abstract void putResourceRecordData( ByteBuffer byteBuffer, ResourceRecord record );
+
+
+    protected void putResourceRecord( ByteBuffer byteBuffer, ResourceRecord record )
+    {
+        int startPosition = byteBuffer.position();
+        byteBuffer.position( startPosition + 2 );
+
+        putResourceRecordData( byteBuffer, record );
+
+        putDataSize( byteBuffer, startPosition );
+    }
+
+
+    protected void putDataSize( ByteBuffer byteBuffer, int startPosition )
+    {
+        int endPosition = byteBuffer.position();
+        short length = ( short ) ( endPosition - startPosition - 2 );
+
+        byteBuffer.position( startPosition );
+        byteBuffer.putShort( length );
+        byteBuffer.position( endPosition );
+    }
+
+
+    /**
+     * <domain-name> is a domain name represented as a series of labels, and
+     * terminated by a label with zero length.
+     * 
+     * @param byteBuffer the ByteBuffer to encode the domain name into
+     * @param domainName the domain name to encode
+     */
+    protected void putDomainName( ByteBuffer byteBuffer, String domainName )
+    {
+        String[] labels = domainName.split( "\\." );
+
+        for ( int ii = 0; ii < labels.length; ii++ )
+        {
+            byteBuffer.put( ( byte ) labels[ii].length() );
+
+            char[] characters = labels[ii].toCharArray();
+            for ( int jj = 0; jj < characters.length; jj++ )
+            {
+                byteBuffer.put( ( byte ) characters[jj] );
+            }
+        }
+
+        byteBuffer.put( ( byte ) 0x00 );
+    }
+
+
+    protected void putRecordType( ByteBuffer byteBuffer, RecordType recordType )
+    {
+        byteBuffer.putShort( recordType.convert() );
+    }
+
+
+    protected void putRecordClass( ByteBuffer byteBuffer, RecordClass recordClass )
+    {
+        byteBuffer.putShort( recordClass.convert() );
+    }
+
+
+    /**
+     * <character-string> is a single length octet followed by that number
+     * of characters.  <character-string> is treated as binary information,
+     * and can be up to 256 characters in length (including the length octet).
+     * 
+     * @param byteBuffer The byte buffer to encode the character string into.
+     * @param characterString the character string to encode
+     */
+    protected void putCharacterString( ByteBuffer byteBuffer, String characterString )
+    {
+        byteBuffer.put( ( byte ) characterString.length() );
+
+        char[] characters = characterString.toCharArray();
+
+        for ( int ii = 0; ii < characters.length; ii++ )
+        {
+            byteBuffer.put( ( byte ) characters[ii] );
+        }
+    }
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/ServerSelectionRecordEncoder.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/ServerSelectionRecordEncoder.java
new file mode 100755
index 0000000..aba3d8e
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/ServerSelectionRecordEncoder.java
@@ -0,0 +1,140 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.io.encoder;
+
+
+import org.apache.directory.server.dns.messages.ResourceRecord;
+import org.apache.directory.server.dns.store.DnsAttribute;
+import org.apache.mina.common.ByteBuffer;
+
+
+/**
+ * The format of the SRV RR
+ * 
+ *    Here is the format of the SRV RR, whose DNS type code is 33:
+ * 
+ *         _Service._Proto.Name TTL Class SRV Priority Weight Port Target
+ * 
+ *         (There is an example near the end of this document.)
+ * 
+ *    Service
+ *         The symbolic name of the desired service, as defined in Assigned
+ *         Numbers [STD 2] or locally.  An underscore (_) is prepended to
+ *         the service identifier to avoid collisions with DNS labels that
+ *         occur in nature.
+ * 
+ *         Some widely used services, notably POP, don't have a single
+ *         universal name.  If Assigned Numbers names the service
+ *         indicated, that name is the only name which is legal for SRV
+ *         lookups.  The Service is case insensitive.
+ * 
+ *    Proto
+ *         The symbolic name of the desired protocol, with an underscore
+ *         (_) prepended to prevent collisions with DNS labels that occur
+ *         in nature.  _TCP and _UDP are at present the most useful values
+ *         for this field, though any name defined by Assigned Numbers or
+ *         locally may be used (as for Service).  The Proto is case
+ *         insensitive.
+ * 
+ *    Name
+ *         The domain this RR refers to.  The SRV RR is unique in that the
+ *         name one searches for is not this name; the example near the end
+ *         shows this clearly.
+ * 
+ *    TTL
+ *         Standard DNS meaning [RFC 1035].
+ * 
+ *    Class
+ *         Standard DNS meaning [RFC 1035].   SRV records occur in the IN
+ *         Class.
+ * 
+ *    Priority
+ *         The priority of this target host.  A client MUST attempt to
+ *         contact the target host with the lowest-numbered priority it can
+ *         reach; target hosts with the same priority SHOULD be tried in an
+ *         order defined by the weight field.  The range is 0-65535.  This
+ *         is a 16 bit unsigned integer in network byte order.
+ * 
+ *    Weight
+ *         A server selection mechanism.  The weight field specifies a
+ *         relative weight for entries with the same priority. Larger
+ *         weights SHOULD be given a proportionately higher probability of
+ *         being selected. The range of this number is 0-65535.  This is a
+ *         16 bit unsigned integer in network byte order.  Domain
+ *         administrators SHOULD use Weight 0 when there isn't any server
+ *         selection to do, to make the RR easier to read for humans (less
+ *         noisy).  In the presence of records containing weights greater
+ *         than 0, records with weight 0 should have a very small chance of
+ *         being selected.
+ * 
+ *         In the absence of a protocol whose specification calls for the
+ *         use of other weighting information, a client arranges the SRV
+ *         RRs of the same Priority in the order in which target hosts,
+ *         specified by the SRV RRs, will be contacted. The following
+ *         algorithm SHOULD be used to order the SRV RRs of the same
+ *         priority:
+ * 
+ *         To select a target to be contacted next, arrange all SRV RRs
+ *         (that have not been ordered yet) in any order, except that all
+ *         those with weight 0 are placed at the beginning of the list.
+ * 
+ *         Compute the sum of the weights of those RRs, and with each RR
+ *         associate the running sum in the selected order. Then choose a
+ *         uniform random number between 0 and the sum computed
+ *         (inclusive), and select the RR whose running sum value is the
+ *         first in the selected order which is greater than or equal to
+ *         the random number selected. The target host specified in the
+ *         selected SRV RR is the next one to be contacted by the client.
+ *         Remove this SRV RR from the set of the unordered SRV RRs and
+ *         apply the described algorithm to the unordered SRV RRs to select
+ *         the next target host.  Continue the ordering process until there
+ *         are no unordered SRV RRs.  This process is repeated for each
+ *         Priority.
+ * 
+ *    Port
+ *         The port on this target host of this service.  The range is 0-
+ *         65535.  This is a 16 bit unsigned integer in network byte order.
+ *         This is often as specified in Assigned Numbers but need not be.
+ * 
+ *    Target
+ *         The domain name of the target host.  There MUST be one or more
+ *         address records for this name, the name MUST NOT be an alias (in
+ *         the sense of RFC 1034 or RFC 2181).  Implementors are urged, but
+ *         not required, to return the address record(s) in the Additional
+ *         Data section.  Unless and until permitted by future standards
+ *         action, name compression is not to be used for this field.
+ * 
+ *         A Target of "." means that the service is decidedly not
+ *         available at this domain.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ServerSelectionRecordEncoder extends ResourceRecordEncoder
+{
+    protected void putResourceRecordData( ByteBuffer byteBuffer, ResourceRecord record )
+    {
+        byteBuffer.putShort( Short.parseShort( record.get( DnsAttribute.SERVICE_PRIORITY ) ) );
+        byteBuffer.putShort( Short.parseShort( record.get( DnsAttribute.SERVICE_WEIGHT ) ) );
+        byteBuffer.putShort( Short.parseShort( record.get( DnsAttribute.SERVICE_PORT ) ) );
+        putDomainName( byteBuffer, record.get( DnsAttribute.DOMAIN_NAME ) );
+    }
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/SignatureRecordEncoder.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/SignatureRecordEncoder.java
new file mode 100755
index 0000000..6719809
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/SignatureRecordEncoder.java
@@ -0,0 +1,182 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.io.encoder;
+
+
+/**
+ * 4.1 SIG RDATA Format
+ * 
+ *    The RDATA portion of a SIG RR is as shown below.  The integrity of
+ *    the RDATA information is protected by the signature field.
+ * 
+ *                            1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ *        0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ *       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *       |        type covered           |  algorithm    |     labels    |
+ *       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *       |                         original TTL                          |
+ *       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *       |                      signature expiration                     |
+ *       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *       |                      signature inception                      |
+ *       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *       |            key  tag           |                               |
+ *       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+         signer's name         +
+ *       |                                                               /
+ *       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-/
+ *       /                                                               /
+ *       /                            signature                          /
+ *       /                                                               /
+ *       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 
+ * 4.1.1 Type Covered Field
+ * 
+ *    The "type covered" is the type of the other RRs covered by this SIG.
+ * 
+ * 4.1.2 Algorithm Number Field
+ * 
+ *    This octet is as described in section 3.2.
+ * 
+ * 4.1.3 Labels Field
+ * 
+ *    The "labels" octet is an unsigned count of how many labels there are
+ *    in the original SIG RR owner name not counting the null label for
+ *    root and not counting any initial "*" for a wildcard.  If a secured
+ *    retrieval is the result of wild card substitution, it is necessary
+ *    for the resolver to use the original form of the name in verifying
+ *    the digital signature.  This field makes it easy to determine the
+ *    original form.
+ * 
+ *    If, on retrieval, the RR appears to have a longer name than indicated
+ *    by "labels", the resolver can tell it is the result of wildcard
+ *    substitution.  If the RR owner name appears to be shorter than the
+ *    labels count, the SIG RR must be considered corrupt and ignored.  The
+ *    maximum number of labels allowed in the current DNS is 127 but the
+ *    entire octet is reserved and would be required should DNS names ever
+ *    be expanded to 255 labels.  The following table gives some examples.
+ *    The value of "labels" is at the top, the retrieved owner name on the
+ *    left, and the table entry is the name to use in signature
+ *    verification except that "bad" means the RR is corrupt.
+ * 
+ *    labels= |  0  |   1  |    2   |      3   |      4   |
+ *    --------+-----+------+--------+----------+----------+
+ *           .|   . | bad  |  bad   |    bad   |    bad   |
+ *          d.|  *. |   d. |  bad   |    bad   |    bad   |
+ *        c.d.|  *. | *.d. |   c.d. |    bad   |    bad   |
+ *      b.c.d.|  *. | *.d. | *.c.d. |   b.c.d. |    bad   |
+ *    a.b.c.d.|  *. | *.d. | *.c.d. | *.b.c.d. | a.b.c.d. |
+ * 
+ * 4.1.4 Original TTL Field
+ * 
+ *    The "original TTL" field is included in the RDATA portion to avoid
+ *    (1) authentication problems that caching servers would otherwise
+ *    cause by decrementing the real TTL field and (2) security problems
+ *    that unscrupulous servers could otherwise cause by manipulating the
+ *    real TTL field.  This original TTL is protected by the signature
+ *    while the current TTL field is not.
+ * 
+ *    NOTE:  The "original TTL" must be restored into the covered RRs when
+ *    the signature is verified (see Section 8).  This generaly implies
+ *    that all RRs for a particular type, name, and class, that is, all the
+ *    RRs in any particular RRset, must have the same TTL to start with.
+ * 
+ * 4.1.5 Signature Expiration and Inception Fields
+ * 
+ *    The SIG is valid from the "signature inception" time until the
+ *    "signature expiration" time.  Both are unsigned numbers of seconds
+ *    since the start of 1 January 1970, GMT, ignoring leap seconds.  (See
+ *    also Section 4.4.)  Ring arithmetic is used as for DNS SOA serial
+ *    numbers [RFC 1982] which means that these times can never be more
+ *    than about 68 years in the past or the future.  This means that these
+ *    times are ambiguous modulo ~136.09 years.  However there is no
+ *    security flaw because keys are required to be changed to new random
+ *    keys by [RFC 2541] at least every five years.  This means that the
+ *    probability that the same key is in use N*136.09 years later should
+ *    be the same as the probability that a random guess will work.
+ * 
+ *    A SIG RR may have an expiration time numerically less than the
+ *    inception time if the expiration time is near the 32 bit wrap around
+ *    point and/or the signature is long lived.
+ * 
+ *    (To prevent misordering of network requests to update a zone
+ *    dynamically, monotonically increasing "signature inception" times may
+ *    be necessary.)
+ * 
+ *    A secure zone must be considered changed for SOA serial number
+ *    purposes not only when its data is updated but also when new SIG RRs
+ *    are inserted (ie, the zone or any part of it is re-signed).
+ * 
+ * 4.1.6 Key Tag Field
+ * 
+ *    The "key Tag" is a two octet quantity that is used to efficiently
+ *    select between multiple keys which may be applicable and thus check
+ *    that a public key about to be used for the computationally expensive
+ *    effort to check the signature is possibly valid.  For algorithm 1
+ *    (MD5/RSA) as defined in [RFC 2537], it is the next to the bottom two
+ *    octets of the public key modulus needed to decode the signature
+ *    field.  That is to say, the most significant 16 of the least
+ *    significant 24 bits of the modulus in network (big endian) order. For
+ *    all other algorithms, including private algorithms, it is calculated
+ *    as a simple checksum of the KEY RR as described in Appendix C.
+ * 
+ * 4.1.7 Signer's Name Field
+ * 
+ *    The "signer's name" field is the domain name of the signer generating
+ *    the SIG RR.  This is the owner name of the public KEY RR that can be
+ *    used to verify the signature.  It is frequently the zone which
+ *    contained the RRset being authenticated.  Which signers should be
+ *    authorized to sign what is a significant resolver policy question as
+ *    discussed in Section 6. The signer's name may be compressed with
+ *    standard DNS name compression when being transmitted over the
+ *    network.
+ * 
+ * 4.1.8 Signature Field
+ * 
+ *    The actual signature portion of the SIG RR binds the other RDATA
+ *    fields to the RRset of the "type covered" RRs with that owner name
+ *    and class.  This covered RRset is thereby authenticated.  To
+ *    accomplish this, a data sequence is constructed as follows:
+ * 
+ *          data = RDATA | RR(s)...
+ * 
+ *    where "|" is concatenation,
+ * 
+ *    RDATA is the wire format of all the RDATA fields in the SIG RR itself
+ *    (including the canonical form of the signer's name) before but not
+ *    including the signature, and
+ * 
+ *    RR(s) is the RRset of the RR(s) of the type covered with the same
+ *    owner name and class as the SIG RR in canonical form and order as
+ *    defined in Section 8.
+ * 
+ *    How this data sequence is processed into the signature is algorithm
+ *    dependent.  These algorithm dependent formats and procedures are
+ *    described in separate documents (Section 3.2).
+ * 
+ *    SIGs SHOULD NOT be included in a zone for any "meta-type" such as
+ *    ANY, AXFR, etc. (but see section 5.6.2 with regard to IXFR).
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class SignatureRecordEncoder
+{
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/StartOfAuthorityRecordEncoder.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/StartOfAuthorityRecordEncoder.java
new file mode 100644
index 0000000..bb75440
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/StartOfAuthorityRecordEncoder.java
@@ -0,0 +1,120 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.io.encoder;
+
+
+import org.apache.directory.server.dns.messages.ResourceRecord;
+import org.apache.directory.server.dns.store.DnsAttribute;
+import org.apache.mina.common.ByteBuffer;
+
+/**
+ * 3.3.13. SOA RDATA format
+ * 
+ *     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *     /                     MNAME                     /
+ *     /                                               /
+ *     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *     /                     RNAME                     /
+ *     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *     |                    SERIAL                     |
+ *     |                                               |
+ *     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *     |                    REFRESH                    |
+ *     |                                               |
+ *     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *     |                     RETRY                     |
+ *     |                                               |
+ *     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *     |                    EXPIRE                     |
+ *     |                                               |
+ *     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *     |                    MINIMUM                    |
+ *     |                                               |
+ *     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ * 
+ * where:
+ * 
+ * MNAME           The <domain-name> of the name server that was the
+ *                 original or primary source of data for this zone.
+ * 
+ * RNAME           A <domain-name> which specifies the mailbox of the
+ *                 person responsible for this zone.
+ * 
+ * SERIAL          The unsigned 32 bit version number of the original copy
+ *                 of the zone.  Zone transfers preserve this value.  This
+ *                 value wraps and should be compared using sequence space
+ *                 arithmetic.
+ * 
+ * REFRESH         A 32 bit time interval before the zone should be
+ *                 refreshed.
+ * 
+ * RETRY           A 32 bit time interval that should elapse before a
+ *                 failed refresh should be retried.
+ * 
+ * EXPIRE          A 32 bit time value that specifies the upper limit on
+ *                 the time interval that can elapse before the zone is no
+ *                 longer authoritative.
+ * 
+ * MINIMUM         The unsigned 32 bit minimum TTL field that should be
+ *                 exported with any RR from this zone.
+ * 
+ * SOA records cause no additional section processing.
+ * 
+ * All times are in units of seconds.
+ * 
+ * Most of these fields are pertinent only for name server maintenance
+ * operations.  However, MINIMUM is used in all query operations that
+ * retrieve RRs from a zone.  Whenever a RR is sent in a response to a
+ * query, the TTL field is set to the maximum of the TTL field from the RR
+ * and the MINIMUM field in the appropriate SOA.  Thus MINIMUM is a lower
+ * bound on the TTL field for all RRs in a zone.  Note that this use of
+ * MINIMUM should occur when the RRs are copied into the response and not
+ * when the zone is loaded from a master file or via a zone transfer.  The
+ * reason for this provison is to allow future dynamic update facilities to
+ * change the SOA RR with known semantics.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class StartOfAuthorityRecordEncoder extends ResourceRecordEncoder
+{
+    protected void putResourceRecordData( ByteBuffer byteBuffer, ResourceRecord record )
+    {
+        String mName = record.get( DnsAttribute.SOA_M_NAME );
+        String rName = record.get( DnsAttribute.SOA_R_NAME );
+        long serial = Long.parseLong( record.get( DnsAttribute.SOA_SERIAL ) );
+        int refresh = Integer.parseInt( record.get( DnsAttribute.SOA_REFRESH ) );
+        int retry = Integer.parseInt( record.get( DnsAttribute.SOA_RETRY ) );
+        int expire = Integer.parseInt( record.get( DnsAttribute.SOA_EXPIRE ) );
+        long minimum = Long.parseLong( record.get( DnsAttribute.SOA_MINIMUM ) );
+
+        putDomainName( byteBuffer, mName );
+        putDomainName( byteBuffer, rName );
+
+        byteBuffer.putInt( ( int ) serial );
+
+        byteBuffer.putInt( refresh );
+        byteBuffer.putInt( retry );
+        byteBuffer.putInt( expire );
+
+        byteBuffer.putInt( ( int ) minimum );
+    }
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/TextRecordEncoder.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/TextRecordEncoder.java
new file mode 100644
index 0000000..ad18c34
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/TextRecordEncoder.java
@@ -0,0 +1,52 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.io.encoder;
+
+
+import org.apache.directory.server.dns.messages.ResourceRecord;
+import org.apache.directory.server.dns.store.DnsAttribute;
+import org.apache.mina.common.ByteBuffer;
+
+
+/**
+ * 3.3.14. TXT RDATA format
+ * 
+ *     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *     /                   TXT-DATA                    /
+ *     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ * 
+ * where:
+ * 
+ * TXT-DATA        One or more <character-string>s.
+ * 
+ * TXT RRs are used to hold descriptive text.  The semantics of the text
+ * depends on the domain where it is found.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class TextRecordEncoder extends ResourceRecordEncoder
+{
+    protected void putResourceRecordData( ByteBuffer byteBuffer, ResourceRecord record )
+    {
+        putCharacterString( byteBuffer, record.get( DnsAttribute.CHARACTER_STRING ) );
+    }
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/WellKnownServicesRecordEncoder.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/WellKnownServicesRecordEncoder.java
new file mode 100755
index 0000000..5dc3e57
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/WellKnownServicesRecordEncoder.java
@@ -0,0 +1,73 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.io.encoder;
+
+
+/**
+ * 3.4.2. WKS RDATA format
+ * 
+ *     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *     |                    ADDRESS                    |
+ *     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *     |       PROTOCOL        |                       |
+ *     +--+--+--+--+--+--+--+--+                       |
+ *     |                                               |
+ *     /                   <BIT MAP>                   /
+ *     /                                               /
+ *     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ * 
+ * where:
+ * 
+ * ADDRESS         An 32 bit Internet address
+ * 
+ * PROTOCOL        An 8 bit IP protocol number
+ * 
+ * <BIT MAP>       A variable length bit map.  The bit map must be a
+ *                 multiple of 8 bits long.
+ * 
+ * The WKS record is used to describe the well known services supported by
+ * a particular protocol on a particular internet address.  The PROTOCOL
+ * field specifies an IP protocol number, and the bit map has one bit per
+ * port of the specified protocol.  The first bit corresponds to port 0,
+ * the second to port 1, etc.  If the bit map does not include a bit for a
+ * protocol of interest, that bit is assumed zero.  The appropriate values
+ * and mnemonics for ports and protocols are specified in [RFC-1010].
+ * 
+ * For example, if PROTOCOL=TCP (6), the 26th bit corresponds to TCP port
+ * 25 (SMTP).  If this bit is set, a SMTP server should be listening on TCP
+ * port 25; if zero, SMTP service is not supported on the specified
+ * address.
+ * 
+ * The purpose of WKS RRs is to provide availability information for
+ * servers for TCP and UDP.  If a server supports both TCP and UDP, or has
+ * multiple Internet addresses, then multiple WKS RRs are used.
+ * 
+ * WKS RRs cause no additional section processing.
+ * 
+ * In master files, both ports and protocols are expressed using mnemonics
+ * or decimal numbers.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class WellKnownServicesRecordEncoder
+{
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/package-info.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/package-info.java
new file mode 100644
index 0000000..ff87ebf
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/io/encoder/package-info.java
@@ -0,0 +1,28 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+/**
+ * Provides the encoders for DNS messages and resource records.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+
+package org.apache.directory.server.dns.io.encoder;
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/messages/DnsMessage.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/messages/DnsMessage.java
new file mode 100755
index 0000000..b2c3926
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/messages/DnsMessage.java
@@ -0,0 +1,296 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.messages;
+
+
+import java.util.List;
+
+import org.apache.commons.lang.builder.EqualsBuilder;
+import org.apache.commons.lang.builder.HashCodeBuilder;
+import org.apache.commons.lang.builder.ToStringBuilder;
+
+
+/**
+ * All communications inside of the domain protocol are carried in a single
+ * format called a message.  The top level format of message is divided
+ * into 5 sections (some of which are empty in certain cases) shown below:
+ *
+ *     +---------------------+
+ *     |        Header       |
+ *     +---------------------+
+ *     |       Question      | the question for the name server
+ *     +---------------------+
+ *     |        Answer       | ResourceRecords answering the question
+ *     +---------------------+
+ *     |      Authority      | ResourceRecords pointing toward an authority
+ *     +---------------------+
+ *     |      Additional     | ResourceRecords holding additional information
+ *     +---------------------+
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class DnsMessage
+{
+    /**
+     * The header section is always present.  The header includes fields that
+     * specify which of the remaining sections are present, and also specify
+     * whether the message is a query or a response, a standard query or some
+     * other opcode, etc.
+     */
+    private int transactionId;
+    private MessageType messageType;
+    private OpCode opCode;
+    private boolean authoritativeAnswer;
+    private boolean truncated;
+    private boolean recursionDesired;
+    private boolean recursionAvailable;
+    private boolean reserved;
+    private boolean acceptNonAuthenticatedData;
+
+    private ResponseCode responseCode;
+
+    private List<QuestionRecord> questionRecords;
+    private List<ResourceRecord> answerRecords;
+    private List<ResourceRecord> authorityRecords;
+    private List<ResourceRecord> additionalRecords;
+
+
+    /**
+     * Creates a new instance of DnsMessage.
+     *
+     * @param transactionId
+     * @param messageType
+     * @param opCode
+     * @param authoritativeAnswer
+     * @param truncated
+     * @param recursionDesired
+     * @param recursionAvailable
+     * @param reserved
+     * @param acceptNonAuthenticatedData
+     * @param responseCode
+     * @param question
+     * @param answer
+     * @param authority
+     * @param additional
+     */
+    public DnsMessage( int transactionId, MessageType messageType, OpCode opCode, boolean authoritativeAnswer,
+        boolean truncated, boolean recursionDesired, boolean recursionAvailable, boolean reserved,
+        boolean acceptNonAuthenticatedData, ResponseCode responseCode, List<QuestionRecord> question,
+        List<ResourceRecord> answer, List<ResourceRecord> authority, List<ResourceRecord> additional )
+    {
+        this.transactionId = transactionId;
+        this.messageType = messageType;
+        this.opCode = opCode;
+        this.authoritativeAnswer = authoritativeAnswer;
+        this.truncated = truncated;
+        this.recursionDesired = recursionDesired;
+        this.recursionAvailable = recursionAvailable;
+        this.reserved = reserved;
+        this.acceptNonAuthenticatedData = acceptNonAuthenticatedData;
+        this.responseCode = responseCode;
+        this.questionRecords = question;
+        this.answerRecords = answer;
+        this.authorityRecords = authority;
+        this.additionalRecords = additional;
+    }
+
+
+    /**
+     * @return Returns the acceptNonAuthenticatedData.
+     */
+    public boolean isAcceptNonAuthenticatedData()
+    {
+        return acceptNonAuthenticatedData;
+    }
+
+
+    /**
+     * @return Returns the additional.
+     */
+    public List<ResourceRecord> getAdditionalRecords()
+    {
+        return additionalRecords;
+    }
+
+
+    /**
+     * @return Returns the answers.
+     */
+    public List<ResourceRecord> getAnswerRecords()
+    {
+        return answerRecords;
+    }
+
+
+    /**
+     * @return Returns the authoritativeAnswer.
+     */
+    public boolean isAuthoritativeAnswer()
+    {
+        return authoritativeAnswer;
+    }
+
+
+    /**
+     * @return Returns the authority.
+     */
+    public List<ResourceRecord> getAuthorityRecords()
+    {
+        return authorityRecords;
+    }
+
+
+    /**
+     * @return Returns the messageType.
+     */
+    public MessageType getMessageType()
+    {
+        return messageType;
+    }
+
+
+    /**
+     * @return Returns the opCode.
+     */
+    public OpCode getOpCode()
+    {
+        return opCode;
+    }
+
+
+    /**
+     * @return Returns the question.
+     */
+    public List<QuestionRecord> getQuestionRecords()
+    {
+        return questionRecords;
+    }
+
+
+    /**
+     * @return Returns the recursionAvailable.
+     */
+    public boolean isRecursionAvailable()
+    {
+        return recursionAvailable;
+    }
+
+
+    /**
+     * @return Returns the recursionDesired.
+     */
+    public boolean isRecursionDesired()
+    {
+        return recursionDesired;
+    }
+
+
+    /**
+     * @return Returns the reserved.
+     */
+    public boolean isReserved()
+    {
+        return reserved;
+    }
+
+
+    /**
+     * @return Returns the responseCode.
+     */
+    public ResponseCode getResponseCode()
+    {
+        return responseCode;
+    }
+
+
+    /**
+     * @return Returns the transactionId.
+     */
+    public int getTransactionId()
+    {
+        return transactionId;
+    }
+
+
+    /**
+     * @return Returns the truncated.
+     */
+    public boolean isTruncated()
+    {
+        return truncated;
+    }
+
+
+    /**
+     * @see java.lang.Object#equals(Object)
+     */
+    public boolean equals( Object object )
+    {
+        if ( object == this )
+        {
+            return true;
+        }
+        if ( !( object instanceof DnsMessage ) )
+        {
+            return false;
+        }
+        DnsMessage rhs = ( DnsMessage ) object;
+        return new EqualsBuilder().append( this.transactionId, rhs.transactionId ).append( this.answerRecords,
+            rhs.answerRecords ).append( this.opCode, rhs.opCode ).append( this.recursionAvailable,
+            rhs.recursionAvailable ).append( this.messageType, rhs.messageType ).append( this.additionalRecords,
+            rhs.additionalRecords ).append( this.truncated, rhs.truncated ).append( this.recursionDesired,
+            rhs.recursionDesired ).append( this.responseCode, rhs.responseCode ).append( this.authorityRecords,
+            rhs.authorityRecords ).append( this.authoritativeAnswer, rhs.authoritativeAnswer ).append( this.reserved,
+            rhs.reserved ).append( this.acceptNonAuthenticatedData, rhs.acceptNonAuthenticatedData ).append(
+            this.questionRecords, rhs.questionRecords ).isEquals();
+    }
+
+
+    /**
+     * @see java.lang.Object#hashCode()
+     * @return the instance's hash code 
+     */
+    public int hashCode()
+    {
+        return new HashCodeBuilder( -1805208585, -276770303 ).append( this.transactionId ).append( this.answerRecords )
+            .append( this.opCode ).append( this.recursionAvailable ).append( this.messageType ).append(
+                this.additionalRecords ).append( this.truncated ).append( this.recursionDesired ).append(
+                this.responseCode ).append( this.authorityRecords ).append( this.authoritativeAnswer ).append(
+                this.reserved ).append( this.acceptNonAuthenticatedData ).append( this.questionRecords ).toHashCode();
+    }
+
+
+    /**
+     * @see java.lang.Object#toString()
+     */
+    public String toString()
+    {
+        return new ToStringBuilder( this ).appendSuper( super.toString() ).append( "transactionId", this.transactionId )
+            .append( "opCode", this.opCode ).append( "truncated", this.truncated ).append( "messageType",
+                this.messageType ).append( "recursionDesired", this.recursionDesired ).append( "additionalRecords",
+                this.additionalRecords ).append( "responseCode", this.responseCode ).append( "authorityRecords",
+                this.authorityRecords ).append( "acceptNonAuthenticatedData", this.acceptNonAuthenticatedData ).append(
+                "recursionAvailable", this.recursionAvailable ).append( "answerRecords", this.answerRecords ).append(
+                "questionRecords", this.questionRecords ).append( "authoritativeAnswer", this.authoritativeAnswer )
+            .append( "reserved", this.reserved ).toString();
+    }
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/messages/DnsMessageModifier.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/messages/DnsMessageModifier.java
new file mode 100644
index 0000000..c1b63bb
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/messages/DnsMessageModifier.java
@@ -0,0 +1,210 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.messages;
+
+
+import java.util.List;
+
+
+/**
+ * All communications inside of the domain protocol are carried in a single
+ * format called a message.  The top level format of message is divided
+ * into 5 sections (some of which are empty in certain cases) shown below:
+ *
+ *     +---------------------+
+ *     |        Header       |
+ *     +---------------------+
+ *     |       Question      | the question for the name server
+ *     +---------------------+
+ *     |        Answer       | ResourceRecords answering the question
+ *     +---------------------+
+ *     |      Authority      | ResourceRecords pointing toward an authority
+ *     +---------------------+
+ *     |      Additional     | ResourceRecords holding additional information
+ *     +---------------------+
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class DnsMessageModifier
+{
+    /**
+     * The header section is always present.  The header includes fields that
+     * specify which of the remaining sections are present, and also specify
+     * whether the message is a query or a response, a standard query or some
+     * other opcode, etc.
+     */
+    private int transactionId;
+    private MessageType messageType;
+    private OpCode opCode;
+    private boolean authoritativeAnswer;
+    private boolean truncated;
+    private boolean recursionDesired;
+    private boolean recursionAvailable;
+    private boolean reserved;
+    private boolean acceptNonAuthenticatedData;
+
+    private ResponseCode responseCode;
+
+    private List<QuestionRecord> questionRecords;
+    private List<ResourceRecord> answerRecords;
+    private List<ResourceRecord> authorityRecords;
+    private List<ResourceRecord> additionalRecords;
+
+
+    /**
+     * Returns the {@link DnsMessage}.
+     *
+     * @return The {@link DnsMessage}.
+     */
+    public DnsMessage getDnsMessage()
+    {
+        return new DnsMessage( transactionId, messageType, opCode, authoritativeAnswer, truncated, recursionDesired,
+            recursionAvailable, reserved, acceptNonAuthenticatedData, responseCode, questionRecords, answerRecords,
+            authorityRecords, additionalRecords );
+    }
+
+
+    /**
+     * @param acceptNonAuthenticatedData The acceptNonAuthenticatedData to set.
+     */
+    public void setAcceptNonAuthenticatedData( boolean acceptNonAuthenticatedData )
+    {
+        this.acceptNonAuthenticatedData = acceptNonAuthenticatedData;
+    }
+
+
+    /**
+     * @param additionalRecords The additional to set.
+     */
+    public void setAdditionalRecords( List<ResourceRecord> additionalRecords )
+    {
+        this.additionalRecords = additionalRecords;
+    }
+
+
+    /**
+     * @param answerRecords The answer to set.
+     */
+    public void setAnswerRecords( List<ResourceRecord> answerRecords )
+    {
+        this.answerRecords = answerRecords;
+    }
+
+
+    /**
+     * @param authoritativeAnswer The authoritativeAnswer to set.
+     */
+    public void setAuthoritativeAnswer( boolean authoritativeAnswer )
+    {
+        this.authoritativeAnswer = authoritativeAnswer;
+    }
+
+
+    /**
+     * @param authorityRecords The authority to set.
+     */
+    public void setAuthorityRecords( List<ResourceRecord> authorityRecords )
+    {
+        this.authorityRecords = authorityRecords;
+    }
+
+
+    /**
+     * @param messageType The messageType to set.
+     */
+    public void setMessageType( MessageType messageType )
+    {
+        this.messageType = messageType;
+    }
+
+
+    /**
+     * @param opCode The opCode to set.
+     */
+    public void setOpCode( OpCode opCode )
+    {
+        this.opCode = opCode;
+    }
+
+
+    /**
+     * @param questionRecords The question to set.
+     */
+    public void setQuestionRecords( List<QuestionRecord> questionRecords )
+    {
+        this.questionRecords = questionRecords;
+    }
+
+
+    /**
+     * @param recursionAvailable The recursionAvailable to set.
+     */
+    public void setRecursionAvailable( boolean recursionAvailable )
+    {
+        this.recursionAvailable = recursionAvailable;
+    }
+
+
+    /**
+     * @param recursionDesired The recursionDesired to set.
+     */
+    public void setRecursionDesired( boolean recursionDesired )
+    {
+        this.recursionDesired = recursionDesired;
+    }
+
+
+    /**
+     * @param reserved The reserved to set.
+     */
+    public void setReserved( boolean reserved )
+    {
+        this.reserved = reserved;
+    }
+
+
+    /**
+     * @param responseCode The responseCode to set.
+     */
+    public void setResponseCode( ResponseCode responseCode )
+    {
+        this.responseCode = responseCode;
+    }
+
+
+    /**
+     * @param transactionId The transactionId to set.
+     */
+    public void setTransactionId( int transactionId )
+    {
+        this.transactionId = transactionId;
+    }
+
+
+    /**
+     * @param truncated The truncated to set.
+     */
+    public void setTruncated( boolean truncated )
+    {
+        this.truncated = truncated;
+    }
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/messages/MessageType.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/messages/MessageType.java
new file mode 100644
index 0000000..f020477
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/messages/MessageType.java
@@ -0,0 +1,67 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.messages;
+
+
+import org.apache.directory.server.dns.util.EnumConverter;
+import org.apache.directory.server.dns.util.ReverseEnumMap;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public enum MessageType implements EnumConverter<Byte>
+{
+    /** A query message. */
+    QUERY(0),
+
+    /** A response message. */
+    RESPONSE(1);
+
+    private static ReverseEnumMap<Byte, MessageType> map = new ReverseEnumMap<Byte, MessageType>( MessageType.class );
+
+    private final byte value;
+
+
+    private MessageType( int value )
+    {
+        this.value = ( byte ) value;
+    }
+
+
+    public Byte convert()
+    {
+        return this.value;
+    }
+
+
+    /**
+     * Converts an ordinal value into a {@link MessageType}.
+     *
+     * @param value
+     * @return The {@link MessageType}.
+     */
+    public static MessageType convert( byte value )
+    {
+        return map.get( value );
+    }
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/messages/OpCode.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/messages/OpCode.java
new file mode 100755
index 0000000..aa983f1
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/messages/OpCode.java
@@ -0,0 +1,76 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.messages;
+
+
+import org.apache.directory.server.dns.util.EnumConverter;
+import org.apache.directory.server.dns.util.ReverseEnumMap;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public enum OpCode implements EnumConverter<Byte>
+{
+    /** Standard query */
+    QUERY(0),
+
+    /** Inverse query */
+    IQUERY(1),
+
+    /** Server status request */
+    STATUS(2),
+
+    /** Zone transfer notification */
+    NOTIFY(4),
+
+    /** Dynamic update message */
+    UPDATE(5);
+
+    private static ReverseEnumMap<Byte, OpCode> map = new ReverseEnumMap<Byte, OpCode>( OpCode.class );
+
+    private final byte value;
+
+
+    private OpCode( int value )
+    {
+        this.value = ( byte ) value;
+    }
+
+
+    public Byte convert()
+    {
+        return this.value;
+    }
+
+
+    /**
+     * Converts an ordinal value into an {@link OpCode}.
+     *
+     * @param value
+     * @return The {@link OpCode}.
+     */
+    public static OpCode convert( byte value )
+    {
+        return map.get( value );
+    }
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/messages/ProtocolType.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/messages/ProtocolType.java
new file mode 100644
index 0000000..697b532
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/messages/ProtocolType.java
@@ -0,0 +1,190 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.messages;
+
+
+import org.apache.directory.server.dns.util.EnumConverter;
+import org.apache.directory.server.dns.util.ReverseEnumMap;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public enum ProtocolType implements EnumConverter<Byte>
+{
+    /** Null */
+    NULL(0),
+
+    /** Internet Conrol Message */
+    ICMP(1),
+
+    /** Internet Group Management */
+    IGMP(2),
+
+    /** Gateway-to-Gateway */
+    GGP(3),
+
+    /** Stream */
+    ST(5),
+
+    /** Transmission control */
+    TCP(6),
+
+    /** UCL */
+    UCL(7),
+
+    /** Exterior Gateway Protocol */
+    EGP(8),
+
+    /** any private interior gateway */
+    IGP(9),
+
+    /** BBN RCC Monitoring */
+    BBN_RCC_MON(10),
+
+    /** Network Voice Protocol */
+    NVP_II(11),
+
+    /** PUP */
+    PUP(12),
+
+    /** ARGUS */
+    ARGUS(13),
+
+    /** EMCON */
+    EMCON(14),
+
+    /** Cross Net Debugger */
+    XNET(15),
+
+    /** Chaos */
+    CHAOS(16),
+
+    /** User Datagram */
+    UDP(17),
+
+    /** Multiplexing */
+    MUX(18),
+
+    /** DCN Measurement Subsystems */
+    DCN_MEAS(19),
+
+    /** Host Monitoring */
+    HMP(20),
+
+    /** Packet Radio Measurement */
+    PRM(21),
+
+    /** XEROX NS IDP */
+    XNS_IDP(22),
+
+    /** Trunk-1 */
+    TRUNK_1(23),
+
+    /** Trunk-2 */
+    TRUNK_2(24),
+
+    /** Leaf-1 */
+    LEAF_1(25),
+
+    /** Leaf-2 */
+    LEAF_2(26),
+
+    /** Reliable Data Protocol */
+    RDP(27),
+
+    /** Internet Reliable Transaction */
+    IRTP(28),
+
+    /** ISO Transport Protocol Class 4 */
+    ISO_TP4(29),
+
+    /** Bulk Data Transfer Protocol */
+    NETBLT(30),
+
+    /** MFE Network Services Protocol */
+    MFE_NSP(31),
+
+    /** MERIT Internodal Protocol */
+    MERIT_INP(32),
+
+    /** Sequential Exchange Protocol */
+    SEP(33),
+
+    /** CFTP */
+    CFTP(62),
+
+    /** SATNET and Backroom EXPAK */
+    SAT_EXPAK(64),
+
+    /** MIT Subnet Support */
+    MIT_SUBNET(65),
+
+    /** MIT Remote Virtual Disk Protocol */
+    RVD(66),
+
+    /** Internet Pluribus Packet Core */
+    IPPC(67),
+
+    /** SATNET Monitoring */
+    SAT_MON(69),
+
+    /** Internet Packet Core Utility */
+    IPCV(71),
+
+    /** Backroom SETNET Monitoring */
+    BR_SAT_MON(76),
+
+    /** WIDEBAND Monitoring */
+    WB_MON(78),
+
+    /** WIDEBAND EXPAK */
+    WB_EXPAK(79);
+
+    private static ReverseEnumMap<Byte, ProtocolType> map = new ReverseEnumMap<Byte, ProtocolType>( ProtocolType.class );
+
+    private final byte value;
+
+
+    private ProtocolType( int value )
+    {
+        this.value = ( byte ) value;
+    }
+
+
+    public Byte convert()
+    {
+        return this.value;
+    }
+
+
+    /**
+     * Converts an ordinal value into a {@link ProtocolType}.
+     *
+     * @param value
+     * @return The {@link ProtocolType}.
+     */
+    public static ProtocolType convert( byte value )
+    {
+        return map.get( value );
+    }
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/messages/QuestionRecord.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/messages/QuestionRecord.java
new file mode 100644
index 0000000..2dc94cf
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/messages/QuestionRecord.java
@@ -0,0 +1,163 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.messages;
+
+
+import org.apache.commons.lang.builder.EqualsBuilder;
+import org.apache.commons.lang.builder.HashCodeBuilder;
+import org.apache.commons.lang.builder.ToStringBuilder;
+
+
+/**
+ * The question section is used to carry the "question" in most queries,
+ * i.e., the parameters that define what is being asked.  The section
+ * contains QDCOUNT (usually 1) entries, each of the following format:
+ * 
+ *                                     1  1  1  1  1  1
+ *       0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
+ *     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *     |                                               |
+ *     /                     QNAME                     /
+ *     /                                               /
+ *     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *     |                     QTYPE                     |
+ *     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *     |                     QCLASS                    |
+ *     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class QuestionRecord
+{
+    /**
+     * A domain name represented as a sequence of labels, where
+     * each label consists of a length octet followed by that
+     * number of octets.  The domain name terminates with the
+     * zero length octet for the null label of the root.  Note
+     * that this field may be an odd number of octets; no
+     * padding is used.
+     */
+    private String domainName;
+
+    /**
+     * A two octet code which specifies the type.
+     */
+    private RecordType recordType;
+
+    /**
+     * A two octet code that specifies the class.
+     * For example, the CLASS field is IN for the Internet.
+     */
+    private RecordClass recordClass;
+
+
+    /**
+     * Creates a new instance of QuestionRecord.
+     *
+     * @param domainName
+     * @param recordType
+     * @param recordClass
+     */
+    public QuestionRecord( String domainName, RecordType recordType, RecordClass recordClass )
+    {
+        this.domainName = domainName;
+        this.recordType = recordType;
+        this.recordClass = recordClass;
+    }
+
+
+    /**
+     * The domain name of this query.
+     * For example, www.example.com.
+     * 
+     * @return The domain name.
+     */
+    public String getDomainName()
+    {
+        return domainName;
+    }
+
+
+    /**
+     * The type of the query.
+     * For example, the type is A for address records.
+     * 
+     * @return The {@link RecordType}.
+     */
+    public RecordType getRecordType()
+    {
+        return recordType;
+    }
+
+
+    /**
+     * The class for this query.
+     * For example, the class is IN for the Internet.
+     * 
+     * @return The {@link RecordClass}.
+     */
+    public RecordClass getRecordClass()
+    {
+        return recordClass;
+    }
+
+
+    /**
+     * @see java.lang.Object#equals(Object)
+     */
+    public boolean equals( Object object )
+    {
+        if ( object == this )
+        {
+            return true;
+        }
+        if ( !( object instanceof QuestionRecord ) )
+        {
+            return false;
+        }
+        QuestionRecord rhs = ( QuestionRecord ) object;
+        return new EqualsBuilder().append( this.domainName, rhs.domainName ).append( this.recordClass, rhs.recordClass )
+            .append( this.recordType, rhs.recordType ).isEquals();
+    }
+
+
+    /**
+     * @see java.lang.Object#hashCode()
+     * @return the instance's hash code 
+     */
+    public int hashCode()
+    {
+        return new HashCodeBuilder( 1493545107, 315848479 ).append( this.domainName ).append( this.recordClass )
+            .append( this.recordType ).toHashCode();
+    }
+
+
+    /**
+     * @see java.lang.Object#toString()
+     */
+    public String toString()
+    {
+        return new ToStringBuilder( this ).appendSuper( super.toString() ).append( "domainName", this.domainName )
+            .append( "recordClass", this.recordClass ).append( "recordType", this.recordType ).toString();
+    }
+
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/messages/RecordClass.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/messages/RecordClass.java
new file mode 100644
index 0000000..94ddc24
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/messages/RecordClass.java
@@ -0,0 +1,79 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.messages;
+
+
+import org.apache.directory.server.dns.util.EnumConverter;
+import org.apache.directory.server.dns.util.ReverseEnumMap;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public enum RecordClass implements EnumConverter<Short>
+{
+    /** Internet */
+    IN(1),
+
+    /** CSNET class */
+    CS(2),
+
+    /** CHAOS class */
+    CH(3),
+
+    /** Hesiod [Dyer 87] */
+    HS(4),
+
+    /** Special value used in dynamic update messages */
+    NONE(254),
+
+    /** Any class */
+    ANY(255);
+
+    private static ReverseEnumMap<Short, RecordClass> map = new ReverseEnumMap<Short, RecordClass>( RecordClass.class );
+
+    private final short value;
+
+
+    private RecordClass( int value )
+    {
+        this.value = ( short ) value;
+    }
+
+
+    public Short convert()
+    {
+        return this.value;
+    }
+
+
+    /**
+     * Converts an ordinal value into a {@link RecordClass}.
+     *
+     * @param value
+     * @return The {@link RecordClass}.
+     */
+    public static RecordClass convert( short value )
+    {
+        return map.get( value );
+    }
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/messages/RecordType.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/messages/RecordType.java
new file mode 100755
index 0000000..a126bd6
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/messages/RecordType.java
@@ -0,0 +1,245 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.messages;
+
+
+import org.apache.directory.server.dns.util.EnumConverter;
+import org.apache.directory.server.dns.util.ReverseEnumMap;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public enum RecordType implements EnumConverter<Short>
+{
+    /** Host address */
+    A(1),
+
+    /** Authoritative name server */
+    NS(2),
+
+    /** Mail destination */
+    MD(3),
+
+    /** Mail forwarder */
+    MF(4),
+
+    /** Canonical name for an alias */
+    CNAME(5),
+
+    /** Start of a zone of authority */
+    SOA(6),
+
+    /** Mailbox domain name */
+    MB(7),
+
+    /** Mail group member */
+    MG(8),
+
+    /** Mail rename domain name */
+    MR(9),
+
+    /** Null resource record */
+    NULL(10),
+
+    /** Well know service description */
+    WKS(11),
+
+    /** Domain name pointer */
+    PTR(12),
+
+    /** Host information */
+    HINFO(13),
+
+    /** Mailbox or mail list information */
+    MINFO(14),
+
+    /** Mail exchange */
+    MX(15),
+
+    /** Text strings */
+    TXT(16),
+
+    /** Responsible person */
+    RP(17),
+
+    /** AFS cell database */
+    AFSDB(18),
+
+    /** X.25 calling address */
+    X25(19),
+
+    /** ISDN calling address */
+    ISDN(20),
+
+    /** Router */
+    RT(21),
+
+    /** NSAP address */
+    NSAP(22),
+
+    /** Reverse NSAP address (deprecated) */
+    NSAP_PTR(23),
+
+    /** Signature */
+    SIG(24),
+
+    /** Key */
+    KEY(25),
+
+    /** X.400 mail mapping */
+    PX(26),
+
+    /** Geographical position (withdrawn) */
+    GPOS(27),
+
+    /** IPv6 address */
+    AAAA(28),
+
+    /** Location */
+    LOC(29),
+
+    /** Next valid name in zone */
+    NXT(30),
+
+    /** Endpoint identifier */
+    EID(31),
+
+    /** Nimrod locator */
+    NIMLOC(32),
+
+    /** Server selection */
+    SRV(33),
+
+    /** ATM address */
+    ATMA(34),
+
+    /** Naming authority pointer */
+    NAPTR(35),
+
+    /** Key exchange */
+    KX(36),
+
+    /** Certificate */
+    CERT(34),
+
+    /** IPv6 address (experimental) */
+    A6(38),
+
+    /** Non-terminal name redirection */
+    DNAME(39),
+
+    /** Options - contains EDNS metadata */
+    OPT(41),
+
+    /** Address Prefix List */
+    APL(42),
+
+    /** Delegation Signer */
+    DS(43),
+
+    /** SSH Key Fingerprint */
+    SSHFP(44),
+
+    /** Resource Record Signature */
+    RRSIG(46),
+
+    /** Next Secure Name */
+    NSEC(47),
+
+    /** DNSSEC Key */
+    DNSKEY(48),
+
+    /** Transaction key - used to compute a shared secret or exchange a key */
+    TKEY(249),
+
+    /** Transaction signature */
+    TSIG(250),
+
+    /** Incremental zone transfer */
+    IXFR(251),
+
+    /** Request for transfer of an entire zone */
+    AXFR(252),
+
+    /** Request for mailbox-related records */
+    MAILB(253),
+
+    /** Request for mail agent resource records */
+    MAILA(254),
+
+    /** Request for all records */
+    ANY(255);
+
+    private static ReverseEnumMap<Short, RecordType> map = new ReverseEnumMap<Short, RecordType>( RecordType.class );
+
+    private final short value;
+
+
+    private RecordType( int value )
+    {
+        this.value = ( short ) value;
+    }
+
+
+    public Short convert()
+    {
+        return this.value;
+    }
+
+
+    /**
+     * Converts an ordinal value into a {@link RecordType}.
+     *
+     * @param value
+     * @return The {@link RecordType}.
+     */
+    public static RecordType convert( short value )
+    {
+        return map.get( value );
+    }
+
+
+    /**
+     * Returns whether a given {@link RecordType} is a {@link ResourceRecord}.
+     *
+     * @param resourceType
+     * @return true of the {@link RecordType} is a {@link ResourceRecord}.
+     */
+    public static boolean isResourceRecord( RecordType resourceType )
+    {
+        switch ( resourceType )
+        {
+            case OPT:
+            case TKEY:
+            case TSIG:
+            case IXFR:
+            case AXFR:
+            case MAILB:
+            case MAILA:
+            case ANY:
+                return false;
+            default:
+                return true;
+        }
+    }
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/messages/ResourceRecord.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/messages/ResourceRecord.java
new file mode 100755
index 0000000..c296841
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/messages/ResourceRecord.java
@@ -0,0 +1,59 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.messages;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public interface ResourceRecord
+{
+    /**
+     * @return Returns the domainName.
+     */
+    public String getDomainName();
+
+
+    /**
+     * @return Returns the recordType.
+     */
+    public RecordType getRecordType();
+
+
+    /**
+     * @return Returns the recordClass.
+     */
+    public RecordClass getRecordClass();
+
+
+    /**
+     * @return Returns the timeToLive.
+     */
+    public int getTimeToLive();
+
+
+    /**
+     * @param id 
+     * @return Returns the value for an id.
+     */
+    public String get( String id );
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/messages/ResourceRecordImpl.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/messages/ResourceRecordImpl.java
new file mode 100755
index 0000000..772760d
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/messages/ResourceRecordImpl.java
@@ -0,0 +1,194 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.messages;
+
+
+import java.util.Map;
+
+
+/**
+ * The answer, authority, and additional sections all share the same
+ * format: a variable number of resource records, where the number of
+ * records is specified in the corresponding count field in the header.
+ * Each resource record has the following format:
+ *                                     1  1  1  1  1  1
+ *       0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
+ *     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *     |                                               |
+ *     /                                               /
+ *     /                      NAME                     /
+ *     |                                               |
+ *     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *     |                      TYPE                     |
+ *     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *     |                     CLASS                     |
+ *     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *     |                      TTL                      |
+ *     |                                               |
+ *     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *     |                   RDLENGTH                    |
+ *     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
+ *     /                     RDATA                     /
+ *     /                                               /
+ *     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ResourceRecordImpl implements ResourceRecord
+{
+    /**
+     * An owner name, i.e., the name of the node to which this
+     * resource record pertains.
+     */
+    private String domainName;
+
+    /**
+     * Two octets containing one of the resource record TYPE codes.
+     */
+    private RecordType recordType;
+
+    /**
+     * Two octets containing one of the resource record CLASS codes.
+     * For example, the CLASS field is IN for the Internet.
+     */
+    private RecordClass recordClass;
+
+    /**
+     * A 32 bit signed integer that specifies the time interval
+     * that the resource record may be cached before the source
+     * of the information should again be consulted.  Zero
+     * values are interpreted to mean that the resource record can only be
+     * used for the transaction in progress, and should not be
+     * cached.  For example, SOA records are always distributed
+     * with a zero TTL to prohibit caching.  Zero values can
+     * also be used for extremely volatile data.
+     */
+    private int timeToLive;
+
+    /**
+     * A variable length string of octets that describes the
+     * resource.  The format of this information varies
+     * according to the TYPE and CLASS of the resource record.
+     */
+    private Map<String, Object> attributes;
+
+
+    /**
+     * Creates a new instance of ResourceRecordImpl.
+     *
+     * @param domainName
+     * @param recordType
+     * @param recordClass
+     * @param timeToLive
+     * @param attributes
+     */
+    public ResourceRecordImpl( String domainName, RecordType recordType, RecordClass recordClass, int timeToLive,
+        Map<String, Object> attributes )
+    {
+        this.domainName = domainName;
+        this.recordType = recordType;
+        this.recordClass = recordClass;
+        this.timeToLive = timeToLive;
+        this.attributes = attributes;
+    }
+
+
+    /**
+     * @return Returns the domainName.
+     */
+    public String getDomainName()
+    {
+        return domainName;
+    }
+
+
+    /**
+     * @return Returns the recordType.
+     */
+    public RecordType getRecordType()
+    {
+        return recordType;
+    }
+
+
+    /**
+     * @return Returns the recordClass.
+     */
+    public RecordClass getRecordClass()
+    {
+        return recordClass;
+    }
+
+
+    /**
+     * @return Returns the timeToLive.
+     */
+    public int getTimeToLive()
+    {
+        return timeToLive;
+    }
+
+
+    /**
+     * @return Returns the value for the id.
+     */
+    public String get( String id )
+    {
+        return ( String ) attributes.get( id.toLowerCase() );
+    }
+
+
+    public boolean equals( Object o )
+    {
+        if ( this == o )
+        {
+            return true;
+        }
+
+        if ( !( o instanceof ResourceRecord ) )
+        {
+            return false;
+        }
+
+        ResourceRecordImpl that = ( ResourceRecordImpl ) o;
+
+        return ( this.domainName.equalsIgnoreCase( that.domainName ) ) && ( this.recordType == that.recordType )
+            && ( this.recordClass == that.recordClass );
+    }
+
+
+    /**
+     * Compute the instance hash code
+     * @return the instance's hash code 
+     */
+    public int hashCode()
+    {
+        return domainName.hashCode() + recordType.hashCode() + recordClass.hashCode();
+    }
+
+
+    public String toString()
+    {
+        return getClass().getName() + " [ " + domainName + " ( " + recordType + " " + recordClass + " " + timeToLive
+            + " " + attributes + " ) ]";
+    }
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/messages/ResourceRecordModifier.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/messages/ResourceRecordModifier.java
new file mode 100644
index 0000000..154d11c
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/messages/ResourceRecordModifier.java
@@ -0,0 +1,96 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.dns.messages;
+
+
+import java.util.HashMap;
+import java.util.Map;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ResourceRecordModifier
+{
+    private String dnsName;
+    private RecordType dnsType;
+    private RecordClass dnsClass;
+    private int dnsTtl;
+
+    private Map<String, Object> attributes = new HashMap<String, Object>();
+
+
+    /**
+     * Returns the {@link ResourceRecord} built by this {@link ResourceRecordModifier}.
+     *
+     * @return The {@link ResourceRecord}.
+     */
+    public ResourceRecord getEntry()
+    {
+        return new ResourceRecordImpl( dnsName, dnsType, dnsClass, dnsTtl, attributes );
+    }
+
+
+    /**
+     * @param dnsName The dnsName to set.
+     */
+    public void setDnsName( String dnsName )
+    {
+        this.dnsName = dnsName;
+    }
+
+
+    /**
+     * @param dnsType The dnsType to set.
+     */
+    public void setDnsType( RecordType dnsType )
+    {
+        this.dnsType = dnsType;
+    }
+
+
+    /**
+     * @param dnsClass The dnsClass to set.
+     */
+    public void setDnsClass( RecordClass dnsClass )
+    {
+        this.dnsClass = dnsClass;
+    }
+
+
+    /**
+     * @param dnsTtl The dnsTtl to set.
+     */
+    public void setDnsTtl( int dnsTtl )
+    {
+        this.dnsTtl = dnsTtl;
+    }
+
+
+    /**
+     * @param id The id to set
+     * @param value The value to set 
+     */
+    public void put( String id, String value )
+    {
+        attributes.put( id.toLowerCase(), value );
+    }
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/messages/ResponseCode.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/messages/ResponseCode.java
new file mode 100755
index 0000000..0615ace
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/messages/ResponseCode.java
@@ -0,0 +1,79 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.messages;
+
+
+import org.apache.directory.server.dns.util.EnumConverter;
+import org.apache.directory.server.dns.util.ReverseEnumMap;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public enum ResponseCode implements EnumConverter<Byte>
+{
+    /** No error condition. */
+    NO_ERROR(0),
+
+    /** The name server was unable to interpret the query. */
+    FORMAT_ERROR(1),
+
+    /** The name server was unable to process this query due to a problem with the name server. */
+    SERVER_FAILURE(2),
+
+    /** The domain name referenced in the query does not exist. */
+    NAME_ERROR(3),
+
+    /** The name server does not support the requested kind of query. */
+    NOT_IMPLEMENTED(4),
+
+    /** The name server refuses to perform the specified operation for policy reasons. */
+    REFUSED(5);
+
+    private static ReverseEnumMap<Byte, ResponseCode> map = new ReverseEnumMap<Byte, ResponseCode>( ResponseCode.class );
+
+    private final byte value;
+
+
+    private ResponseCode( int value )
+    {
+        this.value = ( byte ) value;
+    }
+
+
+    public Byte convert()
+    {
+        return this.value;
+    }
+
+
+    /**
+     * Converts an ordinal value into a {@link ResponseCode}.
+     *
+     * @param value
+     * @return The {@link ResponseCode}.
+     */
+    public static ResponseCode convert( byte value )
+    {
+        return map.get( value );
+    }
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/messages/ServiceType.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/messages/ServiceType.java
new file mode 100644
index 0000000..77161d6
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/messages/ServiceType.java
@@ -0,0 +1,301 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.messages;
+
+
+import org.apache.directory.server.dns.util.EnumConverter;
+import org.apache.directory.server.dns.util.ReverseEnumMap;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public enum ServiceType implements EnumConverter<Byte>
+{
+    /** Null */
+    NULL(0),
+
+    /** Remote job entry */
+    RJE(5),
+
+    /** Echo */
+    ECHO(7),
+
+    /** Discard */
+    DISCARD(9),
+
+    /** Active users */
+    USERS(11),
+
+    /** Daytime */
+    DAYTIME(13),
+
+    /** Quote of the day */
+    QUOTE(17),
+
+    /** Character generator */
+    CHARGEN(19),
+
+    /** File Transfer [Default Data] */
+    FTP_DATA(20),
+
+    /** File Transfer [Control] */
+    FTP(21),
+
+    /** Telnet */
+    TELNET(23),
+
+    /** Simple Mail Transfer */
+    SMTP(25),
+
+    /** NSW User System FE */
+    NSW_FE(27),
+
+    /** MSG ICP */
+    MSG_ICP(29),
+
+    /** MSG Authentication */
+    MSG_AUTH(31),
+
+    /** Display Support Protocol */
+    DSP(33),
+
+    /** Time */
+    TIME(37),
+
+    /** Resource Location Protocol */
+    RLP(39),
+
+    /** Graphics */
+    GRAPHICS(41),
+
+    /** Host Name Server */
+    NAMESERVER(42),
+
+    /** Who Is */
+    NICKNAME(43),
+
+    /** MPM FLAGS Protocol */
+    MPM_FLAGS(44),
+
+    /** Message Processing Module [recv] */
+    MPM(45),
+
+    /** MPM [default send] */
+    MPM_SND(46),
+
+    /** NI FTP */
+    NI_FTP(47),
+
+    /** Login Host Protocol */
+    LOGIN(49),
+
+    /** IMP Logical Address Maintenance */
+    LA_MAINT(51),
+
+    /** Domain Name Server */
+    DOMAIN(53),
+
+    /** ISI Graphics Language */
+    ISI_GL(55),
+
+    /** NI MAIL */
+    NI_MAIL(61),
+
+    /** VIA Systems - FTP */
+    VIA_FTP(63),
+
+    /** TACACS-Database Service */
+    TACACS_DS(65),
+
+    /** Bootstrap Protocol Server */
+    BOOTPS(67),
+
+    /** Bootstrap Protocol Client */
+    BOOTPC(68),
+
+    /** Trivial File Transfer */
+    TFTP(69),
+
+    /** Remote Job Service */
+    NETRJS_1(71),
+
+    /** Remote Job Service */
+    NETRJS_2(72),
+
+    /** Remote Job Service */
+    NETRJS_3(73),
+
+    /** Remote Job Service */
+    NETRJS_4(74),
+
+    /** Finger */
+    FINGER(79),
+
+    /** HOSTS2 Name Server */
+    HOSTS2_NS(81),
+
+    /** SU/MIT Telnet Gateway */
+    SU_MIT_TG(89),
+
+    /** MIT Dover Spooler */
+    MIT_DOV(91),
+
+    /** Device Control Protocol */
+    DCP(93),
+
+    /** SUPDUP */
+    SUPDUP(95),
+
+    /** Swift Remote Virtual File Protocol */
+    SWIFT_RVF(97),
+
+    /** TAC News */
+    TACNEWS(98),
+
+    /** Metagram Relay */
+    METAGRAM(99),
+
+    /** NIC Host Name Server */
+    HOSTNAME(101),
+
+    /** ISO-TSAP */
+    ISO_TSAP(102),
+
+    /** X400 */
+    X400(103),
+
+    /** X400-SND */
+    X400_SND(104),
+
+    /** Mailbox Name Nameserver */
+    CSNET_NS(105),
+
+    /** Remote Telnet Service */
+    RTELNET(107),
+
+    /** Post Office Protocol - Version 2 */
+    POP_2(109),
+
+    /** SUN Remote Procedure Call */
+    SUNRPC(111),
+
+    /** Authentication Service */
+    AUTH(113),
+
+    /** Simple File Transfer Protocol */
+    SFTP(115),
+
+    /** UUCP Path Service */
+    UUCP_PATH(117),
+
+    /** Network News Transfer Protocol */
+    NNTP(119),
+
+    /** HYDRA Expedited Remote Procedure */
+    ERPC(121),
+
+    /** Network Time Protocol */
+    NTP(123),
+
+    /** Locus PC-Interface Net Map Server */
+    LOCUS_MAP(125),
+
+    /** Locus PC-Interface Conn Server */
+    LOCUS_CON(127),
+
+    /** Password Generator Protocol */
+    PWDGEN(129),
+
+    /** CISCO FNATIVE */
+    CISCO_FNA(130),
+
+    /** CISCO TNATIVE */
+    CISCO_TNA(131),
+
+    /** CISCO SYSMAINT */
+    CISCO_SYS(132),
+
+    /** Statistics Service */
+    STATSRV(133),
+
+    /** INGRES-NET Service */
+    INGRES_NET(134),
+
+    /** Location Service */
+    LOC_SRV(135),
+
+    /** PROFILE Naming System */
+    PROFILE(136),
+
+    /** NETBIOS Name Service */
+    NETBIOS_NS(137),
+
+    /** NETBIOS Datagram Service */
+    NETBIOS_DGM(138),
+
+    /** NETBIOS Session Service */
+    NETBIOS_SSN(139),
+
+    /** EMFIS Data Service */
+    EMFIS_DATA(140),
+
+    /** EMFIS Control Service */
+    EMFIS_CNTL(141),
+
+    /** Britton-Lee IDM */
+    BL_IDM(142),
+
+    /** Survey Measurement */
+    SUR_MEAS(243),
+
+    /** LINK */
+    LINK(245);
+
+    private static ReverseEnumMap<Byte, ServiceType> map = new ReverseEnumMap<Byte, ServiceType>( ServiceType.class );
+
+    private final byte value;
+
+
+    private ServiceType( int value )
+    {
+        this.value = ( byte ) value;
+    }
+
+
+    public Byte convert()
+    {
+        return this.value;
+    }
+
+
+    /**
+     * Converts an ordinal value into a {@link ServiceType}.
+     *
+     * @param value
+     * @return The {@link ServiceType}.
+     */
+    public static ServiceType convert( byte value )
+    {
+        return map.get( value );
+    }
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/messages/package-info.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/messages/package-info.java
new file mode 100644
index 0000000..b883ceb
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/messages/package-info.java
@@ -0,0 +1,28 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+/**
+ * Provides message objects for DNS messages and resource records.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+
+package org.apache.directory.server.dns.messages;
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/package-info.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/package-info.java
new file mode 100644
index 0000000..065140b
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/package-info.java
@@ -0,0 +1,31 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+/**
+ * Provides the entry point to an instance of the
+ * {@link org.apache.directory.server.dns.DnsServer},
+ * as well as support for configuration and the root
+ * of the exception hierarchy.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+
+package org.apache.directory.server.dns;
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/protocol/DnsProtocolHandler.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/protocol/DnsProtocolHandler.java
new file mode 100644
index 0000000..8e2f067
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/protocol/DnsProtocolHandler.java
@@ -0,0 +1,173 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.protocol;
+
+
+import java.util.ArrayList;
+
+import org.apache.directory.server.dns.DnsServer;
+import org.apache.directory.server.dns.DnsException;
+import org.apache.directory.server.dns.messages.DnsMessage;
+import org.apache.directory.server.dns.messages.DnsMessageModifier;
+import org.apache.directory.server.dns.messages.MessageType;
+import org.apache.directory.server.dns.messages.OpCode;
+import org.apache.directory.server.dns.messages.ResourceRecord;
+import org.apache.directory.server.dns.messages.ResponseCode;
+import org.apache.directory.server.dns.service.DnsContext;
+import org.apache.directory.server.dns.service.DomainNameService;
+import org.apache.directory.server.dns.store.RecordStore;
+import org.apache.mina.common.IdleStatus;
+import org.apache.mina.common.IoHandler;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.common.TransportType;
+import org.apache.mina.filter.codec.ProtocolCodecFilter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class DnsProtocolHandler implements IoHandler
+{
+    private static final Logger LOG = LoggerFactory.getLogger( DnsProtocolHandler.class );
+
+    private DnsServer config;
+    private RecordStore store;
+    private String contextKey = "context";
+
+
+    /**
+     * Creates a new instance of DnsProtocolHandler.
+     *
+     * @param config
+     * @param store
+     */
+    public DnsProtocolHandler( DnsServer config, RecordStore store )
+    {
+        this.config = config;
+        this.store = store;
+    }
+
+
+    public void sessionCreated( IoSession session ) throws Exception
+    {
+        if ( LOG.isDebugEnabled() )
+        {
+            LOG.debug( "{} CREATED:  {}", session.getRemoteAddress(), session.getTransportType() );
+        }
+
+        if ( session.getTransportType() == TransportType.DATAGRAM )
+        {
+            session.getFilterChain().addFirst( "codec",
+                new ProtocolCodecFilter( DnsProtocolUdpCodecFactory.getInstance() ) );
+        }
+        else
+        {
+            session.getFilterChain().addFirst( "codec",
+                new ProtocolCodecFilter( DnsProtocolTcpCodecFactory.getInstance() ) );
+        }
+    }
+
+
+    public void sessionOpened( IoSession session )
+    {
+        LOG.debug( "{} OPENED", session.getRemoteAddress() );
+    }
+
+
+    public void sessionClosed( IoSession session )
+    {
+        LOG.debug( "{} CLOSED", session.getRemoteAddress() );
+    }
+
+
+    public void sessionIdle( IoSession session, IdleStatus status )
+    {
+        LOG.debug( "{} IDLE ({})", session.getRemoteAddress(), status );
+    }
+
+
+    public void exceptionCaught( IoSession session, Throwable cause )
+    {
+        LOG.error( session.getRemoteAddress() + " EXCEPTION", cause );
+        session.close();
+    }
+
+
+    public void messageReceived( IoSession session, Object message )
+    {
+        LOG.debug( "{} RCVD:  {}", session.getRemoteAddress(), message );
+
+        try
+        {
+            DnsContext dnsContext = new DnsContext();
+            dnsContext.setConfig( config );
+            dnsContext.setStore( store );
+            session.setAttribute( getContextKey(), dnsContext );
+
+            DomainNameService.execute( dnsContext, (DnsMessage)message );
+
+            DnsMessage response = dnsContext.getReply();
+
+            session.write( response );
+        }
+        catch ( Exception e )
+        {
+            LOG.error( e.getMessage(), e );
+
+            DnsMessage request = ( DnsMessage ) message;
+            DnsException de = ( DnsException ) e;
+
+            DnsMessageModifier modifier = new DnsMessageModifier();
+
+            modifier.setTransactionId( request.getTransactionId() );
+            modifier.setMessageType( MessageType.RESPONSE );
+            modifier.setOpCode( OpCode.QUERY );
+            modifier.setAuthoritativeAnswer( false );
+            modifier.setTruncated( false );
+            modifier.setRecursionDesired( request.isRecursionDesired() );
+            modifier.setRecursionAvailable( false );
+            modifier.setReserved( false );
+            modifier.setAcceptNonAuthenticatedData( false );
+            modifier.setResponseCode( ResponseCode.convert( ( byte ) de.getResponseCode() ) );
+            modifier.setQuestionRecords( request.getQuestionRecords() );
+            modifier.setAnswerRecords( new ArrayList<ResourceRecord>() );
+            modifier.setAuthorityRecords( new ArrayList<ResourceRecord>() );
+            modifier.setAdditionalRecords( new ArrayList<ResourceRecord>() );
+
+            session.write( modifier.getDnsMessage() );
+        }
+    }
+
+
+    public void messageSent( IoSession session, Object message )
+    {
+        LOG.debug( "{} SENT:  {}", session.getRemoteAddress(), message );
+    }
+
+
+    protected String getContextKey()
+    {
+        return ( this.contextKey );
+    }
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/protocol/DnsProtocolTcpCodecFactory.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/protocol/DnsProtocolTcpCodecFactory.java
new file mode 100644
index 0000000..ec0c3f7
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/protocol/DnsProtocolTcpCodecFactory.java
@@ -0,0 +1,67 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.protocol;
+
+
+import org.apache.mina.filter.codec.ProtocolCodecFactory;
+import org.apache.mina.filter.codec.ProtocolDecoder;
+import org.apache.mina.filter.codec.ProtocolEncoder;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 545041 $, $Date: 2007-06-06 20:31:34 -0700 (Wed, 06 Jun 2007) $
+ */
+public class DnsProtocolTcpCodecFactory implements ProtocolCodecFactory
+{
+    private static final DnsProtocolTcpCodecFactory INSTANCE = new DnsProtocolTcpCodecFactory();
+
+
+    /**
+     * Returns the singleton instance of {@link DnsProtocolTcpCodecFactory}.
+     *
+     * @return The singleton instance of {@link DnsProtocolTcpCodecFactory}.
+     */
+    public static DnsProtocolTcpCodecFactory getInstance()
+    {
+        return INSTANCE;
+    }
+
+
+    private DnsProtocolTcpCodecFactory()
+    {
+        // Private constructor prevents instantiation outside this class.
+    }
+
+
+    public ProtocolEncoder getEncoder()
+    {
+        // Create a new encoder.
+        return new DnsTcpEncoder();
+    }
+
+
+    public ProtocolDecoder getDecoder()
+    {
+        // Create a new decoder.
+        return new DnsTcpDecoder();
+    }
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/protocol/DnsProtocolUdpCodecFactory.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/protocol/DnsProtocolUdpCodecFactory.java
new file mode 100644
index 0000000..9b04844
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/protocol/DnsProtocolUdpCodecFactory.java
@@ -0,0 +1,67 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.protocol;
+
+
+import org.apache.mina.filter.codec.ProtocolCodecFactory;
+import org.apache.mina.filter.codec.ProtocolDecoder;
+import org.apache.mina.filter.codec.ProtocolEncoder;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class DnsProtocolUdpCodecFactory implements ProtocolCodecFactory
+{
+    private static final DnsProtocolUdpCodecFactory INSTANCE = new DnsProtocolUdpCodecFactory();
+
+
+    /**
+     * Returns the singleton instance of {@link DnsProtocolUdpCodecFactory}.
+     *
+     * @return The singleton instance of {@link DnsProtocolUdpCodecFactory}.
+     */
+    public static DnsProtocolUdpCodecFactory getInstance()
+    {
+        return INSTANCE;
+    }
+
+
+    private DnsProtocolUdpCodecFactory()
+    {
+        // Private constructor prevents instantiation outside this class.
+    }
+
+
+    public ProtocolEncoder getEncoder()
+    {
+        // Create a new encoder.
+        return new DnsUdpEncoder();
+    }
+
+
+    public ProtocolDecoder getDecoder()
+    {
+        // Create a new decoder.
+        return new DnsUdpDecoder();
+    }
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/protocol/DnsTcpDecoder.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/protocol/DnsTcpDecoder.java
new file mode 100644
index 0000000..394e860
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/protocol/DnsTcpDecoder.java
@@ -0,0 +1,93 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.protocol;
+
+
+import org.apache.directory.server.dns.io.decoder.DnsMessageDecoder;
+import org.apache.mina.common.BufferDataException;
+import org.apache.mina.common.ByteBuffer;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.filter.codec.CumulativeProtocolDecoder;
+import org.apache.mina.filter.codec.ProtocolDecoderOutput;
+
+
+/**
+ * A {@link CumulativeProtocolDecoder} which supports DNS operation over TCP,
+ * by reassembling split packets prior to decoding.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 545041 $, $Date: 2007-06-06 20:31:34 -0700 (Wed, 06 Jun 2007) $
+ */
+public class DnsTcpDecoder extends CumulativeProtocolDecoder
+{
+    private DnsMessageDecoder decoder = new DnsMessageDecoder();
+
+    private int maxObjectSize = 16384; // 16KB
+
+
+    /**
+     * Returns the allowed maximum size of the object to be decoded.
+     * If the size of the object to be decoded exceeds this value, this
+     * decoder will throw a {@link BufferDataException}.  The default
+     * value is <tt>16384</tt> (16KB).
+     * 
+     * @return The max object size.
+     */
+    public int getMaxObjectSize()
+    {
+        return maxObjectSize;
+    }
+
+
+    /**
+     * Sets the allowed maximum size of the object to be decoded.
+     * If the size of the object to be decoded exceeds this value, this
+     * decoder will throw a {@link BufferDataException}.  The default
+     * value is <tt>16384</tt> (16KB).
+     * 
+     * @param maxObjectSize 
+     */
+    public void setMaxObjectSize( int maxObjectSize )
+    {
+        if ( maxObjectSize <= 0 )
+        {
+            throw new IllegalArgumentException( "maxObjectSize: " + maxObjectSize );
+        }
+
+        this.maxObjectSize = maxObjectSize;
+    }
+
+
+    @Override
+    protected boolean doDecode( IoSession session, ByteBuffer in, ProtocolDecoderOutput out ) throws Exception
+    {
+        if ( !in.prefixedDataAvailable( 2, maxObjectSize ) )
+        {
+            return false;
+        }
+
+        in.getShort();
+
+        out.write( decoder.decode( in ) );
+
+        return true;
+    }
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/protocol/DnsTcpEncoder.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/protocol/DnsTcpEncoder.java
new file mode 100644
index 0000000..baa3b27
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/protocol/DnsTcpEncoder.java
@@ -0,0 +1,69 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.protocol;
+
+
+import org.apache.directory.server.dns.io.encoder.DnsMessageEncoder;
+import org.apache.directory.server.dns.messages.DnsMessage;
+import org.apache.mina.common.ByteBuffer;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.filter.codec.ProtocolEncoderAdapter;
+import org.apache.mina.filter.codec.ProtocolEncoderOutput;
+
+
+/**
+ * A ProtocolEncoder for use in the MINA framework that uses the 
+ * DnsMessageEncoder to encode DnsMessages.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 545041 $, $Date: 2007-06-06 20:31:34 -0700 (Wed, 06 Jun 2007) $
+ */
+public class DnsTcpEncoder extends ProtocolEncoderAdapter
+{
+    private DnsMessageEncoder encoder = new DnsMessageEncoder();
+
+
+    public void encode( IoSession session, Object message, ProtocolEncoderOutput out )
+    {
+        ByteBuffer buf = ByteBuffer.allocate( 1024 );
+
+        // make space for short length
+        buf.putShort( ( short ) 0 );
+
+        encoder.encode( buf, ( DnsMessage ) message );
+
+        // mark position
+        int end = buf.position();
+
+        // length is the data minus 2 bytes for the pre-pended length
+        short recordLength = ( short ) ( end - 2 );
+
+        // write the length
+        buf.rewind();
+        buf.putShort( recordLength );
+
+        // set the position back before flipping the buffer
+        buf.position( end );
+        buf.flip();
+
+        out.write( buf );
+    }
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/protocol/DnsUdpDecoder.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/protocol/DnsUdpDecoder.java
new file mode 100644
index 0000000..c09f2f7
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/protocol/DnsUdpDecoder.java
@@ -0,0 +1,49 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.protocol;
+
+
+import java.io.IOException;
+
+import org.apache.directory.server.dns.io.decoder.DnsMessageDecoder;
+import org.apache.mina.common.ByteBuffer;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.filter.codec.ProtocolDecoderAdapter;
+import org.apache.mina.filter.codec.ProtocolDecoderOutput;
+
+
+/**
+ * A ProtocolDecoder for use in the MINA framework that uses the 
+ * DnsMessageDecoder to decode DnsMessages.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class DnsUdpDecoder extends ProtocolDecoderAdapter
+{
+    private DnsMessageDecoder decoder = new DnsMessageDecoder();
+
+
+    public void decode( IoSession session, ByteBuffer in, ProtocolDecoderOutput out ) throws IOException
+    {
+        out.write( decoder.decode( in ) );
+    }
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/protocol/DnsUdpEncoder.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/protocol/DnsUdpEncoder.java
new file mode 100644
index 0000000..611c3e3
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/protocol/DnsUdpEncoder.java
@@ -0,0 +1,53 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.protocol;
+
+
+import org.apache.directory.server.dns.io.encoder.DnsMessageEncoder;
+import org.apache.directory.server.dns.messages.DnsMessage;
+import org.apache.mina.common.ByteBuffer;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.filter.codec.ProtocolEncoderAdapter;
+import org.apache.mina.filter.codec.ProtocolEncoderOutput;
+
+
+/**
+ * A ProtocolEncoder for use in the MINA framework that uses the 
+ * DnsMessageEncoder to encode DnsMessages.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class DnsUdpEncoder extends ProtocolEncoderAdapter
+{
+    private DnsMessageEncoder encoder = new DnsMessageEncoder();
+
+
+    public void encode( IoSession session, Object message, ProtocolEncoderOutput out )
+    {
+        ByteBuffer buf = ByteBuffer.allocate( 1024 );
+        encoder.encode( buf, ( DnsMessage ) message );
+
+        buf.flip();
+
+        out.write( buf );
+    }
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/protocol/package-info.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/protocol/package-info.java
new file mode 100644
index 0000000..1e7014e
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/protocol/package-info.java
@@ -0,0 +1,30 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+/**
+ * Provides the {@link org.apache.mina.common.IoHandler} and associated
+ * {@link org.apache.mina.filter.codec.ProtocolCodecFactory} required
+ * to implement the DNS Service with the MINA NIO framework.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+
+package org.apache.directory.server.dns.protocol;
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/service/DnsContext.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/service/DnsContext.java
new file mode 100644
index 0000000..9ce0ad7
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/service/DnsContext.java
@@ -0,0 +1,126 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.dns.service;
+
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.directory.server.dns.DnsServer;
+import org.apache.directory.server.dns.messages.DnsMessage;
+import org.apache.directory.server.dns.messages.ResourceRecord;
+import org.apache.directory.server.dns.store.RecordStore;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class DnsContext
+{
+    private static final long serialVersionUID = -5911142975867852436L;
+
+    private DnsServer config;
+    private RecordStore store;
+    private DnsMessage reply;
+    private List<ResourceRecord> records = new ArrayList<ResourceRecord>();
+
+
+    /**
+     * @return Returns the recordEntry.
+     */
+    public List<ResourceRecord> getResourceRecords()
+    {
+        return records;
+    }
+
+
+    /**
+     * @param resourceRecord The resourceRecord to add.
+     */
+    public void addResourceRecord( ResourceRecord resourceRecord )
+    {
+        this.records.add( resourceRecord );
+    }
+
+
+    /**
+     * @param resourceRecords The resourceRecords to add.
+     */
+    public void addResourceRecords( Collection<ResourceRecord> resourceRecords )
+    {
+        this.records.addAll( resourceRecords );
+    }
+
+
+    /**
+     * @return Returns the config.
+     */
+    public DnsServer getConfig()
+    {
+        return config;
+    }
+
+
+    /**
+     * @param config The config to set.
+     */
+    public void setConfig( DnsServer config )
+    {
+        this.config = config;
+    }
+
+
+    /**
+     * @return Returns the reply.
+     */
+    public DnsMessage getReply()
+    {
+        return reply;
+    }
+
+
+    /**
+     * @param reply The reply to set.
+     */
+    public void setReply( DnsMessage reply )
+    {
+        this.reply = reply;
+    }
+
+
+    /**
+     * @return Returns the store.
+     */
+    public RecordStore getStore()
+    {
+        return store;
+    }
+
+
+    /**
+     * @param store The store to set.
+     */
+    public void setStore( RecordStore store )
+    {
+        this.store = store;
+    }
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/service/DomainNameService.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/service/DomainNameService.java
new file mode 100644
index 0000000..3499a43
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/service/DomainNameService.java
@@ -0,0 +1,223 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.dns.service;
+
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.directory.server.dns.DnsException;
+import org.apache.directory.server.dns.messages.DnsMessage;
+import org.apache.directory.server.dns.messages.DnsMessageModifier;
+import org.apache.directory.server.dns.messages.MessageType;
+import org.apache.directory.server.dns.messages.OpCode;
+import org.apache.directory.server.dns.messages.QuestionRecord;
+import org.apache.directory.server.dns.messages.ResourceRecord;
+import org.apache.directory.server.dns.messages.ResponseCode;
+import org.apache.directory.server.dns.store.RecordStore;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * Domain Name Service (DNS) Protocol (RFC 1034, 1035)
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class DomainNameService
+{
+    /** the log for this class */
+    private static final Logger LOG = LoggerFactory.getLogger( DomainNameService.class );
+
+
+    /**
+     * Creates a new instance of DomainNameService.
+     */
+    public static void execute( DnsContext dnsContext, DnsMessage request ) throws Exception
+    {
+        if ( LOG.isDebugEnabled() )
+        {
+            monitorRequest( request );
+        }
+
+        getResourceRecords( dnsContext, request );
+
+        if ( LOG.isDebugEnabled() )
+        {
+            monitorContext( dnsContext );
+        }
+
+        buildReply( dnsContext, request );
+
+        if ( LOG.isDebugEnabled() )
+        {
+            monitorReply( dnsContext );
+        }
+    }
+    
+    private static void monitorRequest( DnsMessage request ) throws Exception
+    {
+        try
+        {
+            LOG.debug( monitorMessage( request, "request" ) );
+        }
+        catch ( Exception e )
+        {
+            // This is a monitor.  No exceptions should bubble up.
+            LOG.error( "Error in request monitor", e );
+        }
+    }
+    
+
+    private static void getResourceRecords( DnsContext dnsContext, DnsMessage request ) throws Exception
+    {
+        RecordStore store = dnsContext.getStore();
+
+        List<QuestionRecord> questions = request.getQuestionRecords();
+
+        Iterator<QuestionRecord> it = questions.iterator();
+
+        while ( it.hasNext() )
+        {
+            dnsContext.addResourceRecords( getEntry( store, it.next() ) );
+        }
+    }
+    
+    
+    /**
+     * Returns a set of {@link ResourceRecord}s from a {@link RecordStore}, given a DNS {@link QuestionRecord}.
+     *
+     * @param store
+     * @param question
+     * @return The set of {@link ResourceRecord}s.
+     * @throws DNSException
+     */
+    private static Set<ResourceRecord> getEntry( RecordStore store, QuestionRecord question ) throws DnsException
+    {
+        Set<ResourceRecord> records = null;
+
+        records = store.getRecords( question );
+
+        if ( records == null || records.isEmpty() )
+        {
+            LOG.debug( "The domain name referenced in the query does not exist." );
+
+            throw new DnsException( ResponseCode.NAME_ERROR );
+        }
+
+        return records;
+    }
+    
+    
+    private static void monitorContext( DnsContext dnsContext ) throws Exception
+    {
+        try
+        {
+            RecordStore store = dnsContext.getStore();
+            List<ResourceRecord> records = dnsContext.getResourceRecords();
+
+            StringBuffer sb = new StringBuffer();
+            sb.append( "Monitoring context:" );
+            sb.append( "\n\t" + "store:                     " + store );
+            sb.append( "\n\t" + "records:                   " + records );
+
+            LOG.debug( sb.toString() );
+        }
+        catch ( Exception e )
+        {
+            // This is a monitor.  No exceptions should bubble up.
+            LOG.error( "Error in context monitor", e );
+        }
+    }
+    
+    
+    private static void buildReply( DnsContext dnsContext, DnsMessage request ) throws Exception
+    {
+        List<ResourceRecord> records = dnsContext.getResourceRecords();
+
+        DnsMessageModifier modifier = new DnsMessageModifier();
+
+        modifier.setTransactionId( request.getTransactionId() );
+        modifier.setMessageType( MessageType.RESPONSE );
+        modifier.setOpCode( OpCode.QUERY );
+        modifier.setAuthoritativeAnswer( false );
+        modifier.setTruncated( false );
+        modifier.setRecursionDesired( request.isRecursionDesired() );
+        modifier.setRecursionAvailable( false );
+        modifier.setReserved( false );
+        modifier.setAcceptNonAuthenticatedData( false );
+        modifier.setResponseCode( ResponseCode.NO_ERROR );
+        modifier.setQuestionRecords( request.getQuestionRecords() );
+
+        modifier.setAnswerRecords( records );
+        modifier.setAuthorityRecords( new ArrayList<ResourceRecord>() );
+        modifier.setAdditionalRecords( new ArrayList<ResourceRecord>() );
+
+        dnsContext.setReply( modifier.getDnsMessage() );
+    }
+ 
+    
+    private static void monitorReply( DnsContext dnsContext ) throws Exception
+    {
+        try
+        {
+            DnsMessage reply = dnsContext.getReply();
+
+            LOG.debug( monitorMessage( reply, "reply" ) );
+        }
+        catch ( Exception e )
+        {
+            // This is a monitor.  No exceptions should bubble up.
+            LOG.error( "Error in reply monitor", e );
+        }
+    }
+    
+    
+    private static String monitorMessage( DnsMessage message, String direction )
+    {
+        MessageType messageType = message.getMessageType();
+        OpCode opCode = message.getOpCode();
+        ResponseCode responseCode = message.getResponseCode();
+        int transactionId = message.getTransactionId();
+
+        StringBuffer sb = new StringBuffer();
+        sb.append( "Monitoring " + direction + ":" );
+        sb.append( "\n\t" + "messageType                " + messageType );
+        sb.append( "\n\t" + "opCode                     " + opCode );
+        sb.append( "\n\t" + "responseCode               " + responseCode );
+        sb.append( "\n\t" + "transactionId              " + transactionId );
+
+        sb.append( "\n\t" + "authoritativeAnswer        " + message.isAuthoritativeAnswer() );
+        sb.append( "\n\t" + "truncated                  " + message.isTruncated() );
+        sb.append( "\n\t" + "recursionDesired           " + message.isRecursionDesired() );
+        sb.append( "\n\t" + "recursionAvailable         " + message.isRecursionAvailable() );
+        sb.append( "\n\t" + "reserved                   " + message.isReserved() );
+        sb.append( "\n\t" + "acceptNonAuthenticatedData " + message.isAcceptNonAuthenticatedData() );
+
+        List<QuestionRecord> questions = message.getQuestionRecords();
+
+        sb.append( "\n\t" + "questions:                 " + questions );
+
+        return sb.toString();
+    }
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/service/MonitorContext.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/service/MonitorContext.java
new file mode 100644
index 0000000..ea8bff8
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/service/MonitorContext.java
@@ -0,0 +1,77 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.dns.service;
+
+
+import java.util.List;
+
+import org.apache.directory.server.dns.messages.ResourceRecord;
+import org.apache.directory.server.dns.store.RecordStore;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.handler.chain.IoHandlerCommand;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class MonitorContext implements IoHandlerCommand
+{
+    /** the log for this class */
+    private static final Logger log = LoggerFactory.getLogger( MonitorContext.class );
+
+    private String contextKey = "context";
+
+
+    public void execute( NextCommand next, IoSession session, Object message ) throws Exception
+    {
+        if ( log.isDebugEnabled() )
+        {
+            try
+            {
+                DnsContext dnsContext = ( DnsContext ) session.getAttribute( getContextKey() );
+                RecordStore store = dnsContext.getStore();
+                List<ResourceRecord> records = dnsContext.getResourceRecords();
+
+                StringBuffer sb = new StringBuffer();
+                sb.append( "Monitoring context:" );
+                sb.append( "\n\t" + "store:                     " + store );
+                sb.append( "\n\t" + "records:                   " + records );
+
+                log.debug( sb.toString() );
+            }
+            catch ( Exception e )
+            {
+                // This is a monitor.  No exceptions should bubble up.
+                log.error( "Error in context monitor", e );
+            }
+        }
+
+        next.execute( session, message );
+    }
+
+
+    protected String getContextKey()
+    {
+        return ( this.contextKey );
+    }
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/service/package-info.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/service/package-info.java
new file mode 100644
index 0000000..1ca588d
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/service/package-info.java
@@ -0,0 +1,28 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+/**
+ * Provides the DNS Service.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+
+package org.apache.directory.server.dns.service;
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/store/DnsAttribute.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/store/DnsAttribute.java
new file mode 100644
index 0000000..37b9bda
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/store/DnsAttribute.java
@@ -0,0 +1,92 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.dns.store;
+
+
+/**
+ * Constants representing the DNS attribute ids as defined by the Apache DNS schema.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class DnsAttribute
+{
+    /**
+     * Apache DNS Schema Attributes
+     */
+
+    /**
+     * An abstract DNS record objectClass used to build other specific structural
+     * objectclasses for different record types
+     */
+
+    /** the apachedns schema name for an apacheDnsAbstractRecord */
+    public static final String NAME = "apacheDnsName";
+    /** the apachedns schema type for an apacheDnsAbstractRecord */
+    public static final String TYPE = "apacheDnsType";
+    /** the apachedns schema class for an apacheDnsAbstractRecord */
+    public static final String CLASS = "apacheDnsClass";
+    /** the apachedns schema TTL for an apacheDnsAbstractRecord */
+    public static final String TTL = "apacheDnsTtl";
+
+    /**
+     * DNS record type - Start of Authority
+     */
+
+    /** the apachedns schema apacheDnsSoaMName for an apacheDnsStartOfAuthorityRecord */
+    public static final String SOA_M_NAME = "apacheDnsSoaMName";
+    /** the apachedns schema apacheDnsSoaRName for an apacheDnsStartOfAuthorityRecord */
+    public static final String SOA_R_NAME = "apacheDnsSoaRName";
+    /** the apachedns schema apacheDnsSoaSerial for an apacheDnsStartOfAuthorityRecord */
+    public static final String SOA_SERIAL = "apacheDnsSoaSerial";
+    /** the apachedns schema apacheDnsSoaRefresh for an apacheDnsStartOfAuthorityRecord */
+    public static final String SOA_REFRESH = "apacheDnsSoaRefresh";
+    /** the apachedns schema apacheDnsSoaRetry for an apacheDnsStartOfAuthorityRecord */
+    public static final String SOA_RETRY = "apacheDnsSoaRetry";
+    /** the apachedns schema apacheDnsSoaExpire for an apacheDnsStartOfAuthorityRecord */
+    public static final String SOA_EXPIRE = "apacheDnsSoaExpire";
+    /** the apachedns schema apacheDnsSoaMinimum for an apacheDnsStartOfAuthorityRecord */
+    public static final String SOA_MINIMUM = "apacheDnsSoaMinimum";
+
+    /**
+     * Other DNS record attributes
+     */
+
+    /** the apachedns schema apacheDnsDomainName */
+    public static final String DOMAIN_NAME = "apacheDnsDomainName";
+
+    /** the apachedns schema apacheDnsIpAddress */
+    public static final String IP_ADDRESS = "apacheDnsIpAddress";
+
+    /** the apachedns schema apacheDnsMxPreference */
+    public static final String MX_PREFERENCE = "apacheDnsMxPreference";
+
+    /** the apachedns schema apacheDnsCharacterString */
+    public static final String CHARACTER_STRING = "apacheDnsCharacterString";
+
+    /** the apachedns schema apacheDnsServicePriority */
+    public static final String SERVICE_PRIORITY = "apacheDnsServicePriority";
+
+    /** the apachedns schema apacheDnsServiceWeight */
+    public static final String SERVICE_WEIGHT = "apacheDnsServiceWeight";
+
+    /** the apachedns schema apacheDnsServicePort */
+    public static final String SERVICE_PORT = "apacheDnsServicePort";
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/store/RecordStore.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/store/RecordStore.java
new file mode 100644
index 0000000..62624ba
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/store/RecordStore.java
@@ -0,0 +1,46 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.dns.store;
+
+
+import java.util.Set;
+
+import org.apache.directory.server.dns.DnsException;
+import org.apache.directory.server.dns.messages.QuestionRecord;
+import org.apache.directory.server.dns.messages.ResourceRecord;
+
+
+/**
+ * The store interface used by the DNS protocol to lookup resource records.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public interface RecordStore
+{
+    /**
+     * Returns a set of {@link ResourceRecord}s, given a DNS {@link QuestionRecord}.
+     * If something bad happens throw the accurate {@link DnsException} 
+     * @param question
+     * @return The set of {@link ResourceRecord}s.
+     * @throws DnsException
+     */
+    public Set<ResourceRecord> getRecords( QuestionRecord question ) throws DnsException;
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/store/RecordStoreStub.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/store/RecordStoreStub.java
new file mode 100644
index 0000000..be02b7d
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/store/RecordStoreStub.java
@@ -0,0 +1,64 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.dns.store;
+
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.directory.server.dns.DnsException;
+import org.apache.directory.server.dns.messages.QuestionRecord;
+import org.apache.directory.server.dns.messages.RecordClass;
+import org.apache.directory.server.dns.messages.RecordType;
+import org.apache.directory.server.dns.messages.ResourceRecord;
+import org.apache.directory.server.dns.messages.ResourceRecordModifier;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class RecordStoreStub implements RecordStore
+{
+    public Set<ResourceRecord> getRecords( QuestionRecord question ) throws DnsException
+    {
+        Set<ResourceRecord> set = new HashSet<ResourceRecord>();
+
+        ResourceRecordModifier rm = new ResourceRecordModifier();
+        rm.setDnsClass( RecordClass.IN );
+        rm.setDnsName( "ldap.example.com" );
+        rm.setDnsTtl( 100 );
+        rm.setDnsType( RecordType.A );
+        rm.put( DnsAttribute.IP_ADDRESS, "10.0.0.2" );
+
+        set.add( rm.getEntry() );
+
+        ResourceRecordModifier rm2 = new ResourceRecordModifier();
+        rm2.setDnsClass( RecordClass.IN );
+        rm2.setDnsName( "www.example.com" );
+        rm2.setDnsTtl( 100 );
+        rm2.setDnsType( RecordType.A );
+        rm2.put( DnsAttribute.IP_ADDRESS, "10.0.0.3" );
+
+        set.add( rm2.getEntry() );
+
+        return set;
+    }
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/store/jndi/DnsCatalog.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/store/jndi/DnsCatalog.java
new file mode 100644
index 0000000..7eb8955
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/store/jndi/DnsCatalog.java
@@ -0,0 +1,81 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.store.jndi;
+
+
+import java.util.Map;
+
+import org.apache.directory.server.protocol.shared.catalog.Catalog;
+
+
+/**
+ * A catalog for mapping DNS zones to search base DN's. 
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+class DnsCatalog implements Catalog
+{
+    private Map<String, Object> map;
+
+
+    /**
+     * Creates a new instance of DnsCatalog.
+     *
+     * @param map
+     */
+    public DnsCatalog( Map<String, Object> map )
+    {
+        this.map = map;
+    }
+
+
+    public String getBaseDn( String name )
+    {
+        if ( name.endsWith( "." ) )
+        {
+            int last = name.lastIndexOf( "." );
+            name = name.substring( 0, last );
+        }
+
+        while ( !name.equals( "" ) && name != null )
+        {
+            String candidate = ( String ) map.get( name );
+            if ( candidate != null )
+            {
+                return candidate;
+            }
+
+            int period = name.indexOf( "." );
+
+            if ( period > -1 )
+            {
+                name = name.substring( period + 1 );
+            }
+            else
+            {
+                return "";
+            }
+        }
+
+        return "";
+    }
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/store/jndi/DnsOperation.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/store/jndi/DnsOperation.java
new file mode 100644
index 0000000..9ea65cf
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/store/jndi/DnsOperation.java
@@ -0,0 +1,49 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.dns.store.jndi;
+
+
+import java.io.Serializable;
+import java.util.Set;
+
+import javax.naming.Name;
+import javax.naming.directory.DirContext;
+
+import org.apache.directory.server.dns.messages.ResourceRecord;
+
+
+/**
+ * Interface to support the command pattern with JNDI contexts.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 437023 $, $Date: 2006-08-25 16:50:47 -0700 (Fri, 25 Aug 2006) $
+ */
+public interface DnsOperation extends Serializable
+{
+    /**
+     * The command pattern execute method.
+     * 
+     * @param ctx The context to execute the command with
+     * @param baseDn The base DN for working with the context
+     * @return Set<ResourceRecord> The result returned by the command
+     * @throws Exception The exception thrown by the command
+     */
+    public Set<ResourceRecord> execute( DirContext ctx, Name baseDn ) throws Exception;
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/store/jndi/JndiRecordStoreImpl.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/store/jndi/JndiRecordStoreImpl.java
new file mode 100644
index 0000000..7932bd3
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/store/jndi/JndiRecordStoreImpl.java
@@ -0,0 +1,79 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.dns.store.jndi;
+
+
+import java.util.Set;
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.dns.DnsException;
+import org.apache.directory.server.dns.messages.QuestionRecord;
+import org.apache.directory.server.dns.messages.ResourceRecord;
+import org.apache.directory.server.dns.store.RecordStore;
+
+
+/**
+ * A DirectoryService-backed implementation of the RecordStore interface.  This RecordStore uses
+ * the Strategy pattern to either serve records based on a single base DN or to lookup
+ * catalog mappings from directory configuration.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class JndiRecordStoreImpl implements RecordStore
+{
+    /**
+     * a handle on the searchh strategy
+     */
+    private final SearchStrategy strategy;
+
+
+    /**
+     * Creates a new instance of JndiRecordStoreImpl.
+     *
+     * @param catalogBaseDn base of catalog of searchDns
+     * @param searchBaseDn single search base for when there is no catalog
+     * @param directoryService DirectoryService backend for the searches.
+     */
+    public JndiRecordStoreImpl( String catalogBaseDn, String searchBaseDn, DirectoryService directoryService )
+    {
+
+        strategy = getSearchStrategy( catalogBaseDn, searchBaseDn, directoryService );
+    }
+
+
+    public Set<ResourceRecord> getRecords( QuestionRecord question ) throws DnsException
+    {
+        return strategy.getRecords( question );
+    }
+
+
+    private SearchStrategy getSearchStrategy( String catalogBaseDn, String searchBaseDn, DirectoryService directoryService )
+    {
+        if ( catalogBaseDn != null )
+        {
+            // build catalog from factory
+            return new MultiBaseSearch( catalogBaseDn, directoryService );
+        }
+
+        // use config for catalog baseDN
+        return new SingleBaseSearch( searchBaseDn, directoryService );
+    }
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/store/jndi/MultiBaseSearch.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/store/jndi/MultiBaseSearch.java
new file mode 100644
index 0000000..a3627b0
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/store/jndi/MultiBaseSearch.java
@@ -0,0 +1,109 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.store.jndi;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.dns.DnsException;
+import org.apache.directory.server.dns.messages.QuestionRecord;
+import org.apache.directory.server.dns.messages.ResourceRecord;
+import org.apache.directory.server.dns.messages.ResponseCode;
+import org.apache.directory.server.dns.store.jndi.operations.GetRecords;
+import org.apache.directory.server.protocol.shared.ServiceConfigurationException;
+import org.apache.directory.server.protocol.shared.catalog.Catalog;
+import org.apache.directory.server.protocol.shared.catalog.GetCatalog;
+import org.apache.directory.shared.ldap.exception.LdapNameNotFoundException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.naming.NamingException;
+import javax.naming.directory.DirContext;
+
+import java.util.Map;
+import java.util.Set;
+
+
+/**
+ * A JNDI-backed search strategy implementation.  This search strategy builds a catalog
+ * from directory configuration to determine where zones are to search for
+ * resource records.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class MultiBaseSearch implements SearchStrategy
+{
+    /** the LOG for this class */
+    private static final Logger LOG = LoggerFactory.getLogger( MultiBaseSearch.class );
+
+    private final Catalog catalog;
+    private final DirectoryService directoryService;
+
+
+    MultiBaseSearch( String catalogBaseDn, DirectoryService directoryService )
+    {
+        this.directoryService = directoryService;
+        try
+        {
+            DirContext ctx = directoryService.getJndiContext(catalogBaseDn);
+            //noinspection unchecked
+            catalog = new DnsCatalog( ( Map<String, Object> ) new GetCatalog().execute( ctx, null ) );
+        }
+        catch ( Exception e )
+        {
+            LOG.error( e.getMessage(), e );
+            String message = "Failed to get catalog context " + catalogBaseDn;
+            throw new ServiceConfigurationException( message, e );
+        }
+    }
+
+
+    public Set<ResourceRecord> getRecords( QuestionRecord question ) throws DnsException
+    {
+        try
+        {
+            GetRecords getRecords = new GetRecords( question );
+            String baseDn = catalog.getBaseDn( question.getDomainName() );
+            DirContext dirContext = directoryService.getJndiContext( baseDn );
+            return getRecords.execute( dirContext, null );
+        }
+        catch ( LdapNameNotFoundException lnnfe )
+        {
+            LOG.debug( "Name for DNS record search does not exist.", lnnfe );
+
+            throw new DnsException( ResponseCode.NAME_ERROR );
+        }
+        catch ( NamingException ne )
+        {
+            LOG.error( ne.getMessage(), ne );
+            String message = "Failed to get initial context " + question.getDomainName();
+            throw new ServiceConfigurationException( message, ne );
+        }
+        catch ( Exception e )
+        {
+            LOG.debug( "Unexpected error retrieving DNS records.", e );
+            throw new DnsException( ResponseCode.SERVER_FAILURE );
+        }
+
+    }
+
+
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/store/jndi/SearchStrategy.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/store/jndi/SearchStrategy.java
new file mode 100644
index 0000000..dc0d5f5
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/store/jndi/SearchStrategy.java
@@ -0,0 +1,49 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.store.jndi;
+
+
+import java.util.Set;
+
+import org.apache.directory.server.dns.DnsException;
+import org.apache.directory.server.dns.messages.QuestionRecord;
+import org.apache.directory.server.dns.messages.ResourceRecord;
+
+
+/**
+ * Interface for search strategies.  The DNS protocol may search a single
+ * base DN for resource records or use a catalog to lookup resource records
+ * in multiple zones.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+interface SearchStrategy
+{
+    /**
+     * Returns a set of {@link ResourceRecord}s, given a DNS {@link QuestionRecord}.
+     *
+     * @param question
+     * @return The set of {@link ResourceRecord}s.
+     * @throws Exception
+     */
+    Set<ResourceRecord> getRecords( QuestionRecord question ) throws DnsException;
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/store/jndi/SingleBaseSearch.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/store/jndi/SingleBaseSearch.java
new file mode 100644
index 0000000..6b626de
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/store/jndi/SingleBaseSearch.java
@@ -0,0 +1,85 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.store.jndi;
+
+
+import java.util.Set;
+
+import javax.naming.NamingException;
+import javax.naming.directory.DirContext;
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.dns.DnsException;
+import org.apache.directory.server.dns.messages.QuestionRecord;
+import org.apache.directory.server.dns.messages.ResourceRecord;
+import org.apache.directory.server.dns.messages.ResponseCode;
+import org.apache.directory.server.dns.store.jndi.operations.GetRecords;
+import org.apache.directory.server.protocol.shared.ServiceConfigurationException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * A JNDI-backed search strategy implementation.  This search strategy searches a
+ * single base DN for resource records.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class SingleBaseSearch implements SearchStrategy
+{
+    /**
+     * the LOG for this class
+     */
+    private static final Logger LOG = LoggerFactory.getLogger( SingleBaseSearch.class );
+
+    private final DirContext ctx;
+
+
+    SingleBaseSearch( String searchBaseDn, DirectoryService directoryService )
+    {
+        try
+        {
+            ctx = directoryService.getJndiContext( searchBaseDn );
+        } catch ( NamingException e )
+        {
+            throw new ServiceConfigurationException( "Can't get context at" + searchBaseDn, e );
+        }
+
+    }
+
+
+    public Set<ResourceRecord> getRecords( QuestionRecord question ) throws DnsException
+    {
+        try
+        {
+
+            return new GetRecords( question ).execute( ctx, null );
+        }
+        catch ( Exception e )
+        {
+            LOG.debug( "Unexpected error retrieving DNS records.", e );
+            throw new DnsException( ResponseCode.SERVER_FAILURE );
+        }
+    }
+
+
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/store/jndi/operations/GetFlatRecord.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/store/jndi/operations/GetFlatRecord.java
new file mode 100644
index 0000000..3f27c92
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/store/jndi/operations/GetFlatRecord.java
@@ -0,0 +1,142 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.dns.store.jndi.operations;
+
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.naming.Name;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.SearchResult;
+
+import org.apache.directory.server.dns.messages.QuestionRecord;
+import org.apache.directory.server.dns.messages.RecordClass;
+import org.apache.directory.server.dns.messages.RecordType;
+import org.apache.directory.server.dns.messages.ResourceRecord;
+import org.apache.directory.server.dns.messages.ResourceRecordModifier;
+import org.apache.directory.server.dns.store.DnsAttribute;
+import org.apache.directory.server.dns.store.jndi.DnsOperation;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+
+
+/**
+ * A JNDI context operation for looking up a Resource Record with flat attributes.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class GetFlatRecord implements DnsOperation
+{
+    private static final long serialVersionUID = 4931303293468915435L;
+
+    /** The name of the question to get. */
+    private final QuestionRecord question;
+
+
+    /**
+     * Creates the action to be used against the embedded JNDI provider.
+     * 
+     * @param question 
+     */
+    public GetFlatRecord( QuestionRecord question )
+    {
+        this.question = question;
+    }
+
+
+    /**
+     * Note that the base is a relative path from the exiting context.
+     * It is not a DN.
+     */
+    public Set<ResourceRecord> execute( DirContext ctx, Name base ) throws Exception
+    {
+        if ( question == null )
+        {
+            return null;
+        }
+
+        Attributes matchAttrs = new AttributesImpl( true );
+
+        matchAttrs.put( new AttributeImpl( DnsAttribute.NAME, question.getDomainName() ) );
+        matchAttrs.put( new AttributeImpl( DnsAttribute.TYPE, question.getRecordType().name() ) );
+        matchAttrs.put( new AttributeImpl( DnsAttribute.CLASS, question.getRecordClass().name() ) );
+
+        Set<ResourceRecord> record = new HashSet<ResourceRecord>();
+
+        NamingEnumeration<SearchResult> answer = ctx.search( base, matchAttrs );
+
+        if ( answer.hasMore() )
+        {
+            SearchResult result = answer.next();
+
+            Attributes attrs = result.getAttributes();
+
+            if ( attrs == null )
+            {
+                return null;
+            }
+
+            record.add( getRecord( attrs ) );
+        }
+
+        return record;
+    }
+
+
+    /**
+     * Marshals a RecordStoreEntry from an Attributes object.
+     *
+     * @param attrs the attributes of the DNS question
+     * @return the entry for the question
+     * @throws NamingException if there are any access problems
+     */
+    private ResourceRecord getRecord( Attributes attrs ) throws NamingException
+    {
+        ResourceRecordModifier modifier = new ResourceRecordModifier();
+
+        Attribute attr;
+
+        String dnsName = ( attr = attrs.get( DnsAttribute.NAME ) ) != null ? ( String ) attr.get() : null;
+        String dnsType = ( attr = attrs.get( DnsAttribute.TYPE ) ) != null ? ( String ) attr.get() : null;
+        String dnsClass = ( attr = attrs.get( DnsAttribute.CLASS ) ) != null ? ( String ) attr.get() : null;
+        String dnsTtl = ( attr = attrs.get( DnsAttribute.TTL ) ) != null ? ( String ) attr.get() : null;
+
+        modifier.setDnsName( dnsName );
+        modifier.setDnsType( RecordType.valueOf( dnsType ) );
+        modifier.setDnsClass( RecordClass.valueOf( dnsClass ) );
+        modifier.setDnsTtl( Integer.parseInt( dnsTtl ) );
+
+        NamingEnumeration<String> ids = attrs.getIDs();
+
+        while ( ids.hasMore() )
+        {
+            String id = ids.next();
+            modifier.put( id, ( String ) attrs.get( id ).get() );
+        }
+
+        return modifier.getEntry();
+    }
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/store/jndi/operations/GetRecords.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/store/jndi/operations/GetRecords.java
new file mode 100644
index 0000000..ffc0265
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/store/jndi/operations/GetRecords.java
@@ -0,0 +1,325 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.store.jndi.operations;
+
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import javax.naming.CompoundName;
+import javax.naming.Name;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+
+import org.apache.directory.server.dns.messages.QuestionRecord;
+import org.apache.directory.server.dns.messages.RecordClass;
+import org.apache.directory.server.dns.messages.RecordType;
+import org.apache.directory.server.dns.messages.ResourceRecord;
+import org.apache.directory.server.dns.messages.ResourceRecordModifier;
+import org.apache.directory.server.dns.store.DnsAttribute;
+import org.apache.directory.server.dns.store.jndi.DnsOperation;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+
+
+/**
+ * A JNDI context operation for looking up Resource Records from an embedded JNDI provider.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class GetRecords implements DnsOperation
+{
+    private static final long serialVersionUID = 1077580995617778894L;
+
+    /** The name of the question to get. */
+    private final QuestionRecord question;
+
+
+    /**
+     * Creates the action to be used against the embedded JNDI provider.
+     * 
+     * @param question 
+     */
+    public GetRecords( QuestionRecord question )
+    {
+        this.question = question;
+    }
+
+    /**
+     * Mappings of type to objectClass.
+     */
+    private static final Map<RecordType, String> TYPE_TO_OBJECTCLASS;
+
+    static
+    {
+        Map<RecordType, String> typeToObjectClass = new HashMap<RecordType, String>();
+        typeToObjectClass.put( RecordType.SOA, "apacheDnsStartOfAuthorityRecord" );
+        typeToObjectClass.put( RecordType.A, "apacheDnsAddressRecord" );
+        typeToObjectClass.put( RecordType.NS, "apacheDnsNameServerRecord" );
+        typeToObjectClass.put( RecordType.CNAME, "apacheDnsCanonicalNameRecord" );
+        typeToObjectClass.put( RecordType.PTR, "apacheDnsPointerRecord" );
+        typeToObjectClass.put( RecordType.MX, "apacheDnsMailExchangeRecord" );
+        typeToObjectClass.put( RecordType.SRV, "apacheDnsServiceRecord" );
+        typeToObjectClass.put( RecordType.TXT, "apacheDnsTextRecord" );
+
+        TYPE_TO_OBJECTCLASS = Collections.unmodifiableMap( typeToObjectClass );
+    }
+
+    /**
+     * Mappings of type to objectClass.
+     */
+    private static final Map<String, RecordType> OBJECTCLASS_TO_TYPE;
+
+    static
+    {
+        Map<String, RecordType> objectClassToType = new HashMap<String, RecordType>();
+        objectClassToType.put( "apacheDnsStartOfAuthorityRecord", RecordType.SOA );
+        objectClassToType.put( "apacheDnsAddressRecord", RecordType.A );
+        objectClassToType.put( "apacheDnsNameServerRecord", RecordType.NS );
+        objectClassToType.put( "apacheDnsCanonicalNameRecord", RecordType.CNAME );
+        objectClassToType.put( "apacheDnsPointerRecord", RecordType.PTR );
+        objectClassToType.put( "apacheDnsMailExchangeRecord", RecordType.MX );
+        objectClassToType.put( "apacheDnsServiceRecord", RecordType.SRV );
+        objectClassToType.put( "apacheDnsTextRecord", RecordType.TXT );
+        objectClassToType.put( "apacheDnsReferralNameServer", RecordType.NS );
+        objectClassToType.put( "apacheDnsReferralAddress", RecordType.A );
+
+        OBJECTCLASS_TO_TYPE = Collections.unmodifiableMap( objectClassToType );
+    }
+
+
+    /**
+     * Note that the base is a relative path from the exiting context.
+     * It is not a DN.
+     */
+    public Set<ResourceRecord> execute( DirContext ctx, Name base ) throws Exception
+    {
+        if ( question == null )
+        {
+            return null;
+        }
+
+        String name = question.getDomainName();
+        RecordType type = question.getRecordType();
+
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+
+        String filter = "(objectClass=" + TYPE_TO_OBJECTCLASS.get( type ) + ")";
+
+        NamingEnumeration<SearchResult> list = ctx.search( transformDomainName( name ), filter, controls );
+
+        Set<ResourceRecord> set = new HashSet<ResourceRecord>();
+
+        while ( list.hasMore() )
+        {
+            SearchResult result = list.next();
+            Name relative = getRelativeName( ctx.getNameInNamespace(), result.getName() );
+
+            set.add( getRecord( result.getAttributes(), relative ) );
+        }
+
+        return set;
+    }
+
+
+    /**
+     * Marshals a RecordStoreEntry from an Attributes object.
+     *
+     * @param attrs the attributes of the DNS question
+     * @return the entry for the question
+     * @throws NamingException if there are any access problems
+     */
+    private ResourceRecord getRecord( Attributes attrs, Name relative ) throws NamingException
+    {
+        String SOA_MINIMUM = "86400";
+        String SOA_CLASS = "IN";
+
+        ResourceRecordModifier modifier = new ResourceRecordModifier();
+
+        Attribute attr;
+
+        // if no name, transform rdn
+        attr = attrs.get( DnsAttribute.NAME );
+
+        if ( attr != null )
+        {
+            modifier.setDnsName( ( String ) attr.get() );
+        }
+        else
+        {
+            relative = getDomainComponents( relative );
+
+            String dnsName;
+            dnsName = transformDistinguishedName( relative.toString() );
+            modifier.setDnsName( dnsName );
+        }
+
+        // type is implicit in objectclass
+        attr = attrs.get( DnsAttribute.TYPE );
+
+        if ( attr != null )
+        {
+            modifier.setDnsType( RecordType.valueOf( ( String ) attr.get() ) );
+        }
+        else
+        {
+            modifier.setDnsType( getType( attrs.get( SchemaConstants.OBJECT_CLASS_AT ) ) );
+        }
+
+        // class defaults to SOA CLASS
+        String dnsClass = ( attr = attrs.get( DnsAttribute.CLASS ) ) != null ? ( String ) attr.get() : SOA_CLASS;
+        modifier.setDnsClass( RecordClass.valueOf( dnsClass ) );
+
+        // ttl defaults to SOA MINIMUM
+        String dnsTtl = ( attr = attrs.get( DnsAttribute.TTL ) ) != null ? ( String ) attr.get() : SOA_MINIMUM;
+        modifier.setDnsTtl( Integer.parseInt( dnsTtl ) );
+
+        NamingEnumeration<String> ids = attrs.getIDs();
+
+        while ( ids.hasMore() )
+        {
+            String id = ids.next();
+            modifier.put( id, ( String ) attrs.get( id ).get() );
+        }
+
+        return modifier.getEntry();
+    }
+
+
+    /**
+     * Uses the algorithm in <a href="http://www.faqs.org/rfcs/rfc2247.html">RFC 2247</a>
+     * to transform any Internet domain name into a distinguished name.
+     *
+     * @param domainName the domain name
+     * @return the distinguished name
+     */
+    String transformDomainName( String domainName )
+    {
+        if ( domainName == null || domainName.length() == 0 )
+        {
+            return "";
+        }
+
+        StringBuffer buf = new StringBuffer( domainName.length() + 16 );
+
+        buf.append( "dc=" );
+        buf.append( domainName.replaceAll( "\\.", ",dc=" ) );
+
+        return buf.toString();
+    }
+
+
+    /**
+     * Uses the algorithm in <a href="http://www.faqs.org/rfcs/rfc2247.html">RFC 2247</a>
+     * to transform a distinguished name into an Internet domain name.
+     *
+     * @param distinguishedName the distinguished name
+     * @return the domain name
+     */
+    String transformDistinguishedName( String distinguishedName )
+    {
+        if ( distinguishedName == null || distinguishedName.length() == 0 )
+        {
+            return "";
+        }
+
+        String domainName = distinguishedName.replaceFirst( "dc=", "" );
+        domainName = domainName.replaceAll( ",dc=", "." );
+
+        return domainName;
+    }
+
+
+    private RecordType getType( Attribute objectClass ) throws NamingException
+    {
+        NamingEnumeration<?> list = objectClass.getAll();
+
+        while ( list.hasMore() )
+        {
+            String value = ( String ) list.next();
+
+            if ( !value.equals( "apacheDnsAbstractRecord" ) )
+            {
+                RecordType type = OBJECTCLASS_TO_TYPE.get( value );
+
+                if ( type == null )
+                {
+                    throw new RuntimeException( "Record type to objectClass mapping has not been set." );
+                }
+
+                return type;
+            }
+        }
+
+        throw new NamingException( "ResourceRecord requires STRUCTURAL objectClass" );
+    }
+
+
+    private Name getRelativeName( String nameInNamespace, String baseDn ) throws NamingException
+    {
+        Properties props = new Properties();
+        props.setProperty( "jndi.syntax.direction", "right_to_left" );
+        props.setProperty( "jndi.syntax.separator", "," );
+        props.setProperty( "jndi.syntax.ignorecase", "true" );
+        props.setProperty( "jndi.syntax.trimblanks", "true" );
+
+        Name searchBaseDn = null;
+
+        Name ctxRoot = new CompoundName( nameInNamespace, props );
+        searchBaseDn = new CompoundName( baseDn, props );
+
+        if ( !searchBaseDn.startsWith( ctxRoot ) )
+        {
+            throw new NamingException( "Invalid search base " + baseDn );
+        }
+
+        for ( int ii = 0; ii < ctxRoot.size(); ii++ )
+        {
+            searchBaseDn.remove( 0 );
+        }
+
+        return searchBaseDn;
+    }
+
+
+    private Name getDomainComponents( Name name ) throws NamingException
+    {
+        for ( int ii = 0; ii < name.size(); ii++ )
+        {
+            if ( !name.get( ii ).startsWith( "dc=" ) )
+            {
+                name.remove( ii );
+            }
+        }
+
+        return name;
+    }
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/store/jndi/operations/package-info.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/store/jndi/operations/package-info.java
new file mode 100644
index 0000000..cc4ba88
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/store/jndi/operations/package-info.java
@@ -0,0 +1,28 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+/**
+ * Provides Command pattern objects for working with the JNDI backing store.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+
+package org.apache.directory.server.dns.store.jndi.operations;
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/store/package-info.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/store/package-info.java
new file mode 100644
index 0000000..da86b63
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/store/package-info.java
@@ -0,0 +1,29 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+/**
+ * Provides {@link org.apache.directory.server.dns.store.RecordStore} interface for serving DNS resource records
+ * to {@link org.apache.directory.server.dns.DnsServer}.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+
+package org.apache.directory.server.dns.store;
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/util/EnumConverter.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/util/EnumConverter.java
new file mode 100644
index 0000000..ddb23b9
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/util/EnumConverter.java
@@ -0,0 +1,48 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.util;
+
+
+/**
+ * An interface that allows an Enum to be converted to another type, such as an
+ * integer or long.  Useful in cases where the Java assigned ordinal just isn't
+ * reliable enough or is unable to represent the values we need.<p>
+ * 
+ * Implementers should also implement (though there is no way of requiring it)
+ * a static method for taking the conversion the other way:
+ * 
+ * <code>
+ *   public static Enum convert (K value);
+ * </code>
+ * 
+ * @param <K> 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public interface EnumConverter<K>
+{
+    /**
+     * Convert the enum to another type.
+     *
+     * @return The other type.
+     */
+    K convert();
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/util/ReverseEnumMap.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/util/ReverseEnumMap.java
new file mode 100644
index 0000000..c820081
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/util/ReverseEnumMap.java
@@ -0,0 +1,72 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.util;
+
+
+import java.util.HashMap;
+import java.util.Map;
+
+
+/**
+ * A map to easily get the actual Enum instance from it's value as seen in the
+ * <a href="http://www.javaspecialists.co.za/archive/newsletter.do?issue=113">
+ * The JavaSpecialists newsletter</a>.
+ * 
+ * @param <K> 
+ * @param <E> 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ReverseEnumMap<K, E extends Enum<E> & EnumConverter<K>>
+{
+    private Map<K, E> reverseMap = new HashMap<K, E>();
+
+
+    /**
+     * Creates a new instance of ReverseEnumMap.
+     *
+     * @param enumType
+     */
+    public ReverseEnumMap( Class<E> enumType )
+    {
+        for ( E e : enumType.getEnumConstants() )
+        {
+            reverseMap.put( e.convert(), e );
+        }
+    }
+
+
+    /**
+     * Return the enum given an ordinal value.
+     *
+     * @param value
+     * @return The enum.
+     */
+    public E get( K value )
+    {
+        E e = reverseMap.get( value );
+        if ( e == null )
+        {
+            throw new IllegalArgumentException( "Invalid enum value: " + value );
+        }
+        return e;
+    }
+}
diff --git a/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/util/package-info.java b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/util/package-info.java
new file mode 100644
index 0000000..5f34daa
--- /dev/null
+++ b/old_trunk/protocol-dns/src/main/java/org/apache/directory/server/dns/util/package-info.java
@@ -0,0 +1,28 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+/**
+ * Provides utility code for working with enumerators.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+
+package org.apache.directory.server.dns.util;
diff --git a/old_trunk/protocol-dns/src/site/site.xml b/old_trunk/protocol-dns/src/site/site.xml
new file mode 100644
index 0000000..9ec7c16
--- /dev/null
+++ b/old_trunk/protocol-dns/src/site/site.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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.
+-->
+<!--
+  @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+-->
+<project name="${project.name}">
+    <bannerLeft>
+        <name>${project.name}</name>
+    </bannerLeft>
+    <publishDate position="navigation-bottom" format="dd-MM-yyyy HH:mm" />
+    <body>
+        <menu name="Parent">
+            <item name="Apache Directory ApacheDS" href="../index.html" />
+        </menu>
+        <menu ref="reports" />
+    </body>
+</project>
diff --git a/old_trunk/protocol-dns/src/test/java/org/apache/directory/server/dns/AbstractDnsTestCase.java b/old_trunk/protocol-dns/src/test/java/org/apache/directory/server/dns/AbstractDnsTestCase.java
new file mode 100644
index 0000000..765d91b
--- /dev/null
+++ b/old_trunk/protocol-dns/src/test/java/org/apache/directory/server/dns/AbstractDnsTestCase.java
@@ -0,0 +1,281 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.dns;
+
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.apache.directory.server.dns.messages.DnsMessage;
+import org.apache.directory.server.dns.messages.DnsMessageModifier;
+import org.apache.directory.server.dns.messages.MessageType;
+import org.apache.directory.server.dns.messages.OpCode;
+import org.apache.directory.server.dns.messages.QuestionRecord;
+import org.apache.directory.server.dns.messages.RecordClass;
+import org.apache.directory.server.dns.messages.RecordType;
+import org.apache.directory.server.dns.messages.ResourceRecord;
+import org.apache.directory.server.dns.messages.ResourceRecordModifier;
+import org.apache.directory.server.dns.messages.ResponseCode;
+import org.apache.directory.server.dns.store.DnsAttribute;
+import org.apache.mina.common.ByteBuffer;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public abstract class AbstractDnsTestCase extends TestCase
+{
+    protected static final int MINIMUM_DNS_DATAGRAM_SIZE = 576;
+
+
+    protected ByteBuffer getTestQueryByteBuffer() throws IOException
+    {
+        return getByteBufferFromFile( "DNS-QUERY.pdu" );
+    }
+
+
+    protected ByteBuffer getTestResponseByteBuffer() throws IOException
+    {
+        return getByteBufferFromFile( "DNS-RESPONSE.pdu" );
+    }
+
+
+    protected ByteBuffer getTestMxQueryByteBuffer() throws IOException
+    {
+        return getByteBufferFromFile( "MX-QUERY.pdu" );
+    }
+
+
+    protected ByteBuffer getTestMxResponseByteBuffer() throws IOException
+    {
+        return getByteBufferFromFile( "MX-RESPONSE.pdu" );
+    }
+
+
+    protected ByteBuffer getByteBufferFromFile( String file ) throws IOException
+    {
+        InputStream is = getClass().getResourceAsStream( file );
+
+        byte[] bytes = new byte[MINIMUM_DNS_DATAGRAM_SIZE];
+
+        int offset = 0;
+        int numRead = 0;
+        while ( offset < bytes.length && ( numRead = is.read( bytes, offset, bytes.length - offset ) ) >= 0 )
+        {
+            offset += numRead;
+        }
+
+        is.close();
+
+        return ByteBuffer.wrap( bytes );
+    }
+
+
+    protected DnsMessage getTestQuery()
+    {
+        DnsMessageModifier modifier = new DnsMessageModifier();
+        modifier.setTransactionId( ( short ) 27799 );
+        modifier.setMessageType( MessageType.QUERY );
+        modifier.setOpCode( OpCode.QUERY );
+        modifier.setRecursionDesired( true );
+        modifier.setQuestionRecords( Collections.singletonList( getTestQuestionRecord() ) );
+        modifier.setResponseCode( ResponseCode.NO_ERROR );
+        modifier.setAnswerRecords( new ArrayList<ResourceRecord>() );
+        modifier.setAuthorityRecords( new ArrayList<ResourceRecord>() );
+        modifier.setAdditionalRecords( new ArrayList<ResourceRecord>() );
+        return modifier.getDnsMessage();
+    }
+
+
+    protected QuestionRecord getTestQuestionRecord()
+    {
+        return new QuestionRecord( "www.example.com", RecordType.A, RecordClass.IN );
+    }
+
+
+    protected DnsMessage getTestMxQuery()
+    {
+        DnsMessageModifier modifier = new DnsMessageModifier();
+        modifier.setTransactionId( 51511 );
+        modifier.setMessageType( MessageType.QUERY );
+        modifier.setOpCode( OpCode.QUERY );
+        modifier.setRecursionDesired( true );
+        modifier.setQuestionRecords( Collections.singletonList( getTestMxQuestionRecord() ) );
+        modifier.setResponseCode( ResponseCode.NO_ERROR );
+        modifier.setAnswerRecords( new ArrayList<ResourceRecord>() );
+        modifier.setAuthorityRecords( new ArrayList<ResourceRecord>() );
+        modifier.setAdditionalRecords( new ArrayList<ResourceRecord>() );
+        return modifier.getDnsMessage();
+    }
+
+
+    protected QuestionRecord getTestMxQuestionRecord()
+    {
+        return new QuestionRecord( "apache.org", RecordType.MX, RecordClass.IN );
+    }
+
+
+    protected DnsMessage getTestMxResponse() throws UnknownHostException
+    {
+        DnsMessageModifier modifier = new DnsMessageModifier();
+        modifier.setTransactionId( 51511 );
+        modifier.setMessageType( MessageType.RESPONSE );
+        modifier.setOpCode( OpCode.QUERY );
+        modifier.setRecursionDesired( true );
+        modifier.setRecursionAvailable( true );
+        modifier.setQuestionRecords( Collections.singletonList( getTestMxQuestionRecord() ) );
+        modifier.setResponseCode( ResponseCode.NO_ERROR );
+        modifier.setAnswerRecords( getTestMxAnswerRecords() );
+        modifier.setAuthorityRecords( getTestMxAuthorityRecords() );
+        modifier.setAdditionalRecords( getTestMxAdditionalRecords() );
+        return modifier.getDnsMessage();
+    }
+
+
+    protected List<ResourceRecord> getTestMxAnswerRecords()
+    {
+        List<ResourceRecord> records = new ArrayList<ResourceRecord>();
+
+        ResourceRecordModifier modifier = new ResourceRecordModifier();
+        modifier.setDnsName( "apache.org" );
+        modifier.setDnsType( RecordType.MX );
+        modifier.setDnsClass( RecordClass.IN );
+        modifier.setDnsTtl( 267 );
+        modifier.put( DnsAttribute.MX_PREFERENCE, "10" );
+        modifier.put( DnsAttribute.DOMAIN_NAME, "herse.apache.org" );
+        records.add( modifier.getEntry() );
+
+        modifier = new ResourceRecordModifier();
+        modifier.setDnsName( "apache.org" );
+        modifier.setDnsType( RecordType.MX );
+        modifier.setDnsClass( RecordClass.IN );
+        modifier.setDnsTtl( 267 );
+        modifier.put( DnsAttribute.MX_PREFERENCE, "20" );
+        modifier.put( DnsAttribute.DOMAIN_NAME, "mail.apache.org" );
+        records.add( modifier.getEntry() );
+
+        return records;
+    }
+
+
+    protected List<ResourceRecord> getTestMxAuthorityRecords()
+    {
+        List<ResourceRecord> records = new ArrayList<ResourceRecord>();
+
+        ResourceRecordModifier modifier = new ResourceRecordModifier();
+        modifier.setDnsClass( RecordClass.IN );
+        modifier.setDnsName( "apache.org" );
+        modifier.setDnsTtl( 1932 );
+        modifier.setDnsType( RecordType.NS );
+        modifier.put( DnsAttribute.DOMAIN_NAME, "ns.hyperreal.org" );
+        records.add( modifier.getEntry() );
+
+        modifier = new ResourceRecordModifier();
+        modifier.setDnsClass( RecordClass.IN );
+        modifier.setDnsName( "apache.org" );
+        modifier.setDnsTtl( 1932 );
+        modifier.setDnsType( RecordType.NS );
+        modifier.put( DnsAttribute.DOMAIN_NAME, "ns1.eu.bitnames.com" );
+        records.add( modifier.getEntry() );
+
+        modifier = new ResourceRecordModifier();
+        modifier.setDnsClass( RecordClass.IN );
+        modifier.setDnsName( "apache.org" );
+        modifier.setDnsTtl( 1932 );
+        modifier.setDnsType( RecordType.NS );
+        modifier.put( DnsAttribute.DOMAIN_NAME, "ns1.us.bitnames.com" );
+        records.add( modifier.getEntry() );
+
+        modifier = new ResourceRecordModifier();
+        modifier.setDnsClass( RecordClass.IN );
+        modifier.setDnsName( "apache.org" );
+        modifier.setDnsTtl( 1932 );
+        modifier.setDnsType( RecordType.NS );
+        modifier.put( DnsAttribute.DOMAIN_NAME, "ns2.surfnet.nl" );
+        records.add( modifier.getEntry() );
+
+        return records;
+    }
+
+
+    protected List<ResourceRecord> getTestMxAdditionalRecords() throws UnknownHostException
+    {
+        List<ResourceRecord> records = new ArrayList<ResourceRecord>();
+
+        ResourceRecordModifier modifier = new ResourceRecordModifier();
+        modifier.setDnsClass( RecordClass.IN );
+        modifier.setDnsName( "herse.apache.org" );
+        modifier.setDnsTtl( 3313 );
+        modifier.setDnsType( RecordType.A );
+        modifier.put( DnsAttribute.IP_ADDRESS, InetAddress.getByName( "140.211.11.133" ).toString() );
+        records.add( modifier.getEntry() );
+
+        modifier = new ResourceRecordModifier();
+        modifier.setDnsClass( RecordClass.IN );
+        modifier.setDnsName( "mail.apache.org" );
+        modifier.setDnsTtl( 3313 );
+        modifier.setDnsType( RecordType.A );
+        modifier.put( DnsAttribute.IP_ADDRESS, InetAddress.getByName( "140.211.11.2" ).toString() );
+        records.add( modifier.getEntry() );
+
+        modifier = new ResourceRecordModifier();
+        modifier.setDnsClass( RecordClass.IN );
+        modifier.setDnsName( "ns1.eu.bitnames.com" );
+        modifier.setDnsTtl( 156234 );
+        modifier.setDnsType( RecordType.A );
+        modifier.put( DnsAttribute.IP_ADDRESS, InetAddress.getByName( "82.195.149.118" ).toString() );
+        records.add( modifier.getEntry() );
+
+        modifier = new ResourceRecordModifier();
+        modifier.setDnsClass( RecordClass.IN );
+        modifier.setDnsName( "ns1.us.bitnames.com" );
+        modifier.setDnsTtl( 156236 );
+        modifier.setDnsType( RecordType.A );
+        modifier.put( DnsAttribute.IP_ADDRESS, InetAddress.getByName( "216.52.237.236" ).toString() );
+        records.add( modifier.getEntry() );
+
+        modifier = new ResourceRecordModifier();
+        modifier.setDnsClass( RecordClass.IN );
+        modifier.setDnsName( "ns2.surfnet.nl" );
+        modifier.setDnsTtl( 77100 );
+        modifier.setDnsType( RecordType.A );
+        modifier.put( DnsAttribute.IP_ADDRESS, InetAddress.getByName( "192.87.36.2" ).toString() );
+        records.add( modifier.getEntry() );
+
+        modifier = new ResourceRecordModifier();
+        modifier.setDnsClass( RecordClass.IN );
+        modifier.setDnsName( "ns2.surfnet.nl" );
+        modifier.setDnsTtl( 77100 );
+        modifier.setDnsType( RecordType.AAAA );
+        modifier.put( DnsAttribute.IP_ADDRESS, InetAddress.getByName( "2001:610:3:200a:192:87:36:2" ).toString() );
+        records.add( modifier.getEntry() );
+
+        return records;
+    }
+
+}
diff --git a/old_trunk/protocol-dns/src/test/java/org/apache/directory/server/dns/io/decoder/AddressRecordDecoderTest.java b/old_trunk/protocol-dns/src/test/java/org/apache/directory/server/dns/io/decoder/AddressRecordDecoderTest.java
new file mode 100644
index 0000000..9d36076
--- /dev/null
+++ b/old_trunk/protocol-dns/src/test/java/org/apache/directory/server/dns/io/decoder/AddressRecordDecoderTest.java
@@ -0,0 +1,63 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.io.decoder;
+
+
+import java.net.InetAddress;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.apache.directory.server.dns.store.DnsAttribute;
+import org.apache.mina.common.ByteBuffer;
+
+
+/**
+ * Tests for the A resource record decoder.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 501160 $, $Date: 2007-01-29 12:41:33 -0700 (Mon, 29 Jan 2007) $
+ */
+public class AddressRecordDecoderTest extends TestCase
+{
+    InetAddress address;
+    ByteBuffer inputBuffer;
+
+    AddressRecordDecoder decoder;
+
+
+    public void setUp() throws Exception
+    {
+        address = InetAddress.getByName( "127.0.0.1" );
+        inputBuffer = ByteBuffer.allocate( address.getAddress().length );
+        inputBuffer.put( address.getAddress() );
+        inputBuffer.flip();
+
+        decoder = new AddressRecordDecoder();
+    }
+
+
+    public void testDecode() throws Exception
+    {
+        Map attributes = decoder.decode( inputBuffer, ( short ) address.getAddress().length );
+        assertEquals( address, attributes.get( DnsAttribute.IP_ADDRESS ) );
+    }
+}
diff --git a/old_trunk/protocol-dns/src/test/java/org/apache/directory/server/dns/io/decoder/IPv6RecordDecoderTest.java b/old_trunk/protocol-dns/src/test/java/org/apache/directory/server/dns/io/decoder/IPv6RecordDecoderTest.java
new file mode 100644
index 0000000..47713c4
--- /dev/null
+++ b/old_trunk/protocol-dns/src/test/java/org/apache/directory/server/dns/io/decoder/IPv6RecordDecoderTest.java
@@ -0,0 +1,64 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.io.decoder;
+
+
+import java.net.InetAddress;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.apache.directory.server.dns.store.DnsAttribute;
+import org.apache.mina.common.ByteBuffer;
+
+
+/**
+ * Tests for the AAAA resource record decoder.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 501160 $, $Date: 2007-01-29 12:41:33 -0700 (Mon, 29 Jan 2007) $
+ */
+public class IPv6RecordDecoderTest extends TestCase
+{
+    InetAddress address;
+    ByteBuffer inputBuffer;
+
+    IPv6RecordDecoder decoder;
+
+
+    public void setUp() throws Exception
+    {
+        address = InetAddress.getByName( "0:0:0:0:0:0:0:1" );
+        inputBuffer = ByteBuffer.allocate( address.getAddress().length );
+        inputBuffer.put( address.getAddress() );
+        inputBuffer.flip();
+
+        decoder = new IPv6RecordDecoder();
+    }
+
+
+    public void testDecode() throws Exception
+    {
+        Map attributes = decoder.decode( inputBuffer, ( short ) address.getAddress().length );
+        assertEquals( address, attributes.get( DnsAttribute.IP_ADDRESS ) );
+    }
+
+}
diff --git a/old_trunk/protocol-dns/src/test/java/org/apache/directory/server/dns/io/decoder/MailExchangeRecordDecoderTest.java b/old_trunk/protocol-dns/src/test/java/org/apache/directory/server/dns/io/decoder/MailExchangeRecordDecoderTest.java
new file mode 100644
index 0000000..f2cbbc2
--- /dev/null
+++ b/old_trunk/protocol-dns/src/test/java/org/apache/directory/server/dns/io/decoder/MailExchangeRecordDecoderTest.java
@@ -0,0 +1,73 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.io.decoder;
+
+
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.apache.directory.server.dns.store.DnsAttribute;
+import org.apache.mina.common.ByteBuffer;
+
+
+/**
+ * Tests for the MX resource record decoder.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 501160 $, $Date: 2007-01-29 12:41:33 -0700 (Mon, 29 Jan 2007) $
+ */
+public class MailExchangeRecordDecoderTest extends TestCase
+{
+    ByteBuffer inputBuffer;
+
+    short preference = 10;
+    String domainName = "herse.apache.org";
+    String[] domainNameParts = new String[]
+        { "herse", "apache", "org" };
+
+    MailExchangeRecordDecoder decoder;
+
+
+    public void setUp()
+    {
+        inputBuffer = ByteBuffer.allocate( 128 );
+        inputBuffer.putShort( preference );
+        inputBuffer.put( ( byte ) domainNameParts[0].length() );
+        inputBuffer.put( domainNameParts[0].getBytes() );
+        inputBuffer.put( ( byte ) domainNameParts[1].length() );
+        inputBuffer.put( domainNameParts[1].getBytes() );
+        inputBuffer.put( ( byte ) domainNameParts[2].length() );
+        inputBuffer.put( domainNameParts[2].getBytes() );
+        inputBuffer.put( ( byte ) 0x00 );
+        inputBuffer.flip();
+
+        decoder = new MailExchangeRecordDecoder();
+    }
+
+
+    public void testDecode() throws Exception
+    {
+        Map attributes = decoder.decode( inputBuffer, ( short ) inputBuffer.remaining() );
+        assertEquals( preference, attributes.get( DnsAttribute.MX_PREFERENCE ) );
+        assertEquals( domainName, attributes.get( DnsAttribute.DOMAIN_NAME ) );
+    }
+}
diff --git a/old_trunk/protocol-dns/src/test/java/org/apache/directory/server/dns/io/decoder/NameServerRecordDecoderTest.java b/old_trunk/protocol-dns/src/test/java/org/apache/directory/server/dns/io/decoder/NameServerRecordDecoderTest.java
new file mode 100644
index 0000000..440bf75
--- /dev/null
+++ b/old_trunk/protocol-dns/src/test/java/org/apache/directory/server/dns/io/decoder/NameServerRecordDecoderTest.java
@@ -0,0 +1,70 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.io.decoder;
+
+
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.apache.directory.server.dns.store.DnsAttribute;
+import org.apache.mina.common.ByteBuffer;
+
+
+/**
+ * Tests for the NS resource record decoder.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 501160 $, $Date: 2007-01-29 12:41:33 -0700 (Mon, 29 Jan 2007) $
+ */
+public class NameServerRecordDecoderTest extends TestCase
+{
+    ByteBuffer inputBuffer;
+
+    String domainName = "ns.hyperreal.org";
+    String[] domainNameParts = new String[]
+        { "ns", "hyperreal", "org" };
+
+    NameServerRecordDecoder decoder;
+
+
+    public void setUp()
+    {
+        inputBuffer = ByteBuffer.allocate( 128 );
+        inputBuffer.put( ( byte ) domainNameParts[0].length() );
+        inputBuffer.put( domainNameParts[0].getBytes() );
+        inputBuffer.put( ( byte ) domainNameParts[1].length() );
+        inputBuffer.put( domainNameParts[1].getBytes() );
+        inputBuffer.put( ( byte ) domainNameParts[2].length() );
+        inputBuffer.put( domainNameParts[2].getBytes() );
+        inputBuffer.put( ( byte ) 0x00 );
+        inputBuffer.flip();
+
+        decoder = new NameServerRecordDecoder();
+    }
+
+
+    public void testDecode() throws Exception
+    {
+        Map attributes = decoder.decode( inputBuffer, ( short ) inputBuffer.remaining() );
+        assertEquals( domainName, attributes.get( DnsAttribute.DOMAIN_NAME ) );
+    }
+}
diff --git a/old_trunk/protocol-dns/src/test/java/org/apache/directory/server/dns/io/encoder/AbstractResourceRecordEncoderTest.java b/old_trunk/protocol-dns/src/test/java/org/apache/directory/server/dns/io/encoder/AbstractResourceRecordEncoderTest.java
new file mode 100644
index 0000000..f6d956b
--- /dev/null
+++ b/old_trunk/protocol-dns/src/test/java/org/apache/directory/server/dns/io/encoder/AbstractResourceRecordEncoderTest.java
@@ -0,0 +1,112 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.dns.io.encoder;
+
+
+import java.io.IOException;
+import java.net.UnknownHostException;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.apache.directory.server.dns.messages.RecordClass;
+import org.apache.directory.server.dns.messages.RecordType;
+import org.apache.directory.server.dns.messages.ResourceRecord;
+import org.apache.directory.server.dns.messages.ResourceRecordImpl;
+import org.apache.mina.common.ByteBuffer;
+
+
+/**
+ * A base class for testing different types of ResourceRecordEncoders.  It 
+ * handles setting up the expected output buffer not having to do specifically
+ * with the resource data, and handles creating the ResourceRecord to be tested.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 501160 $, $Date: 2007-01-29 12:41:33 -0700 (Mon, 29 Jan 2007) $
+ */
+public abstract class AbstractResourceRecordEncoderTest extends TestCase
+{
+    ByteBuffer expectedData;
+    String domainName = "herse.apache.org";
+    String[] domainNameParts = domainName.split( "\\." );
+    int timeToLive = 3400;
+    ResourceRecord record;
+
+
+    public void setUp() throws UnknownHostException
+    {
+        setUpResourceData();
+        record = new ResourceRecordImpl( domainName, RecordType.A, RecordClass.IN, timeToLive, getAttributes() );
+
+        expectedData = ByteBuffer.allocate( 128 );
+        expectedData.put( ( byte ) 18 );
+        expectedData.put( ( byte ) domainNameParts[0].length() ); // 1
+        expectedData.put( domainNameParts[0].getBytes() ); // + 5
+        expectedData.put( ( byte ) domainNameParts[1].length() ); // + 1
+        expectedData.put( domainNameParts[1].getBytes() ); // + 6
+        expectedData.put( ( byte ) domainNameParts[2].length() ); // + 1
+        expectedData.put( domainNameParts[2].getBytes() ); // + 3
+        expectedData.put( ( byte ) 0x00 ); // + 1 = 18
+        expectedData.putShort( RecordType.A.convert() );
+        expectedData.putShort( RecordClass.IN.convert() );
+        expectedData.putInt( timeToLive );
+        putExpectedResourceData( expectedData );
+    }
+
+
+    public void testEncode() throws IOException
+    {
+        ByteBuffer outBuffer = ByteBuffer.allocate( 128 );
+        getEncoder().put( outBuffer, record );
+        assertEquals( expectedData, outBuffer );
+    }
+
+
+    /**
+     * A method that implementers can override if they need to do some setup 
+     * before the resource record and expected data buffer are created, such
+     * as initialize an ip address.
+     */
+    protected void setUpResourceData()
+    {
+    }
+
+
+    /**
+     * @return the encoder to be tested
+     */
+    protected abstract ResourceRecordEncoder getEncoder();
+
+
+    /**
+     * @return the attributes to be used as the resource data for the resource
+     *         record
+     */
+    protected abstract Map getAttributes();
+
+
+    /**
+     * Put the encoded resource data into a buffer that will compared to the
+     * result of using the encoder under test.
+     * 
+     * @param expectedData buffer where the expected resource data should be put
+     */
+    protected abstract void putExpectedResourceData( ByteBuffer expectedData );
+}
diff --git a/old_trunk/protocol-dns/src/test/java/org/apache/directory/server/dns/io/encoder/AddressRecordEncoderTest.java b/old_trunk/protocol-dns/src/test/java/org/apache/directory/server/dns/io/encoder/AddressRecordEncoderTest.java
new file mode 100644
index 0000000..56aeb8d
--- /dev/null
+++ b/old_trunk/protocol-dns/src/test/java/org/apache/directory/server/dns/io/encoder/AddressRecordEncoderTest.java
@@ -0,0 +1,76 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.io.encoder;
+
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.directory.server.dns.store.DnsAttribute;
+import org.apache.mina.common.ByteBuffer;
+
+
+/**
+ * Tests for the A record encoder.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 501160 $, $Date: 2007-01-29 12:41:33 -0700 (Mon, 29 Jan 2007) $
+ */
+public class AddressRecordEncoderTest extends AbstractResourceRecordEncoderTest
+{
+    InetAddress address;
+
+
+    protected void setUpResourceData()
+    {
+        try
+        {
+            address = InetAddress.getByName( "127.0.0.1" );
+        }
+        catch ( UnknownHostException e )
+        {
+            // should never happen
+        }
+    }
+
+
+    protected Map getAttributes()
+    {
+        Map attributes = new HashMap();
+        attributes.put( DnsAttribute.IP_ADDRESS, address );
+        return attributes;
+    }
+
+
+    protected ResourceRecordEncoder getEncoder()
+    {
+        return new AddressRecordEncoder();
+    }
+
+
+    protected void putExpectedResourceData( ByteBuffer expectedData )
+    {
+        expectedData.put( ( byte ) address.getAddress().length );
+        expectedData.put( address.getAddress() );
+    }
+}
diff --git a/old_trunk/protocol-dns/src/test/java/org/apache/directory/server/dns/io/encoder/CanonicalNameRecordEncoderTest.java b/old_trunk/protocol-dns/src/test/java/org/apache/directory/server/dns/io/encoder/CanonicalNameRecordEncoderTest.java
new file mode 100644
index 0000000..efdd76e
--- /dev/null
+++ b/old_trunk/protocol-dns/src/test/java/org/apache/directory/server/dns/io/encoder/CanonicalNameRecordEncoderTest.java
@@ -0,0 +1,88 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.io.encoder;
+
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.directory.server.dns.store.DnsAttribute;
+import org.apache.mina.common.ByteBuffer;
+
+
+/**
+ * Tests for the CNAME record encoder.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 501160 $, $Date: 2007-01-29 12:41:33 -0700 (Mon, 29 Jan 2007) $
+ */
+public class CanonicalNameRecordEncoderTest extends AbstractResourceRecordEncoderTest
+{
+    String cname = "cname.apache.org";
+    String[] cnameParts = cname.split( "\\." );
+
+
+    protected Map getAttributes()
+    {
+        Map map = new HashMap();
+        map.put( DnsAttribute.DOMAIN_NAME.toLowerCase(), cname );
+        return map;
+    }
+
+
+    protected ResourceRecordEncoder getEncoder()
+    {
+        return new CanonicalNameRecordEncoder();
+    }
+
+
+    protected void putExpectedResourceData( ByteBuffer expectedData )
+    {
+        expectedData.put( ( byte ) 18 );
+        expectedData.put( ( byte ) cnameParts[0].length() ); // 1
+        expectedData.put( cnameParts[0].getBytes() ); // + 5
+        expectedData.put( ( byte ) cnameParts[1].length() ); // + 1
+        expectedData.put( cnameParts[1].getBytes() ); // + 6
+        expectedData.put( ( byte ) cnameParts[2].length() ); // + 1
+        expectedData.put( cnameParts[2].getBytes() ); // + 3
+        expectedData.put( ( byte ) 0x00 ); // + 1 = 18
+    }
+}
diff --git a/old_trunk/protocol-dns/src/test/java/org/apache/directory/server/dns/io/encoder/MailExchangeRecordEncoderTest.java b/old_trunk/protocol-dns/src/test/java/org/apache/directory/server/dns/io/encoder/MailExchangeRecordEncoderTest.java
new file mode 100644
index 0000000..80c7b67
--- /dev/null
+++ b/old_trunk/protocol-dns/src/test/java/org/apache/directory/server/dns/io/encoder/MailExchangeRecordEncoderTest.java
@@ -0,0 +1,71 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.io.encoder;
+
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.directory.server.dns.store.DnsAttribute;
+import org.apache.mina.common.ByteBuffer;
+
+
+/**
+ * Tests for the MX record encoder.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 501160 $, $Date: 2007-01-29 12:41:33 -0700 (Mon, 29 Jan 2007) $
+ */
+public class MailExchangeRecordEncoderTest extends AbstractResourceRecordEncoderTest
+{
+    String mxPreference = "10";
+    String mxHost = "mail.apache.org";
+    String[] mxParts = mxHost.split( "\\." );
+
+
+    protected Map getAttributes()
+    {
+        Map map = new HashMap();
+        map.put( DnsAttribute.MX_PREFERENCE.toLowerCase(), mxPreference );
+        map.put( DnsAttribute.DOMAIN_NAME.toLowerCase(), mxHost );
+        return map;
+    }
+
+
+    protected ResourceRecordEncoder getEncoder()
+    {
+        return new MailExchangeRecordEncoder();
+    }
+
+
+    protected void putExpectedResourceData( ByteBuffer expectedData )
+    {
+        expectedData.put( ( byte ) 20 );
+        expectedData.putShort( Short.parseShort( mxPreference ) );
+        expectedData.put( ( byte ) mxParts[0].length() ); // 1
+        expectedData.put( mxParts[0].getBytes() ); // + 4
+        expectedData.put( ( byte ) mxParts[1].length() ); // + 1
+        expectedData.put( mxParts[1].getBytes() ); // + 6
+        expectedData.put( ( byte ) mxParts[2].length() ); // + 1
+        expectedData.put( mxParts[2].getBytes() ); // + 3
+        expectedData.put( ( byte ) 0x00 ); // + 1 = 17
+    }
+}
diff --git a/old_trunk/protocol-dns/src/test/java/org/apache/directory/server/dns/io/encoder/NameServerRecordEncoderTest.java b/old_trunk/protocol-dns/src/test/java/org/apache/directory/server/dns/io/encoder/NameServerRecordEncoderTest.java
new file mode 100644
index 0000000..0379cf0
--- /dev/null
+++ b/old_trunk/protocol-dns/src/test/java/org/apache/directory/server/dns/io/encoder/NameServerRecordEncoderTest.java
@@ -0,0 +1,70 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.io.encoder;
+
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.directory.server.dns.store.DnsAttribute;
+import org.apache.mina.common.ByteBuffer;
+
+
+/**
+ * Tests for the NS record encoder.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 501160 $, $Date: 2007-01-29 12:41:33 -0700 (Mon, 29 Jan 2007) $
+ */
+public class NameServerRecordEncoderTest extends AbstractResourceRecordEncoderTest
+{
+    String nsName = "ns1.eu.bitnames.com";
+    String[] nsParts = nsName.split( "\\." );
+
+
+    protected Map getAttributes()
+    {
+        Map map = new HashMap();
+        map.put( DnsAttribute.DOMAIN_NAME.toLowerCase(), nsName );
+        return map;
+    }
+
+
+    protected ResourceRecordEncoder getEncoder()
+    {
+        return new NameServerRecordEncoder();
+    }
+
+
+    protected void putExpectedResourceData( ByteBuffer expectedData )
+    {
+        expectedData.put( ( byte ) 19 );
+        expectedData.put( ( byte ) nsParts[0].length() ); // 1
+        expectedData.put( nsParts[0].getBytes() ); // + 3
+        expectedData.put( ( byte ) nsParts[1].length() ); // + 1
+        expectedData.put( nsParts[1].getBytes() ); // + 2
+        expectedData.put( ( byte ) nsParts[2].length() ); // + 1
+        expectedData.put( nsParts[2].getBytes() ); // + 7
+        expectedData.put( ( byte ) nsParts[3].length() ); // + 1
+        expectedData.put( nsParts[3].getBytes() ); // + 3
+        expectedData.put( ( byte ) 0x00 ); // + 1 = 19
+    }
+}
diff --git a/old_trunk/protocol-dns/src/test/java/org/apache/directory/server/dns/io/encoder/PointerRecordEncoderTest.java b/old_trunk/protocol-dns/src/test/java/org/apache/directory/server/dns/io/encoder/PointerRecordEncoderTest.java
new file mode 100644
index 0000000..98035d5
--- /dev/null
+++ b/old_trunk/protocol-dns/src/test/java/org/apache/directory/server/dns/io/encoder/PointerRecordEncoderTest.java
@@ -0,0 +1,68 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.io.encoder;
+
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.directory.server.dns.store.DnsAttribute;
+import org.apache.mina.common.ByteBuffer;
+
+
+/**
+ * Tests for the PTR record encoder.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 501160 $, $Date: 2007-01-29 12:41:33 -0700 (Mon, 29 Jan 2007) $
+ */
+public class PointerRecordEncoderTest extends AbstractResourceRecordEncoderTest
+{
+    String ptrName = "ptr.apache.org";
+    String[] ptrParts = ptrName.split( "\\." );
+
+
+    protected Map getAttributes()
+    {
+        Map map = new HashMap();
+        map.put( DnsAttribute.DOMAIN_NAME.toLowerCase(), ptrName );
+        return map;
+    }
+
+
+    protected ResourceRecordEncoder getEncoder()
+    {
+        return new PointerRecordEncoder();
+    }
+
+
+    protected void putExpectedResourceData( ByteBuffer expectedData )
+    {
+        expectedData.put( ( byte ) 15 );
+        expectedData.put( ( byte ) ptrParts[0].length() ); // 1
+        expectedData.put( ptrParts[0].getBytes() ); // + 3
+        expectedData.put( ( byte ) ptrParts[1].length() ); // + 1
+        expectedData.put( ptrParts[1].getBytes() ); // + 6
+        expectedData.put( ( byte ) ptrParts[2].length() ); // + 1
+        expectedData.put( ptrParts[2].getBytes() ); // + 3
+        expectedData.put( ( byte ) 0x00 ); // + 1 = 15
+    }
+}
diff --git a/old_trunk/protocol-dns/src/test/java/org/apache/directory/server/dns/io/encoder/QuestionRecordEncoderTest.java b/old_trunk/protocol-dns/src/test/java/org/apache/directory/server/dns/io/encoder/QuestionRecordEncoderTest.java
new file mode 100644
index 0000000..7f0eec2
--- /dev/null
+++ b/old_trunk/protocol-dns/src/test/java/org/apache/directory/server/dns/io/encoder/QuestionRecordEncoderTest.java
@@ -0,0 +1,75 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.io.encoder;
+
+
+import junit.framework.TestCase;
+
+import org.apache.directory.server.dns.messages.QuestionRecord;
+import org.apache.directory.server.dns.messages.RecordClass;
+import org.apache.directory.server.dns.messages.RecordType;
+import org.apache.mina.common.ByteBuffer;
+
+
+/**
+ * Tests for the Question record encoder.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 501160 $, $Date: 2007-01-29 12:41:33 -0700 (Mon, 29 Jan 2007) $
+ */
+public class QuestionRecordEncoderTest extends TestCase
+{
+    ByteBuffer expectedData;
+
+    QuestionRecordEncoder encoder;
+
+    String name = "www.apache.org";
+    String[] nameParts = name.split( "\\." );
+    RecordType type = RecordType.A;
+    RecordClass rClass = RecordClass.IN;
+
+    QuestionRecord record = new QuestionRecord( name, type, rClass );
+
+
+    public void setUp()
+    {
+        encoder = new QuestionRecordEncoder();
+
+        expectedData = ByteBuffer.allocate( 128 );
+        expectedData.put( ( byte ) nameParts[0].length() ); // 1
+        expectedData.put( nameParts[0].getBytes() ); // + 3
+        expectedData.put( ( byte ) nameParts[1].length() ); // + 1
+        expectedData.put( nameParts[1].getBytes() ); // + 6
+        expectedData.put( ( byte ) nameParts[2].length() ); // + 1
+        expectedData.put( nameParts[2].getBytes() ); // + 3
+        expectedData.put( ( byte ) 0x00 ); // + 1 = 16
+        expectedData.putShort( type.convert() );
+        expectedData.putShort( rClass.convert() );
+    }
+
+
+    public void testEncode()
+    {
+        ByteBuffer out = ByteBuffer.allocate( 128 );
+        encoder.put( out, record );
+        assertEquals( expectedData, out );
+    }
+}
diff --git a/old_trunk/protocol-dns/src/test/java/org/apache/directory/server/dns/io/encoder/ServerSelectionRecordEncoderTest.java b/old_trunk/protocol-dns/src/test/java/org/apache/directory/server/dns/io/encoder/ServerSelectionRecordEncoderTest.java
new file mode 100644
index 0000000..c70061a
--- /dev/null
+++ b/old_trunk/protocol-dns/src/test/java/org/apache/directory/server/dns/io/encoder/ServerSelectionRecordEncoderTest.java
@@ -0,0 +1,80 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.io.encoder;
+
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.directory.server.dns.store.DnsAttribute;
+import org.apache.mina.common.ByteBuffer;
+
+
+/**
+ * Tests for the SRV record encoder.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 501160 $, $Date: 2007-01-29 12:41:33 -0700 (Mon, 29 Jan 2007) $
+ */
+public class ServerSelectionRecordEncoderTest extends AbstractResourceRecordEncoderTest
+{
+    String priority = "0";
+    String weight = "3";
+    String port = "9";
+    String srvName = "srv.apache.org";
+    String[] srvParts = srvName.split( "\\." );
+
+
+    @Override
+    protected Map getAttributes()
+    {
+        Map map = new HashMap();
+        map.put( DnsAttribute.SERVICE_PRIORITY.toLowerCase(), priority );
+        map.put( DnsAttribute.SERVICE_WEIGHT.toLowerCase(), weight );
+        map.put( DnsAttribute.SERVICE_PORT.toLowerCase(), port );
+        map.put( DnsAttribute.DOMAIN_NAME.toLowerCase(), srvName );
+        return map;
+    }
+
+
+    @Override
+    protected ResourceRecordEncoder getEncoder()
+    {
+        return new ServerSelectionRecordEncoder();
+    }
+
+
+    @Override
+    protected void putExpectedResourceData( ByteBuffer expectedData )
+    {
+        expectedData.put( ( byte ) 22 );
+        expectedData.putShort( Short.parseShort( priority ) );
+        expectedData.putShort( Short.parseShort( weight ) );
+        expectedData.putShort( Short.parseShort( port ) );
+        expectedData.put( ( byte ) srvParts[0].length() ); // 1
+        expectedData.put( srvParts[0].getBytes() ); // + 3
+        expectedData.put( ( byte ) srvParts[1].length() ); // + 1
+        expectedData.put( srvParts[1].getBytes() ); // + 6
+        expectedData.put( ( byte ) srvParts[2].length() ); // + 1
+        expectedData.put( srvParts[2].getBytes() ); // + 3
+        expectedData.put( ( byte ) 0x00 ); // + 1 = 16
+    }
+}
diff --git a/old_trunk/protocol-dns/src/test/java/org/apache/directory/server/dns/io/encoder/StartOfAuthorityRecordEncoderTest.java b/old_trunk/protocol-dns/src/test/java/org/apache/directory/server/dns/io/encoder/StartOfAuthorityRecordEncoderTest.java
new file mode 100644
index 0000000..2364b98
--- /dev/null
+++ b/old_trunk/protocol-dns/src/test/java/org/apache/directory/server/dns/io/encoder/StartOfAuthorityRecordEncoderTest.java
@@ -0,0 +1,93 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.io.encoder;
+
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.directory.server.dns.store.DnsAttribute;
+import org.apache.mina.common.ByteBuffer;
+
+
+/**
+ * Tests for the SOA record encoder.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 501160 $, $Date: 2007-01-29 12:41:33 -0700 (Mon, 29 Jan 2007) $
+ */
+public class StartOfAuthorityRecordEncoderTest extends AbstractResourceRecordEncoderTest
+{
+    String mName = "ns.hyperreal.org";
+    String[] mNameParts = mName.split( "\\." );
+    String rName = "root.hyperreal.org";
+    String[] rNameParts = rName.split( "\\." );
+    String serial = "2007013001";
+    String refresh = "3600";
+    String retry = "900";
+    String expire = "604800";
+    String minimum = "3600";
+
+
+    protected Map getAttributes()
+    {
+        Map map = new HashMap();
+        map.put( DnsAttribute.SOA_M_NAME.toLowerCase(), mName );
+        map.put( DnsAttribute.SOA_R_NAME.toLowerCase(), rName );
+        map.put( DnsAttribute.SOA_SERIAL.toLowerCase(), serial );
+        map.put( DnsAttribute.SOA_REFRESH.toLowerCase(), refresh );
+        map.put( DnsAttribute.SOA_RETRY.toLowerCase(), retry );
+        map.put( DnsAttribute.SOA_EXPIRE.toLowerCase(), expire );
+        map.put( DnsAttribute.SOA_MINIMUM.toLowerCase(), minimum );
+        return map;
+    }
+
+
+    protected ResourceRecordEncoder getEncoder()
+    {
+        return new StartOfAuthorityRecordEncoder();
+    }
+
+
+    protected void putExpectedResourceData( ByteBuffer expectedData )
+    {
+        expectedData.put( ( byte ) 60 ); // 1 + 18 + 1 + 20 + 4 + 4 + 4 + 4 + 4
+        expectedData.put( ( byte ) mNameParts[0].length() ); // 1
+        expectedData.put( mNameParts[0].getBytes() ); // + 2
+        expectedData.put( ( byte ) mNameParts[1].length() ); // + 1
+        expectedData.put( mNameParts[1].getBytes() ); // + 9
+        expectedData.put( ( byte ) mNameParts[2].length() ); // + 1
+        expectedData.put( mNameParts[2].getBytes() ); // + 3
+        expectedData.put( ( byte ) 0x00 ); // + 1 = 18
+        expectedData.put( ( byte ) rNameParts[0].length() ); // 1
+        expectedData.put( rNameParts[0].getBytes() ); // + 4
+        expectedData.put( ( byte ) rNameParts[1].length() ); // + 1
+        expectedData.put( rNameParts[1].getBytes() ); // + 9
+        expectedData.put( ( byte ) rNameParts[2].length() ); // + 1
+        expectedData.put( rNameParts[2].getBytes() ); // + 3
+        expectedData.put( ( byte ) 0x00 ); // + 1 = 20
+        expectedData.putInt( ( int ) Long.parseLong( serial ) );
+        expectedData.putInt( Integer.parseInt( refresh ) );
+        expectedData.putInt( Integer.parseInt( retry ) );
+        expectedData.putInt( Integer.parseInt( expire ) );
+        expectedData.putInt( ( int ) Long.parseLong( minimum ) );
+    }
+}
diff --git a/old_trunk/protocol-dns/src/test/java/org/apache/directory/server/dns/io/encoder/TextRecordEncoderTest.java b/old_trunk/protocol-dns/src/test/java/org/apache/directory/server/dns/io/encoder/TextRecordEncoderTest.java
new file mode 100644
index 0000000..e1bb899
--- /dev/null
+++ b/old_trunk/protocol-dns/src/test/java/org/apache/directory/server/dns/io/encoder/TextRecordEncoderTest.java
@@ -0,0 +1,62 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.dns.io.encoder;
+
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.directory.server.dns.store.DnsAttribute;
+import org.apache.mina.common.ByteBuffer;
+
+
+/**
+ * Tests for the TXT record encoder.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 501160 $, $Date: 2007-01-29 12:41:33 -0700 (Mon, 29 Jan 2007) $
+ */
+public class TextRecordEncoderTest extends AbstractResourceRecordEncoderTest
+{
+    String characterString = "This is a string";
+
+
+    protected Map getAttributes()
+    {
+        Map map = new HashMap();
+        map.put( DnsAttribute.CHARACTER_STRING.toLowerCase(), characterString );
+        return map;
+    }
+
+
+    protected ResourceRecordEncoder getEncoder()
+    {
+        return new TextRecordEncoder();
+    }
+
+
+    protected void putExpectedResourceData( ByteBuffer expectedData )
+    {
+        expectedData.put( ( byte ) ( characterString.length() + 1 ) );
+        expectedData.put( ( byte ) characterString.length() );
+        expectedData.put( characterString.getBytes() );
+    }
+}
diff --git a/old_trunk/protocol-dns/src/test/java/org/apache/directory/server/dns/messages/RecordClassTest.java b/old_trunk/protocol-dns/src/test/java/org/apache/directory/server/dns/messages/RecordClassTest.java
new file mode 100644
index 0000000..4b020e5
--- /dev/null
+++ b/old_trunk/protocol-dns/src/test/java/org/apache/directory/server/dns/messages/RecordClassTest.java
@@ -0,0 +1,43 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.dns.messages;
+
+
+import junit.framework.TestCase;
+
+
+/**
+ * Test case for the RecordClass class.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class RecordClassTest extends TestCase
+{
+    /**
+     * Tests conversion of ordinals to RecordClass enums.
+     */
+    public void testRecordClassConversion()
+    {
+        assertEquals( RecordClass.IN, RecordClass.convert( ( short ) 1 ) );
+        assertEquals( RecordClass.NONE, RecordClass.convert( ( short ) 254 ) );
+        assertEquals( RecordClass.ANY, RecordClass.convert( ( short ) 255 ) );
+    }
+}
diff --git a/old_trunk/protocol-dns/src/test/resources/log4j.properties b/old_trunk/protocol-dns/src/test/resources/log4j.properties
new file mode 100644
index 0000000..facb3e6
--- /dev/null
+++ b/old_trunk/protocol-dns/src/test/resources/log4j.properties
@@ -0,0 +1,21 @@
+#############################################################################
+#    Licensed to the Apache Software Foundation (ASF) under one or more
+#    contributor license agreements.  See the NOTICE file distributed with
+#    this work for additional information regarding copyright ownership.
+#    The ASF licenses this file to You 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.
+#############################################################################
+log4j.rootCategory=OFF, stdout
+
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=[%d{HH:mm:ss}] %p [%c] - %m%n
diff --git a/old_trunk/protocol-dns/src/test/resources/org/apache/directory/server/dns/protocol/DNS-QUERY.pdu b/old_trunk/protocol-dns/src/test/resources/org/apache/directory/server/dns/protocol/DNS-QUERY.pdu
new file mode 100644
index 0000000..f898da7
--- /dev/null
+++ b/old_trunk/protocol-dns/src/test/resources/org/apache/directory/server/dns/protocol/DNS-QUERY.pdu
Binary files differ
diff --git a/old_trunk/protocol-dns/src/test/resources/org/apache/directory/server/dns/protocol/DNS-RESPONSE.pdu b/old_trunk/protocol-dns/src/test/resources/org/apache/directory/server/dns/protocol/DNS-RESPONSE.pdu
new file mode 100644
index 0000000..675ba48
--- /dev/null
+++ b/old_trunk/protocol-dns/src/test/resources/org/apache/directory/server/dns/protocol/DNS-RESPONSE.pdu
Binary files differ
diff --git a/old_trunk/protocol-dns/src/test/resources/org/apache/directory/server/dns/protocol/DNS-TRAFFIC.libpcap b/old_trunk/protocol-dns/src/test/resources/org/apache/directory/server/dns/protocol/DNS-TRAFFIC.libpcap
new file mode 100644
index 0000000..1f64102
--- /dev/null
+++ b/old_trunk/protocol-dns/src/test/resources/org/apache/directory/server/dns/protocol/DNS-TRAFFIC.libpcap
Binary files differ
diff --git a/old_trunk/protocol-dns/src/test/resources/org/apache/directory/server/dns/protocol/MX-QUERY.pdu b/old_trunk/protocol-dns/src/test/resources/org/apache/directory/server/dns/protocol/MX-QUERY.pdu
new file mode 100644
index 0000000..080d4d9
--- /dev/null
+++ b/old_trunk/protocol-dns/src/test/resources/org/apache/directory/server/dns/protocol/MX-QUERY.pdu
Binary files differ
diff --git a/old_trunk/protocol-dns/src/test/resources/org/apache/directory/server/dns/protocol/MX-RESPONSE.pdu b/old_trunk/protocol-dns/src/test/resources/org/apache/directory/server/dns/protocol/MX-RESPONSE.pdu
new file mode 100644
index 0000000..5b240d2
--- /dev/null
+++ b/old_trunk/protocol-dns/src/test/resources/org/apache/directory/server/dns/protocol/MX-RESPONSE.pdu
Binary files differ
diff --git a/old_trunk/protocol-dns/src/test/resources/org/apache/directory/server/dns/protocol/MX-TRAFFIC.libpcap b/old_trunk/protocol-dns/src/test/resources/org/apache/directory/server/dns/protocol/MX-TRAFFIC.libpcap
new file mode 100644
index 0000000..0ff7d8d
--- /dev/null
+++ b/old_trunk/protocol-dns/src/test/resources/org/apache/directory/server/dns/protocol/MX-TRAFFIC.libpcap
Binary files differ
diff --git a/old_trunk/protocol-kerberos/pom.xml b/old_trunk/protocol-kerberos/pom.xml
new file mode 100644
index 0000000..e3c8d68
--- /dev/null
+++ b/old_trunk/protocol-kerberos/pom.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you 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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.directory.server</groupId>
+    <artifactId>apacheds-parent</artifactId>
+    <version>1.5.4-SNAPSHOT</version>
+  </parent>
+  <artifactId>apacheds-protocol-kerberos</artifactId>
+  <name>ApacheDS Protocol Kerberos</name>
+
+  <description>
+    The Kerberos Protocol Provider for ApacheDS
+  </description>
+
+  <packaging>jar</packaging>  
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-kerberos-shared</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-server-unit</artifactId>
+      <version>${pom.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-bootstrap-partition</artifactId>
+      <version>${pom.version}</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <configuration>
+          <excludes>
+            <exclude>**/TestUtils.java</exclude>
+          </excludes>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-source-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>attach-sources</id>
+            <phase>verify</phase>
+            <goals>
+              <goal>jar</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+
+</project>
+
diff --git a/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/kdc/KdcContext.java b/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/kdc/KdcContext.java
new file mode 100644
index 0000000..f445471
--- /dev/null
+++ b/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/kdc/KdcContext.java
@@ -0,0 +1,177 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.kdc;
+
+
+import java.net.InetAddress;
+
+import org.apache.directory.server.kerberos.shared.crypto.encryption.CipherTextHandler;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
+import org.apache.directory.server.kerberos.shared.messages.KdcRequest;
+import org.apache.directory.server.kerberos.shared.messages.KerberosMessage;
+import org.apache.directory.server.kerberos.shared.store.PrincipalStore;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class KdcContext
+{
+    private static final long serialVersionUID = 6490030984626825108L;
+
+    private KdcServer config;
+    private PrincipalStore store;
+    private KdcRequest request;
+    private KerberosMessage reply;
+    private InetAddress clientAddress;
+    private CipherTextHandler cipherTextHandler;
+    private EncryptionType encryptionType;
+
+
+    /**
+     * @return Returns the config.
+     */
+    public KdcServer getConfig()
+    {
+        return config;
+    }
+
+
+    /**
+     * @param config The config to set.
+     */
+    public void setConfig( KdcServer config )
+    {
+        this.config = config;
+    }
+
+
+    /**
+     * @return Returns the store.
+     */
+    public PrincipalStore getStore()
+    {
+        return store;
+    }
+
+
+    /**
+     * @param store The store to set.
+     */
+    public void setStore( PrincipalStore store )
+    {
+        this.store = store;
+    }
+
+
+    /**
+     * @return Returns the request.
+     */
+    public KdcRequest getRequest()
+    {
+        return request;
+    }
+
+
+    /**
+     * @param request The request to set.
+     */
+    public void setRequest( KdcRequest request )
+    {
+        this.request = request;
+    }
+
+
+    /**
+     * @return Returns the reply.
+     */
+    public KerberosMessage getReply()
+    {
+        return reply;
+    }
+
+
+    /**
+     * @param reply The reply to set.
+     */
+    public void setReply( KerberosMessage reply )
+    {
+        this.reply = reply;
+    }
+
+
+    /**
+     * @return Returns the clientAddress.
+     */
+    public InetAddress getClientAddress()
+    {
+        return clientAddress;
+    }
+
+
+    /**
+     * @param clientAddress The clientAddress to set.
+     */
+    public void setClientAddress( InetAddress clientAddress )
+    {
+        this.clientAddress = clientAddress;
+    }
+
+
+    /**
+     * @return Returns the {@link CipherTextHandler}.
+     */
+    public CipherTextHandler getCipherTextHandler()
+    {
+        return cipherTextHandler;
+    }
+
+
+    /**
+     * @param cipherTextHandler The {@link CipherTextHandler} to set.
+     */
+    public void setCipherTextHandler( CipherTextHandler cipherTextHandler )
+    {
+        this.cipherTextHandler = cipherTextHandler;
+    }
+
+
+    /**
+     * Returns the encryption type to use for this session.
+     *
+     * @return The encryption type.
+     */
+    public EncryptionType getEncryptionType()
+    {
+        return encryptionType;
+    }
+
+
+    /**
+     * Sets the encryption type to use for this session.
+     *
+     * @param encryptionType The encryption type to set.
+     */
+    public void setEncryptionType( EncryptionType encryptionType )
+    {
+        this.encryptionType = encryptionType;
+    }
+}
diff --git a/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/kdc/KdcServer.java b/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/kdc/KdcServer.java
new file mode 100644
index 0000000..499e82d
--- /dev/null
+++ b/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/kdc/KdcServer.java
@@ -0,0 +1,485 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.kdc;
+
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.apache.directory.server.constants.ServerDNConstants;
+import org.apache.directory.server.kerberos.protocol.KerberosProtocolHandler;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
+import org.apache.directory.server.kerberos.shared.store.JndiPrincipalStoreImpl;
+import org.apache.directory.server.kerberos.shared.store.PrincipalStore;
+import org.apache.directory.server.protocol.shared.DirectoryBackedService;
+import org.apache.mina.transport.socket.nio.DatagramAcceptorConfig;
+import org.apache.mina.transport.socket.nio.SocketAcceptorConfig;
+
+
+/**
+ * Contains the configuration parameters for the Kerberos protocol provider.
+ *
+ * @org.apache.xbean.XBean
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class KdcServer extends DirectoryBackedService
+{
+    private static final long serialVersionUID = 522567370475574165L;
+
+    /** The default kdc port */
+    private static final int DEFAULT_IP_PORT = 88;
+
+    /** The default kdc service pid */
+    private static final String DEFAULT_PID = "org.apache.directory.server.kerberos";
+
+    /** The default kdc service name */
+    private static final String DEFAULT_NAME = "ApacheDS Kerberos Service";
+
+    /** The default kdc service principal */
+    private static final String DEFAULT_PRINCIPAL = "krbtgt/EXAMPLE.COM@EXAMPLE.COM";
+
+    /** The default kdc realm */
+    private static final String DEFAULT_REALM = "EXAMPLE.COM";
+
+    /** The default allowable clockskew */
+    private static final long DEFAULT_ALLOWABLE_CLOCKSKEW = 5 * 60000;
+
+    /** The default encryption types */
+    private static final String[] DEFAULT_ENCRYPTION_TYPES = new String[]
+        { "des-cbc-md5" };
+
+    /** The default for allowing empty addresses */
+    private static final boolean DEFAULT_EMPTY_ADDRESSES_ALLOWED = true;
+
+    /** The default for requiring encrypted timestamps */
+    private static final boolean DEFAULT_PA_ENC_TIMESTAMP_REQUIRED = true;
+
+    /** The default for the maximum ticket lifetime */
+    private static final int DEFAULT_TGS_MAXIMUM_TICKET_LIFETIME = 60000 * 1440;
+
+    /** The default for the maximum renewable lifetime */
+    private static final int DEFAULT_TGS_MAXIMUM_RENEWABLE_LIFETIME = 60000 * 10080;
+
+    /** The default for allowing forwardable tickets */
+    private static final boolean DEFAULT_TGS_FORWARDABLE_ALLOWED = true;
+
+    /** The default for allowing proxiable tickets */
+    private static final boolean DEFAULT_TGS_PROXIABLE_ALLOWED = true;
+
+    /** The default for allowing postdated tickets */
+    private static final boolean DEFAULT_TGS_POSTDATED_ALLOWED = true;
+
+    /** The default for allowing renewable tickets */
+    private static final boolean DEFAULT_TGS_RENEWABLE_ALLOWED = true;
+
+    /** The default for verifying the body checksum */
+    private static final boolean DEFAULT_VERIFY_BODY_CHECKSUM = true;
+
+    /** The encryption types. */
+    private Set<EncryptionType> encryptionTypes;
+
+    /** The primary realm */
+    private String primaryRealm = DEFAULT_REALM;
+
+    /** The service principal name. */
+    private String servicePrincipal = DEFAULT_PRINCIPAL;
+
+    /** The allowable clock skew. */
+    private long allowableClockSkew = DEFAULT_ALLOWABLE_CLOCKSKEW;
+
+    /** Whether pre-authentication by encrypted timestamp is required. */
+    private boolean isPaEncTimestampRequired = DEFAULT_PA_ENC_TIMESTAMP_REQUIRED;
+
+    /** The maximum ticket lifetime. */
+    private long maximumTicketLifetime = DEFAULT_TGS_MAXIMUM_TICKET_LIFETIME;
+
+    /** The maximum renewable lifetime. */
+    private long maximumRenewableLifetime = DEFAULT_TGS_MAXIMUM_RENEWABLE_LIFETIME;
+
+    /** Whether empty addresses are allowed. */
+    private boolean isEmptyAddressesAllowed = DEFAULT_EMPTY_ADDRESSES_ALLOWED;
+
+    /** Whether forwardable addresses are allowed. */
+    private boolean isForwardableAllowed = DEFAULT_TGS_FORWARDABLE_ALLOWED;
+
+    /** Whether proxiable addresses are allowed. */
+    private boolean isProxiableAllowed = DEFAULT_TGS_PROXIABLE_ALLOWED;
+
+    /** Whether postdated tickets are allowed. */
+    private boolean isPostdatedAllowed = DEFAULT_TGS_POSTDATED_ALLOWED;
+
+    /** Whether renewable tickets are allowed. */
+    private boolean isRenewableAllowed = DEFAULT_TGS_RENEWABLE_ALLOWED;
+
+    /** Whether to verify the body checksum. */
+    private boolean isBodyChecksumVerified = DEFAULT_VERIFY_BODY_CHECKSUM;
+
+
+    /**
+     * Creates a new instance of KdcConfiguration.
+     */
+    public KdcServer()
+    {
+        super.setServiceName( DEFAULT_NAME );
+        super.setIpPort( DEFAULT_IP_PORT );
+        super.setServiceId( DEFAULT_PID );
+        super.setSearchBaseDn( ServerDNConstants.USER_EXAMPLE_COM_DN );
+
+        prepareEncryptionTypes();
+    }
+
+
+    /**
+     * Returns the allowable clock skew.
+     *
+     * @return The allowable clock skew.
+     */
+    public long getAllowableClockSkew()
+    {
+        return allowableClockSkew;
+    }
+
+
+    /**
+     * @return the isEmptyAddressesAllowed
+     */
+    public boolean isEmptyAddressesAllowed()
+    {
+        return isEmptyAddressesAllowed;
+    }
+
+
+    /**
+     * @return the isForwardableAllowed
+     */
+    public boolean isForwardableAllowed()
+    {
+        return isForwardableAllowed;
+    }
+
+
+    /**
+     * @return the isPostdatedAllowed
+     */
+    public boolean isPostdatedAllowed()
+    {
+        return isPostdatedAllowed;
+    }
+
+
+    /**
+     * @return the isProxiableAllowed
+     */
+    public boolean isProxiableAllowed()
+    {
+        return isProxiableAllowed;
+    }
+
+
+    /**
+     * @return the isRenewableAllowed
+     */
+    public boolean isRenewableAllowed()
+    {
+        return isRenewableAllowed;
+    }
+
+
+    /**
+     * @return the maximumRenewableLifetime
+     */
+    public long getMaximumRenewableLifetime()
+    {
+        return maximumRenewableLifetime;
+    }
+
+
+    /**
+     * @return the maximumTicketLifetime
+     */
+    public long getMaximumTicketLifetime()
+    {
+        return maximumTicketLifetime;
+    }
+
+
+    /**
+     * @param allowableClockSkew the allowableClockSkew to set
+     */
+    public void setAllowableClockSkew( long allowableClockSkew )
+    {
+        this.allowableClockSkew = allowableClockSkew;
+    }
+
+
+    /**
+     * Initialize the encryptionTypes set
+     * 
+     * @param encryptionTypes the encryptionTypes to set
+     */
+    public void setEncryptionTypes( EncryptionType[] encryptionTypes )
+    {
+        if ( encryptionTypes != null )
+        {
+            this.encryptionTypes.clear();
+            
+            for ( EncryptionType encryptionType:encryptionTypes )
+            {
+                this.encryptionTypes.add( encryptionType );
+            }
+        }
+    }
+
+
+    /**
+     * Initialize the encryptionTypes set
+     * 
+     * @param encryptionTypes the encryptionTypes to set
+     */
+    public void setEncryptionTypes( Set<EncryptionType> encryptionTypes )
+    {
+        this.encryptionTypes = encryptionTypes;
+    }
+
+
+    /**
+     * @param isEmptyAddressesAllowed the isEmptyAddressesAllowed to set
+     */
+    public void setEmptyAddressesAllowed( boolean isEmptyAddressesAllowed )
+    {
+        this.isEmptyAddressesAllowed = isEmptyAddressesAllowed;
+    }
+
+
+    /**
+     * @param isForwardableAllowed the isForwardableAllowed to set
+     */
+    public void setForwardableAllowed( boolean isForwardableAllowed )
+    {
+        this.isForwardableAllowed = isForwardableAllowed;
+    }
+
+
+    /**
+     * @param isPaEncTimestampRequired the isPaEncTimestampRequired to set
+     */
+    public void setPaEncTimestampRequired( boolean isPaEncTimestampRequired )
+    {
+        this.isPaEncTimestampRequired = isPaEncTimestampRequired;
+    }
+
+
+    /**
+     * @param isPostdatedAllowed the isPostdatedAllowed to set
+     */
+    public void setPostdatedAllowed( boolean isPostdatedAllowed )
+    {
+        this.isPostdatedAllowed = isPostdatedAllowed;
+    }
+
+
+    /**
+     * @param isProxiableAllowed the isProxiableAllowed to set
+     */
+    public void setProxiableAllowed( boolean isProxiableAllowed )
+    {
+        this.isProxiableAllowed = isProxiableAllowed;
+    }
+
+
+    /**
+     * @param isRenewableAllowed the isRenewableAllowed to set
+     */
+    public void setRenewableAllowed( boolean isRenewableAllowed )
+    {
+        this.isRenewableAllowed = isRenewableAllowed;
+    }
+
+
+    /**
+     * @param kdcPrincipal the kdcPrincipal to set
+     */
+    public void setKdcPrincipal( String kdcPrincipal )
+    {
+        this.servicePrincipal = kdcPrincipal;
+    }
+
+
+    /**
+     * @param maximumRenewableLifetime the maximumRenewableLifetime to set
+     */
+    public void setMaximumRenewableLifetime( long maximumRenewableLifetime )
+    {
+        this.maximumRenewableLifetime = maximumRenewableLifetime;
+    }
+
+
+    /**
+     * @param maximumTicketLifetime the maximumTicketLifetime to set
+     */
+    public void setMaximumTicketLifetime( long maximumTicketLifetime )
+    {
+        this.maximumTicketLifetime = maximumTicketLifetime;
+    }
+
+
+    /**
+     * @param primaryRealm the primaryRealm to set
+     */
+    public void setPrimaryRealm( String primaryRealm )
+    {
+        this.primaryRealm = primaryRealm;
+    }
+
+
+    /**
+     * Returns the primary realm.
+     *
+     * @return The primary realm.
+     */
+    public String getPrimaryRealm()
+    {
+        return primaryRealm;
+    }
+
+
+    /**
+     * Returns the service principal for this KDC service.
+     *
+     * @return The service principal for this KDC service.
+     */
+    public KerberosPrincipal getServicePrincipal()
+    {
+        return new KerberosPrincipal( servicePrincipal );
+    }
+
+
+    /**
+     * Returns the encryption types.
+     *
+     * @return The encryption types.
+     */
+    public Set<EncryptionType> getEncryptionTypes()
+    {
+        return encryptionTypes;
+    }
+
+
+    /**
+     * Returns whether pre-authentication by encrypted timestamp is required.
+     *
+     * @return Whether pre-authentication by encrypted timestamp is required.
+     */
+    public boolean isPaEncTimestampRequired()
+    {
+        return isPaEncTimestampRequired;
+    }
+
+
+    /**
+     * @return the isBodyChecksumVerified
+     */
+    public boolean isBodyChecksumVerified()
+    {
+        return isBodyChecksumVerified;
+    }
+
+
+    /**
+     * @param isBodyChecksumVerified the isBodyChecksumVerified to set
+     */
+    public void setBodyChecksumVerified( boolean isBodyChecksumVerified )
+    {
+        this.isBodyChecksumVerified = isBodyChecksumVerified;
+    }
+
+
+    /**
+     * @throws IOException if we cannot bind to the sockets
+     */
+    public void start() throws IOException
+    {
+        PrincipalStore store;
+
+        if ( isCatelogBased() )
+        {
+            store = new JndiPrincipalStoreImpl( getSearchBaseDn(), null, getDirectoryService() );
+        }
+        else
+        {
+            store = new JndiPrincipalStoreImpl( null, getSearchBaseDn(), getDirectoryService() );
+        }
+
+        if ( getDatagramAcceptor() != null )
+        {
+            DatagramAcceptorConfig udpConfig = new DatagramAcceptorConfig();
+            getDatagramAcceptor().bind( new InetSocketAddress( getIpPort() ), new KerberosProtocolHandler( this, store ), udpConfig );
+        }
+
+        if ( getSocketAcceptor() != null )
+        {
+            SocketAcceptorConfig tcpConfig = new SocketAcceptorConfig();
+            tcpConfig.setDisconnectOnUnbind( false );
+            tcpConfig.setReuseAddress( true );
+            getSocketAcceptor().bind( new InetSocketAddress( getIpPort() ), new KerberosProtocolHandler( this, store ), tcpConfig );
+        }
+    }
+
+    
+    public void stop()
+    {
+        if ( getDatagramAcceptor() != null )
+        {
+            getDatagramAcceptor().unbind( new InetSocketAddress( getIpPort() ));
+        }
+        if ( getSocketAcceptor() != null )
+        {
+            getSocketAcceptor().unbind( new InetSocketAddress( getIpPort() ));
+        }
+    }
+
+
+    /**
+     * Construct an HashSet containing the default encryption types
+     */
+    private void prepareEncryptionTypes()
+    {
+        String[] encryptionTypeStrings = DEFAULT_ENCRYPTION_TYPES;
+
+        encryptionTypes = new HashSet<EncryptionType>();
+
+        for ( String enc : encryptionTypeStrings )
+        {
+            for ( EncryptionType type : EncryptionType.getEncryptionTypes() )
+            {
+                if ( type.getName().equalsIgnoreCase( enc ) )
+                {
+                    encryptionTypes.add( type );
+                }
+            }
+        }
+    }
+}
diff --git a/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/kdc/authentication/AuthenticationContext.java b/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/kdc/authentication/AuthenticationContext.java
new file mode 100644
index 0000000..e99242f
--- /dev/null
+++ b/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/kdc/authentication/AuthenticationContext.java
@@ -0,0 +1,175 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.kdc.authentication;
+
+
+import org.apache.directory.server.kerberos.kdc.KdcContext;
+import org.apache.directory.server.kerberos.shared.messages.components.Ticket;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+import org.apache.directory.server.kerberos.shared.replay.ReplayCache;
+import org.apache.directory.server.kerberos.shared.store.PrincipalStoreEntry;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class AuthenticationContext extends KdcContext
+{
+    private static final long serialVersionUID = -2249170923251265359L;
+
+    //private Map checksumEngines = new HashMap();
+
+    private Ticket ticket;
+    private EncryptionKey clientKey;
+    private ReplayCache replayCache;
+
+    private PrincipalStoreEntry clientEntry;
+    private PrincipalStoreEntry serverEntry;
+
+    private boolean isPreAuthenticated;
+
+
+    /**
+     * @return Returns the serverEntry.
+     */
+    public PrincipalStoreEntry getServerEntry()
+    {
+        return serverEntry;
+    }
+
+
+    /**
+     * @param serverEntry The serverEntry to set.
+     */
+    public void setServerEntry( PrincipalStoreEntry serverEntry )
+    {
+        this.serverEntry = serverEntry;
+    }
+
+
+    /**
+     * @return Returns the clientEntry.
+     */
+    public PrincipalStoreEntry getClientEntry()
+    {
+        return clientEntry;
+    }
+
+
+    /**
+     * @param clientEntry The clientEntry to set.
+     */
+    public void setClientEntry( PrincipalStoreEntry clientEntry )
+    {
+        this.clientEntry = clientEntry;
+    }
+
+
+    /**
+     * @return Returns the checksumEngines.
+     *
+    public Map getChecksumEngines()
+    {
+        return checksumEngines;
+    }
+    */
+
+    /**
+     * @param checksumEngines The checksumEngines to set.
+     *
+    public void setChecksumEngines( Map checksumEngines )
+    {
+        this.checksumEngines = checksumEngines;
+    }
+    */
+
+
+    /**
+     * @return Returns the replayCache.
+     */
+    public ReplayCache getReplayCache()
+    {
+        return replayCache;
+    }
+
+
+    /**
+     * @param replayCache The replayCache to set.
+     */
+    public void setReplayCache( ReplayCache replayCache )
+    {
+        this.replayCache = replayCache;
+    }
+
+
+    /**
+     * @return Returns the clientKey.
+     */
+    public EncryptionKey getClientKey()
+    {
+        return clientKey;
+    }
+
+
+    /**
+     * @param clientKey The clientKey to set.
+     */
+    public void setClientKey( EncryptionKey clientKey )
+    {
+        this.clientKey = clientKey;
+    }
+
+
+    /**
+     * @return Returns the ticket.
+     */
+    public Ticket getTicket()
+    {
+        return ticket;
+    }
+
+
+    /**
+     * @param ticket The ticket to set.
+     */
+    public void setTicket( Ticket ticket )
+    {
+        this.ticket = ticket;
+    }
+
+
+    /**
+     * @return true if the client used pre-authentication.
+     */
+    public boolean isPreAuthenticated()
+    {
+        return isPreAuthenticated;
+    }
+
+
+    /**
+     * @param isPreAuthenticated Whether the client used pre-authentication.
+     */
+    public void setPreAuthenticated( boolean isPreAuthenticated )
+    {
+        this.isPreAuthenticated = isPreAuthenticated;
+    }
+}
diff --git a/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/kdc/authentication/AuthenticationService.java b/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/kdc/authentication/AuthenticationService.java
new file mode 100644
index 0000000..482763a
--- /dev/null
+++ b/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/kdc/authentication/AuthenticationService.java
@@ -0,0 +1,815 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.kdc.authentication;
+
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.util.Date;
+import java.util.Set;
+
+import javax.security.auth.kerberos.KerberosKey;
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.apache.directory.server.kerberos.kdc.KdcContext;
+import org.apache.directory.server.kerberos.kdc.KdcServer;
+import org.apache.directory.server.kerberos.sam.SamException;
+import org.apache.directory.server.kerberos.sam.SamSubsystem;
+import org.apache.directory.server.kerberos.shared.KerberosConstants;
+import org.apache.directory.server.kerberos.shared.KerberosUtils;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.CipherTextHandler;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.KeyUsage;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.RandomKeyFactory;
+import org.apache.directory.server.kerberos.shared.exceptions.ErrorType;
+import org.apache.directory.server.kerberos.shared.exceptions.KerberosException;
+import org.apache.directory.server.kerberos.shared.io.decoder.EncryptedDataDecoder;
+import org.apache.directory.server.kerberos.shared.io.encoder.EncryptionTypeInfoEncoder;
+import org.apache.directory.server.kerberos.shared.io.encoder.PreAuthenticationDataEncoder;
+import org.apache.directory.server.kerberos.shared.messages.AuthenticationReply;
+import org.apache.directory.server.kerberos.shared.messages.KdcReply;
+import org.apache.directory.server.kerberos.shared.messages.KdcRequest;
+import org.apache.directory.server.kerberos.shared.messages.components.EncTicketPart;
+import org.apache.directory.server.kerberos.shared.messages.components.EncTicketPartModifier;
+import org.apache.directory.server.kerberos.shared.messages.components.InvalidTicketException;
+import org.apache.directory.server.kerberos.shared.messages.components.Ticket;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptedData;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptedTimeStamp;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionTypeInfoEntry;
+import org.apache.directory.server.kerberos.shared.messages.value.KdcOptions;
+import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
+import org.apache.directory.server.kerberos.shared.messages.value.LastRequest;
+import org.apache.directory.server.kerberos.shared.messages.value.PaData;
+import org.apache.directory.server.kerberos.shared.messages.value.TransitedEncoding;
+import org.apache.directory.server.kerberos.shared.messages.value.flags.TicketFlag;
+import org.apache.directory.server.kerberos.shared.messages.value.types.PaDataType;
+import org.apache.directory.server.kerberos.shared.replay.InMemoryReplayCache;
+import org.apache.directory.server.kerberos.shared.replay.ReplayCache;
+import org.apache.directory.server.kerberos.shared.store.PrincipalStore;
+import org.apache.directory.server.kerberos.shared.store.PrincipalStoreEntry;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class AuthenticationService
+{
+    /** The log for this class. */
+    private static final Logger LOG = LoggerFactory.getLogger( AuthenticationService.class );
+
+    private static final ReplayCache replayCache = new InMemoryReplayCache();
+    private static final CipherTextHandler cipherTextHandler = new CipherTextHandler();
+
+    private static final String SERVICE_NAME = "Authentication Service (AS)";
+
+
+    public static void execute( AuthenticationContext authContext ) throws Exception
+    {
+        if ( LOG.isDebugEnabled() )
+        {
+            monitorRequest( authContext );
+        }
+        
+        authContext.setReplayCache( replayCache );
+        authContext.setCipherTextHandler( cipherTextHandler );
+
+        if ( authContext.getRequest().getProtocolVersionNumber() != KerberosConstants.KERBEROS_V5 )
+        {
+            throw new KerberosException( ErrorType.KDC_ERR_BAD_PVNO );
+        }
+
+        selectEncryptionType( authContext );
+        getClientEntry( authContext );
+        verifyPolicy( authContext );
+        verifySam( authContext );
+        verifyEncryptedTimestamp( authContext );
+        
+        if ( authContext.getClientKey() == null )
+        {
+            verifyEncryptedTimestamp( authContext );
+        }
+
+        getServerEntry( authContext );
+        generateTicket( authContext );
+        buildReply( authContext );
+
+        if ( LOG.isDebugEnabled() )
+        {
+            monitorContext( authContext );
+            monitorReply( ( KdcContext ) authContext );
+        }
+        
+        sealReply( authContext );
+    }
+
+    
+    private static void selectEncryptionType( AuthenticationContext authContext ) throws KerberosException, InvalidTicketException
+    {
+        KdcContext kdcContext = ( KdcContext ) authContext;
+        KdcServer config = kdcContext.getConfig();
+
+        Set<EncryptionType> requestedTypes = kdcContext.getRequest().getEType();
+
+        EncryptionType bestType = KerberosUtils.getBestEncryptionType( requestedTypes, config.getEncryptionTypes() );
+
+        LOG.debug( "Session will use encryption type {}.", bestType );
+
+        if ( bestType == null )
+        {
+            throw new KerberosException( ErrorType.KDC_ERR_ETYPE_NOSUPP );
+        }
+
+        kdcContext.setEncryptionType( bestType );
+    }
+
+    
+    private static void getClientEntry( AuthenticationContext authContext ) throws KerberosException, InvalidTicketException
+    {
+        KerberosPrincipal principal = authContext.getRequest().getClientPrincipal();
+        PrincipalStore store = authContext.getStore();
+
+        PrincipalStoreEntry storeEntry = getEntry( principal, store, ErrorType.KDC_ERR_C_PRINCIPAL_UNKNOWN ); 
+        authContext.setClientEntry( storeEntry );
+    }
+    
+    
+    private static void verifyPolicy( AuthenticationContext authContext ) throws KerberosException, InvalidTicketException
+    {
+        PrincipalStoreEntry entry = authContext.getClientEntry();
+
+        if ( entry.isDisabled() )
+        {
+            throw new KerberosException( ErrorType.KDC_ERR_CLIENT_REVOKED );
+        }
+
+        if ( entry.isLockedOut() )
+        {
+            throw new KerberosException( ErrorType.KDC_ERR_CLIENT_REVOKED );
+        }
+
+        if ( entry.getExpiration().getTime() < new Date().getTime() )
+        {
+            throw new KerberosException( ErrorType.KDC_ERR_CLIENT_REVOKED );
+        }
+    }
+    
+    
+    private static void verifySam( AuthenticationContext authContext ) throws KerberosException, InvalidTicketException
+    {
+        LOG.debug( "Verifying using SAM subsystem." );
+        KdcRequest request = authContext.getRequest();
+        KdcServer config = authContext.getConfig();
+
+        PrincipalStoreEntry clientEntry = authContext.getClientEntry();
+        String clientName = clientEntry.getPrincipal().getName();
+
+        EncryptionKey clientKey = null;
+
+        if ( clientEntry.getSamType() != null )
+        {
+            if ( LOG.isDebugEnabled() )
+            {
+                LOG.debug( "Entry for client principal {} has a valid SAM type.  Invoking SAM subsystem for pre-authentication.", clientName );
+            }
+
+            PaData[] preAuthData = request.getPreAuthData();
+
+            if ( preAuthData == null || preAuthData.length == 0 )
+            {
+                throw new KerberosException( ErrorType.KDC_ERR_PREAUTH_REQUIRED, preparePreAuthenticationError( config
+                    .getEncryptionTypes() ) );
+            }
+
+            try
+            {
+                for ( int ii = 0; ii < preAuthData.length; ii++ )
+                {
+                    if ( preAuthData[ii].getPaDataType().equals( PaDataType.PA_ENC_TIMESTAMP ) )
+                    {
+                        KerberosKey samKey = SamSubsystem.getInstance().verify( clientEntry,
+                            preAuthData[ii].getPaDataValue() );
+                        clientKey = new EncryptionKey( EncryptionType.getTypeByOrdinal( samKey.getKeyType() ), samKey
+                            .getEncoded() );
+                    }
+                }
+            }
+            catch ( SamException se )
+            {
+                throw new KerberosException( ErrorType.KRB_ERR_GENERIC, se );
+            }
+
+            authContext.setClientKey( clientKey );
+            authContext.setPreAuthenticated( true );
+
+            if ( LOG.isDebugEnabled() )
+            {
+                LOG.debug( "Pre-authentication using SAM subsystem successful for {}.", clientName );
+            }
+        }
+    }
+    
+    
+    private static void verifyEncryptedTimestamp( AuthenticationContext authContext ) throws KerberosException, InvalidTicketException
+    {
+        LOG.debug( "Verifying using encrypted timestamp." );
+        
+        KdcServer config = authContext.getConfig();
+        KdcRequest request = authContext.getRequest();
+        CipherTextHandler cipherTextHandler = authContext.getCipherTextHandler();
+        PrincipalStoreEntry clientEntry = authContext.getClientEntry();
+        String clientName = clientEntry.getPrincipal().getName();
+
+        EncryptionKey clientKey = null;
+
+        if ( clientEntry.getSamType() == null )
+        {
+            if ( LOG.isDebugEnabled() )
+            {
+                LOG.debug(
+                    "Entry for client principal {} has no SAM type.  Proceeding with standard pre-authentication.",
+                    clientName );
+            }
+
+            EncryptionType encryptionType = authContext.getEncryptionType();
+            clientKey = clientEntry.getKeyMap().get( encryptionType );
+
+            if ( clientKey == null )
+            {
+                throw new KerberosException( ErrorType.KDC_ERR_NULL_KEY );
+            }
+
+            if ( config.isPaEncTimestampRequired() )
+            {
+                PaData[] preAuthData = request.getPreAuthData();
+
+                if ( preAuthData == null )
+                {
+                    throw new KerberosException( ErrorType.KDC_ERR_PREAUTH_REQUIRED,
+                        preparePreAuthenticationError( config.getEncryptionTypes() ) );
+                }
+
+                EncryptedTimeStamp timestamp = null;
+
+                for ( int ii = 0; ii < preAuthData.length; ii++ )
+                {
+                    if ( preAuthData[ii].getPaDataType().equals( PaDataType.PA_ENC_TIMESTAMP ) )
+                    {
+                        EncryptedData dataValue;
+
+                        try
+                        {
+                            dataValue = EncryptedDataDecoder.decode( preAuthData[ii].getPaDataValue() );
+                        }
+                        catch ( IOException ioe )
+                        {
+                            throw new KerberosException( ErrorType.KRB_AP_ERR_BAD_INTEGRITY, ioe );
+                        }
+                        catch ( ClassCastException cce )
+                        {
+                            throw new KerberosException( ErrorType.KRB_AP_ERR_BAD_INTEGRITY, cce );
+                        }
+
+                        timestamp = ( EncryptedTimeStamp ) cipherTextHandler.unseal( EncryptedTimeStamp.class,
+                            clientKey, dataValue, KeyUsage.NUMBER1 );
+                    }
+                }
+
+                if ( preAuthData.length > 0 && timestamp == null )
+                {
+                    throw new KerberosException( ErrorType.KDC_ERR_PADATA_TYPE_NOSUPP );
+                }
+
+                if ( timestamp == null )
+                {
+                    throw new KerberosException( ErrorType.KDC_ERR_PREAUTH_REQUIRED,
+                        preparePreAuthenticationError( config.getEncryptionTypes() ) );
+                }
+
+                if ( !timestamp.getTimeStamp().isInClockSkew( config.getAllowableClockSkew() ) )
+                {
+                    throw new KerberosException( ErrorType.KDC_ERR_PREAUTH_FAILED );
+                }
+
+                /*
+                 * if(decrypted_enc_timestamp and usec is replay)
+                 *         error_out(KDC_ERR_PREAUTH_FAILED);
+                 * endif
+                 * 
+                 * add decrypted_enc_timestamp and usec to replay cache;
+                 */
+            }
+        }
+
+        authContext.setClientKey( clientKey );
+        authContext.setPreAuthenticated( true );
+
+        if ( LOG.isDebugEnabled() )
+        {
+            LOG.debug( "Pre-authentication by encrypted timestamp successful for {}.", clientName );
+        }
+    }
+    
+    
+    private static void getServerEntry( AuthenticationContext authContext ) throws KerberosException, InvalidTicketException
+    {
+        KerberosPrincipal principal = authContext.getRequest().getServerPrincipal();
+        PrincipalStore store = authContext.getStore();
+    
+        authContext.setServerEntry( getEntry( principal, store, ErrorType.KDC_ERR_S_PRINCIPAL_UNKNOWN ) );
+    }    
+    
+    
+    private static void generateTicket( AuthenticationContext authContext ) throws KerberosException, InvalidTicketException
+    {
+        KdcRequest request = authContext.getRequest();
+        CipherTextHandler cipherTextHandler = authContext.getCipherTextHandler();
+        KerberosPrincipal serverPrincipal = request.getServerPrincipal();
+
+        EncryptionType encryptionType = authContext.getEncryptionType();
+        EncryptionKey serverKey = authContext.getServerEntry().getKeyMap().get( encryptionType );
+
+        KerberosPrincipal ticketPrincipal = request.getServerPrincipal();
+        EncTicketPartModifier newTicketBody = new EncTicketPartModifier();
+        KdcServer config = authContext.getConfig();
+
+        // The INITIAL flag indicates that a ticket was issued using the AS protocol.
+        newTicketBody.setFlag( TicketFlag.INITIAL );
+
+        // The PRE-AUTHENT flag indicates that the client used pre-authentication.
+        if ( authContext.isPreAuthenticated() )
+        {
+            newTicketBody.setFlag( TicketFlag.PRE_AUTHENT );
+        }
+
+        if ( request.getOption( KdcOptions.FORWARDABLE ) )
+        {
+            if ( !config.isForwardableAllowed() )
+            {
+                throw new KerberosException( ErrorType.KDC_ERR_POLICY );
+            }
+
+            newTicketBody.setFlag( TicketFlag.FORWARDABLE );
+        }
+
+        if ( request.getOption( KdcOptions.PROXIABLE ) )
+        {
+            if ( !config.isProxiableAllowed() )
+            {
+                throw new KerberosException( ErrorType.KDC_ERR_POLICY );
+            }
+
+            newTicketBody.setFlag( TicketFlag.PROXIABLE );
+        }
+
+        if ( request.getOption( KdcOptions.ALLOW_POSTDATE ) )
+        {
+            if ( !config.isPostdatedAllowed() )
+            {
+                throw new KerberosException( ErrorType.KDC_ERR_POLICY );
+            }
+
+            newTicketBody.setFlag( TicketFlag.MAY_POSTDATE );
+        }
+
+        if ( request.getOption( KdcOptions.RENEW ) || request.getOption( KdcOptions.VALIDATE )
+            || request.getOption( KdcOptions.PROXY ) || request.getOption( KdcOptions.FORWARDED )
+            || request.getOption( KdcOptions.ENC_TKT_IN_SKEY ) )
+        {
+            throw new KerberosException( ErrorType.KDC_ERR_BADOPTION );
+        }
+
+        EncryptionKey sessionKey = RandomKeyFactory.getRandomKey( authContext.getEncryptionType() );
+        newTicketBody.setSessionKey( sessionKey );
+
+        newTicketBody.setClientPrincipal( request.getClientPrincipal() );
+        newTicketBody.setTransitedEncoding( new TransitedEncoding() );
+
+        KerberosTime now = new KerberosTime();
+
+        newTicketBody.setAuthTime( now );
+
+        KerberosTime startTime = request.getFrom();
+
+        /*
+         * "If the requested starttime is absent, indicates a time in the past,
+         * or is within the window of acceptable clock skew for the KDC and the
+         * POSTDATE option has not been specified, then the starttime of the
+         * ticket is set to the authentication server's current time."
+         */
+        if ( startTime == null || startTime.lessThan( now ) || startTime.isInClockSkew( config.getAllowableClockSkew() )
+            && !request.getOption( KdcOptions.POSTDATED ) )
+        {
+            startTime = now;
+        }
+
+        /*
+         * "If it indicates a time in the future beyond the acceptable clock skew,
+         * but the POSTDATED option has not been specified, then the error
+         * KDC_ERR_CANNOT_POSTDATE is returned."
+         */
+        if ( startTime != null && startTime.greaterThan( now )
+            && !startTime.isInClockSkew( config.getAllowableClockSkew() ) && !request.getOption( KdcOptions.POSTDATED ) )
+        {
+            throw new KerberosException( ErrorType.KDC_ERR_CANNOT_POSTDATE );
+        }
+
+        /*
+         * "Otherwise the requested starttime is checked against the policy of the
+         * local realm and if the ticket's starttime is acceptable, it is set as
+         * requested, and the INVALID flag is set in the new ticket."
+         */
+        if ( request.getOption( KdcOptions.POSTDATED ) )
+        {
+            if ( !config.isPostdatedAllowed() )
+            {
+                throw new KerberosException( ErrorType.KDC_ERR_POLICY );
+            }
+
+            newTicketBody.setFlag( TicketFlag.POSTDATED );
+            newTicketBody.setFlag( TicketFlag.INVALID );
+            newTicketBody.setStartTime( startTime );
+        }
+
+        long till = 0;
+        
+        if ( request.getTill().getTime() == 0 )
+        {
+            till = Long.MAX_VALUE;
+        }
+        else
+        {
+            till = request.getTill().getTime();
+        }
+
+        /*
+         * The end time is the minimum of (a) the requested till time or (b)
+         * the start time plus maximum lifetime as configured in policy.
+         */
+        long endTime = Math.min( till, startTime.getTime() + config.getMaximumTicketLifetime() );
+        KerberosTime kerberosEndTime = new KerberosTime( endTime );
+        newTicketBody.setEndTime( kerberosEndTime );
+
+        /*
+         * "If the requested expiration time minus the starttime (as determined
+         * above) is less than a site-determined minimum lifetime, an error
+         * message with code KDC_ERR_NEVER_VALID is returned."
+         */
+        if ( kerberosEndTime.lessThan( startTime ) )
+        {
+            throw new KerberosException( ErrorType.KDC_ERR_NEVER_VALID );
+        }
+
+        long ticketLifeTime = Math.abs( startTime.getTime() - kerberosEndTime.getTime() );
+        
+        if ( ticketLifeTime < config.getAllowableClockSkew() )
+        {
+            throw new KerberosException( ErrorType.KDC_ERR_NEVER_VALID );
+        }
+
+        /*
+         * "If the requested expiration time for the ticket exceeds what was determined
+         * as above, and if the 'RENEWABLE-OK' option was requested, then the 'RENEWABLE'
+         * flag is set in the new ticket, and the renew-till value is set as if the
+         * 'RENEWABLE' option were requested."
+         */
+        KerberosTime tempRtime = request.getRtime();
+
+        if ( request.getOption( KdcOptions.RENEWABLE_OK ) && request.getTill().greaterThan( kerberosEndTime ) )
+        {
+            if ( !config.isRenewableAllowed() )
+            {
+                throw new KerberosException( ErrorType.KDC_ERR_POLICY );
+            }
+
+            request.setOption( KdcOptions.RENEWABLE );
+            tempRtime = request.getTill();
+        }
+
+        if ( request.getOption( KdcOptions.RENEWABLE ) )
+        {
+            if ( !config.isRenewableAllowed() )
+            {
+                throw new KerberosException( ErrorType.KDC_ERR_POLICY );
+            }
+
+            newTicketBody.setFlag( TicketFlag.RENEWABLE );
+
+            if ( tempRtime == null || tempRtime.isZero() )
+            {
+                tempRtime = KerberosTime.INFINITY;
+            }
+
+            /*
+             * The renew-till time is the minimum of (a) the requested renew-till
+             * time or (b) the start time plus maximum renewable lifetime as
+             * configured in policy.
+             */
+            long renewTill = Math.min( tempRtime.getTime(), startTime.getTime() + config.getMaximumRenewableLifetime() );
+            newTicketBody.setRenewTill( new KerberosTime( renewTill ) );
+        }
+
+        if ( request.getAddresses() != null && request.getAddresses().getAddresses() != null
+            && request.getAddresses().getAddresses().length > 0 )
+        {
+            newTicketBody.setClientAddresses( request.getAddresses() );
+        }
+        else
+        {
+            if ( !config.isEmptyAddressesAllowed() )
+            {
+                throw new KerberosException( ErrorType.KDC_ERR_POLICY );
+            }
+        }
+
+        EncTicketPart ticketPart = newTicketBody.getEncTicketPart();
+
+        EncryptedData encryptedData = cipherTextHandler.seal( serverKey, ticketPart, KeyUsage.NUMBER2 );
+
+        Ticket newTicket = new Ticket( ticketPrincipal, encryptedData );
+        newTicket.setEncTicketPart( ticketPart );
+
+        if ( LOG.isDebugEnabled() )
+        {
+            LOG.debug( "Ticket will be issued for access to {}.", serverPrincipal.toString() );
+        }
+
+        authContext.setTicket( newTicket );
+    }
+    
+    
+    private static void buildReply( AuthenticationContext authContext ) throws KerberosException, InvalidTicketException
+    {
+        KdcRequest request = authContext.getRequest();
+        Ticket ticket = authContext.getTicket();
+
+        AuthenticationReply reply = new AuthenticationReply();
+
+        reply.setClientPrincipal( request.getClientPrincipal() );
+        reply.setTicket( ticket );
+        reply.setKey( ticket.getEncTicketPart().getSessionKey() );
+
+        // TODO - fetch lastReq for this client; requires store
+        reply.setLastRequest( new LastRequest() );
+        // TODO - resp.key-expiration := client.expiration; requires store
+
+        reply.setNonce( request.getNonce() );
+
+        reply.setFlags( ticket.getEncTicketPart().getFlags() );
+        reply.setAuthTime( ticket.getEncTicketPart().getAuthTime() );
+        reply.setStartTime( ticket.getEncTicketPart().getStartTime() );
+        reply.setEndTime( ticket.getEncTicketPart().getEndTime() );
+
+        if ( ticket.getEncTicketPart().getFlags().isRenewable() )
+        {
+            reply.setRenewTill( ticket.getEncTicketPart().getRenewTill() );
+        }
+
+        reply.setServerPrincipal( ticket.getServerPrincipal() );
+        reply.setClientAddresses( ticket.getEncTicketPart().getClientAddresses() );
+
+        authContext.setReply( reply );
+    }
+    
+    
+    private static void sealReply( AuthenticationContext authContext ) throws KerberosException, InvalidTicketException
+    {
+        AuthenticationReply reply = ( AuthenticationReply ) authContext.getReply();
+        EncryptionKey clientKey = authContext.getClientKey();
+        CipherTextHandler cipherTextHandler = authContext.getCipherTextHandler();
+
+        EncryptedData encryptedData = cipherTextHandler.seal( clientKey, reply, KeyUsage.NUMBER3 );
+        reply.setEncPart( encryptedData );
+    }
+    
+    
+    private static void monitorRequest( KdcContext kdcContext )
+    {
+        KdcRequest request = kdcContext.getRequest();
+
+        if ( LOG.isDebugEnabled() )
+        {
+            try
+            {
+                String clientAddress = kdcContext.getClientAddress().getHostAddress();
+
+                StringBuffer sb = new StringBuffer();
+
+                sb.append( "Received " + SERVICE_NAME + " request:" );
+                sb.append( "\n\t" + "messageType:           " + request.getMessageType() );
+                sb.append( "\n\t" + "protocolVersionNumber: " + request.getProtocolVersionNumber() );
+                sb.append( "\n\t" + "clientAddress:         " + clientAddress );
+                sb.append( "\n\t" + "nonce:                 " + request.getNonce() );
+                sb.append( "\n\t" + "kdcOptions:            " + request.getKdcOptions() );
+                sb.append( "\n\t" + "clientPrincipal:       " + request.getClientPrincipal() );
+                sb.append( "\n\t" + "serverPrincipal:       " + request.getServerPrincipal() );
+                sb.append( "\n\t" + "encryptionType:        " + KerberosUtils.getEncryptionTypesString( request.getEType() ) );
+                sb.append( "\n\t" + "realm:                 " + request.getRealm() );
+                sb.append( "\n\t" + "from time:             " + request.getFrom() );
+                sb.append( "\n\t" + "till time:             " + request.getTill() );
+                sb.append( "\n\t" + "renew-till time:       " + request.getRtime() );
+                sb.append( "\n\t" + "hostAddresses:         " + request.getAddresses() );
+
+                LOG.debug( sb.toString() );
+            }
+            catch ( Exception e )
+            {
+                // This is a monitor.  No exceptions should bubble up.
+                LOG.error( "Error in request monitor", e );
+            }
+        }
+    }
+    
+    private static void monitorContext( AuthenticationContext authContext )
+    {
+        try
+        {
+            long clockSkew = authContext.getConfig().getAllowableClockSkew();
+            InetAddress clientAddress = authContext.getClientAddress();
+
+            StringBuilder sb = new StringBuilder();
+
+            sb.append( "Monitoring " + SERVICE_NAME + " context:" );
+
+            sb.append( "\n\t" + "clockSkew              " + clockSkew );
+            sb.append( "\n\t" + "clientAddress          " + clientAddress );
+
+            KerberosPrincipal clientPrincipal = authContext.getClientEntry().getPrincipal();
+            PrincipalStoreEntry clientEntry = authContext.getClientEntry();
+
+            sb.append( "\n\t" + "principal              " + clientPrincipal );
+            sb.append( "\n\t" + "cn                     " + clientEntry.getCommonName() );
+            sb.append( "\n\t" + "realm                  " + clientEntry.getRealmName() );
+            sb.append( "\n\t" + "principal              " + clientEntry.getPrincipal() );
+            sb.append( "\n\t" + "SAM type               " + clientEntry.getSamType() );
+
+            KerberosPrincipal serverPrincipal = authContext.getRequest().getServerPrincipal();
+            PrincipalStoreEntry serverEntry = authContext.getServerEntry();
+
+            sb.append( "\n\t" + "principal              " + serverPrincipal );
+            sb.append( "\n\t" + "cn                     " + serverEntry.getCommonName() );
+            sb.append( "\n\t" + "realm                  " + serverEntry.getRealmName() );
+            sb.append( "\n\t" + "principal              " + serverEntry.getPrincipal() );
+            sb.append( "\n\t" + "SAM type               " + serverEntry.getSamType() );
+
+            EncryptionType encryptionType = authContext.getEncryptionType();
+            int clientKeyVersion = clientEntry.getKeyMap().get( encryptionType ).getKeyVersion();
+            int serverKeyVersion = serverEntry.getKeyMap().get( encryptionType ).getKeyVersion();
+            sb.append( "\n\t" + "Request key type       " + encryptionType );
+            sb.append( "\n\t" + "Client key version     " + clientKeyVersion );
+            sb.append( "\n\t" + "Server key version     " + serverKeyVersion );
+
+            LOG.debug( sb.toString() );
+        }
+        catch ( Exception e )
+        {
+            // This is a monitor.  No exceptions should bubble up.
+            LOG.error( "Error in context monitor", e );
+        }
+    }
+    
+    
+    private static void monitorReply( KdcContext kdcContext )
+    {
+        Object reply = kdcContext.getReply();
+
+        if ( LOG.isDebugEnabled() )
+        {
+            if ( reply instanceof KdcReply )
+            {
+                KdcReply success = ( KdcReply ) reply;
+
+                try
+                {
+                    StringBuffer sb = new StringBuffer();
+
+                    sb.append( "Responding with " + SERVICE_NAME + " reply:" );
+                    sb.append( "\n\t" + "messageType:           " + success.getMessageType() );
+                    sb.append( "\n\t" + "protocolVersionNumber: " + success.getProtocolVersionNumber() );
+                    sb.append( "\n\t" + "nonce:                 " + success.getNonce() );
+                    sb.append( "\n\t" + "clientPrincipal:       " + success.getClientPrincipal() );
+                    sb.append( "\n\t" + "client realm:          " + success.getClientRealm() );
+                    sb.append( "\n\t" + "serverPrincipal:       " + success.getServerPrincipal() );
+                    sb.append( "\n\t" + "server realm:          " + success.getServerRealm() );
+                    sb.append( "\n\t" + "auth time:             " + success.getAuthTime() );
+                    sb.append( "\n\t" + "start time:            " + success.getStartTime() );
+                    sb.append( "\n\t" + "end time:              " + success.getEndTime() );
+                    sb.append( "\n\t" + "renew-till time:       " + success.getRenewTill() );
+                    sb.append( "\n\t" + "hostAddresses:         " + success.getClientAddresses() );
+
+                    LOG.debug( sb.toString() );
+                }
+                catch ( Exception e )
+                {
+                    // This is a monitor.  No exceptions should bubble up.
+                    LOG.error( "Error in reply monitor", e );
+                }
+            }
+        }
+    }
+    
+    
+    /**
+     * Get a PrincipalStoreEntry given a principal.  The ErrorType is used to indicate
+     * whether any resulting error pertains to a server or client.
+     */
+    private static PrincipalStoreEntry getEntry( KerberosPrincipal principal, PrincipalStore store, ErrorType errorType )
+        throws KerberosException
+    {
+        PrincipalStoreEntry entry = null;
+
+        try
+        {
+            entry = store.getPrincipal( principal );
+        }
+        catch ( Exception e )
+        {
+            throw new KerberosException( errorType, e );
+        }
+
+        if ( entry == null )
+        {
+            throw new KerberosException( errorType );
+        }
+
+        if ( entry.getKeyMap() == null || entry.getKeyMap().isEmpty() )
+        {
+            throw new KerberosException( ErrorType.KDC_ERR_NULL_KEY );
+        }
+
+        return entry;
+    }
+    
+    
+    /**
+     * Prepares a pre-authentication error message containing required
+     * encryption types.
+     *
+     * @param encryptionTypes
+     * @return The error message as bytes.
+     */
+    private static byte[] preparePreAuthenticationError( Set<EncryptionType> encryptionTypes )
+    {
+        PaData[] paDataSequence = new PaData[2];
+
+        PaData paData = new PaData();
+        paData.setPaDataType( PaDataType.PA_ENC_TIMESTAMP );
+        paData.setPaDataValue( new byte[0] );
+
+        paDataSequence[0] = paData;
+
+        EncryptionTypeInfoEntry[] entries = new EncryptionTypeInfoEntry[ encryptionTypes.size() ];
+        int i = 0;
+        
+        for ( EncryptionType encryptionType:encryptionTypes )
+        {
+            entries[i++] = new EncryptionTypeInfoEntry( encryptionType, null );
+        }
+
+        byte[] encTypeInfo = null;
+
+        try
+        {
+            encTypeInfo = EncryptionTypeInfoEncoder.encode( entries );
+        }
+        catch ( IOException ioe )
+        {
+            return null;
+        }
+
+        PaData encType = new PaData();
+        encType.setPaDataType( PaDataType.PA_ENCTYPE_INFO );
+        encType.setPaDataValue( encTypeInfo );
+
+        paDataSequence[1] = encType;
+
+        try
+        {
+            return PreAuthenticationDataEncoder.encode( paDataSequence );
+        }
+        catch ( IOException ioe )
+        {
+            return null;
+        }
+    }
+}
diff --git a/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/kdc/authentication/package-info.java b/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/kdc/authentication/package-info.java
new file mode 100644
index 0000000..4ed1ccf
--- /dev/null
+++ b/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/kdc/authentication/package-info.java
@@ -0,0 +1,28 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+/**
+ * Provides the KDC's Authentication Service (AS).
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+
+package org.apache.directory.server.kerberos.kdc.authentication;
diff --git a/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/kdc/package-info.java b/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/kdc/package-info.java
new file mode 100644
index 0000000..564ed71
--- /dev/null
+++ b/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/kdc/package-info.java
@@ -0,0 +1,34 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+/**
+ * Provides the entry point to an instance of the {@link org.apache.directory.server.kerberos.kdc.KdcServer}
+ * (KDC), as well as classes common to the KDC's two services:  the
+ * Authentication Service (AS) and the Ticket-Granting Service (TGS).
+ * <p/>
+ * Classes common to all of the services provide configuration
+ * support, the execution context, monitors for logging, and
+ * "links" for selecting checksum and encryption types.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+
+package org.apache.directory.server.kerberos.kdc;
diff --git a/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/kdc/ticketgrant/TicketGrantingContext.java b/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/kdc/ticketgrant/TicketGrantingContext.java
new file mode 100644
index 0000000..394d0ee
--- /dev/null
+++ b/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/kdc/ticketgrant/TicketGrantingContext.java
@@ -0,0 +1,173 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.kdc.ticketgrant;
+
+
+import org.apache.directory.server.kerberos.kdc.KdcContext;
+import org.apache.directory.server.kerberos.shared.messages.ApplicationRequest;
+import org.apache.directory.server.kerberos.shared.messages.components.Authenticator;
+import org.apache.directory.server.kerberos.shared.messages.components.Ticket;
+import org.apache.directory.server.kerberos.shared.replay.ReplayCache;
+import org.apache.directory.server.kerberos.shared.store.PrincipalStoreEntry;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class TicketGrantingContext extends KdcContext
+{
+    private static final long serialVersionUID = 2130665703752837491L;
+
+    private ApplicationRequest authHeader;
+    private Ticket tgt;
+    private Ticket newTicket;
+    private Authenticator authenticator;
+    private ReplayCache replayCache;
+
+    private PrincipalStoreEntry ticketPrincipalEntry;
+    private PrincipalStoreEntry requestPrincipalEntry;
+
+
+    /**
+     * @return Returns the requestPrincipalEntry.
+     */
+    public PrincipalStoreEntry getRequestPrincipalEntry()
+    {
+        return requestPrincipalEntry;
+    }
+
+
+    /**
+     * @param requestPrincipalEntry The requestPrincipalEntry to set.
+     */
+    public void setRequestPrincipalEntry( PrincipalStoreEntry requestPrincipalEntry )
+    {
+        this.requestPrincipalEntry = requestPrincipalEntry;
+    }
+
+
+    /**
+     * @return Returns the ticketPrincipalEntry.
+     */
+    public PrincipalStoreEntry getTicketPrincipalEntry()
+    {
+        return ticketPrincipalEntry;
+    }
+
+
+    /**
+     * @param ticketPrincipalEntry The ticketPrincipalEntry to set.
+     */
+    public void setTicketPrincipalEntry( PrincipalStoreEntry ticketPrincipalEntry )
+    {
+        this.ticketPrincipalEntry = ticketPrincipalEntry;
+    }
+
+
+    /**
+     * @return Returns the replayCache.
+     */
+    public ReplayCache getReplayCache()
+    {
+        return replayCache;
+    }
+
+
+    /**
+     * @param replayCache The replayCache to set.
+     */
+    public void setReplayCache( ReplayCache replayCache )
+    {
+        this.replayCache = replayCache;
+    }
+
+
+    /**
+     * @return Returns the authenticator.
+     */
+    public Authenticator getAuthenticator()
+    {
+        return authenticator;
+    }
+
+
+    /**
+     * @param authenticator The authenticator to set.
+     */
+    public void setAuthenticator( Authenticator authenticator )
+    {
+        this.authenticator = authenticator;
+    }
+
+
+    /**
+     * @return Returns the newTicket.
+     */
+    public Ticket getNewTicket()
+    {
+        return newTicket;
+    }
+
+
+    /**
+     * @param newTicket The newTicket to set.
+     */
+    public void setNewTicket( Ticket newTicket )
+    {
+        this.newTicket = newTicket;
+    }
+
+
+    /**
+     * @return Returns the tgt.
+     */
+    public Ticket getTgt()
+    {
+        return tgt;
+    }
+
+
+    /**
+     * @param tgt The tgt to set.
+     */
+    public void setTgt( Ticket tgt )
+    {
+        this.tgt = tgt;
+    }
+
+
+    /**
+     * @return Returns the authHeader.
+     */
+    public ApplicationRequest getAuthHeader()
+    {
+        return authHeader;
+    }
+
+
+    /**
+     * @param authHeader The authHeader to set.
+     */
+    public void setAuthHeader( ApplicationRequest authHeader )
+    {
+        this.authHeader = authHeader;
+    }
+}
diff --git a/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/kdc/ticketgrant/TicketGrantingService.java b/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/kdc/ticketgrant/TicketGrantingService.java
new file mode 100644
index 0000000..421ca5b
--- /dev/null
+++ b/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/kdc/ticketgrant/TicketGrantingService.java
@@ -0,0 +1,911 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.kdc.ticketgrant;
+
+
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.apache.directory.server.kerberos.kdc.KdcContext;
+import org.apache.directory.server.kerberos.kdc.KdcServer;
+import org.apache.directory.server.kerberos.shared.KerberosConstants;
+import org.apache.directory.server.kerberos.shared.KerberosUtils;
+import org.apache.directory.server.kerberos.shared.crypto.checksum.ChecksumHandler;
+import org.apache.directory.server.kerberos.shared.crypto.checksum.ChecksumType;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.CipherTextHandler;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.KeyUsage;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.RandomKeyFactory;
+import org.apache.directory.server.kerberos.shared.exceptions.ErrorType;
+import org.apache.directory.server.kerberos.shared.exceptions.KerberosException;
+import org.apache.directory.server.kerberos.shared.io.decoder.ApplicationRequestDecoder;
+import org.apache.directory.server.kerberos.shared.messages.ApplicationRequest;
+import org.apache.directory.server.kerberos.shared.messages.KdcReply;
+import org.apache.directory.server.kerberos.shared.messages.KdcRequest;
+import org.apache.directory.server.kerberos.shared.messages.TicketGrantReply;
+import org.apache.directory.server.kerberos.shared.messages.components.Authenticator;
+import org.apache.directory.server.kerberos.shared.messages.components.EncTicketPart;
+import org.apache.directory.server.kerberos.shared.messages.components.EncTicketPartModifier;
+import org.apache.directory.server.kerberos.shared.messages.components.Ticket;
+import org.apache.directory.server.kerberos.shared.messages.value.AuthorizationData;
+import org.apache.directory.server.kerberos.shared.messages.value.Checksum;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptedData;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+import org.apache.directory.server.kerberos.shared.messages.value.HostAddress;
+import org.apache.directory.server.kerberos.shared.messages.value.HostAddresses;
+import org.apache.directory.server.kerberos.shared.messages.value.KdcOptions;
+import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
+import org.apache.directory.server.kerberos.shared.messages.value.LastRequest;
+import org.apache.directory.server.kerberos.shared.messages.value.PaData;
+import org.apache.directory.server.kerberos.shared.messages.value.flags.TicketFlag;
+import org.apache.directory.server.kerberos.shared.messages.value.types.PaDataType;
+import org.apache.directory.server.kerberos.shared.replay.InMemoryReplayCache;
+import org.apache.directory.server.kerberos.shared.replay.ReplayCache;
+import org.apache.directory.server.kerberos.shared.store.PrincipalStore;
+import org.apache.directory.server.kerberos.shared.store.PrincipalStoreEntry;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 583938 $, $Date: 2007-10-11 21:57:20 +0200 (Thu, 11 Oct 2007) $
+ */
+public class TicketGrantingService
+{
+    
+    /** the log for this class */
+    private static final Logger LOG = LoggerFactory.getLogger( TicketGrantingService.class );
+    
+    private static final InMemoryReplayCache replayCache = new InMemoryReplayCache();
+    private static final CipherTextHandler cipherTextHandler = new CipherTextHandler();
+
+    private static final String SERVICE_NAME = "Ticket-Granting Service (TGS)";
+
+    private static final ChecksumHandler checksumHandler = new ChecksumHandler();
+
+    public static void execute( TicketGrantingContext tgsContext ) throws Exception
+    {
+        if ( LOG.isDebugEnabled() )
+        {
+            monitorRequest( tgsContext );
+        }
+
+        configureTicketGranting( tgsContext);
+        selectEncryptionType( tgsContext );
+        getAuthHeader( tgsContext );
+        verifyTgt( tgsContext );
+        getTicketPrincipalEntry( tgsContext );
+        verifyTgtAuthHeader( tgsContext );
+        verifyBodyChecksum( tgsContext );
+        getRequestPrincipalEntry( tgsContext );
+        generateTicket( tgsContext );
+        buildReply( tgsContext );
+
+        if ( LOG.isDebugEnabled() )
+        {
+            monitorContext( tgsContext );
+            monitorReply( tgsContext );
+        }
+
+        sealReply( tgsContext );
+    }
+    
+    
+    private static void configureTicketGranting( TicketGrantingContext tgsContext ) throws KerberosException
+    {
+        KdcServer config = tgsContext.getConfig();
+        long clockSkew = config.getAllowableClockSkew();
+        replayCache.setClockSkew( clockSkew );
+        tgsContext.setReplayCache( replayCache );
+
+        tgsContext.setCipherTextHandler( cipherTextHandler );
+
+        if ( tgsContext.getRequest().getProtocolVersionNumber() != KerberosConstants.KERBEROS_V5 )
+        {
+            throw new KerberosException( ErrorType.KDC_ERR_BAD_PVNO );
+        }
+    }
+    
+
+    private static void monitorRequest( KdcContext kdcContext ) throws Exception
+    {
+        KdcRequest request = kdcContext.getRequest();
+
+        try
+        {
+            String clientAddress = kdcContext.getClientAddress().getHostAddress();
+
+            StringBuffer sb = new StringBuffer();
+
+            sb.append( "Received " + SERVICE_NAME + " request:" );
+            sb.append( "\n\t" + "messageType:           " + request.getMessageType() );
+            sb.append( "\n\t" + "protocolVersionNumber: " + request.getProtocolVersionNumber() );
+            sb.append( "\n\t" + "clientAddress:         " + clientAddress );
+            sb.append( "\n\t" + "nonce:                 " + request.getNonce() );
+            sb.append( "\n\t" + "kdcOptions:            " + request.getKdcOptions() );
+            sb.append( "\n\t" + "clientPrincipal:       " + request.getClientPrincipal() );
+            sb.append( "\n\t" + "serverPrincipal:       " + request.getServerPrincipal() );
+            sb.append( "\n\t" + "encryptionType:        " + KerberosUtils.getEncryptionTypesString( request.getEType() ) );
+            sb.append( "\n\t" + "realm:                 " + request.getRealm() );
+            sb.append( "\n\t" + "from time:             " + request.getFrom() );
+            sb.append( "\n\t" + "till time:             " + request.getTill() );
+            sb.append( "\n\t" + "renew-till time:       " + request.getRtime() );
+            sb.append( "\n\t" + "hostAddresses:         " + request.getAddresses() );
+
+            LOG.debug( sb.toString() );
+        }
+        catch ( Exception e )
+        {
+            // This is a monitor.  No exceptions should bubble up.
+            LOG.error( "Error in request monitor", e );
+        }
+    }
+    
+    
+    private static void selectEncryptionType( TicketGrantingContext tgsContext ) throws Exception
+    {
+        KdcContext kdcContext = (KdcContext)tgsContext;
+        KdcServer config = kdcContext.getConfig();
+
+        Set<EncryptionType> requestedTypes = kdcContext.getRequest().getEType();
+
+        EncryptionType bestType = KerberosUtils.getBestEncryptionType( requestedTypes, config.getEncryptionTypes() );
+
+        LOG.debug( "Session will use encryption type {}.", bestType );
+
+        if ( bestType == null )
+        {
+            throw new KerberosException( ErrorType.KDC_ERR_ETYPE_NOSUPP );
+        }
+
+        kdcContext.setEncryptionType( bestType );
+    }
+    
+    
+    private static void getAuthHeader( TicketGrantingContext tgsContext ) throws Exception
+    {
+        KdcRequest request = tgsContext.getRequest();
+
+        PaData[] preAuthData = request.getPreAuthData();
+
+        if ( preAuthData == null || preAuthData.length < 1 )
+        {
+            throw new KerberosException( ErrorType.KDC_ERR_PADATA_TYPE_NOSUPP );
+        }
+
+        byte[] undecodedAuthHeader = null;
+
+        for ( int ii = 0; ii < preAuthData.length; ii++ )
+        {
+            if ( preAuthData[ii].getPaDataType() == PaDataType.PA_TGS_REQ )
+            {
+                undecodedAuthHeader = preAuthData[ii].getPaDataValue();
+            }
+        }
+
+        if ( undecodedAuthHeader == null )
+        {
+            throw new KerberosException( ErrorType.KDC_ERR_PADATA_TYPE_NOSUPP );
+        }
+
+        ApplicationRequestDecoder decoder = new ApplicationRequestDecoder();
+        ApplicationRequest authHeader = decoder.decode( undecodedAuthHeader );
+        
+        Ticket tgt = authHeader.getTicket();
+
+        tgsContext.setAuthHeader( authHeader );
+        tgsContext.setTgt( tgt );
+    }
+    
+    
+    public static void verifyTgt( TicketGrantingContext tgsContext ) throws KerberosException
+    {
+        KdcServer config = tgsContext.getConfig();
+        Ticket tgt = tgsContext.getTgt();
+
+        // Check primary realm.
+        if ( !tgt.getRealm().equals( config.getPrimaryRealm() ) )
+        {
+            throw new KerberosException( ErrorType.KRB_AP_ERR_NOT_US );
+        }
+
+        String tgtServerName = tgt.getServerPrincipal().getName();
+        String requestServerName = tgsContext.getRequest().getServerPrincipal().getName();
+
+        /*
+         * if (tgt.sname is not a TGT for local realm and is not req.sname)
+         *     then error_out(KRB_AP_ERR_NOT_US);
+         */
+        if ( !tgtServerName.equals( config.getServicePrincipal().getName() )
+            && !tgtServerName.equals( requestServerName ) )
+        {
+            throw new KerberosException( ErrorType.KRB_AP_ERR_NOT_US );
+        }
+    }
+    
+    
+    private static void getTicketPrincipalEntry( TicketGrantingContext tgsContext ) throws KerberosException
+    {
+        KerberosPrincipal principal = tgsContext.getTgt().getServerPrincipal();
+        PrincipalStore store = tgsContext.getStore();
+
+        PrincipalStoreEntry entry = KerberosUtils.getEntry( principal, store, ErrorType.KDC_ERR_S_PRINCIPAL_UNKNOWN );
+        tgsContext.setTicketPrincipalEntry( entry );
+    }
+
+
+    private static void verifyTgtAuthHeader( TicketGrantingContext tgsContext ) throws KerberosException
+    {
+        ApplicationRequest authHeader = tgsContext.getAuthHeader();
+        Ticket tgt = tgsContext.getTgt();
+        
+        boolean isValidate = tgsContext.getRequest().getKdcOptions().get( KdcOptions.VALIDATE );
+
+        EncryptionType encryptionType = tgt.getEncPart().getEType();
+        EncryptionKey serverKey = tgsContext.getTicketPrincipalEntry().getKeyMap().get( encryptionType );
+
+        long clockSkew = tgsContext.getConfig().getAllowableClockSkew();
+        ReplayCache replayCache = tgsContext.getReplayCache();
+        boolean emptyAddressesAllowed = tgsContext.getConfig().isEmptyAddressesAllowed();
+        InetAddress clientAddress = tgsContext.getClientAddress();
+        CipherTextHandler cipherTextHandler = tgsContext.getCipherTextHandler();
+
+        Authenticator authenticator = KerberosUtils.verifyAuthHeader( authHeader, tgt, serverKey, clockSkew, replayCache,
+            emptyAddressesAllowed, clientAddress, cipherTextHandler, KeyUsage.NUMBER7, isValidate );
+
+        tgsContext.setAuthenticator( authenticator );
+    }
+    
+    
+    private static void verifyBodyChecksum( TicketGrantingContext tgsContext ) throws KerberosException
+    {
+        KdcServer config = tgsContext.getConfig();
+
+        if ( config.isBodyChecksumVerified() )
+        {
+            byte[] bodyBytes = tgsContext.getRequest().getBodyBytes();
+            Checksum authenticatorChecksum = tgsContext.getAuthenticator().getChecksum();
+
+            if ( authenticatorChecksum == null || authenticatorChecksum.getChecksumType() == null
+                || authenticatorChecksum.getChecksumValue() == null || bodyBytes == null )
+            {
+                throw new KerberosException( ErrorType.KRB_AP_ERR_INAPP_CKSUM );
+            }
+
+            LOG.debug( "Verifying body checksum type '{}'.", authenticatorChecksum.getChecksumType() );
+
+            checksumHandler.verifyChecksum( authenticatorChecksum, bodyBytes, null, KeyUsage.NUMBER8 );
+        }
+    }
+    
+
+    public static void getRequestPrincipalEntry( TicketGrantingContext tgsContext ) throws KerberosException
+    {
+        KerberosPrincipal principal = tgsContext.getRequest().getServerPrincipal();
+        PrincipalStore store = tgsContext.getStore();
+
+        PrincipalStoreEntry entry = KerberosUtils.getEntry( principal, store, ErrorType.KDC_ERR_S_PRINCIPAL_UNKNOWN );
+        tgsContext.setRequestPrincipalEntry( entry );
+    }
+
+    
+    private static void generateTicket( TicketGrantingContext tgsContext ) throws KerberosException
+    {
+        KdcRequest request = tgsContext.getRequest();
+        Ticket tgt = tgsContext.getTgt();
+        Authenticator authenticator = tgsContext.getAuthenticator();
+        CipherTextHandler cipherTextHandler = tgsContext.getCipherTextHandler();
+        KerberosPrincipal ticketPrincipal = request.getServerPrincipal();
+
+        EncryptionType encryptionType = tgsContext.getEncryptionType();
+        EncryptionKey serverKey = tgsContext.getRequestPrincipalEntry().getKeyMap().get( encryptionType );
+
+        KdcServer config = tgsContext.getConfig();
+
+        EncTicketPartModifier newTicketBody = new EncTicketPartModifier();
+
+        newTicketBody.setClientAddresses( tgt.getEncTicketPart().getClientAddresses() );
+
+        processFlags( config, request, tgt, newTicketBody );
+
+        EncryptionKey sessionKey = RandomKeyFactory.getRandomKey( tgsContext.getEncryptionType() );
+        newTicketBody.setSessionKey( sessionKey );
+
+        newTicketBody.setClientPrincipal( tgt.getEncTicketPart().getClientPrincipal() );
+
+        if ( request.getEncAuthorizationData() != null )
+        {
+            AuthorizationData authData = ( AuthorizationData ) cipherTextHandler.unseal( AuthorizationData.class,
+                authenticator.getSubSessionKey(), request.getEncAuthorizationData(), KeyUsage.NUMBER4 );
+            authData.add( tgt.getEncTicketPart().getAuthorizationData() );
+            newTicketBody.setAuthorizationData( authData );
+        }
+
+        processTransited( newTicketBody, tgt );
+
+        processTimes( config, request, newTicketBody, tgt );
+
+        EncTicketPart ticketPart = newTicketBody.getEncTicketPart();
+
+        if ( request.getOption( KdcOptions.ENC_TKT_IN_SKEY ) )
+        {
+            /*
+             * if (server not specified) then
+             *         server = req.second_ticket.client;
+             * endif
+             * 
+             * if ((req.second_ticket is not a TGT) or
+             *     (req.second_ticket.client != server)) then
+             *         error_out(KDC_ERR_POLICY);
+             * endif
+             * 
+             * new_tkt.enc-part := encrypt OCTET STRING using etype_for_key(second-ticket.key), second-ticket.key;
+             */
+            throw new KerberosException( ErrorType.KDC_ERR_BADOPTION );
+        }
+        else
+        {
+            EncryptedData encryptedData = cipherTextHandler.seal( serverKey, ticketPart, KeyUsage.NUMBER2 );
+
+            Ticket newTicket = new Ticket( ticketPrincipal, encryptedData );
+            newTicket.setEncTicketPart( ticketPart );
+
+            tgsContext.setNewTicket( newTicket );
+        }
+    }
+    
+
+    private static void buildReply( TicketGrantingContext tgsContext ) throws KerberosException
+    {
+        KdcRequest request = tgsContext.getRequest();
+        Ticket tgt = tgsContext.getTgt();
+        Ticket newTicket = tgsContext.getNewTicket();
+
+        TicketGrantReply reply = new TicketGrantReply();
+        reply.setClientPrincipal( tgt.getEncTicketPart().getClientPrincipal() );
+        reply.setTicket( newTicket );
+        reply.setKey( newTicket.getEncTicketPart().getSessionKey() );
+        reply.setNonce( request.getNonce() );
+        // TODO - resp.last-req := fetch_last_request_info(client); requires store
+        reply.setLastRequest( new LastRequest() );
+        reply.setFlags( newTicket.getEncTicketPart().getFlags() );
+        reply.setClientAddresses( newTicket.getEncTicketPart().getClientAddresses() );
+        reply.setAuthTime( newTicket.getEncTicketPart().getAuthTime() );
+        reply.setStartTime( newTicket.getEncTicketPart().getStartTime() );
+        reply.setEndTime( newTicket.getEncTicketPart().getEndTime() );
+        reply.setServerPrincipal( newTicket.getServerPrincipal() );
+
+        if ( newTicket.getEncTicketPart().getFlags().isRenewable() )
+        {
+            reply.setRenewTill( newTicket.getEncTicketPart().getRenewTill() );
+        }
+
+        tgsContext.setReply( reply );
+    }
+    
+    
+    private static void sealReply( TicketGrantingContext tgsContext ) throws KerberosException
+    {
+        TicketGrantReply reply = ( TicketGrantReply ) tgsContext.getReply();
+        Ticket tgt = tgsContext.getTgt();
+        CipherTextHandler cipherTextHandler = tgsContext.getCipherTextHandler();
+        Authenticator authenticator = tgsContext.getAuthenticator();
+
+        EncryptedData encryptedData;
+
+        if ( authenticator.getSubSessionKey() != null )
+        {
+            encryptedData = cipherTextHandler.seal( authenticator.getSubSessionKey(), reply, KeyUsage.NUMBER9 );
+        }
+        else
+        {
+            encryptedData = cipherTextHandler.seal( tgt.getEncTicketPart().getSessionKey(), reply, KeyUsage.NUMBER8 );
+        }
+
+        reply.setEncPart( encryptedData );
+    }
+    
+    
+    
+    private static void monitorContext( TicketGrantingContext tgsContext )
+    {
+        try
+        {
+            Ticket tgt = tgsContext.getTgt();
+            long clockSkew = tgsContext.getConfig().getAllowableClockSkew();
+            ChecksumType checksumType = tgsContext.getAuthenticator().getChecksum().getChecksumType();
+            InetAddress clientAddress = tgsContext.getClientAddress();
+            HostAddresses clientAddresses = tgt.getEncTicketPart().getClientAddresses();
+
+            boolean caddrContainsSender = false;
+            if ( tgt.getEncTicketPart().getClientAddresses() != null )
+            {
+                caddrContainsSender = tgt.getEncTicketPart().getClientAddresses().contains( new HostAddress( clientAddress ) );
+            }
+
+            StringBuffer sb = new StringBuffer();
+
+            sb.append( "Monitoring " + SERVICE_NAME + " context:" );
+
+            sb.append( "\n\t" + "clockSkew              " + clockSkew );
+            sb.append( "\n\t" + "checksumType           " + checksumType );
+            sb.append( "\n\t" + "clientAddress          " + clientAddress );
+            sb.append( "\n\t" + "clientAddresses        " + clientAddresses );
+            sb.append( "\n\t" + "caddr contains sender  " + caddrContainsSender );
+
+            KerberosPrincipal requestServerPrincipal = tgsContext.getRequest().getServerPrincipal();
+            PrincipalStoreEntry requestPrincipal = tgsContext.getRequestPrincipalEntry();
+
+            sb.append( "\n\t" + "principal              " + requestServerPrincipal );
+            sb.append( "\n\t" + "cn                     " + requestPrincipal.getCommonName() );
+            sb.append( "\n\t" + "realm                  " + requestPrincipal.getRealmName() );
+            sb.append( "\n\t" + "principal              " + requestPrincipal.getPrincipal() );
+            sb.append( "\n\t" + "SAM type               " + requestPrincipal.getSamType() );
+
+            KerberosPrincipal ticketServerPrincipal = tgsContext.getTgt().getServerPrincipal();
+            PrincipalStoreEntry ticketPrincipal = tgsContext.getTicketPrincipalEntry();
+
+            sb.append( "\n\t" + "principal              " + ticketServerPrincipal );
+            sb.append( "\n\t" + "cn                     " + ticketPrincipal.getCommonName() );
+            sb.append( "\n\t" + "realm                  " + ticketPrincipal.getRealmName() );
+            sb.append( "\n\t" + "principal              " + ticketPrincipal.getPrincipal() );
+            sb.append( "\n\t" + "SAM type               " + ticketPrincipal.getSamType() );
+
+            EncryptionType encryptionType = tgsContext.getTgt().getEncPart().getEType();
+            int keyVersion = ticketPrincipal.getKeyMap().get( encryptionType ).getKeyVersion();
+            sb.append( "\n\t" + "Ticket key type        " + encryptionType );
+            sb.append( "\n\t" + "Service key version    " + keyVersion );
+
+            LOG.debug( sb.toString() );
+        }
+        catch ( Exception e )
+        {
+            // This is a monitor.  No exceptions should bubble up.
+            LOG.error( "Error in context monitor", e );
+        }
+    }
+
+    
+    private static void monitorReply( KdcContext kdcContext )
+    {
+        Object reply = kdcContext.getReply();
+
+        if ( reply instanceof KdcReply )
+        {
+            KdcReply success = ( KdcReply ) reply;
+
+            try
+            {
+                StringBuffer sb = new StringBuffer();
+
+                sb.append( "Responding with " + SERVICE_NAME + " reply:" );
+                sb.append( "\n\t" + "messageType:           " + success.getMessageType() );
+                sb.append( "\n\t" + "protocolVersionNumber: " + success.getProtocolVersionNumber() );
+                sb.append( "\n\t" + "nonce:                 " + success.getNonce() );
+                sb.append( "\n\t" + "clientPrincipal:       " + success.getClientPrincipal() );
+                sb.append( "\n\t" + "client realm:          " + success.getClientRealm() );
+                sb.append( "\n\t" + "serverPrincipal:       " + success.getServerPrincipal() );
+                sb.append( "\n\t" + "server realm:          " + success.getServerRealm() );
+                sb.append( "\n\t" + "auth time:             " + success.getAuthTime() );
+                sb.append( "\n\t" + "start time:            " + success.getStartTime() );
+                sb.append( "\n\t" + "end time:              " + success.getEndTime() );
+                sb.append( "\n\t" + "renew-till time:       " + success.getRenewTill() );
+                sb.append( "\n\t" + "hostAddresses:         " + success.getClientAddresses() );
+
+                LOG.debug( sb.toString() );
+            }
+            catch ( Exception e )
+            {
+                // This is a monitor.  No exceptions should bubble up.
+                LOG.error( "Error in reply monitor", e );
+            }
+        }
+    }
+    
+
+    
+    private static void processFlags( KdcServer config, KdcRequest request, Ticket tgt,
+        EncTicketPartModifier newTicketBody ) throws KerberosException
+    {
+        if ( tgt.getEncTicketPart().getFlags().isPreAuth() )
+        {
+            newTicketBody.setFlag( TicketFlag.PRE_AUTHENT );
+        }
+
+        if ( request.getOption( KdcOptions.FORWARDABLE ) )
+        {
+            if ( !config.isForwardableAllowed() )
+            {
+                throw new KerberosException( ErrorType.KDC_ERR_POLICY );
+            }
+
+            if ( !tgt.getEncTicketPart().getFlags().isForwardable() )
+            {
+                throw new KerberosException( ErrorType.KDC_ERR_BADOPTION );
+            }
+
+            newTicketBody.setFlag( TicketFlag.FORWARDABLE );
+        }
+
+        if ( request.getOption( KdcOptions.FORWARDED ) )
+        {
+            if ( !config.isForwardableAllowed() )
+            {
+                throw new KerberosException( ErrorType.KDC_ERR_POLICY );
+            }
+
+            if ( !tgt.getEncTicketPart().getFlags().isForwardable() )
+            {
+                throw new KerberosException( ErrorType.KDC_ERR_BADOPTION );
+            }
+
+            if ( request.getAddresses() != null && request.getAddresses().getAddresses() != null
+                && request.getAddresses().getAddresses().length > 0 )
+            {
+                newTicketBody.setClientAddresses( request.getAddresses() );
+            }
+            else
+            {
+                if ( !config.isEmptyAddressesAllowed() )
+                {
+                    throw new KerberosException( ErrorType.KDC_ERR_POLICY );
+                }
+            }
+
+            newTicketBody.setFlag( TicketFlag.FORWARDED );
+        }
+
+        if ( tgt.getEncTicketPart().getFlags().isForwarded() )
+        {
+            newTicketBody.setFlag( TicketFlag.FORWARDED );
+        }
+
+        if ( request.getOption( KdcOptions.PROXIABLE ) )
+        {
+            if ( !config.isProxiableAllowed() )
+            {
+                throw new KerberosException( ErrorType.KDC_ERR_POLICY );
+            }
+
+            if ( !tgt.getEncTicketPart().getFlags().isProxiable() )
+            {
+                throw new KerberosException( ErrorType.KDC_ERR_BADOPTION );
+            }
+
+            newTicketBody.setFlag( TicketFlag.PROXIABLE );
+        }
+
+        if ( request.getOption( KdcOptions.PROXY ) )
+        {
+            if ( !config.isProxiableAllowed() )
+            {
+                throw new KerberosException( ErrorType.KDC_ERR_POLICY );
+            }
+
+            if ( !tgt.getEncTicketPart().getFlags().isProxiable() )
+            {
+                throw new KerberosException( ErrorType.KDC_ERR_BADOPTION );
+            }
+
+            if ( request.getAddresses() != null && request.getAddresses().getAddresses() != null
+                && request.getAddresses().getAddresses().length > 0 )
+            {
+                newTicketBody.setClientAddresses( request.getAddresses() );
+            }
+            else
+            {
+                if ( !config.isEmptyAddressesAllowed() )
+                {
+                    throw new KerberosException( ErrorType.KDC_ERR_POLICY );
+                }
+            }
+
+            newTicketBody.setFlag( TicketFlag.PROXY );
+        }
+
+        if ( request.getOption( KdcOptions.ALLOW_POSTDATE ) )
+        {
+            if ( !config.isPostdatedAllowed() )
+            {
+                throw new KerberosException( ErrorType.KDC_ERR_POLICY );
+            }
+
+            if ( !tgt.getEncTicketPart().getFlags().isMayPosdate() )
+            {
+                throw new KerberosException( ErrorType.KDC_ERR_BADOPTION );
+            }
+
+            newTicketBody.setFlag( TicketFlag.MAY_POSTDATE );
+        }
+
+        /*
+         * "Otherwise, if the TGT has the MAY-POSTDATE flag set, then the resulting
+         * ticket will be postdated, and the requested starttime is checked against
+         * the policy of the local realm.  If acceptable, the ticket's starttime is
+         * set as requested, and the INVALID flag is set.  The postdated ticket MUST
+         * be validated before use by presenting it to the KDC after the starttime
+         * has been reached.  However, in no case may the starttime, endtime, or
+         * renew-till time of a newly-issued postdated ticket extend beyond the
+         * renew-till time of the TGT."
+         */
+        if ( request.getOption( KdcOptions.POSTDATED ) )
+        {
+            if ( !config.isPostdatedAllowed() )
+            {
+                throw new KerberosException( ErrorType.KDC_ERR_POLICY );
+            }
+
+            if ( !tgt.getEncTicketPart().getFlags().isMayPosdate() )
+            {
+                throw new KerberosException( ErrorType.KDC_ERR_BADOPTION );
+            }
+
+            newTicketBody.setFlag( TicketFlag.POSTDATED );
+            newTicketBody.setFlag( TicketFlag.INVALID );
+
+            newTicketBody.setStartTime( request.getFrom() );
+        }
+
+        if ( request.getOption( KdcOptions.VALIDATE ) )
+        {
+            if ( !config.isPostdatedAllowed() )
+            {
+                throw new KerberosException( ErrorType.KDC_ERR_POLICY );
+            }
+
+            if ( !tgt.getEncTicketPart().getFlags().isInvalid() )
+            {
+                throw new KerberosException( ErrorType.KDC_ERR_POLICY );
+            }
+
+            KerberosTime startTime = ( tgt.getEncTicketPart().getStartTime() != null ) ? 
+                    tgt.getEncTicketPart().getStartTime() : 
+                        tgt.getEncTicketPart().getAuthTime();
+
+            if ( startTime.greaterThan( new KerberosTime() ) )
+            {
+                throw new KerberosException( ErrorType.KRB_AP_ERR_TKT_NYV );
+            }
+
+            echoTicket( newTicketBody, tgt );
+            newTicketBody.clearFlag( TicketFlag.INVALID );
+        }
+
+        if ( request.getOption( KdcOptions.RESERVED ) )
+        {
+            throw new KerberosException( ErrorType.KDC_ERR_BADOPTION );
+        }
+    }
+
+
+    private static void processTimes( KdcServer config, KdcRequest request, EncTicketPartModifier newTicketBody,
+        Ticket tgt ) throws KerberosException
+    {
+        KerberosTime now = new KerberosTime();
+
+        newTicketBody.setAuthTime( tgt.getEncTicketPart().getAuthTime() );
+
+        KerberosTime startTime = request.getFrom();
+
+        /*
+         * "If the requested starttime is absent, indicates a time in the past,
+         * or is within the window of acceptable clock skew for the KDC and the
+         * POSTDATE option has not been specified, then the starttime of the
+         * ticket is set to the authentication server's current time."
+         */
+        if ( startTime == null || startTime.lessThan( now ) || startTime.isInClockSkew( config.getAllowableClockSkew() )
+            && !request.getOption( KdcOptions.POSTDATED ) )
+        {
+            startTime = now;
+        }
+
+        /*
+         * "If it indicates a time in the future beyond the acceptable clock skew,
+         * but the POSTDATED option has not been specified or the MAY-POSTDATE flag
+         * is not set in the TGT, then the error KDC_ERR_CANNOT_POSTDATE is
+         * returned."
+         */
+        if ( startTime != null && startTime.greaterThan( now )
+            && !startTime.isInClockSkew( config.getAllowableClockSkew() )
+            && ( !request.getOption( KdcOptions.POSTDATED ) || !tgt.getEncTicketPart().getFlags().isMayPosdate() ) )
+        {
+            throw new KerberosException( ErrorType.KDC_ERR_CANNOT_POSTDATE );
+        }
+
+        KerberosTime renewalTime = null;
+        KerberosTime kerberosEndTime = null;
+
+        if ( request.getOption( KdcOptions.RENEW ) )
+        {
+            if ( !config.isRenewableAllowed() )
+            {
+                throw new KerberosException( ErrorType.KDC_ERR_POLICY );
+            }
+
+            if ( !tgt.getEncTicketPart().getFlags().isRenewable() )
+            {
+                throw new KerberosException( ErrorType.KDC_ERR_BADOPTION );
+            }
+
+            if ( tgt.getEncTicketPart().getRenewTill().lessThan( now ) )
+            {
+                throw new KerberosException( ErrorType.KRB_AP_ERR_TKT_EXPIRED );
+            }
+
+            echoTicket( newTicketBody, tgt );
+
+            newTicketBody.setStartTime( now );
+
+            KerberosTime tgtStartTime = ( tgt.getEncTicketPart().getStartTime() != null ) ? 
+                tgt.getEncTicketPart().getStartTime() : 
+                    tgt.getEncTicketPart().getAuthTime();
+
+            long oldLife = tgt.getEncTicketPart().getEndTime().getTime() - tgtStartTime.getTime();
+
+            kerberosEndTime = new KerberosTime( Math.min( tgt.getEncTicketPart().getRenewTill().getTime(), now.getTime() + oldLife ) );
+            newTicketBody.setEndTime( kerberosEndTime );
+        }
+        else
+        {
+            if ( newTicketBody.getEncTicketPart().getStartTime() == null )
+            {
+                newTicketBody.setStartTime( now );
+            }
+
+            KerberosTime till;
+            if ( request.getTill().isZero() )
+            {
+                till = KerberosTime.INFINITY;
+            }
+            else
+            {
+                till = request.getTill();
+            }
+
+            /*
+             * The end time is the minimum of (a) the requested till time or (b)
+             * the start time plus maximum lifetime as configured in policy or (c)
+             * the end time of the TGT.
+             */
+            List<KerberosTime> minimizer = new ArrayList<KerberosTime>();
+            minimizer.add( till );
+            minimizer.add( new KerberosTime( startTime.getTime() + config.getMaximumTicketLifetime() ) );
+            minimizer.add( tgt.getEncTicketPart().getEndTime() );
+            kerberosEndTime = Collections.min( minimizer );
+
+            newTicketBody.setEndTime( kerberosEndTime );
+
+            if ( request.getOption( KdcOptions.RENEWABLE_OK ) && kerberosEndTime.lessThan( request.getTill() )
+                && tgt.getEncTicketPart().getFlags().isRenewable() )
+            {
+                if ( !config.isRenewableAllowed() )
+                {
+                    throw new KerberosException( ErrorType.KDC_ERR_POLICY );
+                }
+
+                // We set the RENEWABLE option for later processing.                           
+                request.setOption( KdcOptions.RENEWABLE );
+                long rtime = Math.min( request.getTill().getTime(), tgt.getEncTicketPart().getRenewTill().getTime() );
+                renewalTime = new KerberosTime( rtime );
+            }
+        }
+
+        if ( renewalTime == null )
+        {
+            renewalTime = request.getRtime();
+        }
+
+        KerberosTime rtime;
+        if ( renewalTime != null && renewalTime.isZero() )
+        {
+            rtime = KerberosTime.INFINITY;
+        }
+        else
+        {
+            rtime = renewalTime;
+        }
+
+        if ( request.getOption( KdcOptions.RENEWABLE ) && tgt.getEncTicketPart().getFlags().isRenewable() )
+        {
+            if ( !config.isRenewableAllowed() )
+            {
+                throw new KerberosException( ErrorType.KDC_ERR_POLICY );
+            }
+
+            newTicketBody.setFlag( TicketFlag.RENEWABLE );
+
+            /*
+             * The renew-till time is the minimum of (a) the requested renew-till
+             * time or (b) the start time plus maximum renewable lifetime as
+             * configured in policy or (c) the renew-till time of the TGT.
+             */
+            List<KerberosTime> minimizer = new ArrayList<KerberosTime>();
+
+            /*
+             * 'rtime' KerberosTime is OPTIONAL
+             */
+            if ( rtime != null )
+            {
+                minimizer.add( rtime );
+            }
+
+            minimizer.add( new KerberosTime( startTime.getTime() + config.getMaximumRenewableLifetime() ) );
+            minimizer.add( tgt.getEncTicketPart().getRenewTill() );
+            newTicketBody.setRenewTill( Collections.min( minimizer ) );
+        }
+
+        /*
+         * "If the requested expiration time minus the starttime (as determined
+         * above) is less than a site-determined minimum lifetime, an error
+         * message with code KDC_ERR_NEVER_VALID is returned."
+         */
+        if ( kerberosEndTime.lessThan( startTime ) )
+        {
+            throw new KerberosException( ErrorType.KDC_ERR_NEVER_VALID );
+        }
+
+        long ticketLifeTime = Math.abs( startTime.getTime() - kerberosEndTime.getTime() );
+        if ( ticketLifeTime < config.getAllowableClockSkew() )
+        {
+            throw new KerberosException( ErrorType.KDC_ERR_NEVER_VALID );
+        }
+    }
+
+
+    /*
+     * if (realm_tgt_is_for(tgt) := tgt.realm) then
+     *         // tgt issued by local realm
+     *         new_tkt.transited := tgt.transited;
+     * else
+     *         // was issued for this realm by some other realm
+     *         if (tgt.transited.tr-type not supported) then
+     *                 error_out(KDC_ERR_TRTYPE_NOSUPP);
+     *         endif
+     * 
+     *         new_tkt.transited := compress_transited(tgt.transited + tgt.realm)
+     * endif
+     */    
+    private static void processTransited( EncTicketPartModifier newTicketBody, Ticket tgt )
+    {
+        // TODO - currently no transited support other than local
+        newTicketBody.setTransitedEncoding( tgt.getEncTicketPart().getTransitedEncoding() );
+    }
+
+    
+    private static void echoTicket( EncTicketPartModifier newTicketBody, Ticket tgt )
+    {
+        EncTicketPart encTicketpart = tgt.getEncTicketPart();
+        newTicketBody.setAuthorizationData( encTicketpart.getAuthorizationData() );
+        newTicketBody.setAuthTime( encTicketpart.getAuthTime() );
+        newTicketBody.setClientAddresses( encTicketpart.getClientAddresses() );
+        newTicketBody.setClientPrincipal( encTicketpart.getClientPrincipal() );
+        newTicketBody.setEndTime( encTicketpart.getEndTime() );
+        newTicketBody.setFlags( encTicketpart.getFlags() );
+        newTicketBody.setRenewTill( encTicketpart.getRenewTill() );
+        newTicketBody.setSessionKey( encTicketpart.getSessionKey() );
+        newTicketBody.setTransitedEncoding( encTicketpart.getTransitedEncoding() );
+    }
+}
diff --git a/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/kdc/ticketgrant/package-info.java b/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/kdc/ticketgrant/package-info.java
new file mode 100644
index 0000000..37f69bc
--- /dev/null
+++ b/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/kdc/ticketgrant/package-info.java
@@ -0,0 +1,28 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+/**
+ * Provides the KDC's Ticket-Granting Service (TGS).
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+
+package org.apache.directory.server.kerberos.kdc.ticketgrant;
diff --git a/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/protocol/KerberosProtocolHandler.java b/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/protocol/KerberosProtocolHandler.java
new file mode 100644
index 0000000..251f34b
--- /dev/null
+++ b/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/protocol/KerberosProtocolHandler.java
@@ -0,0 +1,264 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.protocol;
+
+
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.apache.directory.server.kerberos.kdc.KdcServer;
+import org.apache.directory.server.kerberos.kdc.authentication.AuthenticationContext;
+import org.apache.directory.server.kerberos.kdc.authentication.AuthenticationService;
+import org.apache.directory.server.kerberos.kdc.ticketgrant.TicketGrantingContext;
+import org.apache.directory.server.kerberos.kdc.ticketgrant.TicketGrantingService;
+import org.apache.directory.server.kerberos.shared.KerberosMessageType;
+import org.apache.directory.server.kerberos.shared.exceptions.ErrorType;
+import org.apache.directory.server.kerberos.shared.exceptions.KerberosException;
+import org.apache.directory.server.kerberos.shared.messages.ErrorMessage;
+import org.apache.directory.server.kerberos.shared.messages.ErrorMessageModifier;
+import org.apache.directory.server.kerberos.shared.messages.KdcRequest;
+import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
+import org.apache.directory.server.kerberos.shared.store.PrincipalStore;
+import org.apache.mina.common.IdleStatus;
+import org.apache.mina.common.IoHandler;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.common.TransportType;
+import org.apache.mina.filter.codec.ProtocolCodecFilter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * The Kerberos protocol handler for MINA which handles requests for the authentication
+ * service and the ticket granting service of the KDC.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class KerberosProtocolHandler implements IoHandler
+{
+    private static final Logger log = LoggerFactory.getLogger( KerberosProtocolHandler.class );
+
+    private KdcServer config;
+    private PrincipalStore store;
+    private static final String CONTEXT_KEY = "context";
+    
+
+
+    /**
+     * Creates a new instance of KerberosProtocolHandler.
+     *
+     * @param config
+     * @param store
+     */
+    public KerberosProtocolHandler( KdcServer config, PrincipalStore store )
+    {
+        this.config = config;
+        this.store = store;
+    }
+
+
+    public void sessionCreated( IoSession session ) throws Exception
+    {
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "{} CREATED:  {}", session.getRemoteAddress(), session.getTransportType() );
+        }
+
+        if ( session.getTransportType() == TransportType.DATAGRAM )
+        {
+            session.getFilterChain().addFirst( "codec",
+                new ProtocolCodecFilter( KerberosUdpProtocolCodecFactory.getInstance() ) );
+        }
+        else
+        {
+            session.getFilterChain().addFirst( "codec",
+                new ProtocolCodecFilter( KerberosTcpProtocolCodecFactory.getInstance() ) );
+        }
+    }
+
+
+    public void sessionOpened( IoSession session )
+    {
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "{} OPENED", session.getRemoteAddress() );
+        }
+    }
+
+
+    public void sessionClosed( IoSession session )
+    {
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "{} CLOSED", session.getRemoteAddress() );
+        }
+    }
+
+
+    public void sessionIdle( IoSession session, IdleStatus status )
+    {
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "{} IDLE ({})", session.getRemoteAddress(), status );
+        }
+    }
+
+
+    public void exceptionCaught( IoSession session, Throwable cause )
+    {
+        log.error( session.getRemoteAddress() + " EXCEPTION", cause );
+        session.close();
+    }
+
+
+    public void messageReceived( IoSession session, Object message )
+    {
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "{} RCVD:  {}", session.getRemoteAddress(), message );
+        }
+
+        InetAddress clientAddress = ( ( InetSocketAddress ) session.getRemoteAddress() ).getAddress();
+        KdcRequest request = ( KdcRequest ) message;
+
+        KerberosMessageType messageType = request.getMessageType();
+
+        try
+        {
+            switch ( messageType )
+            {
+                case AS_REQ :
+                    AuthenticationContext authContext = new AuthenticationContext();
+                    authContext.setConfig( config );
+                    authContext.setStore( store );
+                    authContext.setClientAddress( clientAddress );
+                    authContext.setRequest( request );
+                    session.setAttribute( CONTEXT_KEY, authContext );
+
+                    AuthenticationService.execute( authContext );
+
+                    session.write( authContext.getReply() );
+                    break;
+
+                case TGS_REQ:
+                    TicketGrantingContext tgsContext = new TicketGrantingContext();
+                    tgsContext.setConfig( config );
+                    tgsContext.setStore( store );
+                    tgsContext.setClientAddress( clientAddress );
+                    tgsContext.setRequest( request );
+                    session.setAttribute( CONTEXT_KEY, tgsContext );
+
+                    TicketGrantingService.execute( tgsContext );
+
+                    session.write( tgsContext.getReply() );
+                    break;
+
+                case AS_REP:
+                case TGS_REP:
+                    throw new KerberosException( ErrorType.KRB_AP_ERR_BADDIRECTION );
+
+                default:
+                    throw new KerberosException( ErrorType.KRB_AP_ERR_MSG_TYPE );
+            }
+        }
+        catch ( KerberosException ke )
+        {
+            String messageText = ke.getMessage() + " (" + ke.getErrorCode() + ")";
+
+            if ( log.isDebugEnabled() )
+            {
+                log.warn( messageText, ke );
+            }
+            else
+            {
+                log.warn( messageText );
+            }
+
+            ErrorMessage error = getErrorMessage( config.getServicePrincipal(), ke );
+
+            if ( log.isDebugEnabled() )
+            {
+                logErrorMessage( error );
+            }
+
+            session.write( error );
+        }
+        catch ( Exception e )
+        {
+            log.error( "Unexpected exception:  " + e.getMessage(), e );
+
+            session.write( getErrorMessage( config.getServicePrincipal(), new KerberosException(
+                ErrorType.KDC_ERR_SVC_UNAVAILABLE ) ) );
+        }
+    }
+
+
+    public void messageSent( IoSession session, Object message )
+    {
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "{} SENT:  {}", session.getRemoteAddress(), message );
+        }
+    }
+
+
+    protected ErrorMessage getErrorMessage( KerberosPrincipal principal, KerberosException exception )
+    {
+        ErrorMessageModifier modifier = new ErrorMessageModifier();
+
+        KerberosTime now = new KerberosTime();
+
+        modifier.setErrorCode( exception.getErrorCode() );
+        modifier.setExplanatoryText( exception.getMessage() );
+        modifier.setServerPrincipal( principal );
+        modifier.setServerTime( now );
+        modifier.setServerMicroSecond( 0 );
+        modifier.setExplanatoryData( exception.getExplanatoryData() );
+
+        return modifier.getErrorMessage();
+    }
+
+
+    protected void logErrorMessage( ErrorMessage error )
+    {
+        try
+        {
+            StringBuffer sb = new StringBuffer();
+
+            sb.append( "Responding to request with error:" );
+            sb.append( "\n\t" + "explanatory text:      " + error.getExplanatoryText() );
+            sb.append( "\n\t" + "error code:            " + error.getErrorCode() );
+            sb.append( "\n\t" + "clientPrincipal:       " + error.getClientPrincipal() );
+            sb.append( "\n\t" + "client time:           " + error.getServerTime() );
+            sb.append( "\n\t" + "serverPrincipal:       " + error.getServerPrincipal() );
+            sb.append( "\n\t" + "server time:           " + error.getClientTime() );
+
+            log.debug( sb.toString() );
+        }
+        catch ( Exception e )
+        {
+            // This is a monitor.  No exceptions should bubble up.
+            log.error( "Error in reply monitor", e );
+        }
+    }
+}
diff --git a/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/protocol/KerberosTcpDecoder.java b/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/protocol/KerberosTcpDecoder.java
new file mode 100644
index 0000000..9eedb33
--- /dev/null
+++ b/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/protocol/KerberosTcpDecoder.java
@@ -0,0 +1,92 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.protocol;
+
+
+import org.apache.directory.server.kerberos.shared.io.decoder.KdcRequestDecoder;
+import org.apache.mina.common.BufferDataException;
+import org.apache.mina.common.ByteBuffer;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.filter.codec.CumulativeProtocolDecoder;
+import org.apache.mina.filter.codec.ProtocolDecoderOutput;
+
+
+/**
+ * A {@link CumulativeProtocolDecoder} which supports Kerberos operation over TCP,
+ * by reassembling split packets prior to ASN.1 DER decoding.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class KerberosTcpDecoder extends CumulativeProtocolDecoder
+{
+    private KdcRequestDecoder decoder = new KdcRequestDecoder();
+
+    private int maxObjectSize = 16384; // 16KB
+
+
+    /**
+     * Returns the allowed maximum size of the object to be decoded.
+     * If the size of the object to be decoded exceeds this value, this
+     * decoder will throw a {@link BufferDataException}.  The default
+     * value is <tt>16384</tt> (16KB).
+     * 
+     * @return The max object size.
+     */
+    public int getMaxObjectSize()
+    {
+        return maxObjectSize;
+    }
+
+
+    /**
+     * Sets the allowed maximum size of the object to be decoded.
+     * If the size of the object to be decoded exceeds this value, this
+     * decoder will throw a {@link BufferDataException}.  The default
+     * value is <tt>16384</tt> (16KB).
+     * 
+     * @param maxObjectSize 
+     */
+    public void setMaxObjectSize( int maxObjectSize )
+    {
+        if ( maxObjectSize <= 0 )
+        {
+            throw new IllegalArgumentException( "maxObjectSize: " + maxObjectSize );
+        }
+
+        this.maxObjectSize = maxObjectSize;
+    }
+
+
+    @Override
+    protected boolean doDecode( IoSession session, ByteBuffer in, ProtocolDecoderOutput out ) throws Exception
+    {
+        if ( !in.prefixedDataAvailable( 4, maxObjectSize ) )
+        {
+            return false;
+        }
+
+        in.getInt();
+
+        out.write( decoder.decode( in.buf() ) );
+
+        return true;
+    }
+}
diff --git a/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/protocol/KerberosTcpEncoder.java b/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/protocol/KerberosTcpEncoder.java
new file mode 100644
index 0000000..0ab14a1
--- /dev/null
+++ b/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/protocol/KerberosTcpEncoder.java
@@ -0,0 +1,80 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.protocol;
+
+
+import java.io.IOException;
+
+import org.apache.directory.server.kerberos.shared.io.encoder.ErrorMessageEncoder;
+import org.apache.directory.server.kerberos.shared.io.encoder.KdcReplyEncoder;
+import org.apache.directory.server.kerberos.shared.messages.ErrorMessage;
+import org.apache.directory.server.kerberos.shared.messages.KdcReply;
+import org.apache.mina.common.ByteBuffer;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.filter.codec.ProtocolEncoderAdapter;
+import org.apache.mina.filter.codec.ProtocolEncoderOutput;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class KerberosTcpEncoder extends ProtocolEncoderAdapter
+{
+    private KdcReplyEncoder replyEncoder = new KdcReplyEncoder();
+    private ErrorMessageEncoder errorEncoder = new ErrorMessageEncoder();
+
+
+    public void encode( IoSession session, Object message, ProtocolEncoderOutput out ) throws IOException
+    {
+        ByteBuffer buf = ByteBuffer.allocate( 1024 );
+
+        // make space for int length
+        buf.putInt( 0 );
+
+        if ( message instanceof KdcReply )
+        {
+            replyEncoder.encode( ( KdcReply ) message, buf.buf() );
+        }
+        else
+        {
+            if ( message instanceof ErrorMessage )
+            {
+                errorEncoder.encode( ( ErrorMessage ) message, buf.buf() );
+            }
+        }
+
+        // mark position
+        int pos = buf.position();
+
+        // length is the data minus 4 bytes for the pre-pended length
+        int recordLength = buf.position() - 4;
+
+        // write the length
+        buf.rewind();
+        buf.putInt( recordLength );
+
+        // set the position back before flipping the buffer
+        buf.position( pos );
+        buf.flip();
+
+        out.write( buf );
+    }
+}
diff --git a/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/protocol/KerberosTcpProtocolCodecFactory.java b/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/protocol/KerberosTcpProtocolCodecFactory.java
new file mode 100644
index 0000000..141462c
--- /dev/null
+++ b/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/protocol/KerberosTcpProtocolCodecFactory.java
@@ -0,0 +1,66 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.protocol;
+
+
+import org.apache.mina.filter.codec.ProtocolCodecFactory;
+import org.apache.mina.filter.codec.ProtocolDecoder;
+import org.apache.mina.filter.codec.ProtocolEncoder;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class KerberosTcpProtocolCodecFactory implements ProtocolCodecFactory
+{
+    private static final KerberosTcpProtocolCodecFactory INSTANCE = new KerberosTcpProtocolCodecFactory();
+
+
+    /**
+     * Returns the singleton {@link KerberosTcpProtocolCodecFactory}.
+     *
+     * @return The singleton {@link KerberosTcpProtocolCodecFactory}.
+     */
+    public static KerberosTcpProtocolCodecFactory getInstance()
+    {
+        return INSTANCE;
+    }
+
+
+    private KerberosTcpProtocolCodecFactory()
+    {
+        // Private constructor prevents instantiation outside this class.
+    }
+
+
+    public ProtocolEncoder getEncoder()
+    {
+        // Create a new encoder.
+        return new KerberosTcpEncoder();
+    }
+
+
+    public ProtocolDecoder getDecoder()
+    {
+        // Create a new decoder.
+        return new KerberosTcpDecoder();
+    }
+}
diff --git a/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/protocol/KerberosUdpDecoder.java b/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/protocol/KerberosUdpDecoder.java
new file mode 100644
index 0000000..a405547
--- /dev/null
+++ b/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/protocol/KerberosUdpDecoder.java
@@ -0,0 +1,45 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.protocol;
+
+
+import java.io.IOException;
+
+import org.apache.directory.server.kerberos.shared.io.decoder.KdcRequestDecoder;
+import org.apache.mina.common.ByteBuffer;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.filter.codec.ProtocolDecoderAdapter;
+import org.apache.mina.filter.codec.ProtocolDecoderOutput;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class KerberosUdpDecoder extends ProtocolDecoderAdapter
+{
+    private KdcRequestDecoder decoder = new KdcRequestDecoder();
+
+
+    public void decode( IoSession session, ByteBuffer in, ProtocolDecoderOutput out ) throws IOException
+    {
+        out.write( decoder.decode( in.buf() ) );
+    }
+}
diff --git a/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/protocol/KerberosUdpEncoder.java b/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/protocol/KerberosUdpEncoder.java
new file mode 100644
index 0000000..c8ecd21
--- /dev/null
+++ b/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/protocol/KerberosUdpEncoder.java
@@ -0,0 +1,65 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.protocol;
+
+
+import java.io.IOException;
+
+import org.apache.directory.server.kerberos.shared.io.encoder.ErrorMessageEncoder;
+import org.apache.directory.server.kerberos.shared.io.encoder.KdcReplyEncoder;
+import org.apache.directory.server.kerberos.shared.messages.ErrorMessage;
+import org.apache.directory.server.kerberos.shared.messages.KdcReply;
+import org.apache.mina.common.ByteBuffer;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.filter.codec.ProtocolEncoderAdapter;
+import org.apache.mina.filter.codec.ProtocolEncoderOutput;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class KerberosUdpEncoder extends ProtocolEncoderAdapter
+{
+    private KdcReplyEncoder replyEncoder = new KdcReplyEncoder();
+    private ErrorMessageEncoder errorEncoder = new ErrorMessageEncoder();
+
+
+    public void encode( IoSession session, Object message, ProtocolEncoderOutput out ) throws IOException
+    {
+        ByteBuffer buf = ByteBuffer.allocate( 1024 );
+
+        if ( message instanceof KdcReply )
+        {
+            replyEncoder.encode( ( KdcReply ) message, buf.buf() );
+        }
+        else
+        {
+            if ( message instanceof ErrorMessage )
+            {
+                errorEncoder.encode( ( ErrorMessage ) message, buf.buf() );
+            }
+        }
+
+        buf.flip();
+
+        out.write( buf );
+    }
+}
diff --git a/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/protocol/KerberosUdpProtocolCodecFactory.java b/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/protocol/KerberosUdpProtocolCodecFactory.java
new file mode 100644
index 0000000..55ee8e4
--- /dev/null
+++ b/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/protocol/KerberosUdpProtocolCodecFactory.java
@@ -0,0 +1,66 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.protocol;
+
+
+import org.apache.mina.filter.codec.ProtocolCodecFactory;
+import org.apache.mina.filter.codec.ProtocolDecoder;
+import org.apache.mina.filter.codec.ProtocolEncoder;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class KerberosUdpProtocolCodecFactory implements ProtocolCodecFactory
+{
+    private static final KerberosUdpProtocolCodecFactory INSTANCE = new KerberosUdpProtocolCodecFactory();
+
+
+    /**
+     * Returns the singleton {@link KerberosUdpProtocolCodecFactory}.
+     *
+     * @return The singleton {@link KerberosUdpProtocolCodecFactory}.
+     */
+    public static KerberosUdpProtocolCodecFactory getInstance()
+    {
+        return INSTANCE;
+    }
+
+
+    private KerberosUdpProtocolCodecFactory()
+    {
+        // Private constructor prevents instantiation outside this class.
+    }
+
+
+    public ProtocolEncoder getEncoder()
+    {
+        // Create a new encoder.
+        return new KerberosUdpEncoder();
+    }
+
+
+    public ProtocolDecoder getDecoder()
+    {
+        // Create a new decoder.
+        return new KerberosUdpDecoder();
+    }
+}
diff --git a/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/protocol/package-info.java b/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/protocol/package-info.java
new file mode 100644
index 0000000..95289cf
--- /dev/null
+++ b/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/protocol/package-info.java
@@ -0,0 +1,30 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+/**
+ * Provides the {@link org.apache.mina.common.IoHandler} and associated {@link org.apache.mina.filter.codec.ProtocolCodecFactory}
+ * required to implement the KDC's Authentication Service (AS) and
+ * Ticket-Granting Service (TGS) with the MINA NIO framework.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+
+package org.apache.directory.server.kerberos.protocol;
diff --git a/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/sam/KeyIntegrityChecker.java b/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/sam/KeyIntegrityChecker.java
new file mode 100644
index 0000000..2b36c7d
--- /dev/null
+++ b/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/sam/KeyIntegrityChecker.java
@@ -0,0 +1,46 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.sam;
+
+
+import javax.security.auth.kerberos.KerberosKey;
+
+
+/**
+ * Checks the integrity of a kerberos key to decode-decrypt an encrypted
+ * generalized timestamp representing the pre-auth data.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public interface KeyIntegrityChecker
+{
+    /**
+     * Checks the integrity of a KerberosKey to decrypt-decode and compare an
+     * encrypted encoded generalized timestamp representing the preauth data.
+     *
+     * @param preauthData the generalized timestamp encrypted with client hotp
+     * generated KerberosKey
+     * @param key the KerberosKey generated from server side hotp value
+     * @return true if the key can decrypt-decode and make sense out of the
+     * timestamp verifying that it is in skew, false otherwise
+     */
+    boolean checkKeyIntegrity( byte[] preauthData, KerberosKey key );
+}
diff --git a/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/sam/SamException.java b/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/sam/SamException.java
new file mode 100644
index 0000000..5d0783b
--- /dev/null
+++ b/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/sam/SamException.java
@@ -0,0 +1,109 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.sam;
+
+
+import org.apache.directory.server.kerberos.shared.messages.value.SamType;
+
+
+/**
+ * Base class for all SAM subsystem errors.
+ *
+ * @warning this should extend from KerberosException in o.a.k.exception.
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class SamException extends Exception
+{
+    private static final long serialVersionUID = -677444708375928227L;
+
+    /** the SAM type that caused this exception */
+    private final SamType type;
+
+
+    /**
+     * Creates a SamException for a specific SamType.
+     *
+     * @param type the type value for the SAM algorithm associated with this exception
+     */
+    public SamException(SamType type)
+    {
+        super();
+
+        this.type = type;
+    }
+
+
+    /**
+     * Creates a SamException for a specific SamType, with message.
+     *
+     * @param type the type value for the SAM algorithm associated with this exception
+     * @param message a message regarding the nature of the fault
+     */
+    public SamException(SamType type, String message)
+    {
+        super( message );
+
+        this.type = type;
+    }
+
+
+    /**
+     * Creates a SamException for a specific SamType, with the cause resulted in
+     * this exception.
+     *
+     * @param type the type value for the SAM algorithm associated with this exception
+     * @param cause the throwable that resulted in this exception being thrown
+     */
+    public SamException(SamType type, Throwable cause)
+    {
+        super( cause );
+
+        this.type = type;
+    }
+
+
+    /**
+     * Creates a SamException for a specific SamType, with a message and the
+     * cause that resulted in this exception.
+     *
+     *
+     * @param type the type value for the SAM algorithm associated with this exception
+     * @param message a message regarding the nature of the fault
+     * @param cause the throwable that resulted in this exception being thrown
+     */
+    public SamException(SamType type, String message, Throwable cause)
+    {
+        super( message, cause );
+
+        this.type = type;
+    }
+
+
+    /**
+     * Gets the registered SAM algorithm type associated with this SamException.
+     *
+     * @return the type value for the SAM algorithm associated with this exception
+     */
+    public SamType getSamType()
+    {
+        return this.type;
+    }
+}
diff --git a/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/sam/SamSubsystem.java b/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/sam/SamSubsystem.java
new file mode 100644
index 0000000..2bd8059
--- /dev/null
+++ b/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/sam/SamSubsystem.java
@@ -0,0 +1,212 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.sam;
+
+
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+
+import javax.naming.NamingException;
+import javax.naming.directory.DirContext;
+import javax.security.auth.kerberos.KerberosKey;
+
+import org.apache.directory.server.kerberos.shared.messages.value.SamType;
+import org.apache.directory.server.kerberos.shared.store.PrincipalStoreEntry;
+
+
+/**
+ * The Subsystem that enables the Kerberos server to use plugable Single-use
+ * Authentication mechanisms.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public final class SamSubsystem
+{
+    /** the property key base used for SAM algorithm verifiers */
+    public static final String PROPKEY_BASE = "kerberos.sam.type.";
+
+    /** the SAM subsystem instance */
+    public static SamSubsystem instance;
+
+    /** a map of verifiers so we do not need to create a new one every time */
+    private final Map<SamType, SamVerifier> verifiers = new HashMap<SamType, SamVerifier>();
+
+    /** the key integrity checker used by the subsystem for all sam types */
+    private KeyIntegrityChecker keyChecker;
+
+    /** the user context the SamSubsystem would use to verify passwords */
+    private DirContext userContext;
+    private String userBaseRdn;
+
+
+    /**
+     * Gets the singleton instance of the SamSubsystem.
+     *
+     * @return the singleton for the SamSubsystem
+     */
+    public static SamSubsystem getInstance()
+    {
+        if ( instance == null )
+        {
+            instance = new SamSubsystem();
+        }
+
+        return instance;
+    }
+
+
+    /**
+     * Sets the KeyIntegrityChecker used by the entire SamSubsystem.
+     *
+     * @param keyChecker the KeyIntegrityChecker used by the entire SamSubsystem
+     */
+    public void setIntegrityChecker( KeyIntegrityChecker keyChecker )
+    {
+        this.keyChecker = keyChecker;
+    }
+
+
+    /**
+     * Uses the principal entry information to load the approapriate SamVerifier
+     * and verify the Single-use password.
+     *
+     * @param entry the store entry for the Kerberos principal
+     * @param sad the single-use authentication data encrypted timestamp payload
+     * @return true if verification passed, false otherwise
+     * @throws SamException thrown when there is a failure within the verifier
+     * or a verifier cannot be found.
+     */
+    public KerberosKey verify( PrincipalStoreEntry entry, byte[] sad ) throws SamException
+    {
+        SamVerifier verifier = null;
+
+        if ( keyChecker == null )
+        {
+            throw new IllegalStateException( "SamSubsystem not enabled with key integrity checker" );
+        }
+
+        if ( entry.getSamType() == null )
+        {
+            throw new SamException( entry.getSamType(), "Entry has null SAM type" );
+        }
+
+        if ( verifiers.containsKey( entry.getSamType() ) )
+        {
+            verifier = verifiers.get( entry.getSamType() );
+
+            return verifier.verify( entry.getPrincipal(), sad );
+        }
+
+        String key = PROPKEY_BASE + entry.getSamType().getOrdinal();
+
+        Hashtable<Object, Object> env = new Hashtable<Object, Object>();
+
+        try
+        {
+            env.putAll( userContext.getEnvironment() );
+        }
+        catch ( NamingException e )
+        {
+            e.printStackTrace();
+        }
+
+        if ( !env.containsKey( key ) )
+        {
+            String msg = "Could not find property '" + key + "'";
+
+            throw new SamException( entry.getSamType(), msg );
+        }
+
+        String fqcn = ( String ) env.get( key );
+
+        try
+        {
+            Class c = Class.forName( fqcn );
+
+            verifier = ( SamVerifier ) c.newInstance();
+
+            try
+            {
+                verifier.setUserContext( ( DirContext ) userContext.lookup( userBaseRdn ) );
+            }
+            catch ( NamingException e )
+            {
+                e.printStackTrace();
+
+            }
+
+            verifier.setIntegrityChecker( keyChecker );
+
+            verifier.startup();
+
+            if ( !verifier.getSamType().equals( entry.getSamType() ) )
+            {
+                String msg = "Expecting entries with SAM type of " + verifier.getSamType();
+
+                msg += " but got a type of entry with SAM type of " + entry.getSamType();
+
+                throw new SamException( entry.getSamType(), msg );
+            }
+
+            verifiers.put( verifier.getSamType(), verifier );
+
+            return verifier.verify( entry.getPrincipal(), sad );
+        }
+        catch ( ClassNotFoundException e )
+        {
+            String msg = "Could not find verifier class '" + fqcn;
+
+            msg += "' for SamType( " + entry.getSamType() + " ) ";
+
+            throw new SamException( entry.getSamType(), msg, e );
+        }
+        catch ( IllegalAccessException e )
+        {
+            String msg = "No public default constructor on class '" + fqcn;
+
+            msg += "' for SamType( " + entry.getSamType() + " ) ";
+
+            throw new SamException( entry.getSamType(), msg, e );
+        }
+        catch ( InstantiationException e )
+        {
+            String msg = "Failed on default constructor invocation for class '" + fqcn;
+
+            msg += "' for SamType( " + entry.getSamType() + " ) ";
+
+            throw new SamException( entry.getSamType(), msg, e );
+        }
+    }
+
+
+    /**
+     * Sets the context under which user entries can be found.
+     *
+     * @param userContext the jndi context under which users can be found.
+     * @param userBaseRdn the container with users
+     */
+    public void setUserContext( DirContext userContext, String userBaseRdn )
+    {
+        this.userContext = userContext;
+        this.userBaseRdn = userBaseRdn;
+    }
+}
diff --git a/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/sam/SamVerifier.java b/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/sam/SamVerifier.java
new file mode 100644
index 0000000..a7c6043
--- /dev/null
+++ b/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/sam/SamVerifier.java
@@ -0,0 +1,103 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.sam;
+
+
+import javax.naming.directory.DirContext;
+import javax.security.auth.kerberos.KerberosKey;
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.apache.directory.server.kerberos.shared.messages.value.SamType;
+
+
+/**
+ * Single-use Authentication Mechanism verifier (subsystem) interface.
+ * SamVerifiers are modules that can be configured and are dynamically
+ * loaded as needed.  Implementations have a few requirements and things
+ * implementors should know:
+ *
+ * <ul>
+ *   <li>A public default constructor is required,</li>
+ *   <li>after instantitation environment properties are supplied,</li>
+ *   <li>next the KeyIntegrityChecker is set for the verifier,</li>
+ *   <li>finally the verifier is started up by calling startup(),
+ *       incidentally this is where all initialization work should be
+ *       done using the environment properties supplied.
+ *   </li>
+ * </ul>
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public interface SamVerifier
+{
+    /**
+     * Starts one of many pluggable SAM type subsystem.
+     * 
+     * @throws SamException
+     */
+    void startup() throws SamException;
+
+
+    /**
+     * Shuts down one of many pluggable SAM type subsystem.
+     */
+    void shutdown();
+
+
+    /**
+     * SamVerifiers require a KeyIntegrityChecker to calculate the integrity of
+     * a generated KerberosKey.  The Kerberos service exposes this interface
+     * and supplies it to the verifier to check generated keys to conduct the
+     * verification workflow.
+     *
+     * @param keyChecker The integrity checker that validates whether or not a
+     * key can decrypt-decode preauth data (an encryped-encoded generalized
+     * timestamp).
+     */
+    void setIntegrityChecker( KeyIntegrityChecker keyChecker );
+
+
+    /**
+     * Verifies the single use password supplied.
+     *
+     * @param principal The kerberos principal to use.
+     * @param sad Single-use authentication data (encrypted generalized timestamp).
+     * @return The {@link KerberosKey}.
+     * @throws SamException 
+     */
+    KerberosKey verify( KerberosPrincipal principal, byte[] sad ) throws SamException;
+
+
+    /**
+     * Gets the registered SAM algorithm type implemented by this SamVerifier.
+     *
+     * @return The type value for the SAM algorithm used to verify the SUP.
+     */
+    SamType getSamType();
+
+
+    /**
+     * Sets the user context where users are stored for the primary realm.
+     *  
+     * @param userContext
+     */
+    void setUserContext( DirContext userContext );
+}
diff --git a/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/sam/TimestampChecker.java b/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/sam/TimestampChecker.java
new file mode 100644
index 0000000..6c3b1f4
--- /dev/null
+++ b/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/sam/TimestampChecker.java
@@ -0,0 +1,94 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.sam;
+
+
+import java.io.IOException;
+
+import javax.security.auth.kerberos.KerberosKey;
+
+import org.apache.directory.server.kerberos.shared.crypto.encryption.CipherTextHandler;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.KeyUsage;
+import org.apache.directory.server.kerberos.shared.exceptions.KerberosException;
+import org.apache.directory.server.kerberos.shared.io.decoder.EncryptedDataDecoder;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptedData;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptedTimeStamp;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class TimestampChecker implements KeyIntegrityChecker
+{
+    private static final long FIVE_MINUTES = 300000;
+    private static final CipherTextHandler cipherTextHandler = new CipherTextHandler();
+
+
+    public boolean checkKeyIntegrity( byte[] encryptedData, KerberosKey kerberosKey )
+    {
+        EncryptionType keyType = EncryptionType.getTypeByOrdinal( kerberosKey.getKeyType() );
+        EncryptionKey key = new EncryptionKey( keyType, kerberosKey.getEncoded() );
+
+        try
+        {
+            /*
+             * Since the pre-auth value is of type PA-ENC-TIMESTAMP, it should be a valid
+             * ASN.1 PA-ENC-TS-ENC structure, so we can decode it into EncryptedData.
+             */
+            EncryptedData sadValue = EncryptedDataDecoder.decode( encryptedData );
+
+            /*
+             * Decrypt the EncryptedData structure to get the PA-ENC-TS-ENC.  Decode the
+             * decrypted timestamp into our timestamp object.
+             */
+            EncryptedTimeStamp timestamp = ( EncryptedTimeStamp ) cipherTextHandler.unseal( EncryptedTimeStamp.class,
+                key, sadValue, KeyUsage.NUMBER1 );
+
+            /*
+             * Since we got here we must have a valid timestamp structure that we can
+             * validate to be within a five minute skew.
+             */
+            KerberosTime time = timestamp.getTimeStamp();
+
+            if ( time.isInClockSkew( FIVE_MINUTES ) )
+            {
+                return true;
+            }
+        }
+        catch ( IOException ioe )
+        {
+            return false;
+        }
+        catch ( KerberosException ke )
+        {
+            return false;
+        }
+        catch ( ClassCastException cce )
+        {
+            return false;
+        }
+
+        return false;
+    }
+}
diff --git a/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/sam/package-info.java b/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/sam/package-info.java
new file mode 100644
index 0000000..69c7b46
--- /dev/null
+++ b/old_trunk/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/sam/package-info.java
@@ -0,0 +1,31 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+/**
+ * Provides a subsystem that enables the KDC's Authentication Service (AS)
+ * to use pluggable Single-use Authentication Mechanisms (SAM) during
+ * pre-authentication processing.  An example of a SAM mechanism would be
+ * one-time passwords (OTP).
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+
+package org.apache.directory.server.kerberos.sam;
diff --git a/old_trunk/protocol-kerberos/src/site/site.xml b/old_trunk/protocol-kerberos/src/site/site.xml
new file mode 100644
index 0000000..9ec7c16
--- /dev/null
+++ b/old_trunk/protocol-kerberos/src/site/site.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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.
+-->
+<!--
+  @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+-->
+<project name="${project.name}">
+    <bannerLeft>
+        <name>${project.name}</name>
+    </bannerLeft>
+    <publishDate position="navigation-bottom" format="dd-MM-yyyy HH:mm" />
+    <body>
+        <menu name="Parent">
+            <item name="Apache Directory ApacheDS" href="../index.html" />
+        </menu>
+        <menu ref="reports" />
+    </body>
+</project>
diff --git a/old_trunk/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/kdc/AS-REQ-PA-ENC-TS.pdu b/old_trunk/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/kdc/AS-REQ-PA-ENC-TS.pdu
new file mode 100644
index 0000000..3971db6
--- /dev/null
+++ b/old_trunk/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/kdc/AS-REQ-PA-ENC-TS.pdu
Binary files differ
diff --git a/old_trunk/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/kdc/AS-REQ-TCP.pdu b/old_trunk/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/kdc/AS-REQ-TCP.pdu
new file mode 100644
index 0000000..781eff2
--- /dev/null
+++ b/old_trunk/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/kdc/AS-REQ-TCP.pdu
Binary files differ
diff --git a/old_trunk/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/kdc/AS-REQ-UDP.pdu b/old_trunk/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/kdc/AS-REQ-UDP.pdu
new file mode 100644
index 0000000..85e9037
--- /dev/null
+++ b/old_trunk/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/kdc/AS-REQ-UDP.pdu
Binary files differ
diff --git a/old_trunk/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/kdc/KerberosTestUtils.java b/old_trunk/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/kdc/KerberosTestUtils.java
new file mode 100644
index 0000000..1942b54
--- /dev/null
+++ b/old_trunk/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/kdc/KerberosTestUtils.java
@@ -0,0 +1,226 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.kdc;
+
+
+import java.io.BufferedInputStream;
+import java.io.CharArrayWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;                            
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class KerberosTestUtils
+{
+    public static char[] getControlDocument( String resource ) throws IOException
+    {
+        InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream( resource );
+
+        Reader reader = new InputStreamReader( new BufferedInputStream( is ) );
+
+        CharArrayWriter writer = new CharArrayWriter();
+
+        try
+        {
+            char[] buf = new char[2048];
+            int len = 0;
+            while ( len >= 0 )
+            {
+                len = reader.read( buf );
+                if ( len > 0 )
+                {
+                    writer.write( buf, 0, len );
+                }
+            }
+        }
+        finally
+        {
+            try
+            {
+                reader.close();
+            }
+            catch ( IOException ioe )
+            {
+            }
+        }
+
+        char[] isca = writer.toCharArray();
+        return isca;
+    }
+
+
+    public static byte[] getBytesFromResource( String resource ) throws IOException
+    {
+        InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream( resource );
+
+        BufferedInputStream stream = new BufferedInputStream( is );
+        int len = stream.available();
+        byte[] bytes = new byte[len];
+        stream.read( bytes, 0, len );
+
+        return bytes;
+    }
+
+
+    public static void hexdump( byte[] data )
+    {
+        hexdump( data, true );
+    }
+
+
+    public static void hexdump( byte[] data, boolean delimit )
+    {
+        String delimiter = new String( "-------------------------------------------------" );
+
+        if ( delimit )
+        {
+            System.out.println( delimiter );
+        }
+
+        int lineLength = 0;
+        for ( int ii = 0; ii < data.length; ii++ )
+        {
+            System.out.print( byte2hexString( data[ii] ) + " " );
+            lineLength++;
+
+            if ( lineLength == 8 )
+            {
+                System.out.print( "  " );
+            }
+
+            if ( lineLength == 16 )
+            {
+                System.out.println();
+                lineLength = 0;
+            }
+        }
+
+        if ( delimit )
+        {
+            System.out.println();
+            System.out.println( delimiter );
+        }
+    }
+
+    public static final String[] hex_digit =
+        { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };
+
+
+    public static String byte2hexString( byte x )
+    {
+        String s = "";
+        for ( int ii = 0; ii < 2; ii++ )
+        {
+            s = hex_digit[( ( ( x ) & 0xff ) & ( 15 << ( ii * 4 ) ) ) >>> ( ii * 4 )] + s;
+        }
+
+        return s;
+    }
+
+
+    public static String int2hexString( int x )
+    {
+        String s = "";
+        for ( int ii = 0; ii < 8; ii++ )
+        {
+            s = hex_digit[( x & ( 15 << ( ii * 4 ) ) ) >>> ( ii * 4 )] + s;
+        }
+
+        return s;
+    }
+
+
+    public static String int2binString( int x )
+    {
+        String s = "";
+        for ( int ii = 0; ii < 32; ii++ )
+        {
+            if ( ( ii > 0 ) && ( ii % 4 == 0 ) )
+            {
+                s = " " + s;
+            }
+
+            s = hex_digit[( x & ( 1 << ii ) ) >>> ii] + s;
+        }
+
+        return s;
+    }
+
+
+    public static String long2hexString( long x )
+    {
+        String s = "";
+        for ( int ii = 0; ii < 16; ii++ )
+        {
+            s = hex_digit[( int ) ( ( x & ( 15L << ( ii * 4 ) ) ) >>> ( ii * 4 ) )] + s;
+        }
+
+        return s;
+    }
+
+
+    public static String long2binString( long x )
+    {
+        String s = "";
+        for ( int ii = 0; ii < 64; ii++ )
+        {
+            if ( ( ii > 0 ) && ( ii % 4 == 0 ) )
+            {
+                s = " " + s;
+            }
+
+            s = hex_digit[( int ) ( ( x & ( 1L << ii ) ) >>> ii )] + s;
+        }
+
+        return s;
+    }
+
+
+    public static String byte2hexString( byte[] input )
+    {
+        return byte2hexString( input, 0, input.length );
+    }
+
+
+    public static String byte2hexString( byte[] input, int offset )
+    {
+        return byte2hexString( input, offset, input.length );
+    }
+
+
+    public static String byte2hexString( byte[] input, int offset, int length )
+    {
+        String result = "";
+        for ( int ii = 0; ii < length; ii++ )
+        {
+            if ( ii + offset < input.length )
+            {
+                result += byte2hexString( input[ii + offset] );
+            }
+        }
+
+        return result;
+    }
+}
diff --git a/old_trunk/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/kdc/SaslGssapiBindITest.java b/old_trunk/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/kdc/SaslGssapiBindITest.java
new file mode 100644
index 0000000..75e7849
--- /dev/null
+++ b/old_trunk/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/kdc/SaslGssapiBindITest.java
@@ -0,0 +1,248 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.kdc;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.entry.DefaultServerEntry;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.interceptor.Interceptor;
+import org.apache.directory.server.core.kerberos.KeyDerivationInterceptor;
+import org.apache.directory.server.core.partition.Partition;
+import org.apache.directory.server.core.partition.impl.btree.Index;
+import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmIndex;
+import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmPartition;
+import org.apache.directory.server.kerberos.shared.store.KerberosAttribute;
+import org.apache.directory.server.unit.AbstractServerTest;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.message.ModificationItemImpl;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+import javax.naming.Context;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.InitialDirContext;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Set;
+
+
+/**
+ * An {@link AbstractServerTest} testing SASL GSSAPI authentication
+ * and security layer negotiation.  These tests require both the LDAP
+ * and the Kerberos protocol.  As with any "three-headed" Kerberos
+ * scenario, there are 3 principals:  1 for the test user, 1 for the
+ * Kerberos ticket-granting service (TGS), and 1 for the LDAP service.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class SaslGssapiBindITest extends AbstractServerTest
+{
+    private DirContext ctx;
+
+
+    /**
+     * Creates a new instance of SaslGssapiBindTest and sets JAAS system properties
+     * for the KDC and realm, so we don't have to rely on external configuration.
+     */
+    public SaslGssapiBindITest()
+    {
+        System.setProperty( "java.security.krb5.realm", "EXAMPLE.COM" );
+        System.setProperty( "java.security.krb5.kdc", "localhost" );
+    }
+
+
+    /**
+     * Set up a partition for EXAMPLE.COM and add user and service principals to
+     * test authentication with.
+     */
+    public void setUp() throws Exception
+    {
+        super.setUp();
+
+        setAllowAnonymousAccess( false );
+        ldapServer.setSaslHost( "localhost" );
+        ldapServer.setSaslPrincipal( "ldap/localhost@EXAMPLE.COM" );
+
+        KdcServer kdcConfig = new KdcServer();
+        kdcConfig.setSocketAcceptor( socketAcceptor );
+        kdcConfig.setDirectoryService( directoryService );
+        kdcConfig.setEnabled( true );
+        kdcConfig.setSearchBaseDn( "ou=users,dc=example,dc=com" );
+
+        Attributes attrs;
+
+//        doDelete( directoryService.getWorkingDirectory() );
+//        port = AvailablePortFinder.getNextAvailable( 1024 );
+//        ldapServer.setIpPort( port );
+//        directoryService.setShutdownHookEnabled( false );
+
+
+        setContexts( "uid=admin,ou=system", "secret" );
+
+        // -------------------------------------------------------------------
+        // Enable the krb5kdc schema
+        // -------------------------------------------------------------------
+
+        // check if krb5kdc is disabled
+        Attributes krb5kdcAttrs = schemaRoot.getAttributes( "cn=Krb5kdc" );
+        boolean isKrb5KdcDisabled = false;
+        if ( krb5kdcAttrs.get( "m-disabled" ) != null )
+        {
+            isKrb5KdcDisabled = ( ( String ) krb5kdcAttrs.get( "m-disabled" ).get() ).equalsIgnoreCase( "TRUE" );
+        }
+
+        // if krb5kdc is disabled then enable it
+        if ( isKrb5KdcDisabled )
+        {
+            Attribute disabled = new AttributeImpl( "m-disabled" );
+            ModificationItemImpl[] mods = new ModificationItemImpl[]
+                    {new ModificationItemImpl( DirContext.REMOVE_ATTRIBUTE, disabled )};
+            schemaRoot.modifyAttributes( "cn=Krb5kdc", mods );
+        }
+
+        // Get a context, create the ou=users subcontext, then create the 3 principals.
+        Hashtable<String, Object> env = new Hashtable<String, Object>();
+        env.put( DirectoryService.JNDI_KEY, directoryService );
+        env.put( Context.INITIAL_CONTEXT_FACTORY, "org.apache.directory.server.core.jndi.CoreContextFactory" );
+        env.put( Context.PROVIDER_URL, "dc=example,dc=com" );
+        env.put( Context.SECURITY_PRINCIPAL, "uid=admin,ou=system" );
+        env.put( Context.SECURITY_CREDENTIALS, "secret" );
+        env.put( Context.SECURITY_AUTHENTICATION, "simple" );
+
+        ctx = new InitialDirContext( env );
+
+        attrs = getOrgUnitAttributes( "users" );
+        DirContext users = ctx.createSubcontext( "ou=users", attrs );
+
+        attrs = getPrincipalAttributes( "Nelson", "Horatio Nelson", "hnelson", "secret", "hnelson@EXAMPLE.COM" );
+        users.createSubcontext( "uid=hnelson", attrs );
+
+        attrs = getPrincipalAttributes( "Service", "KDC Service", "krbtgt", "secret", "krbtgt/EXAMPLE.COM@EXAMPLE.COM" );
+        users.createSubcontext( "uid=krbtgt", attrs );
+
+        attrs = getPrincipalAttributes( "Service", "LDAP Service", "ldap", "randall", "ldap/localhost@EXAMPLE.COM" );
+        users.createSubcontext( "uid=ldap", attrs );
+    }
+
+    protected void configureDirectoryService() throws NamingException
+    {
+        Set<Partition> partitions = new HashSet<Partition>();
+
+        // Add partition 'example'
+        JdbmPartition partition = new JdbmPartition();
+        partition.setId( "example" );
+        partition.setSuffix( "dc=example,dc=com" );
+
+        Set<Index> indexedAttrs = new HashSet<Index>();
+        indexedAttrs.add( new JdbmIndex( "ou" ) );
+        indexedAttrs.add( new JdbmIndex( "dc" ) );
+        indexedAttrs.add( new JdbmIndex( "objectClass" ) );
+        partition.setIndexedAttributes( indexedAttrs );
+
+        ServerEntry serverEntry = new DefaultServerEntry( directoryService.getRegistries(), new LdapDN( "dc=example, dc=com" ) );
+        serverEntry.put( "objectClass", "top", "domain" );
+        serverEntry.put( "dc", "example" );
+        partition.setContextEntry( serverEntry );
+
+        partitions.add( partition );
+        directoryService.setPartitions( partitions );
+
+        List<Interceptor> list = directoryService.getInterceptors();
+        list.add( new KeyDerivationInterceptor() );
+        directoryService.setInterceptors( list );
+    }
+
+
+    /**
+     * Convenience method for creating principals.
+     *
+     * @param cn           the commonName of the person
+     * @param principal    the kerberos principal name for the person
+     * @param sn           the surName of the person
+     * @param uid          the unique identifier for the person
+     * @param userPassword the credentials of the person
+     * @return the attributes of the person principal
+     */
+    protected Attributes getPrincipalAttributes( String sn, String cn, String uid, String userPassword, String principal )
+    {
+        Attributes attrs = new AttributesImpl();
+        Attribute ocls = new AttributeImpl( "objectClass" );
+        ocls.add( "top" );
+        ocls.add( "person" ); // sn $ cn
+        ocls.add( "inetOrgPerson" ); // uid
+        ocls.add( "krb5principal" );
+        ocls.add( "krb5kdcentry" );
+        attrs.put( ocls );
+        attrs.put( "cn", cn );
+        attrs.put( "sn", sn );
+        attrs.put( "uid", uid );
+        attrs.put( SchemaConstants.USER_PASSWORD_AT, userPassword );
+        attrs.put( KerberosAttribute.KRB5_PRINCIPAL_NAME_AT, principal );
+        attrs.put( KerberosAttribute.KRB5_KEY_VERSION_NUMBER_AT, "0" );
+
+        return attrs;
+    }
+
+
+    /**
+     * Convenience method for creating an organizational unit.
+     *
+     * @param ou the ou of the organizationalUnit
+     * @return the attributes of the organizationalUnit
+     */
+    protected Attributes getOrgUnitAttributes( String ou )
+    {
+        Attributes attrs = new AttributesImpl();
+        Attribute ocls = new AttributeImpl( "objectClass" );
+        ocls.add( "top" );
+        ocls.add( "organizationalUnit" );
+        attrs.put( ocls );
+        attrs.put( "ou", ou );
+
+        return attrs;
+    }
+
+
+    /**
+     * Tests to make sure GSSAPI binds below the RootDSE work.
+     */
+    public void testSaslGssapiBind()
+    {
+        assertTrue( true );
+    }
+
+
+    /**
+     * Tear down.
+     */
+    public void tearDown() throws Exception
+    {
+        ctx.close();
+        ctx = null;
+        super.tearDown();
+    }
+}
diff --git a/old_trunk/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/kdc/ms_krb5.pcap b/old_trunk/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/kdc/ms_krb5.pcap
new file mode 100644
index 0000000..1e769d9
--- /dev/null
+++ b/old_trunk/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/kdc/ms_krb5.pcap
Binary files differ
diff --git a/old_trunk/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/kdc/oracle_krb5.pcap b/old_trunk/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/kdc/oracle_krb5.pcap
new file mode 100644
index 0000000..5251f2a
--- /dev/null
+++ b/old_trunk/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/kdc/oracle_krb5.pcap
Binary files differ
diff --git a/old_trunk/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/kdc/pam_krb5.pcap b/old_trunk/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/kdc/pam_krb5.pcap
new file mode 100644
index 0000000..dbbe7e8
--- /dev/null
+++ b/old_trunk/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/kdc/pam_krb5.pcap
Binary files differ
diff --git a/old_trunk/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/messages/value/OptionsTest.java b/old_trunk/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/messages/value/OptionsTest.java
new file mode 100644
index 0000000..51a4be8
--- /dev/null
+++ b/old_trunk/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/messages/value/OptionsTest.java
@@ -0,0 +1,79 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.messages.value;
+
+
+import java.util.Arrays;
+
+import junit.framework.TestCase;
+
+import org.apache.directory.server.kerberos.shared.messages.value.KdcOptions;
+import org.apache.directory.server.kerberos.shared.messages.value.flags.TicketFlag;
+import org.apache.directory.server.kerberos.shared.messages.value.flags.TicketFlags;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class OptionsTest extends TestCase
+{
+    private byte[] fpriOptions =
+        { ( byte ) 0x50, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x10 };
+
+
+    /**
+     * Tests converting the ticket flags to a descriptive String.
+     */
+    public void testToString()
+    {
+        TicketFlags flags = new TicketFlags();
+        flags.setFlag( TicketFlag.FORWARDABLE );
+        flags.setFlag( TicketFlag.PROXIABLE );
+        flags.setFlag( TicketFlag.RENEWABLE );
+        flags.setFlag( TicketFlag.INITIAL );
+        assertEquals( flags.toString(), "FORWARDABLE(1) PROXIABLE(3) RENEWABLE(8) INITIAL(9)" );
+    }
+
+
+    /**
+     * Tests that setting flags is idempotent.
+     */
+    public void testDuplicateSetting()
+    {
+        TicketFlags flags = new TicketFlags();
+        flags.setFlag( TicketFlag.MAY_POSTDATE );
+        flags.setFlag( TicketFlag.FORWARDABLE );
+        flags.setFlag( TicketFlag.PROXIABLE );
+        flags.setFlag( TicketFlag.MAY_POSTDATE );
+        flags.setFlag( TicketFlag.RENEWABLE );
+        assertEquals( flags.toString(), "FORWARDABLE(1) PROXIABLE(3) MAY_POSTDATE(5) RENEWABLE(8)" );
+    }
+
+
+    /**
+     * Tests the basic construction of the {@link KdcOptions}.
+     */
+    public void testConstruction()
+    {
+        KdcOptions options = new KdcOptions( fpriOptions );
+        assertTrue( Arrays.equals( options.getBytes(), fpriOptions ) );
+    }
+}
diff --git a/old_trunk/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/protocol/AbstractAuthenticationServiceTest.java b/old_trunk/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/protocol/AbstractAuthenticationServiceTest.java
new file mode 100644
index 0000000..22fe073
--- /dev/null
+++ b/old_trunk/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/protocol/AbstractAuthenticationServiceTest.java
@@ -0,0 +1,216 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.protocol;
+
+
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.security.SecureRandom;
+
+import javax.security.auth.kerberos.KerberosKey;
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import junit.framework.TestCase;
+
+import org.apache.directory.server.kerberos.shared.crypto.encryption.CipherTextHandler;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.KeyUsage;
+import org.apache.directory.server.kerberos.shared.io.encoder.EncryptedDataEncoder;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptedData;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptedTimeStamp;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
+import org.apache.directory.server.kerberos.shared.messages.value.PaData;
+import org.apache.directory.server.kerberos.shared.messages.value.PrincipalName;
+import org.apache.directory.server.kerberos.shared.messages.value.types.PaDataType;
+import org.apache.directory.server.kerberos.shared.messages.value.types.PrincipalNameType;
+import org.apache.mina.common.IoFilterChain;
+import org.apache.mina.common.IoHandler;
+import org.apache.mina.common.IoService;
+import org.apache.mina.common.IoServiceConfig;
+import org.apache.mina.common.IoSessionConfig;
+import org.apache.mina.common.TransportType;
+import org.apache.mina.common.WriteFuture;
+import org.apache.mina.common.support.BaseIoSession;
+
+
+/**
+ * Abstract base class for Authentication Service (AS) tests, with utility methods
+ * for generating message components.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public abstract class AbstractAuthenticationServiceTest extends TestCase
+{
+    protected CipherTextHandler lockBox;
+    protected static final SecureRandom random = new SecureRandom();
+
+
+    protected PaData[] getPreAuthEncryptedTimeStamp( KerberosPrincipal clientPrincipal, String passPhrase )
+        throws Exception
+    {
+        KerberosTime timeStamp = new KerberosTime();
+
+        return getPreAuthEncryptedTimeStamp( clientPrincipal, passPhrase, timeStamp );
+    }
+
+
+    protected PaData[] getPreAuthEncryptedTimeStamp( KerberosPrincipal clientPrincipal,
+        String passPhrase, KerberosTime timeStamp ) throws Exception
+    {
+        PaData[] paData = new PaData[1];
+
+        EncryptedTimeStamp encryptedTimeStamp = new EncryptedTimeStamp( timeStamp, 0 );
+
+        EncryptionKey clientKey = getEncryptionKey( clientPrincipal, passPhrase );
+
+        EncryptedData encryptedData = lockBox.seal( clientKey, encryptedTimeStamp, KeyUsage.NUMBER1 );
+
+        byte[] encodedEncryptedData = EncryptedDataEncoder.encode( encryptedData );
+
+        PaData preAuth = new PaData();
+        preAuth.setPaDataType( PaDataType.PA_ENC_TIMESTAMP );
+        preAuth.setPaDataValue( encodedEncryptedData );
+
+        paData[0] = preAuth;
+
+        return paData;
+    }
+
+
+    protected PrincipalName getPrincipalName( String name )
+    {
+        PrincipalName principalName = new PrincipalName();
+        principalName.addName( name );
+        principalName.setNameType( PrincipalNameType.KRB_NT_PRINCIPAL );
+
+        return principalName;
+    }
+
+
+    /**
+     * Returns an encryption key derived from a principal name and passphrase.
+     *
+     * @param principal
+     * @param passPhrase
+     * @return The server's {@link EncryptionKey}.
+     */
+    protected EncryptionKey getEncryptionKey( KerberosPrincipal principal, String passPhrase )
+    {
+        KerberosKey kerberosKey = new KerberosKey( principal, passPhrase.toCharArray(), "DES" );
+        byte[] keyBytes = kerberosKey.getEncoded();
+        EncryptionKey key = new EncryptionKey( EncryptionType.DES_CBC_MD5, keyBytes );
+
+        return key;
+    }
+
+    protected static class DummySession extends BaseIoSession
+    {
+        Object message;
+
+
+        @Override
+        public WriteFuture write( Object message )
+        {
+            this.message = message;
+
+            return super.write( message );
+        }
+
+
+        protected Object getMessage()
+        {
+            return message;
+        }
+
+
+        protected void updateTrafficMask()
+        {
+            // Do nothing.
+        }
+
+
+        public IoService getService()
+        {
+            return null;
+        }
+
+
+        public IoHandler getHandler()
+        {
+            return null;
+        }
+
+
+        public IoFilterChain getFilterChain()
+        {
+            return null;
+        }
+
+
+        public TransportType getTransportType()
+        {
+            return null;
+        }
+
+
+        public SocketAddress getRemoteAddress()
+        {
+            return new InetSocketAddress( 10088 );
+        }
+
+
+        public SocketAddress getLocalAddress()
+        {
+            return null;
+        }
+
+
+        public IoSessionConfig getConfig()
+        {
+            return null;
+        }
+
+
+        public int getScheduledWriteRequests()
+        {
+            return 0;
+        }
+
+
+        public SocketAddress getServiceAddress()
+        {
+            return null;
+        }
+
+
+        public IoServiceConfig getServiceConfig()
+        {
+            return null;
+        }
+
+
+        public int getScheduledWriteBytes()
+        {
+            return 0;
+        }
+    }
+}
diff --git a/old_trunk/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/protocol/AbstractTicketGrantingServiceTest.java b/old_trunk/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/protocol/AbstractTicketGrantingServiceTest.java
new file mode 100644
index 0000000..e1a97cf
--- /dev/null
+++ b/old_trunk/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/protocol/AbstractTicketGrantingServiceTest.java
@@ -0,0 +1,418 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.protocol;
+
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.security.SecureRandom;
+
+import javax.security.auth.kerberos.KerberosKey;
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import junit.framework.TestCase;
+
+import org.apache.directory.server.kerberos.shared.KerberosConstants;
+import org.apache.directory.server.kerberos.shared.KerberosMessageType;
+import org.apache.directory.server.kerberos.shared.crypto.checksum.ChecksumHandler;
+import org.apache.directory.server.kerberos.shared.crypto.checksum.ChecksumType;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.CipherTextHandler;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.KeyUsage;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.RandomKeyFactory;
+import org.apache.directory.server.kerberos.shared.exceptions.KerberosException;
+import org.apache.directory.server.kerberos.shared.io.encoder.ApplicationRequestEncoder;
+import org.apache.directory.server.kerberos.shared.io.encoder.KdcRequestEncoder;
+import org.apache.directory.server.kerberos.shared.messages.ApplicationRequest;
+import org.apache.directory.server.kerberos.shared.messages.KdcRequest;
+import org.apache.directory.server.kerberos.shared.messages.components.Authenticator;
+import org.apache.directory.server.kerberos.shared.messages.components.AuthenticatorModifier;
+import org.apache.directory.server.kerberos.shared.messages.components.EncTicketPart;
+import org.apache.directory.server.kerberos.shared.messages.components.EncTicketPartModifier;
+import org.apache.directory.server.kerberos.shared.messages.components.Ticket;
+import org.apache.directory.server.kerberos.shared.messages.value.ApOptions;
+import org.apache.directory.server.kerberos.shared.messages.value.Checksum;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptedData;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
+import org.apache.directory.server.kerberos.shared.messages.value.PaData;
+import org.apache.directory.server.kerberos.shared.messages.value.PrincipalName;
+import org.apache.directory.server.kerberos.shared.messages.value.RequestBody;
+import org.apache.directory.server.kerberos.shared.messages.value.TransitedEncoding;
+import org.apache.directory.server.kerberos.shared.messages.value.flags.TicketFlag;
+import org.apache.directory.server.kerberos.shared.messages.value.flags.TicketFlags;
+import org.apache.directory.server.kerberos.shared.messages.value.types.PaDataType;
+import org.apache.directory.server.kerberos.shared.messages.value.types.PrincipalNameType;
+import org.apache.mina.common.IoFilterChain;
+import org.apache.mina.common.IoHandler;
+import org.apache.mina.common.IoService;
+import org.apache.mina.common.IoServiceConfig;
+import org.apache.mina.common.IoSessionConfig;
+import org.apache.mina.common.TransportType;
+import org.apache.mina.common.WriteFuture;
+import org.apache.mina.common.support.BaseIoSession;
+
+
+/**
+ * Abstract base class for Ticket-Granting Service (TGS) tests, with utility methods
+ * for generating message components.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$        Ticket ticket = ticketModifier.getTicket();
+
+
+ */
+public abstract class AbstractTicketGrantingServiceTest extends TestCase
+{
+    protected CipherTextHandler lockBox;
+    protected static final SecureRandom random = new SecureRandom();
+
+    /** Session attributes that must be verified. */
+    protected EncryptionKey sessionKey;
+    protected EncryptionKey subSessionKey;
+    protected int sequenceNumber;
+    protected KerberosTime now;
+    protected int clientMicroSeconds = 0;
+
+
+    protected Ticket getTgt( KerberosPrincipal clientPrincipal, KerberosPrincipal serverPrincipal, String serverPassword )
+        throws Exception
+    {
+        EncryptionKey serverKey = getEncryptionKey( serverPrincipal, serverPassword );
+        return getTicket( clientPrincipal, serverPrincipal, serverKey );
+    }
+
+
+    /**
+     * Returns an encryption key derived from a principal name and passphrase.
+     *
+     * @param principal
+     * @param passPhrase
+     * @return The server's {@link EncryptionKey}.
+     */
+    protected EncryptionKey getEncryptionKey( KerberosPrincipal principal, String passPhrase )
+    {
+        KerberosKey kerberosKey = new KerberosKey( principal, passPhrase.toCharArray(), "DES" );
+        byte[] keyBytes = kerberosKey.getEncoded();
+        return new EncryptionKey( EncryptionType.DES_CBC_MD5, keyBytes );
+    }
+
+
+    /**
+     * Build the service ticket.  The service ticket contains the session key generated
+     * by the KDC for the client and service to use.  The service will unlock the
+     * authenticator with the session key from the ticket.  The principal in the ticket
+     * must equal the authenticator client principal.
+     * 
+     * If set in the AP Options, the Ticket can also be sealed with the session key.
+     * 
+     * @param clientPrincipal
+     * @param serverPrincipal
+     * @param serverKey 
+     * @return The {@link Ticket}.
+     * @throws KerberosException
+     */
+    protected Ticket getTicket( KerberosPrincipal clientPrincipal, KerberosPrincipal serverPrincipal,
+        EncryptionKey serverKey ) throws KerberosException
+    {
+        EncTicketPartModifier encTicketModifier = new EncTicketPartModifier();
+
+        TicketFlags ticketFlags = new TicketFlags();
+        ticketFlags.setFlag( TicketFlag.RENEWABLE );
+        encTicketModifier.setFlags( ticketFlags );
+
+        EncryptionKey sessionKey = RandomKeyFactory.getRandomKey( EncryptionType.DES_CBC_MD5 );
+
+        encTicketModifier.setSessionKey( sessionKey );
+        encTicketModifier.setClientPrincipal( clientPrincipal );
+        encTicketModifier.setTransitedEncoding( new TransitedEncoding() );
+        encTicketModifier.setAuthTime( new KerberosTime() );
+
+        long now = System.currentTimeMillis();
+        KerberosTime endTime = new KerberosTime( now + KerberosTime.DAY );
+        encTicketModifier.setEndTime( endTime );
+
+        KerberosTime renewTill = new KerberosTime( now + KerberosTime.WEEK );
+        encTicketModifier.setRenewTill( renewTill );
+
+        EncTicketPart encTicketPart = encTicketModifier.getEncTicketPart();
+
+        EncryptedData encryptedTicketPart = lockBox.seal( serverKey, encTicketPart, KeyUsage.NUMBER2 );
+
+        Ticket ticket = new Ticket( KerberosConstants.KERBEROS_V5, serverPrincipal, encryptedTicketPart );
+
+        ticket.setEncTicketPart( encTicketPart );
+
+        return ticket;
+    }
+
+
+    protected EncTicketPartModifier getTicketArchetype( KerberosPrincipal clientPrincipal ) throws KerberosException
+    {
+        EncTicketPartModifier encTicketModifier = new EncTicketPartModifier();
+
+        TicketFlags ticketFlags = new TicketFlags();
+        ticketFlags.setFlag( TicketFlag.RENEWABLE );
+        encTicketModifier.setFlags( ticketFlags );
+
+        EncryptionKey sessionKey = RandomKeyFactory.getRandomKey( EncryptionType.DES_CBC_MD5 );
+
+        encTicketModifier.setSessionKey( sessionKey );
+        encTicketModifier.setClientPrincipal( clientPrincipal );
+        encTicketModifier.setTransitedEncoding( new TransitedEncoding() );
+        encTicketModifier.setAuthTime( new KerberosTime() );
+
+        long now = System.currentTimeMillis();
+        KerberosTime endTime = new KerberosTime( now + KerberosTime.DAY );
+        encTicketModifier.setEndTime( endTime );
+
+        KerberosTime renewTill = new KerberosTime( now + KerberosTime.WEEK );
+        encTicketModifier.setRenewTill( renewTill );
+
+        return encTicketModifier;
+    }
+
+
+    protected Ticket getTicket( EncTicketPartModifier encTicketModifier, KerberosPrincipal serverPrincipal,
+        EncryptionKey serverKey ) throws KerberosException
+    {
+        EncTicketPart encTicketPart = encTicketModifier.getEncTicketPart();
+
+        EncryptedData encryptedTicketPart = lockBox.seal( serverKey, encTicketPart, KeyUsage.NUMBER2 );
+
+        Ticket ticket = new Ticket();
+        ticket.setTktVno( 5 );
+        ticket.setServerPrincipal( serverPrincipal );
+        ticket.setEncPart( encryptedTicketPart );
+
+        ticket.setEncTicketPart( encTicketPart );
+
+        return ticket;
+    }
+
+
+    protected KdcRequest getKdcRequest( Ticket tgt, RequestBody requestBody ) throws Exception
+    {
+        return getKdcRequest( tgt, requestBody, ChecksumType.RSA_MD5 );
+    }
+
+
+    /**
+     * Create a KdcRequest, suitable for requesting a service Ticket.
+     */
+    protected KdcRequest getKdcRequest( Ticket tgt, RequestBody requestBody, ChecksumType checksumType )
+        throws Exception
+    {
+        // Get the session key from the service ticket.
+        sessionKey = tgt.getEncTicketPart().getSessionKey();
+
+        // Generate a new sequence number.
+        sequenceNumber = random.nextInt();
+        now = new KerberosTime();
+
+        EncryptedData authenticator = getAuthenticator( tgt.getEncTicketPart().getClientPrincipal(), requestBody, checksumType );
+
+        PaData[] paData = getPreAuthenticationData( tgt, authenticator );
+
+        return new KdcRequest( 5, KerberosMessageType.TGS_REQ, paData, requestBody );
+    }
+
+
+    /**
+     * Build the authenticator.  The authenticator communicates the sub-session key the
+     * service will use to unlock the private message.  The service will unlock the
+     * authenticator with the session key from the ticket.  The authenticator client
+     * principal must equal the principal in the ticket.  
+     *
+     * @param clientPrincipal
+     * @return The {@link EncryptedData} containing the {@link Authenticator}.
+     * @throws KerberosException
+     */
+    protected EncryptedData getAuthenticator( KerberosPrincipal clientPrincipal, RequestBody requestBody,
+        ChecksumType checksumType ) throws IOException, KerberosException
+    {
+        AuthenticatorModifier authenticatorModifier = new AuthenticatorModifier();
+
+        clientMicroSeconds = random.nextInt();
+
+        authenticatorModifier.setVersionNumber( 5 );
+        authenticatorModifier.setClientPrincipal( clientPrincipal );
+        authenticatorModifier.setClientTime( now );
+        authenticatorModifier.setClientMicroSecond( clientMicroSeconds );
+        authenticatorModifier.setSubSessionKey( subSessionKey );
+        authenticatorModifier.setSequenceNumber( sequenceNumber );
+
+        Checksum checksum = getBodyChecksum( requestBody, checksumType );
+        authenticatorModifier.setChecksum( checksum );
+
+        Authenticator authenticator = authenticatorModifier.getAuthenticator();
+
+        EncryptedData encryptedAuthenticator = lockBox.seal( sessionKey, authenticator, KeyUsage.NUMBER7 );
+
+        return encryptedAuthenticator;
+    }
+
+
+    protected Checksum getBodyChecksum( RequestBody requestBody, ChecksumType checksumType ) throws IOException,
+        KerberosException
+    {
+        KdcRequestEncoder bodyEncoder = new KdcRequestEncoder();
+        byte[] bodyBytes = bodyEncoder.encodeRequestBody( requestBody );
+
+        ChecksumHandler checksumHandler = new ChecksumHandler();
+        return checksumHandler.calculateChecksum( checksumType, bodyBytes, null, KeyUsage.NUMBER8 );
+    }
+
+
+    /**
+     * Make new AP_REQ, aka the "auth header," and package it into pre-authentication data.
+     *
+     * @param ticket
+     * @param authenticator
+     * @return
+     * @throws IOException
+     */
+    protected PaData[] getPreAuthenticationData( Ticket ticket, EncryptedData authenticator )
+        throws IOException
+    {
+        ApplicationRequest applicationRequest = new ApplicationRequest();
+        applicationRequest.setMessageType( KerberosMessageType.AP_REQ );
+        applicationRequest.setProtocolVersionNumber( 5 );
+        applicationRequest.setApOptions( new ApOptions() );
+        applicationRequest.setTicket( ticket );
+        applicationRequest.setEncPart( authenticator );
+
+        ApplicationRequestEncoder encoder = new ApplicationRequestEncoder();
+        byte[] encodedApReq = encoder.encode( applicationRequest );
+
+        PaData[] paData = new PaData[1];
+
+        PaData preAuth = new PaData();
+        preAuth.setPaDataType( PaDataType.PA_TGS_REQ );
+        preAuth.setPaDataValue( encodedApReq );
+
+        paData[0] = preAuth;
+
+        return paData;
+    }
+
+
+    protected PrincipalName getPrincipalName( String name )
+    {
+        PrincipalName principalName = new PrincipalName();
+        principalName.addName( name );
+        principalName.setNameType( PrincipalNameType.KRB_NT_PRINCIPAL );
+
+        return principalName;
+    }
+
+    protected static class DummySession extends BaseIoSession
+    {
+        Object message;
+
+
+        @Override
+        public WriteFuture write( Object message )
+        {
+            this.message = message;
+
+            return super.write( message );
+        }
+
+
+        protected Object getMessage()
+        {
+            return message;
+        }
+
+
+        protected void updateTrafficMask()
+        {
+            // Do nothing.
+        }
+
+
+        public IoService getService()
+        {
+            return null;
+        }
+
+
+        public IoHandler getHandler()
+        {
+            return null;
+        }
+
+
+        public IoFilterChain getFilterChain()
+        {
+            return null;
+        }
+
+
+        public TransportType getTransportType()
+        {
+            return null;
+        }
+
+
+        public SocketAddress getRemoteAddress()
+        {
+            return new InetSocketAddress( 10088 );
+        }
+
+
+        public SocketAddress getLocalAddress()
+        {
+            return null;
+        }
+
+
+        public IoSessionConfig getConfig()
+        {
+            return null;
+        }
+
+
+        public int getScheduledWriteRequests()
+        {
+            return 0;
+        }
+
+
+        public SocketAddress getServiceAddress()
+        {
+            return null;
+        }
+
+
+        public IoServiceConfig getServiceConfig()
+        {
+            return null;
+        }
+
+
+        public int getScheduledWriteBytes()
+        {
+            return 0;
+        }
+    }
+}
diff --git a/old_trunk/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/protocol/AuthenticationEncryptionTypeTest.java b/old_trunk/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/protocol/AuthenticationEncryptionTypeTest.java
new file mode 100644
index 0000000..81a62ed
--- /dev/null
+++ b/old_trunk/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/protocol/AuthenticationEncryptionTypeTest.java
@@ -0,0 +1,276 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.protocol;
+
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.apache.directory.server.kerberos.kdc.KdcServer;
+import org.apache.directory.server.kerberos.shared.KerberosConstants;
+import org.apache.directory.server.kerberos.shared.KerberosMessageType;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.CipherTextHandler;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.KerberosKeyFactory;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.KeyUsage;
+import org.apache.directory.server.kerberos.shared.io.encoder.EncryptedDataEncoder;
+import org.apache.directory.server.kerberos.shared.messages.AuthenticationReply;
+import org.apache.directory.server.kerberos.shared.messages.ErrorMessage;
+import org.apache.directory.server.kerberos.shared.messages.KdcRequest;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptedData;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptedTimeStamp;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+import org.apache.directory.server.kerberos.shared.messages.value.KdcOptions;
+import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
+import org.apache.directory.server.kerberos.shared.messages.value.PaData;
+import org.apache.directory.server.kerberos.shared.messages.value.RequestBodyModifier;
+import org.apache.directory.server.kerberos.shared.messages.value.types.PaDataType;
+import org.apache.directory.server.kerberos.shared.store.PrincipalStore;
+
+
+/**
+ * Tests various facets of working with encryption types in the Authentication Service (AS).
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class AuthenticationEncryptionTypeTest extends AbstractAuthenticationServiceTest
+{
+    private KdcServer config;
+    private PrincipalStore store;
+    private KerberosProtocolHandler handler;
+    private DummySession session;
+
+
+    /**
+     * Creates a new instance of {@link AuthenticationEncryptionTypeTest}.
+     */
+    public AuthenticationEncryptionTypeTest()
+    {
+        config = new KdcServer();
+        store = new MapPrincipalStoreImpl();
+        handler = new KerberosProtocolHandler( config, store );
+        session = new DummySession();
+        lockBox = new CipherTextHandler();
+    }
+
+
+    /**
+     * Tests a basic request using DES-CBC-MD5.
+     * 
+     * @throws Exception
+     */
+    public void testRequestDesCbcMd5() throws Exception
+    {
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setClientName( getPrincipalName( "hnelson" ) );
+        modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+
+        Set<EncryptionType> encryptionTypes = new HashSet<EncryptionType>();
+        encryptionTypes.add( EncryptionType.DES_CBC_MD5 );
+
+        modifier.setEType( encryptionTypes );
+        modifier.setNonce( random.nextInt() );
+        modifier.setKdcOptions( new KdcOptions() );
+
+        long now = System.currentTimeMillis();
+        KerberosTime requestedEndTime = new KerberosTime( now + KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        String passPhrase = "secret";
+        PaData[] paData = getPreAuthEncryptedTimeStamp( clientPrincipal, passPhrase );
+
+        KdcRequest message = new KdcRequest( KerberosConstants.KERBEROS_V5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
+
+        handler.messageReceived( session, message );
+
+        AuthenticationReply reply = ( AuthenticationReply ) session.getMessage();
+
+        assertEquals( "Encryption type", EncryptionType.DES_CBC_MD5, reply.getEncPart().getEType() );
+    }
+
+
+    /**
+     * Tests the configuration of AES-128 as the sole supported encryption type.
+     * 
+     * @throws Exception
+     */
+    public void testRequestAes128() throws Exception
+    {
+        EncryptionType[] configuredEncryptionTypes =
+            { EncryptionType.AES128_CTS_HMAC_SHA1_96 };
+        config.setEncryptionTypes( configuredEncryptionTypes );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setClientName( getPrincipalName( "hnelson" ) );
+        modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+
+        Set<EncryptionType> encryptionTypes = new HashSet<EncryptionType>();
+        encryptionTypes.add( EncryptionType.AES128_CTS_HMAC_SHA1_96 );
+
+        modifier.setEType( encryptionTypes );
+        modifier.setNonce( random.nextInt() );
+        modifier.setKdcOptions( new KdcOptions() );
+
+        long now = System.currentTimeMillis();
+        KerberosTime requestedEndTime = new KerberosTime( now + KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        String principalName = "hnelson@EXAMPLE.COM";
+        String passPhrase = "secret";
+        Set<EncryptionType> preAuthEncryptionTypes = new HashSet<EncryptionType>();
+        preAuthEncryptionTypes.add( EncryptionType.AES128_CTS_HMAC_SHA1_96 );
+
+        Map<EncryptionType, EncryptionKey> keyMap = KerberosKeyFactory.getKerberosKeys( principalName, passPhrase,
+            preAuthEncryptionTypes );
+        EncryptionKey clientKey = keyMap.get( EncryptionType.AES128_CTS_HMAC_SHA1_96 );
+
+        KerberosTime timeStamp = new KerberosTime();
+        PaData[] paData = getPreAuthEncryptedTimeStamp( clientKey, timeStamp );
+
+        KdcRequest message = new KdcRequest( KerberosConstants.KERBEROS_V5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
+
+        handler.messageReceived( session, message );
+
+        AuthenticationReply reply = ( AuthenticationReply ) session.getMessage();
+
+        assertTrue( "Requested end time", requestedEndTime.equals( reply.getEndTime() ) );
+        assertTrue( "PRE_AUTHENT flag", reply.getTicket().getEncTicketPart().getFlags().isPreAuth() );
+        assertEquals( "Encryption type", EncryptionType.AES128_CTS_HMAC_SHA1_96, reply.getEncPart().getEType() );
+    }
+
+
+    /**
+     * Tests that the client-chosen nonce is correctly returned in the response.
+     * 
+     * @throws Exception
+     */
+    public void testNonce() throws Exception
+    {
+        EncryptionType[] configuredEncryptionTypes =
+            { EncryptionType.AES128_CTS_HMAC_SHA1_96 };
+        config.setEncryptionTypes( configuredEncryptionTypes );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setClientName( getPrincipalName( "hnelson" ) );
+        modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+
+        Set<EncryptionType> encryptionTypes = new HashSet<EncryptionType>();
+        encryptionTypes.add( EncryptionType.AES128_CTS_HMAC_SHA1_96 );
+
+        modifier.setEType( encryptionTypes );
+        int nonce = random.nextInt();
+        modifier.setNonce( nonce );
+        modifier.setKdcOptions( new KdcOptions() );
+
+        long now = System.currentTimeMillis();
+        KerberosTime requestedEndTime = new KerberosTime( now + KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        String principalName = "hnelson@EXAMPLE.COM";
+        String passPhrase = "secret";
+        Set<EncryptionType> preAuthEncryptionTypes = new HashSet<EncryptionType>();
+        preAuthEncryptionTypes.add( EncryptionType.AES128_CTS_HMAC_SHA1_96 );
+
+        Map<EncryptionType, EncryptionKey> keyMap = KerberosKeyFactory.getKerberosKeys( principalName, passPhrase,
+            preAuthEncryptionTypes );
+        EncryptionKey clientKey = keyMap.get( EncryptionType.AES128_CTS_HMAC_SHA1_96 );
+
+        KerberosTime timeStamp = new KerberosTime();
+        PaData[] paData = getPreAuthEncryptedTimeStamp( clientKey, timeStamp );
+
+        KdcRequest message = new KdcRequest( KerberosConstants.KERBEROS_V5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
+
+        handler.messageReceived( session, message );
+
+        AuthenticationReply reply = ( AuthenticationReply ) session.getMessage();
+
+        assertTrue( "Requested end time", requestedEndTime.equals( reply.getEndTime() ) );
+        assertTrue( "PRE_AUTHENT flag", reply.getTicket().getEncTicketPart().getFlags().isPreAuth() );
+        assertEquals( "Encryption type", EncryptionType.AES128_CTS_HMAC_SHA1_96, reply.getEncPart().getEType() );
+
+        assertEquals( "Nonce", nonce, reply.getNonce() );
+    }
+
+
+    /**
+     * Tests when a request is made for an encryption type that is not enabled in
+     * configuration that the request fails with the correct error message.
+     * 
+     * @throws Exception
+     */
+    public void testAes128Configuration() throws Exception
+    {
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setClientName( getPrincipalName( "hnelson" ) );
+        modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+
+        Set<EncryptionType> requestedEncryptionTypes = new HashSet<EncryptionType>();
+        requestedEncryptionTypes.add( EncryptionType.AES128_CTS_HMAC_SHA1_96 );
+
+        modifier.setEType( requestedEncryptionTypes );
+        modifier.setNonce( random.nextInt() );
+        modifier.setKdcOptions( new KdcOptions() );
+
+        long now = System.currentTimeMillis();
+        KerberosTime requestedEndTime = new KerberosTime( now + KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        String passPhrase = "secret";
+        PaData[] paData = getPreAuthEncryptedTimeStamp( clientPrincipal, passPhrase );
+
+        KdcRequest message = new KdcRequest( KerberosConstants.KERBEROS_V5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
+
+        handler.messageReceived( session, message );
+
+        ErrorMessage error = ( ErrorMessage ) session.getMessage();
+        assertEquals( "KDC has no support for encryption type", 14, error.getErrorCode() );
+    }
+
+
+    protected PaData[] getPreAuthEncryptedTimeStamp( EncryptionKey clientKey, KerberosTime timeStamp )
+        throws Exception
+    {
+        PaData[] paData = new PaData[1];
+
+        EncryptedTimeStamp encryptedTimeStamp = new EncryptedTimeStamp( timeStamp, 0 );
+
+        EncryptedData encryptedData = lockBox.seal( clientKey, encryptedTimeStamp, KeyUsage.NUMBER1 );
+
+        byte[] encodedEncryptedData = EncryptedDataEncoder.encode( encryptedData );
+
+        PaData preAuth = new PaData();
+        preAuth.setPaDataType( PaDataType.PA_ENC_TIMESTAMP );
+        preAuth.setPaDataValue( encodedEncryptedData );
+
+        paData[0] = preAuth;
+
+        return paData;
+    }
+}
diff --git a/old_trunk/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/protocol/AuthenticationPolicyTest.java b/old_trunk/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/protocol/AuthenticationPolicyTest.java
new file mode 100644
index 0000000..58ef10f
--- /dev/null
+++ b/old_trunk/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/protocol/AuthenticationPolicyTest.java
@@ -0,0 +1,308 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.protocol;
+
+
+import org.apache.directory.server.kerberos.kdc.KdcServer;
+import org.apache.directory.server.kerberos.shared.KerberosConstants;
+import org.apache.directory.server.kerberos.shared.KerberosMessageType;
+import org.apache.directory.server.kerberos.shared.messages.ErrorMessage;
+import org.apache.directory.server.kerberos.shared.messages.KdcRequest;
+import org.apache.directory.server.kerberos.shared.messages.value.KdcOptions;
+import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
+import org.apache.directory.server.kerberos.shared.messages.value.RequestBodyModifier;
+import org.apache.directory.server.kerberos.shared.store.PrincipalStore;
+
+
+/**
+ * Tests configuration of Authentication Service (AS) policy.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class AuthenticationPolicyTest extends AbstractAuthenticationServiceTest
+{
+    private KdcServer config;
+    private PrincipalStore store;
+    private KerberosProtocolHandler handler;
+    private DummySession session;
+
+
+    /**
+     * Creates a new instance of {@link AuthenticationPolicyTest}.
+     */
+    public AuthenticationPolicyTest()
+    {
+        config = new KdcServer();
+        store = new MapPrincipalStoreImpl();
+        handler = new KerberosProtocolHandler( config, store );
+        session = new DummySession();
+    }
+
+
+    /**
+     * Tests when forwardable tickets are disallowed that requests for
+     * forwardable tickets fail with the correct error message.
+     * 
+     * @throws Exception 
+     */
+    public void testForwardableTicket() throws Exception
+    {
+        // Deny FORWARDABLE tickets in policy.
+        config.setPaEncTimestampRequired( false );
+        config.setForwardableAllowed( false );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setClientName( getPrincipalName( "hnelson" ) );
+        modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        kdcOptions.set( KdcOptions.FORWARDABLE );
+        modifier.setKdcOptions( kdcOptions );
+
+        long now = System.currentTimeMillis();
+        KerberosTime requestedEndTime = new KerberosTime( now + 1 * KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        KdcRequest message = new KdcRequest( KerberosConstants.KERBEROS_V5, KerberosMessageType.AS_REQ, null, modifier.getRequestBody() );
+
+        handler.messageReceived( session, message );
+
+        ErrorMessage error = ( ErrorMessage ) session.getMessage();
+        assertEquals( "KDC policy rejects request", 12, error.getErrorCode() );
+    }
+
+
+    /**
+     * Tests when proxiable tickets are disallowed that requests for
+     * proxiable tickets fail with the correct error message.
+     * 
+     * @throws Exception 
+     */
+    public void testProxiableTicket() throws Exception
+    {
+        // Deny PROXIABLE tickets in policy.
+        config.setPaEncTimestampRequired( false );
+        config.setProxiableAllowed( false );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setClientName( getPrincipalName( "hnelson" ) );
+        modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        kdcOptions.set( KdcOptions.PROXIABLE );
+        modifier.setKdcOptions( kdcOptions );
+
+        long now = System.currentTimeMillis();
+        KerberosTime requestedEndTime = new KerberosTime( now + 1 * KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        KdcRequest message = new KdcRequest( KerberosConstants.KERBEROS_V5, KerberosMessageType.AS_REQ, null, modifier.getRequestBody() );
+
+        handler.messageReceived( session, message );
+
+        ErrorMessage error = ( ErrorMessage ) session.getMessage();
+        assertEquals( "KDC policy rejects request", 12, error.getErrorCode() );
+    }
+
+
+    /**
+     * Tests when postdated tickets are disallowed that requests for
+     * ALLOW-POSTDATE tickets fail with the correct error message.
+     * 
+     * @throws Exception 
+     */
+    public void testAllowPostdate() throws Exception
+    {
+        // Deny POSTDATED tickets in policy.
+        config.setPaEncTimestampRequired( false );
+        config.setPostdatedAllowed( false );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setClientName( getPrincipalName( "hnelson" ) );
+        modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        kdcOptions.set( KdcOptions.ALLOW_POSTDATE );
+        modifier.setKdcOptions( kdcOptions );
+
+        long now = System.currentTimeMillis();
+        KerberosTime requestedEndTime = new KerberosTime( now + 1 * KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        KdcRequest message = new KdcRequest( KerberosConstants.KERBEROS_V5, KerberosMessageType.AS_REQ, null, modifier.getRequestBody() );
+
+        handler.messageReceived( session, message );
+
+        ErrorMessage error = ( ErrorMessage ) session.getMessage();
+        assertEquals( "KDC policy rejects request", 12, error.getErrorCode() );
+    }
+
+
+    /**
+     * Tests when postdated tickets are disallowed that requests for
+     * postdated tickets fail with the correct error message.
+     * 
+     * @throws Exception 
+     */
+    public void testPostdate() throws Exception
+    {
+        // Deny POSTDATED tickets in policy.
+        config.setPaEncTimestampRequired( false );
+        config.setPostdatedAllowed( false );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setClientName( getPrincipalName( "hnelson" ) );
+        modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        kdcOptions.set( KdcOptions.POSTDATED );
+        modifier.setKdcOptions( kdcOptions );
+
+        long now = System.currentTimeMillis();
+        KerberosTime requestedEndTime = new KerberosTime( now + 1 * KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        KdcRequest message = new KdcRequest( KerberosConstants.KERBEROS_V5, KerberosMessageType.AS_REQ, null, modifier.getRequestBody() );
+
+        handler.messageReceived( session, message );
+
+        ErrorMessage error = ( ErrorMessage ) session.getMessage();
+        assertEquals( "KDC policy rejects request", 12, error.getErrorCode() );
+    }
+
+
+    /**
+     * Tests when renewable tickets are disallowed that requests for
+     * RENEWABLE-OK tickets fail with the correct error message.
+     * 
+     * @throws Exception 
+     */
+    public void testRenewableOk() throws Exception
+    {
+        // Deny RENEWABLE tickets in policy.
+        config.setPaEncTimestampRequired( false );
+        config.setRenewableAllowed( false );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setClientName( getPrincipalName( "hnelson" ) );
+        modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        kdcOptions.set( KdcOptions.RENEWABLE_OK );
+        modifier.setKdcOptions( kdcOptions );
+
+        long now = System.currentTimeMillis();
+        KerberosTime requestedEndTime = new KerberosTime( now + KerberosTime.WEEK );
+        modifier.setTill( requestedEndTime );
+
+        KdcRequest message = new KdcRequest( KerberosConstants.KERBEROS_V5, KerberosMessageType.AS_REQ, null, modifier.getRequestBody() );
+
+        handler.messageReceived( session, message );
+
+        ErrorMessage error = ( ErrorMessage ) session.getMessage();
+        assertEquals( "KDC policy rejects request", 12, error.getErrorCode() );
+    }
+
+
+    /**
+     * Tests when renewable tickets are disallowed that requests for
+     * renewable tickets fail with the correct error message.
+     * 
+     * @throws Exception 
+     */
+    public void testRenewableTicket() throws Exception
+    {
+        // Deny RENEWABLE tickets in policy.
+        config.setPaEncTimestampRequired( false );
+        config.setRenewableAllowed( false );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setClientName( getPrincipalName( "hnelson" ) );
+        modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        kdcOptions.set( KdcOptions.RENEWABLE );
+        modifier.setKdcOptions( kdcOptions );
+
+        long now = System.currentTimeMillis();
+        KerberosTime requestedEndTime = new KerberosTime( now + 1 * KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        KerberosTime requestedRenewTillTime = new KerberosTime( now + KerberosTime.WEEK / 2 );
+        modifier.setRtime( requestedRenewTillTime );
+
+        KdcRequest message = new KdcRequest( KerberosConstants.KERBEROS_V5, KerberosMessageType.AS_REQ, null, modifier.getRequestBody() );
+
+        handler.messageReceived( session, message );
+
+        ErrorMessage error = ( ErrorMessage ) session.getMessage();
+        assertEquals( "KDC policy rejects request", 12, error.getErrorCode() );
+    }
+
+
+    /**
+     * Tests when empty addresses are disallowed that requests with no addresses
+     * fail with the correct error message.
+     * 
+     * @throws Exception 
+     */
+    public void testEmptyAddresses() throws Exception
+    {
+        // Deny empty addresses in policy.
+        config.setPaEncTimestampRequired( false );
+        config.setEmptyAddressesAllowed( false );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setClientName( getPrincipalName( "hnelson" ) );
+        modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        modifier.setKdcOptions( kdcOptions );
+
+        long now = System.currentTimeMillis();
+        KerberosTime requestedEndTime = new KerberosTime( now + 1 * KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        KerberosTime requestedRenewTillTime = new KerberosTime( now + KerberosTime.WEEK / 2 );
+        modifier.setRtime( requestedRenewTillTime );
+
+        KdcRequest message = new KdcRequest( KerberosConstants.KERBEROS_V5, KerberosMessageType.AS_REQ, null, modifier.getRequestBody() );
+
+        handler.messageReceived( session, message );
+
+        ErrorMessage error = ( ErrorMessage ) session.getMessage();
+        assertEquals( "KDC policy rejects request", 12, error.getErrorCode() );
+    }
+}
diff --git a/old_trunk/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/protocol/AuthenticationServiceTest.java b/old_trunk/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/protocol/AuthenticationServiceTest.java
new file mode 100644
index 0000000..6730361
--- /dev/null
+++ b/old_trunk/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/protocol/AuthenticationServiceTest.java
@@ -0,0 +1,1249 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.protocol;
+
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.apache.directory.server.kerberos.kdc.KdcServer;
+import org.apache.directory.server.kerberos.shared.KerberosMessageType;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.CipherTextHandler;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
+import org.apache.directory.server.kerberos.shared.messages.AuthenticationReply;
+import org.apache.directory.server.kerberos.shared.messages.ErrorMessage;
+import org.apache.directory.server.kerberos.shared.messages.KdcRequest;
+import org.apache.directory.server.kerberos.shared.messages.value.KdcOptions;
+import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
+import org.apache.directory.server.kerberos.shared.messages.value.PaData;
+import org.apache.directory.server.kerberos.shared.messages.value.RequestBodyModifier;
+import org.apache.directory.server.kerberos.shared.store.PrincipalStore;
+
+
+/**
+ * Tests the Authentication Service (AS) via the {@link KerberosProtocolHandler}.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class AuthenticationServiceTest extends AbstractAuthenticationServiceTest
+{
+    private KdcServer config;
+    private PrincipalStore store;
+    private KerberosProtocolHandler handler;
+    private DummySession session;
+
+
+    /**
+     * Creates a new instance of {@link AuthenticationServiceTest}.
+     */
+    public AuthenticationServiceTest()
+    {
+        config = new KdcServer();
+        store = new MapPrincipalStoreImpl();
+        handler = new KerberosProtocolHandler( config, store );
+        session = new DummySession();
+        lockBox = new CipherTextHandler();
+    }
+
+
+    /**
+     * Tests the default minimum request, which consists of as little as the
+     * client name, realm, till time, nonce, and encryption types.
+     * 
+     * This is the request archetype.
+     */
+    public void testRequestArchetype()
+    {
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setClientName( getPrincipalName( "hnelson" ) );
+        modifier.setServerName( getPrincipalName( "hnelson" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+
+        KerberosTime till = new KerberosTime();
+        modifier.setTill( till );
+
+        KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REQ, null, modifier.getRequestBody() );
+
+        handler.messageReceived( session, message );
+
+        ErrorMessage error = ( ErrorMessage ) session.getMessage();
+
+        assertEquals( "Additional pre-authentication required", 25, error.getErrorCode() );
+    }
+
+
+    /**
+     * Tests the protocol version number, which must be '5'.
+     */
+    public void testProtocolVersionNumber()
+    {
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setClientName( getPrincipalName( "hnelson" ) );
+        modifier.setServerName( getPrincipalName( "hnelson" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+
+        KdcRequest message = new KdcRequest( 4, KerberosMessageType.AS_REQ, null, modifier.getRequestBody() );
+
+        handler.messageReceived( session, message );
+
+        ErrorMessage error = ( ErrorMessage ) session.getMessage();
+        assertEquals( "Requested protocol version number not supported", 3, error.getErrorCode() );
+    }
+
+
+    /**
+     * Tests that Kerberos reply messages sent to the KDC will be rejected with the
+     * correct error message.
+     */
+    public void testIncorrectMessageDirection()
+    {
+        KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REP, null, null );
+
+        handler.messageReceived( session, message );
+
+        ErrorMessage error = ( ErrorMessage ) session.getMessage();
+        assertEquals( "Incorrect message direction", 47, error.getErrorCode() );
+
+        message = new KdcRequest( 5, KerberosMessageType.TGS_REP, null, null );
+
+        handler.messageReceived( session, message );
+
+        error = ( ErrorMessage ) session.getMessage();
+        assertEquals( "Incorrect message direction", 47, error.getErrorCode() );
+    }
+
+
+    /**
+     * Tests that a non-existent client principal returns the correct error message.
+     * 
+     * "If the requested client principal named in the request is
+     * unknown because it doesn't exist in the KDC's principal database,
+     * then an error message with a KDC_ERR_C_PRINCIPAL_UNKNOWN is returned."
+     */
+    public void testClientNotFound()
+    {
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setClientName( getPrincipalName( "baduser" ) );
+        modifier.setServerName( getPrincipalName( "hnelson" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+
+        KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REQ, null, modifier.getRequestBody() );
+
+        handler.messageReceived( session, message );
+
+        ErrorMessage error = ( ErrorMessage ) session.getMessage();
+        assertEquals( "Client not found in Kerberos database", 6, error.getErrorCode() );
+    }
+
+
+    /**
+     * Test when an unsupported encryption type is requested, that the request is
+     * rejected with the correct error message.
+     * 
+     * "If the server cannot accommodate any encryption type requested by the
+     * client, an error message with code KDC_ERR_ETYPE_NOSUPP is returned."
+     * 
+     * @throws Exception 
+     */
+    public void testEncryptionTypeNoSupport() throws Exception
+    {
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setClientName( getPrincipalName( "hnelson" ) );
+        modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+
+        Set<EncryptionType> encryptionTypes = new HashSet<EncryptionType>();
+        encryptionTypes.add( EncryptionType.DES3_CBC_MD5 );
+
+        modifier.setEType( encryptionTypes );
+
+        modifier.setKdcOptions( new KdcOptions() );
+
+        long now = System.currentTimeMillis();
+
+        KerberosTime requestedEndTime = new KerberosTime( now + KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+
+        String passPhrase = "secret";
+        PaData[] paData = getPreAuthEncryptedTimeStamp( clientPrincipal, passPhrase );
+
+        KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
+
+        handler.messageReceived( session, message );
+
+        ErrorMessage error = ( ErrorMessage ) session.getMessage();
+        assertEquals( "KDC has no support for encryption type", 14, error.getErrorCode() );
+    }
+
+
+    /**
+     * Tests that a non-existent server principal returns the correct error message.
+     * 
+     * @throws Exception 
+     */
+    public void testServerNotFound() throws Exception
+    {
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setClientName( getPrincipalName( "hnelson" ) );
+        modifier.setServerName( getPrincipalName( "badserver" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+
+        String passPhrase = "secret";
+        PaData[] paData = getPreAuthEncryptedTimeStamp( clientPrincipal, passPhrase );
+
+        KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
+
+        handler.messageReceived( session, message );
+
+        ErrorMessage error = ( ErrorMessage ) session.getMessage();
+        assertEquals( "Server not found in Kerberos database", 7, error.getErrorCode() );
+    }
+
+
+    /**
+     * Tests that when a client principal is not configured with Kerberos keys that
+     * the correct error message is returned.
+     */
+    public void testClientNullKey()
+    {
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setClientName( getPrincipalName( "tquist" ) );
+        modifier.setServerName( getPrincipalName( "hnelson" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+
+        KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REQ, null, modifier.getRequestBody() );
+
+        handler.messageReceived( session, message );
+
+        ErrorMessage error = ( ErrorMessage ) session.getMessage();
+        assertEquals( "The client or server has a null key", 9, error.getErrorCode() );
+    }
+
+
+    /**
+     * Tests that when a server principal is not configured with Kerberos keys that
+     * the correct error message is returned.
+     * 
+     * @throws Exception 
+     */
+    public void testServerNullKey() throws Exception
+    {
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setClientName( getPrincipalName( "hnelson" ) );
+        modifier.setServerName( getPrincipalName( "tquist" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+
+        String passPhrase = "secret";
+        PaData[] paData = getPreAuthEncryptedTimeStamp( clientPrincipal, passPhrase );
+
+        KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
+
+        handler.messageReceived( session, message );
+
+        ErrorMessage error = ( ErrorMessage ) session.getMessage();
+        assertEquals( "The client or server has a null key", 9, error.getErrorCode() );
+    }
+
+
+    /**
+     * Tests when the starttime is absent and the POSTDATED option has not been
+     * specified, that the starttime of the ticket is set to the authentication
+     * server's current time.
+     * 
+     * "If the requested starttime is absent, indicates a time in the past,
+     * or is within the window of acceptable clock skew for the KDC and the
+     * POSTDATE option has not been specified, then the starttime of the
+     * ticket is set to the authentication server's current time."
+     * 
+     * @throws Exception 
+     */
+    public void testStartTimeAbsentNoPostdate() throws Exception
+    {
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setClientName( getPrincipalName( "hnelson" ) );
+        modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+
+        modifier.setKdcOptions( new KdcOptions() );
+
+        long now = System.currentTimeMillis();
+
+        KerberosTime requestedEndTime = new KerberosTime( now + KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+
+        String passPhrase = "secret";
+        PaData[] paData = getPreAuthEncryptedTimeStamp( clientPrincipal, passPhrase );
+
+        KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
+
+        handler.messageReceived( session, message );
+
+        AuthenticationReply reply = ( AuthenticationReply ) session.getMessage();
+
+        KerberosTime expectedStartTime = new KerberosTime( now );
+        boolean isClose = reply.getStartTime() == null
+            || Math.abs( reply.getStartTime().getTime() - expectedStartTime.getTime() ) < 5000;
+        assertTrue( "Expected start time", isClose );
+    }
+
+
+    /**
+     * Tests when the starttime indicates a time in the past and the POSTDATED option
+     * has not been specified, that the starttime of the ticket is set to the
+     * authentication server's current time.
+     * 
+     * "If the requested starttime is absent, indicates a time in the past,
+     * or is within the window of acceptable clock skew for the KDC and the
+     * POSTDATE option has not been specified, then the starttime of the
+     * ticket is set to the authentication server's current time."
+     * 
+     * @throws Exception 
+     */
+    public void testStartTimeInThePastNoPostdate() throws Exception
+    {
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setClientName( getPrincipalName( "hnelson" ) );
+        modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+
+        modifier.setKdcOptions( new KdcOptions() );
+
+        long now = System.currentTimeMillis();
+
+        KerberosTime requestedStartTime = new KerberosTime( now + -1 * KerberosTime.DAY );
+        modifier.setFrom( requestedStartTime );
+
+        KerberosTime requestedEndTime = new KerberosTime( now + KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+
+        String passPhrase = "secret";
+        PaData[] paData = getPreAuthEncryptedTimeStamp( clientPrincipal, passPhrase );
+
+        KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
+
+        handler.messageReceived( session, message );
+
+        AuthenticationReply reply = ( AuthenticationReply ) session.getMessage();
+
+        KerberosTime expectedStartTime = new KerberosTime( now );
+        boolean isClose = reply.getStartTime() == null
+            || Math.abs( reply.getStartTime().getTime() - expectedStartTime.getTime() ) < 5000;
+        assertTrue( "Expected start time", isClose );
+    }
+
+
+    /**
+     * Tests when the starttime is within the window of acceptable clock skew for
+     * the KDC and the POSTDATED option has not been specified, that the starttime
+     * of the ticket is set to the authentication server's current time.
+     * 
+     * "If the requested starttime is absent, indicates a time in the past,
+     * or is within the window of acceptable clock skew for the KDC and the
+     * POSTDATE option has not been specified, then the starttime of the
+     * ticket is set to the authentication server's current time."
+     * 
+     * @throws Exception 
+     */
+    public void testStartTimeAcceptableClockSkewNoPostdate() throws Exception
+    {
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setClientName( getPrincipalName( "hnelson" ) );
+        modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+
+        modifier.setKdcOptions( new KdcOptions() );
+
+        long now = System.currentTimeMillis();
+
+        KerberosTime requestedStartTime = new KerberosTime( now );
+        modifier.setFrom( requestedStartTime );
+
+        KerberosTime requestedEndTime = new KerberosTime( now + KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+
+        String passPhrase = "secret";
+        PaData[] paData = getPreAuthEncryptedTimeStamp( clientPrincipal, passPhrase );
+
+        KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
+
+        handler.messageReceived( session, message );
+
+        AuthenticationReply reply = ( AuthenticationReply ) session.getMessage();
+
+        KerberosTime expectedStartTime = new KerberosTime( now );
+        boolean isClose = reply.getStartTime() == null
+            || Math.abs( reply.getStartTime().getTime() - expectedStartTime.getTime() ) < 5000;
+        assertTrue( "Expected start time", isClose );
+    }
+
+
+    /**
+     * Tests when a start time is after an end time that the request is rejected with the
+     * correct error message.
+     * 
+     * "If the requested expiration time minus the starttime (as determined above)
+     * is less than a site-determined minimum lifetime, an error message with code
+     * KDC_ERR_NEVER_VALID is returned."
+     *
+     * @throws Exception
+     */
+    public void testStartTimeOrderNeverValid() throws Exception
+    {
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setClientName( getPrincipalName( "hnelson" ) );
+        modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        kdcOptions.set( KdcOptions.POSTDATED );
+        modifier.setKdcOptions( kdcOptions );
+
+        long now = System.currentTimeMillis();
+
+        KerberosTime requestedStartTime = new KerberosTime( now + KerberosTime.DAY );
+        modifier.setFrom( requestedStartTime );
+
+        KerberosTime requestedEndTime = new KerberosTime( now );
+        modifier.setTill( requestedEndTime );
+
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        String passPhrase = "secret";
+        PaData[] paData = getPreAuthEncryptedTimeStamp( clientPrincipal, passPhrase );
+
+        KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
+
+        handler.messageReceived( session, message );
+
+        ErrorMessage error = ( ErrorMessage ) session.getMessage();
+        assertEquals( "Requested start time is later than end time", 11, error.getErrorCode() );
+    }
+
+
+    /**
+     * Tests when the absolute value of the difference between the start time is
+     * and the end time is less than a configured minimum, that the request is
+     * rejected with the correct error message.
+     * 
+     * "If the requested expiration time minus the starttime (as determined above)
+     * is less than a site-determined minimum lifetime, an error message with code
+     * KDC_ERR_NEVER_VALID is returned."
+     *
+     * @throws Exception
+     */
+    public void testStartTimeMinimumNeverValid() throws Exception
+    {
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setClientName( getPrincipalName( "hnelson" ) );
+        modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+
+        modifier.setKdcOptions( new KdcOptions() );
+
+        long now = System.currentTimeMillis();
+
+        KerberosTime requestedStartTime = new KerberosTime( now );
+        modifier.setFrom( requestedStartTime );
+
+        KerberosTime requestedEndTime = new KerberosTime( now + 4 * KerberosTime.MINUTE );
+        modifier.setTill( requestedEndTime );
+
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        String passPhrase = "secret";
+        PaData[] paData = getPreAuthEncryptedTimeStamp( clientPrincipal, passPhrase );
+
+        KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
+
+        handler.messageReceived( session, message );
+
+        ErrorMessage error = ( ErrorMessage ) session.getMessage();
+        assertEquals( "Requested start time is later than end time", 11, error.getErrorCode() );
+    }
+
+
+    /**
+     * Tests when a valid starttime is specified but the POSTDATE flag is not set,
+     * that the request is rejected with the correct error message.
+     * 
+     * "If it indicates a time in the future beyond the acceptable clock skew, but
+     * the POSTDATED option has not been specified, then the error
+     * KDC_ERR_CANNOT_POSTDATE is returned."
+     * 
+     * @throws Exception 
+     */
+    public void testStartTimeNoPostdated() throws Exception
+    {
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setClientName( getPrincipalName( "hnelson" ) );
+        modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+
+        modifier.setKdcOptions( new KdcOptions() );
+
+        long now = System.currentTimeMillis();
+
+        KerberosTime requestedStartTime = new KerberosTime( now + 10 * KerberosTime.MINUTE );
+        modifier.setFrom( requestedStartTime );
+
+        KerberosTime requestedEndTime = new KerberosTime( now + KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        String passPhrase = "secret";
+        PaData[] paData = getPreAuthEncryptedTimeStamp( clientPrincipal, passPhrase );
+
+        KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
+
+        handler.messageReceived( session, message );
+
+        ErrorMessage error = ( ErrorMessage ) session.getMessage();
+        assertEquals( "Ticket not eligible for postdating", 10, error.getErrorCode() );
+    }
+
+
+    /**
+     * Tests that a user-specified start time is honored when that start time does not
+     * violate policy.
+     * 
+     * "Otherwise the requested starttime is checked against the policy of the local
+     * realm (the administrator might decide to prohibit certain types or ranges of
+     * postdated tickets), and if the ticket's starttime is acceptable, it is set as
+     * requested, and the INVALID flag is set in the new ticket.  The postdated
+     * ticket MUST be validated before use by presenting it to the KDC after the
+     * starttime has been reached."
+     * 
+     * "If the new ticket is postdated (the starttime is in the future), its
+     * INVALID flag will also be set."
+     * 
+     * "The flags field of the new ticket will have the following options set
+     * if they have been requested and if the policy of the local realm
+     * allows:  FORWARDABLE, MAY-POSTDATE, POSTDATED, PROXIABLE, RENEWABLE."
+     * 
+     * @throws Exception
+     */
+    public void testSpecificStartTime() throws Exception
+    {
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setClientName( getPrincipalName( "hnelson" ) );
+        modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        kdcOptions.set( KdcOptions.POSTDATED );
+        modifier.setKdcOptions( kdcOptions );
+
+        long now = System.currentTimeMillis();
+
+        KerberosTime requestedStartTime = new KerberosTime( now + KerberosTime.DAY );
+        modifier.setFrom( requestedStartTime );
+
+        KerberosTime requestedEndTime = new KerberosTime( now + 2 * KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        String passPhrase = "secret";
+        PaData[] paData = getPreAuthEncryptedTimeStamp( clientPrincipal, passPhrase );
+
+        KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
+
+        handler.messageReceived( session, message );
+
+        AuthenticationReply reply = ( AuthenticationReply ) session.getMessage();
+
+        assertTrue( "Requested start time", requestedStartTime.equals( reply.getStartTime() ) );
+        assertTrue( "Requested end time", requestedEndTime.equals( reply.getEndTime() ) );
+        assertTrue( "POSTDATED flag", reply.getFlags().isPostdated() );
+        assertTrue( "INVALID flag", reply.getFlags().isInvalid() );
+
+        assertTrue( "Requested start time", requestedStartTime.equals( reply.getTicket().getEncTicketPart().getStartTime() ) );
+        assertTrue( "Requested end time", requestedEndTime.equals( reply.getEndTime() ) );
+        assertTrue( "POSTDATED flag", reply.getTicket().getEncTicketPart().getFlags().isPostdated() );
+        assertTrue( "INVALID flag", reply.getTicket().getEncTicketPart().getFlags().isInvalid() );
+
+        assertTrue( "PRE_AUTHENT flag", reply.getTicket().getEncTicketPart().getFlags().isPreAuth() );
+    }
+
+
+    /**
+     * Tests that a user-specified end time is honored when that end time does not
+     * violate policy.
+     * 
+     * "The expiration time of the ticket will be set to the earlier of the
+     * requested endtime and a time determined by local policy, possibly by
+     * using realm- or principal-specific factors."
+     *
+     * @throws Exception
+     */
+    public void testSpecificEndTime() throws Exception
+    {
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setClientName( getPrincipalName( "hnelson" ) );
+        modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+
+        modifier.setKdcOptions( new KdcOptions() );
+
+        long now = System.currentTimeMillis();
+
+        KerberosTime requestedEndTime = new KerberosTime( now + KerberosTime.DAY / 2 );
+        modifier.setTill( requestedEndTime );
+
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+
+        String passPhrase = "secret";
+        PaData[] paData = getPreAuthEncryptedTimeStamp( clientPrincipal, passPhrase );
+
+        KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
+
+        handler.messageReceived( session, message );
+
+        AuthenticationReply reply = ( AuthenticationReply ) session.getMessage();
+
+        assertTrue( "Requested end time", requestedEndTime.equals( reply.getEndTime() ) );
+
+        assertTrue( "PRE_AUTHENT flag", reply.getTicket().getEncTicketPart().getFlags().isPreAuth() );
+    }
+
+
+    /**
+     * Tests when an end time is requested that exceeds the maximum end time as 
+     * configured in policy that the maximum allowable end time is returned instead
+     * of the requested end time.
+     * 
+     * "The expiration time of the ticket will be set to the earlier of the
+     * requested endtime and a time determined by local policy, possibly by
+     * using realm- or principal-specific factors."
+     *
+     * @throws Exception
+     */
+    public void testEndTimeExceedsMaximumAllowable() throws Exception
+    {
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setClientName( getPrincipalName( "hnelson" ) );
+        modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+
+        modifier.setKdcOptions( new KdcOptions() );
+
+        long now = System.currentTimeMillis();
+
+        KerberosTime requestedEndTime = new KerberosTime( now + KerberosTime.WEEK );
+        modifier.setTill( requestedEndTime );
+
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+
+        String passPhrase = "secret";
+        PaData[] paData = getPreAuthEncryptedTimeStamp( clientPrincipal, passPhrase );
+
+        KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
+
+        handler.messageReceived( session, message );
+
+        AuthenticationReply reply = ( AuthenticationReply ) session.getMessage();
+
+        KerberosTime expectedEndTime = new KerberosTime( now + KerberosTime.DAY );
+        boolean isClose = Math.abs( reply.getEndTime().getTime() - expectedEndTime.getTime() ) < 5000;
+        assertTrue( "Expected end time", isClose );
+    }
+
+
+    /**
+     * Tests that a requested zulu end time of the epoch ("19700101000000Z") results
+     * in the maximum endtime permitted according to KDC policy.  The zulu epoch is
+     * the same as '0' (zero) milliseconds in Java.
+     * 
+     * @throws Exception
+     */
+    public void testEpochEndTime() throws Exception
+    {
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setClientName( getPrincipalName( "hnelson" ) );
+        modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+
+        modifier.setKdcOptions( new KdcOptions() );
+
+        String epoch = "19700101000000Z";
+        KerberosTime requestedEndTime = KerberosTime.getTime( epoch );
+        modifier.setTill( requestedEndTime );
+
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+
+        String passPhrase = "secret";
+        PaData[] paData = getPreAuthEncryptedTimeStamp( clientPrincipal, passPhrase );
+
+        KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
+
+        handler.messageReceived( session, message );
+
+        AuthenticationReply reply = ( AuthenticationReply ) session.getMessage();
+
+        long now = System.currentTimeMillis();
+        KerberosTime expectedEndTime = new KerberosTime( now + KerberosTime.DAY );
+        boolean isClose = Math.abs( reply.getEndTime().getTime() - expectedEndTime.getTime() ) < 5000;
+        assertTrue( "Expected end time", isClose );
+    }
+
+
+    /**
+     * Tests that a service ticket can be requested without the use of a TGT.  The
+     * returned service ticket will have the INITIAL flag set.
+     * 
+     * @throws Exception
+     */
+    public void testInitialServiceTicket() throws Exception
+    {
+        String servicePrincipalName = "ldap/ldap.example.com@EXAMPLE.COM";
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setClientName( getPrincipalName( "hnelson" ) );
+        modifier.setServerName( getPrincipalName( servicePrincipalName ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+
+        modifier.setKdcOptions( new KdcOptions() );
+
+        long now = System.currentTimeMillis();
+        KerberosTime requestedEndTime = new KerberosTime( now + 1 * KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        String passPhrase = "secret";
+        PaData[] paData = getPreAuthEncryptedTimeStamp( clientPrincipal, passPhrase );
+
+        KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
+
+        handler.messageReceived( session, message );
+
+        AuthenticationReply reply = ( AuthenticationReply ) session.getMessage();
+
+        assertTrue( "INITIAL flag", reply.getFlags().isInitial() );
+        assertFalse( "INVALID flag", reply.getFlags().isInvalid() );
+
+        assertTrue( "INITIAL flag", reply.getTicket().getEncTicketPart().getFlags().isInitial() );
+        assertFalse( "INVALID flag", reply.getTicket().getEncTicketPart().getFlags().isInvalid() );
+
+        assertEquals( "Service principal name", reply.getServerPrincipal().getName(), servicePrincipalName );
+        assertEquals( "Service principal name", reply.getTicket().getServerPrincipal().getName(), servicePrincipalName );
+    }
+
+
+    /**
+     * Tests whether a renewable ticket will be accepted in lieu of a non-renewable
+     * ticket if the requested ticket expiration date cannot be satisfied by a
+     * non-renewable ticket (due to configuration constraints).
+     * 
+     * "If the requested expiration time for the ticket exceeds what was determined
+     * as above, and if the 'RENEWABLE-OK' option was requested, then the 'RENEWABLE'
+     * flag is set in the new ticket, and the renew-till value is set as if the
+     * 'RENEWABLE' option were requested (the field and option names are described
+     * fully in Section 5.4.1).
+     * 
+     * @throws Exception 
+     */
+    public void testRenewableOk() throws Exception
+    {
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setClientName( getPrincipalName( "hnelson" ) );
+        modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        kdcOptions.set( KdcOptions.RENEWABLE_OK );
+        modifier.setKdcOptions( kdcOptions );
+
+        long now = System.currentTimeMillis();
+
+        KerberosTime requestedEndTime = new KerberosTime( now + KerberosTime.WEEK );
+        modifier.setTill( requestedEndTime );
+
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+
+        String passPhrase = "secret";
+        PaData[] paData = getPreAuthEncryptedTimeStamp( clientPrincipal, passPhrase );
+
+        KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
+
+        handler.messageReceived( session, message );
+
+        AuthenticationReply reply = ( AuthenticationReply ) session.getMessage();
+
+        KerberosTime expectedEndTime = new KerberosTime( now + KerberosTime.DAY );
+        boolean isClose = Math.abs( reply.getEndTime().getTime() - expectedEndTime.getTime() ) < 5000;
+        assertTrue( "Expected end time", isClose );
+
+        assertTrue( "RENEWABLE flag", reply.getFlags().isRenewable() );
+        assertFalse( "INVALID flag", reply.getFlags().isInvalid() );
+
+        KerberosTime expectedRenewTillTime = new KerberosTime( now + KerberosTime.WEEK );
+        isClose = Math.abs( reply.getRenewTill().getTime() - expectedRenewTillTime.getTime() ) < 5000;
+        assertTrue( "Expected renew-till time", isClose );
+    }
+
+
+    /**
+     * Tests forwardable tickets.
+     * 
+     * "The flags field of the new ticket will have the following options set
+     * if they have been requested and if the policy of the local realm
+     * allows:  FORWARDABLE, MAY-POSTDATE, POSTDATED, PROXIABLE, RENEWABLE."
+     * 
+     * @throws Exception 
+     */
+    public void testForwardableTicket() throws Exception
+    {
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setClientName( getPrincipalName( "hnelson" ) );
+        modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        kdcOptions.set( KdcOptions.FORWARDABLE );
+        modifier.setKdcOptions( kdcOptions );
+
+        long now = System.currentTimeMillis();
+
+        KerberosTime requestedEndTime = new KerberosTime( now + 1 * KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        String passPhrase = "secret";
+        PaData[] paData = getPreAuthEncryptedTimeStamp( clientPrincipal, passPhrase );
+
+        KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
+
+        handler.messageReceived( session, message );
+
+        AuthenticationReply reply = ( AuthenticationReply ) session.getMessage();
+
+        assertTrue( "FORWARDABLE flag", reply.getFlags().isForwardable() );
+        assertFalse( "INVALID flag", reply.getFlags().isInvalid() );
+
+        assertTrue( "FORWARDABLE flag", reply.getTicket().getEncTicketPart().getFlags().isForwardable() );
+        assertFalse( "INVALID flag", reply.getTicket().getEncTicketPart().getFlags().isInvalid() );
+    }
+
+
+    /**
+     * Tests allow postdating of derivative tickets.
+     * 
+     * "The flags field of the new ticket will have the following options set
+     * if they have been requested and if the policy of the local realm
+     * allows:  FORWARDABLE, MAY-POSTDATE, POSTDATED, PROXIABLE, RENEWABLE."
+     * 
+     * @throws Exception 
+     */
+    public void testAllowPostdate() throws Exception
+    {
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setClientName( getPrincipalName( "hnelson" ) );
+        modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        kdcOptions.set( KdcOptions.ALLOW_POSTDATE );
+        modifier.setKdcOptions( kdcOptions );
+
+        long now = System.currentTimeMillis();
+
+        KerberosTime requestedEndTime = new KerberosTime( now + 1 * KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        String passPhrase = "secret";
+        PaData[] paData = getPreAuthEncryptedTimeStamp( clientPrincipal, passPhrase );
+
+        KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
+
+        handler.messageReceived( session, message );
+
+        AuthenticationReply reply = ( AuthenticationReply ) session.getMessage();
+
+        assertTrue( "MAY_POSTDATE flag", reply.getFlags().isMayPosdate() );
+        assertFalse( "INVALID flag", reply.getFlags().isInvalid() );
+
+        assertTrue( "MAY_POSTDATE flag", reply.getTicket().getEncTicketPart().getFlags().isMayPosdate() );
+        assertFalse( "INVALID flag", reply.getTicket().getEncTicketPart().getFlags().isInvalid() );
+    }
+
+
+    /**
+     * Tests proxiable tickets.
+     * 
+     * "The flags field of the new ticket will have the following options set
+     * if they have been requested and if the policy of the local realm
+     * allows:  FORWARDABLE, MAY-POSTDATE, POSTDATED, PROXIABLE, RENEWABLE."
+     * 
+     * @throws Exception 
+     */
+    public void testProxiableTicket() throws Exception
+    {
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setClientName( getPrincipalName( "hnelson" ) );
+        modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        kdcOptions.set( KdcOptions.PROXIABLE );
+        modifier.setKdcOptions( kdcOptions );
+
+        long now = System.currentTimeMillis();
+
+        KerberosTime requestedEndTime = new KerberosTime( now + 1 * KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        String passPhrase = "secret";
+        PaData[] paData = getPreAuthEncryptedTimeStamp( clientPrincipal, passPhrase );
+
+        KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
+
+        handler.messageReceived( session, message );
+
+        AuthenticationReply reply = ( AuthenticationReply ) session.getMessage();
+
+        assertTrue( "PROXIABLE flag", reply.getFlags().isProxiable() );
+        assertFalse( "INVALID flag", reply.getFlags().isInvalid() );
+
+        assertTrue( "PROXIABLE flag", reply.getTicket().getEncTicketPart().getFlags().isProxiable() );
+        assertFalse( "INVALID flag", reply.getTicket().getEncTicketPart().getFlags().isInvalid() );
+    }
+
+
+    /**
+     * Tests that a user-specified renew-till time is honored when that renew-till
+     * time does not violate policy.
+     * 
+     * "If the RENEWABLE option has been requested or if the RENEWABLE-OK
+     * option has been set and a renewable ticket is to be issued, then the
+     * renew-till field MAY be set to the earliest of ... its requested value [or]
+     * the starttime of the ticket plus the maximum renewable lifetime
+     * set by the policy of the local realm."
+     * 
+     * @throws Exception 
+     */
+    public void testRenewableTicket() throws Exception
+    {
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setClientName( getPrincipalName( "hnelson" ) );
+        modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        kdcOptions.set( KdcOptions.RENEWABLE );
+        modifier.setKdcOptions( kdcOptions );
+
+        long now = System.currentTimeMillis();
+
+        KerberosTime requestedEndTime = new KerberosTime( now + 1 * KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        KerberosTime requestedRenewTillTime = new KerberosTime( now + KerberosTime.WEEK / 2 );
+        modifier.setRtime( requestedRenewTillTime );
+
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        String passPhrase = "secret";
+        PaData[] paData = getPreAuthEncryptedTimeStamp( clientPrincipal, passPhrase );
+
+        KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
+
+        handler.messageReceived( session, message );
+
+        AuthenticationReply reply = ( AuthenticationReply ) session.getMessage();
+
+        assertTrue( "RENEWABLE flag", reply.getFlags().isRenewable() );
+        assertFalse( "INVALID flag", reply.getFlags().isInvalid() );
+
+        assertTrue( "RENEWABLE flag", reply.getTicket().getEncTicketPart().getFlags().isRenewable() );
+        assertFalse( "INVALID flag", reply.getTicket().getEncTicketPart().getFlags().isInvalid() );
+
+        assertTrue( "Requested renew-till time", requestedRenewTillTime.equals( reply.getRenewTill() ) );
+    }
+
+
+    /**
+     * Tests when a renew-till time is requested that exceeds the maximum renew-till
+     * time as configured in policy that the maximum allowable renew-till time is
+     * returned instead of the requested renew-till time.
+     * 
+     * "If the RENEWABLE option has been requested or if the RENEWABLE-OK
+     * option has been set and a renewable ticket is to be issued, then the
+     * renew-till field MAY be set to the earliest of ... its requested value [or]
+     * the starttime of the ticket plus the maximum renewable lifetime
+     * set by the policy of the local realm."
+     * 
+     * @throws Exception 
+     */
+    public void testRenewableTicketExceedsMaximumAllowable() throws Exception
+    {
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setClientName( getPrincipalName( "hnelson" ) );
+        modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        kdcOptions.set( KdcOptions.RENEWABLE );
+        modifier.setKdcOptions( kdcOptions );
+
+        long now = System.currentTimeMillis();
+
+        KerberosTime requestedEndTime = new KerberosTime( now + 1 * KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        KerberosTime requestedRenewTillTime = new KerberosTime( now + 2 * KerberosTime.WEEK );
+        modifier.setRtime( requestedRenewTillTime );
+
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        String passPhrase = "secret";
+        PaData[] paData = getPreAuthEncryptedTimeStamp( clientPrincipal, passPhrase );
+
+        KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
+
+        handler.messageReceived( session, message );
+
+        AuthenticationReply reply = ( AuthenticationReply ) session.getMessage();
+
+        assertTrue( "RENEWABLE flag", reply.getFlags().isRenewable() );
+        assertFalse( "INVALID flag", reply.getFlags().isInvalid() );
+
+        assertTrue( "RENEWABLE flag", reply.getTicket().getEncTicketPart().getFlags().isRenewable() );
+        assertFalse( "INVALID flag", reply.getTicket().getEncTicketPart().getFlags().isInvalid() );
+
+        KerberosTime expectedRenewTillTime = new KerberosTime( now + KerberosTime.WEEK );
+        boolean isClose = Math.abs( reply.getRenewTill().getTime() - expectedRenewTillTime.getTime() ) < 5000;
+        assertTrue( "Expected renew-till time", isClose );
+    }
+
+
+    /**
+     * Tests that the option RENEW, which is bad for an AS_REQ, is rejected
+     * with the correct error message.
+     *
+     * @throws Exception
+     */
+    public void testBadOptionRenew() throws Exception
+    {
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setClientName( getPrincipalName( "hnelson" ) );
+        modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        kdcOptions.set( KdcOptions.RENEW );
+        modifier.setKdcOptions( kdcOptions );
+
+        long now = System.currentTimeMillis();
+
+        KerberosTime requestedEndTime = new KerberosTime( now + 1 * KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        String passPhrase = "secret";
+        PaData[] paData = getPreAuthEncryptedTimeStamp( clientPrincipal, passPhrase );
+
+        KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
+
+        handler.messageReceived( session, message );
+
+        ErrorMessage error = ( ErrorMessage ) session.getMessage();
+        assertEquals( "KDC cannot accommodate requested option", 13, error.getErrorCode() );
+    }
+
+
+    /**
+     * Tests that the option VALIDATE, which is bad for an AS_REQ, is rejected
+     * with the correct error message.
+     *
+     * @throws Exception
+     */
+    public void testBadOptionValidate() throws Exception
+    {
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setClientName( getPrincipalName( "hnelson" ) );
+        modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        kdcOptions.set( KdcOptions.VALIDATE );
+        modifier.setKdcOptions( kdcOptions );
+
+        long now = System.currentTimeMillis();
+
+        KerberosTime requestedEndTime = new KerberosTime( now + 1 * KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        String passPhrase = "secret";
+        PaData[] paData = getPreAuthEncryptedTimeStamp( clientPrincipal, passPhrase );
+
+        KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
+
+        handler.messageReceived( session, message );
+
+        ErrorMessage error = ( ErrorMessage ) session.getMessage();
+        assertEquals( "KDC cannot accommodate requested option", 13, error.getErrorCode() );
+    }
+
+
+    /**
+     * Tests that the option PROXY, which is bad for an AS_REQ, is rejected
+     * with the correct error message.
+     *
+     * @throws Exception
+     */
+    public void testBadOptionProxy() throws Exception
+    {
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setClientName( getPrincipalName( "hnelson" ) );
+        modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        kdcOptions.set( KdcOptions.PROXY );
+        modifier.setKdcOptions( kdcOptions );
+
+        long now = System.currentTimeMillis();
+
+        KerberosTime requestedEndTime = new KerberosTime( now + 1 * KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        String passPhrase = "secret";
+        PaData[] paData = getPreAuthEncryptedTimeStamp( clientPrincipal, passPhrase );
+
+        KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
+
+        handler.messageReceived( session, message );
+
+        ErrorMessage error = ( ErrorMessage ) session.getMessage();
+        assertEquals( "KDC cannot accommodate requested option", 13, error.getErrorCode() );
+    }
+
+
+    /**
+     * Tests that the option FORWARDED, which is bad for an AS_REQ, is rejected
+     * with the correct error message.
+     *
+     * @throws Exception
+     */
+    public void testBadOptionForwarded() throws Exception
+    {
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setClientName( getPrincipalName( "hnelson" ) );
+        modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        kdcOptions.set( KdcOptions.FORWARDED );
+        modifier.setKdcOptions( kdcOptions );
+
+        long now = System.currentTimeMillis();
+
+        KerberosTime requestedEndTime = new KerberosTime( now + 1 * KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        String passPhrase = "secret";
+        PaData[] paData = getPreAuthEncryptedTimeStamp( clientPrincipal, passPhrase );
+
+        KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
+
+        handler.messageReceived( session, message );
+
+        ErrorMessage error = ( ErrorMessage ) session.getMessage();
+        assertEquals( "KDC cannot accommodate requested option", 13, error.getErrorCode() );
+    }
+
+
+    /**
+     * Tests that the option ENC_TKT_IN_SKEY, which is bad for an AS_REQ, is rejected
+     * with the correct error message.
+     *
+     * @throws Exception
+     */
+    public void testBadOptionEncTktInSkey() throws Exception
+    {
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setClientName( getPrincipalName( "hnelson" ) );
+        modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        kdcOptions.set( KdcOptions.ENC_TKT_IN_SKEY );
+        modifier.setKdcOptions( kdcOptions );
+
+        long now = System.currentTimeMillis();
+
+        KerberosTime requestedEndTime = new KerberosTime( now + 1 * KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        String passPhrase = "secret";
+        PaData[] paData = getPreAuthEncryptedTimeStamp( clientPrincipal, passPhrase );
+
+        KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
+
+        handler.messageReceived( session, message );
+
+        ErrorMessage error = ( ErrorMessage ) session.getMessage();
+        assertEquals( "KDC cannot accommodate requested option", 13, error.getErrorCode() );
+    }
+}
diff --git a/old_trunk/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/protocol/EncTktInSkeyTest.java b/old_trunk/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/protocol/EncTktInSkeyTest.java
new file mode 100644
index 0000000..e4ac89e
--- /dev/null
+++ b/old_trunk/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/protocol/EncTktInSkeyTest.java
@@ -0,0 +1,133 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.protocol;
+
+
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.apache.directory.server.kerberos.kdc.KdcServer;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.CipherTextHandler;
+import org.apache.directory.server.kerberos.shared.messages.ErrorMessage;
+import org.apache.directory.server.kerberos.shared.messages.KdcRequest;
+import org.apache.directory.server.kerberos.shared.messages.components.EncTicketPartModifier;
+import org.apache.directory.server.kerberos.shared.messages.components.Ticket;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+import org.apache.directory.server.kerberos.shared.messages.value.KdcOptions;
+import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
+import org.apache.directory.server.kerberos.shared.messages.value.RequestBody;
+import org.apache.directory.server.kerberos.shared.messages.value.RequestBodyModifier;
+import org.apache.directory.server.kerberos.shared.store.PrincipalStore;
+
+
+/**
+ * Test case for RFC 4120 Section 3.7. "User-to-User Authentication Exchanges."  This
+ * is option "ENC-TKT-IN-SKEY."
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class EncTktInSkeyTest extends AbstractTicketGrantingServiceTest
+{
+    private KdcServer config;
+    private PrincipalStore store;
+    private KerberosProtocolHandler handler;
+    private DummySession session;
+
+
+    /**
+     * Creates a new instance of {@link EncTktInSkeyTest}.
+     */
+    public EncTktInSkeyTest()
+    {
+        config = new KdcServer();
+
+        /*
+         * Body checksum verification must be disabled because we are bypassing
+         * the codecs, where the body bytes are set on the KdcRequest message.
+         */
+        config.setBodyChecksumVerified( false );
+
+        store = new MapPrincipalStoreImpl();
+        handler = new KerberosProtocolHandler( config, store );
+        session = new DummySession();
+        lockBox = new CipherTextHandler();
+    }
+
+
+    /**
+     * If the ENC-TKT-IN-SKEY option has been specified and an additional ticket
+     * has been included in the request, it indicates that the client is using
+     * user-to-user authentication to prove its identity to a server that does
+     * not have access to a persistent key.  Section 3.7 describes the effect
+     * of this option on the entire Kerberos protocol.  When generating the
+     * KRB_TGS_REP message, this option in the KRB_TGS_REQ message tells the KDC
+     * to decrypt the additional ticket using the key for the server to which the
+     * additional ticket was issued and to verify that it is a TGT.  If the name
+     * of the requested server is missing from the request, the name of the client
+     * in the additional ticket will be used.  Otherwise, the name of the requested
+     * server will be compared to the name of the client in the additional ticket.
+     * If it is different, the request will be rejected.  If the request succeeds,
+     * the session key from the additional ticket will be used to encrypt the new
+     * ticket that is issued instead of using the key of the server for which the
+     * new ticket will be used.
+     * 
+     * @throws Exception 
+     */
+    public void testEncTktInSkey() throws Exception
+    {
+        // Get the mutable ticket part.
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        EncTicketPartModifier encTicketPartModifier = getTicketArchetype( clientPrincipal );
+
+        // Make changes to test.
+
+        // Seal the ticket for the server.
+        KerberosPrincipal serverPrincipal = new KerberosPrincipal( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" );
+        String passPhrase = "randomKey";
+        EncryptionKey serverKey = getEncryptionKey( serverPrincipal, passPhrase );
+        Ticket tgt = getTicket( encTicketPartModifier, serverPrincipal, serverKey );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setServerName( getPrincipalName( "ldap/ldap.example.com@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+        modifier.setNonce( random.nextInt() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        kdcOptions.set( KdcOptions.ENC_TKT_IN_SKEY );
+        modifier.setKdcOptions( kdcOptions );
+
+        long now = System.currentTimeMillis();
+
+        KerberosTime requestedEndTime = new KerberosTime( now + 1 * KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        KerberosTime requestedRenewTillTime = new KerberosTime( now + KerberosTime.WEEK / 2 );
+        modifier.setRtime( requestedRenewTillTime );
+
+        RequestBody requestBody = modifier.getRequestBody();
+        KdcRequest message = getKdcRequest( tgt, requestBody );
+
+        handler.messageReceived( session, message );
+
+        ErrorMessage error = ( ErrorMessage ) session.getMessage();
+        assertEquals( "KDC cannot accommodate requested option", 13, error.getErrorCode() );
+    }
+}
diff --git a/old_trunk/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/protocol/MapPrincipalStoreImpl.java b/old_trunk/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/protocol/MapPrincipalStoreImpl.java
new file mode 100644
index 0000000..a019da5
--- /dev/null
+++ b/old_trunk/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/protocol/MapPrincipalStoreImpl.java
@@ -0,0 +1,131 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.protocol;
+
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.KerberosKeyFactory;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+import org.apache.directory.server.kerberos.shared.store.PrincipalStore;
+import org.apache.directory.server.kerberos.shared.store.PrincipalStoreEntry;
+import org.apache.directory.server.kerberos.shared.store.PrincipalStoreEntryModifier;
+
+
+/**
+ * An implementation of {@link PrincipalStore} that is backed by a {@link Map}.  This
+ * store implements only getPrincipal, as required by the Kerberos service.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class MapPrincipalStoreImpl implements PrincipalStore
+{
+    private static Map<KerberosPrincipal, PrincipalStoreEntry> store = new HashMap<KerberosPrincipal, PrincipalStoreEntry>();
+
+    static
+    {
+        String principalName = "hnelson@EXAMPLE.COM";
+        String passPhrase = "secret";
+
+        PrincipalStoreEntry entry = getEntry( principalName, passPhrase );
+        store.put( entry.getPrincipal(), entry );
+
+        principalName = "tquist@EXAMPLE.COM";
+        passPhrase = "secret";
+
+        entry = getNullKeyEntry( principalName );
+        store.put( entry.getPrincipal(), entry );
+
+        principalName = "krbtgt/EXAMPLE.COM@EXAMPLE.COM";
+        passPhrase = "randomKey";
+
+        entry = getEntry( principalName, passPhrase );
+        store.put( entry.getPrincipal(), entry );
+
+        principalName = "ldap/ldap.example.com@EXAMPLE.COM";
+        passPhrase = "randomKey";
+
+        entry = getEntry( principalName, passPhrase );
+        store.put( entry.getPrincipal(), entry );
+    }
+
+
+    public PrincipalStoreEntry getPrincipal( KerberosPrincipal principal ) throws Exception
+    {
+        PrincipalStoreEntry entry = store.get( principal );
+
+        return entry;
+    }
+
+
+    public String addPrincipal( PrincipalStoreEntry entry ) throws Exception
+    {
+        return null;
+    }
+
+
+    public String changePassword( KerberosPrincipal principal, String newPassword ) throws Exception
+    {
+        return null;
+    }
+
+
+    public String deletePrincipal( KerberosPrincipal principal ) throws Exception
+    {
+        return null;
+    }
+
+
+    public PrincipalStoreEntry[] getAllPrincipals( String realm ) throws Exception
+    {
+        return null;
+    }
+
+
+    private static PrincipalStoreEntry getEntry( String principalName, String passPhrase )
+    {
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( principalName );
+
+        PrincipalStoreEntryModifier modifier = new PrincipalStoreEntryModifier();
+        modifier.setPrincipal( clientPrincipal );
+
+        Map<EncryptionType, EncryptionKey> keyMap = KerberosKeyFactory.getKerberosKeys( principalName, passPhrase );
+
+        modifier.setKeyMap( keyMap );
+
+        return modifier.getEntry();
+    }
+
+
+    private static PrincipalStoreEntry getNullKeyEntry( String principalName )
+    {
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( principalName );
+
+        PrincipalStoreEntryModifier modifier = new PrincipalStoreEntryModifier();
+        modifier.setPrincipal( clientPrincipal );
+
+        return modifier.getEntry();
+    }
+}
diff --git a/old_trunk/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/protocol/PreAuthenticationTest.java b/old_trunk/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/protocol/PreAuthenticationTest.java
new file mode 100644
index 0000000..a244680
--- /dev/null
+++ b/old_trunk/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/protocol/PreAuthenticationTest.java
@@ -0,0 +1,264 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.protocol;
+
+
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.apache.directory.server.kerberos.kdc.KdcServer;
+import org.apache.directory.server.kerberos.shared.KerberosMessageType;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.CipherTextHandler;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.KeyUsage;
+import org.apache.directory.server.kerberos.shared.io.encoder.EncryptedDataEncoder;
+import org.apache.directory.server.kerberos.shared.messages.ErrorMessage;
+import org.apache.directory.server.kerberos.shared.messages.KdcRequest;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptedData;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptedTimeStamp;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+import org.apache.directory.server.kerberos.shared.messages.value.KdcOptions;
+import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
+import org.apache.directory.server.kerberos.shared.messages.value.PaData;
+import org.apache.directory.server.kerberos.shared.messages.value.RequestBodyModifier;
+import org.apache.directory.server.kerberos.shared.messages.value.types.PaDataType;
+import org.apache.directory.server.kerberos.shared.store.PrincipalStore;
+
+
+/**
+ * Tests pre-authentication processing in the Authentication Service (AS) via the
+ * {@link KerberosProtocolHandler}.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class PreAuthenticationTest extends AbstractAuthenticationServiceTest
+{
+    private KdcServer config;
+    private PrincipalStore store;
+    private KerberosProtocolHandler handler;
+    private DummySession session;
+
+
+    /**
+     * Creates a new instance of {@link PreAuthenticationTest}.
+     */
+    public PreAuthenticationTest()
+    {
+        config = new KdcServer();
+        store = new MapPrincipalStoreImpl();
+        handler = new KerberosProtocolHandler( config, store );
+        session = new DummySession();
+        lockBox = new CipherTextHandler();
+    }
+
+
+    /**
+     * Tests when the KDC configuration requires pre-authentication by encrypted
+     * timestamp that an AS_REQ without pre-authentication is rejected with the
+     * correct error message.
+     * 
+     * "If pre-authentication is required, but was not present in the request, an
+     * error message with the code KDC_ERR_PREAUTH_REQUIRED is returned, and a
+     * METHOD-DATA object will be stored in the e-data field of the KRB-ERROR
+     * message to specify which pre-authentication mechanisms are acceptable."
+     */
+    public void testPreAuthenticationRequired()
+    {
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setClientName( getPrincipalName( "hnelson" ) );
+        modifier.setServerName( getPrincipalName( "hnelson" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+
+        KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REQ, null, modifier.getRequestBody() );
+
+        handler.messageReceived( session, message );
+
+        ErrorMessage error = ( ErrorMessage ) session.getMessage();
+        assertEquals( "Additional pre-authentication required", 25, error.getErrorCode() );
+    }
+
+
+    /**
+     * Tests when the KDC configuration requires pre-authentication by encrypted
+     * timestamp that an AS_REQ with pre-authentication using an incorrect key is
+     * rejected with the correct error message.
+     * 
+     * "If required to do so, the server pre-authenticates the request, and
+     * if the pre-authentication check fails, an error message with the code
+     * KDC_ERR_PREAUTH_FAILED is returned."
+     * 
+     * @throws Exception 
+     */
+    public void testPreAuthenticationIntegrityFailed() throws Exception
+    {
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setClientName( getPrincipalName( "hnelson" ) );
+        modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+
+        modifier.setKdcOptions( new KdcOptions() );
+
+        long now = System.currentTimeMillis();
+
+        KerberosTime requestedEndTime = new KerberosTime( now + KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+
+        String passPhrase = "badpassword";
+        PaData[] paData = getPreAuthEncryptedTimeStamp( clientPrincipal, passPhrase );
+
+        KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
+
+        handler.messageReceived( session, message );
+
+        ErrorMessage error = ( ErrorMessage ) session.getMessage();
+        assertEquals( "Integrity check on decrypted field failed", 31, error.getErrorCode() );
+    }
+
+
+    /**
+     * "If required to do so, the server pre-authenticates the request, and
+     * if the pre-authentication check fails, an error message with the code
+     * KDC_ERR_PREAUTH_FAILED is returned."
+     * 
+     * @throws Exception 
+     */
+    public void testPreAuthenticationFailed() throws Exception
+    {
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setClientName( getPrincipalName( "hnelson" ) );
+        modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+
+        modifier.setKdcOptions( new KdcOptions() );
+
+        long now = System.currentTimeMillis();
+
+        KerberosTime requestedEndTime = new KerberosTime( now + KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+
+        KerberosTime timeStamp = new KerberosTime( 0 );
+        String passPhrase = "secret";
+        PaData[] paData = getPreAuthEncryptedTimeStamp( clientPrincipal, passPhrase, timeStamp );
+
+        KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
+
+        handler.messageReceived( session, message );
+
+        ErrorMessage error = ( ErrorMessage ) session.getMessage();
+
+        assertEquals( "Pre-authentication information was invalid", 24, error.getErrorCode() );
+    }
+
+
+    /**
+     * Tests when pre-authentication is included that is not supported by the KDC, that
+     * the correct error message is returned.
+     * 
+     * @throws Exception 
+     */
+    public void testPreAuthenticationNoSupport() throws Exception
+    {
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setClientName( getPrincipalName( "hnelson" ) );
+        modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+
+        modifier.setKdcOptions( new KdcOptions() );
+
+        long now = System.currentTimeMillis();
+
+        KerberosTime requestedEndTime = new KerberosTime( now + KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        String passPhrase = "secret";
+        PaData[] paData = getPreAuthPublicKey( clientPrincipal, passPhrase );
+
+        KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
+
+        handler.messageReceived( session, message );
+
+        ErrorMessage error = ( ErrorMessage ) session.getMessage();
+
+        assertEquals( "KDC has no support for padata type", 16, error.getErrorCode() );
+    }
+
+
+    /**
+     * Returns pre-authentication payload of type PA_PK_AS_REQ.  Note that the actual
+     * payload is an encrypted timestamp, but with only the type set to PA_PK_AS_REQ.
+     * This is being used to test the error condition when an unsupported pre-authentication
+     * type is received by the KDC.  The time for the timestamp is set to the current time.
+     *
+     * @param clientPrincipal
+     * @param passPhrase
+     * @return The array of pre-authentication data.
+     * @throws Exception
+     */
+    private PaData[] getPreAuthPublicKey( KerberosPrincipal clientPrincipal, String passPhrase )
+        throws Exception
+    {
+        KerberosTime timeStamp = new KerberosTime();
+
+        return getPreAuthPublicKey( clientPrincipal, passPhrase, timeStamp );
+    }
+
+
+    /**
+     * Returns pre-authentication payload of type PA_PK_AS_REQ.  Note that the actual
+     * payload is an encrypted timestamp, but with the type set to PA_PK_AS_REQ.  This
+     * is being used to test the error condition caused when an unsupported
+     * pre-authentication type is received by the KDC.
+     *
+     * @param clientPrincipal
+     * @param passPhrase
+     * @param timeStamp
+     * @return The array of pre-authentication data.
+     * @throws Exception
+     */
+    private PaData[] getPreAuthPublicKey( KerberosPrincipal clientPrincipal, String passPhrase,
+        KerberosTime timeStamp ) throws Exception
+    {
+        PaData[] paData = new PaData[1];
+
+        EncryptedTimeStamp encryptedTimeStamp = new EncryptedTimeStamp( timeStamp, 0 );
+
+        EncryptionKey clientKey = getEncryptionKey( clientPrincipal, passPhrase );
+
+        EncryptedData encryptedData = lockBox.seal( clientKey, encryptedTimeStamp, KeyUsage.NUMBER1 );
+
+        byte[] encodedEncryptedData = EncryptedDataEncoder.encode( encryptedData );
+
+        PaData preAuth = new PaData();
+        preAuth.setPaDataType( PaDataType.PA_PK_AS_REQ );
+        preAuth.setPaDataValue( encodedEncryptedData );
+
+        paData[0] = preAuth;
+
+        return paData;
+    }
+}
diff --git a/old_trunk/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/protocol/TicketGrantingEncryptionTypeTest.java b/old_trunk/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/protocol/TicketGrantingEncryptionTypeTest.java
new file mode 100644
index 0000000..1803967
--- /dev/null
+++ b/old_trunk/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/protocol/TicketGrantingEncryptionTypeTest.java
@@ -0,0 +1,436 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.protocol;
+
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.apache.directory.server.kerberos.kdc.KdcServer;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.CipherTextHandler;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.KerberosKeyFactory;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.RandomKeyFactory;
+import org.apache.directory.server.kerberos.shared.messages.KdcRequest;
+import org.apache.directory.server.kerberos.shared.messages.TicketGrantReply;
+import org.apache.directory.server.kerberos.shared.messages.components.EncTicketPartModifier;
+import org.apache.directory.server.kerberos.shared.messages.components.Ticket;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+import org.apache.directory.server.kerberos.shared.messages.value.KdcOptions;
+import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
+import org.apache.directory.server.kerberos.shared.messages.value.RequestBody;
+import org.apache.directory.server.kerberos.shared.messages.value.RequestBodyModifier;
+import org.apache.directory.server.kerberos.shared.store.PrincipalStore;
+
+
+/**
+ * Tests various facets of working with encryption types in the Ticket-Granting Service (TGS).
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class TicketGrantingEncryptionTypeTest extends AbstractTicketGrantingServiceTest
+{
+    private KdcServer config;
+    private PrincipalStore store;
+    private KerberosProtocolHandler handler;
+    private DummySession session;
+
+
+    /**
+     * Creates a new instance of {@link TicketGrantingEncryptionTypeTest}.
+     */
+    public TicketGrantingEncryptionTypeTest()
+    {
+        config = new KdcServer();
+
+        /*
+         * Body checksum verification must be disabled because we are bypassing
+         * the codecs, where the body bytes are set on the KdcRequest message.
+         */
+        config.setBodyChecksumVerified( false );
+
+        store = new MapPrincipalStoreImpl();
+        handler = new KerberosProtocolHandler( config, store );
+        session = new DummySession();
+        lockBox = new CipherTextHandler();
+    }
+
+
+    /**
+     * Tests a basic request using DES-CBC-MD5.
+     *
+     * @throws Exception
+     */
+    public void testRequestDesCbcMd5() throws Exception
+    {
+        // Get the mutable ticket part.
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        EncTicketPartModifier encTicketPartModifier = getTicketArchetype( clientPrincipal );
+
+        // Seal the ticket for the server.
+        KerberosPrincipal serverPrincipal = new KerberosPrincipal( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" );
+        String passPhrase = "randomKey";
+        EncryptionKey serverKey = getEncryptionKey( serverPrincipal, passPhrase );
+        Ticket tgt = getTicket( encTicketPartModifier, serverPrincipal, serverKey );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setServerName( getPrincipalName( "ldap/ldap.example.com@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+
+        Set<EncryptionType> encryptionTypes = new HashSet<EncryptionType>();
+        encryptionTypes.add( EncryptionType.DES_CBC_MD5 );
+
+        modifier.setEType( encryptionTypes );
+
+        modifier.setNonce( random.nextInt() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        modifier.setKdcOptions( kdcOptions );
+
+        long now = System.currentTimeMillis();
+        KerberosTime requestedEndTime = new KerberosTime( now + 1 * KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        RequestBody requestBody = modifier.getRequestBody();
+        KdcRequest message = getKdcRequest( tgt, requestBody );
+
+        handler.messageReceived( session, message );
+
+        TicketGrantReply reply = ( TicketGrantReply ) session.getMessage();
+
+        assertEquals( "Encryption type", EncryptionType.DES_CBC_MD5, reply.getEncPart().getEType() );
+    }
+
+
+    /**
+     * Tests the use of a TGT containing a DES-CBC-MD5 session key while the
+     * requested encryption type is AES-128.
+     *
+     * @throws Exception
+     */
+    public void testRequestAes128() throws Exception
+    {
+        EncryptionType[] configuredEncryptionTypes =
+                {EncryptionType.AES128_CTS_HMAC_SHA1_96};
+        config.setEncryptionTypes( configuredEncryptionTypes );
+
+        // Get the mutable ticket part.
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        EncTicketPartModifier encTicketPartModifier = getTicketArchetype( clientPrincipal );
+
+        // Seal the ticket for the server.
+        KerberosPrincipal serverPrincipal = new KerberosPrincipal( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" );
+        String passPhrase = "randomKey";
+        EncryptionKey serverKey = getEncryptionKey( serverPrincipal, passPhrase );
+        Ticket tgt = getTicket( encTicketPartModifier, serverPrincipal, serverKey );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setServerName( getPrincipalName( "ldap/ldap.example.com@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+
+        Set<EncryptionType> encryptionTypes = new HashSet<EncryptionType>();
+        encryptionTypes.add( EncryptionType.AES128_CTS_HMAC_SHA1_96 );
+
+        modifier.setEType( encryptionTypes );
+
+        modifier.setNonce( random.nextInt() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        modifier.setKdcOptions( kdcOptions );
+
+        long now = System.currentTimeMillis();
+        KerberosTime requestedEndTime = new KerberosTime( now + 1 * KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        RequestBody requestBody = modifier.getRequestBody();
+        KdcRequest message = getKdcRequest( tgt, requestBody );
+
+        handler.messageReceived( session, message );
+
+        TicketGrantReply reply = ( TicketGrantReply ) session.getMessage();
+
+        assertEquals( "Encryption type", EncryptionType.DES_CBC_MD5, reply.getEncPart().getEType() );
+        assertEquals( "Encryption type", EncryptionType.AES128_CTS_HMAC_SHA1_96, reply.getTicket().getEncPart()
+                .getEType() );
+    }
+
+
+    /**
+     * Tests the use of a TGT containing an AES-128 session key while the
+     * requested encryption type is also AES-128.
+     *
+     * @throws Exception
+     */
+    public void testRequestAes128TgtAndRequested() throws Exception
+    {
+        EncryptionType[] configuredEncryptionTypes =
+                {EncryptionType.AES128_CTS_HMAC_SHA1_96};
+        config.setEncryptionTypes( configuredEncryptionTypes );
+
+        // Get the mutable ticket part.
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        EncTicketPartModifier encTicketPartModifier = getTicketArchetype( clientPrincipal );
+
+        // Make changes to test.
+        sessionKey = RandomKeyFactory.getRandomKey( EncryptionType.AES128_CTS_HMAC_SHA1_96 );
+        encTicketPartModifier.setSessionKey( sessionKey );
+
+        // Seal the ticket for the server.
+        String principalName = "krbtgt/EXAMPLE.COM@EXAMPLE.COM";
+        KerberosPrincipal serverPrincipal = new KerberosPrincipal( principalName );
+        String passPhrase = "randomKey";
+        Set<EncryptionType> preAuthEncryptionTypes = new HashSet<EncryptionType>();
+        preAuthEncryptionTypes.add( EncryptionType.AES128_CTS_HMAC_SHA1_96 );
+
+        Map<EncryptionType, EncryptionKey> keyMap = KerberosKeyFactory.getKerberosKeys( principalName, passPhrase,
+                preAuthEncryptionTypes );
+        EncryptionKey serverKey = keyMap.get( EncryptionType.AES128_CTS_HMAC_SHA1_96 );
+
+        Ticket tgt = getTicket( encTicketPartModifier, serverPrincipal, serverKey );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setServerName( getPrincipalName( "ldap/ldap.example.com@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+
+        Set<EncryptionType> encryptionTypes = new HashSet<EncryptionType>();
+        encryptionTypes.add( EncryptionType.AES128_CTS_HMAC_SHA1_96 );
+
+        modifier.setEType( encryptionTypes );
+
+        modifier.setNonce( random.nextInt() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        modifier.setKdcOptions( kdcOptions );
+
+        long now = System.currentTimeMillis();
+        KerberosTime requestedEndTime = new KerberosTime( now + 1 * KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        RequestBody requestBody = modifier.getRequestBody();
+        KdcRequest message = getKdcRequest( tgt, requestBody );
+
+        handler.messageReceived( session, message );
+
+        TicketGrantReply reply = ( TicketGrantReply ) session.getMessage();
+
+        assertEquals( "Encryption type", EncryptionType.AES128_CTS_HMAC_SHA1_96, reply.getEncPart().getEType() );
+        assertEquals( "Encryption type", EncryptionType.AES128_CTS_HMAC_SHA1_96, reply.getTicket().getEncPart()
+                .getEType() );
+    }
+
+
+    /**
+     * Tests that the client-chosen nonce is correctly returned in the response.
+     *
+     * @throws Exception
+     */
+    public void testNonce() throws Exception
+    {
+        EncryptionType[] configuredEncryptionTypes =
+                {EncryptionType.AES128_CTS_HMAC_SHA1_96};
+        config.setEncryptionTypes( configuredEncryptionTypes );
+
+        // Get the mutable ticket part.
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        EncTicketPartModifier encTicketPartModifier = getTicketArchetype( clientPrincipal );
+
+        // Make changes to test.
+        sessionKey = RandomKeyFactory.getRandomKey( EncryptionType.AES128_CTS_HMAC_SHA1_96 );
+        encTicketPartModifier.setSessionKey( sessionKey );
+
+        // Seal the ticket for the server.
+        String principalName = "krbtgt/EXAMPLE.COM@EXAMPLE.COM";
+        KerberosPrincipal serverPrincipal = new KerberosPrincipal( principalName );
+        String passPhrase = "randomKey";
+        Set<EncryptionType> preAuthEncryptionTypes = new HashSet<EncryptionType>();
+        preAuthEncryptionTypes.add( EncryptionType.AES128_CTS_HMAC_SHA1_96 );
+
+        Map<EncryptionType, EncryptionKey> keyMap = KerberosKeyFactory.getKerberosKeys( principalName, passPhrase,
+                preAuthEncryptionTypes );
+        EncryptionKey serverKey = keyMap.get( EncryptionType.AES128_CTS_HMAC_SHA1_96 );
+
+        Ticket tgt = getTicket( encTicketPartModifier, serverPrincipal, serverKey );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setServerName( getPrincipalName( "ldap/ldap.example.com@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+
+        Set<EncryptionType> encryptionTypes = new HashSet<EncryptionType>();
+        encryptionTypes.add( EncryptionType.AES128_CTS_HMAC_SHA1_96 );
+
+        modifier.setEType( encryptionTypes );
+
+        int nonce = random.nextInt();
+        modifier.setNonce( nonce );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        modifier.setKdcOptions( kdcOptions );
+
+        long now = System.currentTimeMillis();
+        KerberosTime requestedEndTime = new KerberosTime( now + 1 * KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        RequestBody requestBody = modifier.getRequestBody();
+        KdcRequest message = getKdcRequest( tgt, requestBody );
+
+        handler.messageReceived( session, message );
+
+        TicketGrantReply reply = ( TicketGrantReply ) session.getMessage();
+
+        assertEquals( "Encryption type", EncryptionType.AES128_CTS_HMAC_SHA1_96, reply.getEncPart().getEType() );
+        assertEquals( "Encryption type", EncryptionType.AES128_CTS_HMAC_SHA1_96, reply.getTicket().getEncPart()
+                .getEType() );
+
+        assertEquals( "Nonce", nonce, reply.getNonce() );
+    }
+
+
+    /**
+     * Tests that the default reply key is the session key from the TGT.
+     *
+     * @throws Exception
+     */
+    public void testDecryptWithSessionKey() throws Exception
+    {
+        EncryptionType[] configuredEncryptionTypes =
+                {EncryptionType.AES128_CTS_HMAC_SHA1_96};
+        config.setEncryptionTypes( configuredEncryptionTypes );
+
+        // Get the mutable ticket part.
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        EncTicketPartModifier encTicketPartModifier = getTicketArchetype( clientPrincipal );
+
+        // Make changes to test.
+        sessionKey = RandomKeyFactory.getRandomKey( EncryptionType.AES128_CTS_HMAC_SHA1_96 );
+        encTicketPartModifier.setSessionKey( sessionKey );
+
+        // Seal the ticket for the server.
+        String principalName = "krbtgt/EXAMPLE.COM@EXAMPLE.COM";
+        KerberosPrincipal serverPrincipal = new KerberosPrincipal( principalName );
+        String passPhrase = "randomKey";
+        Set<EncryptionType> preAuthEncryptionTypes = new HashSet<EncryptionType>();
+        preAuthEncryptionTypes.add( EncryptionType.AES128_CTS_HMAC_SHA1_96 );
+
+        Map<EncryptionType, EncryptionKey> keyMap = KerberosKeyFactory.getKerberosKeys( principalName, passPhrase,
+                preAuthEncryptionTypes );
+        EncryptionKey serverKey = keyMap.get( EncryptionType.AES128_CTS_HMAC_SHA1_96 );
+
+        Ticket tgt = getTicket( encTicketPartModifier, serverPrincipal, serverKey );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setServerName( getPrincipalName( "ldap/ldap.example.com@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+
+        Set<EncryptionType> encryptionTypes = new HashSet<EncryptionType>();
+        encryptionTypes.add( EncryptionType.AES128_CTS_HMAC_SHA1_96 );
+
+        modifier.setEType( encryptionTypes );
+
+        modifier.setNonce( random.nextInt() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        modifier.setKdcOptions( kdcOptions );
+
+        long now = System.currentTimeMillis();
+        KerberosTime requestedEndTime = new KerberosTime( now + 1 * KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        RequestBody requestBody = modifier.getRequestBody();
+        KdcRequest message = getKdcRequest( tgt, requestBody );
+
+        handler.messageReceived( session, message );
+
+        TicketGrantReply reply = ( TicketGrantReply ) session.getMessage();
+
+        assertEquals( "Encryption type", EncryptionType.AES128_CTS_HMAC_SHA1_96, reply.getEncPart().getEType() );
+        assertEquals( "Encryption type", EncryptionType.AES128_CTS_HMAC_SHA1_96, reply.getTicket().getEncPart()
+                .getEType() );
+    }
+
+
+    /**
+     * Tests when a sub-session key is placed in the Authenticator that the
+     * reply key is the sub-session key and not the TGT session key.
+     *
+     * @throws Exception
+     */
+    public void testDecryptWithSubSessionKey() throws Exception
+    {
+        EncryptionType[] configuredEncryptionTypes =
+                {EncryptionType.AES128_CTS_HMAC_SHA1_96};
+        config.setEncryptionTypes( configuredEncryptionTypes );
+
+        // Get the mutable ticket part.
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        EncTicketPartModifier encTicketPartModifier = getTicketArchetype( clientPrincipal );
+
+        // Make changes to test.
+        sessionKey = RandomKeyFactory.getRandomKey( EncryptionType.AES128_CTS_HMAC_SHA1_96 );
+        encTicketPartModifier.setSessionKey( sessionKey );
+
+        // Seal the ticket for the server.
+        String principalName = "krbtgt/EXAMPLE.COM@EXAMPLE.COM";
+        KerberosPrincipal serverPrincipal = new KerberosPrincipal( principalName );
+        String passPhrase = "randomKey";
+        Set<EncryptionType> preAuthEncryptionTypes = new HashSet<EncryptionType>();
+        preAuthEncryptionTypes.add( EncryptionType.AES128_CTS_HMAC_SHA1_96 );
+
+        Map<EncryptionType, EncryptionKey> keyMap = KerberosKeyFactory.getKerberosKeys( principalName, passPhrase,
+                preAuthEncryptionTypes );
+        EncryptionKey serverKey = keyMap.get( EncryptionType.AES128_CTS_HMAC_SHA1_96 );
+
+        Ticket tgt = getTicket( encTicketPartModifier, serverPrincipal, serverKey );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setServerName( getPrincipalName( "ldap/ldap.example.com@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+
+        Set<EncryptionType> encryptionTypes = new HashSet<EncryptionType>();
+        encryptionTypes.add( EncryptionType.AES128_CTS_HMAC_SHA1_96 );
+
+        modifier.setEType( encryptionTypes );
+
+        modifier.setNonce( random.nextInt() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        modifier.setKdcOptions( kdcOptions );
+
+        long now = System.currentTimeMillis();
+        KerberosTime requestedEndTime = new KerberosTime( now + 1 * KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        subSessionKey = RandomKeyFactory.getRandomKey( EncryptionType.DES_CBC_MD5 );
+
+        RequestBody requestBody = modifier.getRequestBody();
+        KdcRequest message = getKdcRequest( tgt, requestBody );
+
+        handler.messageReceived( session, message );
+
+        TicketGrantReply reply = ( TicketGrantReply ) session.getMessage();
+
+        assertEquals( "Encryption type", EncryptionType.DES_CBC_MD5, reply.getEncPart().getEType() );
+        assertEquals( "Encryption type", EncryptionType.AES128_CTS_HMAC_SHA1_96, reply.getTicket().getEncPart()
+                .getEType() );
+    }
+}
diff --git a/old_trunk/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/protocol/TicketGrantingPolicyTest.java b/old_trunk/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/protocol/TicketGrantingPolicyTest.java
new file mode 100644
index 0000000..20787c4
--- /dev/null
+++ b/old_trunk/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/protocol/TicketGrantingPolicyTest.java
@@ -0,0 +1,677 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.protocol;
+
+
+import java.net.InetAddress;
+
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.apache.directory.server.kerberos.kdc.KdcServer;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.CipherTextHandler;
+import org.apache.directory.server.kerberos.shared.messages.ErrorMessage;
+import org.apache.directory.server.kerberos.shared.messages.KdcRequest;
+import org.apache.directory.server.kerberos.shared.messages.components.EncTicketPartModifier;
+import org.apache.directory.server.kerberos.shared.messages.components.Ticket;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+import org.apache.directory.server.kerberos.shared.messages.value.HostAddress;
+import org.apache.directory.server.kerberos.shared.messages.value.HostAddresses;
+import org.apache.directory.server.kerberos.shared.messages.value.KdcOptions;
+import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
+import org.apache.directory.server.kerberos.shared.messages.value.RequestBody;
+import org.apache.directory.server.kerberos.shared.messages.value.RequestBodyModifier;
+import org.apache.directory.server.kerberos.shared.messages.value.flags.TicketFlag;
+import org.apache.directory.server.kerberos.shared.store.PrincipalStore;
+
+
+/**
+ * Tests configuration of Ticket-Granting Service (TGS) policy.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class TicketGrantingPolicyTest extends AbstractTicketGrantingServiceTest
+{
+    private KdcServer config;
+    private PrincipalStore store;
+    private KerberosProtocolHandler handler;
+    private DummySession session;
+
+
+    /**
+     * Creates a new instance of {@link TicketGrantingPolicyTest}.
+     */
+    public TicketGrantingPolicyTest()
+    {
+        config = new KdcServer();
+
+        /*
+         * Body checksum verification must be disabled because we are bypassing
+         * the codecs, where the body bytes are set on the KdcRequest message.
+         */
+        config.setBodyChecksumVerified( false );
+
+        store = new MapPrincipalStoreImpl();
+        handler = new KerberosProtocolHandler( config, store );
+        session = new DummySession();
+        lockBox = new CipherTextHandler();
+    }
+
+
+    /**
+     * Tests when forwardable tickets are disallowed that requests for
+     * forwardable tickets fail with the correct error message.
+     *
+     * @throws Exception
+     */
+    public void testForwardableTicket() throws Exception
+    {
+        // Deny FORWARDABLE tickets in policy.
+        config.setForwardableAllowed( false );
+
+        // Get the mutable ticket part.
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        EncTicketPartModifier encTicketPartModifier = getTicketArchetype( clientPrincipal );
+
+        // Make changes to test.
+        encTicketPartModifier.setFlag( TicketFlag.FORWARDABLE );
+
+        // Seal the ticket for the server.
+        KerberosPrincipal serverPrincipal = new KerberosPrincipal( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" );
+        String passPhrase = "randomKey";
+        EncryptionKey serverKey = getEncryptionKey( serverPrincipal, passPhrase );
+        Ticket tgt = getTicket( encTicketPartModifier, serverPrincipal, serverKey );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setServerName( getPrincipalName( "ldap/ldap.example.com@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+        modifier.setNonce( random.nextInt() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        kdcOptions.set( KdcOptions.FORWARDABLE );
+        modifier.setKdcOptions( kdcOptions );
+
+        long now = System.currentTimeMillis();
+
+        KerberosTime requestedEndTime = new KerberosTime( now + 1 * KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        RequestBody requestBody = modifier.getRequestBody();
+        KdcRequest message = getKdcRequest( tgt, requestBody );
+
+        handler.messageReceived( session, message );
+
+        ErrorMessage error = ( ErrorMessage ) session.getMessage();
+        assertEquals( "KDC policy rejects request", 12, error.getErrorCode() );
+    }
+
+
+    /**
+     * Tests when forwardable tickets are disallowed that requests for
+     * forwarded tickets fail with the correct error message.
+     *
+     * @throws Exception
+     */
+    public void testForwardedTicket() throws Exception
+    {
+        // Deny FORWARDABLE tickets in policy.
+        config.setForwardableAllowed( false );
+
+        // Get the mutable ticket part.
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        EncTicketPartModifier encTicketPartModifier = getTicketArchetype( clientPrincipal );
+
+        // Make changes to test.
+        encTicketPartModifier.setFlag( TicketFlag.FORWARDABLE );
+
+        // Seal the ticket for the server.
+        KerberosPrincipal serverPrincipal = new KerberosPrincipal( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" );
+        String passPhrase = "randomKey";
+        EncryptionKey serverKey = getEncryptionKey( serverPrincipal, passPhrase );
+        Ticket tgt = getTicket( encTicketPartModifier, serverPrincipal, serverKey );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setServerName( getPrincipalName( "ldap/ldap.example.com@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+        modifier.setNonce( random.nextInt() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        kdcOptions.set( KdcOptions.FORWARDED );
+        modifier.setKdcOptions( kdcOptions );
+
+        long now = System.currentTimeMillis();
+
+        KerberosTime requestedEndTime = new KerberosTime( now + 1 * KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        RequestBody requestBody = modifier.getRequestBody();
+        KdcRequest message = getKdcRequest( tgt, requestBody );
+
+        handler.messageReceived( session, message );
+
+        ErrorMessage error = ( ErrorMessage ) session.getMessage();
+        assertEquals( "KDC policy rejects request", 12, error.getErrorCode() );
+    }
+
+
+    /**
+     * Tests when empty addresses are disallowed and forwarded tickets are requested
+     * that requests with no addresses fail with the correct error message.
+     *
+     * @throws Exception
+     */
+    public void testForwardedNoAddressesTicket() throws Exception
+    {
+        // Deny empty addresses tickets in policy.
+        config.setEmptyAddressesAllowed( false );
+
+        // Get the mutable ticket part.
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        EncTicketPartModifier encTicketPartModifier = getTicketArchetype( clientPrincipal );
+
+        // Make changes to test.
+        encTicketPartModifier.setFlag( TicketFlag.FORWARDABLE );
+
+        HostAddress[] address =
+                {new HostAddress( InetAddress.getByAddress( new byte[4] ) )};
+        HostAddresses addresses = new HostAddresses( address );
+        encTicketPartModifier.setClientAddresses( addresses );
+
+        // Seal the ticket for the server.
+        KerberosPrincipal serverPrincipal = new KerberosPrincipal( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" );
+        String passPhrase = "randomKey";
+        EncryptionKey serverKey = getEncryptionKey( serverPrincipal, passPhrase );
+        Ticket tgt = getTicket( encTicketPartModifier, serverPrincipal, serverKey );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setServerName( getPrincipalName( "ldap/ldap.example.com@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+        modifier.setNonce( random.nextInt() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        kdcOptions.set( KdcOptions.FORWARDED );
+        modifier.setKdcOptions( kdcOptions );
+
+        long now = System.currentTimeMillis();
+
+        KerberosTime requestedEndTime = new KerberosTime( now + 1 * KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        RequestBody requestBody = modifier.getRequestBody();
+        KdcRequest message = getKdcRequest( tgt, requestBody );
+
+        handler.messageReceived( session, message );
+
+        ErrorMessage error = ( ErrorMessage ) session.getMessage();
+        assertEquals( "KDC policy rejects request", 12, error.getErrorCode() );
+    }
+
+
+    /**
+     * Tests when proxiable tickets are disallowed that requests for
+     * proxiable tickets fail with the correct error message.
+     *
+     * @throws Exception
+     */
+    public void testProxiableTicket() throws Exception
+    {
+        // Deny PROXIABLE tickets in policy.
+        config.setProxiableAllowed( false );
+
+        // Get the mutable ticket part.
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        EncTicketPartModifier encTicketPartModifier = getTicketArchetype( clientPrincipal );
+
+        // Make changes to test.
+        encTicketPartModifier.setFlag( TicketFlag.PROXIABLE );
+
+        // Seal the ticket for the server.
+        KerberosPrincipal serverPrincipal = new KerberosPrincipal( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" );
+        String passPhrase = "randomKey";
+        EncryptionKey serverKey = getEncryptionKey( serverPrincipal, passPhrase );
+        Ticket tgt = getTicket( encTicketPartModifier, serverPrincipal, serverKey );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setServerName( getPrincipalName( "ldap/ldap.example.com@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+        modifier.setNonce( random.nextInt() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        kdcOptions.set( KdcOptions.PROXIABLE );
+        modifier.setKdcOptions( kdcOptions );
+
+        long now = System.currentTimeMillis();
+
+        KerberosTime requestedEndTime = new KerberosTime( now + 1 * KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        RequestBody requestBody = modifier.getRequestBody();
+        KdcRequest message = getKdcRequest( tgt, requestBody );
+
+        handler.messageReceived( session, message );
+
+        ErrorMessage error = ( ErrorMessage ) session.getMessage();
+        assertEquals( "KDC policy rejects request", 12, error.getErrorCode() );
+    }
+
+
+    /**
+     * Tests when proxiable tickets are disallowed that requests for
+     * proxy tickets fail with the correct error message.
+     *
+     * @throws Exception
+     */
+    public void testProxyTicket() throws Exception
+    {
+        // Deny PROXIABLE tickets in policy.
+        config.setProxiableAllowed( false );
+
+        // Get the mutable ticket part.
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        EncTicketPartModifier encTicketPartModifier = getTicketArchetype( clientPrincipal );
+
+        // Make changes to test.
+        encTicketPartModifier.setFlag( TicketFlag.PROXIABLE );
+
+        // Seal the ticket for the server.
+        KerberosPrincipal serverPrincipal = new KerberosPrincipal( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" );
+        String passPhrase = "randomKey";
+        EncryptionKey serverKey = getEncryptionKey( serverPrincipal, passPhrase );
+        Ticket tgt = getTicket( encTicketPartModifier, serverPrincipal, serverKey );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setServerName( getPrincipalName( "ldap/ldap.example.com@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+        modifier.setNonce( random.nextInt() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        kdcOptions.set( KdcOptions.PROXY );
+        modifier.setKdcOptions( kdcOptions );
+
+        long now = System.currentTimeMillis();
+
+        KerberosTime requestedEndTime = new KerberosTime( now + 1 * KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        HostAddress[] address =
+                {new HostAddress( InetAddress.getLocalHost() )};
+        HostAddresses addresses = new HostAddresses( address );
+        modifier.setAddresses( addresses );
+
+        RequestBody requestBody = modifier.getRequestBody();
+        KdcRequest message = getKdcRequest( tgt, requestBody );
+
+        handler.messageReceived( session, message );
+
+        ErrorMessage error = ( ErrorMessage ) session.getMessage();
+        assertEquals( "KDC policy rejects request", 12, error.getErrorCode() );
+    }
+
+
+    /**
+     * Tests when empty addresses are disallowed and proxy tickets are requested
+     * that requests with no addresses fail with the correct error message.
+     *
+     * @throws Exception
+     */
+    public void testProxyNoAddressesTicket() throws Exception
+    {
+        // Deny empty addresses tickets in policy.
+        config.setEmptyAddressesAllowed( false );
+
+        // Get the mutable ticket part.
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        EncTicketPartModifier encTicketPartModifier = getTicketArchetype( clientPrincipal );
+
+        // Make changes to test.
+        encTicketPartModifier.setFlag( TicketFlag.PROXIABLE );
+
+        HostAddress[] address =
+                {new HostAddress( InetAddress.getByAddress( new byte[4] ) )};
+        HostAddresses addresses = new HostAddresses( address );
+        encTicketPartModifier.setClientAddresses( addresses );
+
+        // Seal the ticket for the server.
+        KerberosPrincipal serverPrincipal = new KerberosPrincipal( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" );
+        String passPhrase = "randomKey";
+        EncryptionKey serverKey = getEncryptionKey( serverPrincipal, passPhrase );
+        Ticket tgt = getTicket( encTicketPartModifier, serverPrincipal, serverKey );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setServerName( getPrincipalName( "ldap/ldap.example.com@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+        modifier.setNonce( random.nextInt() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        kdcOptions.set( KdcOptions.PROXY );
+        modifier.setKdcOptions( kdcOptions );
+
+        long now = System.currentTimeMillis();
+
+        KerberosTime requestedEndTime = new KerberosTime( now + 1 * KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        RequestBody requestBody = modifier.getRequestBody();
+        KdcRequest message = getKdcRequest( tgt, requestBody );
+
+        handler.messageReceived( session, message );
+
+        ErrorMessage error = ( ErrorMessage ) session.getMessage();
+        assertEquals( "KDC policy rejects request", 12, error.getErrorCode() );
+    }
+
+
+    /**
+     * Tests when postdated tickets are disallowed that requests for
+     * ALLOW-POSTDATE tickets fail with the correct error message.
+     *
+     * @throws Exception
+     */
+    public void testAllowPostdate() throws Exception
+    {
+        // Deny ALLOW_POSTDATE tickets in policy.
+        config.setPostdatedAllowed( false );
+
+        // Get the mutable ticket part.
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        EncTicketPartModifier encTicketPartModifier = getTicketArchetype( clientPrincipal );
+
+        // Make changes to test.
+        encTicketPartModifier.setFlag( TicketFlag.MAY_POSTDATE );
+
+        // Seal the ticket for the server.
+        KerberosPrincipal serverPrincipal = new KerberosPrincipal( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" );
+        String passPhrase = "randomKey";
+        EncryptionKey serverKey = getEncryptionKey( serverPrincipal, passPhrase );
+        Ticket tgt = getTicket( encTicketPartModifier, serverPrincipal, serverKey );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setServerName( getPrincipalName( "ldap/ldap.example.com@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+        modifier.setNonce( random.nextInt() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        kdcOptions.set( KdcOptions.ALLOW_POSTDATE );
+        modifier.setKdcOptions( kdcOptions );
+
+        long now = System.currentTimeMillis();
+
+        KerberosTime requestedEndTime = new KerberosTime( now + 1 * KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        RequestBody requestBody = modifier.getRequestBody();
+        KdcRequest message = getKdcRequest( tgt, requestBody );
+
+        handler.messageReceived( session, message );
+
+        ErrorMessage error = ( ErrorMessage ) session.getMessage();
+        assertEquals( "KDC policy rejects request", 12, error.getErrorCode() );
+    }
+
+
+    /**
+     * Tests when postdated tickets are disallowed that requests for
+     * postdated tickets fail with the correct error message.
+     *
+     * @throws Exception
+     */
+    public void testPostdated() throws Exception
+    {
+        // Deny POSTDATED tickets in policy.
+        config.setPostdatedAllowed( false );
+
+        // Get the mutable ticket part.
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        EncTicketPartModifier encTicketPartModifier = getTicketArchetype( clientPrincipal );
+
+        // Make changes to test.
+        encTicketPartModifier.setFlag( TicketFlag.MAY_POSTDATE );
+
+        // Seal the ticket for the server.
+        KerberosPrincipal serverPrincipal = new KerberosPrincipal( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" );
+        String passPhrase = "randomKey";
+        EncryptionKey serverKey = getEncryptionKey( serverPrincipal, passPhrase );
+        Ticket tgt = getTicket( encTicketPartModifier, serverPrincipal, serverKey );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setServerName( getPrincipalName( "ldap/ldap.example.com@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+        modifier.setNonce( random.nextInt() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        kdcOptions.set( KdcOptions.POSTDATED );
+        modifier.setKdcOptions( kdcOptions );
+
+        long now = System.currentTimeMillis();
+
+        KerberosTime requestedEndTime = new KerberosTime( now + 1 * KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        RequestBody requestBody = modifier.getRequestBody();
+        KdcRequest message = getKdcRequest( tgt, requestBody );
+
+        handler.messageReceived( session, message );
+
+        ErrorMessage error = ( ErrorMessage ) session.getMessage();
+        assertEquals( "KDC policy rejects request", 12, error.getErrorCode() );
+    }
+
+
+    /**
+     * Tests when postdated tickets are disallowed that requests for
+     * validation of invalid tickets fail with the correct error message.
+     *
+     * @throws Exception
+     */
+    public void testValidateInvalidTicket() throws Exception
+    {
+        // Deny VALIDATE tickets in policy.
+        config.setPostdatedAllowed( false );
+
+        // Get the mutable ticket part.
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        EncTicketPartModifier encTicketPartModifier = getTicketArchetype( clientPrincipal );
+
+        // Make changes to test.
+        encTicketPartModifier.setFlag( TicketFlag.INVALID );
+
+        // Seal the ticket for the server.
+        KerberosPrincipal serverPrincipal = new KerberosPrincipal( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" );
+        String passPhrase = "randomKey";
+        EncryptionKey serverKey = getEncryptionKey( serverPrincipal, passPhrase );
+        Ticket tgt = getTicket( encTicketPartModifier, serverPrincipal, serverKey );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setServerName( getPrincipalName( "hnelson" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+        modifier.setNonce( random.nextInt() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        kdcOptions.set( KdcOptions.VALIDATE );
+        modifier.setKdcOptions( kdcOptions );
+
+        long currentTime = System.currentTimeMillis();
+
+        KerberosTime requestedEndTime = new KerberosTime( currentTime + KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        RequestBody requestBody = modifier.getRequestBody();
+
+        KdcRequest message = getKdcRequest( tgt, requestBody );
+
+        handler.messageReceived( session, message );
+
+        ErrorMessage error = ( ErrorMessage ) session.getMessage();
+        assertEquals( "KDC policy rejects request", 12, error.getErrorCode() );
+    }
+
+
+    /**
+     * Tests when renewable tickets are disallowed that requests for
+     * renewal of tickets fail with the correct error message.
+     *
+     * @throws Exception
+     */
+    public void testRenewTicket() throws Exception
+    {
+        // Deny RENEWABLE tickets in policy.
+        config.setRenewableAllowed( false );
+
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        KerberosPrincipal serverPrincipal = new KerberosPrincipal( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" );
+        String serverPassword = "randomKey";
+
+        Ticket tgt = getTgt( clientPrincipal, serverPrincipal, serverPassword );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setServerName( getPrincipalName( "hnelson" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+        modifier.setNonce( random.nextInt() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        kdcOptions.set( KdcOptions.RENEW );
+        modifier.setKdcOptions( kdcOptions );
+
+        long currentTime = System.currentTimeMillis();
+
+        KerberosTime requestedEndTime = new KerberosTime( currentTime + KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        RequestBody requestBody = modifier.getRequestBody();
+
+        KdcRequest message = getKdcRequest( tgt, requestBody );
+
+        handler.messageReceived( session, message );
+
+        ErrorMessage error = ( ErrorMessage ) session.getMessage();
+        assertEquals( "KDC policy rejects request", 12, error.getErrorCode() );
+    }
+
+
+    /**
+     * Tests when renewable tickets are disallowed that requests for
+     * RENEWABLE-OK tickets fail with the correct error message.
+     *
+     * @throws Exception
+     */
+    public void testRenewableOk() throws Exception
+    {
+        // Deny RENEWABLE tickets in policy.
+        config.setRenewableAllowed( false );
+
+        // Get the mutable ticket part.
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        EncTicketPartModifier encTicketPartModifier = getTicketArchetype( clientPrincipal );
+
+        // Make changes to test.
+        encTicketPartModifier.setFlag( TicketFlag.RENEWABLE );
+
+        // Seal the ticket for the server.
+        KerberosPrincipal serverPrincipal = new KerberosPrincipal( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" );
+        String passPhrase = "randomKey";
+        EncryptionKey serverKey = getEncryptionKey( serverPrincipal, passPhrase );
+        Ticket tgt = getTicket( encTicketPartModifier, serverPrincipal, serverKey );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setServerName( getPrincipalName( "ldap/ldap.example.com@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+        modifier.setNonce( random.nextInt() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        kdcOptions.set( KdcOptions.RENEWABLE_OK );
+        modifier.setKdcOptions( kdcOptions );
+
+        long now = System.currentTimeMillis();
+
+        KerberosTime requestedEndTime = new KerberosTime( now + KerberosTime.WEEK );
+        modifier.setTill( requestedEndTime );
+
+        RequestBody requestBody = modifier.getRequestBody();
+        KdcRequest message = getKdcRequest( tgt, requestBody );
+
+        handler.messageReceived( session, message );
+
+        ErrorMessage error = ( ErrorMessage ) session.getMessage();
+        assertEquals( "KDC policy rejects request", 12, error.getErrorCode() );
+    }
+
+
+    /**
+     * Tests when renewable tickets are disallowed that requests for
+     * renewable tickets fail with the correct error message.
+     *
+     * @throws Exception
+     */
+    public void testRenewableTicket() throws Exception
+    {
+        // Deny RENEWABLE tickets in policy.
+        config.setRenewableAllowed( false );
+
+        // Get the mutable ticket part.
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        EncTicketPartModifier encTicketPartModifier = getTicketArchetype( clientPrincipal );
+
+        // Make changes to test.
+        encTicketPartModifier.setFlag( TicketFlag.RENEWABLE );
+
+        // Seal the ticket for the server.
+        KerberosPrincipal serverPrincipal = new KerberosPrincipal( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" );
+        String passPhrase = "randomKey";
+        EncryptionKey serverKey = getEncryptionKey( serverPrincipal, passPhrase );
+        Ticket tgt = getTicket( encTicketPartModifier, serverPrincipal, serverKey );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setServerName( getPrincipalName( "ldap/ldap.example.com@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+        modifier.setNonce( random.nextInt() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        kdcOptions.set( KdcOptions.RENEWABLE );
+        modifier.setKdcOptions( kdcOptions );
+
+        long now = System.currentTimeMillis();
+
+        KerberosTime requestedEndTime = new KerberosTime( now + 1 * KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        KerberosTime requestedRenewTillTime = new KerberosTime( now + KerberosTime.WEEK / 2 );
+        modifier.setRtime( requestedRenewTillTime );
+
+        RequestBody requestBody = modifier.getRequestBody();
+        KdcRequest message = getKdcRequest( tgt, requestBody );
+
+        handler.messageReceived( session, message );
+
+        ErrorMessage error = ( ErrorMessage ) session.getMessage();
+        assertEquals( "KDC policy rejects request", 12, error.getErrorCode() );
+    }
+}
diff --git a/old_trunk/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/protocol/TicketGrantingServiceTest.java b/old_trunk/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/protocol/TicketGrantingServiceTest.java
new file mode 100644
index 0000000..b4dd873
--- /dev/null
+++ b/old_trunk/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/protocol/TicketGrantingServiceTest.java
@@ -0,0 +1,1908 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.kerberos.protocol;
+
+
+import java.net.InetAddress;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.apache.directory.server.kerberos.kdc.KdcServer;
+import org.apache.directory.server.kerberos.shared.KerberosMessageType;
+import org.apache.directory.server.kerberos.shared.crypto.checksum.ChecksumType;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.CipherTextHandler;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.RandomKeyFactory;
+import org.apache.directory.server.kerberos.shared.exceptions.KerberosException;
+import org.apache.directory.server.kerberos.shared.messages.ErrorMessage;
+import org.apache.directory.server.kerberos.shared.messages.KdcRequest;
+import org.apache.directory.server.kerberos.shared.messages.TicketGrantReply;
+import org.apache.directory.server.kerberos.shared.messages.components.EncTicketPartModifier;
+import org.apache.directory.server.kerberos.shared.messages.components.Ticket;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+import org.apache.directory.server.kerberos.shared.messages.value.HostAddress;
+import org.apache.directory.server.kerberos.shared.messages.value.HostAddresses;
+import org.apache.directory.server.kerberos.shared.messages.value.KdcOptions;
+import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
+import org.apache.directory.server.kerberos.shared.messages.value.RequestBody;
+import org.apache.directory.server.kerberos.shared.messages.value.RequestBodyModifier;
+import org.apache.directory.server.kerberos.shared.messages.value.flags.TicketFlag;
+import org.apache.directory.server.kerberos.shared.store.PrincipalStore;
+
+
+/**
+ * Tests the Ticket-Granting Service (TGS) via the {@link KerberosProtocolHandler}.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class TicketGrantingServiceTest extends AbstractTicketGrantingServiceTest
+{
+    private KdcServer config;
+    private PrincipalStore store;
+    private KerberosProtocolHandler handler;
+    private DummySession session;
+
+
+    /**
+     * Creates a new instance of {@link TicketGrantingServiceTest}.
+     */
+    public TicketGrantingServiceTest()
+    {
+        config = new KdcServer();
+
+        /*
+         * Body checksum verification must be disabled because we are bypassing
+         * the codecs, where the body bytes are set on the KdcRequest message.
+         */
+        config.setBodyChecksumVerified( false );
+
+        store = new MapPrincipalStoreImpl();
+        handler = new KerberosProtocolHandler( config, store );
+        session = new DummySession();
+        lockBox = new CipherTextHandler();
+    }
+
+
+    /**
+     * Tests the default minimum request, which consists of as little as the
+     * client name, service name, realm, till time, nonce, and encryption types.
+     * 
+     * This is the request archetype.
+     * 
+     * "The TGS exchange between a client and the Kerberos TGS is initiated by a
+     * client ... when it seeks to obtain authentication credentials for a given
+     * server (which might be registered in a remote realm)."
+     * 
+     * "In the first case, the client must already have acquired a ticket for the
+     * Ticket-Granting Service using the AS exchange (the TGT is usually obtained
+     * when a client initially authenticates to the system, such as when a user
+     * logs in)."
+     * 
+     * @throws Exception 
+     */
+    public void testRequestArchetype() throws Exception
+    {
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        KerberosPrincipal serverPrincipal = new KerberosPrincipal( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" );
+        String serverPassword = "randomKey";
+
+        Ticket tgt = getTgt( clientPrincipal, serverPrincipal, serverPassword );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setServerName( getPrincipalName( "hnelson" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+        modifier.setNonce( random.nextInt() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        modifier.setKdcOptions( kdcOptions );
+
+        long currentTime = System.currentTimeMillis();
+
+        KerberosTime requestedEndTime = new KerberosTime( currentTime + KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        RequestBody requestBody = modifier.getRequestBody();
+
+        KdcRequest message = getKdcRequest( tgt, requestBody );
+
+        handler.messageReceived( session, message );
+    }
+
+
+    /**
+     * Tests the protocol version number, which must be '5'.
+     */
+    public void testProtocolVersionNumber()
+    {
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setServerName( getPrincipalName( "hnelson" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+
+        KdcRequest message = new KdcRequest( 4, KerberosMessageType.TGS_REQ, null, modifier.getRequestBody() );
+
+        handler.messageReceived( session, message );
+
+        ErrorMessage error = ( ErrorMessage ) session.getMessage();
+        assertEquals( "Requested protocol version number not supported", 3, error.getErrorCode() );
+    }
+
+
+    /**
+     * Tests that a non-existent server principal returns the correct error message.
+     * 
+     * @throws Exception 
+     */
+    public void testServerNotFound() throws Exception
+    {
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        KerberosPrincipal serverPrincipal = new KerberosPrincipal( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" );
+        String serverPassword = "randomKey";
+
+        Ticket tgt = getTgt( clientPrincipal, serverPrincipal, serverPassword );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setServerName( getPrincipalName( "badservice" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+        modifier.setNonce( random.nextInt() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        modifier.setKdcOptions( kdcOptions );
+
+        long currentTime = System.currentTimeMillis();
+
+        KerberosTime requestedEndTime = new KerberosTime( currentTime + KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        RequestBody requestBody = modifier.getRequestBody();
+
+        KdcRequest message = getKdcRequest( tgt, requestBody );
+
+        handler.messageReceived( session, message );
+
+        ErrorMessage error = ( ErrorMessage ) session.getMessage();
+        assertEquals( "Server not found in Kerberos database", 7, error.getErrorCode() );
+    }
+
+
+    /**
+     * Tests when no ticket is found in the auth header that the request is rejected
+     * with the correct error message.
+     * 
+     * "If no ticket can be found in the padata field, the KDC_ERR_PADATA_TYPE_NOSUPP
+     * error is returned."
+     * 
+     * @throws Exception 
+     */
+    public void testNoTicketFound() throws Exception
+    {
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        KerberosPrincipal serverPrincipal = new KerberosPrincipal( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" );
+        String serverPassword = "randomKey";
+
+        Ticket tgt = getTgt( clientPrincipal, serverPrincipal, serverPassword );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setServerName( getPrincipalName( "hnelson" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+        modifier.setNonce( random.nextInt() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        modifier.setKdcOptions( kdcOptions );
+
+        long currentTime = System.currentTimeMillis();
+
+        KerberosTime requestedEndTime = new KerberosTime( currentTime + KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        RequestBody requestBody = modifier.getRequestBody();
+
+        // Get the session key from the service ticket.
+        sessionKey = tgt.getEncTicketPart().getSessionKey();
+
+        // Generate a new sequence number.
+        sequenceNumber = random.nextInt();
+        now = new KerberosTime();
+
+        KdcRequest message = new KdcRequest( 5, KerberosMessageType.TGS_REQ, null, requestBody );
+
+        handler.messageReceived( session, message );
+
+        ErrorMessage error = ( ErrorMessage ) session.getMessage();
+        assertEquals( "KDC has no support for padata type", 16, error.getErrorCode() );
+    }
+
+
+    /**
+     * Tests that an inappropriate checksum returns the correct error message.
+     * 
+     * @throws Exception 
+     */
+    public void testInappropriateChecksum() throws Exception
+    {
+        config.setBodyChecksumVerified( true );
+
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "tquist@EXAMPLE.COM" );
+        KerberosPrincipal serverPrincipal = new KerberosPrincipal( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" );
+        String serverPassword = "randomKey";
+
+        Ticket tgt = getTgt( clientPrincipal, serverPrincipal, serverPassword );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setServerName( getPrincipalName( "hnelson" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+        modifier.setNonce( random.nextInt() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        modifier.setKdcOptions( kdcOptions );
+
+        long currentTime = System.currentTimeMillis();
+
+        KerberosTime requestedEndTime = new KerberosTime( currentTime + KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        RequestBody requestBody = modifier.getRequestBody();
+
+        KdcRequest message = getKdcRequest( tgt, requestBody );
+
+        handler.messageReceived( session, message );
+
+        ErrorMessage error = ( ErrorMessage ) session.getMessage();
+        assertEquals( "Inappropriate type of checksum in message", 50, error.getErrorCode() );
+    }
+
+
+    /**
+     * Tests that an inappropriate checksum returns the correct error message.
+     * 
+     * @throws Exception 
+     */
+    public void testChecksumTypeNoSupport() throws Exception
+    {
+        config.setBodyChecksumVerified( true );
+
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "tquist@EXAMPLE.COM" );
+        KerberosPrincipal serverPrincipal = new KerberosPrincipal( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" );
+        String serverPassword = "randomKey";
+
+        Ticket tgt = getTgt( clientPrincipal, serverPrincipal, serverPassword );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setServerName( getPrincipalName( "hnelson" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+        modifier.setNonce( random.nextInt() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        modifier.setKdcOptions( kdcOptions );
+
+        long currentTime = System.currentTimeMillis();
+
+        KerberosTime requestedEndTime = new KerberosTime( currentTime + KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        RequestBody requestBody = modifier.getRequestBody();
+
+        try
+        {
+            getKdcRequest( tgt, requestBody, ChecksumType.DES_MAC_K );
+        }
+        catch ( KerberosException ke )
+        {
+            assertEquals( "KDC has no support for checksum type", 15, ke.getErrorCode() );
+        }
+    }
+
+
+    /**
+     * "If any of the decryptions indicate failed integrity checks, the
+     * KRB_AP_ERR_BAD_INTEGRITY error is returned."
+     * 
+     * @throws Exception
+     */
+    public void testIntegrityCheckedFailed() throws Exception
+    {
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        KerberosPrincipal serverPrincipal = new KerberosPrincipal( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" );
+        String serverPassword = "badpassword";
+
+        Ticket tgt = getTgt( clientPrincipal, serverPrincipal, serverPassword );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setServerName( getPrincipalName( "hnelson" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+        modifier.setNonce( random.nextInt() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        modifier.setKdcOptions( kdcOptions );
+
+        long currentTime = System.currentTimeMillis();
+
+        KerberosTime requestedEndTime = new KerberosTime( currentTime + KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        RequestBody requestBody = modifier.getRequestBody();
+
+        KdcRequest message = getKdcRequest( tgt, requestBody );
+
+        handler.messageReceived( session, message );
+
+        ErrorMessage error = ( ErrorMessage ) session.getMessage();
+        assertEquals( "Integrity check on decrypted field failed", 31, error.getErrorCode() );
+    }
+
+
+    /**
+     * Tests when the ticket isn't for us that the correct error message is returned.
+     * 
+     * @throws Exception
+     */
+    public void testNotUs() throws Exception
+    {
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        KerberosPrincipal serverPrincipal = new KerberosPrincipal( "krbtgt/EXAMPLE.COM@APACHE.ORG" );
+        String serverPassword = "randomKey";
+
+        Ticket tgt = getTgt( clientPrincipal, serverPrincipal, serverPassword );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setServerName( getPrincipalName( "hnelson" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+        modifier.setNonce( random.nextInt() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        modifier.setKdcOptions( kdcOptions );
+
+        long currentTime = System.currentTimeMillis();
+
+        KerberosTime requestedEndTime = new KerberosTime( currentTime + KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        RequestBody requestBody = modifier.getRequestBody();
+
+        KdcRequest message = getKdcRequest( tgt, requestBody );
+
+        handler.messageReceived( session, message );
+
+        ErrorMessage error = ( ErrorMessage ) session.getMessage();
+        assertEquals( "The ticket isn't for us", 35, error.getErrorCode() );
+    }
+
+
+    /**
+     * "The TGS exchange between a client and the Kerberos TGS is initiated by a
+     * client when ... it seeks to renew an existing ticket."
+     * 
+     * @throws Exception 
+     */
+    public void testRenewTicket() throws Exception
+    {
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        KerberosPrincipal serverPrincipal = new KerberosPrincipal( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" );
+        String serverPassword = "randomKey";
+
+        Ticket tgt = getTgt( clientPrincipal, serverPrincipal, serverPassword );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setServerName( getPrincipalName( "hnelson" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+        modifier.setNonce( random.nextInt() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        kdcOptions.set( KdcOptions.RENEW );
+        modifier.setKdcOptions( kdcOptions );
+
+        long currentTime = System.currentTimeMillis();
+
+        KerberosTime requestedEndTime = new KerberosTime( currentTime + KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        RequestBody requestBody = modifier.getRequestBody();
+
+        KdcRequest message = getKdcRequest( tgt, requestBody );
+
+        handler.messageReceived( session, message );
+
+        TicketGrantReply reply = ( TicketGrantReply ) session.getMessage();
+
+        KerberosTime expectedRenewTillTime = tgt.getEncTicketPart().getRenewTill();
+        boolean isClose = Math.abs( reply.getRenewTill().getTime() - expectedRenewTillTime.getTime() ) < 5000;
+        assertTrue( "Expected renew till time", isClose );
+    }
+
+
+    /**
+     * "The TGS exchange between a client and the Kerberos TGS is initiated by a
+     * client when ... it seeks to validate an existing ticket."
+     * 
+     * @throws Exception 
+     */
+    public void testValidateTicket() throws Exception
+    {
+        // Get the mutable ticket part.
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        EncTicketPartModifier encTicketPartModifier = getTicketArchetype( clientPrincipal );
+
+        // Make changes to test.
+        encTicketPartModifier.setFlag( TicketFlag.INVALID );
+
+        // Seal the ticket for the server.
+        KerberosPrincipal serverPrincipal = new KerberosPrincipal( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" );
+        String passPhrase = "randomKey";
+        EncryptionKey serverKey = getEncryptionKey( serverPrincipal, passPhrase );
+        Ticket tgt = getTicket( encTicketPartModifier, serverPrincipal, serverKey );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setServerName( getPrincipalName( "hnelson" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+        modifier.setNonce( random.nextInt() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        kdcOptions.set( KdcOptions.VALIDATE );
+        modifier.setKdcOptions( kdcOptions );
+
+        long currentTime = System.currentTimeMillis();
+
+        KerberosTime requestedEndTime = new KerberosTime( currentTime + KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        RequestBody requestBody = modifier.getRequestBody();
+
+        KdcRequest message = getKdcRequest( tgt, requestBody );
+
+        handler.messageReceived( session, message );
+
+        TicketGrantReply reply = ( TicketGrantReply ) session.getMessage();
+
+        KerberosTime expectedRenewTillTime = tgt.getEncTicketPart().getRenewTill();
+        boolean isClose = Math.abs( reply.getRenewTill().getTime() - expectedRenewTillTime.getTime() ) < 5000;
+        assertTrue( "Expected renew till time", isClose );
+    }
+
+
+    /**
+     * "The TGS exchange between a client and the Kerberos TGS is initiated by a
+     * client when ... it seeks to obtain a proxy ticket."
+     * 
+     * @throws Exception 
+     */
+    public void testProxyTicket() throws Exception
+    {
+        // Get the mutable ticket part.
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        EncTicketPartModifier encTicketPartModifier = getTicketArchetype( clientPrincipal );
+
+        // Make changes to test.
+        encTicketPartModifier.setFlag( TicketFlag.PROXIABLE );
+
+        // Seal the ticket for the server.
+        KerberosPrincipal serverPrincipal = new KerberosPrincipal( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" );
+        String passPhrase = "randomKey";
+        EncryptionKey serverKey = getEncryptionKey( serverPrincipal, passPhrase );
+        Ticket tgt = getTicket( encTicketPartModifier, serverPrincipal, serverKey );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setServerName( getPrincipalName( "ldap/ldap.example.com@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+        modifier.setNonce( random.nextInt() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        kdcOptions.set( KdcOptions.PROXY );
+        modifier.setKdcOptions( kdcOptions );
+
+        long now = System.currentTimeMillis();
+
+        KerberosTime requestedEndTime = new KerberosTime( now + 1 * KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        HostAddress[] address =
+            { new HostAddress( InetAddress.getByName( null ) ) };
+        HostAddresses addresses = new HostAddresses( address );
+        modifier.setAddresses( addresses );
+
+        RequestBody requestBody = modifier.getRequestBody();
+        KdcRequest message = getKdcRequest( tgt, requestBody );
+
+        handler.messageReceived( session, message );
+
+        TicketGrantReply reply = ( TicketGrantReply ) session.getMessage();
+
+        assertTrue( "PROXY flag", reply.getFlags().isProxy() );
+        assertFalse( "INVALID flag", reply.getFlags().isInvalid() );
+
+        assertTrue( "PROXY flag", reply.getTicket().getEncTicketPart().getFlags().isProxy() );
+        assertFalse( "INVALID flag", reply.getTicket().getEncTicketPart().getFlags().isInvalid() );
+
+        assertNotNull( reply.getTicket().getEncTicketPart().getClientAddresses() );
+    }
+
+
+    /**
+     * "The TGS exchange between a client and the Kerberos TGS is initiated by a
+     * client when ... it seeks to obtain a forwarded ticket."
+     * 
+     * @throws Exception 
+     */
+    public void testForwardedTicket() throws Exception
+    {
+        // Get the mutable ticket part.
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        EncTicketPartModifier encTicketPartModifier = getTicketArchetype( clientPrincipal );
+
+        // Make changes to test.
+        encTicketPartModifier.setFlag( TicketFlag.FORWARDABLE );
+
+        // Seal the ticket for the server.
+        KerberosPrincipal serverPrincipal = new KerberosPrincipal( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" );
+        String passPhrase = "randomKey";
+        EncryptionKey serverKey = getEncryptionKey( serverPrincipal, passPhrase );
+        Ticket tgt = getTicket( encTicketPartModifier, serverPrincipal, serverKey );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setServerName( getPrincipalName( "ldap/ldap.example.com@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+        modifier.setNonce( random.nextInt() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        kdcOptions.set( KdcOptions.FORWARDED );
+        modifier.setKdcOptions( kdcOptions );
+
+        long now = System.currentTimeMillis();
+
+        KerberosTime requestedEndTime = new KerberosTime( now + 1 * KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        HostAddress[] address =
+            { new HostAddress( InetAddress.getByName( null ) ) };
+        HostAddresses addresses = new HostAddresses( address );
+        modifier.setAddresses( addresses );
+
+        RequestBody requestBody = modifier.getRequestBody();
+        KdcRequest message = getKdcRequest( tgt, requestBody );
+
+        handler.messageReceived( session, message );
+
+        TicketGrantReply reply = ( TicketGrantReply ) session.getMessage();
+
+        assertTrue( "FORWARDED flag", reply.getFlags().isForwarded() );
+        assertFalse( "INVALID flag", reply.getFlags().isInvalid() );
+
+        assertTrue( "FORWARDED flag", reply.getTicket().getEncTicketPart().getFlags().isForwarded() );
+        assertFalse( "INVALID flag", reply.getTicket().getEncTicketPart().getFlags().isInvalid() );
+
+        assertNotNull( reply.getTicket().getEncTicketPart().getClientAddresses() );
+    }
+
+
+    /**
+     * As is the case for all application servers, expired tickets are not
+     * accepted by the TGS, so once a renewable or TGT expires, the client
+     * must use a separate exchange to obtain valid tickets.
+     * 
+     * @throws Exception 
+     */
+    public void testExpiredTgt() throws Exception
+    {
+        // Get the mutable ticket part.
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        EncTicketPartModifier encTicketPartModifier = getTicketArchetype( clientPrincipal );
+
+        // Make changes to test.
+        encTicketPartModifier.setEndTime( new KerberosTime( 0 ) );
+
+        // Seal the ticket for the server.
+        KerberosPrincipal serverPrincipal = new KerberosPrincipal( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" );
+        String passPhrase = "randomKey";
+        EncryptionKey serverKey = getEncryptionKey( serverPrincipal, passPhrase );
+        Ticket tgt = getTicket( encTicketPartModifier, serverPrincipal, serverKey );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setServerName( getPrincipalName( "hnelson" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+        modifier.setNonce( random.nextInt() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        modifier.setKdcOptions( kdcOptions );
+
+        long now = System.currentTimeMillis();
+
+        KerberosTime requestedEndTime = new KerberosTime( now + KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        RequestBody requestBody = modifier.getRequestBody();
+        KdcRequest message = getKdcRequest( tgt, requestBody );
+
+        handler.messageReceived( session, message );
+
+        ErrorMessage error = ( ErrorMessage ) session.getMessage();
+        assertEquals( "Ticket expired", 32, error.getErrorCode() );
+    }
+
+
+    /**
+     * As is the case for all application servers, expired tickets are not accepted
+     * by the TGS, so once a renewable or TGT expires, the client must use a separate
+     * exchange to obtain valid tickets.
+     * 
+     * @throws Exception 
+     */
+    public void testExpiredRenewableTicket() throws Exception
+    {
+        // Get the mutable ticket part.
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        EncTicketPartModifier encTicketPartModifier = getTicketArchetype( clientPrincipal );
+
+        // Make changes to test.
+        encTicketPartModifier.setFlag( TicketFlag.RENEWABLE );
+        encTicketPartModifier.setRenewTill( new KerberosTime( 0 ) );
+
+        // Seal the ticket for the server.
+        KerberosPrincipal serverPrincipal = new KerberosPrincipal( "ldap/ldap.example.com@EXAMPLE.COM" );
+        String passPhrase = "randomKey";
+        EncryptionKey serverKey = getEncryptionKey( serverPrincipal, passPhrase );
+        Ticket tgt = getTicket( encTicketPartModifier, serverPrincipal, serverKey );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setServerName( getPrincipalName( "ldap/ldap.example.com@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+        modifier.setNonce( random.nextInt() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        kdcOptions.set( KdcOptions.RENEW );
+        modifier.setKdcOptions( kdcOptions );
+
+        long now = System.currentTimeMillis();
+
+        KerberosTime requestedEndTime = new KerberosTime( now + KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        RequestBody requestBody = modifier.getRequestBody();
+        KdcRequest message = getKdcRequest( tgt, requestBody );
+
+        handler.messageReceived( session, message );
+
+        ErrorMessage error = ( ErrorMessage ) session.getMessage();
+        assertEquals( "Ticket expired", 32, error.getErrorCode() );
+    }
+
+
+    /**
+     * Tests when a renewable ticket is presented for renewal, that if the RENEW
+     * flag is NOT set, the ticket is renewed for the endtime of the presented
+     * ticket, as though it were a TGT.
+     *
+     * @throws Exception
+     */
+    public void testRenewableTicketNoRenew() throws Exception
+    {
+        long now = System.currentTimeMillis();
+
+        // Get the mutable ticket part.
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        EncTicketPartModifier encTicketPartModifier = getTicketArchetype( clientPrincipal );
+
+        // Make changes to test.
+        encTicketPartModifier.setFlag( TicketFlag.RENEWABLE );
+        encTicketPartModifier.setStartTime( new KerberosTime( now - KerberosTime.DAY / 2 ) );
+        encTicketPartModifier.setEndTime( new KerberosTime( now + KerberosTime.DAY / 2 ) );
+
+        // Seal the ticket for the server.
+        KerberosPrincipal serverPrincipal = new KerberosPrincipal( "ldap/ldap.example.com@EXAMPLE.COM" );
+        String passPhrase = "randomKey";
+        EncryptionKey serverKey = getEncryptionKey( serverPrincipal, passPhrase );
+        Ticket tgt = getTicket( encTicketPartModifier, serverPrincipal, serverKey );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setServerName( getPrincipalName( "ldap/ldap.example.com@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+        modifier.setNonce( random.nextInt() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        modifier.setKdcOptions( kdcOptions );
+
+        KerberosTime requestedEndTime = new KerberosTime( now + KerberosTime.DAY / 2 );
+        modifier.setTill( requestedEndTime );
+
+        RequestBody requestBody = modifier.getRequestBody();
+        KdcRequest message = getKdcRequest( tgt, requestBody );
+
+        handler.messageReceived( session, message );
+
+        TicketGrantReply reply = ( TicketGrantReply ) session.getMessage();
+
+        KerberosTime expectedEndTime = tgt.getEncTicketPart().getEndTime();
+        boolean isClose = Math.abs( reply.getEndTime().getTime() - expectedEndTime.getTime() ) < 5000;
+        assertTrue( "Expected renew till time", isClose );
+    }
+
+
+    /**
+     * Tests when a renewable ticket is presented for renewal, that if the RENEW
+     * flag is set, the ticket is renewed for the lifetime of the presented ticket.
+     *
+     * @throws Exception
+     */
+    public void testRenewableTicketRenewal() throws Exception
+    {
+        long now = System.currentTimeMillis();
+
+        // Get the mutable ticket part.
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        EncTicketPartModifier encTicketPartModifier = getTicketArchetype( clientPrincipal );
+
+        // Make changes to test.
+        encTicketPartModifier.setFlag( TicketFlag.RENEWABLE );
+        encTicketPartModifier.setStartTime( new KerberosTime( now - KerberosTime.DAY / 2 ) );
+        encTicketPartModifier.setEndTime( new KerberosTime( now + KerberosTime.DAY / 2 ) );
+
+        // Seal the ticket for the server.
+        KerberosPrincipal serverPrincipal = new KerberosPrincipal( "ldap/ldap.example.com@EXAMPLE.COM" );
+        String passPhrase = "randomKey";
+        EncryptionKey serverKey = getEncryptionKey( serverPrincipal, passPhrase );
+        Ticket tgt = getTicket( encTicketPartModifier, serverPrincipal, serverKey );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setServerName( getPrincipalName( "ldap/ldap.example.com@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+        modifier.setNonce( random.nextInt() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        kdcOptions.set( KdcOptions.RENEW );
+        modifier.setKdcOptions( kdcOptions );
+
+        KerberosTime requestedEndTime = new KerberosTime( now + KerberosTime.DAY / 2 );
+        modifier.setTill( requestedEndTime );
+
+        RequestBody requestBody = modifier.getRequestBody();
+        KdcRequest message = getKdcRequest( tgt, requestBody );
+
+        handler.messageReceived( session, message );
+
+        TicketGrantReply reply = ( TicketGrantReply ) session.getMessage();
+
+        KerberosTime expectedEndTime = new KerberosTime( now + KerberosTime.DAY );
+        boolean isClose = Math.abs( reply.getEndTime().getTime() - expectedEndTime.getTime() ) < 5000;
+        assertTrue( "Expected renew till time", isClose );
+    }
+
+
+    /**
+     * Test when an unsupported encryption type is requested, that the request is
+     * rejected with the correct error message.
+     * 
+     * "If the server cannot accommodate any encryption type requested by the
+     * client, an error message with code KDC_ERR_ETYPE_NOSUPP is returned."
+     * 
+     * @throws Exception 
+     */
+    public void testEncryptionTypeNoSupport() throws Exception
+    {
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+
+        Set<EncryptionType> encryptionTypes = new HashSet<EncryptionType>();
+        encryptionTypes.add( EncryptionType.DES3_CBC_MD5 );
+
+        modifier.setEType( encryptionTypes );
+
+        modifier.setKdcOptions( new KdcOptions() );
+
+        long now = System.currentTimeMillis();
+
+        KerberosTime requestedEndTime = new KerberosTime( now + KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        KdcRequest message = new KdcRequest( 5, KerberosMessageType.TGS_REQ, null, modifier.getRequestBody() );
+
+        handler.messageReceived( session, message );
+
+        ErrorMessage error = ( ErrorMessage ) session.getMessage();
+        assertEquals( "KDC has no support for encryption type", 14, error.getErrorCode() );
+    }
+
+
+    /**
+     * Tests that when a server principal is not configured with Kerberos keys that
+     * the correct error message is returned.
+     * 
+     * @throws Exception 
+     */
+    public void testServerNullKey() throws Exception
+    {
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        KerberosPrincipal serverPrincipal = new KerberosPrincipal( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" );
+        String serverPassword = "randomKey";
+
+        Ticket tgt = getTgt( clientPrincipal, serverPrincipal, serverPassword );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setServerName( getPrincipalName( "tquist" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+        modifier.setNonce( random.nextInt() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        modifier.setKdcOptions( kdcOptions );
+
+        long currentTime = System.currentTimeMillis();
+
+        KerberosTime requestedEndTime = new KerberosTime( currentTime + KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        RequestBody requestBody = modifier.getRequestBody();
+
+        KdcRequest message = getKdcRequest( tgt, requestBody );
+
+        handler.messageReceived( session, message );
+
+        ErrorMessage error = ( ErrorMessage ) session.getMessage();
+        assertEquals( "The client or server has a null key", 9, error.getErrorCode() );
+    }
+
+
+    /**
+     * Tests when the starttime is absent and the POSTDATED option has not been
+     * specified, that the starttime of the ticket is set to the authentication
+     * server's current time.
+     * 
+     * "If the requested starttime is absent, indicates a time in the past,
+     * or is within the window of acceptable clock skew for the KDC and the
+     * POSTDATE option has not been specified, then the starttime of the
+     * ticket is set to the authentication server's current time."
+     * 
+     * @throws Exception 
+     */
+    public void testStartTimeAbsentNoPostdate() throws Exception
+    {
+        // Get the mutable ticket part.
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        EncTicketPartModifier encTicketPartModifier = getTicketArchetype( clientPrincipal );
+
+        // Make changes to test.
+
+        // Seal the ticket for the server.
+        KerberosPrincipal serverPrincipal = new KerberosPrincipal( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" );
+        String passPhrase = "randomKey";
+        EncryptionKey serverKey = getEncryptionKey( serverPrincipal, passPhrase );
+        Ticket tgt = getTicket( encTicketPartModifier, serverPrincipal, serverKey );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setServerName( getPrincipalName( "ldap/ldap.example.com@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+        modifier.setNonce( random.nextInt() );
+
+        modifier.setKdcOptions( new KdcOptions() );
+
+        long now = System.currentTimeMillis();
+
+        KerberosTime requestedEndTime = new KerberosTime( now + KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        RequestBody requestBody = modifier.getRequestBody();
+        KdcRequest message = getKdcRequest( tgt, requestBody );
+
+        handler.messageReceived( session, message );
+
+        TicketGrantReply reply = ( TicketGrantReply ) session.getMessage();
+
+        KerberosTime expectedStartTime = new KerberosTime( now );
+        boolean isClose = reply.getStartTime() == null
+            || Math.abs( reply.getStartTime().getTime() - expectedStartTime.getTime() ) < 5000;
+        assertTrue( "Expected start time", isClose );
+    }
+
+
+    /**
+     * Tests when the starttime indicates a time in the past and the POSTDATED option
+     * has not been specified, that the starttime of the ticket is set to the
+     * authentication server's current time.
+     * 
+     * "If the requested starttime is absent, indicates a time in the past,
+     * or is within the window of acceptable clock skew for the KDC and the
+     * POSTDATE option has not been specified, then the starttime of the
+     * ticket is set to the authentication server's current time."
+     * 
+     * @throws Exception 
+     */
+    public void testStartTimeInThePastNoPostdate() throws Exception
+    {
+        // Get the mutable ticket part.
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        EncTicketPartModifier encTicketPartModifier = getTicketArchetype( clientPrincipal );
+
+        // Make changes to test.
+
+        // Seal the ticket for the server.
+        KerberosPrincipal serverPrincipal = new KerberosPrincipal( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" );
+        String passPhrase = "randomKey";
+        EncryptionKey serverKey = getEncryptionKey( serverPrincipal, passPhrase );
+        Ticket tgt = getTicket( encTicketPartModifier, serverPrincipal, serverKey );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setServerName( getPrincipalName( "ldap/ldap.example.com@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+        modifier.setNonce( random.nextInt() );
+
+        modifier.setKdcOptions( new KdcOptions() );
+
+        long now = System.currentTimeMillis();
+
+        KerberosTime requestedStartTime = new KerberosTime( now + -1 * KerberosTime.DAY );
+        modifier.setFrom( requestedStartTime );
+
+        KerberosTime requestedEndTime = new KerberosTime( now + KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        RequestBody requestBody = modifier.getRequestBody();
+        KdcRequest message = getKdcRequest( tgt, requestBody );
+
+        handler.messageReceived( session, message );
+
+        TicketGrantReply reply = ( TicketGrantReply ) session.getMessage();
+
+        KerberosTime expectedStartTime = new KerberosTime( now );
+        boolean isClose = reply.getStartTime() == null
+            || Math.abs( reply.getStartTime().getTime() - expectedStartTime.getTime() ) < 5000;
+        assertTrue( "Expected start time", isClose );
+    }
+
+
+    /**
+     * Tests when the starttime is within the window of acceptable clock skew for
+     * the KDC and the POSTDATED option has not been specified, that the starttime
+     * of the ticket is set to the authentication server's current time.
+     * 
+     * "If the requested starttime is absent, indicates a time in the past,
+     * or is within the window of acceptable clock skew for the KDC and the
+     * POSTDATE option has not been specified, then the starttime of the
+     * ticket is set to the authentication server's current time."
+     * 
+     * @throws Exception 
+     */
+    public void testStartTimeAcceptableClockSkewNoPostdate() throws Exception
+    {
+        // Get the mutable ticket part.
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        EncTicketPartModifier encTicketPartModifier = getTicketArchetype( clientPrincipal );
+
+        // Make changes to test.
+
+        // Seal the ticket for the server.
+        KerberosPrincipal serverPrincipal = new KerberosPrincipal( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" );
+        String passPhrase = "randomKey";
+        EncryptionKey serverKey = getEncryptionKey( serverPrincipal, passPhrase );
+        Ticket tgt = getTicket( encTicketPartModifier, serverPrincipal, serverKey );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setServerName( getPrincipalName( "ldap/ldap.example.com@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+        modifier.setNonce( random.nextInt() );
+
+        modifier.setKdcOptions( new KdcOptions() );
+
+        long now = System.currentTimeMillis();
+
+        KerberosTime requestedStartTime = new KerberosTime( now );
+        modifier.setFrom( requestedStartTime );
+
+        KerberosTime requestedEndTime = new KerberosTime( now + KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        RequestBody requestBody = modifier.getRequestBody();
+        KdcRequest message = getKdcRequest( tgt, requestBody );
+
+        handler.messageReceived( session, message );
+
+        TicketGrantReply reply = ( TicketGrantReply ) session.getMessage();
+
+        KerberosTime expectedStartTime = new KerberosTime( now );
+        boolean isClose = reply.getStartTime() == null
+            || Math.abs( reply.getStartTime().getTime() - expectedStartTime.getTime() ) < 5000;
+        assertTrue( "Expected start time", isClose );
+    }
+
+
+    /**
+     * Tests when a start time is after an end time that the request is rejected with the
+     * correct error message.
+     * 
+     * "If the requested expiration time minus the starttime (as determined above)
+     * is less than a site-determined minimum lifetime, an error message with code
+     * KDC_ERR_NEVER_VALID is returned."
+     *
+     * @throws Exception
+     */
+    public void testStartTimeOrderNeverValid() throws Exception
+    {
+        // Get the mutable ticket part.
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        EncTicketPartModifier encTicketPartModifier = getTicketArchetype( clientPrincipal );
+
+        // Make changes to test.
+        encTicketPartModifier.setFlag( TicketFlag.MAY_POSTDATE );
+
+        // Seal the ticket for the server.
+        KerberosPrincipal serverPrincipal = new KerberosPrincipal( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" );
+        String passPhrase = "randomKey";
+        EncryptionKey serverKey = getEncryptionKey( serverPrincipal, passPhrase );
+        Ticket tgt = getTicket( encTicketPartModifier, serverPrincipal, serverKey );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setServerName( getPrincipalName( "ldap/ldap.example.com@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+        modifier.setNonce( random.nextInt() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        kdcOptions.set( KdcOptions.POSTDATED );
+        modifier.setKdcOptions( kdcOptions );
+
+        long now = System.currentTimeMillis();
+
+        KerberosTime requestedStartTime = new KerberosTime( now + KerberosTime.DAY );
+        modifier.setFrom( requestedStartTime );
+
+        KerberosTime requestedEndTime = new KerberosTime( now );
+        modifier.setTill( requestedEndTime );
+
+        RequestBody requestBody = modifier.getRequestBody();
+        KdcRequest message = getKdcRequest( tgt, requestBody );
+
+        handler.messageReceived( session, message );
+
+        ErrorMessage error = ( ErrorMessage ) session.getMessage();
+        assertEquals( "Requested start time is later than end time", 11, error.getErrorCode() );
+    }
+
+
+    /**
+     * Tests when the absolute value of the difference between the start time is
+     * and the end time is less than a configured minimum, that the request is
+     * rejected with the correct error message.
+     * 
+     * "If the requested expiration time minus the starttime (as determined above)
+     * is less than a site-determined minimum lifetime, an error message with code
+     * KDC_ERR_NEVER_VALID is returned."
+     *
+     * @throws Exception
+     */
+    public void testStartTimeMinimumNeverValid() throws Exception
+    {
+        // Get the mutable ticket part.
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        EncTicketPartModifier encTicketPartModifier = getTicketArchetype( clientPrincipal );
+
+        // Make changes to test.
+        encTicketPartModifier.setFlag( TicketFlag.MAY_POSTDATE );
+
+        // Seal the ticket for the server.
+        KerberosPrincipal serverPrincipal = new KerberosPrincipal( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" );
+        String passPhrase = "randomKey";
+        EncryptionKey serverKey = getEncryptionKey( serverPrincipal, passPhrase );
+        Ticket tgt = getTicket( encTicketPartModifier, serverPrincipal, serverKey );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setServerName( getPrincipalName( "ldap/ldap.example.com@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+        modifier.setNonce( random.nextInt() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        modifier.setKdcOptions( kdcOptions );
+
+        long now = System.currentTimeMillis();
+
+        KerberosTime requestedStartTime = new KerberosTime( now );
+        modifier.setFrom( requestedStartTime );
+
+        KerberosTime requestedEndTime = new KerberosTime( now + 4 * KerberosTime.MINUTE );
+        modifier.setTill( requestedEndTime );
+
+        RequestBody requestBody = modifier.getRequestBody();
+        KdcRequest message = getKdcRequest( tgt, requestBody );
+
+        handler.messageReceived( session, message );
+
+        ErrorMessage error = ( ErrorMessage ) session.getMessage();
+        assertEquals( "Requested start time is later than end time", 11, error.getErrorCode() );
+    }
+
+
+    /**
+     * Tests when a valid starttime is specified but the POSTDATE flag is not set,
+     * that the request is rejected with the correct error message.
+     * 
+     * "If it indicates a time in the future beyond the acceptable clock skew, but
+     * the POSTDATED option has not been specified, then the error
+     * KDC_ERR_CANNOT_POSTDATE is returned."
+     * 
+     * @throws Exception 
+     */
+    public void testStartTimeNoPostdated() throws Exception
+    {
+        // Get the mutable ticket part.
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        EncTicketPartModifier encTicketPartModifier = getTicketArchetype( clientPrincipal );
+
+        // Make changes to test.
+        encTicketPartModifier.setFlag( TicketFlag.MAY_POSTDATE );
+
+        // Seal the ticket for the server.
+        KerberosPrincipal serverPrincipal = new KerberosPrincipal( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" );
+        String passPhrase = "randomKey";
+        EncryptionKey serverKey = getEncryptionKey( serverPrincipal, passPhrase );
+        Ticket tgt = getTicket( encTicketPartModifier, serverPrincipal, serverKey );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setServerName( getPrincipalName( "ldap/ldap.example.com@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+        modifier.setNonce( random.nextInt() );
+
+        modifier.setKdcOptions( new KdcOptions() );
+
+        long now = System.currentTimeMillis();
+
+        KerberosTime requestedStartTime = new KerberosTime( now + 10 * KerberosTime.MINUTE );
+        modifier.setFrom( requestedStartTime );
+
+        KerberosTime requestedEndTime = new KerberosTime( now + KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        RequestBody requestBody = modifier.getRequestBody();
+        KdcRequest message = getKdcRequest( tgt, requestBody );
+
+        handler.messageReceived( session, message );
+
+        ErrorMessage error = ( ErrorMessage ) session.getMessage();
+        assertEquals( "Ticket not eligible for postdating", 10, error.getErrorCode() );
+    }
+
+
+    /**
+     * Tests that a user-specified start time is honored when that start time does not
+     * violate policy.
+     * 
+     * "Otherwise the requested starttime is checked against the policy of the local
+     * realm (the administrator might decide to prohibit certain types or ranges of
+     * postdated tickets), and if the ticket's starttime is acceptable, it is set as
+     * requested, and the INVALID flag is set in the new ticket.  The postdated
+     * ticket MUST be validated before use by presenting it to the KDC after the
+     * starttime has been reached."
+     * 
+     * "If the new ticket is postdated (the starttime is in the future), its
+     * INVALID flag will also be set."
+     * 
+     * "The flags field of the new ticket will have the following options set
+     * if they have been requested and if the policy of the local realm
+     * allows:  FORWARDABLE, MAY-POSTDATE, POSTDATED, PROXIABLE, RENEWABLE."
+     * 
+     * @throws Exception
+     */
+    public void testSpecificStartTime() throws Exception
+    {
+        long now = System.currentTimeMillis();
+
+        // Get the mutable ticket part.
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        EncTicketPartModifier encTicketPartModifier = getTicketArchetype( clientPrincipal );
+
+        // Make changes to test.
+        encTicketPartModifier.setFlag( TicketFlag.MAY_POSTDATE );
+        // Service ticket end time will be limited by TGT end time.
+        encTicketPartModifier.setEndTime( new KerberosTime( now + 3 * KerberosTime.DAY ) );
+
+        // Seal the ticket for the server.
+        KerberosPrincipal serverPrincipal = new KerberosPrincipal( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" );
+        String passPhrase = "randomKey";
+        EncryptionKey serverKey = getEncryptionKey( serverPrincipal, passPhrase );
+        Ticket tgt = getTicket( encTicketPartModifier, serverPrincipal, serverKey );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setServerName( getPrincipalName( "hnelson" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+        modifier.setNonce( random.nextInt() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        kdcOptions.set( KdcOptions.POSTDATED );
+        modifier.setKdcOptions( kdcOptions );
+
+        KerberosTime requestedStartTime = new KerberosTime( now + KerberosTime.DAY );
+        modifier.setFrom( requestedStartTime );
+
+        KerberosTime requestedEndTime = new KerberosTime( now + 2 * KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        RequestBody requestBody = modifier.getRequestBody();
+        KdcRequest message = getKdcRequest( tgt, requestBody );
+
+        handler.messageReceived( session, message );
+
+        TicketGrantReply reply = ( TicketGrantReply ) session.getMessage();
+
+        assertTrue( "Requested start time", requestedStartTime.equals( reply.getStartTime() ) );
+        assertTrue( "Requested end time", requestedEndTime.equals( reply.getEndTime() ) );
+        assertTrue( "POSTDATED flag", reply.getFlags().isPostdated() );
+        assertTrue( "INVALID flag", reply.getFlags().isInvalid() );
+
+        assertTrue( "Requested start time", requestedStartTime.equals( reply.getTicket().getEncTicketPart().getStartTime() ) );
+        assertTrue( "Requested end time", requestedEndTime.equals( reply.getEndTime() ) );
+        assertTrue( "POSTDATED flag", reply.getTicket().getEncTicketPart().getFlags().isPostdated() );
+        assertTrue( "INVALID flag", reply.getTicket().getEncTicketPart().getFlags().isInvalid() );
+    }
+
+
+    /**
+     * Tests when pre-authentication used during initial authentication, that the flag
+     * is carried forward to derivative tickets.
+     *
+     * @throws Exception
+     */
+    public void testPreAuthenticationFlag() throws Exception
+    {
+        // Get the mutable ticket part.
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        EncTicketPartModifier encTicketPartModifier = getTicketArchetype( clientPrincipal );
+
+        // Make changes to test.
+        encTicketPartModifier.setFlag( TicketFlag.PRE_AUTHENT );
+
+        // Seal the ticket for the server.
+        KerberosPrincipal serverPrincipal = new KerberosPrincipal( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" );
+        String passPhrase = "randomKey";
+        EncryptionKey serverKey = getEncryptionKey( serverPrincipal, passPhrase );
+        Ticket tgt = getTicket( encTicketPartModifier, serverPrincipal, serverKey );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setServerName( getPrincipalName( "hnelson" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+        modifier.setNonce( random.nextInt() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        modifier.setKdcOptions( kdcOptions );
+
+        long now = System.currentTimeMillis();
+
+        KerberosTime requestedEndTime = new KerberosTime( now + KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        RequestBody requestBody = modifier.getRequestBody();
+        KdcRequest message = getKdcRequest( tgt, requestBody );
+
+        handler.messageReceived( session, message );
+
+        TicketGrantReply reply = ( TicketGrantReply ) session.getMessage();
+
+        assertTrue( "PRE_AUTHENT flag", reply.getTicket().getEncTicketPart().getFlags().isPreAuth() );
+    }
+
+
+    /**
+     * Tests that a user-specified end time is honored when that end time does not
+     * violate policy.
+     * 
+     * "The expiration time of the ticket will be set to the earlier of the
+     * requested endtime and a time determined by local policy, possibly by
+     * using realm- or principal-specific factors."
+     *
+     * @throws Exception
+     */
+    public void testSpecificEndTime() throws Exception
+    {
+        // Get the mutable ticket part.
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        EncTicketPartModifier encTicketPartModifier = getTicketArchetype( clientPrincipal );
+
+        // Make changes to test.
+
+        // Seal the ticket for the server.
+        KerberosPrincipal serverPrincipal = new KerberosPrincipal( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" );
+        String passPhrase = "randomKey";
+        EncryptionKey serverKey = getEncryptionKey( serverPrincipal, passPhrase );
+        Ticket tgt = getTicket( encTicketPartModifier, serverPrincipal, serverKey );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setServerName( getPrincipalName( "hnelson" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+        modifier.setNonce( random.nextInt() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        modifier.setKdcOptions( kdcOptions );
+
+        long now = System.currentTimeMillis();
+
+        KerberosTime requestedEndTime = new KerberosTime( now + KerberosTime.DAY / 2 );
+        modifier.setTill( requestedEndTime );
+
+        RequestBody requestBody = modifier.getRequestBody();
+        KdcRequest message = getKdcRequest( tgt, requestBody );
+
+        handler.messageReceived( session, message );
+
+        TicketGrantReply reply = ( TicketGrantReply ) session.getMessage();
+
+        assertTrue( "Requested end time", requestedEndTime.equals( reply.getEndTime() ) );
+    }
+
+
+    /**
+     * Tests when an end time is requested that exceeds the maximum end time as 
+     * configured in policy that the maximum allowable end time is returned instead
+     * of the requested end time.
+     * 
+     * "The expiration time of the ticket will be set to the earlier of the
+     * requested endtime and a time determined by local policy, possibly by
+     * using realm- or principal-specific factors."
+     *
+     * @throws Exception
+     */
+    public void testEndTimeExceedsMaximumAllowable() throws Exception
+    {
+        // Get the mutable ticket part.
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        EncTicketPartModifier encTicketPartModifier = getTicketArchetype( clientPrincipal );
+
+        // Make changes to test.
+
+        // Seal the ticket for the server.
+        KerberosPrincipal serverPrincipal = new KerberosPrincipal( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" );
+        String passPhrase = "randomKey";
+        EncryptionKey serverKey = getEncryptionKey( serverPrincipal, passPhrase );
+        Ticket tgt = getTicket( encTicketPartModifier, serverPrincipal, serverKey );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setServerName( getPrincipalName( "hnelson" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+        modifier.setNonce( random.nextInt() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        modifier.setKdcOptions( kdcOptions );
+
+        long now = System.currentTimeMillis();
+
+        KerberosTime requestedEndTime = new KerberosTime( now + KerberosTime.WEEK );
+        modifier.setTill( requestedEndTime );
+
+        RequestBody requestBody = modifier.getRequestBody();
+        KdcRequest message = getKdcRequest( tgt, requestBody );
+
+        handler.messageReceived( session, message );
+
+        TicketGrantReply reply = ( TicketGrantReply ) session.getMessage();
+
+        KerberosTime expectedEndTime = new KerberosTime( now + KerberosTime.DAY );
+        boolean isClose = Math.abs( reply.getEndTime().getTime() - expectedEndTime.getTime() ) < 5000;
+        assertTrue( "Expected end time", isClose );
+    }
+
+
+    /**
+     * Tests that a requested zulu end time of the epoch ("19700101000000Z") results
+     * in the maximum endtime permitted according to KDC policy.  The zulu epoch is
+     * the same as '0' (zero) milliseconds in Java.
+     * 
+     * @throws Exception
+     */
+    public void testEpochEndTime() throws Exception
+    {
+        // Get the mutable ticket part.
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        EncTicketPartModifier encTicketPartModifier = getTicketArchetype( clientPrincipal );
+
+        // Make changes to test.
+
+        // Seal the ticket for the server.
+        KerberosPrincipal serverPrincipal = new KerberosPrincipal( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" );
+        String passPhrase = "randomKey";
+        EncryptionKey serverKey = getEncryptionKey( serverPrincipal, passPhrase );
+        Ticket tgt = getTicket( encTicketPartModifier, serverPrincipal, serverKey );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setServerName( getPrincipalName( "hnelson" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+        modifier.setNonce( random.nextInt() );
+
+        modifier.setKdcOptions( new KdcOptions() );
+
+        String epoch = "19700101000000Z";
+        KerberosTime requestedEndTime = KerberosTime.getTime( epoch );
+        modifier.setTill( requestedEndTime );
+
+        RequestBody requestBody = modifier.getRequestBody();
+
+        KdcRequest message = getKdcRequest( tgt, requestBody );
+
+        handler.messageReceived( session, message );
+
+        TicketGrantReply reply = ( TicketGrantReply ) session.getMessage();
+
+        long now = System.currentTimeMillis();
+        KerberosTime expectedEndTime = new KerberosTime( now + KerberosTime.DAY );
+        boolean isClose = Math.abs( reply.getEndTime().getTime() - expectedEndTime.getTime() ) < 5000;
+        assertTrue( "Expected end time", isClose );
+    }
+
+
+    /**
+     * Tests whether a renewable ticket will be accepted in lieu of a non-renewable
+     * ticket if the requested ticket expiration date cannot be satisfied by a
+     * non-renewable ticket (due to configuration constraints).
+     * 
+     * "If the requested expiration time for the ticket exceeds what was determined
+     * as above, and if the 'RENEWABLE-OK' option was requested, then the 'RENEWABLE'
+     * flag is set in the new ticket, and the renew-till value is set as if the
+     * 'RENEWABLE' option were requested (the field and option names are described
+     * fully in Section 5.4.1).
+     * 
+     * @throws Exception 
+     */
+    public void testRenewableOk() throws Exception
+    {
+        // Get the mutable ticket part.
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        EncTicketPartModifier encTicketPartModifier = getTicketArchetype( clientPrincipal );
+
+        // Make changes to test.
+        encTicketPartModifier.setFlag( TicketFlag.RENEWABLE );
+
+        // Seal the ticket for the server.
+        KerberosPrincipal serverPrincipal = new KerberosPrincipal( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" );
+        String passPhrase = "randomKey";
+        EncryptionKey serverKey = getEncryptionKey( serverPrincipal, passPhrase );
+        Ticket tgt = getTicket( encTicketPartModifier, serverPrincipal, serverKey );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setServerName( getPrincipalName( "ldap/ldap.example.com@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+        modifier.setNonce( random.nextInt() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        kdcOptions.set( KdcOptions.RENEWABLE_OK );
+        modifier.setKdcOptions( kdcOptions );
+
+        long now = System.currentTimeMillis();
+
+        KerberosTime requestedEndTime = new KerberosTime( now + KerberosTime.WEEK );
+        modifier.setTill( requestedEndTime );
+
+        RequestBody requestBody = modifier.getRequestBody();
+        KdcRequest message = getKdcRequest( tgt, requestBody );
+
+        handler.messageReceived( session, message );
+
+        TicketGrantReply reply = ( TicketGrantReply ) session.getMessage();
+
+        KerberosTime expectedEndTime = new KerberosTime( now + KerberosTime.DAY );
+        boolean isClose = Math.abs( reply.getEndTime().getTime() - expectedEndTime.getTime() ) < 5000;
+        assertTrue( "Expected end time", isClose );
+
+        assertTrue( "RENEWABLE flag", reply.getFlags().isRenewable() );
+        assertFalse( "INVALID flag", reply.getFlags().isInvalid() );
+
+        KerberosTime expectedRenewTillTime = new KerberosTime( now + KerberosTime.WEEK );
+        isClose = Math.abs( reply.getRenewTill().getTime() - expectedRenewTillTime.getTime() ) < 5000;
+        assertTrue( "Expected renew-till time", isClose );
+    }
+
+
+    /**
+     * Tests forwardable tickets.
+     * 
+     * "The flags field of the new ticket will have the following options set
+     * if they have been requested and if the policy of the local realm
+     * allows:  FORWARDABLE, MAY-POSTDATE, POSTDATED, PROXIABLE, RENEWABLE."
+     * 
+     * @throws Exception 
+     */
+    public void testForwardableTicket() throws Exception
+    {
+        // Get the mutable ticket part.
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        EncTicketPartModifier encTicketPartModifier = getTicketArchetype( clientPrincipal );
+
+        // Make changes to test.
+        encTicketPartModifier.setFlag( TicketFlag.FORWARDABLE );
+
+        // Seal the ticket for the server.
+        KerberosPrincipal serverPrincipal = new KerberosPrincipal( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" );
+        String passPhrase = "randomKey";
+        EncryptionKey serverKey = getEncryptionKey( serverPrincipal, passPhrase );
+        Ticket tgt = getTicket( encTicketPartModifier, serverPrincipal, serverKey );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setServerName( getPrincipalName( "ldap/ldap.example.com@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+        modifier.setNonce( random.nextInt() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        kdcOptions.set( KdcOptions.FORWARDABLE );
+        modifier.setKdcOptions( kdcOptions );
+
+        long now = System.currentTimeMillis();
+
+        KerberosTime requestedEndTime = new KerberosTime( now + 1 * KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        RequestBody requestBody = modifier.getRequestBody();
+        KdcRequest message = getKdcRequest( tgt, requestBody );
+
+        handler.messageReceived( session, message );
+
+        TicketGrantReply reply = ( TicketGrantReply ) session.getMessage();
+
+        assertTrue( "FORWARDABLE flag", reply.getFlags().isForwardable() );
+        assertFalse( "INVALID flag", reply.getFlags().isInvalid() );
+
+        assertTrue( "FORWARDABLE flag", reply.getTicket().getEncTicketPart().getFlags().isForwardable() );
+        assertFalse( "INVALID flag", reply.getTicket().getEncTicketPart().getFlags().isInvalid() );
+    }
+
+
+    /**
+     * Tests allow postdating of derivative tickets.
+     * 
+     * "The flags field of the new ticket will have the following options set
+     * if they have been requested and if the policy of the local realm
+     * allows:  FORWARDABLE, MAY-POSTDATE, POSTDATED, PROXIABLE, RENEWABLE."
+     * 
+     * @throws Exception 
+     */
+    public void testAllowPostdate() throws Exception
+    {
+        // Get the mutable ticket part.
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        EncTicketPartModifier encTicketPartModifier = getTicketArchetype( clientPrincipal );
+
+        // Make changes to test.
+        encTicketPartModifier.setFlag( TicketFlag.MAY_POSTDATE );
+
+        // Seal the ticket for the server.
+        KerberosPrincipal serverPrincipal = new KerberosPrincipal( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" );
+        String passPhrase = "randomKey";
+        EncryptionKey serverKey = getEncryptionKey( serverPrincipal, passPhrase );
+        Ticket tgt = getTicket( encTicketPartModifier, serverPrincipal, serverKey );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setServerName( getPrincipalName( "ldap/ldap.example.com@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+        modifier.setNonce( random.nextInt() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        kdcOptions.set( KdcOptions.ALLOW_POSTDATE );
+        modifier.setKdcOptions( kdcOptions );
+
+        long now = System.currentTimeMillis();
+
+        KerberosTime requestedEndTime = new KerberosTime( now + 1 * KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        RequestBody requestBody = modifier.getRequestBody();
+        KdcRequest message = getKdcRequest( tgt, requestBody );
+
+        handler.messageReceived( session, message );
+
+        TicketGrantReply reply = ( TicketGrantReply ) session.getMessage();
+
+        assertTrue( "MAY_POSTDATE flag", reply.getFlags().isMayPosdate() );
+        assertFalse( "INVALID flag", reply.getFlags().isInvalid() );
+
+        assertTrue( "MAY_POSTDATE flag", reply.getTicket().getEncTicketPart().getFlags().isMayPosdate() );
+        assertFalse( "INVALID flag", reply.getTicket().getEncTicketPart().getFlags().isInvalid() );
+    }
+
+
+    /**
+     * Tests proxiable tickets.
+     * 
+     * "The flags field of the new ticket will have the following options set
+     * if they have been requested and if the policy of the local realm
+     * allows:  FORWARDABLE, MAY-POSTDATE, POSTDATED, PROXIABLE, RENEWABLE."
+     * 
+     * @throws Exception 
+     */
+    public void testProxiableTicket() throws Exception
+    {
+        // Get the mutable ticket part.
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        EncTicketPartModifier encTicketPartModifier = getTicketArchetype( clientPrincipal );
+
+        // Make changes to test.
+        encTicketPartModifier.setFlag( TicketFlag.PROXIABLE );
+
+        // Seal the ticket for the server.
+        KerberosPrincipal serverPrincipal = new KerberosPrincipal( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" );
+        String passPhrase = "randomKey";
+        EncryptionKey serverKey = getEncryptionKey( serverPrincipal, passPhrase );
+        Ticket tgt = getTicket( encTicketPartModifier, serverPrincipal, serverKey );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setServerName( getPrincipalName( "ldap/ldap.example.com@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+        modifier.setNonce( random.nextInt() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        kdcOptions.set( KdcOptions.PROXIABLE );
+        modifier.setKdcOptions( kdcOptions );
+
+        long now = System.currentTimeMillis();
+
+        KerberosTime requestedEndTime = new KerberosTime( now + 1 * KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        RequestBody requestBody = modifier.getRequestBody();
+        KdcRequest message = getKdcRequest( tgt, requestBody );
+
+        handler.messageReceived( session, message );
+
+        TicketGrantReply reply = ( TicketGrantReply ) session.getMessage();
+
+        assertTrue( "PROXIABLE flag", reply.getFlags().isProxiable() );
+        assertFalse( "INVALID flag", reply.getFlags().isInvalid() );
+
+        assertTrue( "PROXIABLE flag", reply.getTicket().getEncTicketPart().getFlags().isProxiable() );
+        assertFalse( "INVALID flag", reply.getTicket().getEncTicketPart().getFlags().isInvalid() );
+    }
+
+
+    /**
+     * Tests that a user-specified renew-till time is honored when that renew-till
+     * time does not violate policy.
+     * 
+     * "If the RENEWABLE option has been requested or if the RENEWABLE-OK
+     * option has been set and a renewable ticket is to be issued, then the
+     * renew-till field MAY be set to the earliest of ... its requested value [or]
+     * the starttime of the ticket plus the maximum renewable lifetime
+     * set by the policy of the local realm."
+     * 
+     * @throws Exception 
+     */
+    public void testRenewableTicket() throws Exception
+    {
+        // Get the mutable ticket part.
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        EncTicketPartModifier encTicketPartModifier = getTicketArchetype( clientPrincipal );
+
+        // Make changes to test.
+        encTicketPartModifier.setFlag( TicketFlag.RENEWABLE );
+
+        // Seal the ticket for the server.
+        KerberosPrincipal serverPrincipal = new KerberosPrincipal( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" );
+        String passPhrase = "randomKey";
+        EncryptionKey serverKey = getEncryptionKey( serverPrincipal, passPhrase );
+        Ticket tgt = getTicket( encTicketPartModifier, serverPrincipal, serverKey );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setServerName( getPrincipalName( "ldap/ldap.example.com@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+        modifier.setNonce( random.nextInt() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        kdcOptions.set( KdcOptions.RENEWABLE );
+        modifier.setKdcOptions( kdcOptions );
+
+        long now = System.currentTimeMillis();
+
+        KerberosTime requestedEndTime = new KerberosTime( now + 1 * KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        KerberosTime requestedRenewTillTime = new KerberosTime( now + KerberosTime.WEEK / 2 );
+        modifier.setRtime( requestedRenewTillTime );
+
+        RequestBody requestBody = modifier.getRequestBody();
+        KdcRequest message = getKdcRequest( tgt, requestBody );
+
+        handler.messageReceived( session, message );
+
+        TicketGrantReply reply = ( TicketGrantReply ) session.getMessage();
+
+        assertTrue( "RENEWABLE flag", reply.getFlags().isRenewable() );
+        assertFalse( "INVALID flag", reply.getFlags().isInvalid() );
+
+        assertTrue( "RENEWABLE flag", reply.getTicket().getEncTicketPart().getFlags().isRenewable() );
+        assertFalse( "INVALID flag", reply.getTicket().getEncTicketPart().getFlags().isInvalid() );
+
+        assertTrue( "Requested renew-till time", requestedRenewTillTime.equals( reply.getRenewTill() ) );
+    }
+
+
+    /**
+     * Tests when a renew-till time is requested that exceeds the maximum renew-till
+     * time as configured in policy that the maximum allowable renew-till time is
+     * returned instead of the requested renew-till time.
+     * 
+     * "If the RENEWABLE option has been requested or if the RENEWABLE-OK
+     * option has been set and a renewable ticket is to be issued, then the
+     * renew-till field MAY be set to the earliest of ... its requested value [or]
+     * the starttime of the ticket plus the maximum renewable lifetime
+     * set by the policy of the local realm."
+     * 
+     * @throws Exception 
+     */
+    public void testRenewableTicketExceedsMaximumAllowable() throws Exception
+    {
+        // Get the mutable ticket part.
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        EncTicketPartModifier encTicketPartModifier = getTicketArchetype( clientPrincipal );
+
+        // Make changes to test.
+        encTicketPartModifier.setFlag( TicketFlag.RENEWABLE );
+
+        // Seal the ticket for the server.
+        KerberosPrincipal serverPrincipal = new KerberosPrincipal( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" );
+        String passPhrase = "randomKey";
+        EncryptionKey serverKey = getEncryptionKey( serverPrincipal, passPhrase );
+        Ticket tgt = getTicket( encTicketPartModifier, serverPrincipal, serverKey );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setServerName( getPrincipalName( "ldap/ldap.example.com@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+        modifier.setNonce( random.nextInt() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        kdcOptions.set( KdcOptions.RENEWABLE );
+        modifier.setKdcOptions( kdcOptions );
+
+        long now = System.currentTimeMillis();
+
+        KerberosTime requestedEndTime = new KerberosTime( now + 1 * KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        KerberosTime requestedRenewTillTime = new KerberosTime( now + 2 * KerberosTime.WEEK );
+        modifier.setRtime( requestedRenewTillTime );
+
+        RequestBody requestBody = modifier.getRequestBody();
+        KdcRequest message = getKdcRequest( tgt, requestBody );
+
+        handler.messageReceived( session, message );
+
+        TicketGrantReply reply = ( TicketGrantReply ) session.getMessage();
+
+        assertTrue( "RENEWABLE flag", reply.getFlags().isRenewable() );
+        assertFalse( "INVALID flag", reply.getFlags().isInvalid() );
+
+        assertTrue( "RENEWABLE flag", reply.getTicket().getEncTicketPart().getFlags().isRenewable() );
+        assertFalse( "INVALID flag", reply.getTicket().getEncTicketPart().getFlags().isInvalid() );
+
+        KerberosTime expectedRenewTillTime = new KerberosTime( now + KerberosTime.WEEK );
+        boolean isClose = Math.abs( reply.getRenewTill().getTime() - expectedRenewTillTime.getTime() ) < 5000;
+        assertTrue( "Expected renew-till time", isClose );
+    }
+
+
+    /**
+     * "The ciphertext part of the response in the KRB_TGS_REP message is encrypted
+     * in the sub-session key from the Authenticator, if present, or in the session
+     * key from the TGT."
+     *     
+     * @throws Exception 
+     */
+    public void testAuthenticatorSubKey() throws Exception
+    {
+        // Get the mutable ticket part.
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        EncTicketPartModifier encTicketPartModifier = getTicketArchetype( clientPrincipal );
+
+        // Make changes to test.
+
+        // Seal the ticket for the server.
+        KerberosPrincipal serverPrincipal = new KerberosPrincipal( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" );
+        String passPhrase = "randomKey";
+        EncryptionKey serverKey = getEncryptionKey( serverPrincipal, passPhrase );
+        Ticket tgt = getTicket( encTicketPartModifier, serverPrincipal, serverKey );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setServerName( getPrincipalName( "ldap/ldap.example.com@EXAMPLE.COM" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+        modifier.setNonce( random.nextInt() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        modifier.setKdcOptions( kdcOptions );
+
+        long now = System.currentTimeMillis();
+
+        KerberosTime requestedEndTime = new KerberosTime( now + 1 * KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        subSessionKey = RandomKeyFactory.getRandomKey( EncryptionType.DES_CBC_MD5 );
+
+        RequestBody requestBody = modifier.getRequestBody();
+        KdcRequest message = getKdcRequest( tgt, requestBody );
+
+        handler.messageReceived( session, message );
+
+        TicketGrantReply reply = ( TicketGrantReply ) session.getMessage();
+
+        assertFalse( "INVALID flag", reply.getFlags().isInvalid() );
+        assertFalse( "INVALID flag", reply.getTicket().getEncTicketPart().getFlags().isInvalid() );
+    }
+
+
+    /**
+     * Tests that the option RESERVED, which is bad for a TGS_REQ, is rejected
+     * with the correct error message.
+     *
+     * @throws Exception
+     */
+    public void testBadOptionReserved() throws Exception
+    {
+        KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+        KerberosPrincipal serverPrincipal = new KerberosPrincipal( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" );
+        String serverPassword = "randomKey";
+
+        Ticket tgt = getTgt( clientPrincipal, serverPrincipal, serverPassword );
+
+        RequestBodyModifier modifier = new RequestBodyModifier();
+        modifier.setServerName( getPrincipalName( "hnelson" ) );
+        modifier.setRealm( "EXAMPLE.COM" );
+        modifier.setEType( config.getEncryptionTypes() );
+        modifier.setNonce( random.nextInt() );
+
+        KdcOptions kdcOptions = new KdcOptions();
+        kdcOptions.set( KdcOptions.RESERVED );
+        modifier.setKdcOptions( kdcOptions );
+
+        long currentTime = System.currentTimeMillis();
+
+        KerberosTime requestedEndTime = new KerberosTime( currentTime + KerberosTime.DAY );
+        modifier.setTill( requestedEndTime );
+
+        RequestBody requestBody = modifier.getRequestBody();
+
+        KdcRequest message = getKdcRequest( tgt, requestBody );
+
+        handler.messageReceived( session, message );
+
+        ErrorMessage error = ( ErrorMessage ) session.getMessage();
+        assertEquals( "KDC cannot accommodate requested option", 13, error.getErrorCode() );
+    }
+}
diff --git a/old_trunk/protocol-ldap/pom.xml b/old_trunk/protocol-ldap/pom.xml
new file mode 100644
index 0000000..67f2add
--- /dev/null
+++ b/old_trunk/protocol-ldap/pom.xml
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you 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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.directory.server</groupId>
+    <artifactId>apacheds-parent</artifactId>
+    <version>1.5.4-SNAPSHOT</version>
+  </parent>
+  <artifactId>apacheds-protocol-ldap</artifactId>
+
+  <description>
+    The LDAPv3 protocol provider for ApacheDS
+  </description>
+
+  <name>ApacheDS Protocol Ldap</name>
+  <packaging>jar</packaging>  
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.directory.shared</groupId>
+      <artifactId>shared-asn1-codec</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.mina</groupId>
+      <artifactId>mina-filter-ssl</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-kerberos-shared</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-core</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-bootstrap-partition</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-protocol-shared</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-source-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>attach-sources</id>
+            <phase>verify</phase>
+            <goals>
+              <goal>jar</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+
+</project>
+
diff --git a/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/ExtendedOperationHandler.java b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/ExtendedOperationHandler.java
new file mode 100644
index 0000000..f63c493
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/ExtendedOperationHandler.java
@@ -0,0 +1,74 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.ldap;
+
+
+import java.util.Set;
+
+import org.apache.directory.shared.ldap.message.ExtendedRequest;
+import org.apache.mina.common.IoSession;
+
+
+/**
+ * An extension (hook) point that enables an implementor to provide his or her
+ * own LDAP 'Extended' operation.  
+ *
+ * @org.apache.xbean.XBean
+*
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ *
+ */
+public interface ExtendedOperationHandler
+{
+    /**
+     * Returns the EXTENSION_OID of the extended request this handler can handle.
+     */
+    String getOid();
+
+
+    /**
+     * The OIDs of the extensions supported by this handler.  This includes the 
+     * request as well as any responses associated with the request.  These OIDs 
+     * will be registered with the server to publish them as supportedExtensions.
+     * 
+     * @return the OIDs supported by this handler.
+     */
+    Set<String> getExtensionOids();
+
+
+    /**
+     * Handles the specified extended operation.
+     * 
+     * @param session the MINA session object related with current connection
+     * @param req the LDAP Extended operation request
+     * 
+     * @throws Exception if failed to handle the operation
+     */
+    void handleExtendedOperation( IoSession session, SessionRegistry registry, ExtendedRequest req ) throws Exception;
+
+
+    /**
+     * Sets the LDAP provider for this extendedOperation handler.
+     * 
+     * @param provider the ldap protocol provider 
+     */
+    void setLdapProvider( LdapServer provider );
+}
diff --git a/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/LdapServer.java b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/LdapServer.java
new file mode 100644
index 0000000..3dcf600
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/LdapServer.java
@@ -0,0 +1,1204 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.ldap;
+
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.security.KeyStore;
+import java.security.Provider;
+import java.security.Security;
+import java.util.*;
+
+import javax.naming.Context;
+import javax.naming.NamingException;
+import javax.naming.ldap.Control;
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.partition.PartitionNexus;
+import org.apache.directory.server.core.security.CoreKeyStoreSpi;
+import org.apache.directory.server.ldap.handlers.AbandonHandler;
+import org.apache.directory.server.ldap.handlers.AddHandler;
+import org.apache.directory.server.ldap.handlers.BindHandler;
+import org.apache.directory.server.ldap.handlers.CompareHandler;
+import org.apache.directory.server.ldap.handlers.DefaultAbandonHandler;
+import org.apache.directory.server.ldap.handlers.DefaultAddHandler;
+import org.apache.directory.server.ldap.handlers.DefaultBindHandler;
+import org.apache.directory.server.ldap.handlers.DefaultCompareHandler;
+import org.apache.directory.server.ldap.handlers.DefaultDeleteHandler;
+import org.apache.directory.server.ldap.handlers.DefaultExtendedHandler;
+import org.apache.directory.server.ldap.handlers.DefaultModifyDnHandler;
+import org.apache.directory.server.ldap.handlers.DefaultModifyHandler;
+import org.apache.directory.server.ldap.handlers.DefaultSearchHandler;
+import org.apache.directory.server.ldap.handlers.DefaultUnbindHandler;
+import org.apache.directory.server.ldap.handlers.DeleteHandler;
+import org.apache.directory.server.ldap.handlers.ExtendedHandler;
+import org.apache.directory.server.ldap.handlers.ModifyDnHandler;
+import org.apache.directory.server.ldap.handlers.ModifyHandler;
+import org.apache.directory.server.ldap.handlers.SearchHandler;
+import org.apache.directory.server.ldap.handlers.UnbindHandler;
+import org.apache.directory.server.ldap.handlers.bind.*;
+import org.apache.directory.server.ldap.handlers.ssl.LdapsInitializer;
+import org.apache.directory.server.protocol.shared.DirectoryBackedService;
+import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
+import org.apache.directory.shared.asn1.codec.Asn1CodecDecoder;
+import org.apache.directory.shared.asn1.codec.Asn1CodecEncoder;
+import org.apache.directory.shared.ldap.constants.SaslQoP;
+import org.apache.directory.shared.ldap.exception.LdapConfigurationException;
+import org.apache.directory.shared.ldap.message.AbandonRequest;
+import org.apache.directory.shared.ldap.message.AddRequest;
+import org.apache.directory.shared.ldap.message.BindRequest;
+import org.apache.directory.shared.ldap.message.CascadeControl;
+import org.apache.directory.shared.ldap.message.CompareRequest;
+import org.apache.directory.shared.ldap.message.DeleteRequest;
+import org.apache.directory.shared.ldap.message.EntryChangeControl;
+import org.apache.directory.shared.ldap.message.ExtendedRequest;
+import org.apache.directory.shared.ldap.message.ExtendedRequestImpl;
+import org.apache.directory.shared.ldap.message.ManageDsaITControl;
+import org.apache.directory.shared.ldap.message.MessageDecoder;
+import org.apache.directory.shared.ldap.message.MessageEncoder;
+import org.apache.directory.shared.ldap.message.ModifyDnRequest;
+import org.apache.directory.shared.ldap.message.ModifyRequest;
+import org.apache.directory.shared.ldap.message.MutableControl;
+import org.apache.directory.shared.ldap.message.PersistentSearchControl;
+import org.apache.directory.shared.ldap.message.Request;
+import org.apache.directory.shared.ldap.message.ResponseCarryingMessageException;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.message.ResultResponse;
+import org.apache.directory.shared.ldap.message.ResultResponseRequest;
+import org.apache.directory.shared.ldap.message.SearchRequest;
+import org.apache.directory.shared.ldap.message.SubentriesControl;
+import org.apache.directory.shared.ldap.message.UnbindRequest;
+import org.apache.directory.shared.ldap.message.extended.NoticeOfDisconnect;
+import org.apache.directory.shared.ldap.message.spi.BinaryAttributeDetector;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.mina.common.DefaultIoFilterChainBuilder;
+import org.apache.mina.common.IoFilterChain;
+import org.apache.mina.common.IoFilterChainBuilder;
+import org.apache.mina.common.IoHandler;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.common.ThreadModel;
+import org.apache.mina.common.WriteFuture;
+import org.apache.mina.filter.SSLFilter;
+import org.apache.mina.filter.codec.ProtocolCodecFactory;
+import org.apache.mina.filter.codec.ProtocolCodecFilter;
+import org.apache.mina.filter.codec.ProtocolDecoder;
+import org.apache.mina.filter.codec.ProtocolEncoder;
+import org.apache.mina.handler.demux.DemuxingIoHandler;
+import org.apache.mina.transport.socket.nio.SocketAcceptorConfig;
+import org.apache.mina.util.SessionLog;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * An LDAP protocol provider implementation which dynamically associates
+ * handlers.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ * @org.apache.xbean.XBean
+ */
+public class LdapServer extends DirectoryBackedService
+{
+    @SuppressWarnings( { "UnusedDeclaration" } )
+    private static final long serialVersionUID = 3757127143811666817L;
+
+    /** logger for this class */
+    private static final Logger LOG = LoggerFactory.getLogger( LdapServer.class.getName() );
+
+    /** The default maximum size limit. */
+    private static final int MAX_SIZE_LIMIT_DEFAULT = 100;
+
+    /** The default maximum time limit. */
+    private static final int MAX_TIME_LIMIT_DEFAULT = 10000;
+
+    /**
+     * The default service pid.
+     */
+    private static final String SERVICE_PID_DEFAULT = "org.apache.directory.server.ldap";
+
+    /**
+     * The default service name.
+     */
+    private static final String SERVICE_NAME_DEFAULT = "ApacheDS LDAP Service";
+
+    /** The default IP port. */
+    private static final int IP_PORT_DEFAULT = 389;
+
+    /** the constant service name of this ldap protocol provider **/
+    public static final String SERVICE_NAME = "ldap";
+
+    /** a set of supported controls */
+    private Set<String> supportedControls;
+
+    /** The maximum size limit. */
+    private int maxSizeLimit = MAX_SIZE_LIMIT_DEFAULT; // set to default value
+
+    /** The maximum time limit. */
+    private int maxTimeLimit = MAX_TIME_LIMIT_DEFAULT; // set to default value (milliseconds)
+
+    /** Whether LDAPS is enabled. */
+    private boolean enableLdaps;
+
+    /** Whether to allow anonymous access: allowed by default */
+    private boolean allowAnonymousAccess = true;
+    
+    /** 
+     * Whether or not confidentiality (TLS secured connection) is required: 
+     * disabled by default. 
+     */
+    private boolean confidentialityRequired;
+
+    /** The extended operation handlers. */
+    private final Collection<ExtendedOperationHandler> extendedOperationHandlers =
+        new ArrayList<ExtendedOperationHandler>();
+
+    /** The supported authentication mechanisms. */
+    private Map<String, MechanismHandler> saslMechanismHandlers =
+        new HashMap<String, MechanismHandler>();
+
+    /** The name of this host, validated during SASL negotiation. */
+    private String saslHost = "ldap.example.com";
+
+    /** The service principal, used by GSSAPI. */
+    private String saslPrincipal = "ldap/ldap.example.com@EXAMPLE.COM";
+
+    /** The quality of protection (QoP), used by DIGEST-MD5 and GSSAPI. */
+    private Set<String> saslQop;
+    private String      saslQopString;
+
+    /** The list of realms serviced by this host. */
+    private List<String> saslRealms;
+
+    private AbandonHandler abandonHandler;
+    private AddHandler addHandler;
+    private BindHandler bindHandler;
+    private CompareHandler compareHandler;
+    private DeleteHandler deleteHandler;
+    private ExtendedHandler extendedHandler;
+    private ModifyHandler modifyHandler;
+    private ModifyDnHandler modifyDnHandler;
+    private SearchHandler searchHandler;
+    private UnbindHandler unbindHandler;
+
+
+    private SessionRegistry registry;
+
+    /** the underlying provider codec factory */
+    private ProtocolCodecFactory codecFactory;
+
+    /** the MINA protocol handler */
+    private final LdapProtocolHandler handler = new LdapProtocolHandler();
+
+    /** tracks state of the server */
+    private boolean started;
+
+
+    /**
+     * Creates an LDAP protocol provider.
+     */
+    public LdapServer()
+    {
+        super.setIpPort( IP_PORT_DEFAULT );
+        super.setEnabled( true );
+        super.setServiceId( SERVICE_PID_DEFAULT );
+        super.setServiceName( SERVICE_NAME_DEFAULT );
+
+        saslQop = new HashSet<String>();
+        saslQop.add( SaslQoP.QOP_AUTH );
+        saslQop.add( SaslQoP.QOP_AUTH_INT );
+        saslQop.add( SaslQoP.QOP_AUTH_CONF );
+        saslQopString = SaslQoP.QOP_AUTH + ',' + SaslQoP.QOP_AUTH_INT + ',' + SaslQoP.QOP_AUTH_CONF;
+
+        saslRealms = new ArrayList<String>();
+        saslRealms.add( "example.com" );
+
+        this.supportedControls = new HashSet<String>();
+        this.supportedControls.add( PersistentSearchControl.CONTROL_OID );
+        this.supportedControls.add( EntryChangeControl.CONTROL_OID );
+        this.supportedControls.add( SubentriesControl.CONTROL_OID );
+        this.supportedControls.add( ManageDsaITControl.CONTROL_OID );
+        this.supportedControls.add( CascadeControl.CONTROL_OID );
+    }
+
+
+    /**
+     * Install the LDAP request handlers.
+     */
+    private void installDefaultHandlers()
+    {
+        if ( getAbandonHandler() == null )
+        {
+            setAbandonHandler( new DefaultAbandonHandler() );
+        }
+        
+        if ( getAddHandler() == null )
+        {
+            setAddHandler( new DefaultAddHandler() );
+        }
+        
+        if ( getBindHandler() == null )
+        {
+            DefaultBindHandler handler = new DefaultBindHandler();
+            handler.setSessionRegistry( registry );
+            handler.setSaslMechanismHandlers( saslMechanismHandlers );
+            setBindHandler( handler );
+        }
+        
+        if ( getCompareHandler() == null )
+        {
+            setCompareHandler( new DefaultCompareHandler() );
+        }
+        
+        if ( getDeleteHandler() == null )
+        {
+            setDeleteHandler( new DefaultDeleteHandler() );
+        }
+        
+        if ( getExtendedHandler() == null )
+        {
+            setExtendedHandler( new DefaultExtendedHandler() );
+        }
+        
+        if ( getModifyHandler() == null )
+        {
+            setModifyHandler( new DefaultModifyHandler() );
+        }
+        
+        if ( getModifyDnHandler() == null )
+        {
+            setModifyDnHandler( new DefaultModifyDnHandler() );
+        }
+        
+        if ( getSearchHandler() == null )
+        {
+            setSearchHandler( new DefaultSearchHandler() );
+        }
+        
+        if ( getUnbindHandler() == null )
+        {
+            setUnbindHandler( new DefaultUnbindHandler() );
+        }
+    }
+
+
+    /**
+     * @throws IOException if we cannot bind to the specified port
+     * @throws NamingException if the LDAP server cannot be started
+     */
+    public void start() throws NamingException, IOException
+    {
+        if ( ! isEnabled() )
+        {
+            return;
+        }
+
+        IoFilterChainBuilder chain;
+        
+        if ( isEnableLdaps() )
+        {
+            Provider provider = Security.getProvider( "SUN" );
+            LOG.debug( "provider = {}", provider );
+            CoreKeyStoreSpi coreKeyStoreSpi = new CoreKeyStoreSpi( getDirectoryService() );
+            KeyStore keyStore = new KeyStore( coreKeyStoreSpi, provider, "JKS" ) {};
+            try
+            {
+                keyStore.load( null, null );
+            }
+            catch ( Exception e )
+            {
+                // nothing really happens with this keystore
+            }
+            chain = LdapsInitializer.init( keyStore );
+        }
+        else
+        {
+            chain = new DefaultIoFilterChainBuilder();
+        }
+
+        /*
+         * The serveur is now initialized, we can
+         * install the default requests handlers, which need 
+         * access to the DirectoryServer instance.
+         */ 
+        installDefaultHandlers();      
+
+        startLDAP0( getIpPort(), chain );
+        
+        started = true;
+    }
+
+
+    public void stop()
+    {
+        try
+        {
+            // we should unbind the service before we begin sending the notice
+            // of disconnect so new connections are not formed while we process
+            List<WriteFuture> writeFutures = new ArrayList<WriteFuture>();
+
+            // If the socket has already been unbound as with a successful
+            // GracefulShutdownRequest then this will complain that the service
+            // is not bound - this is ok because the GracefulShutdown has already
+            // sent notices to to the existing active sessions
+            List<IoSession> sessions;
+
+            try
+            {
+                sessions = new ArrayList<IoSession>(
+                        getSocketAcceptor().getManagedSessions( new InetSocketAddress( getIpPort() ) ) );
+            }
+            catch ( IllegalArgumentException e )
+            {
+                LOG.warn( "Seems like the LDAP service (" + getIpPort() + ") has already been unbound." );
+                return;
+            }
+
+            getSocketAcceptor().unbind( new InetSocketAddress( getIpPort() ) );
+
+            if ( LOG.isInfoEnabled() )
+            {
+                LOG.info( "Unbind of an LDAP service (" + getIpPort() + ") is complete." );
+                LOG.info( "Sending notice of disconnect to existing clients sessions." );
+            }
+
+            // Send Notification of Disconnection messages to all connected clients.
+            if ( sessions != null )
+            {
+                for ( IoSession session:sessions )
+                {
+                    writeFutures.add( session.write( NoticeOfDisconnect.UNAVAILABLE ) );
+                }
+            }
+
+            // And close the connections when the NoDs are sent.
+            Iterator<IoSession> sessionIt = sessions.iterator();
+
+            for ( WriteFuture future:writeFutures )
+            {
+                future.join( 1000 );
+                sessionIt.next().close();
+            }
+        }
+        catch ( Exception e )
+        {
+            LOG.warn( "Failed to sent NoD.", e );
+        }
+    }
+
+
+    private void startLDAP0( int port, IoFilterChainBuilder chainBuilder )
+        throws NamingException
+    {
+        PartitionNexus nexus = getDirectoryService().getPartitionNexus();
+
+        for ( ExtendedOperationHandler h : extendedOperationHandlers )
+        {
+            extendedHandler.addHandler( h );
+            LOG.info( "Added Extended Request Handler: " + h.getOid() );
+            h.setLdapProvider( this );
+            nexus.registerSupportedExtensions( h.getExtensionOids() );
+        }
+
+        nexus.registerSupportedSaslMechanisms( saslMechanismHandlers.keySet() );
+
+        try
+        {
+            SocketAcceptorConfig acceptorCfg = new SocketAcceptorConfig();
+
+            // Disable the disconnection of the clients on unbind
+            acceptorCfg.setDisconnectOnUnbind( false );
+            acceptorCfg.setReuseAddress( true );
+            acceptorCfg.setFilterChainBuilder( chainBuilder );
+            acceptorCfg.setThreadModel( ThreadModel.MANUAL );
+
+            acceptorCfg.getSessionConfig().setTcpNoDelay( true );
+
+            getSocketAcceptor().bind( new InetSocketAddress( port ), getHandler(), acceptorCfg );
+            started = true;
+
+            if ( LOG.isInfoEnabled() )
+            {
+                LOG.info( "Successful bind of an LDAP Service (" + port + ") is complete." );
+            }
+        }
+        catch ( IOException e )
+        {
+            String msg = "Failed to bind an LDAP service (" + port + ") to the service registry.";
+            LdapConfigurationException lce = new LdapConfigurationException( msg );
+            lce.setRootCause( e );
+            LOG.error( msg, e );
+            throw lce;
+        }
+    }
+
+
+    public String getName()
+    {
+        return SERVICE_NAME;
+    }
+
+
+    public ProtocolCodecFactory getCodecFactory()
+    {
+        return codecFactory;
+    }
+
+
+    public IoHandler getHandler()
+    {
+        return handler;
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Configuration Methods
+    // ------------------------------------------------------------------------
+
+
+    /**
+     * Registeres the specified {@link ExtendedOperationHandler} to this
+     * protocol provider to provide a specific LDAP extended operation.
+     *
+     * @param eoh an extended operation handler
+     * @throws NamingException on failure to add the handler
+     */
+    public void addExtendedOperationHandler( ExtendedOperationHandler eoh ) throws NamingException
+    {
+        if ( started )
+        {
+            extendedHandler.addHandler( eoh );
+            eoh.setLdapProvider( this );
+            PartitionNexus nexus = getDirectoryService().getPartitionNexus();
+            nexus.registerSupportedExtensions( eoh.getExtensionOids() );
+        }
+        else
+        {
+            extendedOperationHandlers.add( eoh );
+        }
+    }
+
+
+    /**
+     * Deregisteres an {@link ExtendedOperationHandler} with the specified <tt>oid</tt>
+     * from this protocol provider.
+     *
+     * @param oid the numeric identifier for the extended operation associated with
+     * the handler to remove
+     */
+    public void removeExtendedOperationHandler( String oid )
+    {
+        if ( started )
+        {
+            extendedHandler.removeHandler( oid );
+
+            // need to do something like this to make this work right
+            //            PartitionNexus nexus = getDirectoryService().getPartitionNexus();
+            //            nexus.unregisterSupportedExtensions( eoh.getExtensionOids() );
+        }
+        else
+        {
+            ExtendedOperationHandler handler = null;
+            for ( ExtendedOperationHandler h : extendedOperationHandlers )
+            {
+                if ( h.getOid().equals( oid ) )
+                {
+                    handler = h;
+                    break;
+                }
+            }
+            extendedOperationHandlers.remove( handler );
+        }
+    }
+
+
+    /**
+     * Returns an {@link ExtendedOperationHandler} with the specified <tt>oid</tt>
+     * which is registered to this protocol provider.
+     *
+     * @param oid the oid of the extended request of associated with the extended
+     * request handler
+     * @return the exnteded operation handler
+     */
+    public ExtendedOperationHandler getExtendedOperationHandler( String oid )
+    {
+        if ( started )
+        {
+            return extendedHandler.getHandler( oid );
+        }
+        else
+        {
+            for ( ExtendedOperationHandler h : extendedOperationHandlers )
+            {
+                if ( h.getOid().equals( oid ) )
+                {
+                    return h;
+                }
+            }
+        }
+
+        return null;
+    }
+
+
+    /**
+     * Returns a {@link Map} of all registered OID-{@link ExtendedOperationHandler}
+     * pairs.
+     *
+     * @return map of all extended operation handlers
+     */
+    public Map<String,ExtendedOperationHandler> getExtendedOperationHandlerMap()
+    {
+        return extendedHandler.getHandlerMap();
+    }
+
+
+    /**
+     * Returns <tt>true</tt> if LDAPS is enabled.
+     *
+     * @return True if LDAPS is enabled.
+     */
+    public boolean isEnableLdaps()
+    {
+        return enableLdaps;
+    }
+
+
+    /**
+     * Sets if LDAPS is enabled or not.
+     *
+     * @param enableLdaps Whether LDAPS is enabled.
+     */
+    public void setEnableLdaps( boolean enableLdaps )
+    {
+        this.enableLdaps = enableLdaps;
+    }
+
+
+    /**
+     * Returns <code>true</code> if anonymous access is allowed.
+     *
+     * @return True if anonymous access is allowed.
+     */
+    public boolean isAllowAnonymousAccess()
+    {
+        return allowAnonymousAccess;
+    }
+
+
+    /**
+     * Sets whether to allow anonymous access or not.
+     *
+     * @param enableAnonymousAccess Set <code>true</code> to allow anonymous access.
+     */
+    public void setAllowAnonymousAccess( boolean enableAnonymousAccess )
+    {
+        this.allowAnonymousAccess = enableAnonymousAccess;
+    }
+
+
+    /**
+     * Sets the maximum size limit in number of entries to return for search.
+     *
+     * @param maxSizeLimit the maximum number of entries to return for search
+     */
+    public void setMaxSizeLimit( int maxSizeLimit )
+    {
+        this.maxSizeLimit = maxSizeLimit;
+    }
+
+
+    /**
+     * Returns the maximum size limit in number of entries to return for search.
+     *
+     * @return The maximum size limit.
+     */
+    public int getMaxSizeLimit()
+    {
+        return maxSizeLimit;
+    }
+
+
+    /**
+     * Sets the maximum time limit in miliseconds to conduct a search.
+     *
+     * @param maxTimeLimit the maximum length of time in milliseconds for search
+     */
+    public void setMaxTimeLimit( int maxTimeLimit )
+    {
+        this.maxTimeLimit = maxTimeLimit;
+    }
+
+
+    /**
+     * Returns the maximum time limit in milliseonds to conduct a search.
+     *
+     * @return The maximum time limit in milliseconds for search
+     */
+    public int getMaxTimeLimit()
+    {
+        return maxTimeLimit;
+    }
+
+
+    /**
+     * Gets the {@link ExtendedOperationHandler}s.
+     *
+     * @return A collection of {@link ExtendedOperationHandler}s.
+     */
+    public Collection<ExtendedOperationHandler> getExtendedOperationHandlers()
+    {
+        return new ArrayList<ExtendedOperationHandler>( extendedOperationHandlers );
+    }
+
+
+    /**
+     * Sets the {@link ExtendedOperationHandler}s.
+     *
+     * @org.apache.xbean.Property nestedType="org.apache.directory.server.ldap.ExtendedOperationHandler"
+     *
+     * @param handlers A collection of {@link ExtendedOperationHandler}s.
+     */
+    public void setExtendedOperationHandlers( Collection<ExtendedOperationHandler> handlers )
+    {
+        this.extendedOperationHandlers.clear();
+        this.extendedOperationHandlers.addAll( handlers );
+    }
+
+
+    /**
+     * Returns the FQDN of this SASL host, validated during SASL negotiation.
+     *
+     * @return The FQDN of this SASL host, validated during SASL negotiation.
+     */
+    public String getSaslHost()
+    {
+        return saslHost;
+    }
+
+
+    /**
+     * Sets the FQDN of this SASL host, validated during SASL negotiation.
+     *
+     * @param saslHost The FQDN of this SASL host, validated during SASL negotiation.
+     */
+    public void setSaslHost( String saslHost )
+    {
+        this.saslHost = saslHost;
+    }
+
+
+    /**
+     * Returns the Kerberos principal name for this LDAP service, used by GSSAPI.
+     *
+     * @return The Kerberos principal name for this LDAP service, used by GSSAPI.
+     */
+    public String getSaslPrincipal()
+    {
+        return saslPrincipal;
+    }
+
+
+    /**
+     * Sets the Kerberos principal name for this LDAP service, used by GSSAPI.
+     *
+     * @param saslPrincipal The Kerberos principal name for this LDAP service, used by GSSAPI.
+     */
+    public void setSaslPrincipal( String saslPrincipal )
+    {
+        this.saslPrincipal = saslPrincipal;
+    }
+
+
+    /**
+     * Returns the quality-of-protection, used by DIGEST-MD5 and GSSAPI.
+     *
+     * @return The quality-of-protection, used by DIGEST-MD5 and GSSAPI.
+     */
+    public String getSaslQopString()
+    {
+        return saslQopString;
+    }
+
+
+    /**
+     * Returns the Set of quality-of-protection, used by DIGEST-MD5 and GSSAPI.
+     *
+     * @return The quality-of-protection, used by DIGEST-MD5 and GSSAPI.
+     */
+    public Set<String> getSaslQop()
+    {
+        return saslQop;
+    }
+
+
+    /**
+     * Sets the desired quality-of-protection, used by DIGEST-MD5 and GSSAPI.
+     * 
+     * We build a string from this list, where QoP are comma delimited 
+     *
+     * @org.apache.xbean.Property nestedType="java.lang.String"
+     *
+     * @param saslQop The desired quality-of-protection, used by DIGEST-MD5 and GSSAPI.
+     */
+    public void setSaslQop( Set<String> saslQop )
+    {
+        StringBuilder qopList = new StringBuilder();
+        boolean isFirst = true;
+
+        for ( String qop:saslQop )
+        {
+            if ( isFirst )
+            {
+                isFirst = false;
+            }
+            else
+            {
+                qopList.append( ',' );
+            }
+            
+            qopList.append( qop );
+        }
+
+        this.saslQopString = qopList.toString();
+        this.saslQop = saslQop;
+    }
+
+
+    /**
+     * Returns the realms serviced by this SASL host, used by DIGEST-MD5 and GSSAPI.
+     *
+     * @return The realms serviced by this SASL host, used by DIGEST-MD5 and GSSAPI.
+     */
+    public List<String> getSaslRealms()
+    {
+        return saslRealms;
+    }
+
+
+    /**
+     * Sets the realms serviced by this SASL host, used by DIGEST-MD5 and GSSAPI.
+     *
+     * @org.apache.xbean.Property nestedType="java.lang.String"
+     *
+     * @param saslRealms The realms serviced by this SASL host, used by DIGEST-MD5 and GSSAPI.
+     */
+    public void setSaslRealms( List<String> saslRealms )
+    {
+        this.saslRealms = saslRealms;
+    }
+
+
+    /**
+     * @org.apache.xbean.Map flat="true" dups="replace" keyName="mech-name"
+     */
+    public Map<String, MechanismHandler> getSaslMechanismHandlers()
+    {
+        return saslMechanismHandlers;
+    }
+
+    public void setSaslMechanismHandlers( Map<String, MechanismHandler> saslMechanismHandlers )
+    {
+        this.saslMechanismHandlers = saslMechanismHandlers;
+    }
+
+
+
+    public MechanismHandler addSaslMechanismHandler( String mechanism, MechanismHandler handler )
+    {
+        return this.saslMechanismHandlers.put( mechanism, handler );
+    }
+
+
+    public MechanismHandler removeSaslMechanismHandler( String mechanism )
+    {
+        return this.saslMechanismHandlers.remove( mechanism );
+    }
+
+
+    public MechanismHandler getMechanismHandler( String mechanism )
+    {
+        return this.saslMechanismHandlers.get( mechanism );
+    }
+
+
+    public Set<String> getSupportedMechanisms()
+    {
+        return saslMechanismHandlers.keySet();
+    }
+
+
+    public void setDirectoryService( DirectoryService directoryService )
+    {
+        super.setDirectoryService( directoryService );
+        this.codecFactory = new ProtocolCodecFactoryImpl( directoryService );
+        Hashtable<String,Object> copy = new Hashtable<String,Object>();
+        copy.put( Context.PROVIDER_URL, "" );
+        copy.put( Context.INITIAL_CONTEXT_FACTORY, "org.apache.directory.server.core.jndi.CoreContextFactory" );
+        copy.put( DirectoryService.JNDI_KEY, directoryService );
+        this.registry = new SessionRegistry( this, copy );
+    }
+
+
+    public Set<String> getSupportedControls()
+    {
+        return supportedControls;
+    }
+
+
+    public void setSupportedControls( Set<String> supportedControls )
+    {
+        this.supportedControls = supportedControls;
+    }
+
+
+    public AbandonHandler getAbandonHandler()
+    {
+        return abandonHandler;
+    }
+
+
+    public void setAbandonHandler( AbandonHandler abandonHandler )
+    {
+        this.handler.removeMessageHandler( AbandonRequest.class );
+        this.abandonHandler = abandonHandler;
+        this.abandonHandler.setProtocolProvider( this );
+        //noinspection unchecked
+        this.handler.addMessageHandler( AbandonRequest.class, this.abandonHandler );
+    }
+
+
+    public AddHandler getAddHandler()
+    {
+        return addHandler;
+    }
+
+
+    public void setAddHandler( AddHandler addHandler )
+    {
+        this.handler.removeMessageHandler( AddRequest.class );
+        this.addHandler = addHandler;
+        this.addHandler.setProtocolProvider( this );
+        //noinspection unchecked
+        this.handler.addMessageHandler( AddRequest.class, this.addHandler );
+    }
+
+
+    public BindHandler getBindHandler()
+    {
+        return bindHandler;
+    }
+
+
+    public void setBindHandler( BindHandler bindHandler )
+    {
+        this.handler.removeMessageHandler( BindRequest.class );
+        this.bindHandler = bindHandler;
+        this.bindHandler.setProtocolProvider( this );
+        this.bindHandler.setDirectoryService( getDirectoryService() );
+        //noinspection unchecked
+        this.handler.addMessageHandler( BindRequest.class, this.bindHandler );
+    }
+
+
+    public CompareHandler getCompareHandler()
+    {
+        return compareHandler;
+    }
+
+
+    public void setCompareHandler( CompareHandler compareHandler )
+    {
+        this.handler.removeMessageHandler( CompareRequest.class );
+        this.compareHandler = compareHandler;
+        this.compareHandler.setProtocolProvider( this );
+        //noinspection unchecked
+        this.handler.addMessageHandler( CompareRequest.class, this.compareHandler );
+    }
+
+
+    public DeleteHandler getDeleteHandler()
+    {
+        return deleteHandler;
+    }
+
+
+    public void setDeleteHandler( DeleteHandler deleteHandler )
+    {
+        this.handler.removeMessageHandler( DeleteRequest.class );
+        this.deleteHandler = deleteHandler;
+        this.deleteHandler.setProtocolProvider( this );
+        //noinspection unchecked
+        this.handler.addMessageHandler( DeleteRequest.class, this.deleteHandler );
+    }
+
+
+    public ExtendedHandler getExtendedHandler()
+    {
+        return extendedHandler;
+    }
+
+
+    public void setExtendedHandler( ExtendedHandler extendedHandler )
+    {
+        this.handler.removeMessageHandler( ExtendedRequest.class );
+        this.extendedHandler = extendedHandler;
+        this.extendedHandler.setProtocolProvider( this );
+        //noinspection unchecked
+        this.handler.addMessageHandler( ExtendedRequest.class, this.extendedHandler );
+    }
+
+
+    public ModifyHandler getModifyHandler()
+    {
+        return modifyHandler;
+    }
+
+
+    public void setModifyHandler( ModifyHandler modifyHandler )
+    {
+        this.handler.removeMessageHandler( ModifyRequest.class );
+        this.modifyHandler = modifyHandler;
+        this.modifyHandler.setProtocolProvider( this );
+        //noinspection unchecked
+        this.handler.addMessageHandler( ModifyRequest.class, this.modifyHandler );
+    }
+
+
+    public ModifyDnHandler getModifyDnHandler()
+    {
+        return modifyDnHandler;
+    }
+
+
+    public void setModifyDnHandler( ModifyDnHandler modifyDnHandler )
+    {
+        this.handler.removeMessageHandler( ModifyDnRequest.class );
+        this.modifyDnHandler = modifyDnHandler;
+        this.modifyDnHandler.setProtocolProvider( this );
+        //noinspection unchecked
+        this.handler.addMessageHandler( ModifyDnRequest.class, this.modifyDnHandler );
+    }
+
+
+    public SearchHandler getSearchHandler()
+    {
+        return searchHandler;
+    }
+
+
+    public void setSearchHandler( SearchHandler searchHandler )
+    {
+        this.handler.removeMessageHandler( SearchRequest.class );
+        this.searchHandler = searchHandler;
+        this.searchHandler.setProtocolProvider( this );
+        //noinspection unchecked
+        this.handler.addMessageHandler( SearchRequest.class, this.searchHandler );
+    }
+
+
+    public UnbindHandler getUnbindHandler()
+    {
+        return unbindHandler;
+    }
+
+
+    public void setUnbindHandler( UnbindHandler unbindHandler )
+    {
+        this.handler.removeMessageHandler( UnbindRequest.class );
+        this.unbindHandler = unbindHandler;
+        this.unbindHandler.setProtocolProvider( this );
+        //noinspection unchecked
+        this.handler.addMessageHandler( UnbindRequest.class, this.unbindHandler );
+    }
+
+
+    public SessionRegistry getRegistry()
+    {
+        return registry;
+    }
+
+
+    public boolean isStarted()
+    {
+        return started;
+    }
+
+
+    public void setStarted( boolean started )
+    {
+        this.started = started;
+    }
+
+
+    /**
+     * Sets the mode for this LdapServer to accept requests with or without a
+     * TLS secured connection via either StartTLS extended operations or using
+     * LDAPS.
+     * 
+     * @param confidentialityRequired true to require confidentiality
+     */
+    public void setConfidentialityRequired( boolean confidentialityRequired ) 
+    {
+		this.confidentialityRequired = confidentialityRequired;
+	}
+
+
+    /**
+     * Gets whether or not TLS secured connections are required to perform 
+     * operations on this LdapServer.
+     * 
+     * @return true if TLS secured connections are required, false otherwise
+     */
+	public boolean isConfidentialityRequired() 
+	{
+		return confidentialityRequired;
+	}
+
+
+	/**
+     * A snickers based BER Decoder factory.
+     */
+    private static final class ProtocolCodecFactoryImpl implements ProtocolCodecFactory
+    {
+        final DirectoryService directoryService;
+
+
+        public ProtocolCodecFactoryImpl( DirectoryService directoryService )
+        {
+            this.directoryService = directoryService;
+        }
+
+
+        public ProtocolEncoder getEncoder()
+        {
+            return new Asn1CodecEncoder( new MessageEncoder() );
+        }
+
+
+        public ProtocolDecoder getDecoder()
+        {
+            return new Asn1CodecDecoder( new MessageDecoder( new BinaryAttributeDetector()
+            {
+                public boolean isBinary( String id )
+                {
+                    AttributeTypeRegistry attrRegistry = directoryService.getRegistries().getAttributeTypeRegistry();
+                    try
+                    {
+                        AttributeType type = attrRegistry.lookup( id );
+                        return ! type.getSyntax().isHumanReadable();
+                    }
+                    catch ( NamingException e )
+                    {
+                        return false;
+                    }
+                }
+            }) );
+        }
+    }
+
+    private class LdapProtocolHandler extends DemuxingIoHandler
+    {
+        public void sessionCreated( IoSession session ) throws Exception
+        {
+            session.setAttribute( LdapServer.class.toString(), LdapServer.this );
+            IoFilterChain filters = session.getFilterChain();
+            filters.addLast( "codec", new ProtocolCodecFilter( codecFactory ) );
+        }
+
+
+        public void sessionClosed( IoSession session )
+        {
+            registry.remove( session );
+        }
+
+
+        public void messageReceived( IoSession session, Object message ) throws Exception
+        {
+            // Translate SSLFilter messages into LDAP extended request
+            // defined in RFC #2830, 'Lightweight Directory Access Protocol (v3):
+            // Extension for Transport Layer Security'.
+            // 
+            // The RFC specifies the payload should be empty, but we use
+            // it to notify the TLS state changes.  This hack should be
+            // OK from the viewpointd of security because StartTLS
+            // handler should react to only SESSION_UNSECURED message
+            // and degrade authentication level to 'anonymous' as specified
+            // in the RFC, and this is no threat.
+
+            if ( message == SSLFilter.SESSION_SECURED )
+            {
+                ExtendedRequest req = new ExtendedRequestImpl( 0 );
+                req.setOid( "1.3.6.1.4.1.1466.20037" );
+                req.setPayload( "SECURED".getBytes( "ISO-8859-1" ) );
+                message = req;
+            }
+            else if ( message == SSLFilter.SESSION_UNSECURED )
+            {
+                ExtendedRequest req = new ExtendedRequestImpl( 0 );
+                req.setOid( "1.3.6.1.4.1.1466.20037" );
+                req.setPayload( "UNSECURED".getBytes( "ISO-8859-1" ) );
+                message = req;
+            }
+
+            if ( ( ( Request ) message ).getControls().size() > 0 && message instanceof ResultResponseRequest )
+            {
+                ResultResponseRequest req = ( ResultResponseRequest ) message;
+                for ( Control control1 : req.getControls().values() )
+                {
+                    MutableControl control = ( MutableControl ) control1;
+                    if ( control.isCritical() && !supportedControls.contains( control.getID() ) )
+                    {
+                        ResultResponse resp = req.getResultResponse();
+                        resp.getLdapResult().setErrorMessage( "Unsupport critical control: " + control.getID() );
+                        resp.getLdapResult().setResultCode( ResultCodeEnum.UNAVAILABLE_CRITICAL_EXTENSION );
+                        session.write( resp );
+                        return;
+                    }
+                }
+            }
+
+            super.messageReceived( session, message );
+        }
+
+
+        public void exceptionCaught( IoSession session, Throwable cause )
+        {
+            if ( cause.getCause() instanceof ResponseCarryingMessageException )
+            {
+                ResponseCarryingMessageException rcme = ( ResponseCarryingMessageException ) cause.getCause();
+                
+                if ( rcme.getResponse() != null )
+                {
+                    session.write( rcme.getResponse() );
+                    return;
+                }
+            }
+            
+            SessionLog.warn( session,
+                "Unexpected exception forcing session to close: sending disconnect notice to client.", cause );
+            
+            session.write( NoticeOfDisconnect.PROTOCOLERROR );
+            registry.remove( session );
+            session.close();
+        }
+    }
+}
diff --git a/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/SessionRegistry.java b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/SessionRegistry.java
new file mode 100644
index 0000000..f90f8bd
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/SessionRegistry.java
@@ -0,0 +1,418 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.ldap;
+
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+
+import javax.naming.Context;
+import javax.naming.NamingException;
+import javax.naming.ldap.Control;
+import javax.naming.ldap.InitialLdapContext;
+import javax.naming.ldap.LdapContext;
+import javax.naming.spi.InitialContextFactory;
+
+import org.apache.directory.server.core.jndi.ServerLdapContext;
+import org.apache.directory.shared.ldap.constants.AuthenticationLevel;
+import org.apache.directory.shared.ldap.exception.LdapNoPermissionException;
+import org.apache.directory.shared.ldap.message.AbandonableRequest;
+import org.apache.directory.shared.ldap.message.Request;
+import org.apache.mina.common.IoSession;
+
+
+/**
+ * A client session state based on JNDI contexts.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class SessionRegistry
+{
+    /** the set of client contexts */
+    private final Map<IoSession, LdapContext> contexts = new HashMap<IoSession, LdapContext>();
+
+    /** outstanding requests for a session */
+    private final Map<IoSession, Map<Integer, Request>> requests = new HashMap<IoSession, Map<Integer, Request>>();
+
+    /** the properties associated with this SessionRegistry */
+    private Hashtable<String, Object> env;
+
+    /** the configuration associated with this SessionRegistry */
+    private LdapServer ldapServer;
+
+
+    /**
+     * Creates a singleton session state object for the system.
+     *
+     * @param ldapServer the ldap server instance
+     * @param env the properties associated with this SessionRegistry
+     */
+    public SessionRegistry( LdapServer ldapServer, Hashtable<String, Object> env )
+    {
+        this.ldapServer = ldapServer;
+
+        if ( env == null )
+        {
+            this.env = new Hashtable<String, Object>();
+            this.env.put( Context.PROVIDER_URL, "" );
+            this.env.put( Context.INITIAL_CONTEXT_FACTORY, "org.apache.directory.server.jndi.ServerContextFactory" );
+        }
+        else
+        {
+            this.env = env;
+            this.env.put( Context.PROVIDER_URL, "" );
+        }
+    }
+
+
+    /**
+     * Gets a cloned copy of the environment associated with this registry.
+     *
+     * @return the registry environment
+     */
+    @SuppressWarnings( "unchecked" )
+    public Hashtable<String, Object> getEnvironmentByCopy()
+    {
+        return ( Hashtable<String, Object> ) env.clone();
+    }
+
+
+    /**
+     * Adds a request to the map of outstanding requests for a session.
+     * 
+     * @param session the session the request was issued on
+     * @param req the request to add
+     */
+    public void addOutstandingRequest( IoSession session, Request req )
+    {
+        // pull out the map of requests by id
+        synchronized ( requests )
+        {
+            Map<Integer, Request> reqmap = requests.get( session );
+            
+            if ( reqmap == null )
+            {
+                reqmap = new HashMap<Integer, Request>();
+                requests.put( session, reqmap );
+            }
+            
+            reqmap.put( req.getMessageId(), req );
+        }
+    }
+
+
+    /**
+     * Overload that does not require boxing of primitive messageId.
+     * 
+     * @param session the session associated with the request
+     * @param messageId the id of the request
+     * @return the Request if it is removed or null if no such request was mapped as outstanding
+     */
+    public Request removeOutstandingRequest( IoSession session, int messageId )
+    {
+        return removeOutstandingRequest( session, new Integer( messageId ) );
+    }
+
+
+    /**
+     * Removes an outstanding request from the session's outstanding request map.
+     * 
+     * @param session the session the request is removed from
+     * @param id the messageId of the request to remove
+     * @return the Request if it is removed or null if no such request was mapped as outstanding
+     */
+    public Request removeOutstandingRequest( IoSession session, Integer id )
+    {
+        // pull out the map of requests by id
+        synchronized ( requests )
+        {
+            Map<Integer, Request> reqmap = requests.get( session );
+            
+            if ( reqmap == null )
+            {
+                return null;
+            }
+            
+            return reqmap.remove( id );
+        }
+    }
+
+
+    /**
+     * Returns a shallow copied map of all outstanding requests for an IoSession.
+     * 
+     * @param session the session to get outstanding requests for
+     * @return a map by message id as an Integer to Request objects
+     */
+    public Map<Integer, Request> getOutstandingRequests( IoSession session )
+    {
+        Map<Integer, Request> reqmap = requests.get( session );
+        
+        if ( reqmap == null )
+        {
+            //noinspection unchecked
+            return Collections.EMPTY_MAP;
+        }
+        
+        // Copy the maps
+        return new HashMap<Integer, Request>( reqmap );
+    }
+
+
+    /**
+     * Overload that does not require boxing of primitive messageId.
+     * 
+     * @param session the session associated with the request
+     * @param abandonedId the id of the request
+     * @return the request in session for id or null if request has completed
+     */
+    public Request getOutstandingRequest( IoSession session, int abandonedId )
+    {
+        return getOutstandingRequest( session, new Integer( abandonedId ) );
+    }
+
+
+    /**
+     * Gets an outstanding request by messageId for a session.
+     * 
+     * @param session the LDAP session 
+     * @param id the message id of the request
+     * @return the request in session for id or null if request has completed
+     */
+    public Request getOutstandingRequest( IoSession session, Integer id )
+    {
+        Map<Integer, Request> reqmap = requests.get( session );
+        
+        if ( reqmap == null )
+        {
+            return null;
+        }
+        
+        return reqmap.get( id );
+    }
+
+
+    public IoSession[] getSessions()
+    {
+        IoSession[] sessions;
+        
+        synchronized ( contexts )
+        {
+            sessions = new IoSession[contexts.size()];
+            sessions = contexts.keySet().toArray( sessions );
+        }
+        
+        return sessions;
+    }
+
+
+    /**
+     * Gets the InitialContext to the root of the system that was gotten for
+     * client.  If the context is not present then there was no bind operation
+     * that set it.  Hence this operation requesting the context is anonymous.
+     *
+     * @todo this allowAnonymous parameter is a bit confusing - figure out
+     * something better to call it.  I think only bind requests a context
+     * that is not anonymous.  Have to refactor the heck out of this lousy code.
+     * 
+     * @param session the client's key
+     * @param connCtls connection controls if any to use if creating anon context
+     * @param allowAnonymous true if anonymous requests will create anonymous
+     * InitialContext if one is not present for the operation
+     * @return the InitialContext or null
+     * @throws NamingException if something goes wrong
+     */
+    public LdapContext getLdapContext( IoSession session, Control[] connCtls, boolean allowAnonymous )
+        throws NamingException
+    {
+        LdapContext ctx;
+
+        synchronized ( contexts )
+        {
+            ctx = contexts.get( session );
+        }
+
+        // there is no context so its an implicit bind, no bind operation is being performed
+        if ( ctx == null && allowAnonymous )
+        {
+            // if configuration says disable anonymous binds we throw exception
+            if ( ! ldapServer.isAllowAnonymousAccess() )
+            {
+                throw new LdapNoPermissionException( "Anonymous binds have been disabled!" );
+            }
+
+            if ( env.containsKey( "server.use.factory.instance" ) )
+            {
+                InitialContextFactory factory = ( InitialContextFactory ) env.get( "server.use.factory.instance" );
+
+                if ( factory == null )
+                {
+                    throw new NullPointerException( "server.use.factory.instance was set in env but was null" );
+                }
+
+                ctx = ( LdapContext ) factory.getInitialContext( env );
+            }
+            else
+            {
+                //noinspection unchecked
+                Hashtable<String, Object> cloned = ( Hashtable<String, Object> ) env.clone();
+                cloned.put( Context.SECURITY_AUTHENTICATION, AuthenticationLevel.NONE.toString() );
+                cloned.remove( Context.SECURITY_PRINCIPAL );
+                cloned.remove( Context.SECURITY_CREDENTIALS );
+                ctx = new InitialLdapContext( cloned, connCtls );
+            }
+        }
+        // the context came up non null so we binded explicitly and op now is not bind
+        else if ( ctx != null && allowAnonymous )
+        {
+            ServerLdapContext slc;
+            
+            if ( !( ctx instanceof ServerLdapContext ) )
+            {
+                slc = ( ServerLdapContext ) ctx.lookup( "" );
+            }
+            else
+            {
+                slc = ( ServerLdapContext ) ctx;
+            }
+            
+            boolean isAnonymousUser = slc.getPrincipal().getName().trim().equals( "" );
+
+            // if the user principal is anonymous and the configuration does not allow anonymous binds we
+            // prevent the operation by blowing a NoPermissionsException
+            if ( isAnonymousUser && ! ldapServer.isAllowAnonymousAccess() )
+            {
+                throw new LdapNoPermissionException( "Anonymous binds have been disabled!" );
+            }
+        }
+
+        return ctx;
+    }
+
+
+    /**
+     * Gets the InitialContext to the root of the system that was gotten for
+     * client ONLY to be used for RootDSE Search operations.  This bypasses
+     * checks to only allow anonymous binds for this special case.
+     *
+     * @param session the client's key
+     * @param connCtls connection controls if any to use if creating anon context
+     * @return the InitialContext or null
+     * @throws NamingException if something goes wrong
+     */
+    public LdapContext getLdapContextOnRootDSEAccess( IoSession session, Control[] connCtls ) throws NamingException
+    {
+        LdapContext ctx;
+
+        synchronized ( contexts )
+        {
+            ctx = contexts.get( session );
+        }
+
+        if ( ctx == null )
+        {
+            if ( env.containsKey( "server.use.factory.instance" ) )
+            {
+                InitialContextFactory factory = ( InitialContextFactory ) env.get( "server.use.factory.instance" );
+
+                if ( factory == null )
+                {
+                    throw new NullPointerException( "server.use.factory.instance was set in env but was null" );
+                }
+
+                ctx = ( LdapContext ) factory.getInitialContext( env );
+            }
+            else
+            {
+                //noinspection unchecked
+                Hashtable<String, Object> cloned = ( Hashtable<String, Object> ) env.clone();
+                cloned.put( Context.SECURITY_AUTHENTICATION, AuthenticationLevel.NONE.toString() );
+                cloned.remove( Context.SECURITY_PRINCIPAL );
+                cloned.remove( Context.SECURITY_CREDENTIALS );
+                ctx = new InitialLdapContext( cloned, connCtls );
+            }
+        }
+
+        return ctx;
+    }
+
+
+    /**
+     * Sets the initial context associated with a newly authenticated client.
+     *
+     * @param session the client session
+     * @param ictx the initial context gotten
+     */
+    public void setLdapContext( IoSession session, LdapContext ictx )
+    {
+        synchronized ( contexts )
+        {
+            contexts.put( session, ictx );
+        }
+    }
+
+
+    /**
+     * Removes the state mapping a JNDI initial context for the client's key.
+     *
+     * @param session the client's key
+     */
+    public void remove( IoSession session )
+    {
+        synchronized ( contexts )
+        {
+            contexts.remove( session );
+        }
+
+        Map<Integer, Request> reqmap;
+        
+        synchronized ( requests )
+        {
+            reqmap = requests.remove( session );
+        }
+
+        if ( reqmap == null || reqmap.isEmpty() )
+        {
+            return;
+        }
+
+        for ( Request request : reqmap.values() )
+        {
+            if ( request instanceof AbandonableRequest )
+            {
+                ( ( AbandonableRequest ) request ).abandon();
+            }
+        }
+    }
+
+
+    /**
+     * Terminates the session by publishing a disconnect event.
+     *
+     * @param session the client key of the client to disconnect
+     */
+    public void terminateSession( IoSession session )
+    {
+        session.close();
+    }
+}
diff --git a/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/gui/OutstandingRequestsDialog.java b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/gui/OutstandingRequestsDialog.java
new file mode 100644
index 0000000..f3ef3a8
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/gui/OutstandingRequestsDialog.java
@@ -0,0 +1,338 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.ldap.gui;
+
+
+import java.awt.BorderLayout;
+import java.net.InetSocketAddress;
+import java.util.Map;
+
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+import javax.swing.JDialog;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.JButton;
+
+import org.apache.directory.server.ldap.SessionRegistry;
+import org.apache.directory.shared.ldap.message.AbandonableRequest;
+import org.apache.directory.shared.ldap.message.Request;
+import org.apache.mina.common.IoSession;
+import javax.swing.JTextArea;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+
+
+public class OutstandingRequestsDialog extends JDialog
+{
+    private static final long serialVersionUID = -3777123348215825711L;
+    private static final AbandonableRequest[] EMPTY_REQUEST_ARRAY = new AbandonableRequest[0];
+    private JPanel jContentPane;
+    private JPanel jPanel;
+    private JScrollPane jScrollPane;
+    private JTable jTable;
+    private JPanel jPanel1;
+    private JButton jButton;
+
+    final IoSession session;
+    final SessionRegistry registry;
+
+    private JPanel jPanel2;
+    private JTextArea jTextArea;
+    private JButton jButton1;
+    private JButton jButton2;
+
+
+    /**
+     * This is the default constructor
+     * @param owner the owning frame
+     * @param session the MINA IoSession to get outstanding requests for
+     * @param sessionRegistry the session registry
+     */
+    public OutstandingRequestsDialog( JFrame owner, IoSession session, SessionRegistry sessionRegistry )
+    {
+        super( owner, true );
+        this.session = session;
+        this.registry = sessionRegistry;
+
+        StringBuffer buf = new StringBuffer();
+        buf.append( "Outstanding Requests: " );
+        buf.append( ( ( InetSocketAddress ) session.getRemoteAddress() ).getHostName() );
+        buf.append( ":" );
+        buf.append( ( ( InetSocketAddress ) session.getRemoteAddress() ).getPort() );
+        setTitle( buf.toString() );
+        initialize();
+    }
+
+
+    /**
+     * This method initializes this
+     */
+    private void initialize()
+    {
+        this.setSize( 549, 341 );
+        this.setContentPane( getJContentPane() );
+    }
+
+
+    /**
+     * This method initializes jContentPane
+     * 
+     * @return javax.swing.JPanel
+     */
+    private JPanel getJContentPane()
+    {
+        if ( jContentPane == null )
+        {
+            jContentPane = new JPanel();
+            jContentPane.setLayout( new BorderLayout() );
+            jContentPane.add( getJPanel(), java.awt.BorderLayout.CENTER );
+        }
+        return jContentPane;
+    }
+
+
+    /**
+     * This method initializes jPanel    
+     *     
+     * @return javax.swing.JPanel    
+     */
+    private JPanel getJPanel()
+    {
+        if ( jPanel == null )
+        {
+            jPanel = new JPanel();
+            jPanel.setLayout( new BorderLayout() );
+            jPanel.add( getJScrollPane(), java.awt.BorderLayout.CENTER );
+            jPanel.add( getJPanel1(), java.awt.BorderLayout.SOUTH );
+            jPanel.add( getJPanel2(), java.awt.BorderLayout.NORTH );
+        }
+        return jPanel;
+    }
+
+
+    /**
+     * This method initializes jScrollPane    
+     *     
+     * @return javax.swing.JScrollPane    
+     */
+    private JScrollPane getJScrollPane()
+    {
+        if ( jScrollPane == null )
+        {
+            jScrollPane = new JScrollPane();
+            jScrollPane.setViewportView( getJTable() );
+        }
+        return jScrollPane;
+    }
+
+
+    /**
+     * This method initializes jTable    
+     *     
+     * @return javax.swing.JTable    
+     */
+    private JTable getJTable()
+    {
+        if ( jTable == null )
+        {
+            jTable = new JTable();
+        }
+
+        setRequestsModel();
+        jTable.getSelectionModel().addListSelectionListener( new ListSelectionListener()
+        {
+            public void valueChanged( ListSelectionEvent e )
+            {
+                int row = jTable.getSelectedRow();
+                if ( row > -1 )
+                {
+                    jButton2.setEnabled( true );
+                    AbandonableRequest req = ( ( OutstandingRequestsModel ) jTable.getModel() )
+                        .getAbandonableRequest( row );
+                    jTextArea.setText( req.toString() );
+                    jTextArea.setEnabled( true );
+                }
+                else
+                {
+                    jButton2.setEnabled( false );
+                    jTextArea.setText( "" );
+                    jTextArea.setEnabled( false );
+                }
+            }
+        } );
+        return jTable;
+    }
+
+
+    private void setRequestsModel()
+    {
+        AbandonableRequest[] requests;
+        Map<Integer, Request> reqsMap = registry.getOutstandingRequests( session );
+        
+        if ( reqsMap != null )
+        {
+            requests = new AbandonableRequest[reqsMap.size()];
+            //noinspection unchecked
+            requests = ( AbandonableRequest[] ) reqsMap.values().toArray( requests );
+        }
+        else
+        {
+            requests = EMPTY_REQUEST_ARRAY;
+        }
+
+        jTable.setModel( new OutstandingRequestsModel( requests ) );
+    }
+
+
+    /**
+     * This method initializes jPanel1    
+     *     
+     * @return javax.swing.JPanel    
+     */
+    private JPanel getJPanel1()
+    {
+        if ( jPanel1 == null )
+        {
+            jPanel1 = new JPanel();
+            jPanel1.add( getJButton(), null );
+            jPanel1.add( getJButton1(), null );
+        }
+        return jPanel1;
+    }
+
+
+    /**
+     * This method initializes jButton    
+     *     
+     * @return javax.swing.JButton    
+     */
+    private JButton getJButton()
+    {
+        if ( jButton == null )
+        {
+            jButton = new JButton();
+            jButton.setText( "Done" );
+            jButton.addActionListener( new java.awt.event.ActionListener()
+            {
+                public void actionPerformed( java.awt.event.ActionEvent e )
+                {
+                    OutstandingRequestsDialog.this.setVisible( false );
+                    OutstandingRequestsDialog.this.dispose();
+                }
+            } );
+        }
+        return jButton;
+    }
+
+
+    /**
+     * This method initializes jPanel2    
+     *     
+     * @return javax.swing.JPanel    
+     */
+    private JPanel getJPanel2()
+    {
+        if ( jPanel2 == null )
+        {
+            jPanel2 = new JPanel();
+            jPanel2.setLayout( new BorderLayout() );
+            jPanel2.setBorder( javax.swing.BorderFactory.createTitledBorder( null, "Request",
+                javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION,
+                javax.swing.border.TitledBorder.DEFAULT_POSITION, null, null ) );
+            jPanel2.add( getJButton2(), java.awt.BorderLayout.WEST );
+            jPanel2.add( getJTextArea(), java.awt.BorderLayout.CENTER );
+        }
+        return jPanel2;
+    }
+
+
+    /**
+     * This method initializes jTextArea    
+     *     
+     * @return javax.swing.JTextArea    
+     */
+    private JTextArea getJTextArea()
+    {
+        if ( jTextArea == null )
+        {
+            jTextArea = new JTextArea();
+        }
+
+        jTextArea.setEnabled( false );
+        jTextArea.setEditable( false );
+        return jTextArea;
+    }
+
+
+    /**
+     * This method initializes jButton1    
+     *     
+     * @return javax.swing.JButton    
+     */
+    private JButton getJButton1()
+    {
+        if ( jButton1 == null )
+        {
+            jButton1 = new JButton();
+            jButton1.setText( "Refresh" );
+            jButton1.addActionListener( new java.awt.event.ActionListener()
+            {
+                public void actionPerformed( java.awt.event.ActionEvent e )
+                {
+                    setRequestsModel();
+                    jTextArea.setText( "" );
+                    jTextArea.setEnabled( false );
+                    jButton2.setEnabled( false );
+                }
+            } );
+        }
+        return jButton1;
+    }
+
+
+    /**
+     * This method initializes jButton2    
+     *     
+     * @return javax.swing.JButton    
+     */
+    private JButton getJButton2()
+    {
+        if ( jButton2 == null )
+        {
+            jButton2 = new JButton();
+            jButton2.setText( "Abandon" );
+            jButton2.setEnabled( false );
+            jButton2.addActionListener( new java.awt.event.ActionListener()
+            {
+                public void actionPerformed( java.awt.event.ActionEvent e )
+                {
+                    int row = jTable.getSelectedRow();
+                    AbandonableRequest req = ( ( OutstandingRequestsModel ) jTable.getModel() )
+                        .getAbandonableRequest( row );
+                    req.abandon();
+                    registry.removeOutstandingRequest( session, req.getMessageId() );
+                    setRequestsModel();
+                }
+            } );
+        }
+        return jButton2;
+    }
+} //  @jve:decl-index=0:visual-constraint="10,10"
diff --git a/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/gui/OutstandingRequestsModel.java b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/gui/OutstandingRequestsModel.java
new file mode 100644
index 0000000..c45f0ce
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/gui/OutstandingRequestsModel.java
@@ -0,0 +1,110 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.ldap.gui;
+
+
+import javax.swing.event.TableModelListener;
+import javax.swing.table.TableModel;
+
+import org.apache.directory.shared.ldap.message.AbandonableRequest;
+
+
+public class OutstandingRequestsModel implements TableModel
+{
+    final String[] columns = new String[]
+        { "messageId", "type" };
+    final Class<?>[] columnClasses = new Class[]
+        { Integer.class, String.class };
+    final AbandonableRequest[] requests;
+
+
+    OutstandingRequestsModel(AbandonableRequest[] requests)
+    {
+        this.requests = requests;
+    }
+
+
+    AbandonableRequest getAbandonableRequest( int row )
+    {
+        return requests[row];
+    }
+
+
+    public int getRowCount()
+    {
+        return requests.length;
+    }
+
+
+    public int getColumnCount()
+    {
+        return columns.length;
+    }
+
+
+    public String getColumnName( int columnIndex )
+    {
+        return columns[columnIndex];
+    }
+
+
+    public Class<?> getColumnClass( int columnIndex )
+    {
+        return columnClasses[columnIndex];
+    }
+
+
+    public boolean isCellEditable( int rowIndex, int columnIndex )
+    {
+        return false;
+    }
+
+
+    public Object getValueAt( int rowIndex, int columnIndex )
+    {
+        AbandonableRequest req = requests[rowIndex];
+
+        switch ( columnIndex )
+        {
+            case ( 0 ):
+                return new Integer( req.getMessageId() );
+            case ( 1 ):
+                return req.getType().toString();
+            default:
+                throw new IndexOutOfBoundsException( "column index max is " + ( columns.length - 1 ) );
+        }
+    }
+
+
+    public void setValueAt( Object aValue, int rowIndex, int columnIndex )
+    {
+        throw new UnsupportedOperationException();
+    }
+
+
+    public void addTableModelListener( TableModelListener l )
+    {
+    }
+
+
+    public void removeTableModelListener( TableModelListener l )
+    {
+    }
+}
diff --git a/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/gui/SessionsFrame.java b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/gui/SessionsFrame.java
new file mode 100644
index 0000000..1fe702a
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/gui/SessionsFrame.java
@@ -0,0 +1,859 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.ldap.gui;
+
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.Point;
+import java.awt.Toolkit;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JFrame;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.JButton;
+import javax.swing.BoxLayout;
+import javax.swing.JTextField;
+import javax.swing.JMenuBar;
+import javax.swing.JMenu;
+import javax.swing.JMenuItem;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+
+import org.apache.directory.server.ldap.SessionRegistry;
+import org.apache.directory.server.ldap.handlers.extended.GracefulShutdownHandler;
+import org.apache.directory.shared.ldap.message.extended.GracefulDisconnect;
+import org.apache.directory.shared.ldap.message.extended.NoticeOfDisconnect;
+import org.apache.mina.common.CloseFuture;
+import org.apache.mina.common.IoAcceptor;
+import org.apache.mina.common.IoHandler;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.common.WriteFuture;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+public class SessionsFrame extends JFrame
+{
+    private static final Logger LOG = LoggerFactory.getLogger( SessionsFrame.class );
+    private static final long serialVersionUID = -863445561454536133L;
+    private static final String REFRESH_COMMAND = "Refresh";
+
+    boolean isServiceBound = true;
+    private IoSession requestor;
+    private IoHandler ldapProvider;
+    private JPanel jContentPane;
+    private JPanel mainPanel;
+    private JScrollPane sessionsPane;
+    private JTable sessionsTable;
+    private JPanel filterPanel;
+    private JButton filterButton;
+    private JTextField filterText;
+    private JMenuBar menuBar;
+    private JMenu menuFile;
+    private JMenuItem exitItem;
+    private JMenu menuSession;
+    private JMenuItem closeItem;
+    private JMenu menuSendNoD;
+    private JMenuItem unavailableItem;
+    private JMenuItem protocolErrorItem;
+    private JMenuItem strongAuthRequiredItem;
+    private JPanel southPanel;
+    private JMenuItem showRequests;
+    private JButton refreshButton;
+
+    private IoSession selected;
+    private JMenuItem unbindItem;
+    private JMenuItem bindItem;
+    private SessionRegistry registry;
+
+
+    /**
+     * This is the default constructor
+     * @param registry the session registry
+     */
+    public SessionsFrame( SessionRegistry registry )
+    {
+        super();
+        this.registry = registry;
+        initialize();
+    }
+
+
+    /**
+     * This method initializes this
+     */
+    private void initialize()
+    {
+        this.setSize( 789, 436 );
+        this.setJMenuBar( getMainMenuBar() );
+        this.setContentPane( getJContentPane() );
+        this.setTitle( "Sessions" );
+        this.addWindowListener( new java.awt.event.WindowAdapter()
+        {
+            public void windowClosing( java.awt.event.WindowEvent e )
+            {
+                SessionsFrame.this.setVisible( false );
+                SessionsFrame.this.dispose();
+            }
+        } );
+    }
+
+
+    /**
+     * This method initializes jContentPane
+     * 
+     * @return javax.swing.JPanel
+     */
+    private JPanel getJContentPane()
+    {
+        if ( jContentPane == null )
+        {
+            jContentPane = new JPanel();
+            jContentPane.setLayout( new BorderLayout() );
+            jContentPane.add( getMainPanel(), java.awt.BorderLayout.CENTER );
+        }
+        return jContentPane;
+    }
+
+
+    /**
+     * This method initializes jPanel    
+     *     
+     * @return javax.swing.JPanel    
+     */
+    private JPanel getMainPanel()
+    {
+        if ( mainPanel == null )
+        {
+            mainPanel = new JPanel();
+            mainPanel.setLayout( new BorderLayout() );
+            mainPanel.add( getFilterPanel(), java.awt.BorderLayout.NORTH );
+            mainPanel.add( getSessionsPane(), java.awt.BorderLayout.CENTER );
+            mainPanel.add( getSouthPanel(), java.awt.BorderLayout.SOUTH );
+        }
+        return mainPanel;
+    }
+
+
+    /**
+     * This method initializes jScrollPane    
+     *     
+     * @return javax.swing.JScrollPane    
+     */
+    private JScrollPane getSessionsPane()
+    {
+        if ( sessionsPane == null )
+        {
+            sessionsPane = new JScrollPane();
+            sessionsPane.setName( "jScrollPane" );
+            sessionsPane.setViewportView( getSessionsTable() );
+        }
+        return sessionsPane;
+    }
+
+
+    /**
+     * This method initializes jTable    
+     *     
+     * @return javax.swing.JTable    
+     */
+    private JTable getSessionsTable()
+    {
+        if ( sessionsTable == null )
+        {
+            sessionsTable = new JTable();
+            sessionsTable.setSelectionMode( javax.swing.ListSelectionModel.SINGLE_SELECTION );
+            sessionsTable.setModel( new SessionsModel( registry.getSessions() ) );
+            sessionsTable.getSelectionModel().addListSelectionListener( new ListSelectionListener()
+            {
+                public void valueChanged( ListSelectionEvent e )
+                {
+                    int row = sessionsTable.getSelectedRow();
+                    if ( row == -1 )
+                    {
+                        selected = null;
+                    }
+                    else
+                    {
+                        selected = ( ( SessionsModel ) sessionsTable.getModel() ).getIoSession( row );
+                        closeItem.setEnabled( true );
+                        menuSendNoD.setEnabled( true );
+                        showRequests.setEnabled( true );
+                    }
+                }
+            } );
+        }
+        return sessionsTable;
+    }
+
+
+    /**
+     * This method initializes jPanel    
+     *     
+     * @return javax.swing.JPanel    
+     */
+    private JPanel getFilterPanel()
+    {
+        if ( filterPanel == null )
+        {
+            filterPanel = new JPanel();
+            filterPanel.setLayout( new BoxLayout( getFilterPanel(), BoxLayout.X_AXIS ) );
+            filterPanel.setBorder( javax.swing.BorderFactory
+                .createEtchedBorder( javax.swing.border.EtchedBorder.RAISED ) );
+            filterPanel.add( getFilterButton(), null );
+            filterPanel.add( getFilterText(), null );
+        }
+        return filterPanel;
+    }
+
+
+    /**
+     * This method initializes jButton    
+     *     
+     * @return javax.swing.JButton    
+     */
+    private JButton getFilterButton()
+    {
+        if ( filterButton == null )
+        {
+            filterButton = new JButton();
+            filterButton.setText( "Filter" );
+        }
+        return filterButton;
+    }
+
+
+    /**
+     * This method initializes jTextField    
+     *     
+     * @return javax.swing.JTextField    
+     */
+    private JTextField getFilterText()
+    {
+        if ( filterText == null )
+        {
+            filterText = new JTextField();
+        }
+        return filterText;
+    }
+
+
+    /**
+     * This method initializes jJMenuBar    
+     *     
+     * @return javax.swing.JMenuBar    
+     */
+    private JMenuBar getMainMenuBar()
+    {
+        if ( menuBar == null )
+        {
+            menuBar = new JMenuBar();
+            menuBar.add( getMenuFile() );
+            menuBar.add( getMenuSession() );
+        }
+        return menuBar;
+    }
+
+
+    /**
+     * This method initializes jMenu    
+     *     
+     * @return javax.swing.JMenu    
+     */
+    private JMenu getMenuFile()
+    {
+        if ( menuFile == null )
+        {
+            menuFile = new JMenu();
+            menuFile.setText( "File" );
+            menuFile.add( getExitItem() );
+        }
+        return menuFile;
+    }
+
+
+    /**
+     * This method initializes jMenuItem    
+     *     
+     * @return javax.swing.JMenuItem    
+     */
+    private JMenuItem getExitItem()
+    {
+        if ( exitItem == null )
+        {
+            exitItem = new JMenuItem();
+            exitItem.setText( "exit" );
+            exitItem.addActionListener( new java.awt.event.ActionListener()
+            {
+                public void actionPerformed( java.awt.event.ActionEvent e )
+                {
+                    SessionsFrame.this.setVisible( false );
+                    SessionsFrame.this.dispose();
+                }
+            } );
+        }
+        return exitItem;
+    }
+
+
+    /**
+     * This method initializes jMenu    
+     *     
+     * @return javax.swing.JMenu    
+     */
+    private JMenu getMenuSession()
+    {
+        if ( menuSession == null )
+        {
+            menuSession = new JMenu();
+            menuSession.setText( "Session" );
+            menuSession.add( getCloseItem() );
+            closeItem.setEnabled( false );
+            menuSession.add( getMenuSendNoD() );
+            menuSendNoD.setEnabled( false );
+            menuSession.add( getShowRequests() );
+            menuSession.add( getUnbindItem() );
+            menuSession.add( getBindItem() );
+            showRequests.setEnabled( false );
+        }
+        return menuSession;
+    }
+
+
+    /**
+     * This method initializes jMenuItem    
+     *     
+     * @return javax.swing.JMenuItem    
+     */
+    private JMenuItem getCloseItem()
+    {
+        if ( closeItem == null )
+        {
+            closeItem = new JMenuItem();
+            closeItem.setText( "close" );
+            closeItem.addActionListener( new java.awt.event.ActionListener()
+            {
+                public void actionPerformed( java.awt.event.ActionEvent e )
+                {
+                    registry.terminateSession( selected );
+                    try
+                    {
+                        Thread.sleep( 250 );
+                    }
+                    catch ( InterruptedException e1 )
+                    {
+                        LOG.error( "", e1 );
+                    }
+                    refresh();
+                }
+            } );
+        }
+        return closeItem;
+    }
+
+
+    /**
+     * This method initializes jMenu    
+     *     
+     * @return javax.swing.JMenu    
+     */
+    private JMenu getMenuSendNoD()
+    {
+        if ( menuSendNoD == null )
+        {
+            menuSendNoD = new JMenu();
+            menuSendNoD.setText( "Send NoD" );
+            menuSendNoD.add( getUnavailableItem() );
+            menuSendNoD.add( getProtocolErrorItem() );
+            menuSendNoD.add( getStrongAuthRequiredItem() );
+        }
+        return menuSendNoD;
+    }
+
+
+    /**
+     * This method initializes jMenuItem    
+     *     
+     * @return javax.swing.JMenuItem    
+     */
+    private JMenuItem getUnavailableItem()
+    {
+        if ( unavailableItem == null )
+        {
+            unavailableItem = new JMenuItem();
+            unavailableItem.setText( "unavailable" );
+            unavailableItem.addActionListener( new java.awt.event.ActionListener()
+            {
+                public void actionPerformed( java.awt.event.ActionEvent e )
+                {
+                    selected.write( NoticeOfDisconnect.UNAVAILABLE );
+                    try
+                    {
+                        Thread.sleep( 250 );
+                    }
+                    catch ( InterruptedException e1 )
+                    {
+                        LOG.error( "", e1 );
+                    }
+                    refresh();
+                }
+            } );
+        }
+        return unavailableItem;
+    }
+
+
+    /**
+     * This method initializes jMenuItem    
+     *     
+     * @return javax.swing.JMenuItem    
+     */
+    private JMenuItem getProtocolErrorItem()
+    {
+        if ( protocolErrorItem == null )
+        {
+            protocolErrorItem = new JMenuItem();
+            protocolErrorItem.setText( "protocolError" );
+            protocolErrorItem.addActionListener( new java.awt.event.ActionListener()
+            {
+                public void actionPerformed( java.awt.event.ActionEvent e )
+                {
+                    selected.write( NoticeOfDisconnect.PROTOCOLERROR );
+                    try
+                    {
+                        Thread.sleep( 250 );
+                    }
+                    catch ( InterruptedException e1 )
+                    {
+                        LOG.error( "", e1 );
+                    }
+                    refresh();
+                }
+            } );
+        }
+        return protocolErrorItem;
+    }
+
+
+    /**
+     * This method initializes jMenuItem    
+     *     
+     * @return javax.swing.JMenuItem    
+     */
+    private JMenuItem getStrongAuthRequiredItem()
+    {
+        if ( strongAuthRequiredItem == null )
+        {
+            strongAuthRequiredItem = new JMenuItem();
+            strongAuthRequiredItem.setText( "strongAuthRequired" );
+            strongAuthRequiredItem.addActionListener( new java.awt.event.ActionListener()
+            {
+                public void actionPerformed( java.awt.event.ActionEvent e )
+                {
+                    WriteFuture future = selected.write( NoticeOfDisconnect.STRONGAUTHREQUIRED );
+                    try
+                    {
+                        future.join( 1000 );
+                        CloseFuture cfuture = selected.close();
+                        cfuture.join( 1000 );
+                    }
+                    catch ( Exception e1 )
+                    {
+                        LOG.error( "", e1 );
+                    }
+                    refresh();
+                }
+            } );
+        }
+        return strongAuthRequiredItem;
+    }
+
+
+    //    /**
+    //     * This method initializes jPopupMenu    
+    //     *     
+    //     * @return javax.swing.JPopupMenu    
+    //     */
+    //    private JPopupMenu getSessionsPopupMenu()
+    //    {
+    //        if ( popupMenu == null )
+    //        {
+    //            popupMenu = new JPopupMenu();
+    //            popupMenu.add(getJMenuItem());
+    //            popupMenu.add(getJMenu());
+    //            popupMenu.add(getJMenuItem4());
+    //        }
+    //        return popupMenu;
+    //    }
+
+    /**
+     * This method initializes jPanel    
+     *     
+     * @return javax.swing.JPanel    
+     */
+    private JPanel getSouthPanel()
+    {
+        if ( southPanel == null )
+        {
+            southPanel = new JPanel();
+            southPanel
+                .setBorder( javax.swing.BorderFactory.createEtchedBorder( javax.swing.border.EtchedBorder.RAISED ) );
+            southPanel.add( getRefreshButton(), null );
+        }
+        return southPanel;
+    }
+
+
+    /**
+     * This method initializes jMenuItem    
+     *     
+     * @return javax.swing.JMenuItem    
+     */
+    private JMenuItem getShowRequests()
+    {
+        if ( showRequests == null )
+        {
+            showRequests = new JMenuItem();
+            showRequests.setText( "show requests" );
+            showRequests.addActionListener( new java.awt.event.ActionListener()
+            {
+                public void actionPerformed( java.awt.event.ActionEvent e )
+                {
+                    OutstandingRequestsDialog dialog =
+                            new OutstandingRequestsDialog( SessionsFrame.this, selected, registry );
+                    dialog.addWindowListener( new WindowAdapter()
+                    {
+                        public void windowClosed( WindowEvent e )
+                        {
+                            e.getWindow().dispose();
+                        }
+                    } );
+                    dialog.setVisible( true );
+                }
+            } );
+        }
+        return showRequests;
+    }
+
+
+    //    /**
+    //     * This method initializes jMenuItem    
+    //     *     
+    //     * @return javax.swing.JMenuItem    
+    //     */
+    //    private JMenuItem getJMenuItem()
+    //    {
+    //        if ( jMenuItem == null )
+    //        {
+    //            jMenuItem = new JMenuItem();
+    //            jMenuItem.setText("close");
+    //        }
+    //        return jMenuItem;
+    //    }
+    //
+    //
+    //    /**
+    //     * This method initializes jMenu    
+    //     *     
+    //     * @return javax.swing.JMenu    
+    //     */
+    //    private JMenu getJMenu()
+    //    {
+    //        if ( jMenu == null )
+    //        {
+    //            jMenu = new JMenu();
+    //            jMenu.setText("Send NoD");
+    //            jMenu.add(getJMenuItem1());
+    //            jMenu.add(getJMenuItem2());
+    //            jMenu.add(getJMenuItem3());
+    //        }
+    //        return jMenu;
+    //    }
+    //
+    //
+    //    /**
+    //     * This method initializes jMenuItem1    
+    //     *     
+    //     * @return javax.swing.JMenuItem    
+    //     */
+    //    private JMenuItem getJMenuItem1()
+    //    {
+    //        if ( jMenuItem1 == null )
+    //        {
+    //            jMenuItem1 = new JMenuItem();
+    //            jMenuItem1.setText("unavailable");
+    //        }
+    //        return jMenuItem1;
+    //    }
+    //
+    //
+    //    /**
+    //     * This method initializes jMenuItem2    
+    //     *     
+    //     * @return javax.swing.JMenuItem    
+    //     */
+    //    private JMenuItem getJMenuItem2()
+    //    {
+    //        if ( jMenuItem2 == null )
+    //        {
+    //            jMenuItem2 = new JMenuItem();
+    //            jMenuItem2.setText("protocolError");
+    //        }
+    //        return jMenuItem2;
+    //    }
+    //
+    //
+    //    /**
+    //     * This method initializes jMenuItem3    
+    //     *     
+    //     * @return javax.swing.JMenuItem    
+    //     */
+    //    private JMenuItem getJMenuItem3()
+    //    {
+    //        if ( jMenuItem3 == null )
+    //        {
+    //            jMenuItem3 = new JMenuItem();
+    //            jMenuItem3.setText("strongAuthRequired");
+    //        }
+    //        return jMenuItem3;
+    //    }
+    //
+    //
+    //    /**
+    //     * This method initializes jMenuItem4    
+    //     *     
+    //     * @return javax.swing.JMenuItem    
+    //     */
+    //    private JMenuItem getJMenuItem4()
+    //    {
+    //        if ( jMenuItem4 == null )
+    //        {
+    //            jMenuItem4 = new JMenuItem();
+    //            jMenuItem4.setText("show requests");
+    //        }
+    //        return jMenuItem4;
+    //    }
+
+    /**
+     * This method initializes jButton2    
+     *     
+     * @return javax.swing.JButton    
+     */
+    private JButton getRefreshButton()
+    {
+        if ( refreshButton == null )
+        {
+            refreshButton = new JButton();
+            refreshButton.setText( REFRESH_COMMAND );
+            refreshButton.addActionListener( new java.awt.event.ActionListener()
+            {
+                public void actionPerformed( java.awt.event.ActionEvent e )
+                {
+                    if ( e.getActionCommand().equals( REFRESH_COMMAND ) )
+                    {
+                        refresh();
+                    }
+                }
+            } );
+        }
+        return refreshButton;
+    }
+
+
+    private void refresh()
+    {
+        LOG.info( "Refreshing Sessions UI" );
+        sessionsTable.setModel( new SessionsModel( registry.getSessions() ) );
+        closeItem.setEnabled( false );
+        menuSendNoD.setEnabled( false );
+        showRequests.setEnabled( false );
+        unbindItem.setEnabled( isServiceBound );
+        bindItem.setEnabled( !isServiceBound );
+    }
+
+    public void setRequestor( IoSession requestor )
+    {
+        this.requestor = requestor;
+    }
+
+    /**
+     * This method initializes jMenuItem    
+     *     
+     * @return javax.swing.JMenuItem    
+     */
+    private JMenuItem getUnbindItem()
+    {
+        if ( unbindItem == null )
+        {
+            unbindItem = new JMenuItem();
+            unbindItem.setText( "Unbind Service" );
+            unbindItem.setEnabled( isServiceBound );
+            unbindItem.addActionListener( new java.awt.event.ActionListener()
+            {
+                public void actionPerformed( java.awt.event.ActionEvent e )
+                {
+                    int input = JOptionPane.showConfirmDialog( SessionsFrame.this,
+                        "Selecting no will send a notice of disconnect ONLY.  "
+                            + "\nSelecting yes will send both.  Cancel will abort unbind.",
+                        "Send graceful disconnect before disconnect notice?", JOptionPane.YES_NO_CANCEL_OPTION );
+                    IoAcceptor acceptor = ( IoAcceptor ) requestor.getService();
+                    List<IoSession> sessions = new ArrayList<IoSession>(
+                            acceptor.getManagedSessions( requestor.getServiceAddress() ) );
+
+                    if ( input == JOptionPane.CANCEL_OPTION )
+                    {
+                    }
+                    else if ( input == JOptionPane.NO_OPTION )
+                    {
+                        GracefulShutdownHandler.sendNoticeOfDisconnect( sessions, requestor );
+                        acceptor.unbind( requestor.getServiceAddress() );
+                        isServiceBound = false;
+                        unbindItem.setEnabled( isServiceBound );
+                        bindItem.setEnabled( !isServiceBound );
+                        JOptionPane.showMessageDialog( SessionsFrame.this, "Ldap service for "
+                            + requestor.getLocalAddress() + " has been successfully unbound.", "Success!",
+                            JOptionPane.INFORMATION_MESSAGE );
+                        refresh();
+                    }
+                    else
+                    {
+                        ShutdownDialog dialog = new ShutdownDialog();
+                        setCenteredPosition( SessionsFrame.this, dialog );
+                        dialog.setModal( true );
+                        dialog.setVisible( true );
+
+                        if ( dialog.isSendCanceled() )
+                        {
+                            LOG.debug( "GracefulShutdown was canceled." );
+                            JOptionPane.showMessageDialog( SessionsFrame.this, "Shutdown has been canceled.",
+                                "Graceful Shutdown Aborted", JOptionPane.OK_OPTION );
+                            return;
+                        }
+
+                        LOG.debug( "GracefulShutdown parameters captured." );
+                        int timeOffline = dialog.getTimeOffline();
+                        int delay = dialog.getDelay();
+                        GracefulDisconnect graceful = new GracefulDisconnect( timeOffline, delay );
+                        GracefulShutdownHandler.sendGracefulDisconnect( sessions, graceful, requestor );
+                        acceptor.unbind( requestor.getServiceAddress() );
+                        isServiceBound = false;
+                        unbindItem.setEnabled( isServiceBound );
+                        bindItem.setEnabled( !isServiceBound );
+
+                        // do progress dialog with bypass button to wait for delay time
+                        if ( delay > 0 )
+                        {
+                            ShutdownProgress progress = new ShutdownProgress();
+                            setCenteredPosition( SessionsFrame.this, progress );
+                            progress.setModal( true );
+                            progress.setTime( delay * 1000 );
+                            Thread t = new Thread( progress );
+                            t.start();
+                            progress.setVisible( true );
+                        }
+
+                        // now send the notice of disconnect
+                        GracefulShutdownHandler.sendNoticeOfDisconnect( sessions, requestor );
+                        JOptionPane.showMessageDialog( SessionsFrame.this, "Ldap service for "
+                            + requestor.getLocalAddress() + " has been successfully unbound.", "Success!",
+                            JOptionPane.OK_OPTION );
+                        refresh();
+                    }
+                }
+            } );
+        }
+        return unbindItem;
+    }
+
+
+    private void setCenteredPosition( JFrame frame, Component comp )
+    {
+        Point pt = new Point();
+        Toolkit tk = Toolkit.getDefaultToolkit();
+        Dimension screenSize = tk.getScreenSize();
+        pt.x = ( screenSize.width - frame.getWidth() ) / 2;
+        pt.y = ( screenSize.height - frame.getHeight() ) / 2;
+
+        pt.x += ( frame.getWidth() - comp.getWidth() ) / 2;
+        pt.y += ( frame.getHeight() - comp.getHeight() ) / 2;
+        comp.setLocation( pt );
+    }
+
+
+    /**
+     * This method initializes jMenuItem    
+     *     
+     * @return javax.swing.JMenuItem    
+     */
+    private JMenuItem getBindItem()
+    {
+        if ( bindItem == null )
+        {
+            bindItem = new JMenuItem();
+            bindItem.setText( "Bind Service" );
+            unbindItem.setEnabled( !isServiceBound );
+            bindItem.addActionListener( new java.awt.event.ActionListener()
+            {
+                public void actionPerformed( java.awt.event.ActionEvent e )
+                {
+                    try
+                    {
+                        ( ( IoAcceptor ) requestor.getService() ).bind( requestor.getServiceAddress(), getLdapProvider() );
+                        JOptionPane.showMessageDialog( SessionsFrame.this, "Ldap service " + requestor.getServiceAddress()
+                            + " has been successfully bound.\n" + " Clients may now connect to the server once again.",
+                            "Success!", JOptionPane.INFORMATION_MESSAGE );
+                        isServiceBound = true;
+                        unbindItem.setEnabled( isServiceBound );
+                        bindItem.setEnabled( !isServiceBound );
+                    }
+                    catch ( IOException e1 )
+                    {
+                        LOG.error( "failed to rebind ldap service", e1 );
+                        JOptionPane.showMessageDialog( SessionsFrame.this, e1.getMessage(), "Error encountered!",
+                            JOptionPane.ERROR_MESSAGE );
+                    }
+                }
+            } );
+        }
+        return bindItem;
+    }
+
+
+    public void setLdapProvider( IoHandler ldapProvider )
+    {
+        this.ldapProvider = ldapProvider;
+    }
+
+
+    public IoHandler getLdapProvider()
+    {
+        return ldapProvider;
+    }
+} //  @jve:decl-index=0:visual-constraint="10,10"
diff --git a/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/gui/SessionsModel.java b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/gui/SessionsModel.java
new file mode 100644
index 0000000..e0e5f60
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/gui/SessionsModel.java
@@ -0,0 +1,116 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.ldap.gui;
+
+
+import java.net.InetSocketAddress;
+
+import javax.swing.event.TableModelListener;
+import javax.swing.table.TableModel;
+
+import org.apache.mina.common.IoSession;
+
+
+public class SessionsModel implements TableModel
+{
+    final String[] columns = new String[]
+        { "client address", "client port", "server address", "server port" };
+    final Class<?>[] columnClasses = new Class[]
+        { String.class, Integer.class, String.class, Integer.class };
+    final IoSession[] sessions;
+
+
+    SessionsModel(IoSession[] sessions)
+    {
+        this.sessions = sessions;
+    }
+
+
+    IoSession getIoSession( int row )
+    {
+        return sessions[row];
+    }
+
+
+    public int getRowCount()
+    {
+        return sessions.length;
+    }
+
+
+    public int getColumnCount()
+    {
+        return columns.length;
+    }
+
+
+    public String getColumnName( int columnIndex )
+    {
+        return columns[columnIndex];
+    }
+
+
+    public Class<?> getColumnClass( int columnIndex )
+    {
+        return columnClasses[columnIndex];
+    }
+
+
+    public boolean isCellEditable( int rowIndex, int columnIndex )
+    {
+        return false;
+    }
+
+
+    public Object getValueAt( int rowIndex, int columnIndex )
+    {
+        IoSession session = sessions[rowIndex];
+
+        switch ( columnIndex )
+        {
+            case ( 0 ):
+                return ( ( InetSocketAddress ) session.getRemoteAddress() ).getHostName();
+            case ( 1 ):
+                return new Integer( ( ( InetSocketAddress ) session.getRemoteAddress() ).getPort() );
+            case ( 2 ):
+                return ( ( InetSocketAddress ) session.getLocalAddress() ).getHostName();
+            case ( 3 ):
+                return new Integer( ( ( InetSocketAddress ) session.getLocalAddress() ).getPort() );
+            default:
+                throw new IndexOutOfBoundsException( "column index max is " + ( columns.length - 1 ) );
+        }
+    }
+
+
+    public void setValueAt( Object aValue, int rowIndex, int columnIndex )
+    {
+        throw new UnsupportedOperationException();
+    }
+
+
+    public void addTableModelListener( TableModelListener l )
+    {
+    }
+
+
+    public void removeTableModelListener( TableModelListener l )
+    {
+    }
+}
diff --git a/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/gui/ShutdownDialog.java b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/gui/ShutdownDialog.java
new file mode 100644
index 0000000..41292bc
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/gui/ShutdownDialog.java
@@ -0,0 +1,315 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.ldap.gui;
+
+
+import java.awt.BorderLayout;
+
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JDialog;
+import javax.swing.JButton;
+import javax.swing.JTextField;
+import javax.swing.BoxLayout;
+import javax.swing.JLabel;
+
+
+public class ShutdownDialog extends JDialog
+{
+    private static final long serialVersionUID = -6681747075037789868L;
+
+    private JPanel jContentPane = null;
+    private JPanel inputsPanel = null;
+    private JPanel buttonsPanel = null;
+    private JButton sendButton = null;
+    private JButton cancelButton = null;
+    private JPanel jPanel = null;
+    private JPanel jPanel1 = null;
+    private JLabel jLabel = null;
+    private JTextField timeOfflineField = null;
+    private JLabel jLabel1 = null;
+    private JTextField delayField = null;
+    private boolean canceled = true;
+
+
+    /**
+     * This is the default constructor
+     */
+    public ShutdownDialog()
+    {
+        super();
+        initialize();
+    }
+
+
+    public boolean isSendCanceled()
+    {
+        return canceled;
+    }
+
+
+    public int getTimeOffline()
+    {
+        return Integer.parseInt( timeOfflineField.getText() );
+    }
+
+
+    public int getDelay()
+    {
+        return Integer.parseInt( delayField.getText() );
+    }
+
+
+    public boolean isCanceled()
+    {
+        return canceled;
+    }
+
+
+    /**
+     * This method initializes this
+     * 
+     * @return void
+     */
+    private void initialize()
+    {
+        this.setSize( 248, 171 );
+        this.setTitle( "Shutdown Parameters" );
+        this.setContentPane( getJContentPane() );
+    }
+
+
+    /**
+     * This method initializes jContentPane
+     * 
+     * @return javax.swing.JPanel
+     */
+    private JPanel getJContentPane()
+    {
+        if ( jContentPane == null )
+        {
+            jContentPane = new JPanel();
+            jContentPane.setLayout( new BorderLayout() );
+            jContentPane.add( getJPanel(), java.awt.BorderLayout.CENTER );
+            jContentPane.add( getJPanel2(), java.awt.BorderLayout.SOUTH );
+        }
+        return jContentPane;
+    }
+
+
+    /**
+     * This method initializes jPanel    
+     *     
+     * @return javax.swing.JPanel    
+     */
+    private JPanel getJPanel()
+    {
+        if ( inputsPanel == null )
+        {
+            inputsPanel = new JPanel();
+            inputsPanel.setLayout( null );
+            inputsPanel.setBorder( javax.swing.BorderFactory
+                .createEtchedBorder( javax.swing.border.EtchedBorder.RAISED ) );
+            inputsPanel.add( getJPanel3(), null );
+            inputsPanel.add( getJPanel1(), null );
+        }
+        return inputsPanel;
+    }
+
+
+    /**
+     * This method initializes jPanel    
+     *     
+     * @return javax.swing.JPanel    
+     */
+    private JPanel getJPanel2()
+    {
+        if ( buttonsPanel == null )
+        {
+            buttonsPanel = new JPanel();
+            buttonsPanel.add( getJButton(), null );
+            buttonsPanel.add( getJButton2(), null );
+        }
+        return buttonsPanel;
+    }
+
+
+    /**
+     * This method initializes jButton    
+     *     
+     * @return javax.swing.JButton    
+     */
+    private JButton getJButton()
+    {
+        if ( sendButton == null )
+        {
+            sendButton = new JButton();
+            sendButton.setText( "Send" );
+            sendButton.addActionListener( new java.awt.event.ActionListener()
+            {
+                public void actionPerformed( java.awt.event.ActionEvent e )
+                {
+                    int timeOffline = 0;
+                    try
+                    {
+                        timeOffline = Integer.parseInt( timeOfflineField.getText() );
+                        if ( timeOffline > 720 || timeOffline < 0 )
+                        {
+                            JOptionPane.showMessageDialog( ShutdownDialog.this,
+                                "Time Offline is out of range: 0 ... 720", "Range Problem", JOptionPane.ERROR_MESSAGE );
+                            timeOfflineField.setText( "" );
+                            return;
+                        }
+                    }
+                    catch ( NumberFormatException nfe )
+                    {
+                        JOptionPane.showMessageDialog( ShutdownDialog.this,
+                            "The value for Time Offline is not a number", "Not a Number", JOptionPane.ERROR_MESSAGE );
+                        timeOfflineField.setText( "" );
+                        return;
+                    }
+                    int delay = 0;
+                    try
+                    {
+                        delay = Integer.parseInt( delayField.getText() );
+                        if ( delay > 86400 || delay < 0 )
+                        {
+                            JOptionPane.showMessageDialog( ShutdownDialog.this, "Delay is out of range: 0 ... 86400",
+                                "Range Problem", JOptionPane.ERROR_MESSAGE );
+                            delayField.setText( "" );
+                            return;
+                        }
+                    }
+                    catch ( NumberFormatException nfe )
+                    {
+                        JOptionPane.showMessageDialog( ShutdownDialog.this, "Delay is not a number", "Not a Number",
+                            JOptionPane.ERROR_MESSAGE );
+                        delayField.setText( "" );
+                        return;
+                    }
+                    canceled = false;
+                    setVisible( false );
+                    dispose();
+                }
+            } );
+        }
+        return sendButton;
+    }
+
+
+    /**
+     * This method initializes jButton    
+     *     
+     * @return javax.swing.JButton    
+     */
+    private JButton getJButton2()
+    {
+        if ( cancelButton == null )
+        {
+            cancelButton = new JButton();
+            cancelButton.setText( "Cancel" );
+            cancelButton.setSelected( true );
+            cancelButton.addActionListener( new java.awt.event.ActionListener()
+            {
+                public void actionPerformed( java.awt.event.ActionEvent e )
+                {
+                    canceled = true;
+                    setVisible( false );
+                    dispose();
+                    return;
+                }
+            } );
+        }
+        return cancelButton;
+    }
+
+
+    /**
+     * This method initializes jPanel    
+     *     
+     * @return javax.swing.JPanel    
+     */
+    private JPanel getJPanel3()
+    {
+        if ( jPanel == null )
+        {
+            jLabel = new JLabel();
+            jLabel.setText( "Minutes Offline: " );
+            jPanel = new JPanel();
+            jPanel.setLayout( new BoxLayout( getJPanel3(), BoxLayout.X_AXIS ) );
+            jPanel.setBounds( new java.awt.Rectangle( 35, 28, 163, 16 ) );
+            jPanel.add( jLabel, null );
+            jPanel.add( getJTextField(), null );
+        }
+        return jPanel;
+    }
+
+
+    /**
+     * This method initializes jPanel1    
+     *     
+     * @return javax.swing.JPanel    
+     */
+    private JPanel getJPanel1()
+    {
+        if ( jPanel1 == null )
+        {
+            jLabel1 = new JLabel();
+            jLabel1.setText( "Seconds Delay: " );
+            jPanel1 = new JPanel();
+            jPanel1.setLayout( new BoxLayout( getJPanel1(), BoxLayout.X_AXIS ) );
+            jPanel1.setBounds( new java.awt.Rectangle( 42, 57, 156, 16 ) );
+            jPanel1.add( jLabel1, null );
+            jPanel1.add( getJTextField1(), null );
+        }
+        return jPanel1;
+    }
+
+
+    /**
+     * This method initializes jTextField    
+     *     
+     * @return javax.swing.JTextField    
+     */
+    private JTextField getJTextField()
+    {
+        if ( timeOfflineField == null )
+        {
+            timeOfflineField = new JTextField();
+        }
+        return timeOfflineField;
+    }
+
+
+    /**
+     * This method initializes jTextField1    
+     *     
+     * @return javax.swing.JTextField    
+     */
+    private JTextField getJTextField1()
+    {
+        if ( delayField == null )
+        {
+            delayField = new JTextField();
+        }
+        return delayField;
+    }
+
+} //  @jve:decl-index=0:visual-constraint="10,10"
diff --git a/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/gui/ShutdownProgress.java b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/gui/ShutdownProgress.java
new file mode 100644
index 0000000..5db72c5
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/gui/ShutdownProgress.java
@@ -0,0 +1,175 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.ldap.gui;
+
+
+import java.awt.BorderLayout;
+import java.awt.Cursor;
+
+import javax.swing.JPanel;
+import javax.swing.JDialog;
+import javax.swing.JButton;
+import javax.swing.JProgressBar;
+
+
+public class ShutdownProgress extends JDialog implements Runnable
+{
+    private static final long serialVersionUID = 1L;
+    private JPanel jContentPane = null;
+    private JPanel jPanel = null;
+    private JButton jButton = null;
+    private JProgressBar jProgressBar = null;
+    private long timeMillis = 0;
+    private boolean bypass = false;
+
+
+    public void setTime( long millis )
+    {
+        this.timeMillis = millis;
+    }
+
+
+    public void run()
+    {
+        setCursor( Cursor.getPredefinedCursor( Cursor.WAIT_CURSOR ) );
+        jProgressBar.setEnabled( true );
+        jProgressBar.setMinimum( 0 );
+        jProgressBar.setMaximum( ( int ) timeMillis );
+        jProgressBar.setValue( 0 );
+        jProgressBar.setStringPainted( true );
+        final long startTime = System.currentTimeMillis();
+        while ( System.currentTimeMillis() - startTime < timeMillis && !bypass )
+        {
+            try
+            {
+                Thread.sleep( 100 );
+            }
+            catch ( InterruptedException e )
+            {
+                // TODO Auto-generated catch block
+                e.printStackTrace();
+            }
+            jProgressBar.setString( ( timeMillis - ( System.currentTimeMillis() - startTime ) ) / 1000
+                + " seconds remaining ..." );
+            jProgressBar.setValue( jProgressBar.getValue() + 100 );
+            this.repaint();
+        }
+
+        setCursor( null );
+        setVisible( false );
+        dispose();
+    }
+
+
+    /**
+     * This is the default constructor
+     */
+    public ShutdownProgress()
+    {
+        super();
+        initialize();
+    }
+
+
+    /**
+     * This method initializes this
+     * 
+     * @return void
+     */
+    private void initialize()
+    {
+        this.setSize( 300, 104 );
+        this.setContentPane( getJContentPane() );
+    }
+
+
+    /**
+     * This method initializes jContentPane
+     * 
+     * @return javax.swing.JPanel
+     */
+    private JPanel getJContentPane()
+    {
+        if ( jContentPane == null )
+        {
+            jContentPane = new JPanel();
+            jContentPane.setLayout( new BorderLayout() );
+            jContentPane.add( getJPanel(), java.awt.BorderLayout.SOUTH );
+            jContentPane.add( getJProgressBar(), java.awt.BorderLayout.CENTER );
+        }
+        return jContentPane;
+    }
+
+
+    /**
+     * This method initializes jPanel    
+     *     
+     * @return javax.swing.JPanel    
+     */
+    private JPanel getJPanel()
+    {
+        if ( jPanel == null )
+        {
+            jPanel = new JPanel();
+            jPanel.add( getJButton(), null );
+        }
+        return jPanel;
+    }
+
+
+    /**
+     * This method initializes jButton    
+     *     
+     * @return javax.swing.JButton    
+     */
+    private JButton getJButton()
+    {
+        if ( jButton == null )
+        {
+            jButton = new JButton();
+            jButton.setText( "Bypass Delay" );
+            jButton.setText( "Bypass Delay" );
+            jButton.addActionListener( new java.awt.event.ActionListener()
+            {
+                public void actionPerformed( java.awt.event.ActionEvent e )
+                {
+                    bypass = true;
+                }
+            } );
+        }
+        return jButton;
+    }
+
+
+    /**
+     * This method initializes jProgressBar    
+     *     
+     * @return javax.swing.JProgressBar    
+     */
+    private JProgressBar getJProgressBar()
+    {
+        if ( jProgressBar == null )
+        {
+            jProgressBar = new JProgressBar();
+        }
+        return jProgressBar;
+    }
+
+} //  @jve:decl-index=0:visual-constraint="10,10"
diff --git a/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/gui/package-info.java b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/gui/package-info.java
new file mode 100644
index 0000000..860e551
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/gui/package-info.java
@@ -0,0 +1,27 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+/**
+ * Provides a diagnostic UI for the LDAP protocol provider.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+package org.apache.directory.server.ldap.gui;
diff --git a/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/AbandonHandler.java b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/AbandonHandler.java
new file mode 100644
index 0000000..174b368
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/AbandonHandler.java
@@ -0,0 +1,42 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.ldap.handlers;
+
+
+import org.apache.directory.shared.ldap.message.AbandonRequest;
+import org.apache.mina.common.IoSession;
+
+
+/**
+ * Handler for {@link org.apache.directory.shared.ldap.message.AbandonRequest}s.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public abstract class AbandonHandler extends AbstractLdapHandler
+{
+    public final void messageReceived( IoSession session, Object request ) throws Exception
+    {
+        abandonMessageReceived( session, ( AbandonRequest ) request );
+    }
+
+
+    protected abstract void abandonMessageReceived( IoSession session, AbandonRequest abandonRequest ) throws Exception;
+}
diff --git a/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/AbstractLdapHandler.java b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/AbstractLdapHandler.java
new file mode 100644
index 0000000..a600499
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/AbstractLdapHandler.java
@@ -0,0 +1,113 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.ldap.handlers;
+
+
+import org.apache.directory.server.ldap.LdapServer;
+import org.apache.directory.server.ldap.SessionRegistry;
+import org.apache.directory.shared.ldap.message.Message;
+import org.apache.directory.shared.ldap.message.MutableControl;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.mina.common.IoFilterChain;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.handler.demux.MessageHandler;
+
+import javax.naming.NamingException;
+import javax.naming.ldap.LdapContext;
+
+
+/**
+ * An abstract class to handle common methods used by all the handlers
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 541827 $
+ */
+public abstract class AbstractLdapHandler implements MessageHandler
+{
+    private LdapServer ldapServer;
+
+
+    public final LdapServer getProtocolProvider()
+    {
+        return ldapServer;
+    }
+
+
+    public final void setProtocolProvider( LdapServer provider )
+    {
+        this.ldapServer = provider;
+    }
+    
+    
+    /**
+     * Checks to see if confidentiality requirements are met.  If the 
+     * LdapServer requires confidentiality and the SSLFilter is engaged
+     * this will return true.  If confidentiality is not required this 
+     * will return true.  If confidentially is required and the SSLFilter
+     * is not engaged in the IoFilterChain this will return false.
+     * 
+     * This method is used by handlers to determine whether to send back
+     * {@link ResultCodeEnum#CONFIDENTIALITY_REQUIRED} error responses back
+     * to clients.
+     * 
+     * @param session the MINA IoSession to check for TLS security
+     * @return true if confidentiality requirement is met, false otherwise
+     */
+    public final boolean isConfidentialityRequirementSatisfied( IoSession session )
+    {
+    	
+    	if ( ! ldapServer.isConfidentialityRequired() )
+    	{
+    		return true;
+    	}
+    	
+        IoFilterChain chain = session.getFilterChain();
+        return chain.contains( "sslFilter" );
+    }
+
+
+    public final SessionRegistry getSessionRegistry()
+    {
+        return this.ldapServer.getRegistry();
+    }
+
+
+    /**
+     * Return an array containing the controls for this message.
+     *  
+     * @param context The context in which we will store teh found controls
+     * @param message The message for which we want to extract the controls
+     */
+    protected void setRequestControls( LdapContext context, Message message ) throws NamingException
+    {
+        MutableControl[] controls = null;
+        
+        if ( message.getControls() != null )
+        {
+            int nbControls = message.getControls().size();
+            
+            if ( nbControls != 0 )
+            {
+                controls = new MutableControl[ nbControls ];
+                context.setRequestControls( message.getControls().values().toArray( controls ) );
+            }
+        }
+    }
+}
diff --git a/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/AddHandler.java b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/AddHandler.java
new file mode 100644
index 0000000..4d38553
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/AddHandler.java
@@ -0,0 +1,44 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.ldap.handlers;
+
+
+import org.apache.directory.shared.ldap.message.AddRequest;
+
+import org.apache.mina.common.IoSession;
+import org.apache.mina.handler.demux.MessageHandler;
+
+
+/**
+ * A single reply handler for {@link org.apache.directory.shared.ldap.message.AddRequest}s.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public abstract class AddHandler extends AbstractLdapHandler implements MessageHandler
+{
+    public final void messageReceived( IoSession session, Object request ) throws Exception
+    {
+        addMessageReceived( session, ( AddRequest ) request );
+    }
+
+
+    protected abstract void addMessageReceived( IoSession session, AddRequest req ) throws Exception;
+}
diff --git a/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/BindHandler.java b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/BindHandler.java
new file mode 100644
index 0000000..8647414
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/BindHandler.java
@@ -0,0 +1,50 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.ldap.handlers;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.shared.ldap.message.BindRequest;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.handler.demux.MessageHandler;
+
+
+/**
+ * A single reply handler for {@link org.apache.directory.shared.ldap.message.BindRequest}s.
+ * 
+ * Implements server-side of RFC 2222, sections 4.2 and 4.3.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public abstract class BindHandler extends AbstractLdapHandler implements MessageHandler
+{
+    public abstract void setDirectoryService( DirectoryService directoryService );
+    protected abstract void bindMessageReceived( IoSession session, BindRequest req ) throws Exception;
+
+
+    /**
+     * Deal with a received BindRequest
+     */
+    public final void messageReceived( IoSession session, Object message ) throws Exception
+    {
+        bindMessageReceived( session, ( BindRequest ) message );
+    }
+}
diff --git a/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/CompareHandler.java b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/CompareHandler.java
new file mode 100644
index 0000000..600ca2f
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/CompareHandler.java
@@ -0,0 +1,43 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.ldap.handlers;
+
+
+import org.apache.directory.shared.ldap.message.CompareRequest;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.handler.demux.MessageHandler;
+
+
+/**
+ * A single reply handler for {@link org.apache.directory.shared.ldap.message.CompareRequest}s.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public abstract class CompareHandler extends AbstractLdapHandler implements MessageHandler
+{
+    public final void messageReceived( IoSession session, Object request ) throws Exception
+    {
+        compareMessageReceived( session, ( CompareRequest ) request );
+    }
+
+
+    protected abstract void compareMessageReceived( IoSession session, CompareRequest compareRequest ) throws Exception;
+}
diff --git a/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/DefaultAbandonHandler.java b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/DefaultAbandonHandler.java
new file mode 100644
index 0000000..d75162e
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/DefaultAbandonHandler.java
@@ -0,0 +1,89 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.ldap.handlers;
+
+
+import org.apache.directory.shared.ldap.message.AbandonRequest;
+import org.apache.directory.shared.ldap.message.AbandonableRequest;
+import org.apache.directory.shared.ldap.message.Request;
+import org.apache.mina.common.IoSession;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * Handler for {@link AbandonRequest}s.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class DefaultAbandonHandler extends AbandonHandler
+{
+    private static final Logger LOG = LoggerFactory.getLogger( AbandonHandler.class );
+
+    /** Speedup for logs */
+    private static final boolean IS_DEBUG = LOG.isDebugEnabled();
+
+    
+    public void abandonMessageReceived( IoSession session, AbandonRequest req ) throws Exception
+    {
+        int abandonedId = req.getAbandoned();
+
+        if ( abandonedId < 0 )
+        {
+            return;
+        }
+
+        Request abandonedRequest = getSessionRegistry().getOutstandingRequest( session, abandonedId );
+
+        if ( abandonedRequest == null )
+        {
+            if ( LOG.isWarnEnabled() )
+            {
+                LOG.warn( "Got abandon request from client " + session + " but request must have already "
+                    + "terminated.  Abandon request " + req + " had no effect." );
+            }
+            return;
+        }
+
+        if ( abandonedRequest instanceof AbandonableRequest )
+        {
+            LOG
+                .warn( "Abandon, Bind, Unbind, and StartTLS operations cannot be abandoned.  Abandon request will be ignored." );
+        }
+
+        ( ( AbandonableRequest ) abandonedRequest ).abandon();
+        if ( getSessionRegistry().removeOutstandingRequest( session, abandonedId ) == null )
+        {
+            if ( LOG.isWarnEnabled() )
+            {
+                LOG.warn( "Got abandon request from client " + session + " but request must have already "
+                    + "terminated." );
+            }
+        }
+        else
+        {
+            if ( IS_DEBUG )
+            {
+                LOG.debug( "Abandoned request:  ", req );
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/DefaultAddHandler.java b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/DefaultAddHandler.java
new file mode 100644
index 0000000..d866000
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/DefaultAddHandler.java
@@ -0,0 +1,143 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.ldap.handlers;
+
+
+import javax.naming.Context;
+import javax.naming.NamingException;
+import javax.naming.ReferralException;
+import javax.naming.ldap.LdapContext;
+
+import org.apache.directory.shared.ldap.exception.LdapException;
+import org.apache.directory.shared.ldap.message.AddRequest;
+import org.apache.directory.shared.ldap.message.LdapResult;
+import org.apache.directory.shared.ldap.message.ManageDsaITControl;
+import org.apache.directory.shared.ldap.message.ReferralImpl;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.util.ExceptionUtils;
+
+import org.apache.mina.common.IoSession;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * A single reply handler for {@link AddRequest}s.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class DefaultAddHandler extends AddHandler
+{
+    private static final Logger LOG = LoggerFactory.getLogger( AddHandler.class );
+
+    /** Speedup for logs */
+    private static final boolean IS_DEBUG = LOG.isDebugEnabled();
+
+
+    protected void addMessageReceived( IoSession session, AddRequest req ) throws Exception
+    {
+        LdapResult result = req.getResultResponse().getLdapResult();
+
+        if ( IS_DEBUG )
+        {
+            LOG.debug( "Received an Add message:  {}", req.toString() );
+        }
+
+        try
+        {
+            // protect against insecure conns when confidentiality is required 
+            if ( ! isConfidentialityRequirementSatisfied( session ) )
+            {
+            	result.setResultCode( ResultCodeEnum.CONFIDENTIALITY_REQUIRED );
+            	result.setErrorMessage( "Confidentiality (TLS secured connection) is required." );
+            	session.write( req.getResultResponse() );
+            	return;
+            }
+            
+            LdapContext ctx = getSessionRegistry().getLdapContext( session, null, true );
+
+            if ( req.getControls().containsKey( ManageDsaITControl.CONTROL_OID ) )
+            {
+                ctx.addToEnvironment( Context.REFERRAL, "ignore" );
+            }
+            else
+            {
+                ctx.addToEnvironment( Context.REFERRAL, "throw" );
+            }
+
+            // Inject controls into the context
+            setRequestControls( ctx, req );
+            ctx.createSubcontext( req.getEntry(), req.getAttributes() );
+            result.setResultCode( ResultCodeEnum.SUCCESS );
+            req.getResultResponse().addAll( ctx.getResponseControls() );
+            session.write( req.getResultResponse() );
+        }
+        catch ( ReferralException e )
+        {
+            ReferralImpl refs = new ReferralImpl();
+            result.setReferral( refs );
+            result.setResultCode( ResultCodeEnum.REFERRAL );
+            result.setErrorMessage( "Encountered referral attempting to handle add request." );
+            /* coming up null causing a NPE */
+            // result.setMatchedDn( e.getResolvedName().toString() );
+            do
+            {
+                refs.addLdapUrl( ( String ) e.getReferralInfo() );
+            }
+            while ( e.skipReferral() );
+            session.write( req.getResultResponse() );
+        }
+        catch ( NamingException e )
+        {
+            String msg = "failed to add entry " + req.getEntry() + ": " + e.getMessage();
+
+            if ( LOG.isDebugEnabled() )
+            {
+                msg += ":\n" + ExceptionUtils.getStackTrace( e );
+            }
+
+            ResultCodeEnum code;
+
+            if ( e instanceof LdapException )
+            {
+                code = ( ( LdapException ) e ).getResultCode();
+            }
+            else
+            {
+                code = ResultCodeEnum.getBestEstimate( e, req.getType() );
+            }
+
+            result.setResultCode( code );
+            result.setErrorMessage( msg );
+
+            if ( ( e.getResolvedName() != null )
+                && ( ( code == ResultCodeEnum.NO_SUCH_OBJECT ) || ( code == ResultCodeEnum.ALIAS_PROBLEM )
+                    || ( code == ResultCodeEnum.INVALID_DN_SYNTAX ) || ( code == ResultCodeEnum.ALIAS_DEREFERENCING_PROBLEM ) ) )
+            {
+                result.setMatchedDn( (LdapDN)e.getResolvedName() );
+            }
+
+            session.write( req.getResultResponse() );
+        }
+    }
+}
\ No newline at end of file
diff --git a/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/DefaultBindHandler.java b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/DefaultBindHandler.java
new file mode 100644
index 0000000..dd9bbca
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/DefaultBindHandler.java
@@ -0,0 +1,758 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.ldap.handlers;
+
+
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.Set;
+
+import javax.naming.Context;
+import javax.naming.NamingException;
+import javax.naming.directory.DirContext;
+import javax.naming.ldap.InitialLdapContext;
+import javax.naming.ldap.LdapContext;
+import javax.naming.spi.InitialContextFactory;
+import javax.security.auth.Subject;
+import javax.security.auth.kerberos.KerberosKey;
+import javax.security.auth.kerberos.KerberosPrincipal;
+import javax.security.sasl.Sasl;
+import javax.security.sasl.SaslException;
+import javax.security.sasl.SaslServer;
+
+import org.apache.directory.server.constants.ServerDNConstants;
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.authn.LdapPrincipal;
+import org.apache.directory.server.core.jndi.ServerLdapContext;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+import org.apache.directory.server.kerberos.shared.store.PrincipalStoreEntry;
+import org.apache.directory.server.kerberos.shared.store.operations.GetPrincipal;
+import org.apache.directory.server.ldap.LdapServer;
+import org.apache.directory.server.ldap.SessionRegistry;
+import org.apache.directory.server.ldap.handlers.bind.MechanismHandler;
+import org.apache.directory.server.ldap.handlers.bind.SaslFilter;
+import org.apache.directory.server.protocol.shared.ServiceConfigurationException;
+import org.apache.directory.shared.ldap.constants.AuthenticationLevel;
+import org.apache.directory.shared.ldap.constants.SupportedSaslMechanisms;
+import org.apache.directory.shared.ldap.exception.LdapException;
+import org.apache.directory.shared.ldap.message.BindRequest;
+import org.apache.directory.shared.ldap.message.BindResponse;
+import org.apache.directory.shared.ldap.message.LdapResult;
+import org.apache.directory.shared.ldap.message.ManageDsaITControl;
+import org.apache.directory.shared.ldap.message.MutableControl;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.util.ExceptionUtils;
+import org.apache.mina.common.IoFilterChain;
+import org.apache.mina.common.IoSession;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * A single reply handler for {@link BindRequest}s.
+ *
+ * Implements server-side of RFC 2222, sections 4.2 and 4.3.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class DefaultBindHandler extends BindHandler
+{
+    private static final Logger LOG = LoggerFactory.getLogger( BindHandler.class );
+
+    /** An empty Control array used to get back the controls if any */
+    private static final MutableControl[] EMPTY_CONTROL = new MutableControl[0];
+
+    private DirContext ctx;
+
+    /**
+     * A Hashed Adapter mapping SASL mechanisms to their handlers.
+     */
+    private Map<String, MechanismHandler> handlers;
+
+    private SessionRegistry registry;
+
+    
+    /**
+     * Creates a new instance of BindHandler.
+     */
+    public DefaultBindHandler()
+    {
+        registry = null;
+        handlers = null;
+        ctx = null;
+    }
+
+
+    /**
+     * Set the mechanisms handler map.
+     * 
+     * @param handlers The associations btween a machanism and its handler
+     */
+    public void setSaslMechanismHandlers( Map<String, MechanismHandler> handlers )
+    {
+        this.handlers = handlers;
+    }
+
+
+    /**
+     * Associated a Registry to the handler
+     *
+     * @param registry The registry to attach
+     */
+    public void setSessionRegistry( SessionRegistry registry )
+    {
+        this.registry = registry;
+    }
+
+
+    public void setDirectoryService( DirectoryService directoryService )
+    {
+    }
+
+
+    /**
+     * Create an environment object and inject the Bond informations collected
+     * from the BindRequest message :
+     *  - the principal : the user's who issued the Bind request
+     *  - the credentials : principal's password, if auth level is 'simple'
+     *  - the authentication level : either 'simple' or 'strong'
+     *  - how to handle referral : either 'ignore' or 'throw'
+     *  
+     * @param bindRequest the bind request object
+     * @param authenticationLevel the level of the authentication
+     * @return the environment for the session
+     */
+    private Hashtable<String, Object> getEnvironment( BindRequest bindRequest, String authenticationLevel )
+    {
+        LdapDN principal = bindRequest.getName();
+
+        /**
+         * For simple, this is a password.  For strong, this is unused.
+         */
+        Object credentials = bindRequest.getCredentials();
+
+        if ( LOG.isDebugEnabled() )
+        {
+            LOG.debug( "{} {}", Context.SECURITY_PRINCIPAL, principal );
+            LOG.debug( "{} {}", Context.SECURITY_CREDENTIALS, credentials );
+            LOG.debug( "{} {}", Context.SECURITY_AUTHENTICATION, authenticationLevel );
+        }
+
+        // clone the environment first then add the required security settings
+        Hashtable<String, Object> env = getSessionRegistry().getEnvironmentByCopy();
+
+        // Store the principal
+        env.put( Context.SECURITY_PRINCIPAL, principal );
+
+        // Store the credentials
+        if ( credentials != null )
+        {
+            env.put( Context.SECURITY_CREDENTIALS, credentials );
+        }
+
+        // Store the authentication level
+        env.put( Context.SECURITY_AUTHENTICATION, authenticationLevel );
+
+        // Store the referral handling method
+        if ( bindRequest.getControls().containsKey( ManageDsaITControl.CONTROL_OID ) )
+        {
+            env.put( Context.REFERRAL, "ignore" );
+        }
+        else
+        {
+            env.put( Context.REFERRAL, "throw" );
+        }
+
+        return env;
+    }
+
+    /**
+     * Create the Context associated with the BindRequest.
+     *
+     * @param bindRequest the bind request
+     * @param env the environment to create the context with
+     * @param session the MINA IoSession
+     * @return the ldap context for the session
+     */
+    private LdapContext getLdapContext( IoSession session, BindRequest bindRequest, Hashtable<String, Object> env )
+    {
+        LdapResult result = bindRequest.getResultResponse().getLdapResult();
+        LdapContext context = null;
+
+        try
+        {
+            if ( env.containsKey( "server.use.factory.instance" ) )
+            {
+                InitialContextFactory factory = ( InitialContextFactory ) env.get( "server.use.factory.instance" );
+
+                if ( factory == null )
+                {
+                    LOG.error( "The property 'server.use.factory.instance'  was set in env but was null" );
+                    throw new NullPointerException( "server.use.factory.instance was set in env but was null" );
+                }
+
+                // Bind is a special case where we have to use the referral property to deal
+                context = ( LdapContext ) factory.getInitialContext( env );
+            }
+            else
+            {
+                //noinspection SuspiciousToArrayCall
+                MutableControl[] connCtls = bindRequest.getControls().values().toArray( EMPTY_CONTROL );
+                context = new InitialLdapContext( env, connCtls );
+            }
+        }
+        catch ( NamingException e )
+        {
+            ResultCodeEnum code = null;
+
+            if ( e instanceof LdapException )
+            {
+                code = ( ( LdapException ) e ).getResultCode();
+                result.setResultCode( code );
+            }
+            else
+            {
+                code = ResultCodeEnum.getBestEstimate( e, bindRequest.getType() );
+                result.setResultCode( code );
+            }
+
+            String msg = "Bind failed: " + e.getMessage();
+
+            if ( LOG.isDebugEnabled() )
+            {
+                msg += ":\n" + ExceptionUtils.getStackTrace( e );
+                msg += "\n\nBindRequest = \n" + bindRequest.toString();
+                LOG.debug(  msg  );
+            }
+
+            if ( ( e.getResolvedName() != null )
+                && ( ( code == ResultCodeEnum.NO_SUCH_OBJECT ) || ( code == ResultCodeEnum.ALIAS_PROBLEM )
+                    || ( code == ResultCodeEnum.INVALID_DN_SYNTAX ) || ( code == ResultCodeEnum.ALIAS_DEREFERENCING_PROBLEM ) ) )
+            {
+                result.setMatchedDn( ( LdapDN ) e.getResolvedName() );
+            }
+
+            result.setErrorMessage( msg );
+            session.write( bindRequest.getResultResponse() );
+            context = null;
+        }
+
+        return context;
+    }
+
+    /**
+     * This method handles 'simple' authentication. 
+     *
+     * @param bindRequest the bind request
+     * @param session the mina IoSession
+     * @throws NamingException if the bind fails
+     */
+    private void handleSimpleAuth( IoSession session, BindRequest bindRequest ) throws NamingException
+    {
+        LdapResult bindResult = bindRequest.getResultResponse().getLdapResult();
+
+        // Initialize the environment which will be used to create the context
+        Hashtable<String, Object> env = getEnvironment( bindRequest, AuthenticationLevel.SIMPLE.toString() );
+
+        // Now, get the context
+        LdapContext context = getLdapContext( session, bindRequest, env );
+
+        // Test that we successfully got one. If not, an error has already been returned.
+        if ( context != null )
+        {
+            ServerLdapContext newCtx = ( ServerLdapContext ) context.lookup( "" );
+            setRequestControls( newCtx, bindRequest );
+            getSessionRegistry().setLdapContext( session, newCtx );
+            bindResult.setResultCode( ResultCodeEnum.SUCCESS );
+            BindResponse response = ( BindResponse ) bindRequest.getResultResponse();
+            response.addAll( newCtx.getResponseControls() );
+            session.write( response );
+            LOG.debug( "Returned SUCCESS message." );
+        }
+    }
+
+    
+    /**
+     * Handle the SASL authentication.
+     *
+     * @param session The associated Session
+     * @param message The BindRequest received
+     * @throws Exception If the authentication cannot be done
+     */
+    public void handleSaslAuth( IoSession session, Object message ) throws Exception
+    {
+        LdapServer ldapServer = ( LdapServer )
+                session.getAttribute( LdapServer.class.toString() );
+
+        Map<String, String> saslProps = new HashMap<String, String>();
+        saslProps.put( Sasl.QOP, ldapServer.getSaslQopString() );
+        saslProps.put( "com.sun.security.sasl.digest.realm", getActiveRealms( ldapServer ) );
+        session.setAttribute( "saslProps", saslProps );
+
+        session.setAttribute( "saslHost", ldapServer.getSaslHost() );
+        session.setAttribute( "baseDn", ldapServer.getSearchBaseDn() );
+
+        Set<String> activeMechanisms = ldapServer.getSupportedMechanisms();
+
+        if ( activeMechanisms.contains( SupportedSaslMechanisms.GSSAPI ) )
+        {
+            try
+            {
+                Subject saslSubject = getSubject( ldapServer );
+                session.setAttribute( "saslSubject", saslSubject );
+            }
+            catch ( ServiceConfigurationException sce )
+            {
+                activeMechanisms.remove( "GSSAPI" );
+                LOG.warn( sce.getMessage() );
+            }
+        }
+
+        BindRequest bindRequest = ( BindRequest ) message;
+
+        // Guard clause:  Reject unsupported SASL mechanisms.
+        if ( !ldapServer.getSupportedMechanisms().contains( bindRequest.getSaslMechanism() ) )
+        {
+            LOG.error( "Bind error : {} mechanism not supported. Please check the server.xml " + 
+                "configuration file (supportedMechanisms field)", 
+                bindRequest.getSaslMechanism() );
+
+            LdapResult bindResult = bindRequest.getResultResponse().getLdapResult();
+            bindResult.setResultCode( ResultCodeEnum.AUTH_METHOD_NOT_SUPPORTED );
+            bindResult.setErrorMessage( bindRequest.getSaslMechanism() + " is not a supported mechanism." );
+            session.write( bindRequest.getResultResponse() );
+            return;
+        }
+
+        handleSasl( session, bindRequest );
+    }
+
+    
+    /**
+     * Deal with a SASL bind request
+     * 
+     * @param session The IoSession for this Bind Request
+     * @param bindRequest The BindRequest received
+     * 
+     * @exception Exception if the mechanism cannot handle the authentication
+     */
+    public void handleSasl( IoSession session, BindRequest bindRequest ) throws Exception
+    {
+        String sessionMechanism = bindRequest.getSaslMechanism();
+
+        if ( sessionMechanism.equals( SupportedSaslMechanisms.PLAIN ) )
+        {
+            /*
+             * This is the principal name that will be used to bind to the DIT.
+             */
+            session.setAttribute( Context.SECURITY_PRINCIPAL, bindRequest.getName() );
+
+            /*
+             * These are the credentials that will be used to bind to the DIT.
+             * For the simple mechanism, this will be a password, possibly one-way hashed.
+             */
+            session.setAttribute( Context.SECURITY_CREDENTIALS, bindRequest.getCredentials() );
+
+            getLdapContext( session, bindRequest );
+        }
+        else
+        {
+            MechanismHandler mechanismHandler = handlers.get( sessionMechanism );
+
+            if ( mechanismHandler == null )
+            {
+                LOG.error( "Handler unavailable for " + sessionMechanism );
+                throw new IllegalArgumentException( "Handler unavailable for " + sessionMechanism );
+            }
+
+            SaslServer ss = mechanismHandler.handleMechanism( session, bindRequest );
+            
+            LdapResult result = bindRequest.getResultResponse().getLdapResult();
+
+            if ( !ss.isComplete() )
+            {
+                try
+                {
+                    /*
+                     * SaslServer will throw an exception if the credentials are null.
+                     */
+                    if ( bindRequest.getCredentials() == null )
+                    {
+                        bindRequest.setCredentials( new byte[0] );
+                    }
+
+                    byte[] tokenBytes = ss.evaluateResponse( bindRequest.getCredentials() );
+
+                    if ( ss.isComplete() )
+                    {
+                        if ( tokenBytes != null )
+                        {
+                            /*
+                             * There may be a token to return to the client.  We set it here
+                             * so it will be returned in a SUCCESS message, after an LdapContext
+                             * has been initialized for the client.
+                             */
+                            session.setAttribute( "saslCreds", tokenBytes );
+                        }
+
+                        /*
+                         * If we got here, we're ready to try getting an initial LDAP context.
+                         */
+                        getLdapContext( session, bindRequest );
+                    }
+                    else
+                    {
+                        LOG.info( "Continuation token had length " + tokenBytes.length );
+                        result.setResultCode( ResultCodeEnum.SASL_BIND_IN_PROGRESS );
+                        BindResponse resp = ( BindResponse ) bindRequest.getResultResponse();
+                        resp.setServerSaslCreds( tokenBytes );
+                        session.write( resp );
+                        LOG.debug( "Returning final authentication data to client to complete context." );
+                    }
+                }
+                catch ( SaslException se )
+                {
+                    LOG.error( se.getMessage() );
+                    result.setResultCode( ResultCodeEnum.INVALID_CREDENTIALS );
+                    result.setErrorMessage( se.getMessage() );
+                    session.write( bindRequest.getResultResponse() );
+                }
+            }
+        }
+    }
+
+    
+    /**
+     * Create a list of all the configured realms.
+     * 
+     * @param ldapServer the LdapServer for which we want to get the realms
+     * @return a list of relms, separated by spaces
+     */
+    private String getActiveRealms( LdapServer ldapServer )
+    {
+        StringBuilder realms = new StringBuilder();
+        boolean isFirst = true;
+
+        for ( String realm:ldapServer.getSaslRealms() )
+        {
+            if ( isFirst )
+            {
+                isFirst = false;
+            }
+            else
+            {
+                realms.append( ' ' );
+            }
+            
+            realms.append( realm );
+        }
+
+        return realms.toString();
+    }
+
+
+    private Subject getSubject( LdapServer ldapServer ) //throws ServiceConfigurationException
+    {
+        String servicePrincipalName = ldapServer.getSaslPrincipal();
+
+        KerberosPrincipal servicePrincipal = new KerberosPrincipal( servicePrincipalName );
+        GetPrincipal getPrincipal = new GetPrincipal( servicePrincipal );
+
+        PrincipalStoreEntry entry = null;
+
+        try
+        {
+            entry = findPrincipal( ldapServer, getPrincipal );
+        }
+        catch ( ServiceConfigurationException sce )
+        {
+            String message = "Service principal " + servicePrincipalName + " not found at search base DN "
+                + ldapServer.getSearchBaseDn() + ".";
+            throw new ServiceConfigurationException( message, sce );
+        }
+
+        if ( entry == null )
+        {
+            String message = "Service principal " + servicePrincipalName + " not found at search base DN "
+                + ldapServer.getSearchBaseDn() + ".";
+            throw new ServiceConfigurationException( message );
+        }
+
+        Subject subject = new Subject();
+
+        for ( EncryptionType encryptionType:entry.getKeyMap().keySet() )
+        {
+            EncryptionKey key = entry.getKeyMap().get( encryptionType );
+
+            byte[] keyBytes = key.getKeyValue();
+            int type = key.getKeyType().getOrdinal();
+            int kvno = key.getKeyVersion();
+
+            KerberosKey serviceKey = new KerberosKey( servicePrincipal, keyBytes, type, kvno );
+
+            subject.getPrivateCredentials().add( serviceKey );
+        }
+
+        return subject;
+    }
+    
+    
+    private PrincipalStoreEntry findPrincipal( LdapServer ldapServer, GetPrincipal getPrincipal )
+    {
+        if ( ctx == null )
+        {
+            try
+            {
+                LdapDN adminDN = new LdapDN( ServerDNConstants.ADMIN_SYSTEM_DN );
+                
+                adminDN.normalize( 
+                    ldapServer.getDirectoryService().getRegistries().getAttributeTypeRegistry().getNormalizerMapping() );
+                LdapPrincipal principal = new LdapPrincipal( adminDN, AuthenticationLevel.SIMPLE );
+                ctx = ldapServer.getDirectoryService().getJndiContext( principal, ldapServer.getSearchBaseDn() );
+            }
+            catch ( NamingException ne )
+            {
+                String message = "Failed to get initial context " + ldapServer.getSearchBaseDn();
+                throw new ServiceConfigurationException( message, ne );
+            }
+        }
+
+        return (PrincipalStoreEntry)getPrincipal.execute( ctx, null );
+    }    
+    
+    
+    private Hashtable<String, Object> getEnvironment( IoSession session, BindRequest bindRequest )
+    {
+        Object principal = session.getAttribute( Context.SECURITY_PRINCIPAL );
+
+        /**
+         * For simple, this is a password.  For strong, this is unused.
+         */
+        Object credentials = session.getAttribute( Context.SECURITY_CREDENTIALS );
+
+        String sessionMechanism = bindRequest.getSaslMechanism();
+        String authenticationLevel = getAuthenticationLevel( sessionMechanism );
+
+        LOG.debug( "{} {}", Context.SECURITY_PRINCIPAL, principal );
+        LOG.debug( "{} {}", Context.SECURITY_CREDENTIALS, credentials );
+        LOG.debug( "{} {}", Context.SECURITY_AUTHENTICATION, authenticationLevel );
+
+        // clone the environment first then add the required security settings
+        Hashtable<String, Object> env = registry.getEnvironmentByCopy();
+        env.put( Context.SECURITY_PRINCIPAL, principal );
+
+        if ( credentials != null )
+        {
+            env.put( Context.SECURITY_CREDENTIALS, credentials );
+        }
+
+        env.put( Context.SECURITY_AUTHENTICATION, authenticationLevel );
+
+        if ( bindRequest.getControls().containsKey( ManageDsaITControl.CONTROL_OID ) )
+        {
+            env.put( Context.REFERRAL, "ignore" );
+        }
+        else
+        {
+            env.put( Context.REFERRAL, "throw" );
+        }
+
+        return env;
+    }
+    
+    
+    private void getLdapContext( IoSession session, BindRequest bindRequest ) //throws Exception
+    {
+        Hashtable<String, Object> env = getEnvironment( session, bindRequest );
+        LdapResult result = bindRequest.getResultResponse().getLdapResult();
+        LdapContext context = null;
+
+        try
+        {
+            MutableControl[] connCtls = bindRequest.getControls().values().toArray( EMPTY_CONTROL );
+            context = new InitialLdapContext( env, connCtls );
+
+            registry.setLdapContext( session, context );
+            
+            // add the bind response controls 
+            bindRequest.getResultResponse().addAll( context.getResponseControls() );
+            
+            returnSuccess( session, bindRequest );
+        }
+        catch ( NamingException e )
+        {
+            ResultCodeEnum code = null;
+
+            if ( e instanceof LdapException )
+            {
+                code = ( ( LdapException ) e ).getResultCode();
+                result.setResultCode( code );
+            }
+            else
+            {
+                code = ResultCodeEnum.getBestEstimate( e, bindRequest.getType() );
+                result.setResultCode( code );
+            }
+
+            String msg = "Bind failed: " + e.getMessage();
+
+            if ( LOG.isDebugEnabled() )
+            {
+                msg += ":\n" + ExceptionUtils.getStackTrace( e );
+                msg += "\n\nBindRequest = \n" + bindRequest.toString();
+            }
+
+            if ( ( e.getResolvedName() != null )
+                && ( ( code == ResultCodeEnum.NO_SUCH_OBJECT ) || ( code == ResultCodeEnum.ALIAS_PROBLEM )
+                    || ( code == ResultCodeEnum.INVALID_DN_SYNTAX ) || ( code == ResultCodeEnum.ALIAS_DEREFERENCING_PROBLEM ) ) )
+            {
+                result.setMatchedDn( ( LdapDN ) e.getResolvedName() );
+            }
+
+            result.setErrorMessage( msg );
+            session.write( bindRequest.getResultResponse() );
+        }
+    }
+
+    
+    private void returnSuccess( IoSession session, BindRequest bindRequest ) //throws Exception
+    {
+        /*
+         * We have now both authenticated the client and retrieved a JNDI context for them.
+         * We can return a success message to the client.
+         */
+        LdapResult result = bindRequest.getResultResponse().getLdapResult();
+
+        byte[] tokenBytes = ( byte[] ) session.getAttribute( "saslCreds" );
+
+        result.setResultCode( ResultCodeEnum.SUCCESS );
+        BindResponse response = ( BindResponse ) bindRequest.getResultResponse();
+        response.setServerSaslCreds( tokenBytes );
+
+        String sessionMechanism = bindRequest.getSaslMechanism();
+
+        /*
+         * If the SASL mechanism is DIGEST-MD5 or GSSAPI, we insert a SASLFilter.
+         */
+        if ( sessionMechanism.equals( SupportedSaslMechanisms.DIGEST_MD5 ) ||
+             sessionMechanism.equals( SupportedSaslMechanisms.GSSAPI ) )
+        {
+            LOG.debug( "Inserting SaslFilter to engage negotiated security layer." );
+
+            IoFilterChain chain = session.getFilterChain();
+            if ( !chain.contains( "SASL" ) )
+            {
+                SaslServer saslContext = ( SaslServer ) session.getAttribute( MechanismHandler.SASL_CONTEXT );
+                chain.addBefore( "codec", "SASL", new SaslFilter( saslContext ) );
+            }
+
+            /*
+             * We disable the SASL security layer once, to write the outbound SUCCESS
+             * message without SASL security layer processing.
+             */
+            session.setAttribute( SaslFilter.DISABLE_SECURITY_LAYER_ONCE, Boolean.TRUE );
+        }
+
+        session.write( response );
+        LOG.debug( "Returned SUCCESS message." );
+    }
+
+    
+    /**
+     * Convert a SASL mechanism to an Authentication level
+     *
+     * @param sessionMechanism The requested mechanism
+     * @return The corresponding authentication level
+     */
+    private String getAuthenticationLevel( String sessionMechanism )
+    {
+        if ( sessionMechanism.equals( SupportedSaslMechanisms.PLAIN ) )
+        {
+            return AuthenticationLevel.SIMPLE.toString();
+        }
+        else
+        {
+            return AuthenticationLevel.STRONG.toString();
+        }
+    }
+    
+    
+    /**
+     * Deal with a received BindRequest
+     * 
+     * @param session The current session
+     * @param bindRequest The received BindRequest
+     * @throws Exception If the euthentication cannot be handled
+     */
+    protected void bindMessageReceived( IoSession session, BindRequest bindRequest ) throws Exception
+    {
+        if ( LOG.isDebugEnabled() )
+        {
+            LOG.debug( "User {} is binding", bindRequest.getName() );
+
+            if ( bindRequest.isSimple() )
+            {
+                LOG.debug( "Using simple authentication." );
+
+            }
+            else
+            {
+                LOG.debug( "Using SASL authentication with mechanism:  {}", bindRequest.getSaslMechanism() );
+            }
+        }
+        
+        // protect against insecure conns when confidentiality is required 
+        if ( ! isConfidentialityRequirementSatisfied( session ) )
+        {
+        	LdapResult result = bindRequest.getResultResponse().getLdapResult();
+        	result.setResultCode( ResultCodeEnum.CONFIDENTIALITY_REQUIRED );
+        	result.setErrorMessage( "Confidentiality (TLS secured connection) is required." );
+        	session.write( bindRequest.getResultResponse() );
+        	return;
+        }
+        
+        // Guard clause:  LDAP version 3
+        if ( !bindRequest.getVersion3() )
+        {
+            LOG.error( "Bind error : Only LDAP v3 is supported." );
+            LdapResult bindResult = bindRequest.getResultResponse().getLdapResult();
+            bindResult.setResultCode( ResultCodeEnum.PROTOCOL_ERROR );
+            bindResult.setErrorMessage( "Only LDAP v3 is supported." );
+            session.write( bindRequest.getResultResponse() );
+            return;
+        }
+
+        // Deal with the two kinds of authent :
+        // - if it's simple, handle it in this class for speed
+        // - for sasl, we go through a chain right now (but it may change in the near future)
+        if ( bindRequest.isSimple() )
+        {
+            handleSimpleAuth( session, bindRequest );
+        }
+        else
+        {
+            handleSaslAuth( session, bindRequest );
+        }
+    }
+}
\ No newline at end of file
diff --git a/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/DefaultCompareHandler.java b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/DefaultCompareHandler.java
new file mode 100644
index 0000000..7255273
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/DefaultCompareHandler.java
@@ -0,0 +1,152 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.ldap.handlers;
+
+
+import javax.naming.Context;
+import javax.naming.NamingException;
+import javax.naming.ReferralException;
+import javax.naming.ldap.LdapContext;
+
+import org.apache.directory.server.core.jndi.ServerLdapContext;
+import org.apache.directory.shared.ldap.exception.LdapException;
+import org.apache.directory.shared.ldap.message.CompareRequest;
+import org.apache.directory.shared.ldap.message.LdapResult;
+import org.apache.directory.shared.ldap.message.ManageDsaITControl;
+import org.apache.directory.shared.ldap.message.ReferralImpl;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.util.ExceptionUtils;
+import org.apache.mina.common.IoSession;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * A single reply handler for {@link CompareRequest}s.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class DefaultCompareHandler extends CompareHandler
+{
+    private static final Logger LOG = LoggerFactory.getLogger( CompareHandler.class );
+
+    /** Speedup for logs */
+    private static final boolean IS_DEBUG = LOG.isDebugEnabled();
+
+    public void compareMessageReceived( IoSession session, CompareRequest req ) throws Exception
+    {
+        LdapResult result = req.getResultResponse().getLdapResult();
+
+        try
+        {
+            // protect against insecure conns when confidentiality is required 
+            if ( ! isConfidentialityRequirementSatisfied( session ) )
+            {
+            	result.setResultCode( ResultCodeEnum.CONFIDENTIALITY_REQUIRED );
+            	result.setErrorMessage( "Confidentiality (TLS secured connection) is required." );
+            	session.write( req.getResultResponse() );
+            	return;
+            }
+            
+            LdapContext ctx = getSessionRegistry().getLdapContext( session, null, true );
+            ServerLdapContext newCtx = ( ServerLdapContext ) ctx.lookup( "" );
+
+            if ( req.getControls().containsKey( ManageDsaITControl.CONTROL_OID ) )
+            {
+                newCtx.addToEnvironment( Context.REFERRAL, "ignore" );
+            }
+            else
+            {
+                newCtx.addToEnvironment( Context.REFERRAL, "throw" );
+            }
+
+            // Inject controls into the context
+            setRequestControls( newCtx, req );
+
+            if ( newCtx.compare( req.getName(), req.getAttributeId(), req.getAssertionValue() ) )
+            {
+                result.setResultCode( ResultCodeEnum.COMPARE_TRUE );
+            }
+            else
+            {
+                result.setResultCode( ResultCodeEnum.COMPARE_FALSE );
+            }
+
+            result.setMatchedDn( req.getName() );
+            req.getResultResponse().addAll( newCtx.getResponseControls() );
+            session.write( req.getResultResponse() );
+        }
+        catch ( ReferralException e )
+        {
+            ReferralImpl refs = new ReferralImpl();
+            result.setReferral( refs );
+            result.setResultCode( ResultCodeEnum.REFERRAL );
+            result.setErrorMessage( "Encountered referral attempting to handle compare request." );
+
+            result.setMatchedDn( (LdapDN)e.getResolvedName() );
+
+            do
+            {
+                refs.addLdapUrl( ( String ) e.getReferralInfo() );
+            }
+            while ( e.skipReferral() );
+            session.write( req.getResultResponse() );
+        }
+        catch ( Exception e )
+        {
+            String msg = "failed to compare entry " + req.getName() + ": " + e.getMessage();
+
+            if ( IS_DEBUG )
+            {
+                msg += ":\n" + ExceptionUtils.getStackTrace( e );
+            }
+
+            ResultCodeEnum code;
+
+            if ( e instanceof LdapException )
+            {
+                code = ( ( LdapException ) e ).getResultCode();
+            }
+            else
+            {
+                code = ResultCodeEnum.getBestEstimate( e, req.getType() );
+            }
+
+            result.setResultCode( code );
+            result.setErrorMessage( msg );
+
+            if ( e instanceof NamingException )
+            {
+                NamingException ne = ( NamingException ) e;
+
+                if ( ( ne.getResolvedName() != null )
+                    && ( ( code == ResultCodeEnum.NO_SUCH_OBJECT ) || ( code == ResultCodeEnum.ALIAS_PROBLEM )
+                        || ( code == ResultCodeEnum.INVALID_DN_SYNTAX ) || ( code == ResultCodeEnum.ALIAS_DEREFERENCING_PROBLEM ) ) )
+                {
+                    result.setMatchedDn( (LdapDN)ne.getResolvedName() );
+                }
+            }
+
+            session.write( req.getResultResponse() );
+        }
+    }
+}
\ No newline at end of file
diff --git a/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/DefaultDeleteHandler.java b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/DefaultDeleteHandler.java
new file mode 100644
index 0000000..6eaecd4
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/DefaultDeleteHandler.java
@@ -0,0 +1,134 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.ldap.handlers;
+
+
+import javax.naming.Context;
+import javax.naming.NamingException;
+import javax.naming.ReferralException;
+import javax.naming.ldap.LdapContext;
+
+import org.apache.directory.shared.ldap.exception.LdapException;
+import org.apache.directory.shared.ldap.message.DeleteRequest;
+import org.apache.directory.shared.ldap.message.LdapResult;
+import org.apache.directory.shared.ldap.message.ManageDsaITControl;
+import org.apache.directory.shared.ldap.message.ReferralImpl;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.util.ExceptionUtils;
+import org.apache.mina.common.IoSession;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * A single reply handler for {@link DeleteRequest}s.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class DefaultDeleteHandler extends DeleteHandler
+{
+    private static final Logger LOG = LoggerFactory.getLogger( DeleteHandler.class );
+
+    /** Speedup for logs */
+    private static final boolean IS_DEBUG = LOG.isDebugEnabled();
+
+
+    public void deleteMessageReceived( IoSession session, DeleteRequest req ) throws Exception
+    {
+        LdapResult result = req.getResultResponse().getLdapResult();
+
+        try
+        {
+            // protect against insecure conns when confidentiality is required 
+            if ( ! isConfidentialityRequirementSatisfied( session ) )
+            {
+            	result.setResultCode( ResultCodeEnum.CONFIDENTIALITY_REQUIRED );
+            	result.setErrorMessage( "Confidentiality (TLS secured connection) is required." );
+            	session.write( req.getResultResponse() );
+            	return;
+            }
+            
+            LdapContext ctx = getSessionRegistry().getLdapContext( session, null, true );
+            if ( req.getControls().containsKey( ManageDsaITControl.CONTROL_OID ) )
+            {
+                ctx.addToEnvironment( Context.REFERRAL, "ignore" );
+            }
+            else
+            {
+                ctx.addToEnvironment( Context.REFERRAL, "throw" );
+            }
+
+            // Inject controls into the context
+            setRequestControls( ctx, req );
+
+            ctx.destroySubcontext( req.getName() );
+            result.setResultCode( ResultCodeEnum.SUCCESS );
+            req.getResultResponse().addAll( ctx.getResponseControls() );
+            session.write( req.getResultResponse() );
+        }
+        catch ( ReferralException e )
+        {
+            ReferralImpl refs = new ReferralImpl();
+            result.setReferral( refs );
+            result.setResultCode( ResultCodeEnum.REFERRAL );
+            result.setErrorMessage( "Encountered referral attempting to handle delete request." );
+            /* coming up null causing a NPE */
+            // result.setMatchedDn( e.getResolvedName().toString() );
+            do
+            {
+                refs.addLdapUrl( ( String ) e.getReferralInfo() );
+            }
+            while ( e.skipReferral() );
+            session.write( req.getResultResponse() );
+        }
+        catch ( NamingException e )
+        {
+            String msg = "failed to delete entry " + req.getName() + ": " + e.getMessage();
+
+            if ( IS_DEBUG )
+            {
+                msg += ":\n" + ExceptionUtils.getStackTrace( e );
+            }
+
+            ResultCodeEnum code;
+            if ( e instanceof LdapException )
+            {
+                code = ( ( LdapException ) e ).getResultCode();
+            }
+            else
+            {
+                code = ResultCodeEnum.getBestEstimate( e, req.getType() );
+            }
+
+            result.setResultCode( code );
+            result.setErrorMessage( msg );
+            if ( ( e.getResolvedName() != null )
+                && ( ( code == ResultCodeEnum.NO_SUCH_OBJECT ) || ( code == ResultCodeEnum.ALIAS_PROBLEM )
+                    || ( code == ResultCodeEnum.INVALID_DN_SYNTAX ) || ( code == ResultCodeEnum.ALIAS_DEREFERENCING_PROBLEM ) ) )
+            {
+                result.setMatchedDn( (LdapDN)e.getResolvedName() );
+            }
+
+            session.write( req.getResultResponse() );
+        }
+    }
+}
\ No newline at end of file
diff --git a/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/DefaultExtendedHandler.java b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/DefaultExtendedHandler.java
new file mode 100644
index 0000000..2d01448
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/DefaultExtendedHandler.java
@@ -0,0 +1,86 @@
+    /*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.ldap.handlers;
+
+
+import org.apache.directory.server.ldap.ExtendedOperationHandler;
+import org.apache.directory.server.ldap.handlers.extended.StartTlsHandler;
+import org.apache.directory.shared.ldap.message.ExtendedRequest;
+import org.apache.directory.shared.ldap.message.ExtendedResponse;
+import org.apache.directory.shared.ldap.message.LdapResult;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.util.ExceptionUtils;
+import org.apache.mina.common.IoSession;
+
+
+/**
+* A single reply handler for {@link ExtendedRequest}s.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class DefaultExtendedHandler extends ExtendedHandler
+{
+    public void extendedMessageReceived( IoSession session, ExtendedRequest req ) throws Exception
+    {
+        ExtendedOperationHandler handler = getHandler( req.getOid() );
+
+        if ( handler == null )
+        {
+            // As long as no extended operations are implemented, send appropriate
+            // error back to the client.
+            String msg = "Unrecognized extended operation EXTENSION_OID: " + req.getOid();
+            LdapResult result = req.getResultResponse().getLdapResult();
+            result.setResultCode( ResultCodeEnum.PROTOCOL_ERROR );
+            result.setErrorMessage( msg );
+            session.write( req.getResultResponse() );
+        }
+        else
+        {
+            try
+            {
+            	if ( ! req.getOid().equals( StartTlsHandler.EXTENSION_OID ) )
+            	{
+	                // protect against insecure conns when confidentiality is required 
+	                if ( ! isConfidentialityRequirementSatisfied( session ) )
+	                {
+	                	LdapResult result = req.getResultResponse().getLdapResult();
+	                	result.setResultCode( ResultCodeEnum.CONFIDENTIALITY_REQUIRED );
+	                	result.setErrorMessage( "Confidentiality (TLS secured connection) is required." );
+	                	session.write( req.getResultResponse() );
+	                	return;
+	                }
+            	}
+                
+                handler.handleExtendedOperation( session, getSessionRegistry(), req );
+            }
+            catch ( Exception e )
+            {
+                LdapResult result = req.getResultResponse().getLdapResult();
+                result.setResultCode( ResultCodeEnum.OTHER );
+                result.setErrorMessage( "Extended operation handler for the specified EXTENSION_OID (" + req.getOid()
+                    + ") has failed to process your request:\n" + ExceptionUtils.getStackTrace( e ) );
+                ExtendedResponse resp = ( ExtendedResponse ) req.getResultResponse();
+                resp.setResponse( new byte[0] );
+                session.write( req.getResultResponse() );
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/DefaultModifyDnHandler.java b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/DefaultModifyDnHandler.java
new file mode 100644
index 0000000..e287b5a
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/DefaultModifyDnHandler.java
@@ -0,0 +1,200 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.ldap.handlers;
+
+ 
+import javax.naming.Context;
+import javax.naming.NamingException;
+import javax.naming.ReferralException;
+import javax.naming.ldap.LdapContext;
+
+import org.apache.directory.shared.ldap.constants.JndiPropertyConstants;
+import org.apache.directory.shared.ldap.exception.LdapException;
+import org.apache.directory.shared.ldap.message.LdapResult;
+import org.apache.directory.shared.ldap.message.ManageDsaITControl;
+import org.apache.directory.shared.ldap.message.ModifyDnRequest;
+import org.apache.directory.shared.ldap.message.ReferralImpl;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.util.ExceptionUtils;
+import org.apache.mina.common.IoSession;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * A single reply handler for {@link ModifyDnRequest}s.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class DefaultModifyDnHandler extends ModifyDnHandler
+{
+    private static final Logger LOG = LoggerFactory.getLogger( ModifyDnHandler.class );
+
+    /** Speedup for logs */
+    private static final boolean IS_DEBUG = LOG.isDebugEnabled();
+
+    /**
+     * Deal with a ModifyDN request received from a client.
+     *
+     * A ModifyDN operation has more than one semantic, depending on its parameters.
+     *
+     * In any case, the first argument is the DN entry to be changed. We then
+     * have the new relative DN for this entry.
+     *
+     * Two other arguments can be provided :
+     * - deleteOldRdn : if the old RDN attributes should be removed from the
+     * new entry or not (for instance, if the old RDN was cn=acme, and the new
+     * one is sn=acme, then we may have to remove the cn: acme from the attributes
+     * list)
+     * - newSuperior : this is a move operation. The entry is removed from its
+     * current location, and created in the new one.
+     */
+    public void modifyDnMessageReceived( IoSession session, ModifyDnRequest req ) throws Exception
+    {
+        LdapResult result = req.getResultResponse().getLdapResult();
+
+        if ( IS_DEBUG )
+        {
+            LOG.debug( "req.getName() == [{}]", req.getName() );
+        }
+
+        if ( req.getName().isEmpty() )
+        {
+            // it is not allowed to modify the name of the Root DSE
+            String msg = "Modify DN is not allowed on Root DSE.";
+            result.setResultCode( ResultCodeEnum.PROTOCOL_ERROR );
+            result.setErrorMessage( msg );
+            session.write( req.getResultResponse() );
+        }
+        else
+        {
+            try
+            {
+                // protect against insecure conns when confidentiality is required 
+                if ( ! isConfidentialityRequirementSatisfied( session ) )
+                {
+                	result.setResultCode( ResultCodeEnum.CONFIDENTIALITY_REQUIRED );
+                	result.setErrorMessage( "Confidentiality (TLS secured connection) is required." );
+                	session.write( req.getResultResponse() );
+                	return;
+                }
+                
+                LdapContext ctx = getSessionRegistry().getLdapContext( session, null, true );
+
+                if ( req.getControls().containsKey( ManageDsaITControl.CONTROL_OID ) )
+                {
+                    ctx.addToEnvironment( Context.REFERRAL, "ignore" );
+                }
+                else
+                {
+                    ctx.addToEnvironment( Context.REFERRAL, "throw" );
+                }
+
+                // Inject controls into the context
+                setRequestControls( ctx, req );
+
+                String deleteRDN = String.valueOf( req.getDeleteOldRdn() );
+                ctx.addToEnvironment( JndiPropertyConstants.JNDI_LDAP_DELETE_RDN, deleteRDN );
+
+                LdapDN newSuperior = req.getNewSuperior();
+
+                if ( ( newSuperior != null ) && ( !newSuperior.isEmpty() ) )
+                {
+                    LdapDN oldDn = req.getName();
+                    LdapDN newDn;
+
+                    newDn = newSuperior;
+
+                    if ( req.getNewRdn() != null )
+                    {
+                        newDn.add( req.getNewRdn() );
+                    }
+                    else
+                    {
+                        newDn.add( oldDn.getRdn() );
+                    }
+
+                    ctx.rename( req.getName(), newDn );
+                }
+                else
+                {
+                    LdapDN newDn = ( LdapDN ) req.getName().clone();
+                    newDn.remove( newDn.size() - 1 );
+                    newDn.add( req.getNewRdn() );
+                    ctx.rename( req.getName(), newDn );
+                }
+
+                req.getResultResponse().addAll( ctx.getResponseControls() );
+                result.setResultCode( ResultCodeEnum.SUCCESS );
+                session.write( req.getResultResponse() );
+            }
+            catch ( ReferralException e )
+            {
+                ReferralImpl refs = new ReferralImpl();
+                result.setReferral( refs );
+                result.setResultCode( ResultCodeEnum.REFERRAL );
+                result.setErrorMessage( "Encountered referral attempting to handle modifyDn request." );
+                result.setMatchedDn( (LdapDN)e.getResolvedName() );
+
+                do
+                {
+                    refs.addLdapUrl( ( String ) e.getReferralInfo() );
+                }
+                while ( e.skipReferral() );
+
+                session.write( req.getResultResponse() );
+            }
+            catch ( NamingException e )
+            {
+                String msg = "failed to modify DN of entry " + req.getName() + ": " + e.getMessage();
+
+                if ( IS_DEBUG )
+                {
+                    msg += ":\n" + ExceptionUtils.getStackTrace( e );
+                }
+
+                ResultCodeEnum code;
+
+                if ( e instanceof LdapException )
+                {
+                    code = ( ( LdapException ) e ).getResultCode();
+                }
+                else
+                {
+                    code = ResultCodeEnum.getBestEstimate( e, req.getType() );
+                }
+
+                result.setResultCode( code );
+                result.setErrorMessage( msg );
+
+                if ( ( e.getResolvedName() != null )
+                    && ( ( code == ResultCodeEnum.NO_SUCH_OBJECT ) || ( code == ResultCodeEnum.ALIAS_PROBLEM )
+                        || ( code == ResultCodeEnum.INVALID_DN_SYNTAX ) || ( code == ResultCodeEnum.ALIAS_DEREFERENCING_PROBLEM ) ) )
+                {
+                    result.setMatchedDn( (LdapDN)e.getResolvedName() );
+                }
+
+                session.write( req.getResultResponse() );
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/DefaultModifyHandler.java b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/DefaultModifyHandler.java
new file mode 100644
index 0000000..ff3f0c8
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/DefaultModifyHandler.java
@@ -0,0 +1,155 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.ldap.handlers;
+
+
+import javax.naming.Context;
+import javax.naming.NamingException;
+import javax.naming.ReferralException;
+import javax.naming.ldap.LdapContext;
+
+import org.apache.directory.shared.ldap.exception.LdapException;
+import org.apache.directory.shared.ldap.message.LdapResult;
+import org.apache.directory.shared.ldap.message.ManageDsaITControl;
+import org.apache.directory.shared.ldap.message.ModificationItemImpl;
+import org.apache.directory.shared.ldap.message.ModifyRequest;
+import org.apache.directory.shared.ldap.message.ReferralImpl;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.util.ExceptionUtils;
+import org.apache.mina.common.IoSession;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * A single reply handler for {@link ModifyRequest}s.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class DefaultModifyHandler extends ModifyHandler
+{
+    private static final Logger LOG = LoggerFactory.getLogger( ModifyHandler.class );
+
+    /** Speedup for logs */
+    private static final boolean IS_DEBUG = LOG.isDebugEnabled();
+
+    public void modifyMessageReceived( IoSession session, ModifyRequest req ) throws Exception
+    {
+        LdapResult result = req.getResultResponse().getLdapResult();
+
+        try
+        {
+            // protect against insecure conns when confidentiality is required 
+            if ( ! isConfidentialityRequirementSatisfied( session ) )
+            {
+            	result.setResultCode( ResultCodeEnum.CONFIDENTIALITY_REQUIRED );
+            	result.setErrorMessage( "Confidentiality (TLS secured connection) is required." );
+            	session.write( req.getResultResponse() );
+            	return;
+            }
+        	
+            LdapContext ctx = getSessionRegistry().getLdapContext( session, null, true );
+            if ( req.getControls().containsKey( ManageDsaITControl.CONTROL_OID ) )
+            {
+                ctx.addToEnvironment( Context.REFERRAL, "ignore" );
+            }
+            else
+            {
+                ctx.addToEnvironment( Context.REFERRAL, "throw" );
+            }
+
+            // Inject controls into the context
+            setRequestControls( ctx, req );
+
+            // Process the modifications
+            if ( req.getModificationItems() != null )
+            {
+                int nbItems = req.getModificationItems().size();
+
+                if ( nbItems != 0 )
+                {
+                    ModificationItemImpl[] mods = new ModificationItemImpl[nbItems];
+                    //noinspection SuspiciousToArrayCall
+                    ctx.modifyAttributes( req.getName(), req.getModificationItems().toArray( mods ) );
+                }
+                else
+                {
+                     // What should we do if we don't have any modification ???
+                }
+            }
+            else
+            {
+                // What should we do if we don't have any modification ???
+            }
+
+            result.setResultCode( ResultCodeEnum.SUCCESS );
+            req.getResultResponse().addAll( ctx.getResponseControls() );
+            session.write( req.getResultResponse() );
+        }
+        catch ( ReferralException e )
+        {
+            ReferralImpl refs = new ReferralImpl();
+            result.setReferral( refs );
+            result.setResultCode( ResultCodeEnum.REFERRAL );
+            result.setErrorMessage( "Encountered referral attempting to handle modify request." );
+            /* coming up null causing a NPE */
+            // result.setMatchedDn( e.getResolvedName().toString() );
+            do
+            {
+                refs.addLdapUrl( ( String ) e.getReferralInfo() );
+            }
+            while ( e.skipReferral() );
+            session.write( req.getResultResponse() );
+        }
+        catch ( NamingException e )
+        {
+            String msg = "failed to modify entry " + req.getName() + ": " + e.getMessage();
+
+            if ( IS_DEBUG )
+            {
+                msg += ":\n" + ExceptionUtils.getStackTrace( e );
+            }
+
+            ResultCodeEnum code;
+            if ( e instanceof LdapException )
+            {
+                code = ( ( LdapException ) e ).getResultCode();
+            }
+            else
+            {
+                code = ResultCodeEnum.getBestEstimate( e, req.getType() );
+            }
+
+            result.setResultCode( code );
+            result.setErrorMessage( msg );
+
+            if ( ( e.getResolvedName() != null )
+                && ( ( code == ResultCodeEnum.NO_SUCH_OBJECT ) || ( code == ResultCodeEnum.ALIAS_PROBLEM )
+                    || ( code == ResultCodeEnum.INVALID_DN_SYNTAX ) || ( code == ResultCodeEnum.ALIAS_DEREFERENCING_PROBLEM ) ) )
+            {
+                result.setMatchedDn( (LdapDN)e.getResolvedName() );
+            }
+
+            session.write( req.getResultResponse() );
+        }
+    }
+}
\ No newline at end of file
diff --git a/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/DefaultSearchHandler.java b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/DefaultSearchHandler.java
new file mode 100644
index 0000000..1b03c8c
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/DefaultSearchHandler.java
@@ -0,0 +1,490 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.ldap.handlers;
+
+
+import org.apache.directory.server.constants.ServerDNConstants;
+import org.apache.directory.server.core.jndi.ServerLdapContext;
+import org.apache.directory.server.ldap.LdapServer;
+import org.apache.directory.shared.ldap.constants.JndiPropertyConstants;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.exception.LdapException;
+import org.apache.directory.shared.ldap.exception.OperationAbandonedException;
+import org.apache.directory.shared.ldap.filter.PresenceNode;
+import org.apache.directory.shared.ldap.message.AbandonListener;
+import org.apache.directory.shared.ldap.message.LdapResult;
+import org.apache.directory.shared.ldap.message.ManageDsaITControl;
+import org.apache.directory.shared.ldap.message.PersistentSearchControl;
+import org.apache.directory.shared.ldap.message.ReferralImpl;
+import org.apache.directory.shared.ldap.message.Response;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.message.ResultResponse;
+import org.apache.directory.shared.ldap.message.ScopeEnum;
+import org.apache.directory.shared.ldap.message.SearchRequest;
+import org.apache.directory.shared.ldap.message.SearchResponseDone;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.util.ArrayUtils;
+import org.apache.directory.shared.ldap.util.ExceptionUtils;
+import org.apache.mina.common.IoSession;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.naming.Context;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.ReferralException;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.LdapContext;
+
+import java.net.InetSocketAddress;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+
+
+/**
+ * A handler for processing search requests.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class DefaultSearchHandler extends SearchHandler
+{
+    private static final Logger LOG = LoggerFactory.getLogger( SearchHandler.class );
+    private static final String DEREFALIASES_KEY = JndiPropertyConstants.JNDI_LDAP_DAP_DEREF_ALIASES;
+
+    /** Speedup for logs */
+    private static final boolean IS_DEBUG = LOG.isDebugEnabled();
+
+    /**
+     * Builds the JNDI search controls for a SearchRequest.
+     *
+     * @param req the search request.
+     * @param ids the ids to return
+     * @return the SearchControls to use with the ApacheDS server side JNDI provider
+     * @param isAdmin whether or not user is an admin
+     * @param maxSize the maximum size for the search in # of entries returned
+     * @param maxTime the maximum length of time for the search in seconds
+     */
+    private SearchControls getSearchControls( SearchRequest req, String[] ids, boolean isAdmin, int maxSize, int maxTime )
+    {
+        // prepare all the search controls
+        SearchControls controls = new SearchControls();
+
+        // take the minimum of system limit with request specified value
+        if ( isAdmin )
+        {
+            controls.setCountLimit( req.getSizeLimit() );
+
+            // The setTimeLimit needs a number of milliseconds
+            // when the search control is expressed in seconds
+            int timeLimit = req.getTimeLimit();
+
+            // Just check that we are not exceeding the maximum for a long
+            if ( timeLimit > Integer.MAX_VALUE / 1000 )
+            {
+                timeLimit = 0;
+            }
+
+            // The maximum time we can wait is around 24 days ...
+            // Is it enough ? ;)
+            controls.setTimeLimit( timeLimit * 1000 );
+        }
+        else
+        {
+            controls.setCountLimit( Math.min( req.getSizeLimit(), maxSize ) );
+            controls.setTimeLimit( Math.min( req.getTimeLimit(), maxTime ) );
+        }
+
+        controls.setSearchScope( req.getScope().getValue() );
+        controls.setReturningObjFlag( req.getTypesOnly() );
+        controls.setReturningAttributes( ids );
+        controls.setDerefLinkFlag( true );
+        
+        return controls;
+    }
+
+
+    /**
+     * Determines if a search request is on the RootDSE of the server.
+     * 
+     * It is a RootDSE search if :
+     * - the base DN is empty
+     * - and the scope is BASE OBJECT
+     * - and the filter is (ObjectClass = *)
+     * 
+     * (RFC 4511, 5.1, par. 1 & 2)
+     *
+     * @param req the request issued
+     * @return true if the search is on the RootDSE false otherwise
+     */
+    private static boolean isRootDSESearch( SearchRequest req )
+    {
+        boolean isBaseIsRoot = req.getBase().isEmpty();
+        boolean isBaseScope = req.getScope() == ScopeEnum.BASE_OBJECT;
+        boolean isRootDSEFilter = false;
+        
+        if ( req.getFilter() instanceof PresenceNode )
+        {
+            String attribute = ( ( PresenceNode ) req.getFilter() ).getAttribute();
+            isRootDSEFilter = attribute.equalsIgnoreCase( SchemaConstants.OBJECT_CLASS_AT ) ||
+                                attribute.equals( SchemaConstants.OBJECT_CLASS_AT_OID );
+        }
+        
+        return isBaseIsRoot && isBaseScope && isRootDSEFilter;
+    }
+
+    
+    private void handlePersistentSearch( IoSession session, SearchRequest req, ServerLdapContext ctx, 
+        SearchControls controls, PersistentSearchControl psearchControl, 
+        NamingEnumeration<SearchResult> list ) throws NamingException 
+    {
+        // there are no limits for psearch processing
+        controls.setCountLimit( 0 );
+        controls.setTimeLimit( 0 );
+
+        if ( !psearchControl.isChangesOnly() )
+        {
+            list = ctx.search( req.getBase(), req.getFilter(),
+                controls );
+            
+            if ( list instanceof AbandonListener )
+            {
+                req.addAbandonListener( ( AbandonListener ) list );
+            }
+            
+            if ( list.hasMore() )
+            {
+                Iterator<Response> it = new SearchResponseIterator( req, ctx, list, controls.getSearchScope(),
+                        session, getSessionRegistry() );
+                
+                while ( it.hasNext() )
+                {
+                    Response resp = it.next();
+                    
+                    if ( resp instanceof SearchResponseDone )
+                    {
+                        // ok if normal search beforehand failed somehow quickly abandon psearch
+                        ResultCodeEnum rcode = ( ( SearchResponseDone ) resp ).getLdapResult().getResultCode();
+
+                        if ( rcode != ResultCodeEnum.SUCCESS )
+                        {
+                            session.write( resp );
+                            return;
+                        }
+                        // if search was fine then we returned all entries so now
+                        // instead of returning the DONE response we break from the
+                        // loop and user the notification listener to send back
+                        // notificationss to the client in never ending search
+                        else
+                        {
+                            break;
+                        }
+                    }
+                    else
+                    {
+                        session.write( resp );
+                    }
+                }
+            }
+        }
+
+        // now we process entries for ever as they change
+        PersistentSearchListener handler = new PersistentSearchListener( getSessionRegistry(),
+                ctx, session, req );
+        ctx.addNamingListener( req.getBase(), req.getFilter().toString(), controls, handler );
+        return;
+    }
+
+    /**
+     * Main message handing method for search requests.
+     */
+    public void searchMessageReceived( IoSession session, SearchRequest req ) throws Exception
+    {
+        LdapServer ldapServer = ( LdapServer )
+                session.getAttribute(  LdapServer.class.toString() );
+
+        if ( IS_DEBUG )
+        {
+            LOG.debug( "Message received:  {}", req.toString() );
+        }
+
+        ServerLdapContext ctx;
+        NamingEnumeration<SearchResult> list = null;
+        String[] ids = null;
+        Collection<String> retAttrs = new HashSet<String>();
+        retAttrs.addAll( req.getAttributes() );
+
+        // add the search request to the registry of outstanding requests for this session
+        getSessionRegistry().addOutstandingRequest( session, req );
+
+        // check the attributes to see if a referral's ref attribute is included
+        if ( retAttrs.size() > 0 && !retAttrs.contains( SchemaConstants.REF_AT ) )
+        {
+            retAttrs.add( SchemaConstants.REF_AT );
+            ids = retAttrs.toArray( ArrayUtils.EMPTY_STRING_ARRAY );
+        }
+        else if ( retAttrs.size() > 0 )
+        {
+            ids = retAttrs.toArray( ArrayUtils.EMPTY_STRING_ARRAY );
+        }
+
+        try
+        {
+            // protect against insecure conns when confidentiality is required 
+            if ( ! isConfidentialityRequirementSatisfied( session ) )
+            {
+            	LdapResult result = req.getResultResponse().getLdapResult();
+            	result.setResultCode( ResultCodeEnum.CONFIDENTIALITY_REQUIRED );
+            	result.setErrorMessage( "Confidentiality (TLS secured connection) is required." );
+            	session.write( req.getResultResponse() );
+            	return;
+            }
+
+            // ===============================================================
+            // Find session context
+            // ===============================================================
+
+            boolean isRootDSESearch = isRootDSESearch( req );
+
+            // bypass checks to disallow anonymous binds for search on RootDSE with base obj scope
+            if ( isRootDSESearch )
+            {
+                LdapContext unknown = getSessionRegistry().getLdapContextOnRootDSEAccess( session, null );
+
+                if ( !( unknown instanceof ServerLdapContext ) )
+                {
+                    ctx = ( ServerLdapContext ) unknown.lookup( "" );
+                }
+                else
+                {
+                    ctx = ( ServerLdapContext ) unknown;
+                }
+            }
+            // all those search operations are subject to anonymous bind checks when anonymous binda are disallowed
+            else
+            {
+                LdapContext unknown = getSessionRegistry().getLdapContext( session, null, true );
+
+                if ( !( unknown instanceof ServerLdapContext ) )
+                {
+                    ctx = ( ServerLdapContext ) unknown.lookup( "" );
+                }
+                else
+                {
+                    ctx = ( ServerLdapContext ) unknown;
+                }
+            }
+
+            // Inject controls into the context
+            setRequestControls( ctx, req );
+
+            ctx.addToEnvironment( DEREFALIASES_KEY, req.getDerefAliases().getJndiValue() );
+
+            if ( req.getControls().containsKey( ManageDsaITControl.CONTROL_OID ) )
+            {
+                ctx.addToEnvironment( Context.REFERRAL, "ignore" );
+            }
+            else
+            {
+                ctx.addToEnvironment( Context.REFERRAL, "throw-finding-base" );
+            }
+
+            // ===============================================================
+            // Handle anonymous binds
+            // ===============================================================
+
+            boolean allowAnonymousBinds = ldapServer.isAllowAnonymousAccess();
+            boolean isAnonymousUser = ctx.getPrincipal().getName().trim().equals( "" );
+
+            if ( isAnonymousUser && !allowAnonymousBinds && !isRootDSESearch )
+            {
+                LdapResult result = req.getResultResponse().getLdapResult();
+                result.setResultCode( ResultCodeEnum.INSUFFICIENT_ACCESS_RIGHTS );
+                String msg = "Bind failure: Anonymous binds have been disabled!";
+                result.setErrorMessage( msg );
+                session.write( req.getResultResponse() );
+                return;
+            }
+
+
+            // ===============================================================
+            // Set search limits differently based on user's identity
+            // ===============================================================
+
+            int maxSize = ldapServer.getMaxSizeLimit();
+            int maxTime = ldapServer.getMaxTimeLimit();
+
+            SearchControls controls;
+            
+            if ( isAnonymousUser )
+            {
+                controls = getSearchControls( req, ids, false, maxSize, maxTime );
+            }
+            else if ( ctx.getPrincipal().getName()
+                .trim().equals( ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED ) )
+            {
+                controls = getSearchControls( req, ids, true, maxSize, maxTime );
+            }
+            else
+            {
+                controls = getSearchControls( req, ids, false, maxSize, maxTime );
+            }
+
+
+            // ===============================================================
+            // Handle psearch differently
+            // ===============================================================
+
+            PersistentSearchControl psearchControl = ( PersistentSearchControl ) req.getControls().get(
+                PersistentSearchControl.CONTROL_OID );
+            
+            if ( psearchControl != null )
+            {
+                handlePersistentSearch( session, req, ctx, controls, psearchControl, list );
+                return;
+            }
+
+            // ===============================================================
+            // Handle regular search requests from here down
+            // ===============================================================
+
+            /*
+             * Iterate through all search results building and sending back responses
+             * for each search result returned.
+             */
+            list = ctx.search( req.getBase(), req.getFilter(), controls, ( InetSocketAddress ) session.getRemoteAddress() );
+            
+            if ( list instanceof AbandonListener )
+            {
+                req.addAbandonListener( ( AbandonListener ) list );
+            }
+
+            if ( list.hasMore() )
+            {
+                Iterator<Response> it = new SearchResponseIterator( req, ctx, list, controls.getSearchScope(),
+                        session, getSessionRegistry() );
+                
+                while ( it.hasNext() )
+                {
+                    session.write( it.next() );
+                }
+            }
+            else
+            {
+                list.close();
+                req.getResultResponse().getLdapResult().setResultCode( ResultCodeEnum.SUCCESS );
+                
+                for ( ResultResponse resultResponse : Collections.singleton( req.getResultResponse() ) )
+                {
+                    session.write( resultResponse );
+                }
+            }
+        }
+        catch ( ReferralException e )
+        {
+            LdapResult result = req.getResultResponse().getLdapResult();
+            ReferralImpl refs = new ReferralImpl();
+            result.setReferral( refs );
+            result.setResultCode( ResultCodeEnum.REFERRAL );
+            result.setErrorMessage( "Encountered referral attempting to handle add request." );
+
+            do
+            {
+                refs.addLdapUrl( ( String ) e.getReferralInfo() );
+            }
+            while ( e.skipReferral() );
+            
+            session.write( req.getResultResponse() );
+            getSessionRegistry().removeOutstandingRequest( session, req.getMessageId() );
+        }
+        catch ( NamingException e )
+        {
+            /*
+             * From RFC 2251 Section 4.11:
+             *
+             * In the event that a server receives an Abandon Request on a Search
+             * operation in the midst of transmitting responses to the Search, that
+             * server MUST cease transmitting entry responses to the abandoned
+             * request immediately, and MUST NOT send the SearchResultDone. Of
+             * course, the server MUST ensure that only properly encoded LDAPMessage
+             * PDUs are transmitted.
+             *
+             * SO DON'T SEND BACK ANYTHING!!!!!
+             */
+            if ( e instanceof OperationAbandonedException )
+            {
+                return;
+            }
+
+            String msg = "failed on search operation: " + e.getMessage();
+            
+            if ( LOG.isDebugEnabled() )
+            {
+                msg += ":\n" + req + ":\n" + ExceptionUtils.getStackTrace( e );
+            }
+
+            ResultCodeEnum code;
+            
+            if ( e instanceof LdapException )
+            {
+                code = ( ( LdapException ) e ).getResultCode();
+            }
+            else
+            {
+                code = ResultCodeEnum.getBestEstimate( e, req.getType() );
+            }
+
+            LdapResult result = req.getResultResponse().getLdapResult();
+            result.setResultCode( code );
+            result.setErrorMessage( msg );
+
+            if ( ( e.getResolvedName() != null )
+                && ( ( code == ResultCodeEnum.NO_SUCH_OBJECT ) || ( code == ResultCodeEnum.ALIAS_PROBLEM )
+                    || ( code == ResultCodeEnum.INVALID_DN_SYNTAX ) || ( code == ResultCodeEnum.ALIAS_DEREFERENCING_PROBLEM ) ) )
+            {
+                result.setMatchedDn( (LdapDN)e.getResolvedName() );
+            }
+
+            for ( ResultResponse resultResponse : Collections.singleton( req.getResultResponse() ) )
+            {
+                session.write( resultResponse );
+            }
+            
+            getSessionRegistry().removeOutstandingRequest( session, req.getMessageId() );
+        }
+        finally
+        {
+            if ( list != null )
+            {
+                try
+                {
+                    list.close();
+                }
+                catch ( NamingException e )
+                {
+                    LOG.error( "failed on list.close()", e );
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/DefaultUnbindHandler.java b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/DefaultUnbindHandler.java
new file mode 100644
index 0000000..6eadaf5
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/DefaultUnbindHandler.java
@@ -0,0 +1,67 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.ldap.handlers;
+
+
+import org.apache.directory.server.core.jndi.ServerLdapContext;
+import org.apache.directory.shared.ldap.message.UnbindRequest;
+import org.apache.mina.common.IoSession;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.naming.NamingException;
+import javax.naming.ldap.LdapContext;
+
+
+/**
+ * A no reply protocol handler implementation for LDAP {@link
+ * org.apache.directory.shared.ldap.message.UnbindRequest}s.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class DefaultUnbindHandler extends UnbindHandler
+{
+    private static final Logger LOG = LoggerFactory.getLogger( UnbindHandler.class );
+
+
+    public void unbindMessageReceived( IoSession session, UnbindRequest request ) throws Exception
+    {
+        try
+        {
+            LdapContext ctx = getSessionRegistry().getLdapContext( session, null, false );
+
+            if ( ctx != null )
+            {
+                if ( ctx instanceof ServerLdapContext && ( ( ServerLdapContext ) ctx ).getService().isStarted() )
+                {
+                    ( ( ServerLdapContext ) ctx ).ldapUnbind();
+                }
+                ctx.close();
+            }
+            getSessionRegistry().terminateSession( session );
+            getSessionRegistry().remove( session );
+        }
+        catch ( NamingException e )
+        {
+            LOG.error( "failed to unbind session properly", e );
+        }
+    }
+}
\ No newline at end of file
diff --git a/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/DeleteHandler.java b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/DeleteHandler.java
new file mode 100644
index 0000000..477370e
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/DeleteHandler.java
@@ -0,0 +1,43 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.ldap.handlers;
+
+
+import org.apache.directory.shared.ldap.message.DeleteRequest;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.handler.demux.MessageHandler;
+
+
+/**
+ * A single reply handler for {@link org.apache.directory.shared.ldap.message.DeleteRequest}s.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public abstract class DeleteHandler extends AbstractLdapHandler implements MessageHandler
+{
+    protected abstract void deleteMessageReceived( IoSession session, DeleteRequest deleteRequest ) throws Exception;
+
+
+    public final void messageReceived( IoSession ioSession, Object o ) throws Exception
+    {
+        deleteMessageReceived( ioSession, ( DeleteRequest ) o );
+    }
+}
diff --git a/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/ExtendedHandler.java b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/ExtendedHandler.java
new file mode 100644
index 0000000..e74b658
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/ExtendedHandler.java
@@ -0,0 +1,82 @@
+    /*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.ldap.handlers;
+
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.directory.server.ldap.ExtendedOperationHandler;
+import org.apache.directory.shared.ldap.message.ExtendedRequest;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.handler.demux.MessageHandler;
+
+
+/**
+ * A single reply handler for {@link org.apache.directory.shared.ldap.message.ExtendedRequest}s.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public abstract class ExtendedHandler extends AbstractLdapHandler implements MessageHandler
+{
+    private final Map<String, ExtendedOperationHandler> handlers = new HashMap<String, ExtendedOperationHandler>();
+
+
+    public final ExtendedOperationHandler addHandler( ExtendedOperationHandler eoh )
+    {
+        synchronized ( handlers )
+        {
+            return handlers.put( eoh.getOid(), eoh );
+        }
+    }
+
+
+    public final ExtendedOperationHandler removeHandler( String oid )
+    {
+        synchronized ( handlers )
+        {
+            return handlers.remove( oid );
+        }
+    }
+
+
+    public final ExtendedOperationHandler getHandler( String oid )
+    {
+        return handlers.get( oid );
+    }
+
+
+    public final Map<String,ExtendedOperationHandler> getHandlerMap()
+    {
+        return Collections.unmodifiableMap( handlers );
+    }
+
+
+    public final void messageReceived( IoSession session, Object request ) throws Exception
+    {
+        extendedMessageReceived( session, ( ExtendedRequest ) request );
+    }
+
+
+    protected abstract void extendedMessageReceived( IoSession session, ExtendedRequest extendedRequest )
+            throws Exception;
+}
diff --git a/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/ModifyDnHandler.java b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/ModifyDnHandler.java
new file mode 100644
index 0000000..cba6bab
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/ModifyDnHandler.java
@@ -0,0 +1,59 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.ldap.handlers;
+
+ 
+import org.apache.directory.shared.ldap.message.ModifyDnRequest;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.handler.demux.MessageHandler;
+
+
+/**
+ * A single reply handler for {@link org.apache.directory.shared.ldap.message.ModifyDnRequest}s.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public abstract class ModifyDnHandler extends AbstractLdapHandler implements MessageHandler
+{
+    /**
+     * Deal with a ModifyDN request received from a client.
+     * 
+     * A ModifyDN operation has more than one semantic, depending on its parameters.
+     * 
+     * In any case, the first argument is the DN entry to be changed. We then
+     * have the new relative DN for this entry.
+     * 
+     * Two other arguments can be provided :
+     * - deleteOldRdn : if the old RDN attributes should be removed from the
+     * new entry or not (for instance, if the old RDN was cn=acme, and the new 
+     * one is sn=acme, then we may have to remove the cn: acme from the attributes
+     * list)
+     * - newSuperior : this is a move operation. The entry is removed from its
+     * current location, and created in the new one.
+     */
+    public final void messageReceived( IoSession session, Object request ) throws Exception
+    {
+        modifyDnMessageReceived( session, ( ModifyDnRequest ) request );
+    }
+
+
+    protected abstract void modifyDnMessageReceived( IoSession session, ModifyDnRequest modifyDnRequest ) throws Exception;
+}
diff --git a/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/ModifyHandler.java b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/ModifyHandler.java
new file mode 100644
index 0000000..c917e74
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/ModifyHandler.java
@@ -0,0 +1,43 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.ldap.handlers;
+
+
+import org.apache.directory.shared.ldap.message.ModifyRequest;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.handler.demux.MessageHandler;
+
+
+/**
+ * A single reply handler for {@link org.apache.directory.shared.ldap.message.ModifyRequest}s.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public abstract class ModifyHandler extends AbstractLdapHandler implements MessageHandler
+{
+    public final void messageReceived( IoSession session, Object request ) throws Exception
+    {
+        modifyMessageReceived( session, ( ModifyRequest ) request );
+    }
+
+
+    protected abstract void modifyMessageReceived( IoSession session, ModifyRequest modifyRequest ) throws Exception;
+}
diff --git a/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/PersistentSearchListener.java b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/PersistentSearchListener.java
new file mode 100644
index 0000000..5a10a42
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/PersistentSearchListener.java
@@ -0,0 +1,372 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.ldap.handlers;
+
+
+import javax.naming.InvalidNameException;
+import javax.naming.NamingException;
+import javax.naming.directory.Attributes;
+import javax.naming.event.NamespaceChangeListener;
+import javax.naming.event.NamingEvent;
+import javax.naming.event.NamingExceptionEvent;
+import javax.naming.event.ObjectChangeListener;
+
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerEntryUtils;
+import org.apache.directory.server.core.jndi.ServerLdapContext;
+import org.apache.directory.server.ldap.SessionRegistry;
+import org.apache.directory.shared.ldap.codec.search.controls.ChangeType;
+import org.apache.directory.shared.ldap.exception.LdapException;
+import org.apache.directory.shared.ldap.exception.OperationAbandonedException;
+import org.apache.directory.shared.ldap.message.AbandonListener;
+import org.apache.directory.shared.ldap.message.AbandonableRequest;
+import org.apache.directory.shared.ldap.message.EntryChangeControl;
+import org.apache.directory.shared.ldap.message.LdapResult;
+import org.apache.directory.shared.ldap.message.PersistentSearchControl;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.message.SearchRequest;
+import org.apache.directory.shared.ldap.message.SearchResponseEntry;
+import org.apache.directory.shared.ldap.message.SearchResponseEntryImpl;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.util.ExceptionUtils;
+import org.apache.mina.common.IoSession;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * A JNDI NamingListener implementation which sends back added, deleted, modified or 
+ * renamed entries to a client that created this listener.  This class is part of the
+ * persistent search implementation which uses the event notification scheme built into
+ * the server core.  This is exposed by the server side ApacheDS JNDI LDAP provider.
+ * 
+ * This listener is disabled only when a session closes or when an abandon request 
+ * cancels it.  Hence time and size limits in normal search operations do not apply
+ * here.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+class PersistentSearchListener implements ObjectChangeListener, NamespaceChangeListener, AbandonListener
+{
+    private static final Logger LOG = LoggerFactory.getLogger( SearchHandler.class );
+    final ServerLdapContext ctx;
+    final IoSession session;
+    final SearchRequest req;
+    final PersistentSearchControl control;
+    final SessionRegistry registry;
+
+
+    /** Speedup for logs */
+    private static final boolean IS_DEBUG = LOG.isDebugEnabled();
+
+
+    PersistentSearchListener( SessionRegistry registry, ServerLdapContext ctx, IoSession session, SearchRequest req )
+    {
+        this.registry = registry;
+        this.session = session;
+        this.req = req;
+        req.addAbandonListener( this );
+        this.ctx = ctx;
+        this.control = ( PersistentSearchControl ) req.getControls().get( PersistentSearchControl.CONTROL_OID );
+    }
+
+
+    public void abandon() throws NamingException
+    {
+        // must abandon the operation 
+        ctx.removeNamingListener( this );
+
+        /*
+         * From RFC 2251 Section 4.11:
+         * 
+         * In the event that a server receives an Abandon Request on a Search  
+         * operation in the midst of transmitting responses to the Search, that
+         * server MUST cease transmitting entry responses to the abandoned
+         * request immediately, and MUST NOT send the SearchResultDone. Of
+         * course, the server MUST ensure that only properly encoded LDAPMessage
+         * PDUs are transmitted. 
+         * 
+         * SO DON'T SEND BACK ANYTHING!!!!!
+         */
+    }
+
+
+    public void namingExceptionThrown( NamingExceptionEvent evt )
+    {
+        // must abandon the operation and send response done with an
+        // error message if this occurs because something is wrong
+
+        try
+        {
+            ctx.removeNamingListener( this );
+        }
+        catch ( NamingException e )
+        {
+            LOG.error( "Attempt to remove listener from context failed", e );
+        }
+
+        /*
+         * From RFC 2251 Section 4.11:
+         * 
+         * In the event that a server receives an Abandon Request on a Search  
+         * operation in the midst of transmitting responses to the Search, that
+         * server MUST cease transmitting entry responses to the abandoned
+         * request immediately, and MUST NOT send the SearchResultDone. Of
+         * course, the server MUST ensure that only properly encoded LDAPMessage
+         * PDUs are transmitted. 
+         * 
+         * SO DON'T SEND BACK ANYTHING!!!!!
+         */
+        if ( evt.getException() instanceof OperationAbandonedException )
+        {
+            return;
+        }
+
+        registry.removeOutstandingRequest( session, new Integer( req.getMessageId() ) );
+        String msg = "failed on persistent search operation";
+
+        if ( IS_DEBUG )
+        {
+            msg += ":\n" + req + ":\n" + ExceptionUtils.getStackTrace( evt.getException() );
+        }
+
+        ResultCodeEnum code;
+        
+        if ( evt.getException() instanceof LdapException )
+        {
+            code = ( ( LdapException ) evt.getException() ).getResultCode();
+        }
+        else
+        {
+            code = ResultCodeEnum.getBestEstimate( evt.getException(), req.getType() );
+        }
+
+        LdapResult result = req.getResultResponse().getLdapResult();
+        result.setResultCode( code );
+        result.setErrorMessage( msg );
+        
+        if ( ( evt.getException().getResolvedName() != null )
+            && ( ( code == ResultCodeEnum.NO_SUCH_OBJECT ) || ( code == ResultCodeEnum.ALIAS_PROBLEM )
+                || ( code == ResultCodeEnum.INVALID_DN_SYNTAX ) || ( code == ResultCodeEnum.ALIAS_DEREFERENCING_PROBLEM ) ) )
+        {
+            result.setMatchedDn( (LdapDN)evt.getException().getResolvedName() );
+        }
+        
+        session.write( req.getResultResponse() );
+    }
+
+
+    public void objectChanged( NamingEvent evt )
+    {
+        // send the entry back
+        sendEntry( evt );
+    }
+
+
+    public void objectAdded( NamingEvent evt )
+    {
+        // send the entry back
+        sendEntry( evt );
+    }
+
+
+    public void objectRemoved( NamingEvent evt )
+    {
+        // send the entry back
+        sendEntry( evt );
+    }
+
+
+    public void objectRenamed( NamingEvent evt )
+    {
+        // send the entry back
+        sendEntry( evt );
+    }
+
+
+    private void sendEntry( NamingEvent evt )
+    {
+        /*
+         * @todo eventually you'll want to add the changeNumber once we move 
+         * the CSN functionality into the server.
+         */
+        SearchResponseEntry respEntry = new SearchResponseEntryImpl( req.getMessageId() );
+        EntryChangeControl ecControl = null;
+
+        if ( control.isReturnECs() )
+        {
+            ecControl = new EntryChangeControl();
+            respEntry.add( ecControl );
+        }
+        
+        LdapDN newBinding = null;
+        LdapDN oldBinding = null;
+        
+        if ( evt.getNewBinding() != null )
+        {
+            try
+            {
+                newBinding = new LdapDN( evt.getNewBinding().getName() );
+            }
+            catch ( InvalidNameException ine )
+            {
+                newBinding = LdapDN.EMPTY_LDAPDN;
+            }
+        }
+
+        if ( evt.getOldBinding() != null )
+        {
+            try
+            {
+                oldBinding = new LdapDN( evt.getOldBinding().getName() );
+            }
+            catch ( InvalidNameException ine )
+            {
+                oldBinding = LdapDN.EMPTY_LDAPDN;
+            }
+        }
+
+        Object attr;
+        
+        switch ( evt.getType() )
+        {
+            case ( NamingEvent.OBJECT_ADDED  ):
+                if ( !control.isNotificationEnabled( ChangeType.ADD ) )
+                {
+                    return;
+                }
+            
+                respEntry.setObjectName( newBinding );
+                attr = evt.getChangeInfo();
+                
+                if ( attr instanceof ServerEntry )
+                {
+                    respEntry.setAttributes( ServerEntryUtils.toAttributesImpl( (ServerEntry)attr ) );
+                }
+                else
+                {
+                    respEntry.setAttributes( ( Attributes ) attr );
+                }
+                
+                if ( ecControl != null )
+                {
+                    ecControl.setChangeType( ChangeType.ADD );
+                }
+                
+                break;
+                
+            case ( NamingEvent.OBJECT_CHANGED  ):
+                if ( !control.isNotificationEnabled( ChangeType.MODIFY ) )
+                {
+                    return;
+                }
+            
+                respEntry.setObjectName( oldBinding );
+                attr = evt.getOldBinding().getObject();
+                
+                if ( attr instanceof ServerEntry )
+                {
+                    respEntry.setAttributes( ServerEntryUtils.toAttributesImpl( (ServerEntry)attr ) );
+                }
+                else
+                {
+                    respEntry.setAttributes( ( Attributes ) attr );
+                }
+
+                if ( ecControl != null )
+                {
+                    ecControl.setChangeType( ChangeType.MODIFY );
+                }
+                
+                break;
+                
+            case ( NamingEvent.OBJECT_REMOVED  ):
+                if ( !control.isNotificationEnabled( ChangeType.DELETE ) )
+                {
+                    return;
+                }
+            
+                respEntry.setObjectName( oldBinding );
+                attr = evt.getOldBinding().getObject();
+                
+                if ( attr instanceof ServerEntry )
+                {
+                    respEntry.setAttributes( ServerEntryUtils.toAttributesImpl( (ServerEntry)attr ) );
+                }
+                else
+                {
+                    respEntry.setAttributes( ( Attributes ) attr );
+                }
+
+                if ( ecControl != null )
+                {
+                    ecControl.setChangeType( ChangeType.DELETE );
+                }
+                
+                break;
+                
+            case ( NamingEvent.OBJECT_RENAMED  ):
+                if ( !control.isNotificationEnabled( ChangeType.MODDN ) )
+                {
+                    return;
+                }
+            
+                respEntry.setObjectName( newBinding );
+                attr = evt.getNewBinding().getObject();
+                
+                if ( attr instanceof ServerEntry )
+                {
+                    respEntry.setAttributes( ServerEntryUtils.toAttributesImpl( (ServerEntry)attr ) );
+                }
+                else
+                {
+                    respEntry.setAttributes( ( Attributes ) attr );
+                }
+
+                if ( ecControl != null )
+                {
+                    ecControl.setChangeType( ChangeType.MODDN );
+                    ecControl.setPreviousDn( oldBinding );
+                }
+                
+                break;
+
+            default:
+                return;
+        }
+
+        session.write( respEntry );
+    }
+
+
+    public void requestAbandoned( AbandonableRequest req )
+    {
+        try
+        {
+            abandon();
+        }
+        catch ( NamingException e )
+        {
+            LOG.error( "failed to properly abandon this persistent search", e );
+        }
+    }
+}
\ No newline at end of file
diff --git a/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/SearchHandler.java b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/SearchHandler.java
new file mode 100644
index 0000000..6107703
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/SearchHandler.java
@@ -0,0 +1,46 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.ldap.handlers;
+
+
+import org.apache.directory.shared.ldap.message.SearchRequest;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.handler.demux.MessageHandler;
+
+
+/**
+ * A handler for processing search requests.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public abstract class SearchHandler extends AbstractLdapHandler implements MessageHandler
+{
+    /**
+     * Main message handing method for search requests.
+     */
+    public final void messageReceived( IoSession session, Object request ) throws Exception
+    {
+        searchMessageReceived( session, ( SearchRequest ) request );
+    }
+
+
+    public abstract void searchMessageReceived( IoSession session, SearchRequest searchRequest ) throws Exception;
+}
diff --git a/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/SearchResponseIterator.java b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/SearchResponseIterator.java
new file mode 100644
index 0000000..c680f35
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/SearchResponseIterator.java
@@ -0,0 +1,480 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.ldap.handlers;
+
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+import javax.naming.InvalidNameException;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+
+import org.apache.directory.server.core.jndi.ServerLdapContext;
+import org.apache.directory.server.ldap.SessionRegistry;
+import org.apache.directory.shared.ldap.codec.util.LdapURL;
+import org.apache.directory.shared.ldap.codec.util.LdapURLEncodingException;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.exception.LdapException;
+import org.apache.directory.shared.ldap.message.ManageDsaITControl;
+import org.apache.directory.shared.ldap.message.Message;
+import org.apache.directory.shared.ldap.message.ReferralImpl;
+import org.apache.directory.shared.ldap.message.Response;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.message.SearchRequest;
+import org.apache.directory.shared.ldap.message.SearchResponseDone;
+import org.apache.directory.shared.ldap.message.SearchResponseEntry;
+import org.apache.directory.shared.ldap.message.SearchResponseEntryImpl;
+import org.apache.directory.shared.ldap.message.SearchResponseReference;
+import org.apache.directory.shared.ldap.message.SearchResponseReferenceImpl;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.util.ExceptionUtils;
+import org.apache.mina.common.IoSession;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * A iterator which wraps a search result returning naming enumeration to return 
+ * search responses.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+class SearchResponseIterator implements Iterator<Response>
+{
+    private static final Logger LOG = LoggerFactory.getLogger( SearchResponseIterator.class );
+    private final SearchRequest req;
+    private final ServerLdapContext ctx;
+    private final NamingEnumeration<SearchResult> underlying;
+    private final IoSession session;
+    private final SessionRegistry registry;
+    private SearchResponseDone respDone;
+    private boolean done;
+    private Response prefetched;
+    private final int scope;
+
+    /** Speedup for logs */
+    private static final boolean IS_DEBUG = LOG.isDebugEnabled();
+
+
+    /**
+     * Creates a search response iterator for the resulting enumeration
+     * over a search request.
+     *
+     * @param req the search request to generate responses to
+     * @param underlying the underlying JNDI enumeration containing SearchResults
+     * @param ctx the context where search is applied
+     * @param registry the session registry
+     * @param scope the scope of the search
+     * @param session the session of the issuer of the search
+     */
+    public SearchResponseIterator( SearchRequest req, ServerLdapContext ctx, NamingEnumeration<SearchResult> underlying, int scope,
+        IoSession session, SessionRegistry registry )
+    {
+        this.req = req;
+        this.ctx = ctx;
+        this.scope = scope;
+        this.underlying = underlying;
+        this.session = session;
+        this.registry = registry;
+
+        try
+        {
+            if ( underlying.hasMore() )
+            {
+                SearchResult result = (SearchResult ) underlying.next();
+
+                /*
+                 * Now we have to build the prefetched object from the 'result'
+                 * local variable for the following call to next()
+                 */
+                Attribute ref = result.getAttributes().get( SchemaConstants.REF_AT );
+                
+                LdapDN dn = new LdapDN( result.getName() );
+                
+                if ( !ctx.isReferral( dn )
+                    || req.getControls().containsKey( ManageDsaITControl.CONTROL_OID ) )
+                {
+                    SearchResponseEntry respEntry;
+                    respEntry = new SearchResponseEntryImpl( req.getMessageId() );
+                    respEntry.setAttributes( result.getAttributes() );
+                    respEntry.setObjectName( dn );
+                    prefetched = respEntry;
+                }
+                else
+                {
+                    SearchResponseReference respRef;
+                    respRef = new SearchResponseReferenceImpl( req.getMessageId() );
+                    respRef.setReferral( new ReferralImpl() );
+
+                    for ( int ii = 0; ii < ref.size(); ii++ )
+                    {
+                        String url;
+
+                        try
+                        {
+                            url = ( String ) ref.get( ii );
+                            respRef.getReferral().addLdapUrl( url );
+                        }
+                        catch ( NamingException e )
+                        {
+                            try
+                            {
+                                underlying.close();
+                            }
+                            catch ( Throwable t )
+                            {
+                                LOG.error( "Encountered error while trying to close underlying enumeration", t );
+                            }
+
+                            prefetched = null;
+                            respDone = getResponse( req, e );
+                        }
+                    }
+
+                    prefetched = respRef;
+                }
+            }
+            else
+            {
+                registry.removeOutstandingRequest( session, req.getMessageId() );
+            }
+        }
+        catch ( NamingException e )
+        {
+            try
+            {
+                this.underlying.close();
+            }
+            catch ( Exception e2 )
+            {
+                LOG.error( "Encountered error while trying to close underlying enumeration", e );
+            }
+
+            respDone = getResponse( req, e );
+        }
+    }
+
+
+    public boolean hasNext()
+    {
+        return !done;
+    }
+
+
+    public Response next()
+    {
+        Response next = prefetched;
+        
+        try
+        {
+            if ( prefetched != null )
+            {
+                ( ( Message ) prefetched ).addAll( ctx.getResponseControls() );
+            }
+        }
+        catch ( NamingException e )
+        {
+            NoSuchElementException nsee = new NoSuchElementException();
+            nsee.initCause( e );
+            throw nsee;
+        }
+        
+        SearchResult result;
+
+        // if we're done we got nothing to give back
+        if ( done )
+        {
+            throw new NoSuchElementException();
+        }
+
+        // if respDone has been assembled this is our last object to return
+        if ( respDone != null )
+        {
+            done = true;
+            
+            try
+            {
+                respDone.addAll( ctx.getResponseControls() );
+            }
+            catch ( NamingException e )
+            {
+                NoSuchElementException nsee = new NoSuchElementException();
+                nsee.initCause( e );
+                throw nsee;
+            }
+                
+            return respDone;
+        }
+
+        /*
+         * If we have gotten this far then we have a valid next entry
+         * or referral to return from this call in the 'next' variable.
+         */
+        try
+        {
+            /*
+             * If we have more results from the underlying cursor then
+             * we just set the result and build the response object below.
+             */
+            if ( underlying.hasMore() )
+            {
+                result = underlying.next();
+            }
+            else
+            {
+                try
+                {
+                    underlying.close();
+                }
+                catch ( Throwable t )
+                {
+                    LOG.error( "Encountered error while trying to close underlying enumeration", t );
+                }
+
+                respDone = ( SearchResponseDone ) req.getResultResponse();
+                respDone.getLdapResult().setResultCode( ResultCodeEnum.SUCCESS );
+                prefetched = null;
+                registry.removeOutstandingRequest( session, req.getMessageId() );
+                return next;
+            }
+        }
+        catch ( NamingException e )
+        {
+            try
+            {
+                underlying.close();
+            }
+            catch ( Throwable t )
+            {
+                LOG.error( "Encountered error while trying to close underlying enumeration", t );
+            }
+
+            prefetched = null;
+            respDone = getResponse( req, e );
+            return next;
+        }
+
+        /*
+         * Now we have to build the prefetched object from the 'result'
+         * local variable for the following call to next()
+         */
+        Attribute ref = result.getAttributes().get( SchemaConstants.REF_AT );
+        boolean isReferral;
+
+        try
+        {
+            isReferral = ctx.isReferral( result.getName() );
+        }
+        catch ( NamingException e )
+        {
+            LOG.error( "failed to determine if " + result.getName() + " is a referral", e );
+            throw new RuntimeException( e );
+        }
+
+        // we may need to lookup the object again if the ref attribute was filtered out
+        if ( isReferral && ref == null )
+        {
+            try
+            {
+                ref = ctx.getAttributes( result.getName(), new String[]
+                    { SchemaConstants.REF_AT } ).get( SchemaConstants.REF_AT );
+            }
+            catch ( NamingException e )
+            {
+                LOG.error( "failed to lookup ref attribute for " + result.getName(), e );
+                throw new RuntimeException( e );
+            }
+        }
+
+        if ( !isReferral || req.getControls().containsKey( ManageDsaITControl.CONTROL_OID ) )
+        {
+            SearchResponseEntry respEntry = new SearchResponseEntryImpl( req.getMessageId() );
+            respEntry.setAttributes( result.getAttributes() );
+            
+            try
+            {
+                respEntry.setObjectName( new LdapDN( result.getName() ) );
+            }
+            catch ( InvalidNameException ine )
+            {
+                LOG.error( "Invalid object name : " + result.getName(), ine);
+                throw new RuntimeException( ine );
+            }
+            
+            prefetched = respEntry;
+        }
+        else
+        {
+            SearchResponseReference respRef = new SearchResponseReferenceImpl( req.getMessageId() );
+            respRef.setReferral( new ReferralImpl() );
+
+            for ( int ii = 0; ii < ref.size(); ii++ )
+            {
+                String val;
+                try
+                {
+                    val = ( String ) ref.get( ii );
+                }
+                catch ( NamingException e1 )
+                {
+                    LOG.error( "failed to access referral url." );
+                    try
+                    {
+                        underlying.close();
+                    }
+                    catch ( Throwable t )
+                    {
+                        LOG.error( "Encountered error while trying to close underlying enumeration", t );
+                    }
+
+                    prefetched = null;
+                    respDone = getResponse( req, e1 );
+                    return next;
+                }
+
+                // need to add non-ldap URLs as-is
+                if ( !val.startsWith( "ldap" ) )
+                {
+                    respRef.getReferral().addLdapUrl( val );
+                    continue;
+                }
+
+                // parse the ref value and normalize the DN according to schema 
+                LdapURL ldapUrl = new LdapURL();
+                try
+                {
+                    ldapUrl.parse( val.toCharArray() );
+                }
+                catch ( LdapURLEncodingException e )
+                {
+                    LOG
+                        .error( "Bad URL (" + val + ") for ref in " + result.getName()
+                            + ".  Reference will be ignored." );
+                    try
+                    {
+                        underlying.close();
+                    }
+                    catch ( Throwable t )
+                    {
+                        LOG.error( "Encountered error while trying to close underlying enumeration", t );
+                    }
+
+                    prefetched = null;
+                    respDone = getResponse( req, e );
+                    return next;
+                }
+
+                StringBuffer buf = new StringBuffer();
+                buf.append( ldapUrl.getScheme() );
+                buf.append( ldapUrl.getHost() );
+                if ( ldapUrl.getPort() > 0 )
+                {
+                    buf.append( ":" );
+                    buf.append( ldapUrl.getPort() );
+                }
+                buf.append( "/" );
+                buf.append( ldapUrl.getDn() );
+                buf.append( "??" );
+
+                switch ( scope )
+                {
+                    case ( SearchControls.SUBTREE_SCOPE  ):
+                        buf.append( "sub" );
+                        break;
+
+                    // if we search for one level and encounter a referral then search
+                    // must be continued at that node using base level search scope
+                    case ( SearchControls.ONELEVEL_SCOPE  ):
+                        buf.append( "base" );
+                        break;
+                    case ( SearchControls.OBJECT_SCOPE  ):
+                        buf.append( "base" );
+                        break;
+                    default:
+                        throw new IllegalStateException( "Unknown recognized search scope: " + scope );
+                }
+
+                respRef.getReferral().addLdapUrl( buf.toString() );
+            }
+
+            prefetched = respRef;
+        }
+
+        return next;
+    }
+
+
+    /**
+     * Unsupported so it throws an exception.
+     *
+     * @throws UnsupportedOperationException
+     */
+    public void remove()
+    {
+        throw new UnsupportedOperationException();
+    }
+
+
+    SearchResponseDone getResponse( SearchRequest req, Exception e )
+    {
+        String msg = "failed on search operation";
+        
+        if ( IS_DEBUG )
+        {
+            msg += ":\n" + req + ":\n" + ExceptionUtils.getStackTrace( e );
+        }
+
+        SearchResponseDone resp = ( SearchResponseDone ) req.getResultResponse();
+        ResultCodeEnum code;
+        
+        if ( e instanceof LdapException )
+        {
+            code = ( ( LdapException ) e ).getResultCode();
+        }
+        else
+        {
+            code = ResultCodeEnum.getBestEstimate( e, req.getType() );
+        }
+
+        resp.getLdapResult().setResultCode( code );
+        resp.getLdapResult().setErrorMessage( msg );
+
+        if ( e instanceof NamingException )
+        {
+            NamingException ne = ( NamingException ) e;
+            
+            if ( ( ne.getResolvedName() != null )
+                && ( ( code == ResultCodeEnum.NO_SUCH_OBJECT ) || ( code == ResultCodeEnum.ALIAS_PROBLEM )
+                    || ( code == ResultCodeEnum.INVALID_DN_SYNTAX ) || ( code == ResultCodeEnum.ALIAS_DEREFERENCING_PROBLEM ) ) )
+            {
+                resp.getLdapResult().setMatchedDn( (LdapDN)ne.getResolvedName() );
+            }
+        }
+        
+        registry.removeOutstandingRequest( session, req.getMessageId() );
+        return resp;
+    }
+}
\ No newline at end of file
diff --git a/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/UnbindHandler.java b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/UnbindHandler.java
new file mode 100644
index 0000000..b520f48
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/UnbindHandler.java
@@ -0,0 +1,43 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.ldap.handlers;
+
+
+import org.apache.directory.shared.ldap.message.UnbindRequest;
+import org.apache.mina.common.IoSession;
+
+
+/**
+ * A no reply protocol handler implementation for LDAP {@link
+ * org.apache.directory.shared.ldap.message.UnbindRequest}s.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public abstract class UnbindHandler extends AbstractLdapHandler
+{
+    public final void messageReceived( IoSession session, Object request ) throws Exception
+    {
+        unbindMessageReceived( session, ( UnbindRequest ) request );
+    }
+
+
+    protected abstract void unbindMessageReceived( IoSession session, UnbindRequest unbindRequest ) throws Exception;
+}
diff --git a/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/bind/AbstractSaslCallbackHandler.java b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/bind/AbstractSaslCallbackHandler.java
new file mode 100644
index 0000000..3c6c4c5
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/bind/AbstractSaslCallbackHandler.java
@@ -0,0 +1,274 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.
+ *
+ */
+package org.apache.directory.server.ldap.handlers.bind;
+
+
+import org.apache.directory.server.constants.ServerDNConstants;
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.shared.ldap.constants.AuthenticationLevel;
+import org.apache.directory.shared.ldap.exception.LdapException;
+import org.apache.directory.shared.ldap.message.BindRequest;
+import org.apache.directory.shared.ldap.message.LdapResult;
+import org.apache.directory.shared.ldap.message.MutableControl;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.util.ExceptionUtils;
+import org.apache.mina.common.IoSession;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.naming.Context;
+import javax.naming.NamingException;
+import javax.naming.ldap.InitialLdapContext;
+import javax.naming.ldap.LdapContext;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.sasl.AuthorizeCallback;
+import javax.security.sasl.RealmCallback;
+import java.util.Hashtable;
+
+
+/**
+ * Base class for all SASL {@link CallbackHandler}s.  Implementations of SASL mechanisms
+ * selectively override the methods relevant to their mechanism.
+ * 
+ * @see javax.security.auth.callback.CallbackHandler
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public abstract class AbstractSaslCallbackHandler implements CallbackHandler
+{
+    /** The logger instance */
+    private static final Logger LOG = LoggerFactory.getLogger( AbstractSaslCallbackHandler.class );
+
+    /** An empty control array */ 
+    private static final MutableControl[] EMPTY = new MutableControl[0];
+
+    private String username;
+    private String realm;
+    protected final DirectoryService directoryService;
+
+
+    /**
+     * Creates a new instance of AbstractSaslCallbackHandler.
+     *
+     * @param directoryService
+     */
+    protected AbstractSaslCallbackHandler( DirectoryService directoryService )
+    {
+        this.directoryService = directoryService;
+    }
+
+
+    /**
+     * Implementors use this method to access the username resulting from a callback.
+     * Callback default name will be username, eg 'hnelson', for CRAM-MD5 and DIGEST-MD5.
+     * The {@link NameCallback} is not used by GSSAPI.
+     */
+    protected String getUsername()
+    {
+        return username;
+    }
+
+
+    /**
+     * Implementors use this method to access the realm resulting from a callback.
+     * Callback default text will be realm name, eg 'example.com', for DIGEST-MD5.
+     * The {@link RealmCallback} is not used by GSSAPI nor by CRAM-MD5.
+     */
+    protected String getRealm()
+    {
+        return realm;
+    }
+
+    /**
+     * Implementors set the password based on a lookup, using the username and
+     * realm as keys.
+     * <ul>
+     * <li>For DIGEST-MD5, lookup password based on username and realm.
+     * <li>For CRAM-MD5, lookup password based on username.
+     * <li>For GSSAPI, this callback is unused.
+     * </ul>
+     * @param username The username.
+     * @param realm The realm.
+     * @return The password resulting from the lookup.
+     */
+    protected abstract String lookupPassword( String username, String realm );
+
+
+    /**
+     * Final check to authorize user.  Used by all SASL mechanisms.  This
+     * is the only callback used by GSSAPI.
+     * 
+     * Implementors use setAuthorizedID() to set the base DN after canonicalization.
+     * Implementors must setAuthorized() to <code>true</code> if authentication was successful.
+     * 
+     * @param callback An {@link AuthorizeCallback}.
+     */
+    protected abstract void authorize( AuthorizeCallback callback );
+
+
+    /**
+     * SaslServer will use this method to call various callbacks, depending on the SASL
+     * mechanism in use for a session.
+     * 
+     * @param callbacks An array of one or more callbacks.
+     */
+    public void handle( Callback[] callbacks )
+    {
+        for ( int i = 0; i < callbacks.length; i++ )
+        {
+            Callback callback = callbacks[i];
+
+            if ( LOG.isDebugEnabled() )
+            {
+                LOG.debug( "Processing callback " + ( i + 1 ) + " of " + callbacks.length + ":  "
+                        + callback.getClass().toString() );
+            }
+
+            if ( callback instanceof NameCallback )
+            {
+                NameCallback nameCB = ( NameCallback ) callback;
+                LOG.debug( "NameCallback default name:  {}", nameCB.getDefaultName() );
+
+                username = nameCB.getDefaultName();
+            }
+            else if ( callback instanceof RealmCallback )
+            {
+                RealmCallback realmCB = ( RealmCallback ) callback;
+                LOG.debug( "RealmCallback default text:  {}", realmCB.getDefaultText() );
+
+                realm = realmCB.getDefaultText();
+            }
+            else if ( callback instanceof PasswordCallback )
+            {
+                PasswordCallback passwordCB = ( PasswordCallback ) callback;
+                String userPassword = lookupPassword( getUsername(), getRealm() );
+
+                if ( userPassword != null )
+                {
+                    passwordCB.setPassword( userPassword.toCharArray() );
+                }
+            }
+            else if ( callback instanceof AuthorizeCallback )
+            {
+                AuthorizeCallback authorizeCB = ( AuthorizeCallback ) callback;
+
+                // hnelson (CRAM-MD5, DIGEST-MD5)
+                // hnelson@EXAMPLE.COM (GSSAPI)
+                LOG.debug( "AuthorizeCallback authnID:  {}", authorizeCB.getAuthenticationID() );
+
+                // hnelson (CRAM-MD5, DIGEST-MD5)
+                // hnelson@EXAMPLE.COM (GSSAPI)
+                LOG.debug( "AuthorizeCallback authzID:  {}", authorizeCB.getAuthorizationID() );
+
+                // null (CRAM-MD5, DIGEST-MD5, GSSAPI)
+                LOG.debug( "AuthorizeCallback authorizedID:  {}", authorizeCB.getAuthorizedID() );
+
+                // false (CRAM-MD5, DIGEST-MD5, GSSAPI)
+                LOG.debug( "AuthorizeCallback isAuthorized:  {}", authorizeCB.isAuthorized() );
+
+                authorize( authorizeCB );
+            }
+        }
+    }
+
+
+    /**
+     * Convenience method for acquiring an {@link LdapContext} for the client to use for the
+     * duration of a session.
+     * 
+     * @param session The current session.
+     * @param bindRequest The current BindRequest.
+     * @param env An environment to be used to acquire an {@link LdapContext}.
+     * @return An {@link LdapContext} for the client.
+     */
+    protected LdapContext getContext( IoSession session, BindRequest bindRequest, Hashtable<String, Object> env )
+    {
+        LdapResult result = bindRequest.getResultResponse().getLdapResult();
+
+        LdapContext ctx = null;
+
+        try
+        {
+            MutableControl[] connCtls = bindRequest.getControls().values().toArray( EMPTY );
+            env.put( DirectoryService.JNDI_KEY, directoryService );
+            ctx = new InitialLdapContext( env, connCtls );
+        }
+        catch ( NamingException e )
+        {
+            ResultCodeEnum code;
+
+            if ( e instanceof LdapException )
+            {
+                code = ( ( LdapException ) e ).getResultCode();
+                result.setResultCode( code );
+            }
+            else
+            {
+                code = ResultCodeEnum.getBestEstimate( e, bindRequest.getType() );
+                result.setResultCode( code );
+            }
+
+            String msg = "Bind failed: " + e.getMessage();
+
+            if ( LOG.isDebugEnabled() )
+            {
+                msg += ":\n" + ExceptionUtils.getStackTrace( e );
+                msg += "\n\nBindRequest = \n" + bindRequest.toString();
+            }
+
+            if ( ( e.getResolvedName() != null )
+                && ( ( code == ResultCodeEnum.NO_SUCH_OBJECT ) || ( code == ResultCodeEnum.ALIAS_PROBLEM )
+                    || ( code == ResultCodeEnum.INVALID_DN_SYNTAX ) || ( code == ResultCodeEnum.ALIAS_DEREFERENCING_PROBLEM ) ) )
+            {
+                result.setMatchedDn( ( LdapDN ) e.getResolvedName() );
+            }
+
+            result.setErrorMessage( msg );
+            session.write( bindRequest.getResultResponse() );
+            ctx = null;
+        }
+
+        return ctx;
+    }
+
+
+    /**
+     * Convenience method for getting an environment suitable for acquiring
+     * an {@link LdapContext} for the client.
+     * 
+     * @param session The current session.
+     * @return An environment suitable for acquiring an {@link LdapContext} for the client.
+     */
+    protected Hashtable<String, Object> getEnvironment( IoSession session )
+    {
+        Hashtable<String, Object> env = new Hashtable<String, Object>();
+        env.put( Context.PROVIDER_URL, session.getAttribute( "baseDn" ) );
+        env.put( Context.INITIAL_CONTEXT_FACTORY, "org.apache.directory.server.core.jndi.CoreContextFactory" );
+        env.put( Context.SECURITY_PRINCIPAL, ServerDNConstants.ADMIN_SYSTEM_DN );
+        env.put( Context.SECURITY_CREDENTIALS, "secret" );
+        env.put( Context.SECURITY_AUTHENTICATION, AuthenticationLevel.SIMPLE.toString() );
+
+        return env;
+    }
+}
diff --git a/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/bind/CramMd5CallbackHandler.java b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/bind/CramMd5CallbackHandler.java
new file mode 100644
index 0000000..d9286df
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/bind/CramMd5CallbackHandler.java
@@ -0,0 +1,94 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.
+ *
+ */
+package org.apache.directory.server.ldap.handlers.bind;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.shared.ldap.message.BindRequest;
+import org.apache.mina.common.IoSession;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.naming.Context;
+import javax.naming.ldap.LdapContext;
+import javax.security.sasl.AuthorizeCallback;
+import java.util.Hashtable;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class CramMd5CallbackHandler extends AbstractSaslCallbackHandler
+{
+    private static final Logger LOG = LoggerFactory.getLogger( CramMd5CallbackHandler.class );
+
+    private IoSession session;
+    private BindRequest bindRequest;
+
+    private String bindDn;
+    private String userPassword;
+
+
+    /**
+     * Creates a new instance of CramMd5CallbackHandler.
+     *
+     * @param session the mina IoSession
+     * @param bindRequest the bind message
+     * @param directoryService the directory service core
+     */
+    public CramMd5CallbackHandler( DirectoryService directoryService,  IoSession session, BindRequest bindRequest )
+    {
+        super( directoryService );
+        this.session = session;
+        this.bindRequest = bindRequest;
+    }
+
+
+    protected String lookupPassword( String username, String realm )
+    {
+        Hashtable<String, Object> env = getEnvironment( session );
+
+        LdapContext ctx = getContext( session, bindRequest, env );
+
+        GetBindDn getDn = new GetBindDn( username );
+
+        // Don't actually want the entry, rather the hacked in dn.
+        getDn.execute( ctx, null );
+        bindDn = getDn.getBindDn();
+        userPassword = getDn.getUserPassword();
+
+        return userPassword;
+    }
+
+
+    protected void authorize( AuthorizeCallback authorizeCB )
+    {
+        if ( LOG.isDebugEnabled() )
+        {
+            LOG.debug( "Converted username " + getUsername() + " to DN " + bindDn + " with password " + userPassword );
+        }
+
+        session.setAttribute( Context.SECURITY_PRINCIPAL, bindDn );
+
+        authorizeCB.setAuthorizedID( bindDn );
+        authorizeCB.setAuthorized( true );
+    }
+}
diff --git a/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/bind/CramMd5MechanismHandler.java b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/bind/CramMd5MechanismHandler.java
new file mode 100644
index 0000000..f829fd0
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/bind/CramMd5MechanismHandler.java
@@ -0,0 +1,79 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.
+ *
+ */
+package org.apache.directory.server.ldap.handlers.bind;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.shared.ldap.constants.SupportedSaslMechanisms;
+import org.apache.directory.shared.ldap.message.BindRequest;
+import org.apache.mina.common.IoSession;
+
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.sasl.Sasl;
+import javax.security.sasl.SaslServer;
+import java.util.HashMap;
+import java.util.Map;
+
+
+/**
+ * The CRAM-MD Sasl mechanism handler.
+ *
+ * @org.apache.xbean.XBean
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class CramMd5MechanismHandler implements MechanismHandler
+{
+    private DirectoryService directoryService;
+
+
+    public void setDirectoryService( DirectoryService directoryService )
+    {
+        this.directoryService = directoryService;
+    }
+
+    
+    public SaslServer handleMechanism( IoSession session, BindRequest bindRequest ) throws Exception
+    {
+        SaslServer ss;
+
+        if ( session.containsAttribute( SASL_CONTEXT ) )
+        {
+            ss = ( SaslServer ) session.getAttribute( SASL_CONTEXT );
+        }
+        else
+        {
+            String saslHost = ( String ) session.getAttribute( "saslHost" );
+
+            /*
+             * Sasl will throw an exception is Sasl.QOP properties are set.
+             * CRAM-MD5 doesn't support QoP.
+             */
+            Map<String, String> saslProps = new HashMap<String, String>();
+
+            CallbackHandler callbackHandler = new CramMd5CallbackHandler( directoryService, session, bindRequest );
+
+            ss = Sasl.createSaslServer( SupportedSaslMechanisms.CRAM_MD5, "ldap", saslHost, saslProps, callbackHandler );
+            session.setAttribute( SASL_CONTEXT, ss );
+        }
+
+        return ss;
+    }
+}
diff --git a/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/bind/DigestMd5CallbackHandler.java b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/bind/DigestMd5CallbackHandler.java
new file mode 100644
index 0000000..a99c8ec
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/bind/DigestMd5CallbackHandler.java
@@ -0,0 +1,96 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.
+ *
+ */
+package org.apache.directory.server.ldap.handlers.bind;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.shared.ldap.message.BindRequest;
+import org.apache.mina.common.IoSession;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.naming.Context;
+import javax.naming.ldap.LdapContext;
+import javax.security.sasl.AuthorizeCallback;
+import java.util.Hashtable;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class DigestMd5CallbackHandler extends AbstractSaslCallbackHandler
+{
+    private static final Logger LOG = LoggerFactory.getLogger( DigestMd5CallbackHandler.class );
+
+    private IoSession session;
+    private BindRequest bindRequest;
+
+    private String bindDn;
+    private String userPassword;
+
+
+    /**
+     * Creates a new instance of DigestMd5CallbackHandler.
+     *
+     * @param session the mina IoSession
+     * @param bindRequest the bind message
+     * @param directoryService the directory service core
+     */
+    public DigestMd5CallbackHandler( DirectoryService directoryService, IoSession session, BindRequest bindRequest )
+    {
+        super( directoryService );
+        this.session = session;
+        this.bindRequest = bindRequest;
+    }
+
+
+    protected String lookupPassword( String username, String realm )
+    {
+        Hashtable<String, Object> env = getEnvironment( session );
+
+        LdapContext ctx = getContext( session, bindRequest, env );
+
+        // TODO - Use realm with multi-realm support.
+
+        GetBindDn getDn = new GetBindDn( username );
+
+        // Don't actually want the entry, rather the hacked in dn.
+        getDn.execute( ctx, null );
+        bindDn = getDn.getBindDn();
+        userPassword = getDn.getUserPassword();
+
+        return userPassword;
+    }
+
+
+    protected void authorize( AuthorizeCallback authorizeCB )
+    {
+        if ( LOG.isDebugEnabled() )
+        {
+            LOG.debug( "Converted username " + getUsername() + " to DN " + bindDn + " with password " + userPassword + "." );
+        }
+
+        session.setAttribute( Context.SECURITY_PRINCIPAL, bindDn );
+
+        authorizeCB.setAuthorizedID( bindDn );
+        authorizeCB.setAuthorized( true );
+    }
+}
diff --git a/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/bind/DigestMd5MechanismHandler.java b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/bind/DigestMd5MechanismHandler.java
new file mode 100644
index 0000000..3e294d6
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/bind/DigestMd5MechanismHandler.java
@@ -0,0 +1,73 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.
+ *
+ */
+package org.apache.directory.server.ldap.handlers.bind;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.shared.ldap.constants.SupportedSaslMechanisms;
+import org.apache.directory.shared.ldap.message.BindRequest;
+import org.apache.mina.common.IoSession;
+
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.sasl.Sasl;
+import javax.security.sasl.SaslServer;
+import java.util.Map;
+
+
+/**
+ * The DIGEST-MD5 mechanism handler.
+ * 
+ * @org.apache.xbean.XBean
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class DigestMd5MechanismHandler implements MechanismHandler
+{
+    private DirectoryService directoryService;
+
+
+    public void setDirectoryService( DirectoryService directoryService )
+    {
+        this.directoryService = directoryService;
+    }
+
+    
+    public SaslServer handleMechanism( IoSession session, BindRequest bindRequest ) throws Exception
+    {
+        SaslServer ss;
+
+        if ( session.containsAttribute( SASL_CONTEXT ) )
+        {
+            ss = ( SaslServer ) session.getAttribute( SASL_CONTEXT );
+        }
+        else
+        {
+            String saslHost = ( String ) session.getAttribute( "saslHost" );
+            Map<String, String> saslProps = ( Map<String, String> ) session.getAttribute( "saslProps" );
+
+            CallbackHandler callbackHandler = new DigestMd5CallbackHandler( directoryService, session, bindRequest );
+
+            ss = Sasl.createSaslServer( SupportedSaslMechanisms.DIGEST_MD5, "ldap", saslHost, saslProps, callbackHandler );
+            session.setAttribute( SASL_CONTEXT, ss );
+        }
+
+        return ss;
+    }
+}
diff --git a/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/bind/GetBindDn.java b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/bind/GetBindDn.java
new file mode 100644
index 0000000..17cb8c3
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/bind/GetBindDn.java
@@ -0,0 +1,150 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.ldap.handlers.bind;
+
+
+import javax.naming.Name;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.SearchResult;
+
+import org.apache.directory.server.protocol.shared.store.ContextOperation;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.util.StringTools;
+
+
+/**
+ * Encapsulates the action of looking up a user in an embedded ApacheDS DIT.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 494161 $, $Date: 2007-01-08 11:39:36 -0800 (Mon, 08 Jan 2007) $
+ */
+public class GetBindDn implements ContextOperation
+{
+    private static final long serialVersionUID = 4598007518413451945L;
+
+    /** The name of the principal to get. */
+    private String username;
+
+    private String bindDn;
+    private String userPassword;
+
+
+    /**
+     * Creates the action to be used against the embedded ApacheDS DIT.
+     * 
+     * @param username The username to search for in the directory.
+     */
+    public GetBindDn( String username )
+    {
+        this.username = username;
+    }
+
+
+    /**
+     * Accessor method for retrieving the DN for the username.
+     *
+     * @return The DN to bind the user as.
+     */
+    public String getBindDn()
+    {
+        return bindDn;
+    }
+
+
+    /**
+     * Accessor method for retrieving the user's password.
+     *
+     * @return The user's password.
+     */
+    public String getUserPassword()
+    {
+        return userPassword;
+    }
+
+
+    /**
+     * Note that the base is a relative path from the existing context.
+     * It is not a DN.
+     */
+    public Object execute( DirContext ctx, Name base )
+    {
+        if ( username == null )
+        {
+            return null;
+        }
+
+        String[] attrIDs =
+            { SchemaConstants.USER_PASSWORD_AT };
+
+        Attributes matchAttrs = new AttributesImpl( true );
+        matchAttrs.put( new AttributeImpl( "uid", username ) );
+
+        try
+        {
+            NamingEnumeration<SearchResult> answer = ctx.search( "", matchAttrs, attrIDs );
+
+            if ( answer.hasMore() )
+            {
+                SearchResult result = answer.next();
+
+                // Changed from original GetPrincipal, along with accessor and member variable.
+                bindDn = result.getName();
+
+                Attributes attrs = result.getAttributes();
+
+                if ( attrs == null )
+                {
+                    return null;
+                }
+
+                Object userPassword;
+                Attribute userPasswordAttr = attrs.get( SchemaConstants.USER_PASSWORD_AT );
+
+                if ( userPasswordAttr == null )
+                {
+                    userPassword = "";
+                }
+                else
+                {
+                    userPassword = userPasswordAttr.get();
+
+                    if ( userPassword instanceof byte[] )
+                    {
+                        userPassword = StringTools.asciiBytesToString( ( byte[] ) userPassword );
+                    }
+                }
+
+                this.userPassword = ( String ) userPassword;
+            }
+        }
+        catch ( NamingException e )
+        {
+            return null;
+        }
+
+        return null;
+    }
+}
diff --git a/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/bind/GssapiCallbackHandler.java b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/bind/GssapiCallbackHandler.java
new file mode 100644
index 0000000..0ad4356
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/bind/GssapiCallbackHandler.java
@@ -0,0 +1,92 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.
+ *
+ */
+package org.apache.directory.server.ldap.handlers.bind;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.kerberos.shared.store.PrincipalStoreEntry;
+import org.apache.directory.server.kerberos.shared.store.operations.GetPrincipal;
+import org.apache.directory.shared.ldap.message.BindRequest;
+import org.apache.mina.common.IoSession;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.naming.Context;
+import javax.naming.ldap.LdapContext;
+import javax.security.auth.kerberos.KerberosPrincipal;
+import javax.security.sasl.AuthorizeCallback;
+import java.util.Hashtable;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class GssapiCallbackHandler extends AbstractSaslCallbackHandler
+{
+    private static final Logger LOG = LoggerFactory.getLogger( GssapiCallbackHandler.class );
+
+    private IoSession session;
+    private BindRequest bindRequest;
+
+
+    /**
+     * Creates a new instance of GssapiCallbackHandler.
+     *
+     * @param session the mina IO session
+     * @param bindRequest the bind message
+     * @param directoryService the directory service core
+     */
+    public GssapiCallbackHandler( DirectoryService directoryService, IoSession session, BindRequest bindRequest )
+    {
+        super( directoryService );
+        this.session = session;
+        this.bindRequest = bindRequest;
+    }
+
+
+    protected String lookupPassword( String username, String password )
+    {
+        // do nothing, password not used by GSSAPI
+        return null;
+    }
+
+
+    protected void authorize( AuthorizeCallback authorizeCB )
+    {
+        LOG.debug( "Processing conversion of principal name to DN." );
+
+        Hashtable<String, Object> env = getEnvironment( session );
+
+        LdapContext ctx = getContext( session, bindRequest, env );
+
+        String username = authorizeCB.getAuthorizationID();
+
+        GetPrincipal getPrincipal = new GetPrincipal( new KerberosPrincipal( username ) );
+        PrincipalStoreEntry entry = ( PrincipalStoreEntry ) getPrincipal.execute( ctx, null );
+        String bindDn = entry.getDistinguishedName();
+
+        LOG.debug( "Converted username {} to DN {}.", username, bindDn );
+        session.setAttribute( Context.SECURITY_PRINCIPAL, bindDn );
+
+        authorizeCB.setAuthorizedID( bindDn );
+        authorizeCB.setAuthorized( true );
+    }
+}
diff --git a/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/bind/GssapiMechanismHandler.java b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/bind/GssapiMechanismHandler.java
new file mode 100644
index 0000000..24f171b
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/bind/GssapiMechanismHandler.java
@@ -0,0 +1,84 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.
+ *
+ */
+package org.apache.directory.server.ldap.handlers.bind;
+
+
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.shared.ldap.constants.SupportedSaslMechanisms;
+import org.apache.directory.shared.ldap.message.BindRequest;
+import org.apache.mina.common.IoSession;
+
+import javax.security.auth.Subject;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.sasl.Sasl;
+import javax.security.sasl.SaslServer;
+import java.security.PrivilegedExceptionAction;
+import java.util.Map;
+
+
+/**
+ * The GSSAPI Sasl mechanism handler.
+ *
+ * @org.apache.xbean.XBean
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class GssapiMechanismHandler implements MechanismHandler
+{
+    private DirectoryService directoryService;
+
+
+    public void setDirectoryService( DirectoryService directoryService )
+    {
+        this.directoryService = directoryService;
+    }
+
+    
+    public SaslServer handleMechanism( IoSession session, BindRequest bindRequest ) throws Exception
+    {
+        SaslServer ss;
+
+        if ( session.containsAttribute( SASL_CONTEXT ) )
+        {
+            ss = ( SaslServer ) session.getAttribute( SASL_CONTEXT );
+        }
+        else
+        {
+            Subject subject = ( Subject ) session.getAttribute( "saslSubject" );
+
+            final Map<String, String> saslProps = ( Map<String, String> ) session.getAttribute( "saslProps" );
+            final String saslHost = ( String ) session.getAttribute( "saslHost" );
+
+            final CallbackHandler callbackHandler = new GssapiCallbackHandler( directoryService, session, bindRequest );
+
+            ss = ( SaslServer ) Subject.doAs( subject, new PrivilegedExceptionAction<SaslServer>()
+            {
+                public SaslServer run() throws Exception
+                {
+                    return Sasl.createSaslServer( SupportedSaslMechanisms.GSSAPI, "ldap", saslHost, saslProps, callbackHandler );
+                }
+            } );
+
+            session.setAttribute( SASL_CONTEXT, ss );
+        }
+
+        return ss;
+    }
+}
diff --git a/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/bind/MechanismHandler.java b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/bind/MechanismHandler.java
new file mode 100644
index 0000000..aff4bbf
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/bind/MechanismHandler.java
@@ -0,0 +1,54 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.
+ *
+ */
+package org.apache.directory.server.ldap.handlers.bind;
+
+
+import javax.security.sasl.SaslServer;
+
+import org.apache.directory.shared.ldap.message.BindRequest;
+import org.apache.mina.common.IoSession;
+
+
+/**
+ * An interface for retrieving a {@link SaslServer} for a session.
+ * 
+ * @see javax.security.sasl.SaslServer
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public interface MechanismHandler
+{
+    /**
+     * A key constant ({@value}) for storing the SASL context in the session.
+     */
+    public static final String SASL_CONTEXT = "saslContext";
+
+
+    /**
+     * Implementors will use the session and message to determine what kind of
+     * {@link SaslServer} to create and what initialization parameters it will require.
+     *
+     * @param session
+     * @param bindRequest
+     * @return The {@link SaslServer} to use for the duration of the bound session.
+     * @throws Exception
+     */
+    public SaslServer handleMechanism( IoSession session, BindRequest bindRequest ) throws Exception;
+}
diff --git a/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/bind/SaslFilter.java b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/bind/SaslFilter.java
new file mode 100644
index 0000000..63fabd0
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/bind/SaslFilter.java
@@ -0,0 +1,171 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.
+ *
+ */
+package org.apache.directory.server.ldap.handlers.bind;
+
+
+import javax.security.sasl.Sasl;
+import javax.security.sasl.SaslException;
+import javax.security.sasl.SaslServer;
+
+import org.apache.directory.shared.ldap.constants.SaslQoP;
+import org.apache.mina.common.ByteBuffer;
+import org.apache.mina.common.IoFilterAdapter;
+import org.apache.mina.common.IoSession;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * An {@link IoFilterAdapter} that handles integrity and confidentiality protection
+ * for a SASL bound session.  The SaslFilter must be constructed with a SASL
+ * context that has completed SASL negotiation.  Some SASL mechanisms, such as
+ * CRAM-MD5, only support authentication and thus do not need this filter.  DIGEST-MD5
+ * and GSSAPI do support message integrity and confidentiality and, therefore,
+ * do need this filter.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class SaslFilter extends IoFilterAdapter
+{
+    private static final Logger log = LoggerFactory.getLogger( SaslFilter.class );
+
+    /**
+     * A session attribute key that makes next one write request bypass
+     * this filter (not adding a security layer).  This is a marker attribute,
+     * which means that you can put whatever as its value. ({@link Boolean#TRUE}
+     * is preferred.)  The attribute is automatically removed from the session
+     * attribute map as soon as {@link IoSession#write(Object)} is invoked,
+     * and therefore should be put again if you want to make more messages
+     * bypass this filter.
+     */
+    public static final String DISABLE_SECURITY_LAYER_ONCE = SaslFilter.class.getName() + ".DisableSecurityLayerOnce";
+
+    private SaslServer context;
+
+
+    /**
+     * Creates a new instance of SaslFilter.  The SaslFilter must be constructed
+     * with a SASL context that has completed SASL negotiation.  The SASL context
+     * will be used to provide message integrity and, optionally, message
+     * confidentiality.
+     *
+     * @param context The initialized SASL context.
+     */
+    public SaslFilter( SaslServer context )
+    {
+        if ( context == null )
+        {
+            throw new IllegalStateException();
+        }
+
+        this.context = context;
+    }
+
+
+    public void messageReceived( NextFilter nextFilter, IoSession session, Object message ) throws SaslException
+    {
+        log.debug( "Message received:  {}", message );
+
+        /*
+         * Unwrap the data for mechanisms that support QoP (DIGEST-MD5, GSSAPI).
+         */
+        String qop = ( String ) context.getNegotiatedProperty( Sasl.QOP );
+        boolean hasSecurityLayer = ( qop != null && ( qop.equals( SaslQoP.QOP_AUTH_INT ) || qop.equals( SaslQoP.QOP_AUTH_CONF ) ) );
+
+        if ( hasSecurityLayer )
+        {
+            /*
+             * Get the buffer as bytes.  First 4 bytes are length as int.
+             */
+            ByteBuffer buf = ( ByteBuffer ) message;
+            int bufferLength = buf.getInt();
+            byte[] bufferBytes = new byte[bufferLength];
+            buf.get( bufferBytes );
+
+            log.debug( "Will use SASL to unwrap received message of length:  {}", bufferLength );
+            byte[] token = context.unwrap( bufferBytes, 0, bufferBytes.length );
+            nextFilter.messageReceived( session, ByteBuffer.wrap( token ) );
+        }
+        else
+        {
+            log.debug( "Will not use SASL on received message." );
+            nextFilter.messageReceived( session, message );
+        }
+    }
+
+
+    public void filterWrite( NextFilter nextFilter, IoSession session, WriteRequest writeRequest ) throws SaslException
+    {
+        log.debug( "Filtering write request:  {}", writeRequest );
+
+        /*
+         * Check if security layer processing should be disabled once.
+         */
+        if ( session.containsAttribute( DISABLE_SECURITY_LAYER_ONCE ) )
+        {
+            // Remove the marker attribute because it is temporary.
+            log.debug( "Disabling SaslFilter once; will not use SASL on write request." );
+            session.removeAttribute( DISABLE_SECURITY_LAYER_ONCE );
+            nextFilter.filterWrite( session, writeRequest );
+            return;
+        }
+
+        /*
+         * Wrap the data for mechanisms that support QoP (DIGEST-MD5, GSSAPI).
+         */
+        String qop = ( String ) context.getNegotiatedProperty( Sasl.QOP );
+        boolean hasSecurityLayer = ( qop != null && ( qop.equals( SaslQoP.QOP_AUTH_INT ) || qop.equals( SaslQoP.QOP_AUTH_CONF ) ) );
+
+        ByteBuffer saslLayerBuffer = null;
+
+        if ( hasSecurityLayer )
+        {
+            /*
+             * Get the buffer as bytes.
+             */
+            ByteBuffer buf = ( ByteBuffer ) writeRequest.getMessage();
+            int bufferLength = buf.remaining();
+            byte[] bufferBytes = new byte[bufferLength];
+            buf.get( bufferBytes );
+
+            log.debug( "Will use SASL to wrap message of length:  {}", bufferLength );
+
+            byte[] saslLayer = context.wrap( bufferBytes, 0, bufferBytes.length );
+
+            /*
+             * Prepend 4 byte length.
+             */
+            saslLayerBuffer = ByteBuffer.allocate( 4 + saslLayer.length );
+            saslLayerBuffer.putInt( saslLayer.length );
+            saslLayerBuffer.put( saslLayer );
+            saslLayerBuffer.position( 0 );
+            saslLayerBuffer.limit( 4 + saslLayer.length );
+
+            log.debug( "Sending encrypted token of length {}.", saslLayerBuffer.limit() );
+            nextFilter.filterWrite( session, new WriteRequest( saslLayerBuffer, writeRequest.getFuture() ) );
+        }
+        else
+        {
+            log.debug( "Will not use SASL on write request." );
+            nextFilter.filterWrite( session, writeRequest );
+        }
+    }
+}
diff --git a/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/bind/SimpleMechanismHandler.java b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/bind/SimpleMechanismHandler.java
new file mode 100644
index 0000000..47cf764
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/bind/SimpleMechanismHandler.java
@@ -0,0 +1,43 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.ldap.handlers.bind;
+
+
+import org.apache.mina.common.IoSession;
+import org.apache.directory.shared.ldap.message.BindRequest;
+
+import javax.security.sasl.SaslServer;
+
+
+/**
+ * A Dummy mechanism handler for Simple mechanism: not really used but needed
+ * for the mechanism map.
+ *
+ * @org.apache.xbean.XBean
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $$Rev$$
+ */
+public class SimpleMechanismHandler implements MechanismHandler
+{
+    public SaslServer handleMechanism( IoSession session, BindRequest bindRequest ) throws Exception
+    {
+        return null;
+    }
+}
diff --git a/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/bind/ntlm/NtlmMechanismHandler.java b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/bind/ntlm/NtlmMechanismHandler.java
new file mode 100644
index 0000000..6ffba1d
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/bind/ntlm/NtlmMechanismHandler.java
@@ -0,0 +1,84 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.
+ *
+ */
+package org.apache.directory.server.ldap.handlers.bind.ntlm;
+
+
+import org.apache.directory.server.ldap.handlers.bind.MechanismHandler;
+import org.apache.directory.shared.ldap.message.BindRequest;
+import org.apache.mina.common.IoSession;
+
+import javax.security.sasl.SaslServer;
+
+
+/**
+ * A handler for the NTLM Sasl and GSS-SPNEGO mechanism. Note that both
+ * mechanisms require an NTLM mechanism provider which could be implemented
+ * using jCIFS or native Win32 system calls via a JNI wrapper.
+ *
+ * @org.apache.xbean.XBean
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class NtlmMechanismHandler implements MechanismHandler
+{
+    private String providerFqcn;
+    private NtlmProvider provider;
+
+
+    public void setNtlmProvider( NtlmProvider provider )
+    {
+        this.provider = provider;
+    }
+
+
+    public void setNtlmProviderFqcn( String fqcnProvider )
+    {
+        this.providerFqcn = fqcnProvider;
+    }
+
+
+    public SaslServer handleMechanism( IoSession session, BindRequest bindRequest ) throws Exception
+    {
+        SaslServer ss;
+
+        if ( session.containsAttribute( SASL_CONTEXT ) )
+        {
+            ss = ( SaslServer ) session.getAttribute( SASL_CONTEXT );
+        }
+        else
+        {
+            if ( provider == null )
+            {
+                initProvider();
+            }
+            
+            ss = new NtlmSaslServer( provider, bindRequest, session );
+            session.setAttribute( SASL_CONTEXT, ss );
+        }
+
+        return ss;
+    }
+
+
+    private void initProvider() throws Exception
+    {
+        provider = ( NtlmProvider ) Class.forName( providerFqcn ).newInstance();
+    }
+}
\ No newline at end of file
diff --git a/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/bind/ntlm/NtlmProvider.java b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/bind/ntlm/NtlmProvider.java
new file mode 100644
index 0000000..760fcb1
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/bind/ntlm/NtlmProvider.java
@@ -0,0 +1,55 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.ldap.handlers.bind.ntlm;
+
+
+import org.apache.mina.common.IoSession;
+
+
+/**
+ * An NTLM authentication service provider.  Multiple providers may be
+ * utilized to conduct the NTLM negotiation over various protocols or by
+ * calling native SSPI interfaces.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public interface NtlmProvider
+{
+    /**
+     * Handles a Type 1 NTLM response from the client to generate an NTLM
+     * Type 2 challenge message.
+     *
+     * @param session the MINA IoSession to store any state to be thread safe
+     * @param type1reponse the Type 1 NTLM response from client
+     * @return the NTLM Type 2 message with the challenge
+     */
+    byte[] generateChallenge( IoSession session, byte[] type1reponse ) throws Exception;
+
+
+    /**
+     * Handles a Type 3 NTLM response from the client.
+     *
+     * @param session the MINA IoSession to store any state to be thread safe
+     * @param type3response the Type 3 NTLM response from the client
+     * @return the result of the authentication from the server
+     */
+    boolean authenticate( IoSession session, byte[] type3response ) throws Exception;
+}
diff --git a/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/bind/ntlm/NtlmSaslServer.java b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/bind/ntlm/NtlmSaslServer.java
new file mode 100644
index 0000000..77d6ec5
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/bind/ntlm/NtlmSaslServer.java
@@ -0,0 +1,188 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.ldap.handlers.bind.ntlm;
+
+
+import org.apache.directory.shared.ldap.constants.SupportedSaslMechanisms;
+import org.apache.directory.shared.ldap.message.BindRequest;
+import org.apache.mina.common.IoSession;
+
+import javax.naming.Context;
+import javax.security.sasl.SaslServer;
+import javax.security.sasl.SaslException;
+
+
+/**
+ * A SaslServer implementation for NTLM based SASL mechanism.  This is
+ * required unfortunately because the JDK's SASL provider does not support
+ * this mechanism.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $$Rev$$
+ */
+public class NtlmSaslServer implements SaslServer
+{
+    enum NegotiationState { INITIALIZED, TYPE_1_RECEIVED, TYPE_2_SENT, TYPE_3_RECEIVED, COMPLETED }
+
+    private NegotiationState state = NegotiationState.INITIALIZED;
+    private final NtlmProvider provider;
+    private final BindRequest request;
+    private final IoSession session;
+
+    
+    public NtlmSaslServer( NtlmProvider provider, BindRequest request, IoSession session )
+    {
+        this.session = session;
+        this.request = request;
+        this.provider = provider;
+    }
+
+
+    public String getMechanismName()
+    {
+        return SupportedSaslMechanisms.NTLM;
+    }
+
+
+    protected void responseRecieved()
+    {
+        switch ( state )
+        {
+            case INITIALIZED:
+                state = NegotiationState.TYPE_1_RECEIVED;
+                break;
+            case TYPE_1_RECEIVED:
+                throw new IllegalStateException( "Cannot receive NTLM message before sending Type 2 challenge." );
+            case TYPE_2_SENT:
+                state = NegotiationState.TYPE_3_RECEIVED;
+                break;
+            case TYPE_3_RECEIVED:
+                throw new IllegalStateException( "Cannot receive NTLM message after Type 3 has been received." );
+            case COMPLETED:
+                throw new IllegalStateException( "Sasl challenge response already completed." );
+        }
+    }
+
+
+    protected void responseSent()
+    {
+        switch ( state )
+        {
+            case INITIALIZED:
+                throw new IllegalStateException( "Cannot send Type 2 challenge before Type 1 response." );
+            case TYPE_1_RECEIVED:
+                state = NegotiationState.TYPE_2_SENT;
+                break;
+            case TYPE_2_SENT:
+                throw new IllegalStateException( "Cannot send Type 2 after it's already sent." );
+            case TYPE_3_RECEIVED:
+                state = NegotiationState.COMPLETED;
+                break;
+            case COMPLETED:
+                throw new IllegalStateException( "Sasl challenge response already completed." );
+        }
+    }
+
+
+    public byte[] evaluateResponse( byte[] response ) throws SaslException
+    {
+        if ( response == null )
+        {
+            throw new NullPointerException( "response was null" );
+        }
+
+        if ( response.length == 0 )
+        {
+            throw new IllegalArgumentException( "response with zero bytes" );
+        }
+
+        responseRecieved();
+        byte[] retval = null;
+
+        switch ( state )
+        {
+            case TYPE_1_RECEIVED:
+                try
+                {
+                    retval = provider.generateChallenge( session, response );
+                }
+                catch ( Exception e )
+                {
+                    throw new SaslException( "There was a failure during NTLM Type 1 message handling.", e );
+                }
+                break;
+            case TYPE_3_RECEIVED:
+                boolean result;
+                try
+                {
+                    result = provider.authenticate( session, response );
+                    session.setAttribute( Context.SECURITY_PRINCIPAL, request.getName().toString() );
+                }
+                catch ( Exception e )
+                {
+                    throw new SaslException( "There was a failure during NTLM Type 3 message handling.", e );
+                }
+
+                if ( ! result )
+                {
+                    throw new SaslException( "Authentication occurred but the credentials were invalid." );
+                }
+                break;
+        }       
+        responseSent();
+        return retval;
+    }
+
+
+    public boolean isComplete()
+    {
+        return state == NegotiationState.COMPLETED;
+    }
+
+
+    // --- NOT USED ---
+    public String getAuthorizationID()
+    {
+        return "";
+    }
+
+
+    public byte[] unwrap( byte[] incoming, int offset, int len ) throws SaslException
+    {
+        return new byte[0];
+    }
+
+
+    public byte[] wrap( byte[] outgoing, int offset, int len ) throws SaslException
+    {
+        return new byte[0];
+    }
+
+
+    public Object getNegotiatedProperty( String propName )
+    {
+        return "";
+    }
+
+
+    public void dispose() throws SaslException
+    {
+    }
+}
diff --git a/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/bind/package-info.java b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/bind/package-info.java
new file mode 100644
index 0000000..d92e01a
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/bind/package-info.java
@@ -0,0 +1,28 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+/**
+ * Contains the implementation of LDAP binds
+ * with Simple and SASL authentication mechanisms.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+package org.apache.directory.server.ldap.handlers.bind;
diff --git a/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/extended/GracefulShutdownHandler.java b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/extended/GracefulShutdownHandler.java
new file mode 100644
index 0000000..fe6cc9d
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/extended/GracefulShutdownHandler.java
@@ -0,0 +1,348 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.ldap.handlers.extended;
+
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.directory.server.constants.ServerDNConstants;
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.jndi.ServerLdapContext;
+import org.apache.directory.server.ldap.ExtendedOperationHandler;
+import org.apache.directory.server.ldap.LdapServer;
+import org.apache.directory.server.ldap.SessionRegistry;
+import org.apache.directory.shared.ldap.message.ExtendedRequest;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.message.extended.GracefulDisconnect;
+import org.apache.directory.shared.ldap.message.extended.GracefulShutdownRequest;
+import org.apache.directory.shared.ldap.message.extended.GracefulShutdownResponse;
+import org.apache.directory.shared.ldap.message.extended.NoticeOfDisconnect;
+import org.apache.mina.common.IoAcceptor;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.common.WriteFuture;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.naming.NamingException;
+import javax.naming.ldap.LdapContext;
+
+
+/**
+ * @org.apache.xbean.XBean
+ *
+ */
+public class GracefulShutdownHandler implements ExtendedOperationHandler
+{
+    private static final Logger LOG = LoggerFactory.getLogger( GracefulShutdownHandler.class );
+    public static final Set<String> EXTENSION_OIDS;
+
+    static
+    {
+        Set<String> set = new HashSet<String>( 3 );
+        set.add( GracefulShutdownRequest.EXTENSION_OID );
+        set.add( GracefulShutdownResponse.EXTENSION_OID );
+        set.add( GracefulDisconnect.EXTENSION_OID );
+        EXTENSION_OIDS = Collections.unmodifiableSet( set );
+    }
+
+
+    public String getOid()
+    {
+        return GracefulShutdownRequest.EXTENSION_OID;
+    }
+
+
+    public void handleExtendedOperation( IoSession requestor, SessionRegistry registry, ExtendedRequest req )
+        throws NamingException
+    {
+        DirectoryService service;
+        ServerLdapContext slc;
+        LdapContext ctx = registry.getLdapContext( requestor, null, false );
+        ctx = ( LdapContext ) ctx.lookup( "" );
+
+        // setup some of the variables we need and make sure they are of the 
+        // right types otherwise send back an operations error in response
+        if ( ctx instanceof ServerLdapContext )
+        {
+            slc = ( ServerLdapContext ) ctx;
+            service = slc.getService();
+        }
+        else
+        {
+            LOG.error( "Encountered session context which was not a ServerLdapContext" );
+            GracefulShutdownResponse msg = new GracefulShutdownResponse( req.getMessageId(),
+                ResultCodeEnum.OPERATIONS_ERROR );
+            msg.getLdapResult().setErrorMessage( "The session context was not a ServerLdapContext" );
+            requestor.write( msg );
+            return;
+        }
+
+        // make sue only the administrator can issue this shutdown request if 
+        // not we respond to the requestor with with insufficientAccessRights(50)
+        if ( !slc.getPrincipal().getName().equalsIgnoreCase( ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED ) )
+        {
+            if ( LOG.isInfoEnabled() )
+            {
+                LOG.info( "Rejected with insufficientAccessRights to attempt for server shutdown by "
+                    + slc.getPrincipal().getName() );
+            }
+
+            requestor
+                .write( new GracefulShutdownResponse( req.getMessageId(), ResultCodeEnum.INSUFFICIENT_ACCESS_RIGHTS ) );
+            return;
+        }
+
+        // -------------------------------------------------------------------
+        // handle the body of this operation below here
+        // -------------------------------------------------------------------
+
+        IoAcceptor acceptor = ( IoAcceptor ) requestor.getService();
+        List<IoSession> sessions = new ArrayList<IoSession>(
+                acceptor.getManagedSessions( requestor.getServiceAddress() ) );
+        GracefulShutdownRequest gsreq = ( GracefulShutdownRequest ) req;
+
+        // build the graceful disconnect message with replicationContexts
+        GracefulDisconnect notice = getGracefulDisconnect( gsreq.getTimeOffline(), gsreq.getDelay() );
+
+        // send (synch) the GracefulDisconnect to each client before unbinding
+        sendGracefulDisconnect( sessions, notice, requestor );
+
+        // wait for the specified delay before we unbind the service 
+        waitForDelay( gsreq.getDelay() );
+
+        // -------------------------------------------------------------------
+        // unbind the server socket for the LDAP service here so no new 
+        // connections are accepted while we process this shutdown request
+        // note that the following must be issued before binding the ldap
+        // service in order to prevent client disconnects on service unbind:
+        // 
+        // minaRegistry.getAcceptor( service.getTransportType() )
+        //                       .setDisconnectClientsOnUnbind( false );
+        // -------------------------------------------------------------------
+        // This might not work, either.
+        acceptor.unbind( requestor.getServiceAddress() );
+
+        // -------------------------------------------------------------------
+        // synchronously send a NoD to clients that are not aware of this resp
+        // after sending the NoD the client is disconnected if still connected
+        // -------------------------------------------------------------------
+        sendNoticeOfDisconnect( sessions, requestor );
+
+        // -------------------------------------------------------------------
+        // respond back to the client that requested the graceful shutdown w/
+        // a success resultCode which confirms all clients have been notified
+        // via the graceful disconnect or NoD and the service has been unbound
+        // preventing new connections; after recieving this response the 
+        // requestor should disconnect and stop using the connection
+        // -------------------------------------------------------------------
+        sendShutdownResponse( requestor, req.getMessageId() );
+
+        if ( service.isExitVmOnShutdown() )
+        {
+            System.exit( 0 );
+        }
+
+    }
+
+
+    /**
+     * Sends a successful response.
+     * 
+     * @param requestor the session of the requestor
+     * @param messageId the message id associaed with this shutdown request
+     */
+    public static void sendShutdownResponse( IoSession requestor, int messageId )
+    {
+        GracefulShutdownResponse msg = new GracefulShutdownResponse( messageId, ResultCodeEnum.SUCCESS );
+        WriteFuture future = requestor.write( msg );
+        future.join();
+        if ( future.isWritten() )
+        {
+            if ( LOG.isInfoEnabled() )
+            {
+                LOG.info( "Sent GracefulShutdownResponse to client: " + requestor.getRemoteAddress() );
+            }
+        }
+        else
+        {
+            LOG.error( "Failed to write GracefulShutdownResponse to client: " + requestor.getRemoteAddress() );
+        }
+        requestor.close();
+    }
+
+
+    /**
+     * Blocks to synchronously send the same GracefulDisconnect message to all 
+     * managed sessions except for the requestor of the GracefulShutdown.
+     * 
+     * @param msg the graceful disconnec extended request to send
+     * @param requestor the session of the graceful shutdown requestor
+     * @param sessions the IoSessions to send disconnect message to
+     */
+    public static void sendGracefulDisconnect( List<IoSession> sessions, GracefulDisconnect msg, IoSession requestor )
+    {
+        List<WriteFuture> writeFutures = new ArrayList<WriteFuture>();
+
+        // asynchronously send GracefulDisconnection messages to all connected
+        // clients giving time for the message to arrive before we block 
+        // waiting for message delivery to the client in the loop below
+
+        if ( sessions != null )
+        {
+            for ( IoSession session : sessions )
+            {
+                // make sure we do not send the disconnect mesasge to the
+                // client which sent the initiating GracefulShutdown request
+                if ( session.equals( requestor ) )
+                {
+                    continue;
+                }
+
+                try
+                {
+                    writeFutures.add( session.write( msg ) );
+                }
+                catch ( Exception e )
+                {
+                    LOG.warn( "Failed to write GracefulDisconnect to client session: " + session, e );
+                }
+            }
+        }
+
+        // wait for GracefulDisconnect messages to be sent before returning
+        for ( WriteFuture future : writeFutures )
+        {
+            try
+            {
+                future.join( 1000 );
+            }
+            catch ( Exception e )
+            {
+                LOG.warn( "Failed to sent GracefulDisconnect", e );
+            }
+        }
+    }
+
+
+    /**
+     * Blocks to synchronously send the a NoticeOfDisconnect message with
+     * the resultCode set to unavailable(52) to all managed sessions except 
+     * for the requestor of the GracefulShutdown.
+     * 
+     * @param requestor the session of the graceful shutdown requestor
+     * @param sessions the sessions from mina
+     */
+    public static void sendNoticeOfDisconnect( List<IoSession> sessions, IoSession requestor )
+    {
+        List<WriteFuture> writeFutures = new ArrayList<WriteFuture>();
+
+        // Send Notification of Disconnection messages to all connected clients.
+        if ( sessions != null )
+        {
+            for ( IoSession session : sessions )
+            {
+                // make sure we do not send the disconnect mesasge to the
+                // client which sent the initiating GracefulShutdown request
+                if ( session.equals( requestor ) )
+                {
+                    continue;
+                }
+
+                try
+                {
+                    writeFutures.add( session.write( NoticeOfDisconnect.UNAVAILABLE ) );
+                }
+                catch ( Exception e )
+                {
+                    LOG.warn( "Failed to sent NoD for client: " + session, e );
+                }
+            }
+        }
+
+        // And close the connections when the NoDs are sent.
+        Iterator<IoSession> sessionIt = sessions.iterator();
+        
+        for ( WriteFuture future : writeFutures )
+        {
+            try
+            {
+                future.join( 1000 );
+                sessionIt.next().close();
+            }
+            catch ( Exception e )
+            {
+                LOG.warn( "Failed to sent NoD.", e );
+            }
+        }
+    }
+
+
+    public static GracefulDisconnect getGracefulDisconnect( int timeOffline, int delay )
+    {
+        // build the graceful disconnect message with replicationContexts
+        // @todo add the referral objects for replication contexts using setup code below
+        //        Iterator list = nexus.listSuffixes( true );
+        //        while ( list.hasNext() )
+        //        {
+        //            LdapName dn = new LdapName( ( String ) list.next() );
+        //            DirectoryPartition partition = nexus.getPartition( dn );
+        //        }
+        return new GracefulDisconnect( timeOffline, delay );
+    }
+
+
+    public static void waitForDelay( int delay )
+    {
+        if ( delay > 0 )
+        {
+            // delay is in seconds
+            long delayMillis = delay * 1000L;
+            long startTime = System.currentTimeMillis();
+
+            while ( ( System.currentTimeMillis() - startTime ) < delayMillis )
+            {
+                try
+                {
+                    Thread.sleep( 250 );
+                }
+                catch ( InterruptedException e )
+                {
+                    LOG.warn( "Got interrupted while waiting for delay before shutdown", e );
+                }
+            }
+        }
+    }
+
+
+    public Set<String> getExtensionOids()
+    {
+        return EXTENSION_OIDS;
+    }
+
+
+    public void setLdapProvider( LdapServer provider )
+    {
+    }
+}
diff --git a/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/extended/LaunchDiagnosticUiHandler.java b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/extended/LaunchDiagnosticUiHandler.java
new file mode 100644
index 0000000..9258ffb
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/extended/LaunchDiagnosticUiHandler.java
@@ -0,0 +1,167 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.ldap.handlers.extended;
+
+
+import java.awt.Dimension;
+import java.awt.Point;
+import java.awt.Toolkit;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import javax.naming.NamingException;
+import javax.naming.ldap.LdapContext;
+import javax.swing.JFrame;
+
+import org.apache.directory.server.constants.ServerDNConstants;
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.interceptor.context.ListSuffixOperationContext;
+import org.apache.directory.server.core.jndi.ServerLdapContext;
+import org.apache.directory.server.core.partition.Partition;
+import org.apache.directory.server.core.partition.PartitionNexus;
+import org.apache.directory.server.core.partition.impl.btree.BTreePartition;
+import org.apache.directory.server.core.partition.impl.btree.gui.PartitionFrame;
+import org.apache.directory.server.ldap.ExtendedOperationHandler;
+import org.apache.directory.server.ldap.LdapServer;
+import org.apache.directory.server.ldap.SessionRegistry;
+import org.apache.directory.server.ldap.gui.SessionsFrame;
+import org.apache.directory.shared.ldap.message.ExtendedRequest;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.message.extended.LaunchDiagnosticUiRequest;
+import org.apache.directory.shared.ldap.message.extended.LaunchDiagnosticUiResponse;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.mina.common.IoSession;
+
+/**
+ * @org.apache.xbean.XBean
+ * 
+ */
+public class LaunchDiagnosticUiHandler implements ExtendedOperationHandler
+{
+    public static final Set<String> EXTENSION_OIDS;
+
+    static
+    {
+        Set<String> set = new HashSet<String>( 3 );
+        set.add( LaunchDiagnosticUiRequest.EXTENSION_OID );
+        set.add( LaunchDiagnosticUiResponse.EXTENSION_OID );
+        EXTENSION_OIDS = Collections.unmodifiableSet( set );
+    }
+
+    private LdapServer ldapProvider;
+
+
+    public String getOid()
+    {
+        return LaunchDiagnosticUiRequest.EXTENSION_OID;
+    }
+
+
+    public void handleExtendedOperation( IoSession requestor, SessionRegistry registry, ExtendedRequest req )
+        throws NamingException
+    {
+        LdapContext ctx = registry.getLdapContext( requestor, null, false );
+        ctx = ( LdapContext ) ctx.lookup( "" );
+
+        if ( ctx instanceof ServerLdapContext )
+        {
+            ServerLdapContext slc = ( ServerLdapContext ) ctx;
+            DirectoryService service = slc.getService();
+
+            if ( !slc.getPrincipal().getName().equalsIgnoreCase( ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED ) )
+            {
+                requestor.write( new LaunchDiagnosticUiResponse( req.getMessageId(),
+                    ResultCodeEnum.INSUFFICIENT_ACCESS_RIGHTS ) );
+                return;
+            }
+
+            requestor.write( new LaunchDiagnosticUiResponse( req.getMessageId() ) );
+
+            PartitionNexus nexus = service.getPartitionNexus();
+            Iterator<String> list = nexus.listSuffixes( new ListSuffixOperationContext( service.getRegistries() ) );
+            int launchedWindowCount = 0;
+            
+            while ( list.hasNext() )
+            {
+                LdapDN dn = new LdapDN( list.next() );
+                Partition partition = nexus.getPartition( dn );
+                
+                if ( partition instanceof BTreePartition )
+                {
+                    BTreePartition btPartition = ( BTreePartition ) partition;
+                    PartitionFrame frame = new PartitionFrame( btPartition, service.getRegistries() );
+                    Point pos = getCenteredPosition( frame );
+                    pos.y = launchedWindowCount * 20 + pos.y;
+                    double multiplier = getAspectRatio() * 20.0;
+                    pos.x = ( int ) ( launchedWindowCount * multiplier ) + pos.x;
+                    frame.setLocation( pos );
+                    frame.setVisible( true );
+                    launchedWindowCount++;
+                }
+            }
+
+            SessionsFrame sessions = new SessionsFrame( ldapProvider.getRegistry() );
+            sessions.setRequestor( requestor );
+            sessions.setLdapProvider( ldapProvider.getHandler() );
+            Point pos = getCenteredPosition( sessions );
+            pos.y = launchedWindowCount * 20 + pos.y;
+            double multiplier = getAspectRatio() * 20.0;
+            pos.x = ( int ) ( launchedWindowCount * multiplier ) + pos.x;
+            sessions.setLocation( pos );
+            sessions.setVisible( true );
+            return;
+        }
+
+        requestor.write( new LaunchDiagnosticUiResponse( req.getMessageId(), ResultCodeEnum.OPERATIONS_ERROR ) );
+    }
+
+
+    public double getAspectRatio()
+    {
+        Toolkit tk = Toolkit.getDefaultToolkit();
+        Dimension screenSize = tk.getScreenSize();
+        return screenSize.getWidth() / screenSize.getHeight();
+    }
+
+
+    public Point getCenteredPosition( JFrame frame )
+    {
+        Point pt = new Point();
+        Toolkit tk = Toolkit.getDefaultToolkit();
+        Dimension screenSize = tk.getScreenSize();
+        pt.x = ( screenSize.width - frame.getWidth() ) / 2;
+        pt.y = ( screenSize.height - frame.getHeight() ) / 2;
+        return pt;
+    }
+
+
+    public Set<String> getExtensionOids()
+    {
+        return EXTENSION_OIDS;
+    }
+
+
+    public void setLdapProvider( LdapServer provider )
+    {
+        this.ldapProvider = provider;
+    }
+}
diff --git a/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/extended/StartTlsHandler.java b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/extended/StartTlsHandler.java
new file mode 100644
index 0000000..2863d90
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/extended/StartTlsHandler.java
@@ -0,0 +1,197 @@
+/*
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *   or more contributor license agreements.  See the NOTICE file
+ *   distributed with this work for additional information
+ *   regarding copyright ownership.  The ASF licenses this file
+ *   to you 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.
+ *
+ */
+package org.apache.directory.server.ldap.handlers.extended;
+
+
+import java.security.KeyStore;
+import java.security.Provider;
+import java.security.SecureRandom;
+import java.security.Security;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+
+import org.apache.directory.server.core.security.CoreKeyStoreSpi;
+import org.apache.directory.server.ldap.ExtendedOperationHandler;
+import org.apache.directory.server.ldap.LdapServer;
+import org.apache.directory.server.ldap.SessionRegistry;
+import org.apache.directory.shared.ldap.message.ExtendedRequest;
+import org.apache.directory.shared.ldap.message.ExtendedResponse;
+import org.apache.directory.shared.ldap.message.ExtendedResponseImpl;
+import org.apache.directory.shared.ldap.message.LdapResult;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.mina.common.IoFilterChain;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.filter.SSLFilter;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * Handler for the StartTLS extended operation.
+ *
+ * @org.apache.xbean.XBean
+ * @see <a href="http://www.ietf.org/rfc/rfc2830.txt">RFC 2830</a>
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class StartTlsHandler implements ExtendedOperationHandler
+{
+    public static final String EXTENSION_OID = "1.3.6.1.4.1.1466.20037";
+
+    private static final Set<String> EXTENSION_OIDS;
+    private static final Logger LOG = LoggerFactory.getLogger( StartTlsHandler.class );
+    
+    private SSLContext sslContext;
+
+    
+    static
+    {
+        Set<String> set = new HashSet<String>( 3 );
+        set.add( EXTENSION_OID );
+        EXTENSION_OIDS = Collections.unmodifiableSet( set );
+    }
+    
+
+    public void handleExtendedOperation( IoSession session, SessionRegistry registry, ExtendedRequest req ) throws Exception
+    {
+        LOG.info( "Handling StartTLS request." );
+        
+        IoFilterChain chain = session.getFilterChain();
+        SSLFilter sslFilter = ( SSLFilter ) chain.get( "sslFilter" );
+        if( sslFilter == null )
+        {
+            sslFilter = new SSLFilter( sslContext );
+            chain.addFirst( "sslFilter", sslFilter );
+        }
+        else
+        {
+            sslFilter.startSSL( session );
+        }
+        
+        ExtendedResponse res = new ExtendedResponseImpl( req.getMessageId() );
+        LdapResult result = res.getLdapResult();
+        result.setResultCode( ResultCodeEnum.SUCCESS );
+        res.setResponseName( EXTENSION_OID );
+        res.setResponse( new byte[ 0 ] );
+
+        // Send a response.
+        session.setAttribute( SSLFilter.DISABLE_ENCRYPTION_ONCE );
+        session.write( res );
+    }
+    
+    
+    class ServerX509TrustManager implements X509TrustManager
+    {
+        public void checkClientTrusted( X509Certificate[] chain, String authType ) throws CertificateException
+        {
+            LOG.debug( "checkClientTrusted() called" );
+        }
+
+        public void checkServerTrusted( X509Certificate[] chain, String authType ) throws CertificateException
+        {
+            LOG.debug( "checkServerTrusted() called" );
+        }
+
+        public X509Certificate[] getAcceptedIssuers()
+        {
+            LOG.debug( "getAcceptedIssuers() called" );
+            return new X509Certificate[0];
+        }
+    }
+
+
+    public final Set<String> getExtensionOids()
+    {
+        return EXTENSION_OIDS;
+    }
+
+
+    public final String getOid()
+    {
+        return EXTENSION_OID;
+    }
+
+    
+    public void setLdapProvider( LdapServer ldapServer )
+    {
+        LOG.debug( "Setting LDAP Service" );
+        Provider provider = Security.getProvider( "SUN" );
+        LOG.debug( "provider = {}", provider );
+        CoreKeyStoreSpi coreKeyStoreSpi = new CoreKeyStoreSpi( ldapServer.getDirectoryService() );
+        KeyStore keyStore = new KeyStore( coreKeyStoreSpi, provider, "JKS" ) {};
+
+        try
+        {
+            keyStore.load( null, null );
+        }
+        catch ( Exception e1 )
+        {
+            throw new RuntimeException( "Failed on keystore load which should never really happen." );
+        }
+        
+        KeyManagerFactory keyManagerFactory = null;
+        try
+        {
+            keyManagerFactory = KeyManagerFactory.getInstance( "SunX509" );
+        }
+        catch ( Exception e )
+        {
+            throw new RuntimeException( "Failed to create KeyManagerFactory", e );
+        }
+        
+        try
+        {
+            keyManagerFactory.init( keyStore, null );
+        }
+        catch ( Exception e )
+        {
+            throw new RuntimeException( "Failed to initialize KeyManagerFactory", e );
+        }
+        
+        try
+        {
+            sslContext = SSLContext.getInstance( "TLS" );
+        }
+        catch ( Exception e )
+        {
+            throw new RuntimeException( "Failed to create SSLContext", e );
+        }
+        
+        try
+        {
+            sslContext.init( keyManagerFactory.getKeyManagers(), 
+                new TrustManager[] { new ServerX509TrustManager() }, 
+                new SecureRandom() );
+        }
+        catch ( Exception e )
+        {
+            throw new RuntimeException( "Failed to initialize SSLContext", e );
+        }
+    }
+}
diff --git a/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/extended/StoredProcedureExtendedOperationHandler.java b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/extended/StoredProcedureExtendedOperationHandler.java
new file mode 100644
index 0000000..5a7b2d1
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/extended/StoredProcedureExtendedOperationHandler.java
@@ -0,0 +1,181 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+
+package org.apache.directory.server.ldap.handlers.extended;
+
+
+import java.io.Serializable;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import javax.naming.ldap.Control;
+import javax.naming.ldap.LdapContext;
+
+import org.apache.commons.lang.SerializationUtils;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.jndi.ServerLdapContext;
+import org.apache.directory.server.core.sp.StoredProcEngine;
+import org.apache.directory.server.core.sp.StoredProcEngineConfig;
+import org.apache.directory.server.core.sp.StoredProcExecutionManager;
+import org.apache.directory.server.core.sp.java.JavaStoredProcEngineConfig;
+import org.apache.directory.server.ldap.ExtendedOperationHandler;
+import org.apache.directory.server.ldap.LdapServer;
+import org.apache.directory.server.ldap.SessionRegistry;
+import org.apache.directory.shared.asn1.ber.Asn1Decoder;
+import org.apache.directory.shared.asn1.ber.IAsn1Container;
+import org.apache.directory.shared.ldap.codec.extended.operations.StoredProcedure;
+import org.apache.directory.shared.ldap.codec.extended.operations.StoredProcedureContainer;
+import org.apache.directory.shared.ldap.codec.extended.operations.StoredProcedureDecoder;
+import org.apache.directory.shared.ldap.codec.extended.operations.StoredProcedure.StoredProcedureParameter;
+import org.apache.directory.shared.ldap.message.ExtendedRequest;
+import org.apache.directory.shared.ldap.message.ExtendedResponse;
+import org.apache.directory.shared.ldap.message.extended.StoredProcedureRequest;
+import org.apache.directory.shared.ldap.message.extended.StoredProcedureResponse;
+import org.apache.directory.shared.ldap.sp.LdapContextParameter;
+import org.apache.directory.shared.ldap.util.StringTools;
+import org.apache.mina.common.IoSession;
+
+
+/**
+ * @org.apache.xbean.XBean
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$ $Date$
+ */
+public class StoredProcedureExtendedOperationHandler implements ExtendedOperationHandler
+{
+    private StoredProcExecutionManager manager;
+    private static final Object[] EMPTY_CLASS_ARRAY = new Object[0];
+    
+    public StoredProcedureExtendedOperationHandler()
+    {
+        super();
+        //StoredProcEngineConfig javaxScriptSPEngineConfig = new JavaxStoredProcEngineConfig();
+        StoredProcEngineConfig javaSPEngineConfig = new JavaStoredProcEngineConfig();
+        List<StoredProcEngineConfig> spEngineConfigs = new ArrayList<StoredProcEngineConfig>();
+        //spEngineConfigs.add( javaxScriptSPEngineConfig );
+        spEngineConfigs.add( javaSPEngineConfig );
+        String spContainer = "ou=Stored Procedures,ou=system";
+        this.manager = new StoredProcExecutionManager( spContainer, spEngineConfigs );
+    }
+
+    public void handleExtendedOperation( IoSession session, SessionRegistry registry, ExtendedRequest req ) throws Exception
+    {
+        Control[] connCtls = req.getControls().values().toArray( new Control[ req.getControls().size() ] );
+        LdapContext ldapContext = registry.getLdapContext( session, connCtls, false);
+        ServerLdapContext ctx;
+        
+        if ( ldapContext instanceof ServerLdapContext )
+        {
+            ctx = ( ServerLdapContext ) ldapContext;
+        }
+        else
+        {
+            ctx = ( ServerLdapContext ) ldapContext.lookup( "" );
+        }
+        
+        StoredProcedure spBean = decodeBean( req.getPayload() );
+        
+        String procedure = StringTools.utf8ToString( spBean.getProcedure() );
+        ServerEntry spUnit = manager.findStoredProcUnit( ctx, procedure, ctx.getService().getRegistries() );
+        StoredProcEngine engine = manager.getStoredProcEngineInstance( spUnit );
+        
+        List<Object> valueList = new ArrayList<Object>( spBean.getParameters().size() );
+        Iterator<StoredProcedureParameter> it = spBean.getParameters().iterator();
+        
+        while ( it.hasNext() )
+        {
+            StoredProcedureParameter pPojo = it.next();
+            byte[] serializedValue = pPojo.getValue();
+            Object value = SerializationUtils.deserialize( serializedValue );
+            
+            if ( value.getClass().equals( LdapContextParameter.class ) )
+            {
+                String paramCtx = ( ( LdapContextParameter ) value ).getValue();
+                value = ctx.lookup( paramCtx );
+            }
+            
+            valueList.add( value );
+        }
+        
+        Object[] values = valueList.toArray( EMPTY_CLASS_ARRAY );
+        
+        Object response = engine.invokeProcedure( ctx, procedure, values );
+        
+        byte[] serializedResponse = SerializationUtils.serialize( ( Serializable ) response );
+        ( ( ExtendedResponse )( req.getResultResponse() ) ).setResponse( serializedResponse );
+        session.write( req.getResultResponse() );
+        
+    }
+    
+    private StoredProcedure decodeBean( byte[] payload )
+    {
+        Asn1Decoder storedProcedureDecoder = new StoredProcedureDecoder();
+        ByteBuffer stream = ByteBuffer.wrap( payload );
+        IAsn1Container storedProcedureContainer = new StoredProcedureContainer();
+
+        try
+        {
+            storedProcedureDecoder.decode( stream, storedProcedureContainer );
+        }
+        catch ( Exception de )
+        {
+            de.printStackTrace();
+        }
+
+        StoredProcedure spBean = ( ( StoredProcedureContainer ) storedProcedureContainer ).getStoredProcedure();
+        
+        return spBean;
+    }
+
+    
+    public String getOid()
+    {
+        return StoredProcedureRequest.EXTENSION_OID;
+    }
+
+
+    private static final Set<String> EXTENSION_OIDS;
+    
+    static
+    {
+        Set<String> s = new HashSet<String>();
+        s.add( StoredProcedureRequest.EXTENSION_OID );
+        s.add( StoredProcedureResponse.EXTENSION_OID );
+        EXTENSION_OIDS = Collections.unmodifiableSet( s );
+    }
+    
+    
+    public Set<String> getExtensionOids()
+    {
+        return EXTENSION_OIDS;
+    }
+
+    
+    public void setLdapProvider( LdapServer provider)
+    {
+    }
+}
diff --git a/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/extended/package-info.java b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/extended/package-info.java
new file mode 100644
index 0000000..e80f03c
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/extended/package-info.java
@@ -0,0 +1,28 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+/**
+ * Provides LDAP 'Extended' operations that implement
+ * {@link org.apache.directory.server.ldap.ExtendedOperationHandler}.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+package org.apache.directory.server.ldap.handlers.extended;
diff --git a/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/package-info.java b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/package-info.java
new file mode 100644
index 0000000..27f5930
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/package-info.java
@@ -0,0 +1,28 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+/**
+ * Provides LDAP message handlers that implement
+ * {@link org.apache.mina.handler.demux.MessageHandler}.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+package org.apache.directory.server.ldap.handlers;
diff --git a/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/ssl/LdapsInitializer.java b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/ssl/LdapsInitializer.java
new file mode 100644
index 0000000..49e6f9e
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/ssl/LdapsInitializer.java
@@ -0,0 +1,75 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.ldap.handlers.ssl;
+
+
+import java.security.KeyStore;
+import java.security.SecureRandom;
+import java.security.Security;
+
+import javax.naming.NamingException;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+
+import org.apache.mina.common.DefaultIoFilterChainBuilder;
+import org.apache.mina.common.IoFilterChainBuilder;
+import org.apache.mina.filter.SSLFilter;
+
+
+/**
+ * Loads the certificate file for LDAPS support and creates the appropriate
+ * MINA filter chain.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ *
+ */
+public class LdapsInitializer
+{
+    public static IoFilterChainBuilder init( KeyStore ks ) throws NamingException
+    {
+        SSLContext sslCtx;
+        try
+        {
+            // Set up key manager factory to use our key store
+            String algorithm = Security.getProperty( "ssl.KeyManagerFactory.algorithm" );
+            if ( algorithm == null )
+            {
+                algorithm = "SunX509";
+            }
+            KeyManagerFactory kmf = KeyManagerFactory.getInstance( algorithm );
+            kmf.init( ks, null );
+
+            // Initialize the SSLContext to work with our key managers.
+            sslCtx = SSLContext.getInstance( "TLS" );
+            sslCtx.init( kmf.getKeyManagers(), new TrustManager[]
+                { new ServerX509TrustManager() }, new SecureRandom() );
+        }
+        catch ( Exception e )
+        {
+            throw ( NamingException ) new NamingException( "Failed to create a SSL context." ).initCause( e );
+        }
+
+        DefaultIoFilterChainBuilder chain = new DefaultIoFilterChainBuilder();
+        chain.addLast( "SSL", new SSLFilter( sslCtx ) );
+        return chain;
+    }
+}
diff --git a/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/ssl/ServerX509TrustManager.java b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/ssl/ServerX509TrustManager.java
new file mode 100644
index 0000000..19a014c
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/ssl/ServerX509TrustManager.java
@@ -0,0 +1,59 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.ldap.handlers.ssl;
+
+
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+
+import javax.net.ssl.X509TrustManager;
+
+
+/**
+ * An {@link X509TrustManager} for LDAP server.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ServerX509TrustManager implements X509TrustManager
+{
+    public ServerX509TrustManager()
+    {
+    }
+
+
+    public void checkClientTrusted( X509Certificate[] arg0, String arg1 ) throws CertificateException
+    {
+        // We don't check clients at all right now.
+        // XXX: Do we need a client-side certificates?
+    }
+
+
+    public void checkServerTrusted( X509Certificate[] arg0, String arg1 ) throws CertificateException
+    {
+        // It is server-side trust manager, so we don't need to check the server itself.
+    }
+
+
+    public X509Certificate[] getAcceptedIssuers()
+    {
+        return null;
+    }
+}
diff --git a/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/ssl/package-info.java b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/ssl/package-info.java
new file mode 100644
index 0000000..2c8624b
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/ssl/package-info.java
@@ -0,0 +1,27 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+/**
+ * Contains support for SSL with the LDAP protocol (LDAPS).
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+package org.apache.directory.server.ldap.handlers.ssl;
diff --git a/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/package-info.java b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/package-info.java
new file mode 100644
index 0000000..8dcfec7
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/package-info.java
@@ -0,0 +1,28 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+/**
+ * Contains the entry point for the LDAP protocol provider and the
+ * session registry.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+package org.apache.directory.server.ldap;
diff --git a/old_trunk/protocol-ldap/src/site/site.xml b/old_trunk/protocol-ldap/src/site/site.xml
new file mode 100644
index 0000000..9ec7c16
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/site/site.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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.
+-->
+<!--
+  @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+-->
+<project name="${project.name}">
+    <bannerLeft>
+        <name>${project.name}</name>
+    </bannerLeft>
+    <publishDate position="navigation-bottom" format="dd-MM-yyyy HH:mm" />
+    <body>
+        <menu name="Parent">
+            <item name="Apache Directory ApacheDS" href="../index.html" />
+        </menu>
+        <menu ref="reports" />
+    </body>
+</project>
diff --git a/old_trunk/protocol-ldap/src/test/java/org/apache/directory/server/ldap/LdapServerSettingsTest.java b/old_trunk/protocol-ldap/src/test/java/org/apache/directory/server/ldap/LdapServerSettingsTest.java
new file mode 100644
index 0000000..eb12338
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/test/java/org/apache/directory/server/ldap/LdapServerSettingsTest.java
@@ -0,0 +1,87 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.ldap;
+
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import org.apache.directory.server.ldap.handlers.extended.StartTlsHandler;
+import org.apache.directory.server.ldap.handlers.bind.MechanismHandler;
+import org.apache.directory.server.ldap.handlers.bind.SimpleMechanismHandler;
+import org.apache.directory.shared.ldap.constants.SupportedSaslMechanisms;
+
+import javax.naming.NamingException;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.HashMap;
+
+
+/**
+ * Test to confirm correct behavoir for settings on LdapServer bean.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $$Rev$$
+ */
+public class LdapServerSettingsTest
+{
+    @Test
+    public void testAddExtendedOperationHandler() throws NamingException
+    {
+        LdapServer server = new LdapServer();
+        StartTlsHandler handler = new StartTlsHandler();
+        server.addExtendedOperationHandler( handler );
+        assertEquals( handler, server.getExtendedOperationHandler( handler.getOid() ) );
+        server.removeExtendedOperationHandler( handler.getOid() );
+        assertNull( server.getExtendedOperationHandler( handler.getOid() ) );
+    }
+
+
+    @Test
+    public void testSetExtendedOperationHandlers()
+    {
+        LdapServer server = new LdapServer();
+        StartTlsHandler handler = new StartTlsHandler();
+        List<ExtendedOperationHandler> handlers = new ArrayList<ExtendedOperationHandler>();
+        handlers.add( handler );
+        server.setExtendedOperationHandlers( handlers );
+        assertEquals( handler, server.getExtendedOperationHandler( handler.getOid() ) );
+        server.removeExtendedOperationHandler( handler.getOid() );
+        assertNull( server.getExtendedOperationHandler( handler.getOid() ) );
+    }
+
+
+    @Test
+    public void testSetSaslMechanismHandlers()
+    {
+        LdapServer server = new LdapServer();
+        Map<String, MechanismHandler> handlers = new HashMap<String,MechanismHandler>();
+        MechanismHandler handler = new SimpleMechanismHandler();
+        handlers.put( SupportedSaslMechanisms.PLAIN, handler );
+        server.setSaslMechanismHandlers( handlers );
+        assertEquals( handler, server.getMechanismHandler( SupportedSaslMechanisms.PLAIN ) );
+        assertTrue( server.getSupportedMechanisms().contains( SupportedSaslMechanisms.PLAIN ) );
+        server.removeSaslMechanismHandler( SupportedSaslMechanisms.PLAIN );
+        assertNull( server.getMechanismHandler( SupportedSaslMechanisms.PLAIN ) );
+    }
+}
diff --git a/old_trunk/protocol-ldap/src/test/java/org/apache/directory/server/ldap/SettingAlternativeHandlersTest.java b/old_trunk/protocol-ldap/src/test/java/org/apache/directory/server/ldap/SettingAlternativeHandlersTest.java
new file mode 100644
index 0000000..e1739a5
--- /dev/null
+++ b/old_trunk/protocol-ldap/src/test/java/org/apache/directory/server/ldap/SettingAlternativeHandlersTest.java
@@ -0,0 +1,190 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.ldap;
+
+
+import junit.framework.TestCase;
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.ldap.handlers.AbandonHandler;
+import org.apache.directory.server.ldap.handlers.AddHandler;
+import org.apache.directory.server.ldap.handlers.BindHandler;
+import org.apache.directory.server.ldap.handlers.CompareHandler;
+import org.apache.directory.server.ldap.handlers.DeleteHandler;
+import org.apache.directory.server.ldap.handlers.ModifyDnHandler;
+import org.apache.directory.server.ldap.handlers.ModifyHandler;
+import org.apache.directory.server.ldap.handlers.SearchHandler;
+import org.apache.directory.server.ldap.handlers.UnbindHandler;
+import org.apache.directory.shared.ldap.NotImplementedException;
+import org.apache.directory.shared.ldap.exception.LdapNamingException;
+import org.apache.directory.shared.ldap.message.AbandonRequest;
+import org.apache.directory.shared.ldap.message.AddRequest;
+import org.apache.directory.shared.ldap.message.BindRequest;
+import org.apache.directory.shared.ldap.message.CompareRequest;
+import org.apache.directory.shared.ldap.message.DeleteRequest;
+import org.apache.directory.shared.ldap.message.ModifyDnRequest;
+import org.apache.directory.shared.ldap.message.ModifyRequest;
+import org.apache.directory.shared.ldap.message.SearchRequest;
+import org.apache.directory.shared.ldap.message.UnbindRequest;
+import org.apache.mina.common.IoSession;
+
+
+/**
+ * This test is simply used to test that handlers can be set properly.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class SettingAlternativeHandlersTest extends TestCase
+{
+    LdapServer ldapServer;
+
+
+    public void setUp() throws Exception
+    {
+        ldapServer = new LdapServer();
+        
+        if ( getName().equals( "testAlternativeConfiguration" ) )
+        {
+            ldapServer.setAbandonHandler( new BogusAbandonHandler() );
+            ldapServer.setAddHandler( new BogusAddHandler() );
+            ldapServer.setBindHandler( new BogusBindHandler() );
+            ldapServer.setCompareHandler( new BogusCompareHandler() );
+            ldapServer.setDeleteHandler( new BogusDeleteHandler() );
+            ldapServer.setModifyDnHandler( new BogusModifyDnHandler() );
+            ldapServer.setModifyHandler( new BogusModifyHandler() );
+            ldapServer.setSearchHandler( new BogusSearchHandler() );
+            ldapServer.setUnbindHandler( new BogusUnbindHandler() );
+        }
+    }
+
+
+    /**
+     * Tests to make sure all the default handlers are kicking in properly with
+     * the right request type.
+     *
+     * @throws LdapNamingException if there are problems initializing the
+     * provider
+     */
+    public void testDefaultOperation() throws LdapNamingException
+    {
+        assertEquals( ldapServer.getName(), LdapServer.SERVICE_NAME );
+    }
+
+
+    /**
+     * Tests to make sure handlers for alternative configurations are kicking
+     * in properly with the right request type.
+     *
+     * @throws LdapNamingException if there are problems initializing the
+     * provider
+     */
+    public void testAlternativeConfiguration() throws LdapNamingException
+    {
+        assertEquals( ldapServer.getAbandonHandler().getClass(), BogusAbandonHandler.class  );
+        assertEquals( ldapServer.getAddHandler().getClass(), BogusAddHandler.class  );
+        assertEquals( ldapServer.getBindHandler().getClass(), BogusBindHandler.class  );
+        assertEquals( ldapServer.getCompareHandler().getClass(), BogusCompareHandler.class  );
+        assertEquals( ldapServer.getDeleteHandler().getClass(), BogusDeleteHandler.class  );
+        assertEquals( ldapServer.getModifyDnHandler().getClass(), BogusModifyDnHandler.class  );
+        assertEquals( ldapServer.getModifyHandler().getClass(), BogusModifyHandler.class  );
+        assertEquals( ldapServer.getSearchHandler().getClass(), BogusSearchHandler.class  );
+        assertEquals( ldapServer.getUnbindHandler().getClass(), BogusUnbindHandler.class  );
+        assertEquals( ldapServer.getName(), LdapServer.SERVICE_NAME );
+    }
+
+    
+    public static class BogusAbandonHandler extends AbandonHandler
+    {
+        public void abandonMessageReceived( IoSession session, AbandonRequest request )
+        {
+            throw new NotImplementedException( "handler not implemented!" );
+        }
+    }
+
+    
+    public static class BogusUnbindHandler extends UnbindHandler
+    {
+        public void unbindMessageReceived( IoSession session, UnbindRequest request )
+        {
+            throw new NotImplementedException( "handler not implemented!" );
+        }
+    }
+
+    public static class BogusAddHandler extends AddHandler
+    {
+        public void addMessageReceived( IoSession session, AddRequest request )
+        {
+            throw new NotImplementedException( "handler not implemented!" );
+        }
+    }
+
+    public static class BogusBindHandler extends BindHandler
+    {
+        public void setDirectoryService( DirectoryService directoryService )
+        {
+        }
+
+
+        public void bindMessageReceived( IoSession session, BindRequest request )
+        {
+            throw new NotImplementedException( "handler not implemented!" );
+        }
+    }
+
+    public static class BogusCompareHandler extends CompareHandler
+    {
+        public void compareMessageReceived( IoSession session, CompareRequest request )
+        {
+            throw new NotImplementedException( "handler not implemented!" );
+        }
+    }
+
+    public static class BogusDeleteHandler extends  DeleteHandler
+    {
+        public void deleteMessageReceived( IoSession session, DeleteRequest request )
+        {
+            throw new NotImplementedException( "handler not implemented!" );
+        }
+    }
+
+    public static class BogusModifyDnHandler extends  ModifyDnHandler
+    {
+        public void modifyDnMessageReceived( IoSession session, ModifyDnRequest request )
+        {
+            throw new NotImplementedException( "handler not implemented!" );
+        }
+    }
+
+    public static class BogusModifyHandler extends ModifyHandler
+    {
+        public void modifyMessageReceived( IoSession session, ModifyRequest request )
+        {
+            throw new NotImplementedException( "handler not implemented!" );
+        }
+    }
+
+    public static class BogusSearchHandler extends SearchHandler
+    {
+        public void searchMessageReceived( IoSession session, SearchRequest request )
+        {
+            throw new NotImplementedException( "handler not implemented!" );
+        }
+    }
+}
diff --git a/old_trunk/protocol-ntp/pom.xml b/old_trunk/protocol-ntp/pom.xml
new file mode 100644
index 0000000..34bac13
--- /dev/null
+++ b/old_trunk/protocol-ntp/pom.xml
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you 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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.directory.server</groupId>
+    <artifactId>apacheds-parent</artifactId>
+    <version>1.5.4-SNAPSHOT</version>
+  </parent>
+  <artifactId>apacheds-protocol-ntp</artifactId>
+  <name>ApacheDS Protocol Ntp</name>
+
+  <description>
+    The NTP protocol provider for ApacheDS
+  </description>
+
+  <packaging>jar</packaging>  
+
+  <dependencies>
+
+    <dependency>
+      <groupId>org.apache.mina</groupId>
+      <artifactId>mina-core</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-protocol-shared</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
+    
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-server-unit</artifactId>
+      <version>${pom.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-bootstrap-partition</artifactId>
+      <version>${pom.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>commons-net</groupId>
+      <artifactId>commons-net</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-source-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>attach-sources</id>
+            <phase>verify</phase>
+            <goals>
+              <goal>jar</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+
+</project>
+
diff --git a/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/NtpException.java b/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/NtpException.java
new file mode 100644
index 0000000..86ac004
--- /dev/null
+++ b/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/NtpException.java
@@ -0,0 +1,74 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.ntp;
+
+
+/**
+ * The root of the NTP exception hierarchy.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class NtpException extends Exception
+{
+    private static final long serialVersionUID = -225862469671550203L;
+
+
+    /**
+     * Creates an NtpException.
+     */
+    public NtpException()
+    {
+        super();
+    }
+
+
+    /**
+     * Creates an NtpException with a message and cause.
+     * 
+     * @param message the message
+     * @param cause the cause
+     */
+    public NtpException(String message, Throwable cause)
+    {
+        super( message, cause );
+    }
+
+
+    /**
+     * Creates an NtpException with a message.
+     * 
+     * @param message the message
+     */
+    public NtpException(String message)
+    {
+        super( message );
+    }
+
+
+    /**
+     * @param cause the cause
+     */
+    public NtpException(Throwable cause)
+    {
+        super( cause );
+    }
+}
diff --git a/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/NtpServer.java b/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/NtpServer.java
new file mode 100644
index 0000000..7370328
--- /dev/null
+++ b/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/NtpServer.java
@@ -0,0 +1,102 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.ntp;
+
+
+import org.apache.directory.server.ntp.protocol.NtpProtocolHandler;
+import org.apache.directory.server.protocol.shared.AbstractProtocolService;
+import org.apache.mina.transport.socket.nio.DatagramAcceptorConfig;
+import org.apache.mina.transport.socket.nio.SocketAcceptorConfig;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+
+
+/**
+ * Contains the configuration parameters for the NTP protocol provider.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ * @org.apache.xbean.XBean
+ */
+public class NtpServer extends AbstractProtocolService
+{
+    /**
+     * The default IP port.
+     */
+    private static final int IP_PORT_DEFAULT = 123;
+
+    /**
+     * The default service pid.
+     */
+    private static final String SERVICE_PID_DEFAULT = "org.apache.directory.server.ntp";
+
+    /**
+     * The default service name.
+     */
+    private static final String SERVICE_NAME_DEFAULT = "ApacheDS NTP Service";
+
+
+    /**
+     * Creates a new instance of NtpConfiguration.
+     */
+    public NtpServer()
+    {
+        super.setIpPort( IP_PORT_DEFAULT );
+        super.setServiceId( SERVICE_PID_DEFAULT );
+        super.setServiceName( SERVICE_NAME_DEFAULT );
+    }
+
+    
+    /**
+     * @throws IOException if there are issues binding
+     */
+    public void start() throws IOException
+    {
+        //If appropriate, the udp and tcp servers could be enabled with boolean flags.
+        if ( getDatagramAcceptor() != null )
+        {
+            DatagramAcceptorConfig udpConfig = new DatagramAcceptorConfig();
+            getDatagramAcceptor().bind( new InetSocketAddress( getIpPort() ), new NtpProtocolHandler(), udpConfig );
+        }
+
+        if ( getSocketAcceptor() != null )
+        {
+            SocketAcceptorConfig tcpConfig = new SocketAcceptorConfig();
+            tcpConfig.setDisconnectOnUnbind( false );
+            tcpConfig.setReuseAddress( true );
+            getSocketAcceptor().bind( new InetSocketAddress( getIpPort() ), new NtpProtocolHandler(), tcpConfig );
+        }
+    }
+
+    
+    public void stop()
+    {
+        if ( getDatagramAcceptor() != null )
+        {
+            getDatagramAcceptor().unbind( new InetSocketAddress( getIpPort() ));
+        }
+        
+        if ( getSocketAcceptor() != null )
+        {
+            getSocketAcceptor().unbind( new InetSocketAddress( getIpPort() ));
+        }
+    }
+}
diff --git a/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/NtpService.java b/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/NtpService.java
new file mode 100644
index 0000000..451e211
--- /dev/null
+++ b/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/NtpService.java
@@ -0,0 +1,42 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.ntp;
+
+
+import org.apache.directory.server.ntp.messages.NtpMessage;
+
+
+/**
+ * NTP Protocol (RFC 2030)
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public interface NtpService
+{
+    /**
+     * Returns an {@link NtpMessage} reply for the given {@link NtpMessage} request.
+     *
+     * @param request
+     * @return The {@link NtpMessage} reply.
+     */
+    public NtpMessage getReplyFor( NtpMessage request );
+}
diff --git a/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/io/NtpMessageDecoder.java b/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/io/NtpMessageDecoder.java
new file mode 100644
index 0000000..b9b344b
--- /dev/null
+++ b/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/io/NtpMessageDecoder.java
@@ -0,0 +1,130 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.ntp.io;
+
+
+import java.nio.ByteBuffer;
+
+import org.apache.directory.server.ntp.messages.LeapIndicatorType;
+import org.apache.directory.server.ntp.messages.ModeType;
+import org.apache.directory.server.ntp.messages.NtpMessage;
+import org.apache.directory.server.ntp.messages.NtpMessageModifier;
+import org.apache.directory.server.ntp.messages.NtpTimeStamp;
+import org.apache.directory.server.ntp.messages.ReferenceIdentifier;
+import org.apache.directory.server.ntp.messages.StratumType;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class NtpMessageDecoder
+{
+    /**
+     * Decodes the {@link ByteBuffer} into an {@link NtpMessage}.
+     *
+     * @param request
+     * @return The {@link NtpMessage}.
+     */
+    public NtpMessage decode( ByteBuffer request )
+    {
+        NtpMessageModifier modifier = new NtpMessageModifier();
+
+        byte header = request.get();
+        modifier.setLeapIndicator( parseLeapIndicator( header ) );
+        modifier.setVersionNumber( parseVersionNumber( header ) );
+        modifier.setMode( parseMode( header ) );
+        modifier.setStratum( parseStratum( request ) );
+        modifier.setPollInterval( parsePollInterval( request ) );
+        modifier.setPrecision( parsePrecision( request ) );
+        modifier.setRootDelay( parseRootDelay( request ) );
+        modifier.setRootDispersion( parseRootDispersion( request ) );
+        modifier.setReferenceIdentifier( parseReferenceIdentifier( request ) );
+        modifier.setReferenceTimestamp( new NtpTimeStamp( request ) );
+        modifier.setOriginateTimestamp( new NtpTimeStamp( request ) );
+
+        byte[] unneededBytes = new byte[8];
+        request.get( unneededBytes );
+
+        modifier.setReceiveTimestamp( new NtpTimeStamp() );
+        modifier.setTransmitTimestamp( new NtpTimeStamp( request ) );
+
+        return modifier.getNtpMessage();
+    }
+
+
+    private LeapIndicatorType parseLeapIndicator( byte header )
+    {
+        return LeapIndicatorType.getTypeByOrdinal( ( header & 0xc0 ) >>> 6 );
+    }
+
+
+    private int parseVersionNumber( byte header )
+    {
+        return ( header & 0x38 ) >>> 3;
+    }
+
+
+    private ModeType parseMode( byte header )
+    {
+        return ModeType.getTypeByOrdinal( header & 0x07 );
+    }
+
+
+    private StratumType parseStratum( ByteBuffer request )
+    {
+        return StratumType.getTypeByOrdinal( request.get() );
+    }
+
+
+    private byte parsePollInterval( ByteBuffer bytes )
+    {
+        return ( byte ) Math.round( Math.pow( 2, bytes.get() ) );
+    }
+
+
+    private byte parsePrecision( ByteBuffer bytes )
+    {
+        return ( byte ) ( 1000 * Math.pow( 2, bytes.get() ) );
+    }
+
+
+    private ReferenceIdentifier parseReferenceIdentifier( ByteBuffer request )
+    {
+        byte[] nextFourBytes = new byte[4];
+        request.get( nextFourBytes );
+        return ReferenceIdentifier.getTypeByName( new String( nextFourBytes ) );
+    }
+
+
+    private int parseRootDelay( ByteBuffer bytes )
+    {
+        int temp = 256 * ( 256 * ( 256 * bytes.get() + bytes.get() ) + bytes.get() ) + bytes.get();
+        return 1000 * ( temp / 0x10000 );
+    }
+
+
+    private int parseRootDispersion( ByteBuffer bytes )
+    {
+        int temp = 256 * ( 256 * ( 256 * bytes.get() + bytes.get() ) + bytes.get() ) + bytes.get();
+        return 1000 * ( temp / 0x10000 );
+    }
+}
diff --git a/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/io/NtpMessageEncoder.java b/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/io/NtpMessageEncoder.java
new file mode 100644
index 0000000..22d0024
--- /dev/null
+++ b/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/io/NtpMessageEncoder.java
@@ -0,0 +1,98 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.ntp.io;
+
+
+import java.nio.ByteBuffer;
+
+import org.apache.directory.server.ntp.messages.LeapIndicatorType;
+import org.apache.directory.server.ntp.messages.ModeType;
+import org.apache.directory.server.ntp.messages.NtpMessage;
+import org.apache.directory.server.ntp.messages.ReferenceIdentifier;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class NtpMessageEncoder
+{
+    /**
+     * Encodes the {@link NtpMessage} into the {@link ByteBuffer}.
+     *
+     * @param byteBuffer
+     * @param message
+     */
+    public void encode( ByteBuffer byteBuffer, NtpMessage message )
+    {
+        byte header = 0x00;
+        header = encodeLeapIndicator( message.getLeapIndicator(), header );
+        header = encodeVersionNumber( message.getVersionNumber(), header );
+        header = encodeMode( message.getMode(), header );
+        byteBuffer.put( header );
+
+        byteBuffer.put( ( byte ) ( message.getStratum().getOrdinal() & 0xFF ) );
+        byteBuffer.put( ( byte ) ( message.getPollInterval() & 0xFF ) );
+        byteBuffer.put( ( byte ) ( message.getPrecision() & 0xFF ) );
+
+        byteBuffer.putInt( message.getRootDelay() );
+        byteBuffer.putInt( message.getRootDispersion() );
+
+        encodeReferenceIdentifier( message.getReferenceIdentifier(), byteBuffer );
+
+        message.getReferenceTimestamp().writeTo( byteBuffer );
+        message.getOriginateTimestamp().writeTo( byteBuffer );
+        message.getReceiveTimestamp().writeTo( byteBuffer );
+        message.getTransmitTimestamp().writeTo( byteBuffer );
+    }
+
+
+    private byte encodeLeapIndicator( LeapIndicatorType leapIndicator, byte header )
+    {
+        byte twoBits = ( byte ) ( leapIndicator.getOrdinal() & 0x03 );
+        return ( byte ) ( ( twoBits << 6 ) | header );
+    }
+
+
+    private byte encodeVersionNumber( int versionNumber, byte header )
+    {
+        byte threeBits = ( byte ) ( versionNumber & 0x07 );
+        return ( byte ) ( ( threeBits << 3 ) | header );
+    }
+
+
+    private byte encodeMode( ModeType mode, byte header )
+    {
+        byte threeBits = ( byte ) ( mode.getOrdinal() & 0x07 );
+        return ( byte ) ( threeBits | header );
+    }
+
+
+    private void encodeReferenceIdentifier( ReferenceIdentifier identifier, ByteBuffer byteBuffer )
+    {
+        char[] characters = identifier.getCode().toCharArray();
+
+        for ( int ii = 0; ii < characters.length; ii++ )
+        {
+            byteBuffer.put( ( byte ) characters[ii] );
+        }
+    }
+}
diff --git a/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/io/package-info.java b/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/io/package-info.java
new file mode 100644
index 0000000..48aa378
--- /dev/null
+++ b/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/io/package-info.java
@@ -0,0 +1,28 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+/**
+ * Provides the encoder and decoder for NTP message conversions.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+
+package org.apache.directory.server.ntp.io;
diff --git a/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/messages/LeapIndicatorType.java b/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/messages/LeapIndicatorType.java
new file mode 100644
index 0000000..de95e1c
--- /dev/null
+++ b/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/messages/LeapIndicatorType.java
@@ -0,0 +1,142 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.ntp.messages;
+
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+
+/**
+ * Leap Indicator (LI): This is a two-bit code warning of an impending
+ * leap second to be inserted/deleted in the last minute of the current
+ * day, with bit 0 and bit 1, respectively, coded as follows:
+ *
+ *    LI       Value     Meaning
+ *    -------------------------------------------------------
+ *    00       0         no warning
+ *    01       1         last minute has 61 seconds
+ *    10       2         last minute has 59 seconds)
+ *    11       3         alarm condition (clock not synchronized)
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public final class LeapIndicatorType implements Comparable<LeapIndicatorType>
+{
+    /**
+     * Constant for the "No leap second warning" leap indicator type.
+     */
+    public static final LeapIndicatorType NO_WARNING = new LeapIndicatorType( 0, "No leap second warning." );
+
+    /**
+     * Constant for the "Last minute has 61 seconds" leap indicator type.
+     */
+    public static final LeapIndicatorType POSITIVE_LEAP_SECOND = new LeapIndicatorType( 1,
+        "Last minute has 61 seconds." );
+
+    /**
+     * Constant for the "Last minute has 59 seconds" leap indicator type.
+     */
+    public static final LeapIndicatorType NEGATIVE_LEAP_SECOND = new LeapIndicatorType( 2,
+        "Last minute has 59 seconds." );
+
+    /**
+     * Constant for the "Alarm condition (clock not synchronized)" leap indicator type.
+     */
+    public static final LeapIndicatorType ALARM_CONDITION = new LeapIndicatorType( 3,
+        "Alarm condition (clock not synchronized)." );
+
+    /**
+     * Array for building a List of VALUES.
+     */
+    private static final LeapIndicatorType[] values =
+        { NO_WARNING, POSITIVE_LEAP_SECOND, NEGATIVE_LEAP_SECOND, ALARM_CONDITION };
+
+    /**
+     * A list of all the leap indicator type constants.
+     */
+    public static final List<LeapIndicatorType> VALUES = Collections.unmodifiableList( Arrays.asList( values ) );
+
+    /**
+     * The name of the leap indicator type.
+     */
+    private final String name;
+
+    /**
+     * The value/code for the leap indicator type.
+     */
+    private final int ordinal;
+
+
+    /**
+     * Private constructor prevents construction outside of this class.
+     */
+    private LeapIndicatorType( int ordinal, String name )
+    {
+        this.ordinal = ordinal;
+        this.name = name;
+    }
+
+
+    /**
+     * Returns the leap indicator type when specified by its ordinal.
+     *
+     * @param type
+     * @return The leap indicator type.
+     */
+    public static LeapIndicatorType getTypeByOrdinal( int type )
+    {
+        for ( int ii = 0; ii < values.length; ii++ )
+        {
+            if ( values[ii].ordinal == type )
+            {
+                return values[ii];
+            }
+        }
+
+        return NO_WARNING;
+    }
+
+
+    /**
+     * Returns the number associated with this leap indicator type.
+     *
+     * @return The leap indicator type ordinal.
+     */
+    public int getOrdinal()
+    {
+        return ordinal;
+    }
+
+
+    public int compareTo( LeapIndicatorType that )
+    {
+        return ordinal - that.ordinal;
+    }
+
+
+    public String toString()
+    {
+        return name;
+    }
+}
diff --git a/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/messages/ModeType.java b/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/messages/ModeType.java
new file mode 100644
index 0000000..fa78690
--- /dev/null
+++ b/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/messages/ModeType.java
@@ -0,0 +1,162 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.ntp.messages;
+
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+
+/**
+ * Mode: This is a three-bit integer indicating the mode, with values
+ * defined as follows:
+ *
+ *    Mode     Meaning
+ *    ------------------------------------
+ *    0        reserved
+ *    1        symmetric active
+ *    2        symmetric passive
+ *    3        client
+ *    4        server
+ *    5        broadcast
+ *    6        reserved for NTP control message
+ *    7        reserved for private use
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public final class ModeType implements Comparable<ModeType>
+{
+    /**
+     * Constant for the "Reserved mode" mode type.
+     */
+    public static final ModeType RESERVED = new ModeType( 0, "Reserved mode." );
+
+    /**
+     * Constant for the "Symmetric active mode" mode type.
+     */
+    public static final ModeType SYMMETRIC_ACTIVE = new ModeType( 1, "Symmetric active mode." );
+
+    /**
+     * Constant for the "Symmetric passive mode" mode type.
+     */
+    public static final ModeType RESERVED_PASSIVE = new ModeType( 2, "Symmetric passive mode." );
+
+    /**
+     * Constant for the "Client mode" mode type.
+     */
+    public static final ModeType CLIENT = new ModeType( 3, "Client mode." );
+
+    /**
+     * Constant for the "Server mode" mode type.
+     */
+    public static final ModeType SERVER = new ModeType( 4, "Server mode." );
+
+    /**
+     * Constant for the "Broadcast mode" mode type.
+     */
+    public static final ModeType BROADCAST = new ModeType( 5, "Broadcast mode." );
+
+    /**
+     * Constant for the "Reserved for NTP control message" mode type.
+     */
+    public static final ModeType RESERVED_FOR_NTP_CONTROL = new ModeType( 6, "Reserved for NTP control message." );
+
+    /**
+     * Constant for the "Reserved for private use" mode type.
+     */
+    public static final ModeType RESERVED_FOR_PRIVATE_USE = new ModeType( 7, "Reserved for private use." );
+
+    /**
+     * Array for building a List of VALUES.
+     */
+    private static final ModeType[] values =
+        { RESERVED, SYMMETRIC_ACTIVE, RESERVED_PASSIVE, CLIENT, SERVER, BROADCAST, RESERVED_FOR_NTP_CONTROL,
+            RESERVED_FOR_PRIVATE_USE };
+
+    /**
+     * A list of all the mode type constants.
+     */
+    public static final List<ModeType> VALUES = Collections.unmodifiableList( Arrays.asList( values ) );
+
+    /**
+     * The name of the mode type.
+     */
+    private final String name;
+
+    /**
+     * The value/code for the mode type.
+     */
+    private final int ordinal;
+
+
+    /**
+     * Private constructor prevents construction outside of this class.
+     */
+    private ModeType( int ordinal, String name )
+    {
+        this.ordinal = ordinal;
+        this.name = name;
+    }
+
+
+    /**
+     * Returns the mode type when specified by its ordinal.
+     *
+     * @param type
+     * @return The mode type.
+     */
+    public static ModeType getTypeByOrdinal( int type )
+    {
+        for ( int ii = 0; ii < values.length; ii++ )
+        {
+            if ( values[ii].ordinal == type )
+            {
+                return values[ii];
+            }
+        }
+        return SERVER;
+    }
+
+
+    /**
+     * Returns the number associated with this mode type.
+     *
+     * @return The mode type ordinal.
+     */
+    public int getOrdinal()
+    {
+        return ordinal;
+    }
+
+
+    public int compareTo( ModeType that )
+    {
+        return ordinal - that.ordinal;
+    }
+
+
+    public String toString()
+    {
+        return name;
+    }
+}
diff --git a/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/messages/NtpMessage.java b/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/messages/NtpMessage.java
new file mode 100755
index 0000000..b823364
--- /dev/null
+++ b/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/messages/NtpMessage.java
@@ -0,0 +1,200 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.ntp.messages;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class NtpMessage
+{
+    private LeapIndicatorType leapIndicator;
+    private int versionNumber;
+    private ModeType mode;
+    private StratumType stratumType;
+    private byte pollInterval;
+    private byte precision;
+    private int rootDelay;
+    private int rootDispersion;
+
+    private ReferenceIdentifier referenceIdentifier;
+
+    private NtpTimeStamp referenceTimestamp;
+    private NtpTimeStamp originateTimestamp;
+    private NtpTimeStamp receiveTimestamp;
+    private NtpTimeStamp transmitTimestamp;
+
+
+    /**
+     * Creates a new instance of NtpMessage.
+     *
+     * @param leapIndicator
+     * @param versionNumber
+     * @param mode
+     * @param stratumType
+     * @param pollInterval
+     * @param precision
+     * @param rootDelay
+     * @param rootDispersion
+     * @param referenceIdentifier
+     * @param referenceTimestamp
+     * @param originateTimestamp
+     * @param receiveTimestamp
+     * @param transmitTimestamp
+     */
+    public NtpMessage( LeapIndicatorType leapIndicator, int versionNumber, ModeType mode, StratumType stratumType,
+        byte pollInterval, byte precision, int rootDelay, int rootDispersion, ReferenceIdentifier referenceIdentifier,
+        NtpTimeStamp referenceTimestamp, NtpTimeStamp originateTimestamp, NtpTimeStamp receiveTimestamp,
+        NtpTimeStamp transmitTimestamp )
+    {
+        this.leapIndicator = leapIndicator;
+        this.versionNumber = versionNumber;
+        this.mode = mode;
+        this.stratumType = stratumType;
+        this.pollInterval = pollInterval;
+        this.precision = precision;
+        this.rootDelay = rootDelay;
+        this.rootDispersion = rootDispersion;
+        this.referenceIdentifier = referenceIdentifier;
+        this.referenceTimestamp = referenceTimestamp;
+        this.originateTimestamp = originateTimestamp;
+        this.receiveTimestamp = receiveTimestamp;
+        this.transmitTimestamp = transmitTimestamp;
+    }
+
+
+    /**
+     * @return Returns the Leap Indicator.
+     */
+    public LeapIndicatorType getLeapIndicator()
+    {
+        return leapIndicator;
+    }
+
+
+    /**
+     * @return Returns the Mode.
+     */
+    public ModeType getMode()
+    {
+        return mode;
+    }
+
+
+    /**
+     * @return Returns the Originate Timestamp.
+     */
+    public NtpTimeStamp getOriginateTimestamp()
+    {
+        return originateTimestamp;
+    }
+
+
+    /**
+     * @return Returns the Poll Interval.
+     */
+    public byte getPollInterval()
+    {
+        return pollInterval;
+    }
+
+
+    /**
+     * @return Returns the Precision.
+     */
+    public byte getPrecision()
+    {
+        return precision;
+    }
+
+
+    /**
+     * @return Returns the Receive Timestamp.
+     */
+    public NtpTimeStamp getReceiveTimestamp()
+    {
+        return receiveTimestamp;
+    }
+
+
+    /**
+     * @return Returns the Reference Identifier.
+     */
+    public ReferenceIdentifier getReferenceIdentifier()
+    {
+        return referenceIdentifier;
+    }
+
+
+    /**
+     * @return Returns the Reference Timestamp.
+     */
+    public NtpTimeStamp getReferenceTimestamp()
+    {
+        return referenceTimestamp;
+    }
+
+
+    /**
+     * @return Returns the Root Delay.
+     */
+    public int getRootDelay()
+    {
+        return rootDelay;
+    }
+
+
+    /**
+     * @return Returns the Root Dispersion.
+     */
+    public int getRootDispersion()
+    {
+        return rootDispersion;
+    }
+
+
+    /**
+     * @return Returns the Stratum.
+     */
+    public StratumType getStratum()
+    {
+        return stratumType;
+    }
+
+
+    /**
+     * @return Returns the Transmit Timestamp.
+     */
+    public NtpTimeStamp getTransmitTimestamp()
+    {
+        return transmitTimestamp;
+    }
+
+
+    /**
+     * @return Returns the Version Number.
+     */
+    public int getVersionNumber()
+    {
+        return versionNumber;
+    }
+}
diff --git a/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/messages/NtpMessageModifier.java b/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/messages/NtpMessageModifier.java
new file mode 100644
index 0000000..29529a4
--- /dev/null
+++ b/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/messages/NtpMessageModifier.java
@@ -0,0 +1,175 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.ntp.messages;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class NtpMessageModifier
+{
+    private LeapIndicatorType leapIndicator;
+    private int versionNumber;
+    private ModeType mode;
+    private StratumType stratumType;
+    private byte pollInterval;
+    private byte precision;
+    private int rootDelay;
+    private int rootDispersion;
+
+    private ReferenceIdentifier referenceIdentifier;
+
+    private NtpTimeStamp referenceTimestamp;
+    private NtpTimeStamp originateTimestamp;
+    private NtpTimeStamp receiveTimestamp;
+    private NtpTimeStamp transmitTimestamp;
+
+
+    /**
+     * Returns the built {@link NtpMessage}.
+     *
+     * @return The {@link NtpMessage}.
+     */
+    public NtpMessage getNtpMessage()
+    {
+        return new NtpMessage( leapIndicator, versionNumber, mode, stratumType, pollInterval, precision, rootDelay,
+            rootDispersion, referenceIdentifier, referenceTimestamp, originateTimestamp, receiveTimestamp,
+            transmitTimestamp );
+    }
+
+
+    /**
+     * @param leapIndicator The Leap Indicator to set.
+     */
+    public void setLeapIndicator( LeapIndicatorType leapIndicator )
+    {
+        this.leapIndicator = leapIndicator;
+    }
+
+
+    /**
+     * @param mode The Mode to set.
+     */
+    public void setMode( ModeType mode )
+    {
+        this.mode = mode;
+    }
+
+
+    /**
+     * @param originateTimestamp The Originate Timestamp to set.
+     */
+    public void setOriginateTimestamp( NtpTimeStamp originateTimestamp )
+    {
+        this.originateTimestamp = originateTimestamp;
+    }
+
+
+    /**
+     * @param pollInterval The Poll Interval to set.
+     */
+    public void setPollInterval( byte pollInterval )
+    {
+        this.pollInterval = pollInterval;
+    }
+
+
+    /**
+     * @param precision The Precision to set.
+     */
+    public void setPrecision( byte precision )
+    {
+        this.precision = precision;
+    }
+
+
+    /**
+     * @param receiveTimestamp The Receive Timestamp to set.
+     */
+    public void setReceiveTimestamp( NtpTimeStamp receiveTimestamp )
+    {
+        this.receiveTimestamp = receiveTimestamp;
+    }
+
+
+    /**
+     * @param referenceIdentifier The Reference Identifier to set.
+     */
+    public void setReferenceIdentifier( ReferenceIdentifier referenceIdentifier )
+    {
+        this.referenceIdentifier = referenceIdentifier;
+    }
+
+
+    /**
+     * @param referenceTimestamp The Reference Timestamp to set.
+     */
+    public void setReferenceTimestamp( NtpTimeStamp referenceTimestamp )
+    {
+        this.referenceTimestamp = referenceTimestamp;
+    }
+
+
+    /**
+     * @param rootDelay The Root Delay to set.
+     */
+    public void setRootDelay( int rootDelay )
+    {
+        this.rootDelay = rootDelay;
+    }
+
+
+    /**
+     * @param rootDispersion The Root Dispersion to set.
+     */
+    public void setRootDispersion( int rootDispersion )
+    {
+        this.rootDispersion = rootDispersion;
+    }
+
+
+    /**
+     * @param stratumType The Stratum to set.
+     */
+    public void setStratum( StratumType stratumType )
+    {
+        this.stratumType = stratumType;
+    }
+
+
+    /**
+     * @param transmitTimestamp The Transmit Timestamp to set.
+     */
+    public void setTransmitTimestamp( NtpTimeStamp transmitTimestamp )
+    {
+        this.transmitTimestamp = transmitTimestamp;
+    }
+
+
+    /**
+     * @param versionNumber The Version Number to set.
+     */
+    public void setVersionNumber( int versionNumber )
+    {
+        this.versionNumber = versionNumber;
+    }
+}
diff --git a/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/messages/NtpTimeStamp.java b/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/messages/NtpTimeStamp.java
new file mode 100755
index 0000000..03db913
--- /dev/null
+++ b/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/messages/NtpTimeStamp.java
@@ -0,0 +1,162 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.ntp.messages;
+
+
+import java.nio.ByteBuffer;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.TimeZone;
+
+
+/**
+ * NTP timestamps are represented as a 64-bit unsigned fixed-point number,
+ * in seconds relative to 0h on 1 January 1900. The integer part is in the
+ * first 32 bits and the fraction part in the last 32 bits. In the fraction
+ * part, the non-significant low order can be set to 0.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class NtpTimeStamp
+{
+    /**
+     * The number of milliseconds difference between the Java epoch and
+     * the NTP epoch ( January 1, 1900, 00:00:00 GMT ).
+     */
+    private static final long NTP_EPOCH_DIFFERENCE = -2208988800000L;
+
+    private static final TimeZone UTC_TIME_ZONE = TimeZone.getTimeZone( "UTC" );
+    private static final SimpleDateFormat dateFormat = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss.SSS z" );
+
+    static
+    {
+        dateFormat.setTimeZone( UTC_TIME_ZONE );
+    }
+
+    private long seconds = 0;
+    private long fraction = 0;
+
+
+    /**
+     * Creates a new instance of NtpTimeStamp that represents the time "right now."
+     */
+    public NtpTimeStamp()
+    {
+        this( new Date() );
+    }
+
+
+    /**
+     * Creates a new instance of NtpTimeStamp that represents the given {@link Date}.
+     *
+     * @param date
+     */
+    public NtpTimeStamp( Date date )
+    {
+        long msSinceStartOfNtpEpoch = date.getTime() - NTP_EPOCH_DIFFERENCE;
+
+        seconds = msSinceStartOfNtpEpoch / 1000;
+        fraction = ( ( msSinceStartOfNtpEpoch % 1000 ) * 0x100000000L ) / 1000;
+    }
+
+
+    /**
+     * Creates a new instance of NtpTimeStamp from encoded data in a {@link ByteBuffer}.
+     *
+     * @param data
+     */
+    public NtpTimeStamp( ByteBuffer data )
+    {
+        for ( int ii = 0; ii < 4; ii++ )
+        {
+            seconds = 256 * seconds + makePositive( data.get() );
+        }
+
+        for ( int ii = 4; ii < 8; ii++ )
+        {
+            fraction = 256 * fraction + makePositive( data.get() );
+        }
+    }
+
+
+    /**
+     * Writes this {@link NtpTimeStamp} to the given {@link ByteBuffer}.
+     *
+     * @param buffer
+     */
+    public void writeTo( ByteBuffer buffer )
+    {
+        byte[] bytes = new byte[8];
+
+        long temp = seconds;
+        for ( int ii = 3; ii >= 0; ii-- )
+        {
+            bytes[ii] = ( byte ) ( temp % 256 );
+            temp = temp / 256;
+        }
+
+        temp = fraction;
+        for ( int ii = 7; ii >= 4; ii-- )
+        {
+            bytes[ii] = ( byte ) ( temp % 256 );
+            temp = temp / 256;
+        }
+
+        buffer.put( bytes );
+    }
+
+
+    public String toString()
+    {
+        long msSinceStartOfNtpEpoch = seconds * 1000 + ( fraction * 1000 ) / 0x100000000L;
+        Date date = new Date( msSinceStartOfNtpEpoch + NTP_EPOCH_DIFFERENCE );
+
+        synchronized ( dateFormat )
+        {
+            return "org.apache.ntp.message.NtpTimeStamp[ date = " + dateFormat.format( date ) + " ]";
+        }
+    }
+
+
+    public boolean equals( Object o )
+    {
+        if ( this == o )
+        {
+            return true;
+        }
+
+        if ( !( o instanceof NtpTimeStamp ) )
+        {
+            return false;
+        }
+
+        NtpTimeStamp that = ( NtpTimeStamp ) o;
+        return ( this.seconds == that.seconds ) && ( this.fraction == that.fraction );
+    }
+
+
+    private int makePositive( byte b )
+    {
+        int byteAsInt = b;
+        return ( byteAsInt < 0 ) ? 256 + byteAsInt : byteAsInt;
+    }
+}
diff --git a/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/messages/ReferenceIdentifier.java b/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/messages/ReferenceIdentifier.java
new file mode 100644
index 0000000..cb8b990
--- /dev/null
+++ b/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/messages/ReferenceIdentifier.java
@@ -0,0 +1,255 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.ntp.messages;
+
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+
+/**
+ * Reference Identifier: This is a 32-bit bitstring identifying the
+ * particular reference source. In the case of NTP Version 3 or Version
+ * 4 stratum-0 (unspecified) or stratum-1 (primary) servers, this is a
+ * four-character ASCII string, left justified and zero padded to 32
+ * bits. In NTP Version 3 secondary servers, this is the 32-bit IPv4
+ * address of the reference source. In NTP Version 4 secondary servers,
+ * this is the low order 32 bits of the latest transmit timestamp of the
+ * reference source. NTP primary (stratum 1) servers should set this
+ * field to a code identifying the external reference source according
+ * to the following list. If the external reference is one of those
+ * listed, the associated code should be used. Codes for sources not
+ * listed can be contrived as appropriate.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ReferenceIdentifier implements Comparable<ReferenceIdentifier>
+{
+    /**
+     * Constant for the "INIT" reference identifier type.
+     */
+    public static final ReferenceIdentifier INIT = new ReferenceIdentifier( 0, "INIT", "initializing" );
+
+    /**
+     * Constant for the "LOCL" reference identifier type.
+     */
+    public static final ReferenceIdentifier LOCL = new ReferenceIdentifier( 1, "LOCL", "uncalibrated local clock" );
+
+    /**
+     * Constant for the "PPL" reference identifier type.
+     */
+    public static final ReferenceIdentifier PPS = new ReferenceIdentifier( 2, "PPL", "pulse-per-second source" );
+
+    /**
+     * Constant for the "ACTS" reference identifier type.
+     */
+    public static final ReferenceIdentifier ACTS = new ReferenceIdentifier( 3, "ACTS", "NIST dialup modem service" );
+
+    /**
+     * Constant for the "USNO" reference identifier type.
+     */
+    public static final ReferenceIdentifier USNO = new ReferenceIdentifier( 4, "USNO", "USNO modem service" );
+
+    /**
+     * Constant for the "PTB" reference identifier type.
+     */
+    public static final ReferenceIdentifier PTB = new ReferenceIdentifier( 5, "PTB", "PTB (Germany) modem service" );
+
+    /**
+     * Constant for the "TDF" reference identifier type.
+     */
+    public static final ReferenceIdentifier TDF = new ReferenceIdentifier( 6, "TDF", "Allouis (France) Radio 164 kHz" );
+
+    /**
+     * Constant for the "DCF" reference identifier type.
+     */
+    public static final ReferenceIdentifier DCF = new ReferenceIdentifier( 7, "DCF",
+        "Mainflingen (Germany) Radio 77.5 kHz" );
+
+    /**
+     * Constant for the "MSF" reference identifier type.
+     */
+    public static final ReferenceIdentifier MSF = new ReferenceIdentifier( 8, "MSF", "Rugby (UK) Radio 60 kHz" );
+
+    /**
+     * Constant for the "WWV" reference identifier type.
+     */
+    public static final ReferenceIdentifier WWV = new ReferenceIdentifier( 9, "WWV",
+        "Ft. Collins (US) Radio 2.5, 5, 10, 15, 20 MHz" );
+
+    /**
+     * Constant for the "WWVB" reference identifier type.
+     */
+    public static final ReferenceIdentifier WWVB = new ReferenceIdentifier( 10, "WWVB", "Boulder (US) Radio 60 kHz" );
+
+    /**
+     * Constant for the "WWVH" reference identifier type.
+     */
+    public static final ReferenceIdentifier WWVH = new ReferenceIdentifier( 11, "WWVH",
+        "Kaui Hawaii (US) Radio 2.5, 5, 10, 15 MHz" );
+
+    /**
+     * Constant for the "CHU" reference identifier type.
+     */
+    public static final ReferenceIdentifier CHU = new ReferenceIdentifier( 12, "CHU",
+        "Ottawa (Canada) Radio 3330, 7335, 14670 kHz" );
+
+    /**
+     * Constant for the "LORC" reference identifier type.
+     */
+    public static final ReferenceIdentifier LORC = new ReferenceIdentifier( 13, "LORC",
+        "LORAN-C radionavigation system" );
+
+    /**
+     * Constant for the "OMEG" reference identifier type.
+     */
+    public static final ReferenceIdentifier OMEG = new ReferenceIdentifier( 14, "OMEG", "OMEGA radionavigation system" );
+
+    /**
+     * Constant for the "GPS" reference identifier type.
+     */
+    public static final ReferenceIdentifier GPS = new ReferenceIdentifier( 15, "GPS", "Global Positioning Service" );
+
+    /**
+     * Constant for the "GOES" reference identifier type.
+     */
+    public static final ReferenceIdentifier GOES = new ReferenceIdentifier( 16, "GOES",
+        "Geostationary Orbit Environment Satellite" );
+
+    /**
+     * Constant for the "CDMA" reference identifier type.
+     */
+    public static final ReferenceIdentifier CDMA = new ReferenceIdentifier( 17, "CDMA",
+        "CDMA mobile cellular/PCS telephone system" );
+
+    /**
+     * Array for building a List of VALUES.
+     */
+    private static final ReferenceIdentifier[] values =
+        { INIT, LOCL, PPS, ACTS, USNO, PTB, TDF, DCF, MSF, WWV, WWVB, WWVH, CHU, LORC, OMEG, GPS, GOES, CDMA };
+
+    /**
+     * A list of all the reference identifier type constants.
+     */
+    public static final List<ReferenceIdentifier> VALUES = Collections.unmodifiableList( Arrays.asList( values ) );
+
+    /**
+     * The value/code for the reference identifier type.
+     */
+    private final int ordinal;
+
+    /**
+     * The name of the reference identifier type.
+     */
+    private final String name;
+
+    /**
+     * The code of the reference identifier type.
+     */
+    private final String code;
+
+
+    /**
+     * Private constructor prevents construction outside of this class.
+     */
+    private ReferenceIdentifier( int ordinal, String code, String name )
+    {
+        this.ordinal = ordinal;
+        this.code = code;
+        this.name = name;
+    }
+
+
+    /**
+     * Returns the reference identifier type when specified by its ordinal.
+     *
+     * @param type
+     * @return The reference identifier type.
+     */
+    public static ReferenceIdentifier getTypeByOrdinal( int type )
+    {
+        for ( int ii = 0; ii < values.length; ii++ )
+        {
+            if ( values[ii].ordinal == type )
+            {
+                return values[ii];
+            }
+        }
+
+        return LOCL;
+    }
+
+
+    /**
+     * Returns the reference identifier type when specified by its name.
+     *
+     * @param type
+     * @return The reference identifier type.
+     */
+    public static ReferenceIdentifier getTypeByName( String type )
+    {
+        for ( int ii = 0; ii < values.length; ii++ )
+        {
+            if ( values[ii].code.equalsIgnoreCase( type ) )
+            {
+                return values[ii];
+            }
+        }
+
+        return LOCL;
+    }
+
+
+    /**
+     * Returns the code associated with this reference identifier type.
+     *
+     * @return The reference identifier type code.
+     */
+    public String getCode()
+    {
+        return code;
+    }
+
+
+    /**
+     * Returns the number associated with this reference identifier type.
+     *
+     * @return The reference identifier type ordinal.
+     */
+    public int getOrdinal()
+    {
+        return ordinal;
+    }
+
+
+    public int compareTo( ReferenceIdentifier that )
+    {
+        return ordinal - that.ordinal;
+    }
+
+
+    public String toString()
+    {
+        return name;
+    }
+}
diff --git a/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/messages/StratumType.java b/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/messages/StratumType.java
new file mode 100644
index 0000000..1e4deeb
--- /dev/null
+++ b/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/messages/StratumType.java
@@ -0,0 +1,133 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.ntp.messages;
+
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+
+/**
+ * Stratum: This is a eight-bit unsigned integer indicating the stratum
+ * level of the local clock, with values defined as follows:
+ *
+ *    Stratum  Meaning
+ *    ----------------------------------------------
+ *    0        unspecified or unavailable
+ *    1        primary reference (e.g., radio clock)
+ *    2-15     secondary reference (via NTP or SNTP)
+ *    16-255   reserved
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public final class StratumType implements Comparable<StratumType>
+{
+    /**
+     * Constant for the "Unspecified or unavailable" stratum type.
+     */
+    public static final StratumType UNSPECIFIED = new StratumType( 0, "Unspecified or unavailable." );
+
+    /**
+     * Constant for the "Primary reference" stratum type.
+     */
+    public static final StratumType PRIMARY_REFERENCE = new StratumType( 1, "Primary reference." );
+
+    /**
+     * Constant for the "Secondary reference" stratum type.
+     */
+    public static final StratumType SECONDARY_REFERENCE = new StratumType( 2, "Secondary reference." );
+
+    /**
+     * Array for building a List of VALUES.
+     */
+    private static final StratumType[] values =
+        { UNSPECIFIED, PRIMARY_REFERENCE, SECONDARY_REFERENCE };
+
+    /**
+     * A list of all the stratum type constants.
+     */
+    public static final List<StratumType> VALUES = Collections.unmodifiableList( Arrays.asList( values ) );
+
+    /**
+     * The name of the stratum type.
+     */
+    private final String name;
+
+    /**
+     * The value/code for the stratum type.
+     */
+    private final int ordinal;
+
+
+    /**
+     * Private constructor prevents construction outside of this class.
+     */
+    private StratumType( int ordinal, String name )
+    {
+        this.ordinal = ordinal;
+        this.name = name;
+    }
+
+
+    /**
+     * Returns the stratum type when specified by its ordinal.
+     *
+     * @param type
+     * @return The stratum type.
+     */
+    public static StratumType getTypeByOrdinal( int type )
+    {
+        for ( int ii = 0; ii < values.length; ii++ )
+        {
+            if ( values[ii].ordinal == type )
+            {
+                return values[ii];
+            }
+        }
+
+        return UNSPECIFIED;
+    }
+
+
+    /**
+     * Returns the number associated with this stratum type.
+     *
+     * @return The stratum type ordinal.
+     */
+    public int getOrdinal()
+    {
+        return ordinal;
+    }
+
+
+    public int compareTo( StratumType that )
+    {
+        return ordinal - that.ordinal;
+    }
+
+
+    public String toString()
+    {
+        return name;
+    }
+}
diff --git a/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/messages/package-info.java b/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/messages/package-info.java
new file mode 100644
index 0000000..1400ed1
--- /dev/null
+++ b/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/messages/package-info.java
@@ -0,0 +1,28 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+/**
+ * Provides message objects for NTP messages.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+
+package org.apache.directory.server.ntp.messages;
diff --git a/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/package-info.java b/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/package-info.java
new file mode 100644
index 0000000..24a5a97
--- /dev/null
+++ b/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/package-info.java
@@ -0,0 +1,31 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+/**
+ * Provides the entry point to instances of the
+ * {@link org.apache.directory.server.ntp.NtpServer},
+ * as well as support for configuration and the root
+ * of the exception hierarchy.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+
+package org.apache.directory.server.ntp;
diff --git a/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/protocol/NtpDecoder.java b/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/protocol/NtpDecoder.java
new file mode 100644
index 0000000..42bb13a
--- /dev/null
+++ b/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/protocol/NtpDecoder.java
@@ -0,0 +1,42 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.ntp.protocol;
+
+
+import org.apache.directory.server.ntp.io.NtpMessageDecoder;
+import org.apache.mina.common.ByteBuffer;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.filter.codec.ProtocolDecoderAdapter;
+import org.apache.mina.filter.codec.ProtocolDecoderOutput;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class NtpDecoder extends ProtocolDecoderAdapter
+{
+    public void decode( IoSession session, ByteBuffer in, ProtocolDecoderOutput out )
+    {
+        NtpMessageDecoder decoder = new NtpMessageDecoder();
+        out.write( decoder.decode( in.buf() ) );
+    }
+}
diff --git a/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/protocol/NtpEncoder.java b/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/protocol/NtpEncoder.java
new file mode 100644
index 0000000..a9ab889
--- /dev/null
+++ b/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/protocol/NtpEncoder.java
@@ -0,0 +1,49 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.ntp.protocol;
+
+
+import org.apache.directory.server.ntp.io.NtpMessageEncoder;
+import org.apache.directory.server.ntp.messages.NtpMessage;
+import org.apache.mina.common.ByteBuffer;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.filter.codec.ProtocolEncoderAdapter;
+import org.apache.mina.filter.codec.ProtocolEncoderOutput;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class NtpEncoder extends ProtocolEncoderAdapter
+{
+    public void encode( IoSession session, Object message, ProtocolEncoderOutput out )
+    {
+        NtpMessageEncoder encoder = new NtpMessageEncoder();
+
+        ByteBuffer buf = ByteBuffer.allocate( 1024 );
+        encoder.encode( buf.buf(), ( NtpMessage ) message );
+
+        buf.flip();
+
+        out.write( buf );
+    }
+}
diff --git a/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/protocol/NtpProtocolCodecFactory.java b/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/protocol/NtpProtocolCodecFactory.java
new file mode 100644
index 0000000..0c71cd9
--- /dev/null
+++ b/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/protocol/NtpProtocolCodecFactory.java
@@ -0,0 +1,67 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.ntp.protocol;
+
+
+import org.apache.mina.filter.codec.ProtocolCodecFactory;
+import org.apache.mina.filter.codec.ProtocolDecoder;
+import org.apache.mina.filter.codec.ProtocolEncoder;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class NtpProtocolCodecFactory implements ProtocolCodecFactory
+{
+    private static final NtpProtocolCodecFactory INSTANCE = new NtpProtocolCodecFactory();
+
+
+    /**
+     * Returns the singleton instance of {@link NtpProtocolCodecFactory}.
+     *
+     * @return The singleton instance of {@link NtpProtocolCodecFactory}.
+     */
+    public static NtpProtocolCodecFactory getInstance()
+    {
+        return INSTANCE;
+    }
+
+
+    private NtpProtocolCodecFactory()
+    {
+        // Private constructor prevents instantiation outside this class.
+    }
+
+
+    public ProtocolEncoder getEncoder()
+    {
+        // Create a new encoder.
+        return new NtpEncoder();
+    }
+
+
+    public ProtocolDecoder getDecoder()
+    {
+        // Create a new decoder.
+        return new NtpDecoder();
+    }
+}
diff --git a/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/protocol/NtpProtocolHandler.java b/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/protocol/NtpProtocolHandler.java
new file mode 100644
index 0000000..cf98504
--- /dev/null
+++ b/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/protocol/NtpProtocolHandler.java
@@ -0,0 +1,112 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.ntp.protocol;
+
+
+import org.apache.directory.server.ntp.NtpService;
+import org.apache.directory.server.ntp.messages.NtpMessage;
+import org.apache.directory.server.ntp.service.NtpServiceImpl;
+import org.apache.mina.common.IdleStatus;
+import org.apache.mina.common.IoHandler;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.filter.codec.ProtocolCodecFilter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class NtpProtocolHandler implements IoHandler
+{
+    /** the log for this class */
+    private static final Logger log = LoggerFactory.getLogger( NtpProtocolHandler.class );
+
+    private NtpService ntpService = new NtpServiceImpl();
+
+
+    public void sessionCreated( IoSession session ) throws Exception
+    {
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "{} CREATED", session.getRemoteAddress() );
+        }
+
+        session.getFilterChain().addFirst( "codec", new ProtocolCodecFilter( NtpProtocolCodecFactory.getInstance() ) );
+    }
+
+
+    public void sessionOpened( IoSession session )
+    {
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "{} OPENED", session.getRemoteAddress() );
+        }
+    }
+
+
+    public void sessionClosed( IoSession session )
+    {
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "{} CLOSED", session.getRemoteAddress() );
+        }
+    }
+
+
+    public void sessionIdle( IoSession session, IdleStatus status )
+    {
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "{} IDLE ({})", session.getRemoteAddress(), status );
+        }
+    }
+
+
+    public void exceptionCaught( IoSession session, Throwable cause )
+    {
+        log.error( session.getRemoteAddress() + " EXCEPTION", cause );
+        session.close();
+    }
+
+
+    public void messageReceived( IoSession session, Object message )
+    {
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "{} RCVD:  {}", session.getRemoteAddress(), message );
+        }
+
+        NtpMessage reply = ntpService.getReplyFor( ( NtpMessage ) message );
+
+        session.write( reply );
+    }
+
+
+    public void messageSent( IoSession session, Object message )
+    {
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "{} SENT:  {}", session.getRemoteAddress(), message );
+        }
+    }
+}
diff --git a/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/protocol/package-info.java b/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/protocol/package-info.java
new file mode 100644
index 0000000..2dea9f5
--- /dev/null
+++ b/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/protocol/package-info.java
@@ -0,0 +1,30 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+/**
+ * Provides the {@link org.apache.mina.common.IoHandler} and associated
+ * {@link org.apache.mina.filter.codec.ProtocolCodecFactory} required
+ * to implement the NTP Service with the MINA NIO framework.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+
+package org.apache.directory.server.ntp.protocol;
diff --git a/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/service/NtpServiceImpl.java b/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/service/NtpServiceImpl.java
new file mode 100644
index 0000000..842a461
--- /dev/null
+++ b/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/service/NtpServiceImpl.java
@@ -0,0 +1,63 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.ntp.service;
+
+
+import org.apache.directory.server.ntp.NtpService;
+import org.apache.directory.server.ntp.messages.LeapIndicatorType;
+import org.apache.directory.server.ntp.messages.ModeType;
+import org.apache.directory.server.ntp.messages.NtpMessage;
+import org.apache.directory.server.ntp.messages.NtpMessageModifier;
+import org.apache.directory.server.ntp.messages.NtpTimeStamp;
+import org.apache.directory.server.ntp.messages.ReferenceIdentifier;
+import org.apache.directory.server.ntp.messages.StratumType;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class NtpServiceImpl implements NtpService
+{
+    public NtpMessage getReplyFor( NtpMessage request )
+    {
+        NtpMessageModifier modifier = new NtpMessageModifier();
+
+        modifier.setLeapIndicator( LeapIndicatorType.NO_WARNING );
+        modifier.setVersionNumber( 4 );
+        modifier.setMode( ModeType.SERVER );
+        modifier.setStratum( StratumType.PRIMARY_REFERENCE );
+        modifier.setPollInterval( ( byte ) 0x04 );
+        modifier.setPrecision( ( byte ) 0xFA );
+        modifier.setRootDelay( 0 );
+        modifier.setRootDispersion( 0 );
+        modifier.setReferenceIdentifier( ReferenceIdentifier.LOCL );
+
+        NtpTimeStamp now = new NtpTimeStamp();
+
+        modifier.setReferenceTimestamp( now );
+        modifier.setOriginateTimestamp( request.getTransmitTimestamp() );
+        modifier.setReceiveTimestamp( request.getReceiveTimestamp() );
+        modifier.setTransmitTimestamp( now );
+
+        return modifier.getNtpMessage();
+    }
+}
diff --git a/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/service/package-info.java b/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/service/package-info.java
new file mode 100644
index 0000000..e558111
--- /dev/null
+++ b/old_trunk/protocol-ntp/src/main/java/org/apache/directory/server/ntp/service/package-info.java
@@ -0,0 +1,28 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+/**
+ * Provides the implementation of the {@link org.apache.directory.server.ntp.NtpService}.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+
+package org.apache.directory.server.ntp.service;
diff --git a/old_trunk/protocol-ntp/src/site/site.xml b/old_trunk/protocol-ntp/src/site/site.xml
new file mode 100644
index 0000000..9ec7c16
--- /dev/null
+++ b/old_trunk/protocol-ntp/src/site/site.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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.
+-->
+<!--
+  @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+-->
+<project name="${project.name}">
+    <bannerLeft>
+        <name>${project.name}</name>
+    </bannerLeft>
+    <publishDate position="navigation-bottom" format="dd-MM-yyyy HH:mm" />
+    <body>
+        <menu name="Parent">
+            <item name="Apache Directory ApacheDS" href="../index.html" />
+        </menu>
+        <menu ref="reports" />
+    </body>
+</project>
diff --git a/old_trunk/protocol-ntp/src/test/java/org/apache/directory/server/ntp/NtpITest.java b/old_trunk/protocol-ntp/src/test/java/org/apache/directory/server/ntp/NtpITest.java
new file mode 100644
index 0000000..64fab4b
--- /dev/null
+++ b/old_trunk/protocol-ntp/src/test/java/org/apache/directory/server/ntp/NtpITest.java
@@ -0,0 +1,93 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.ntp;
+
+
+import java.net.InetAddress;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+import org.apache.commons.net.ntp.NTPUDPClient;
+import org.apache.commons.net.ntp.TimeInfo;
+import org.apache.directory.server.protocol.shared.DatagramAcceptor;
+import org.apache.directory.server.unit.AbstractServerTest;
+import org.apache.mina.util.AvailablePortFinder;
+
+
+/**
+ * An {@link AbstractServerTest} testing the Network Time Protocol (NTP).
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class NtpITest extends TestCase
+{
+    private NtpServer ntpConfig;
+    private int port;
+
+
+    /**
+     * Set up a partition for EXAMPLE.COM and enable the NTP service.  The LDAP service is disabled.
+     */
+    public void setUp() throws Exception
+    {
+        DatagramAcceptor datagramAcceptor = new DatagramAcceptor( null );
+        ntpConfig = new NtpServer();
+        ntpConfig.setDatagramAcceptor( datagramAcceptor );
+        ntpConfig.setEnabled( true );
+        port = AvailablePortFinder.getNextAvailable( 10123 );
+        ntpConfig.setIpPort( port );
+        ntpConfig.start();
+
+    }
+
+    /**
+     * Tests to make sure NTP works when enabled in the server.
+     *
+     * @throws Exception if there are errors
+     */
+    public void testNtp() throws Exception
+    {
+        long currentTime = System.currentTimeMillis();
+
+        InetAddress host = InetAddress.getByName( null );
+
+        NTPUDPClient ntp = new NTPUDPClient();
+        ntp.setDefaultTimeout( 5000 );
+
+        TimeInfo timeInfo = ntp.getTime( host, port );
+        long returnTime = timeInfo.getReturnTime();
+        Assert.assertTrue( currentTime - returnTime < 1000 );
+
+        timeInfo.computeDetails();
+
+        Assert.assertTrue( 0 < timeInfo.getOffset() && timeInfo.getOffset() < 1000 );
+        Assert.assertTrue( 0 < timeInfo.getDelay() && timeInfo.getDelay() < 1000 );
+    }
+
+
+    /**
+     * Tear down.
+     */
+    public void tearDown() throws Exception
+    {
+        ntpConfig.stop();
+    }
+}
diff --git a/old_trunk/protocol-ntp/src/test/java/org/apache/directory/server/ntp/NtpMessageDecoderTest.java b/old_trunk/protocol-ntp/src/test/java/org/apache/directory/server/ntp/NtpMessageDecoderTest.java
new file mode 100644
index 0000000..8a3cd9a
--- /dev/null
+++ b/old_trunk/protocol-ntp/src/test/java/org/apache/directory/server/ntp/NtpMessageDecoderTest.java
@@ -0,0 +1,105 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.ntp;
+
+
+import java.nio.ByteBuffer;
+
+import junit.framework.TestCase;
+
+import org.apache.directory.server.ntp.io.NtpMessageDecoder;
+import org.apache.directory.server.ntp.messages.NtpMessage;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class NtpMessageDecoderTest extends TestCase
+{
+    /** the log for this class */
+    private static final Logger log = LoggerFactory.getLogger( NtpMessageDecoderTest.class );
+
+    private static byte[] clientRequest = new byte[]
+        { ( byte ) 0xe3, ( byte ) 0x00, ( byte ) 0x06, ( byte ) 0xee, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00,
+            ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x49, ( byte ) 0x4e,
+            ( byte ) 0x49, ( byte ) 0x54, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00,
+            ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00,
+            ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00,
+            ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0xc5, ( byte ) 0x0f,
+            ( byte ) 0x41, ( byte ) 0x5a, ( byte ) 0xbf, ( byte ) 0xba, ( byte ) 0xdc, ( byte ) 0x09 };
+
+    private static byte[] serverResponse = new byte[]
+        { ( byte ) 0x24, ( byte ) 0x01, ( byte ) 0x06, ( byte ) 0xf0, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00,
+            ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x1b, ( byte ) 0x43, ( byte ) 0x44,
+            ( byte ) 0x4d, ( byte ) 0x41, ( byte ) 0xc5, ( byte ) 0x0f, ( byte ) 0x41, ( byte ) 0x51, ( byte ) 0xba,
+            ( byte ) 0x35, ( byte ) 0x2e, ( byte ) 0xb5, ( byte ) 0xc5, ( byte ) 0x0f, ( byte ) 0x41, ( byte ) 0x5a,
+            ( byte ) 0xbf, ( byte ) 0xba, ( byte ) 0xdc, ( byte ) 0x09, ( byte ) 0xc5, ( byte ) 0x0f, ( byte ) 0x41,
+            ( byte ) 0x5a, ( byte ) 0xc5, ( byte ) 0xeb, ( byte ) 0xa6, ( byte ) 0xac, ( byte ) 0xc5, ( byte ) 0x0f,
+            ( byte ) 0x41, ( byte ) 0x5a, ( byte ) 0xc6, ( byte ) 0x48, ( byte ) 0xd7, ( byte ) 0xe0 };
+
+
+    /**
+     * Tests the parsing of a client request.
+     *
+     * @throws Exception
+     */
+    public void testParseClient() throws Exception
+    {
+        ByteBuffer buffer = ByteBuffer.wrap( clientRequest );
+        NtpMessageDecoder decoder = new NtpMessageDecoder();
+        NtpMessage request = decoder.decode( buffer );
+        print( request );
+    }
+
+
+    /**
+     * Tests the parsing of a server response.
+     *
+     * @throws Exception
+     */
+    public void testParseServer() throws Exception
+    {
+        ByteBuffer buffer = ByteBuffer.wrap( serverResponse );
+        NtpMessageDecoder decoder = new NtpMessageDecoder();
+        NtpMessage request = decoder.decode( buffer );
+        print( request );
+    }
+
+
+    protected void print( NtpMessage request )
+    {
+        log.debug( String.valueOf( request.getLeapIndicator() ) );
+        log.debug( String.valueOf( request.getVersionNumber() ) );
+        log.debug( String.valueOf( request.getMode() ) );
+        log.debug( String.valueOf( request.getStratum() ) );
+        log.debug( String.valueOf( request.getPollInterval() ) );
+        log.debug( String.valueOf( request.getPrecision() ) );
+        log.debug( String.valueOf( request.getRootDelay() ) );
+        log.debug( String.valueOf( request.getRootDispersion() ) );
+        log.debug( String.valueOf( request.getReferenceIdentifier() ) );
+        log.debug( String.valueOf( request.getReferenceTimestamp() ) );
+        log.debug( String.valueOf( request.getOriginateTimestamp() ) );
+        log.debug( String.valueOf( request.getReceiveTimestamp() ) );
+        log.debug( String.valueOf( request.getTransmitTimestamp() ) );
+    }
+}
diff --git a/old_trunk/protocol-ntp/src/test/java/org/apache/directory/server/ntp/NtpMessageEncoderTest.java b/old_trunk/protocol-ntp/src/test/java/org/apache/directory/server/ntp/NtpMessageEncoderTest.java
new file mode 100644
index 0000000..9caafc4
--- /dev/null
+++ b/old_trunk/protocol-ntp/src/test/java/org/apache/directory/server/ntp/NtpMessageEncoderTest.java
@@ -0,0 +1,107 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.ntp;
+
+
+import java.nio.ByteBuffer;
+
+import junit.framework.TestCase;
+
+import org.apache.directory.server.ntp.io.NtpMessageDecoder;
+import org.apache.directory.server.ntp.io.NtpMessageEncoder;
+import org.apache.directory.server.ntp.messages.LeapIndicatorType;
+import org.apache.directory.server.ntp.messages.ModeType;
+import org.apache.directory.server.ntp.messages.NtpMessage;
+import org.apache.directory.server.ntp.messages.NtpMessageModifier;
+import org.apache.directory.server.ntp.messages.NtpTimeStamp;
+import org.apache.directory.server.ntp.messages.ReferenceIdentifier;
+import org.apache.directory.server.ntp.messages.StratumType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class NtpMessageEncoderTest extends TestCase
+{
+    /** the log for this class */
+    private static final Logger log = LoggerFactory.getLogger( NtpMessageEncoderTest.class );
+
+
+    /**
+     * Tests the encoding of an NTP message.
+     *
+     * @throws Exception
+     */
+    public void testEncodeMessage() throws Exception
+    {
+        NtpMessageModifier modifier = new NtpMessageModifier();
+        modifier.setLeapIndicator( LeapIndicatorType.NO_WARNING );
+        modifier.setVersionNumber( 4 );
+        modifier.setMode( ModeType.SERVER );
+        modifier.setStratum( StratumType.PRIMARY_REFERENCE );
+        modifier.setPollInterval( ( byte ) 0x06 ); // 6
+        modifier.setPrecision( ( byte ) 0xFA ); // -6
+        modifier.setRootDelay( 0 );
+        modifier.setRootDispersion( 0 );
+        modifier.setReferenceIdentifier( ReferenceIdentifier.LOCL );
+
+        NtpTimeStamp now = new NtpTimeStamp();
+
+        modifier.setReferenceTimestamp( now );
+        modifier.setOriginateTimestamp( now );
+        modifier.setReceiveTimestamp( now );
+        modifier.setTransmitTimestamp( now );
+
+        NtpMessage message = modifier.getNtpMessage();
+
+        ByteBuffer replyByteBuffer = ByteBuffer.allocate( 1024 );
+
+        NtpMessageEncoder encoder = new NtpMessageEncoder();
+        encoder.encode( replyByteBuffer, message );
+
+        print( message );
+
+        NtpMessageDecoder decoder = new NtpMessageDecoder();
+        NtpMessage reply = decoder.decode( replyByteBuffer );
+
+        print( reply );
+    }
+
+
+    protected void print( NtpMessage request )
+    {
+        log.debug( String.valueOf( request.getLeapIndicator() ) );
+        log.debug( String.valueOf( request.getVersionNumber() ) );
+        log.debug( String.valueOf( request.getMode() ) );
+        log.debug( String.valueOf( request.getStratum() ) );
+        log.debug( String.valueOf( request.getPollInterval() ) );
+        log.debug( String.valueOf( request.getPrecision() ) );
+        log.debug( String.valueOf( request.getRootDelay() ) );
+        log.debug( String.valueOf( request.getRootDispersion() ) );
+        log.debug( String.valueOf( request.getReferenceIdentifier() ) );
+        log.debug( String.valueOf( request.getReferenceTimestamp() ) );
+        log.debug( String.valueOf( request.getOriginateTimestamp() ) );
+        log.debug( String.valueOf( request.getReceiveTimestamp() ) );
+        log.debug( String.valueOf( request.getTransmitTimestamp() ) );
+    }
+}
diff --git a/old_trunk/protocol-ntp/src/test/resources/log4j.properties b/old_trunk/protocol-ntp/src/test/resources/log4j.properties
new file mode 100644
index 0000000..facb3e6
--- /dev/null
+++ b/old_trunk/protocol-ntp/src/test/resources/log4j.properties
@@ -0,0 +1,21 @@
+#############################################################################
+#    Licensed to the Apache Software Foundation (ASF) under one or more
+#    contributor license agreements.  See the NOTICE file distributed with
+#    this work for additional information regarding copyright ownership.
+#    The ASF licenses this file to You 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.
+#############################################################################
+log4j.rootCategory=OFF, stdout
+
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=[%d{HH:mm:ss}] %p [%c] - %m%n
diff --git a/old_trunk/protocol-shared/pom.xml b/old_trunk/protocol-shared/pom.xml
new file mode 100644
index 0000000..326fb50
--- /dev/null
+++ b/old_trunk/protocol-shared/pom.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you 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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.directory.server</groupId>
+    <artifactId>apacheds-parent</artifactId>
+    <version>1.5.4-SNAPSHOT</version>
+  </parent>
+  <groupId>org.apache.directory.server</groupId>
+  <artifactId>apacheds-protocol-shared</artifactId>
+
+  <description>
+    Shared library that is used by all protocol providers in ApacheDS
+  </description>
+
+  <name>ApacheDS Protocol Shared</name>
+  <packaging>jar</packaging>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-core</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.mina</groupId>
+      <artifactId>mina-core</artifactId>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-source-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>attach-sources</id>
+            <phase>verify</phase>
+            <goals>
+              <goal>jar</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+  
+</project>
diff --git a/old_trunk/protocol-shared/src/main/java/org/apache/directory/server/protocol/shared/AbstractProtocolService.java b/old_trunk/protocol-shared/src/main/java/org/apache/directory/server/protocol/shared/AbstractProtocolService.java
new file mode 100644
index 0000000..b3a114c
--- /dev/null
+++ b/old_trunk/protocol-shared/src/main/java/org/apache/directory/server/protocol/shared/AbstractProtocolService.java
@@ -0,0 +1,176 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements.  See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership.  The ASF licenses this file

+ * to you 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.

+ */

+package org.apache.directory.server.protocol.shared;

+

+

+import org.apache.directory.server.core.DirectoryService;

+

+import java.util.Collections;

+import java.util.HashSet;

+import java.util.Set;

+

+

+/**

+ * An abstract base class for a ProtocolService. The start/stop methods have

+ * not been implemented.

+ *

+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>

+ * @version $Rev$, $Date$

+ */

+public abstract class AbstractProtocolService implements ProtocolService

+{

+    private boolean started;

+    private boolean enabled;

+    private String serviceId;

+    private String serviceName;

+    private String ipAddress;

+    private int ipPort = -1;

+    private Set<TransportProtocol> transportProtocols;

+    private DatagramAcceptor datagramAcceptor;

+    private SocketAcceptor socketAcceptor;

+    /** directory service core where protocol data is backed */

+    private DirectoryService directoryService;

+

+

+    public DirectoryService getDirectoryService()

+    {

+        return directoryService;

+    }

+

+

+    public void setDirectoryService( DirectoryService directoryService )

+    {

+        this.directoryService = directoryService;

+    }

+

+

+    public boolean isStarted()

+    {

+        return started;

+    }

+

+

+    protected void setStarted( boolean started )

+    {

+        this.started = started;

+    }

+

+

+    public boolean isEnabled()

+    {

+        return enabled;

+    }

+

+

+    public void setEnabled( boolean enabled )

+    {

+        this.enabled = enabled;

+    }

+

+

+    public String getServiceId()

+    {

+        return serviceId;

+    }

+

+

+    public void setServiceId( String serviceId )

+    {

+        this.serviceId = serviceId;

+    }

+

+

+    public String getServiceName()

+    {

+        return serviceName;

+    }

+

+

+    public void setServiceName( String name )

+    {

+        this.serviceName = name;

+    }

+

+

+    public String getIpAddress()

+    {

+        return ipAddress;

+    }

+

+

+    public void setIpAddress( String ipAddress )

+    {

+        this.ipAddress = ipAddress;

+    }

+

+

+    public int getIpPort()

+    {

+        return ipPort;

+    }

+

+

+    public void setIpPort( int ipPort )

+    {

+        if ( ipPort < 0 || ipPort > 65535 )

+        {

+            throw new IllegalArgumentException( "Invalid port number: " + ipPort );

+        }

+

+        this.ipPort = ipPort;

+    }

+

+

+    public Set<TransportProtocol> getTransportProtocols()

+    {

+        return transportProtocols;

+    }

+

+

+    public void setTransportProtocols( Set<TransportProtocol> transportProtocols )

+    {

+        Set<TransportProtocol> copy = new HashSet<TransportProtocol>( transportProtocols.size() );

+        copy.addAll( transportProtocols );

+        this.transportProtocols = Collections.unmodifiableSet( copy );

+    }

+

+

+    public DatagramAcceptor getDatagramAcceptor()

+    {

+        return datagramAcceptor;

+    }

+

+

+    public void setDatagramAcceptor( DatagramAcceptor datagramAcceptor )

+    {

+        this.datagramAcceptor = datagramAcceptor;

+    }

+

+

+    public SocketAcceptor getSocketAcceptor()

+    {

+        return socketAcceptor;

+    }

+

+

+    public void setSocketAcceptor( SocketAcceptor socketAcceptor )

+    {

+        this.socketAcceptor = socketAcceptor;

+    }

+}

diff --git a/old_trunk/protocol-shared/src/main/java/org/apache/directory/server/protocol/shared/DatagramAcceptor.java b/old_trunk/protocol-shared/src/main/java/org/apache/directory/server/protocol/shared/DatagramAcceptor.java
new file mode 100644
index 0000000..9e76961
--- /dev/null
+++ b/old_trunk/protocol-shared/src/main/java/org/apache/directory/server/protocol/shared/DatagramAcceptor.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+package org.apache.directory.server.protocol.shared;
+
+
+import java.io.IOException;
+import java.net.SocketAddress;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+
+import org.apache.mina.common.IoHandler;
+import org.apache.mina.common.IoServiceConfig;
+import org.apache.mina.common.ThreadModel;
+import org.apache.mina.filter.executor.ExecutorFilter;
+
+
+/**
+ * Extension around a MINA DatagramAcceptor to facilitate better usage with Spring.
+ *
+ * @version $Rev$ $Date$
+ * @org.apache.xbean.XBean
+ */
+public class DatagramAcceptor extends org.apache.mina.transport.socket.nio.DatagramAcceptor
+{
+    private static final int DEFAULT_THREADS = 10;
+
+
+    public DatagramAcceptor( Executor logicExecutor )
+    {
+        super();
+        if ( logicExecutor == null )
+        {
+            logicExecutor = Executors.newFixedThreadPool( DEFAULT_THREADS );
+        }
+        getFilterChain().addLast( "executor", new ExecutorFilter( logicExecutor ) );
+    }
+
+
+    public void bind( SocketAddress address, IoHandler ioHandler, IoServiceConfig udpConfig ) throws IOException
+    {
+        udpConfig.setThreadModel( ThreadModel.MANUAL );
+        super.bind( address, ioHandler, udpConfig );
+    }
+
+
+    public void unbind( SocketAddress address )
+    {
+        super.unbind(address);
+    }
+
+}
diff --git a/old_trunk/protocol-shared/src/main/java/org/apache/directory/server/protocol/shared/DirectoryBackedService.java b/old_trunk/protocol-shared/src/main/java/org/apache/directory/server/protocol/shared/DirectoryBackedService.java
new file mode 100644
index 0000000..b2d8055
--- /dev/null
+++ b/old_trunk/protocol-shared/src/main/java/org/apache/directory/server/protocol/shared/DirectoryBackedService.java
@@ -0,0 +1,87 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.protocol.shared;
+
+import org.apache.directory.server.constants.ServerDNConstants;
+
+
+/**
+ * Base class shared by all protocol providers for configuration.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public abstract class DirectoryBackedService extends AbstractProtocolService
+{
+    /**
+     * The single location where entries are stored.  If this service
+     * is catalog based the store will search the system partition
+     * configuration for catalog entries.  Otherwise it will use this
+     * search base as a single point of searching the DIT.
+     */
+    private String searchBaseDn = ServerDNConstants.USER_EXAMPLE_COM_DN;
+    
+    /** determines if the search base is pointer to a catelog or a single entry point */
+    private boolean catelogBased;
+
+
+    /**
+     * Returns the search base DN.
+     *
+     * @return The search base DN.
+     */
+    public String getSearchBaseDn()
+    {
+        return searchBaseDn;
+    }
+
+
+    /**
+     * @param searchBaseDn The searchBaseDn to set.
+     */
+    public void setSearchBaseDn( String searchBaseDn )
+    {
+        this.searchBaseDn = searchBaseDn;
+    }
+
+
+    /**
+     * Gets true if this service uses a catelog for searching different
+     * regions of the DIT for its data.
+     *
+     * @return true if the search base dn is for a catelog, false otherwise
+     */
+    public boolean isCatelogBased()
+    {
+        return catelogBased;
+    }
+
+
+    /**
+     * Set true if this service uses a catelog for searching different
+     * regions of the DIT for its data.
+     *
+     * @param  catelogBased if the search base dn is for a catelog, false otherwise
+     */
+    public void setCatelogBased( boolean catelogBased )
+    {
+        this.catelogBased = catelogBased;
+    }
+}
diff --git a/old_trunk/protocol-shared/src/main/java/org/apache/directory/server/protocol/shared/MapAdapter.java b/old_trunk/protocol-shared/src/main/java/org/apache/directory/server/protocol/shared/MapAdapter.java
new file mode 100644
index 0000000..a60e125
--- /dev/null
+++ b/old_trunk/protocol-shared/src/main/java/org/apache/directory/server/protocol/shared/MapAdapter.java
@@ -0,0 +1,182 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.protocol.shared;
+
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+
+/**
+ * Adapter to add the Map interface to Dictionary's.  Many of the OSGi interfaces use
+ * Dictionary's for legacy reasons, but the Dictionary is obsolete.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class MapAdapter implements Map<Object, Object>
+{
+    private Dictionary<Object, Object> dictionary;
+
+
+    /**
+     * Creates a new instance of MapAdapter.
+     *
+     * @param dictionary
+     */
+    public MapAdapter( Dictionary<Object, Object> dictionary )
+    {
+        this.dictionary = dictionary;
+    }
+
+
+    /**
+     * @see java.util.Map#clear()
+     */
+    public void clear()
+    {
+        dictionary = new Hashtable<Object, Object>();
+    }
+
+
+    /**
+     * @see java.util.Map#containsKey(java.lang.Object)
+     */
+    public boolean containsKey( Object key )
+    {
+        return Collections.list( dictionary.keys() ).contains( key );
+    }
+
+
+    /**
+     * @see java.util.Map#containsValue(java.lang.Object)
+     */
+    public boolean containsValue( Object value )
+    {
+        return Collections.list( dictionary.elements() ).contains( value );
+    }
+
+
+    /**
+     * @see java.util.Map#entrySet()
+     */
+    public Set<Map.Entry<Object, Object>> entrySet()
+    {
+        Map<Object, Object> map = new HashMap<Object, Object>();
+
+        Enumeration<Object> e = dictionary.keys();
+
+        while ( e.hasMoreElements() )
+        {
+            Object key = e.nextElement();
+            Object value = dictionary.get( key );
+            map.put( key, value );
+        }
+
+        return map.entrySet();
+    }
+
+
+    /**
+     * @see java.util.Map#get(java.lang.Object)
+     */
+    public Object get( Object key )
+    {
+        return dictionary.get( key );
+    }
+
+
+    /**
+     * @see java.util.Map#isEmpty()
+     */
+    public boolean isEmpty()
+    {
+        return dictionary.isEmpty();
+    }
+
+
+    /**
+     * @see java.util.Map#keySet()
+     */
+    public Set<Object> keySet()
+    {
+        return new HashSet<Object>( Collections.list( dictionary.keys() ) );
+    }
+
+
+    /**
+     * @see java.util.Map#put(java.lang.Object, java.lang.Object)
+     */
+    public Object put( Object arg0, Object arg1 )
+    {
+        return dictionary.put( arg0, arg1 );
+    }
+
+
+    /**
+     * @see java.util.Map#putAll(java.util.Map)
+     */
+    public void putAll( Map arg0 )
+    {
+        Iterator it = arg0.entrySet().iterator();
+
+        while ( it.hasNext() )
+        {
+            Map.Entry entry = ( Map.Entry ) it.next();
+            dictionary.put( entry.getKey(), entry.getValue() );
+        }
+    }
+
+
+    /**
+     * @see java.util.Map#remove(java.lang.Object)
+     */
+    public Object remove( Object key )
+    {
+        return dictionary.remove( key );
+    }
+
+
+    /**
+     * @see java.util.Map#size()
+     */
+    public int size()
+    {
+        return dictionary.size();
+    }
+
+
+    /**
+     * @see java.util.Map#values()
+     */
+    public Collection<Object> values()
+    {
+        return Collections.list( dictionary.elements() );
+    }
+}
diff --git a/old_trunk/protocol-shared/src/main/java/org/apache/directory/server/protocol/shared/ProtocolService.java b/old_trunk/protocol-shared/src/main/java/org/apache/directory/server/protocol/shared/ProtocolService.java
new file mode 100644
index 0000000..5958610
--- /dev/null
+++ b/old_trunk/protocol-shared/src/main/java/org/apache/directory/server/protocol/shared/ProtocolService.java
@@ -0,0 +1,211 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements.  See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership.  The ASF licenses this file

+ * to you 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.

+ */

+package org.apache.directory.server.protocol.shared;

+

+

+import org.apache.directory.server.core.DirectoryService;

+

+import java.util.Set;

+

+

+/**

+ * Minimum functionality required by an ApacheDS protocol service.

+ *

+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>

+ * @version $Rev$, $Date$

+ */

+public interface ProtocolService

+{

+    /**

+     * Stops this ProtocolService which unbinds acceptors on the protocol port.

+     *

+     * @throws Exception if there are problems stopping this service

+     */

+    void stop() throws Exception;

+

+

+    /**

+     * Starts this ProtocolService which binds acceptors on the protocol port.

+     *

+     * @throws Exception if there are problems starting this service

+     */

+    void start() throws Exception;

+

+

+    /**

+     * Gets whether or not this service has been started.

+     *

+     * @return true if the service has started, false otherwise

+     */

+    boolean isStarted();

+

+

+    /**

+     * If this protocol service supports UDP transport then this gets the

+     * non-null MINA DatagramAcceptor it uses.

+     *

+     * @return the MINA DatagramAcceptor used for UDP transports

+     */

+    DatagramAcceptor getDatagramAcceptor();

+

+

+    /**

+     * If this protocol service supports UDP transport then this sets the

+     * MINA DatagramAcceptor it uses.

+     *

+     * @param datagramAcceptor the MINA DatagramAcceptor used for UDP transport

+     */

+    void setDatagramAcceptor( DatagramAcceptor datagramAcceptor );

+

+

+    /**

+     * If this protocol service support TCP transport then this gets the

+     * MINA SocketAcceptor it uses.

+     *

+     * @return the MINA SocketAcceptor used for TCP transport

+     */

+    SocketAcceptor getSocketAcceptor();

+

+

+    /**

+     * If this protocol service support TCP transport then this sets the

+     * MINA SocketAcceptor it uses.

+     *

+     * @param socketAcceptor the MINA SocketAcceptor used for TCP transport

+     */

+    void setSocketAcceptor( SocketAcceptor socketAcceptor );

+

+

+    /**

+     * Services can be enabled or disabled. If enabled they will be started, if

+     * not they will not.

+     *

+     * @return true if this service is to be started, false otherwise

+     */

+    boolean isEnabled();

+

+

+    /**

+     * Sets whether or not this ProtocolService is enabled.

+     *

+     * @param enabled true to enable, false to disable

+     */

+    void setEnabled( boolean enabled );

+

+

+    /**

+     * Gets the instance identifier for this ProtocolService.

+     *

+     * @return the identifier for the service instance

+     */

+    String getServiceId();

+

+

+    /**

+     * Sets the instance identifier for this ProtocolService.

+     *

+     * @param serviceId an identifier for the service instance

+     */

+    void setServiceId( String serviceId );

+

+

+    /**

+     * Gets a descriptive name for the kind of service this represents.

+     * This name is constant across instances of this ProtocolService.

+     *

+     * @return a descriptive name for the kind of this service

+     */

+    String getServiceName();

+

+

+    /**

+     * Sets the descriptive name for the kind of service this represents.

+     * This name is constant across instances of this ProtocolService.

+     * 

+     * @param name a descriptive name for the kind of this service

+     */

+    void setServiceName( String name );

+

+

+    /**

+     * Gets the IP address of this service.

+     *

+     * @return the IP address for this service.

+     */

+    String getIpAddress();

+

+

+    /**

+     * Gets the IP address of this service.

+     *

+     * @param ipAddress the Internet Protocol address for this service.

+     */

+    void setIpAddress( String ipAddress );

+

+

+    /**

+     * Gets the IP port for this service.

+     *

+     * @return the IP port for this service

+     */

+    int getIpPort();

+

+

+    /**

+     * Sets the IP port for this service.

+     *

+     * @param ipPort the ip port for this service

+     * @throws IllegalArgumentException if the port number is not within a valid range

+     */

+    void setIpPort( int ipPort );

+

+

+    /**

+     * Gets the transport protocols used by this service. At this point services

+     * which support more than one transport are configured to bind to that transport

+     * on the same port.

+     *

+     * @return the transport protocols used by this service

+     */

+    Set<TransportProtocol> getTransportProtocols();

+

+

+    /**

+     * Sets the transport protocols used by this service.

+     *

+     * @param transportProtocols the transport protocols to be used by this service

+     */

+    void setTransportProtocols( Set<TransportProtocol> transportProtocols );

+

+

+    /**

+     * Gets the DirectoryService assigned to this ProtocolService.

+     *

+     * @return the directory service core assigned to this service

+     */

+    DirectoryService getDirectoryService();

+

+

+    /**

+     * Sets the DirectoryService assigned to this ProtocolService.

+     *

+     * @param directoryService the directory service core assigned to this service

+     */

+    void setDirectoryService( DirectoryService directoryService );

+}

diff --git a/old_trunk/protocol-shared/src/main/java/org/apache/directory/server/protocol/shared/ServiceConfigurationException.java b/old_trunk/protocol-shared/src/main/java/org/apache/directory/server/protocol/shared/ServiceConfigurationException.java
new file mode 100644
index 0000000..52a6957
--- /dev/null
+++ b/old_trunk/protocol-shared/src/main/java/org/apache/directory/server/protocol/shared/ServiceConfigurationException.java
@@ -0,0 +1,73 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.protocol.shared;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ServiceConfigurationException extends RuntimeException
+{
+    private static final long serialVersionUID = 8298096524637031225L;
+
+
+    /**
+     * Creates a new instance of ServiceConfigurationException.
+     */
+    public ServiceConfigurationException()
+    {
+        super();
+    }
+
+
+    /**
+     * Creates a new instance of ServiceConfigurationException.
+     *
+     * @param message
+     * @param cause
+     */
+    public ServiceConfigurationException( String message, Throwable cause )
+    {
+        super( message, cause );
+    }
+
+
+    /**
+     * Creates a new instance of ServiceConfigurationException.
+     *
+     * @param message
+     */
+    public ServiceConfigurationException( String message )
+    {
+        super( message );
+    }
+
+
+    /**
+     * Creates a new instance of ServiceConfigurationException.
+     *
+     * @param cause
+     */
+    public ServiceConfigurationException( Throwable cause )
+    {
+        super( cause );
+    }
+}
diff --git a/old_trunk/protocol-shared/src/main/java/org/apache/directory/server/protocol/shared/SocketAcceptor.java b/old_trunk/protocol-shared/src/main/java/org/apache/directory/server/protocol/shared/SocketAcceptor.java
new file mode 100644
index 0000000..6b03ce5
--- /dev/null
+++ b/old_trunk/protocol-shared/src/main/java/org/apache/directory/server/protocol/shared/SocketAcceptor.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+
+package org.apache.directory.server.protocol.shared;
+
+import java.io.IOException;
+import java.net.SocketAddress;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+
+import org.apache.mina.common.IoHandler;
+import org.apache.mina.common.IoServiceConfig;
+import org.apache.mina.common.ThreadModel;
+import org.apache.mina.filter.executor.ExecutorFilter;
+
+/**
+ * @version $Rev$ $Date$
+ * @org.apache.xbean.XBean
+ */
+public class SocketAcceptor extends org.apache.mina.transport.socket.nio.SocketAcceptor
+{
+
+    private static final int DEFAULT_THREADS = 10;
+
+    public SocketAcceptor( Executor logicExecutor )
+    {
+        super( Runtime.getRuntime().availableProcessors(), Executors.newCachedThreadPool());
+        
+        if ( logicExecutor == null )
+        {
+            logicExecutor = Executors.newFixedThreadPool( DEFAULT_THREADS );
+        }
+        
+        getFilterChain().addLast( "executor", new ExecutorFilter( logicExecutor ) );
+    }
+
+    public void bind( SocketAddress address, IoHandler ioHandler, IoServiceConfig tcpConfig ) throws IOException
+    {
+        tcpConfig.setThreadModel( ThreadModel.MANUAL );
+        super.bind( address, ioHandler, tcpConfig );
+    }
+
+    public void unbind( SocketAddress address )
+    {
+        super.unbind(address);
+    }
+}
diff --git a/old_trunk/protocol-shared/src/main/java/org/apache/directory/server/protocol/shared/StandardThreadPool.java b/old_trunk/protocol-shared/src/main/java/org/apache/directory/server/protocol/shared/StandardThreadPool.java
new file mode 100644
index 0000000..b8c8bdf
--- /dev/null
+++ b/old_trunk/protocol-shared/src/main/java/org/apache/directory/server/protocol/shared/StandardThreadPool.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+
+package org.apache.directory.server.protocol.shared;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @version $Rev$ $Date$
+ * @org.apache.xbean.XBean
+ */
+public class StandardThreadPool implements Executor
+{
+
+    private final ExecutorService delegate;
+
+
+    public StandardThreadPool( int maxThreads )
+    {
+        delegate = Executors.newFixedThreadPool( maxThreads );
+    }
+
+    public void execute( Runnable command )
+    {
+        delegate.execute( command );
+    }
+
+    /**
+     * TODO wont this hang if some tasks are sufficiently badly behaved?
+     * @org.apache.xbean.DestroyMethod
+     */
+    public void stop()
+    {
+        delegate.shutdown();
+        for ( ; ; )
+        {
+            try
+            {
+                if ( delegate.awaitTermination( Integer.MAX_VALUE, TimeUnit.SECONDS ) )
+                {
+                    break;
+                }
+            }
+            catch ( InterruptedException e )
+            {
+                //ignore
+            }
+        }
+
+    }
+}
diff --git a/old_trunk/protocol-shared/src/main/java/org/apache/directory/server/protocol/shared/TransportProtocol.java b/old_trunk/protocol-shared/src/main/java/org/apache/directory/server/protocol/shared/TransportProtocol.java
new file mode 100644
index 0000000..9a97f12
--- /dev/null
+++ b/old_trunk/protocol-shared/src/main/java/org/apache/directory/server/protocol/shared/TransportProtocol.java
@@ -0,0 +1,59 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements.  See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership.  The ASF licenses this file

+ * to you 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.

+ */

+package org.apache.directory.server.protocol.shared;

+

+

+/**

+ * Type safe enumeration for the transport protocol.

+ *

+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>

+ * @version $Rev$, $Date$

+ */

+public enum TransportProtocol

+{

+    TCP( 0, "TCP" ), UDP( 1, "UDP" );

+

+

+    private final int intValue;

+    private final String stringValue;

+

+

+    private TransportProtocol( int intValue, String stringValue )

+    {

+        this.intValue = intValue;

+        this.stringValue = stringValue;

+    }

+

+

+    /**

+     * Gets an integer value for switches.

+     *

+     * @return ordinal integer value

+     */

+    public int getIntValue()

+    {

+        return intValue;

+    }

+

+

+    public String toString()

+    {

+        return stringValue;

+    }

+}

diff --git a/old_trunk/protocol-shared/src/main/java/org/apache/directory/server/protocol/shared/catalog/Catalog.java b/old_trunk/protocol-shared/src/main/java/org/apache/directory/server/protocol/shared/catalog/Catalog.java
new file mode 100644
index 0000000..6c4ac55
--- /dev/null
+++ b/old_trunk/protocol-shared/src/main/java/org/apache/directory/server/protocol/shared/catalog/Catalog.java
@@ -0,0 +1,39 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.protocol.shared.catalog;
+
+
+/**
+ * The interface for resolving search-base DN's from zone or realm names.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public interface Catalog
+{
+    /**
+     * Returns the base DN for searches, given a zone or realm name.
+     *
+     * @param name
+     * @return The base DN for searches.
+     */
+    public String getBaseDn( String name );
+}
diff --git a/old_trunk/protocol-shared/src/main/java/org/apache/directory/server/protocol/shared/catalog/GetCatalog.java b/old_trunk/protocol-shared/src/main/java/org/apache/directory/server/protocol/shared/catalog/GetCatalog.java
new file mode 100644
index 0000000..f0eda8c
--- /dev/null
+++ b/old_trunk/protocol-shared/src/main/java/org/apache/directory/server/protocol/shared/catalog/GetCatalog.java
@@ -0,0 +1,82 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+
+package org.apache.directory.server.protocol.shared.catalog;
+
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.naming.Name;
+import javax.naming.NamingEnumeration;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+
+import org.apache.directory.server.constants.ApacheSchemaConstants;
+import org.apache.directory.server.protocol.shared.store.ContextOperation;
+
+
+/**
+ * A JNDI context operation for building a catalog.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class GetCatalog implements ContextOperation
+{
+    private static final long serialVersionUID = -6657995003127926278L;
+    private static final String ENTRY = ApacheSchemaConstants.APACHE_CATALOG_ENTRY_OC;
+    private static final String ENTRY_NAME = "apacheCatalogEntryName";
+    private static final String ENTRY_BASEDN = "apacheCatalogEntryBaseDn";
+
+
+    /**
+     * Note that the base is relative to the existing context.
+     */
+    public Object execute( DirContext ctx, Name base ) throws Exception
+    {
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+
+        String filter = "(objectClass=" + ENTRY + ")";
+
+        NamingEnumeration<SearchResult> list = ctx.search( "", filter, controls );
+
+        Map<String, String> catalog = new HashMap<String, String>();
+
+        while ( list.hasMore() )
+        {
+            SearchResult result = list.next();
+
+            Attributes attrs = result.getAttributes();
+            Attribute attr;
+
+            String name = ( attr = attrs.get( ENTRY_NAME ) ) != null ? ( String ) attr.get() : null;
+            String basedn = ( attr = attrs.get( ENTRY_BASEDN ) ) != null ? ( String ) attr.get() : null;
+
+            catalog.put( name, basedn );
+        }
+
+        return catalog;
+    }
+}
diff --git a/old_trunk/protocol-shared/src/main/java/org/apache/directory/server/protocol/shared/store/ContextOperation.java b/old_trunk/protocol-shared/src/main/java/org/apache/directory/server/protocol/shared/store/ContextOperation.java
new file mode 100644
index 0000000..e82732b
--- /dev/null
+++ b/old_trunk/protocol-shared/src/main/java/org/apache/directory/server/protocol/shared/store/ContextOperation.java
@@ -0,0 +1,46 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.protocol.shared.store;
+
+
+import java.io.Serializable;
+
+import javax.naming.Name;
+import javax.naming.directory.DirContext;
+
+
+/**
+ * Interface to support the command pattern with JNDI contexts.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public interface ContextOperation extends Serializable
+{
+    /**
+     * The command pattern execute method.
+     * 
+     * @param ctx The context to execute the command with
+     * @param baseDn The base DN for working with the context
+     * @return Object The result returned by the command
+     * @throws Exception The exception thrown by the command
+     */
+    public Object execute( DirContext ctx, Name baseDn ) throws Exception;
+}
diff --git a/old_trunk/protocol-shared/src/main/java/org/apache/directory/server/protocol/shared/store/LdifFileLoader.java b/old_trunk/protocol-shared/src/main/java/org/apache/directory/server/protocol/shared/store/LdifFileLoader.java
new file mode 100644
index 0000000..b472902
--- /dev/null
+++ b/old_trunk/protocol-shared/src/main/java/org/apache/directory/server/protocol/shared/store/LdifFileLoader.java
@@ -0,0 +1,331 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.protocol.shared.store;
+
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.Collections;
+import java.util.List;
+import java.util.Properties;
+
+import javax.naming.CompoundName;
+import javax.naming.Name;
+import javax.naming.NamingException;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.ModificationItem;
+
+import org.apache.directory.shared.ldap.ldif.LdifEntry;
+import org.apache.directory.shared.ldap.ldif.LdifReader;
+import org.apache.directory.shared.ldap.message.ModificationItemImpl;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * Support for commands to load an LDIF file into a DirContext.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class LdifFileLoader
+{
+    /**
+     * the log for this class
+     */
+    private static final Logger log = LoggerFactory.getLogger( LdifFileLoader.class );
+
+    /**
+     * a handle on the top initial context: get new context from this
+     */
+    protected DirContext ctx;
+    /**
+     * the LDIF file or directory containing LDIFs to load
+     */
+    protected File ldif;
+    /**
+     * the filters to use while loading entries into the server
+     */
+    protected final List<LdifLoadFilter> filters;
+    /**
+     * the class loader to use if we cannot file the file as a path
+     */
+    protected final ClassLoader loader;
+    /**
+     * the total count of entries loaded
+     */
+    private int count;
+
+
+    /**
+     * Creates a new instance of LdifFileLoader.
+     *
+     * @param ctx  the context to load the entries into.
+     * @param ldif the file of LDIF entries to load.
+     */
+    public LdifFileLoader( DirContext ctx, String ldif )
+    {
+        this( ctx, new File( ldif ), null );
+    }
+
+
+    /**
+     * Creates a new instance of LdifFileLoader.
+     *
+     * @param ctx
+     * @param ldif
+     * @param filters
+     */
+    public LdifFileLoader( DirContext ctx, File ldif, List<? extends LdifLoadFilter> filters )
+    {
+        this( ctx, ldif, filters, null );
+    }
+
+
+    /**
+     * Creates a new instance of LdifFileLoader.
+     *
+     * @param ctx
+     * @param ldif
+     * @param filters
+     * @param loader
+     */
+    public LdifFileLoader( DirContext ctx, File ldif, List<? extends LdifLoadFilter> filters, ClassLoader loader )
+    {
+        this.ctx = ctx;
+        this.ldif = ldif;
+        this.loader = loader;
+
+        if ( filters == null )
+        {
+            this.filters = Collections.emptyList();
+        } else
+        {
+            this.filters = Collections.unmodifiableList( filters );
+        }
+    }
+
+
+    /**
+     * Applies filters making sure failures in one filter do not effect another.
+     *
+     * @param dn    the DN of the entry
+     * @param entry the attributes of the entry
+     * @return true if all filters passed the entry, false otherwise
+     */
+    private boolean applyFilters( String dn, Attributes entry )
+    {
+        boolean accept = true;
+        final int limit = filters.size();
+
+        if ( limit == 0 )
+        {
+            return true;
+        } // don't waste time with loop
+
+        for ( int ii = 0; ii < limit; ii++ )
+        {
+            try
+            {
+                accept &= ( filters.get( ii ) ).filter( ldif, dn, entry, ctx );
+            }
+            catch ( NamingException e )
+            {
+                log.warn( "filter " + filters.get( ii ) + " was bypassed due to failures", e );
+            }
+
+            // early bypass if entry is rejected
+            if ( !accept )
+            {
+                return false;
+            }
+        }
+        return true;
+    }
+
+
+    /**
+     * Opens the LDIF file and loads the entries into the context.
+     *
+     * @return The count of entries created.
+     */
+    public int execute()
+    {
+        Name rdn = null;
+        InputStream in = null;
+
+        try
+        {
+            in = getLdifStream();
+
+            for ( LdifEntry ldifEntry:new LdifReader( new BufferedReader( new InputStreamReader( in ) ) ) )
+            {
+                String dn = ldifEntry.getDn();
+
+                if ( ldifEntry.isEntry() )
+                {
+                    Attributes attributes = ldifEntry.getAttributes();
+                    boolean filterAccepted = applyFilters( dn, attributes );
+
+                    if ( !filterAccepted )
+                    {
+                        continue;
+                    }
+
+                    rdn = getRelativeName( ctx, dn );
+
+                    try
+                    {
+                        ctx.lookup( rdn );
+                        log.info( "Found {}, will not create.", rdn );
+                    }
+                    catch ( Exception e )
+                    {
+                        try
+                        {
+                            ctx.createSubcontext( rdn, attributes );
+                            count++;
+                            log.info( "Created {}.", rdn );
+                        } catch ( NamingException e1 )
+                        {
+                            log.info( "Could not create: " + dn + " with attributes: " + attributes, e1 );
+                        }
+                    }
+                } else
+                {
+                    //modify
+                    List<ModificationItemImpl> items = ldifEntry.getModificationItems();
+                    try
+                    {
+                        ctx.modifyAttributes( dn, items.toArray( new ModificationItem[items.size()] ) );
+                        log.info( "Modified: " + dn + " with modificationItems: " + items );
+                    }
+                    catch ( NamingException e )
+                    {
+                        log.info( "Could not modify: " + dn + " with modificationItems: " + items, e );
+                    }
+                }
+            }
+        }
+        catch ( FileNotFoundException fnfe )
+        {
+            log.error( "LDIF file does not exist." );
+        }
+        catch ( Exception ioe )
+        {
+            log.error( "Failed to import LDIF into backing store.", ioe );
+        }
+        finally
+        {
+            if ( in != null )
+            {
+                try
+                {
+                    in.close();
+                }
+                catch ( Exception e )
+                {
+                    log.error( "failed to close stream", e );
+                }
+            }
+        }
+
+        return count;
+    }
+
+
+    private Name getRelativeName( DirContext ctx, String baseDn ) throws NamingException
+    {
+        Properties props = new Properties();
+        props.setProperty( "jndi.syntax.direction", "right_to_left" );
+        props.setProperty( "jndi.syntax.separator", "," );
+        props.setProperty( "jndi.syntax.ignorecase", "true" );
+        props.setProperty( "jndi.syntax.trimblanks", "true" );
+
+        Name searchBaseDn;
+
+        try
+        {
+            Name ctxRoot = new CompoundName( ctx.getNameInNamespace(), props );
+            searchBaseDn = new CompoundName( baseDn, props );
+
+            if ( !searchBaseDn.startsWith( ctxRoot ) )
+            {
+                throw new NamingException( "Invalid search base " + baseDn );
+            }
+
+            for ( int ii = 0; ii < ctxRoot.size(); ii++ )
+            {
+                searchBaseDn.remove( 0 );
+            }
+        }
+        catch ( NamingException e )
+        {
+            throw new NamingException( "Failed to initialize search base " + baseDn );
+        }
+
+        return searchBaseDn;
+    }
+
+
+    /**
+     * Tries to find an LDIF file either on the file system or packaged within a jar.
+     *
+     * @return the input stream to the ldif file.
+     * @throws FileNotFoundException if the file cannot be found.
+     */
+    private InputStream getLdifStream() throws FileNotFoundException
+    {
+        InputStream in;
+
+        if ( ldif.exists() )
+        {
+            in = new FileInputStream( ldif );
+        } else
+        {
+            if ( loader != null && ( in = loader.getResourceAsStream( ldif.getName() ) ) != null )
+            {
+                return in;
+            }
+
+            // if file not on system see if something is bundled with the jar ...
+            in = getClass().getResourceAsStream( ldif.getName() );
+            if ( in != null )
+            {
+                return in;
+            }
+
+            in = ClassLoader.getSystemResourceAsStream( ldif.getName() );
+            if ( in != null )
+            {
+                return in;
+            }
+
+            throw new FileNotFoundException( "LDIF file does not exist." );
+        }
+
+        return in;
+    }
+}
diff --git a/old_trunk/protocol-shared/src/main/java/org/apache/directory/server/protocol/shared/store/LdifLoadFilter.java b/old_trunk/protocol-shared/src/main/java/org/apache/directory/server/protocol/shared/store/LdifLoadFilter.java
new file mode 100644
index 0000000..531f797
--- /dev/null
+++ b/old_trunk/protocol-shared/src/main/java/org/apache/directory/server/protocol/shared/store/LdifLoadFilter.java
@@ -0,0 +1,48 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.protocol.shared.store;
+
+
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.NamingException;
+import java.io.File;
+
+
+/**
+ * A filter interface for the LDIF loader.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public interface LdifLoadFilter
+{
+    /**
+     * Filters entries loaded from LDIF files by a LdifFileLoader.
+     *
+     * @param file the file being loaded
+     * @param dn the distinguished name of the entry being loaded
+     * @param entry the entry attributes within the LDIF file
+     * @param ctx context to be used for loading the entry into the DIT
+     * @return true if the entry will be created in the DIT, false if it is to be skipped
+     * @throws NamingException
+     */
+    boolean filter( File file, String dn, Attributes entry, DirContext ctx ) throws NamingException;
+}
diff --git a/old_trunk/protocol-shared/src/site/site.xml b/old_trunk/protocol-shared/src/site/site.xml
new file mode 100644
index 0000000..9ec7c16
--- /dev/null
+++ b/old_trunk/protocol-shared/src/site/site.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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.
+-->
+<!--
+  @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+-->
+<project name="${project.name}">
+    <bannerLeft>
+        <name>${project.name}</name>
+    </bannerLeft>
+    <publishDate position="navigation-bottom" format="dd-MM-yyyy HH:mm" />
+    <body>
+        <menu name="Parent">
+            <item name="Apache Directory ApacheDS" href="../index.html" />
+        </menu>
+        <menu ref="reports" />
+    </body>
+</project>
diff --git a/old_trunk/protocol-shared/src/test/java/org/apache/directory/server/protocol/shared/catalog/configuration-dns.ldif b/old_trunk/protocol-shared/src/test/java/org/apache/directory/server/protocol/shared/catalog/configuration-dns.ldif
new file mode 100644
index 0000000..81b094e
--- /dev/null
+++ b/old_trunk/protocol-shared/src/test/java/org/apache/directory/server/protocol/shared/catalog/configuration-dns.ldif
@@ -0,0 +1,83 @@
+#
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you 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. 
+#
+#   EXAMPLE.COM is reserved for testing according to this RFC:
+#
+#   http://www.rfc-editor.org/rfc/rfc2606.txt
+#
+
+version: 1
+dn: ou=configuration,ou=system
+objectClass: top
+objectClass: organizationalUnit
+ou: configuration
+
+dn: ou=services,ou=configuration,ou=system
+objectClass: top
+objectClass: organizationalUnit
+ou: services
+
+dn: cn=dns,ou=services,ou=configuration,ou=system
+objectClass: top
+objectClass: apacheFactoryConfiguration
+cn: dns
+apacheServicePid: org.apache.dns.factory
+
+dn: cn=org.apache.dns.1,cn=dns,ou=services,ou=configuration,ou=system
+objectClass: apacheServiceConfiguration
+objectClass: extensibleObject
+cn: org.apache.dns.1
+ipAddress: 192.168.0.1
+ipPort: 53
+baseDn: dc=example,dc=com
+apacheServicePid: org.apache.dns.1
+apacheServiceFactoryPid: org.apache.dns.factory
+
+dn: ou=catalog,cn=org.apache.dns.1,cn=dns,ou=services,ou=configuration,ou=system
+objectClass: top
+objectClass: organizationalUnit
+ou: catalog
+
+dn: cn=example.com,ou=catalog,cn=org.apache.dns.1,cn=dns,ou=services,ou=configuration,ou=system
+objectClass: top
+objectClass: apacheCatalogEntry
+cn: example.com
+apacheCatalogEntryName: example.com
+apacheCatalogEntryBaseDn: ou=zones,dc=example,dc=com
+
+dn: cn=org.apache.dns.2,cn=dns,ou=services,ou=configuration,ou=system
+objectClass: apacheServiceConfiguration
+objectClass: extensibleObject
+cn: org.apache.dns.2
+ipAddress:  10.0.0.1
+ipPort: 10000
+baseDn: dc=apache,dc=org
+apacheServicePid: org.apache.dns.2
+apacheServiceFactoryPid: org.apache.dns.factory
+
+dn: ou=catalog,cn=org.apache.dns.2,cn=dns,ou=services,ou=configuration,ou=system
+objectClass: top
+objectClass: organizationalUnit
+ou: catalog
+
+dn: cn=apache.org,ou=catalog,cn=org.apache.dns.2,cn=dns,ou=services,ou=configuration,ou=system
+objectClass: top
+objectClass: apacheCatalogEntry
+cn: apache.org
+apacheCatalogEntryName: apache.org
+apacheCatalogEntryBaseDn: ou=zones,dc=apache,dc=org
diff --git a/old_trunk/protocol-shared/src/test/java/org/apache/directory/server/protocol/shared/kerberos/kerberos-apache.ldif b/old_trunk/protocol-shared/src/test/java/org/apache/directory/server/protocol/shared/kerberos/kerberos-apache.ldif
new file mode 100644
index 0000000..1c10017
--- /dev/null
+++ b/old_trunk/protocol-shared/src/test/java/org/apache/directory/server/protocol/shared/kerberos/kerberos-apache.ldif
@@ -0,0 +1,137 @@
+#
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you 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. 
+#  
+#   EXAMPLE.COM is reserved for testing according to this RFC:
+#
+#   http://www.rfc-editor.org/rfc/rfc2606.txt
+#
+
+dn: ou=users,dc=apache,dc=org
+objectclass: top
+objectclass: organizationalunit
+ou: users
+
+dn: uid=akarasulu,ou=users,dc=apache,dc=org
+cn: Alex Karasulu
+sn: Karasulu
+givenname: Alex
+objectclass: top
+objectclass: person
+objectclass: organizationalPerson
+objectclass: inetOrgPerson
+objectclass: krb5Principal
+objectclass: krb5KDCEntry
+ou: Directory
+ou: users
+l: Jacksonville
+uid: akarasulu
+krb5PrincipalName: akarasulu@APACHE.ORG
+krb5KeyVersionNumber: 0
+mail: akarasulu@apache.org
+telephonenumber: +1 904 982 6882
+facsimiletelephonenumber: +1 904 982 6883
+roomnumber: 666
+userpassword: maxwell
+
+dn: uid=erodriguez,ou=users,dc=apache,dc=org
+cn: Enrique Rodriguez
+sn: Rodriguez
+givenname: Enrique
+objectclass: top
+objectclass: person
+objectclass: organizationalPerson
+objectclass: inetOrgPerson
+objectclass: krb5Principal
+objectclass: krb5KDCEntry
+ou: Directory
+ou: users
+l: Boston
+uid: erodriguez
+krb5PrincipalName: erodriguez@APACHE.ORG
+krb5KeyVersionNumber: 0
+mail: erodriguez@apache.org
+telephonenumber: +1 408 555 9187
+facsimiletelephonenumber: +1 408 555 8473
+roomnumber: 667
+userpassword: kerby
+
+dn: uid=krbtgt,ou=users,dc=apache,dc=org
+cn: Kerberos Server
+sn: Server
+givenname: Kerberos
+objectclass: top
+objectclass: person
+objectclass: organizationalPerson
+objectclass: inetOrgPerson
+objectclass: krb5Principal
+objectclass: krb5KDCEntry
+ou: Directory
+ou: users
+l: Boston
+uid: krbtgt
+krb5PrincipalName: krbtgt/APACHE.ORG@APACHE.ORG
+krb5KeyVersionNumber: 0
+mail: erodriguez@apache.org
+telephonenumber: +1 408 555 9187
+facsimiletelephonenumber: +1 408 555 8473
+roomnumber: 667
+userpassword: kahuna
+
+dn: uid=hostssh,ou=users,dc=apache,dc=org
+cn: SSH Service
+sn: Service
+givenname: SSH
+objectclass: top
+objectclass: person
+objectclass: organizationalPerson
+objectclass: inetOrgPerson
+objectclass: krb5Principal
+objectclass: krb5KDCEntry
+ou: Directory
+ou: users
+l: Boston
+uid: hostssh
+krb5PrincipalName: host/www.apache.org@APACHE.ORG
+krb5KeyVersionNumber: 0
+mail: erodriguez@apache.org
+telephonenumber: +1 408 555 9187
+facsimiletelephonenumber: +1 408 555 8473
+roomnumber: 667
+userpassword: randall
+
+dn: uid=kadmin,ou=users,dc=apache,dc=org
+cn: Changepw Server
+sn: Server
+givenname: Changepw
+objectclass: top
+objectclass: person
+objectclass: organizationalPerson
+objectclass: inetOrgPerson
+objectclass: krb5Principal
+objectclass: krb5KDCEntry
+ou: Directory
+ou: users
+l: Boston
+uid: kadmin
+krb5PrincipalName: kadmin/changepw@APACHE.ORG
+krb5KeyVersionNumber: 0
+mail: erodriguez@apache.org
+telephonenumber: +1 408 555 9187
+facsimiletelephonenumber: +1 408 555 8473
+roomnumber: 667
+userpassword: kahuna
diff --git a/old_trunk/protocol-shared/src/test/java/org/apache/directory/server/protocol/shared/kerberos/kerberos-example.ldif b/old_trunk/protocol-shared/src/test/java/org/apache/directory/server/protocol/shared/kerberos/kerberos-example.ldif
new file mode 100644
index 0000000..0db46b6
--- /dev/null
+++ b/old_trunk/protocol-shared/src/test/java/org/apache/directory/server/protocol/shared/kerberos/kerberos-example.ldif
@@ -0,0 +1,159 @@
+#
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you 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. 
+#  
+#   EXAMPLE.COM is reserved for testing according to this RFC:
+#
+#   http://www.rfc-editor.org/rfc/rfc2606.txt
+#
+
+dn: ou=users,dc=example,dc=com
+objectclass: top
+objectclass: organizationalunit
+ou: users
+
+dn: uid=akarasulu,ou=users,dc=example,dc=com
+cn: Alex Karasulu
+sn: Karasulu
+givenname: Alex
+objectclass: top
+objectclass: person
+objectclass: organizationalPerson
+objectclass: inetOrgPerson
+objectclass: krb5Principal
+objectclass: krb5KDCEntry
+ou: Directory
+ou: users
+l: Jacksonville
+uid: akarasulu
+krb5PrincipalName: akarasulu@EXAMPLE.COM
+krb5KeyVersionNumber: 0
+mail: akarasulu@example.com
+telephonenumber: +1 904 982 6882
+facsimiletelephonenumber: +1 904 982 6883
+roomnumber: 666
+userpassword: maxwell
+
+dn: uid=erodriguez,ou=users,dc=example,dc=com
+cn: Enrique Rodriguez
+sn: Rodriguez
+givenname: Enrique
+objectclass: top
+objectclass: person
+objectclass: organizationalPerson
+objectclass: inetOrgPerson
+objectclass: krb5Principal
+objectclass: krb5KDCEntry
+ou: Directory
+ou: users
+l: Boston
+uid: erodriguez
+krb5PrincipalName: erodriguez@EXAMPLE.COM
+krb5KeyVersionNumber: 0
+mail: erodriguez@example.com
+telephonenumber: +1 408 555 9187
+facsimiletelephonenumber: +1 408 555 8473
+roomnumber: 667
+userpassword: kerby
+
+dn: uid=krbtgt,ou=users,dc=example,dc=com
+cn: Kerberos Server
+sn: Server
+givenname: Kerberos
+objectclass: top
+objectclass: person
+objectclass: organizationalPerson
+objectclass: inetOrgPerson
+objectclass: krb5Principal
+objectclass: krb5KDCEntry
+ou: Directory
+ou: users
+l: Boston
+uid: krbtgt
+krb5PrincipalName: krbtgt/EXAMPLE.COM@EXAMPLE.COM
+krb5KeyVersionNumber: 0
+mail: erodriguez@example.com
+telephonenumber: +1 408 555 9187
+facsimiletelephonenumber: +1 408 555 8473
+roomnumber: 667
+userpassword: kahuna
+
+dn: uid=hostssh,ou=users,dc=example,dc=com
+cn: SSH Service
+sn: Service
+givenname: SSH
+objectclass: top
+objectclass: person
+objectclass: organizationalPerson
+objectclass: inetOrgPerson
+objectclass: krb5Principal
+objectclass: krb5KDCEntry
+ou: Directory
+ou: users
+l: Boston
+uid: hostssh
+krb5PrincipalName: host/www.example.com@EXAMPLE.COM
+krb5KeyVersionNumber: 0
+mail: erodriguez@example.com
+telephonenumber: +1 408 555 9187
+facsimiletelephonenumber: +1 408 555 8473
+roomnumber: 667
+userpassword: randall
+
+dn: uid=dns1,ou=users,dc=example,dc=com
+cn: DNS Service
+sn: Service
+givenname: DNS
+objectclass: top
+objectclass: person
+objectclass: organizationalPerson
+objectclass: inetOrgPerson
+objectclass: krb5Principal
+objectclass: krb5KDCEntry
+ou: Directory
+ou: users
+l: Boston
+uid: dns1
+krb5PrincipalName: DNS/dns1.icann.org@EXAMPLE.COM
+krb5KeyVersionNumber: 0
+mail: erodriguez@example.com
+telephonenumber: +1 408 555 9187
+facsimiletelephonenumber: +1 408 555 8473
+roomnumber: 667
+userpassword: randall
+
+dn: uid=kadmin,ou=users,dc=example,dc=com
+cn: Changepw Server
+sn: Server
+givenname: Changepw
+objectclass: top
+objectclass: person
+objectclass: organizationalPerson
+objectclass: inetOrgPerson
+objectclass: krb5Principal
+objectclass: krb5KDCEntry
+ou: Directory
+ou: users
+l: Boston
+uid: kadmin
+krb5PrincipalName: kadmin/changepw@EXAMPLE.COM
+krb5KeyVersionNumber: 0
+mail: erodriguez@example.com
+telephonenumber: +1 408 555 9187
+facsimiletelephonenumber: +1 408 555 8473
+roomnumber: 667
+userpassword: kahuna
diff --git a/old_trunk/protocol-shared/src/test/log4j.properties b/old_trunk/protocol-shared/src/test/log4j.properties
new file mode 100644
index 0000000..5c5d15a
--- /dev/null
+++ b/old_trunk/protocol-shared/src/test/log4j.properties
@@ -0,0 +1,25 @@
+#############################################################################
+#    Licensed to the Apache Software Foundation (ASF) under one or more
+#    contributor license agreements.  See the NOTICE file distributed with
+#    this work for additional information regarding copyright ownership.
+#    The ASF licenses this file to You 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.
+#############################################################################
+# Set root logger level to DEBUG and its only appender to A1.
+log4j.rootLogger=INFO, A1
+
+# A1 is set to be a ConsoleAppender.
+log4j.appender.A1=org.apache.log4j.ConsoleAppender
+
+# A1 uses PatternLayout.
+log4j.appender.A1.layout=org.apache.log4j.PatternLayout
+log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n
diff --git a/old_trunk/resources/sh-scripts/apidocs.sh b/old_trunk/resources/sh-scripts/apidocs.sh
new file mode 100755
index 0000000..28fe56d
--- /dev/null
+++ b/old_trunk/resources/sh-scripts/apidocs.sh
@@ -0,0 +1,30 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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. 
+
+
+
+OUTPUTDIR='target/site/docs/api'
+
+SOURCEPATH=$(find . -maxdepth 4 -name src | awk '{ print $1"/main/java" }' | tr '\n' ':')
+
+PACKAGES='org.apache.directory.server'
+
+#EXCLUDES="org.apache.mina.examples:$(grep -h '^package org\.apache\.mina.*support;$' * -R | sed 's/^package \(.*\.support\);/\1/g' | sed 's/\r//g' | tr '\n' ':' | sed 's/:$//g'| sort -u)"
+
+javadoc -d $OUTPUTDIR -sourcepath $SOURCEPATH -subpackages $PACKAGES #-exclude $EXCLUDES 
diff --git a/old_trunk/resources/sh-scripts/groups.sh b/old_trunk/resources/sh-scripts/groups.sh
new file mode 100755
index 0000000..9ab6f81
--- /dev/null
+++ b/old_trunk/resources/sh-scripts/groups.sh
@@ -0,0 +1,46 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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. 
+
+RESOURCES=../../resources
+
+for project in *
+do
+	cd $project &> /dev/null
+	if [ $? -ne 0 ]
+	then
+		continue
+	fi
+	if [ ! -d "src/main/java" ]
+	then
+		cd - &> /dev/null
+		continue
+	fi
+	ROOTPACKAGENAME=$(grep -h '^package org\.apache\.directory\.server.*;$' * -R | sed 's/\r//g' | sort -u | head -1 | sed 's/\(package \)//' | tr -d ';')
+	if [ ! -f pom.xml ]
+	then
+		cd - &> /dev/null
+		continue
+	fi
+	NAME=$($RESOURCES/pomutils/name.sh ./pom.xml)
+
+	echo $ROOTPACKAGENAME
+	echo $NAME
+	echo "----------------------------------------------------------"
+	cd - &> /dev/null
+done
diff --git a/old_trunk/schema-bootstrap/pom.xml b/old_trunk/schema-bootstrap/pom.xml
new file mode 100644
index 0000000..f181b70
--- /dev/null
+++ b/old_trunk/schema-bootstrap/pom.xml
@@ -0,0 +1,108 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you 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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.directory.server</groupId>
+    <artifactId>apacheds-parent</artifactId>
+    <version>1.5.4-SNAPSHOT</version>
+  </parent>
+  <artifactId>apacheds-schema-bootstrap</artifactId>
+  <name>ApacheDS Bootstrap Schemas</name>
+
+  <description>
+    The minimal set of schemas needed to bootstrap the server's special schema
+    partition which is used to store all the schemas for the server.
+  </description>
+
+  <packaging>jar</packaging>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-schema-registries</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-core-shared</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-core-constants</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>${pom.groupId}</groupId>
+      <version>${pom.version}</version>
+      <artifactId>apacheds-jdbm</artifactId>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.directory.server</groupId>
+        <artifactId>apacheds-core-plugin</artifactId>
+        <version>${pom.version}</version>
+        <configuration>
+          <schemaSourcesDir>src/main/schema</schemaSourcesDir>
+          <schemas>
+            <schema>
+              <name>apache</name>
+              <dependencies>
+                <dependency>system</dependency>
+              </dependencies>
+            </schema>
+            <schema>
+              <name>apachemeta</name>
+              <dependencies>
+                <dependency>system</dependency>
+              </dependencies>
+            </schema>
+             <schema>
+              <name>core</name>
+              <dependencies>
+                <dependency>system</dependency>
+              </dependencies>
+            </schema>
+            <schema>
+              <name>system</name>
+            </schema>
+           </schemas>
+        </configuration>
+         <executions>
+          <execution>
+            <goals>
+              <goal>generate</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+</project>
+
diff --git a/old_trunk/schema-bootstrap/src/main/appended-resources/META-INF/LICENSE b/old_trunk/schema-bootstrap/src/main/appended-resources/META-INF/LICENSE
new file mode 100644
index 0000000..c4769e8
--- /dev/null
+++ b/old_trunk/schema-bootstrap/src/main/appended-resources/META-INF/LICENSE
@@ -0,0 +1,49 @@
+----------------------------------------------------------------
+
+The OpenLDAP Public License
+  Version 2.8, 17 August 2003
+
+Redistribution and use of this software and associated documentation
+("Software"), with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Redistributions in source form must retain copyright statements
+   and notices,
+
+2. Redistributions in binary form must reproduce applicable copyright
+   statements and notices, this list of conditions, and the following
+   disclaimer in the documentation and/or other materials provided
+   with the distribution, and
+
+3. Redistributions must contain a verbatim copy of this document.
+
+The OpenLDAP Foundation may revise this license from time to time.
+Each revision is distinguished by a version number.  You may use
+this Software under terms of this license revision or under the
+terms of any subsequent revision of the license.
+
+THIS SOFTWARE IS PROVIDED BY THE OPENLDAP FOUNDATION AND ITS
+CONTRIBUTORS ``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 THE OPENLDAP FOUNDATION, ITS CONTRIBUTORS, OR THE AUTHOR(S)
+OR OWNER(S) OF THE 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.
+
+The names of the authors and copyright holders must not be used in
+advertising or otherwise to promote the sale, use or other dealing
+in this Software without specific, written prior permission.  Title
+to copyright in this Software shall at all times remain with copyright
+holders.
+
+OpenLDAP is a registered trademark of the OpenLDAP Foundation.
+
+Copyright 1999-2003 The OpenLDAP Foundation, Redwood City,
+California, USA.  All Rights Reserved.  Permission to copy and
+distribute verbatim copies of this document is granted.
diff --git a/old_trunk/schema-bootstrap/src/main/appended-resources/META-INF/NOTICE b/old_trunk/schema-bootstrap/src/main/appended-resources/META-INF/NOTICE
new file mode 100644
index 0000000..fd458ff
--- /dev/null
+++ b/old_trunk/schema-bootstrap/src/main/appended-resources/META-INF/NOTICE
@@ -0,0 +1,66 @@
+
+
+Copyright 1998-2008 The OpenLDAP Foundation
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted only as authorized by the OpenLDAP
+Public License.
+
+A copy of this license is available in the file LICENSE in the
+top-level directory of the distribution or, alternatively, at
+<http://www.OpenLDAP.org/license.html>.
+
+OpenLDAP is a registered trademark of the OpenLDAP Foundation.
+
+Individual files and/or contributed packages may be copyright by
+other parties and/or subject to additional restrictions.
+
+This work is derived from the University of Michigan LDAP v3.3
+distribution.  Information concerning this software is available
+at <http://www.umich.edu/~dirsvcs/ldap/ldap.html>.
+
+This work also contains materials derived from public sources.
+
+Additional information about OpenLDAP can be obtained at
+<http://www.openldap.org/>.
+
+---
+
+Portions Copyright 1998-2008 Kurt D. Zeilenga.
+Portions Copyright 1998-2006 Net Boolean Incorporated.
+Portions Copyright 2001-2006 IBM Corporation.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted only as authorized by the OpenLDAP
+Public License.
+
+---
+
+Portions Copyright 1999-2007 Howard Y.H. Chu.
+Portions Copyright 1999-2007 Symas Corporation.
+Portions Copyright 1998-2003 Hallvard B. Furuseth.
+Portions Copyright 2007 Gavin Henry
+Portions Copyright 2007 Suretec Systems
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that this notice is preserved.
+The names of the copyright holders may not be used to endorse or
+promote products derived from this software without their specific
+prior written permission.  This software is provided ``as is''
+without express or implied warranty.
+
+---
+
+Portions Copyright (c) 1992-1996 Regents of the University of Michigan.
+All rights reserved.
+
+Redistribution and use in source and binary forms are permitted
+provided that this notice is preserved and that due credit is given
+to the University of Michigan at Ann Arbor.  The name of the
+University may not be used to endorse or promote products derived
+from this software without specific prior written permission.  This
+software is provided ``as is'' without express or implied warranty.
+
diff --git a/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/AbstractBootstrapProducer.java b/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/AbstractBootstrapProducer.java
new file mode 100755
index 0000000..f88a4a8
--- /dev/null
+++ b/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/AbstractBootstrapProducer.java
@@ -0,0 +1,618 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.bootstrap;
+
+
+import java.util.Comparator;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
+import org.apache.directory.server.schema.registries.ComparatorRegistry;
+import org.apache.directory.server.schema.registries.MatchingRuleRegistry;
+import org.apache.directory.server.schema.registries.NormalizerRegistry;
+import org.apache.directory.server.schema.registries.ObjectClassRegistry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.server.schema.registries.SyntaxCheckerRegistry;
+import org.apache.directory.server.schema.registries.SyntaxRegistry;
+import org.apache.directory.shared.ldap.schema.AbstractAttributeType;
+import org.apache.directory.shared.ldap.schema.AbstractMatchingRule;
+import org.apache.directory.shared.ldap.schema.AbstractSchemaObject;
+import org.apache.directory.shared.ldap.schema.AbstractSyntax;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.schema.MatchingRule;
+import org.apache.directory.shared.ldap.schema.Normalizer;
+import org.apache.directory.shared.ldap.schema.ObjectClass;
+import org.apache.directory.shared.ldap.schema.ObjectClassTypeEnum;
+import org.apache.directory.shared.ldap.schema.Syntax;
+import org.apache.directory.shared.ldap.schema.UsageEnum;
+import org.apache.directory.shared.ldap.schema.syntax.SyntaxChecker;
+
+
+/**
+ * An abstract producer implementation.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public abstract class AbstractBootstrapProducer implements BootstrapProducer
+{
+    /** a reused empty String array */
+    protected static final String[] EMPTY = new String[0];
+    /** the producer type */
+    private final ProducerTypeEnum type;
+
+
+    /**
+     * Creates a producer of a specific type.
+     *
+     * @param type the producer type
+     */
+    protected AbstractBootstrapProducer(ProducerTypeEnum type)
+    {
+        this.type = type;
+    }
+
+
+    /**
+     * @see BootstrapProducer#getType()
+     */
+    public ProducerTypeEnum getType()
+    {
+        return type;
+    }
+
+
+    protected static BootstrapSyntax newSyntax( String oid, Registries registries )
+    {
+        return new BootstrapSyntax( oid, registries.getSyntaxCheckerRegistry() );
+    }
+
+
+    protected static BootstrapAttributeType newAttributeType( String oid, Registries registries )
+    {
+        return new BootstrapAttributeType( oid, registries );
+    }
+
+
+    protected static BootstrapObjectClass newObjectClass( String oid, Registries registries )
+    {
+        return new BootstrapObjectClass( oid, registries );
+    }
+
+    
+    /**
+     * A mutable Syntax for the bootstrap phase that uses the
+     * syntaxCheckerRegistry to dynamically resolve syntax checkers.
+     */
+    public static class BootstrapSyntax extends AbstractSyntax
+    {
+        private static final long serialVersionUID = 1L;
+        final SyntaxCheckerRegistry registry;
+
+
+        public BootstrapSyntax(String oid, SyntaxCheckerRegistry registry)
+        {
+            super( oid );
+            this.registry = registry;
+        }
+        
+        
+        public void setSchema( String schema )
+        {
+            super.setSchema( schema );
+        }
+
+
+        public void setDescription( String description )
+        {
+            super.setDescription( description );
+        }
+
+
+        public void setHumanReadable( boolean isHumanReadable )
+        {
+            super.setHumanReadable( isHumanReadable );
+        }
+
+
+        public void setNames( String[] names )
+        {
+            super.setNames( names );
+        }
+
+
+        public SyntaxChecker getSyntaxChecker() throws NamingException
+        {
+            return registry.lookup( getOid() );
+        }
+
+
+        public boolean isObsolete()
+        {
+            return false;
+        }
+    }
+
+    public static class BootstrapMatchingRule extends AbstractMatchingRule
+    {
+        private static final long serialVersionUID = 1L;
+        final SyntaxRegistry syntaxRegistry;
+        final NormalizerRegistry normalizerRegistry;
+        final ComparatorRegistry comparatorRegistry;
+        String syntaxOid;
+
+
+        public BootstrapMatchingRule(String oid, Registries registries)
+        {
+            super( oid );
+            this.syntaxRegistry = registries.getSyntaxRegistry();
+            this.normalizerRegistry = registries.getNormalizerRegistry();
+            this.comparatorRegistry = registries.getComparatorRegistry();
+        }
+
+
+        public void setNames( String[] names )
+        {
+            super.setNames( names );
+        }
+
+
+        public void setSchema( String schema )
+        {
+            super.setSchema( schema );
+        }
+
+
+        public void setSyntaxOid( String syntaxOid )
+        {
+            this.syntaxOid = syntaxOid;
+        }
+
+
+        public void setDescription( String description )
+        {
+            super.setDescription( description );
+        }
+
+
+        public void setObsolete( boolean isObsolete )
+        {
+            super.setObsolete( isObsolete );
+        }
+
+
+        // accessors
+
+        public Syntax getSyntax() throws NamingException
+        {
+            return syntaxRegistry.lookup( syntaxOid );
+        }
+
+
+        public Comparator getComparator() throws NamingException
+        {
+            return comparatorRegistry.lookup( getOid() );
+        }
+
+
+        public Normalizer getNormalizer() throws NamingException
+        {
+            return normalizerRegistry.lookup( getOid() );
+        }
+    }
+
+    /**
+     * A concrete mutable attributeType implementation for bootstrapping which
+     * uses registries for dynamically resolving dependent objects.
+     */
+    public static class BootstrapAttributeType extends AbstractAttributeType
+    {
+        private static final long serialVersionUID = 4050205236738471984L;
+
+        private final SyntaxRegistry syntaxRegistry;
+        private final MatchingRuleRegistry matchingRuleRegistry;
+        private final AttributeTypeRegistry attributeTypeRegistry;
+        private String superiorId;
+        
+        /** The equality OID for this AttributeType */
+        private String equalityId;
+
+        /** The MatchingRule associated with the equalityID */
+        private MatchingRule equalityMR;
+        
+        /** The substring OID for this AttributeType */
+        private String substrId;
+        
+        /** The MatchingRule associated with the substrID */
+        private MatchingRule substrMR;
+        
+        /** The ordering OID for this AttributeType */
+        private String orderingId;
+        
+        /** The MatchingRule associated with the orderingID */
+        private MatchingRule orderingMR;
+
+        /** The syntax OID for this attributeType */
+        private String syntaxId;
+        
+        /** The Syntax associated with the syntaxID */
+        private Syntax syntax;
+
+
+        public BootstrapAttributeType(String oid, Registries registries)
+        {
+            super( oid );
+
+            syntaxRegistry = registries.getSyntaxRegistry();
+            matchingRuleRegistry = registries.getMatchingRuleRegistry();
+            attributeTypeRegistry = registries.getAttributeTypeRegistry();
+        }
+
+
+        public void setSuperiorId( String superiorId )
+        {
+            this.superiorId = superiorId;
+        }
+
+
+        public void setSchema( String schema )
+        {
+            super.setSchema( schema );
+        }
+
+
+        public AttributeType getSuperior() throws NamingException
+        {
+            if ( superiorId == null )
+            {
+                return null;
+            }
+
+            return this.attributeTypeRegistry.lookup( superiorId );
+        }
+
+
+        public void setNames( String[] names )
+        {
+            super.setNames( names );
+        }
+
+
+        /**
+         * @return The MatchingRule associated with the AttributeType
+         */
+        public MatchingRule getEquality() throws NamingException
+        {
+            if ( equalityMR == null )
+            {
+                if ( equalityId != null )
+                {
+                    equalityMR = this.matchingRuleRegistry.lookup( equalityId );
+                }
+                else if ( superiorId != null )
+                {
+                    equalityMR = getSuperior().getEquality();
+                }
+            }
+
+            return equalityMR;
+        }
+
+
+        public void setEqualityId( String equalityId )
+        {
+            this.equalityId = equalityId;
+        }
+
+
+        public MatchingRule getSubstr() throws NamingException
+        {
+            if ( substrMR == null )
+            {
+                if ( substrId != null )
+                {
+                    substrMR = matchingRuleRegistry.lookup( substrId );
+                }
+                else if ( superiorId != null )
+                {
+                    substrMR = getSuperior().getSubstr();
+                }
+            }
+
+            return substrMR;
+        }
+
+
+        public boolean isAncestorOf( AttributeType attributeType ) throws NamingException
+        {
+            return false;
+        }
+
+
+        public boolean isDescentantOf( AttributeType attributeType ) throws NamingException
+        {
+            return false;
+        }
+
+
+        public void setSubstrId( String substrId )
+        {
+            this.substrId = substrId;
+        }
+
+
+        /**
+         * @return The Ordering Matching Rule associated with this AttributeType
+         */
+        public MatchingRule getOrdering() throws NamingException
+        {
+            if ( orderingMR == null )
+            {
+                if ( orderingId != null )
+                {
+                    orderingMR = matchingRuleRegistry.lookup( orderingId );
+                }
+                else if ( superiorId != null )
+                {
+                    orderingMR = getSuperior().getOrdering();
+                }
+            }
+
+            return orderingMR;
+        }
+
+
+        public void setOrderingId( String orderingId )
+        {
+            this.orderingId = orderingId;
+        }
+
+
+        public void setSyntaxId( String syntaxId )
+        {
+            this.syntaxId = syntaxId;
+        }
+
+
+        /**
+         * @return The Syntax associated with the AttributeType
+         */
+        public Syntax getSyntax() throws NamingException
+        {
+            if ( syntax == null )
+            {
+                if ( syntaxId != null )
+                {
+                    syntax = syntaxRegistry.lookup( syntaxId );
+                }
+                else if ( superiorId != null )
+                {
+                    syntax = getSuperior().getSyntax();
+                }
+            }
+
+            return syntax;
+        }
+
+
+        public void setSingleValue( boolean singleValue )
+        {
+            super.setSingleValue( singleValue );
+        }
+
+
+        public void setCollective( boolean collective )
+        {
+            super.setCollective( collective );
+        }
+
+
+        public void setCanUserModify( boolean canUserModify )
+        {
+            super.setCanUserModify( canUserModify );
+        }
+
+
+        public void setObsolete( boolean obsolete )
+        {
+            super.setObsolete( obsolete );
+        }
+
+
+        public void setDescription( String description )
+        {
+            super.setDescription( description );
+        }
+
+
+        public void setUsage( UsageEnum usage )
+        {
+            super.setUsage( usage );
+        }
+
+
+        public void setLength( int length )
+        {
+            super.setLength( length );
+        }
+    }
+
+    /**
+     * A concrete mutable objectClass implementation for bootstrapping which
+     * uses registries for dynamically resolving dependent objects.
+     */
+    public static class BootstrapObjectClass extends AbstractSchemaObject implements ObjectClass
+    {
+        private static final long serialVersionUID = 1L;
+        private final ObjectClassRegistry objectClassRegistry;
+        private final AttributeTypeRegistry attributeTypeRegistry;
+
+        private String[] superClassIds = EMPTY;
+        private ObjectClass[] superClasses;
+        private ObjectClassTypeEnum type = ObjectClassTypeEnum.STRUCTURAL;
+
+        private String[] mayListIds = EMPTY;
+        private AttributeType[] mayList;
+
+        private String[] mustListIds = EMPTY;
+        private AttributeType[] mustList;
+
+
+        /**
+         * Creates a mutable ObjectClass for the bootstrap process.
+         *
+         * @param oid the OID of the new objectClass
+         * @param registries the bootstrap registries to use for resolving dependent objects
+         */
+        public BootstrapObjectClass(String oid, Registries registries)
+        {
+            super( oid );
+
+            objectClassRegistry = registries.getObjectClassRegistry();
+            attributeTypeRegistry = registries.getAttributeTypeRegistry();
+        }
+
+
+        // --------------------------------------------------------------------
+        // ObjectClass Accessors
+        // --------------------------------------------------------------------
+
+        public ObjectClass[] getSuperClasses() throws NamingException
+        {
+            if ( superClasses == null )
+            {
+                superClasses = new ObjectClass[superClassIds.length];
+            }
+
+            for ( int ii = 0; ii < superClassIds.length; ii++ )
+            {
+                superClasses[ii] = objectClassRegistry.lookup( superClassIds[ii] );
+            }
+
+            return superClasses;
+        }
+
+
+        public void setSuperClassIds( String[] superClassIds )
+        {
+            this.superClassIds = superClassIds;
+        }
+
+
+        public ObjectClassTypeEnum getType()
+        {
+            return type;
+        }
+
+        public boolean isStructural()
+        {
+            return type == ObjectClassTypeEnum.STRUCTURAL;
+        }
+
+        public boolean isAbstract()
+        {
+            return type == ObjectClassTypeEnum.ABSTRACT;
+        }
+
+        public boolean isAuxiliary()
+        {
+            return type == ObjectClassTypeEnum.AUXILIARY;
+        }
+
+        public void setSchema( String schema )
+        {
+            super.setSchema( schema );
+        }
+
+
+        public void setType( ObjectClassTypeEnum type )
+        {
+            this.type = type;
+        }
+
+
+        public AttributeType[] getMustList() throws NamingException
+        {
+            if ( mustList == null )
+            {
+                mustList = new AttributeType[mustListIds.length];
+            }
+
+            for ( int ii = 0; ii < mustListIds.length; ii++ )
+            {
+                mustList[ii] = attributeTypeRegistry.lookup( mustListIds[ii] );
+            }
+
+            return mustList;
+        }
+
+
+        public void setMustListIds( String[] mustListIds )
+        {
+            this.mustListIds = mustListIds;
+        }
+
+
+        public AttributeType[] getMayList() throws NamingException
+        {
+            if ( mayList == null )
+            {
+                mayList = new AttributeType[mayListIds.length];
+            }
+
+            for ( int ii = 0; ii < mayListIds.length; ii++ )
+            {
+                mayList[ii] = attributeTypeRegistry.lookup( mayListIds[ii] );
+            }
+
+            return mayList;
+        }
+
+
+        public void setMayListIds( String[] mayListIds )
+        {
+            this.mayListIds = mayListIds;
+        }
+
+
+        // --------------------------------------------------------------------
+        // SchemaObject Mutators
+        // --------------------------------------------------------------------
+
+        public void setObsolete( boolean obsolete )
+        {
+            super.setObsolete( obsolete );
+        }
+
+
+        public void setNames( String[] names )
+        {
+            super.setNames( names );
+        }
+
+
+        public void setDescription( String description )
+        {
+            super.setDescription( description );
+        }
+
+    }
+}
diff --git a/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/ApacheComparatorProducer.java b/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/ApacheComparatorProducer.java
new file mode 100644
index 0000000..f61d9bd
--- /dev/null
+++ b/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/ApacheComparatorProducer.java
@@ -0,0 +1,75 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.bootstrap;
+
+
+import java.util.Comparator;
+
+import javax.naming.NamingException;
+
+import jdbm.helper.StringComparator;
+
+import org.apache.directory.server.schema.bootstrap.ProducerTypeEnum;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.schema.ComparableComparator;
+//import org.apache.directory.shared.ldap.util.BigIntegerComparator;
+import org.apache.directory.shared.ldap.util.LongComparator;
+
+
+/**
+ * A producer of Comparator objects for the eve schema.
+ * Probably modified by hand from generated code
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class ApacheComparatorProducer extends AbstractBootstrapProducer
+{
+    public ApacheComparatorProducer()
+    {
+        super( ProducerTypeEnum.COMPARATOR_PRODUCER );
+    }
+
+
+    // ------------------------------------------------------------------------
+    // BootstrapProducer Methods
+    // ------------------------------------------------------------------------
+
+    /**
+     * @see org.apache.directory.server.schema.bootstrap.BootstrapProducer#produce(org.apache.directory.server.schema.registries.DefaultRegistries, ProducerCallback)
+     */
+    public void produce( Registries registries, ProducerCallback cb ) throws NamingException
+    {
+        Comparator comparator;
+
+        // For exactDnAsStringMatch -> 1.3.6.1.4.1.18060.0.4.1.1.1
+        comparator = new ComparableComparator();
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.18060.0.4.1.1.1", comparator );
+
+        // For bigIntegerMatch -> 1.3.6.1.4.1.18060.0.4.1.1.2
+        comparator = new LongComparator();
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.18060.0.4.1.1.2", comparator );
+
+        // For jdbmStringMatch -> 1.3.6.1.4.1.18060.0.4.1.1.3
+        comparator = new StringComparator();
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.18060.0.4.1.1.3", comparator );
+
+    }
+}
diff --git a/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/ApacheMatchingRuleProducer.java b/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/ApacheMatchingRuleProducer.java
new file mode 100644
index 0000000..286e603
--- /dev/null
+++ b/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/ApacheMatchingRuleProducer.java
@@ -0,0 +1,73 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.bootstrap;
+
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.schema.bootstrap.ProducerTypeEnum;
+import org.apache.directory.server.schema.registries.Registries;
+
+
+/**
+ * A producer of MatchingRule objects for the eve schema. 
+ * Probably modified by hand from generated code
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class ApacheMatchingRuleProducer extends AbstractBootstrapProducer
+{
+    public ApacheMatchingRuleProducer()
+    {
+        super( ProducerTypeEnum.MATCHING_RULE_PRODUCER );
+    }
+
+
+    // ------------------------------------------------------------------------
+    // BootstrapProducer Methods
+    // ------------------------------------------------------------------------
+
+    /**
+     * @see BootstrapProducer#produce(Registries, org.apache.directory.server.schema.bootstrap.ProducerCallback)
+     */
+    public void produce( Registries registries, ProducerCallback cb ) throws NamingException
+    {
+        BootstrapMatchingRule mrule = null;
+        
+        mrule = new BootstrapMatchingRule( "1.3.6.1.4.1.18060.0.4.1.1.1", registries );
+        mrule.setNames( new String[]
+            { "exactDnAsStringMatch" } );
+        mrule.setSyntaxOid( "1.3.6.1.4.1.1466.115.121.1.12" );
+        cb.schemaObjectProduced( this, mrule.getOid(), mrule );
+
+        mrule = new BootstrapMatchingRule( "1.3.6.1.4.1.18060.0.4.1.1.2", registries );
+        mrule.setNames( new String[]
+            { "bigIntegerMatch" } );
+        mrule.setSyntaxOid( "1.3.6.1.4.1.1466.115.121.1.27" );
+        cb.schemaObjectProduced( this, mrule.getOid(), mrule );
+
+        mrule = new BootstrapMatchingRule( "1.3.6.1.4.1.18060.0.4.1.1.3", registries );
+        mrule.setNames( new String[]
+            { "jdbmStringMatch" } );
+        mrule.setSyntaxOid( "1.3.6.1.4.1.1466.115.121.1.15" );
+        cb.schemaObjectProduced( this, mrule.getOid(), mrule );
+    }
+}
diff --git a/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/ApacheNormalizerProducer.java b/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/ApacheNormalizerProducer.java
new file mode 100644
index 0000000..04cd5b7
--- /dev/null
+++ b/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/ApacheNormalizerProducer.java
@@ -0,0 +1,70 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.bootstrap;
+
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.schema.bootstrap.ProducerTypeEnum;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.schema.NoOpNormalizer;
+import org.apache.directory.shared.ldap.schema.Normalizer;
+
+
+/**
+ * A producer of Normalizer objects for the eve schema.
+ * Probably modified by hand from generated code
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class ApacheNormalizerProducer extends AbstractBootstrapProducer
+{
+    public ApacheNormalizerProducer()
+    {
+        super( ProducerTypeEnum.NORMALIZER_PRODUCER );
+    }
+
+
+    // ------------------------------------------------------------------------
+    // BootstrapProducer Methods
+    // ------------------------------------------------------------------------
+
+    /**
+     * @see org.apache.directory.server.schema.bootstrap.BootstrapProducer#produce(org.apache.directory.server.schema.registries.Registries, org.apache.directory.server.schema.bootstrap.ProducerCallback)
+     */
+    public void produce( Registries registries, ProducerCallback cb ) throws NamingException
+    {
+        Normalizer normalizer;
+
+        // For exactDnAsStringMatch -> 1.3.6.1.4.1.18060.0.4.1.1.1
+        normalizer = new NoOpNormalizer();
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.18060.0.4.1.1.1", normalizer );
+
+        // For bigIntegerMatch -> 1.3.6.1.4.1.18060.0.4.1.1.2
+        normalizer = new NoOpNormalizer();
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.18060.0.4.1.1.2", normalizer );
+
+        // For jdbmStringMatch -> 1.3.6.1.4.1.18060.0.4.1.1.3
+        normalizer = new NoOpNormalizer();
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.18060.0.4.1.1.3", normalizer );
+
+    }
+}
diff --git a/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/ApacheSyntaxProducer.java b/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/ApacheSyntaxProducer.java
new file mode 100755
index 0000000..51ce68c
--- /dev/null
+++ b/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/ApacheSyntaxProducer.java
@@ -0,0 +1,158 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.bootstrap;
+
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.schema.AbstractSyntax;
+import org.apache.directory.shared.ldap.schema.syntax.JavaByteSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.JavaIntegerSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.JavaLongSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.JavaShortSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.SyntaxChecker;
+
+
+
+/**
+ * A producer of Syntax objects for the apache schema.  This code has been
+ * automatically generated using schema files in the OpenLDAP format along with
+ * the directory plugin for maven.  This has been done to facilitate
+ * OpenLDAP schema interoperability.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class ApacheSyntaxProducer extends AbstractBootstrapProducer
+{
+    public ApacheSyntaxProducer()
+    {
+        super( ProducerTypeEnum.SYNTAX_PRODUCER );
+    }
+
+
+    // ------------------------------------------------------------------------
+    // BootstrapProducer Methods
+    // ------------------------------------------------------------------------
+
+
+    /**
+     * @see BootstrapProducer#produce(Registries, ProducerCallback)
+     */
+    public void produce( Registries registries, ProducerCallback cb )
+        throws NamingException
+    {
+        AbstractSyntax syntax = null;
+        
+        syntax = new AbstractSyntax( SchemaConstants.JAVA_BYTE_SYNTAX, "a syntax for java byte values", true )
+        {
+            private static final long serialVersionUID = 1L;
+            private final JavaByteSyntaxChecker JAVA_BYTE_SYNTAX_CHECKER = new JavaByteSyntaxChecker();
+
+            public String getName()
+            {
+                return "JAVA_BYTE";
+            }
+            
+            public String[] getNames()
+            {
+                return new String[] { "JAVA_BYTE" };
+            }
+            
+            public SyntaxChecker getSyntaxChecker() throws NamingException
+            {
+                return JAVA_BYTE_SYNTAX_CHECKER;
+            }
+        };
+        syntax.setSchema( "apache" );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        syntax = new AbstractSyntax( SchemaConstants.JAVA_SHORT_SYNTAX, "a syntax for java short values", true )
+        {
+            private static final long serialVersionUID = 1L;
+            private final JavaShortSyntaxChecker JAVA_SHORT_SYNTAX_CHECKER = new JavaShortSyntaxChecker();
+
+            public String getName()
+            {
+                return "JAVA_SHORT";
+            }
+            
+            public String[] getNames()
+            {
+                return new String[] { "JAVA_SHORT" };
+            }
+            
+            public SyntaxChecker getSyntaxChecker() throws NamingException
+            {
+                return JAVA_SHORT_SYNTAX_CHECKER;
+            }
+        };
+        syntax.setSchema( "apache" );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        syntax = new AbstractSyntax( SchemaConstants.JAVA_INT_SYNTAX, "a syntax for java int values", true )
+        {
+            private static final long serialVersionUID = 1L;
+            private final JavaIntegerSyntaxChecker JAVA_INT_SYNTAX_CHECKER = new JavaIntegerSyntaxChecker();
+
+            public String getName()
+            {
+                return "JAVA_INT";
+            }
+            
+            public String[] getNames()
+            {
+                return new String[] { "JAVA_INT" };
+            }
+            
+            public SyntaxChecker getSyntaxChecker() throws NamingException
+            {
+                return JAVA_INT_SYNTAX_CHECKER;
+            }
+        };
+        syntax.setSchema( "apache" );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        syntax = new AbstractSyntax( SchemaConstants.JAVA_LONG_SYNTAX, "a syntax for java long values", true )
+        {
+            private static final long serialVersionUID = 1L;
+            private final JavaLongSyntaxChecker JAVA_LONG_SYNTAX_CHECKER = new JavaLongSyntaxChecker();
+
+            public String getName()
+            {
+                return "JAVA_LONG";
+            }
+            
+            public String[] getNames()
+            {
+                return new String[] { "JAVA_LONG" };
+            }
+            
+            public SyntaxChecker getSyntaxChecker() throws NamingException
+            {
+                return JAVA_LONG_SYNTAX_CHECKER;
+            }
+        };
+        syntax.setSchema( "apache" );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+    }
+}
diff --git a/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/ApachemetaComparatorProducer.java b/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/ApachemetaComparatorProducer.java
new file mode 100644
index 0000000..47effa0
--- /dev/null
+++ b/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/ApachemetaComparatorProducer.java
@@ -0,0 +1,146 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.bootstrap;
+
+
+import java.io.Serializable;
+import java.util.Comparator;
+
+import javax.naming.NamingException;
+
+import jdbm.helper.StringComparator;
+
+import org.apache.directory.server.schema.bootstrap.ProducerTypeEnum;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.schema.DeepTrimToLowerNormalizer;
+import org.apache.directory.shared.ldap.schema.NormalizingComparator;
+import org.apache.directory.shared.ldap.util.StringTools;
+
+
+
+/**
+ * A producer of Comparator objects for the apachemeta schema.
+ * Modified by hand from generated code
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class ApachemetaComparatorProducer extends AbstractBootstrapProducer
+{
+    public ApachemetaComparatorProducer()
+    {
+        super( ProducerTypeEnum.COMPARATOR_PRODUCER );
+    }
+
+    
+    public static class DeepTrimToLowerNormalizingComparator extends NormalizingComparator
+    {
+        public DeepTrimToLowerNormalizingComparator()
+        {
+            super( new DeepTrimToLowerNormalizer(), new StringComparator() );
+        }
+    }
+
+    
+    // ------------------------------------------------------------------------
+    // BootstrapProducer Methods
+    // ------------------------------------------------------------------------
+
+
+    /**
+     * @see BootstrapProducer#produce(Registries, ProducerCallback)
+     */
+    public void produce( Registries registries, ProducerCallback cb )
+        throws NamingException
+    {
+        Comparator comparator = null;
+        
+        comparator = new NameOrNumericIdComparator();
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.18060.0.4.0.1.0", comparator );
+
+        comparator = new ObjectClassTypeComparator();
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.18060.0.4.0.1.1", comparator );
+        
+        comparator = new StringComparator();
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.18060.0.4.0.1.2", comparator );
+        
+        comparator = new DeepTrimToLowerNormalizingComparator();
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.18060.0.4.0.1.3", comparator );
+        
+        comparator = new DeepTrimToLowerNormalizingComparator();
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.18060.0.4.0.1.4", comparator );
+    }
+
+
+    public static class ObjectClassTypeComparator implements Comparator<Object>, Serializable
+    {
+        private static final long serialVersionUID = 1L;
+
+
+        public int compare( Object o1, Object o2 )
+        {
+            String s1 = getString( o1 );
+            String s2 = getString( o2 );
+            
+            if ( s1 == null && s2 == null )
+            {
+                return 0;
+            }
+            
+            if ( s1 == null )
+            {
+                return -1;
+            }
+            
+            if ( s2 == null )
+            {
+                return 1;
+            }
+            
+            return s1.compareTo( s2 );
+        }
+        
+        
+        String getString( Object obj )
+        {
+            String strValue;
+
+            if ( obj == null )
+            {
+                return null;
+            }
+            
+            if ( obj instanceof String )
+            {
+                strValue = ( String ) obj;
+            }
+            else if ( obj instanceof byte[] )
+            {
+                strValue = StringTools.utf8ToString( ( byte[] ) obj ); 
+            }
+            else
+            {
+                strValue = obj.toString();
+            }
+
+            return strValue;
+        }
+    }
+}
diff --git a/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/ApachemetaMatchingRuleProducer.java b/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/ApachemetaMatchingRuleProducer.java
new file mode 100644
index 0000000..e080684
--- /dev/null
+++ b/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/ApachemetaMatchingRuleProducer.java
@@ -0,0 +1,348 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.bootstrap;
+
+
+import java.util.Comparator;
+
+import javax.naming.NamingException;
+
+import jdbm.helper.StringComparator;
+
+import org.apache.commons.collections.comparators.ComparableComparator;
+import org.apache.directory.server.constants.MetaSchemaConstants;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.server.schema.registries.SyntaxRegistry;
+import org.apache.directory.shared.ldap.NotImplementedException;
+import org.apache.directory.shared.ldap.schema.DeepTrimToLowerNormalizer;
+import org.apache.directory.shared.ldap.schema.MatchingRule;
+import org.apache.directory.shared.ldap.schema.NoOpNormalizer;
+import org.apache.directory.shared.ldap.schema.Normalizer;
+import org.apache.directory.shared.ldap.schema.ObjectIdentifierComparator;
+import org.apache.directory.shared.ldap.schema.ObjectIdentifierNormalizer;
+import org.apache.directory.shared.ldap.schema.Syntax;
+
+
+/**
+ * A producer of MatchingRule objects for the apachemeta schema.
+ * Modified by hand from generated code
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class ApachemetaMatchingRuleProducer extends AbstractBootstrapProducer
+{
+    
+    
+    public ApachemetaMatchingRuleProducer()
+    {
+        super( ProducerTypeEnum.MATCHING_RULE_PRODUCER );
+    }
+
+
+    // ------------------------------------------------------------------------
+    // BootstrapProducer Methods
+    // ------------------------------------------------------------------------
+
+
+    /**
+     * @see BootstrapProducer#produce(Registries, ProducerCallback)
+     */
+    public void produce( Registries registries, ProducerCallback cb )
+        throws NamingException
+    {
+        MatchingRule matchingRule = null;
+        
+        matchingRule = new NameOrNumericIdMatch( registries.getOidRegistry(), MetaSchemaConstants.SCHEMA_NAME );
+        cb.schemaObjectProduced( this, matchingRule.getOid(), matchingRule );
+        
+        matchingRule = new ObjectClassTypeMatch();
+        cb.schemaObjectProduced( this, matchingRule.getOid(), matchingRule );
+        
+        matchingRule = new NumericOidMatch( registries.getSyntaxRegistry() );
+        cb.schemaObjectProduced( this, matchingRule.getOid(), matchingRule );
+        
+        matchingRule = new SupDITStructureRuleMatch( registries.getSyntaxRegistry() );
+        cb.schemaObjectProduced( this, matchingRule.getOid(), matchingRule );
+        
+        matchingRule = new RuleIdMatch( registries.getSyntaxRegistry() );
+        cb.schemaObjectProduced( this, matchingRule.getOid(), matchingRule );
+    }
+    
+    
+    public static class RuleIdMatch implements MatchingRule
+    {
+        private static final long serialVersionUID = 1L;
+        private static final String OID = "1.3.6.1.4.1.18060.0.4.0.1.4";
+        private final Syntax syntax;
+        private final String[] NAMES = new String[] { "ruleIdMatch" };
+        
+        
+        RuleIdMatch( SyntaxRegistry registry ) throws NamingException
+        {
+            syntax = registry.lookup( "1.3.6.1.4.1.1466.115.121.1.26" );
+        }
+        
+        public Comparator getComparator() throws NamingException
+        {
+            return new ComparableComparator();
+        }
+        
+        public Normalizer getNormalizer() throws NamingException
+        {
+            return new DeepTrimToLowerNormalizer();
+        }
+
+        public Syntax getSyntax() throws NamingException
+        {
+            return syntax;
+        }
+
+        public String getDescription()
+        {
+            return "Rule identifier of this DIT structure rule";
+        }
+
+        public String getName()
+        {
+            return NAMES[0];
+        }
+
+        public String[] getNamesRef()
+        {
+            return NAMES;
+        }
+
+        public String getOid()
+        {
+            return OID;
+        }
+
+        public boolean isObsolete()
+        {
+            return false;
+        }
+
+        public String getSchema()
+        {
+            return MetaSchemaConstants.SCHEMA_NAME;
+        }
+
+        public void setSchema( String schemaName )
+        {
+            throw new NotImplementedException();
+        }
+    }
+    
+    
+    public static class SupDITStructureRuleMatch implements MatchingRule
+    {
+        private static final String OID = "1.3.6.1.4.1.18060.0.4.0.1.3";
+        private static final long serialVersionUID = 1L;
+        String[] NAMES = new String[] { "supDITStructureRuleMatch" };
+        Syntax syntax;
+        
+        
+        public SupDITStructureRuleMatch( SyntaxRegistry registry ) throws NamingException
+        {
+            this.syntax = registry.lookup( "1.3.6.1.4.1.1466.115.121.1.17" );
+        }
+        
+        
+        public Comparator getComparator() throws NamingException
+        {
+            return new StringComparator();
+        }
+
+        public Normalizer getNormalizer() throws NamingException
+        {
+            return new DeepTrimToLowerNormalizer();
+        }
+
+        public Syntax getSyntax() throws NamingException
+        {
+            return syntax;
+        }
+
+        public String getDescription()
+        {
+            return "A matching rule matching dit structure rule attributes";
+        }
+
+        public String getName()
+        {
+            return NAMES[0];
+        }
+
+        public String[] getNamesRef()
+        {
+            return NAMES;
+        }
+
+        public String getOid()
+        {
+            return OID;
+        }
+
+        public boolean isObsolete()
+        {
+            return false;
+        }
+
+        public String getSchema()
+        {
+            return MetaSchemaConstants.SCHEMA_NAME;
+        }
+
+        public void setSchema( String schemaName )
+        {
+            throw new NotImplementedException();
+        }
+    }
+    
+    
+    public static class NumericOidMatch implements MatchingRule
+    {
+        private static final String OID = "1.3.6.1.4.1.18060.0.4.0.1.2";
+
+        private static final long serialVersionUID = 1L;
+
+        final String[] NAMES = new String[] { "numericOidMatch" };
+        Syntax syntax;
+        
+        public NumericOidMatch( SyntaxRegistry registry ) throws NamingException
+        {
+            this.syntax = registry.lookup( "1.3.6.1.4.1.1466.115.121.1.38" );
+        }
+        
+        public Comparator getComparator() throws NamingException
+        {
+            return new ObjectIdentifierComparator();
+        }
+
+        public Normalizer getNormalizer() throws NamingException
+        {
+            return new ObjectIdentifierNormalizer();
+        }
+
+        public Syntax getSyntax() throws NamingException
+        {
+            return syntax;
+        }
+
+        public String getDescription()
+        {
+            return "a matching rule for numeric oids";
+        }
+
+        public String getName()
+        {
+            return NAMES[0];
+        }
+
+        public String[] getNamesRef()
+        {
+            return NAMES;
+        }
+
+        public String getOid()
+        {
+            return OID;
+        }
+
+        public boolean isObsolete()
+        {
+            return false;
+        }
+
+        public String getSchema()
+        {
+            return MetaSchemaConstants.SCHEMA_NAME;
+        }
+
+        public void setSchema( String schemaName )
+        {
+            throw new NotImplementedException();
+        }
+    }
+
+    
+    public static class ObjectClassTypeMatch implements MatchingRule
+    {
+        private static final long serialVersionUID = 1L;
+        public static final Comparator COMPARATOR = new ApachemetaComparatorProducer.ObjectClassTypeComparator(); 
+        public static final Normalizer NORMALIZER = new NoOpNormalizer();
+        public static final Syntax SYNTAX = new ApachemetaSyntaxProducer.ObjectClassTypeSyntax();
+        public static final String OID = "1.3.6.1.4.1.18060.0.4.0.1.1";
+        
+        private static final String[] NAMES = new String[] { "objectClassTypeMatch" };
+        
+        public Comparator getComparator() throws NamingException
+        {
+            return COMPARATOR;
+        }
+
+        
+        public Normalizer getNormalizer() throws NamingException
+        {
+            return NORMALIZER;
+        }
+
+        public Syntax getSyntax() throws NamingException
+        {
+            return SYNTAX;
+        }
+
+        public String getDescription()
+        {
+            return "objectClassTypeMatch: for mathing AUXILIARY, STRUCTURAL, ABSTRACT";
+        }
+
+        public String getName()
+        {
+            return NAMES[0];
+        }
+
+        public String[] getNamesRef()
+        {
+            return NAMES;
+        }
+
+        public String getOid()
+        {
+            return OID;
+        }
+
+        public boolean isObsolete()
+        {
+            return false;
+        }
+
+        public String getSchema()
+        {
+            return MetaSchemaConstants.SCHEMA_NAME;
+        }
+
+        public void setSchema( String schemaName )
+        {
+            throw new NotImplementedException();
+        }
+    }
+}
diff --git a/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/ApachemetaNormalizerProducer.java b/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/ApachemetaNormalizerProducer.java
new file mode 100644
index 0000000..ac30fa0
--- /dev/null
+++ b/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/ApachemetaNormalizerProducer.java
@@ -0,0 +1,75 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.bootstrap;
+
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.schema.DeepTrimToLowerNormalizer;
+import org.apache.directory.shared.ldap.schema.NoOpNormalizer;
+import org.apache.directory.shared.ldap.schema.Normalizer;
+
+
+
+/**
+ * A producer of Normalizer objects for the apachemeta schema.
+ * Modified by hand from generated code
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class ApachemetaNormalizerProducer extends AbstractBootstrapProducer
+{
+    public ApachemetaNormalizerProducer()
+    {
+        super( ProducerTypeEnum.NORMALIZER_PRODUCER );
+    }
+
+
+    // ------------------------------------------------------------------------
+    // BootstrapProducer Methods
+    // ------------------------------------------------------------------------
+
+
+    /**
+     * @see BootstrapProducer#produce(Registries, ProducerCallback)
+     */
+    public void produce( Registries registries, ProducerCallback cb )
+        throws NamingException
+    {
+        Normalizer normalizer = null;
+        
+        normalizer = new NameOrNumericIdNormalizer( registries.getOidRegistry() );
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.18060.0.4.0.1.0", normalizer );
+
+        normalizer = new NoOpNormalizer();
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.18060.0.4.0.1.1", normalizer );
+        
+        normalizer = new NoOpNormalizer();
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.18060.0.4.0.1.2", normalizer );
+        
+        normalizer = new DeepTrimToLowerNormalizer();
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.18060.0.4.0.1.3", normalizer );
+        
+        normalizer = new DeepTrimToLowerNormalizer();
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.18060.0.4.0.1.4", normalizer );
+    }
+}
diff --git a/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/ApachemetaSyntaxCheckerProducer.java b/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/ApachemetaSyntaxCheckerProducer.java
new file mode 100644
index 0000000..523074a
--- /dev/null
+++ b/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/ApachemetaSyntaxCheckerProducer.java
@@ -0,0 +1,74 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.bootstrap;
+
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.schema.syntax.NumberSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.NumericOidSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.ObjectClassTypeSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.ObjectNameSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.SyntaxChecker;
+
+
+
+/**
+ * A producer of SyntaxChecker objects for the apachemeta schema.
+ * Modified by hand from generated code
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class ApachemetaSyntaxCheckerProducer extends AbstractBootstrapProducer
+{
+    public ApachemetaSyntaxCheckerProducer()
+    {
+        super( ProducerTypeEnum.SYNTAX_CHECKER_PRODUCER );
+    }
+
+
+    // ------------------------------------------------------------------------
+    // BootstrapProducer Methods
+    // ------------------------------------------------------------------------
+
+
+    /**
+     * @see BootstrapProducer#produce(Registries, ProducerCallback)
+     */
+    public void produce( Registries registries, ProducerCallback cb )
+        throws NamingException
+    {
+        SyntaxChecker checker = null;
+        
+        checker = new NumericOidSyntaxChecker();
+        cb.schemaObjectProduced( this, checker.getSyntaxOid(), checker );
+        
+        checker = new ObjectClassTypeSyntaxChecker();
+        cb.schemaObjectProduced( this, checker.getSyntaxOid(), checker );
+
+        checker = new NumberSyntaxChecker();
+        cb.schemaObjectProduced( this, checker.getSyntaxOid(), checker );
+
+        checker = new ObjectNameSyntaxChecker();
+        cb.schemaObjectProduced( this, checker.getSyntaxOid(), checker );
+    }
+}
diff --git a/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/ApachemetaSyntaxProducer.java b/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/ApachemetaSyntaxProducer.java
new file mode 100644
index 0000000..22f12ff
--- /dev/null
+++ b/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/ApachemetaSyntaxProducer.java
@@ -0,0 +1,349 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.bootstrap;
+
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.constants.MetaSchemaConstants;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.NotImplementedException;
+import org.apache.directory.shared.ldap.schema.Syntax;
+import org.apache.directory.shared.ldap.schema.syntax.NumericOidSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.NumericStringSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.ObjectClassTypeSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.ObjectNameSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.OidSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.SyntaxChecker;
+
+
+/**
+ * A producer of Syntax objects for the apachemeta schema.
+ * Modified by hand from generated code
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class ApachemetaSyntaxProducer extends AbstractBootstrapProducer
+{
+    public ApachemetaSyntaxProducer()
+    {
+        super( ProducerTypeEnum.SYNTAX_PRODUCER );
+    }
+
+
+    // ------------------------------------------------------------------------
+    // BootstrapProducer Methods
+    // ------------------------------------------------------------------------
+
+
+    /**
+     * @see BootstrapProducer#produce(DefaultRegistries, ProducerCallback)
+     */
+    public void produce( Registries registries, ProducerCallback cb )
+        throws NamingException
+    {
+        Syntax syntax = null;
+        
+        syntax = new NameOrNumericIdSyntax();
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        syntax = new NumericOidSyntax();
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        syntax = new ObjectClassTypeSyntax();
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        syntax = new NumberSyntax();
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        syntax = new ObjectNameSyntax();
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+    }
+    
+    
+    public static class NumericOidSyntax implements Syntax
+    {
+        private static final long serialVersionUID = 1L;
+        private final static String OID = "1.3.6.1.4.1.18060.0.4.0.0.2";
+        private final static SyntaxChecker CHECKER = new OidSyntaxChecker();
+        private final static String[] NAMES = new String[] { "numericOid" };
+        
+        public final SyntaxChecker getSyntaxChecker() throws NamingException
+        {
+            return CHECKER;
+        }
+
+        public final boolean isHumanReadable()
+        {
+            return true;
+        }
+
+        public final String getDescription()
+        {
+            return "The syntax for numericoids.";
+        }
+
+        public final String getName()
+        {
+            return NAMES[0];
+        }
+
+        public final String[] getNamesRef()
+        {
+            return NAMES;
+        }
+
+        public final String getOid()
+        {
+            return OID;
+        }
+
+        public final boolean isObsolete()
+        {
+            return false;
+        }
+
+        public String getSchema()
+        {
+            return MetaSchemaConstants.SCHEMA_NAME;
+        }
+
+        public void setSchema( String schemaName )
+        {
+            throw new NotImplementedException();
+        }
+    }
+
+
+    public static class NameOrNumericIdSyntax implements Syntax
+    {
+        private static final long serialVersionUID = 1L;
+        private final static String OID = "1.3.6.1.4.1.18060.0.4.0.0.0";
+        private final static SyntaxChecker CHECKER = new NumericOidSyntaxChecker();
+        private final static String[] NAMES = new String[] { "nameOrOid" };
+        
+        public final SyntaxChecker getSyntaxChecker() throws NamingException
+        {
+            return CHECKER;
+        }
+
+        public final boolean isHumanReadable()
+        {
+            return true;
+        }
+
+        public final String getDescription()
+        {
+            return "The syntax for either numeric ids or names.";
+        }
+
+        public final String getName()
+        {
+            return NAMES[0];
+        }
+
+        public final String[] getNamesRef()
+        {
+            return NAMES;
+        }
+
+        public final String getOid()
+        {
+            return OID;
+        }
+
+        public final boolean isObsolete()
+        {
+            return false;
+        }
+
+        public String getSchema()
+        {
+            return MetaSchemaConstants.SCHEMA_NAME;
+        }
+
+        public void setSchema( String schemaName )
+        {
+            throw new NotImplementedException();
+        }
+    }
+
+
+    public static class ObjectClassTypeSyntax implements Syntax
+    {
+        private static final long serialVersionUID = 1L;
+        private final static String OID = "1.3.6.1.4.1.18060.0.4.0.0.1";
+        private final static SyntaxChecker CHECKER = new ObjectClassTypeSyntaxChecker();
+        private final static String[] NAMES = new String[] { "objectClassType" };
+        
+        public final SyntaxChecker getSyntaxChecker() throws NamingException
+        {
+            return CHECKER;
+        }
+
+        public final boolean isHumanReadable()
+        {
+            return true;
+        }
+
+        public final String getDescription()
+        {
+            return "The syntax for either numeric ids or names.";
+        }
+
+        public final String getName()
+        {
+            return NAMES[0];
+        }
+
+        public final String[] getNamesRef()
+        {
+            return NAMES;
+        }
+
+        public final String getOid()
+        {
+            return OID;
+        }
+
+        public final boolean isObsolete()
+        {
+            return false;
+        }
+
+        public String getSchema()
+        {
+            return MetaSchemaConstants.SCHEMA_NAME;
+        }
+
+        public void setSchema( String schemaName )
+        {
+            throw new NotImplementedException();
+        }
+    }
+
+
+    public static class NumberSyntax implements Syntax
+    {
+        private static final long serialVersionUID = 1L;
+        private final static String OID = "1.3.6.1.4.1.18060.0.4.0.0.4";
+        private final static SyntaxChecker CHECKER = new NumericStringSyntaxChecker();
+        private final static String[] NAMES = new String[] { "numeric" };
+        
+        public final SyntaxChecker getSyntaxChecker() throws NamingException
+        {
+            return CHECKER;
+        }
+
+        public final boolean isHumanReadable()
+        {
+            return true;
+        }
+
+        public final String getDescription()
+        {
+            return "The syntax for numeric strings.";
+        }
+
+        public final String getName()
+        {
+            return NAMES[0];
+        }
+
+        public final String[] getNamesRef()
+        {
+            return NAMES;
+        }
+
+        public final String getOid()
+        {
+            return OID;
+        }
+
+        public final boolean isObsolete()
+        {
+            return false;
+        }
+
+        public String getSchema()
+        {
+            return MetaSchemaConstants.SCHEMA_NAME;
+        }
+
+        public void setSchema( String schemaName )
+        {
+            throw new NotImplementedException();
+        }
+    }
+
+    public static class ObjectNameSyntax implements Syntax
+    {
+        private static final long serialVersionUID = 1L;
+        private final static String OID = "1.3.6.1.4.1.18060.0.4.0.0.6";
+        private final static SyntaxChecker CHECKER = new ObjectNameSyntaxChecker();
+        private final static String[] NAMES = new String[] { "objectName" };
+        
+        public final SyntaxChecker getSyntaxChecker() throws NamingException
+        {
+            return CHECKER;
+        }
+
+        public final boolean isHumanReadable()
+        {
+            return true;
+        }
+
+        public final String getDescription()
+        {
+            return "The syntax for object names.";
+        }
+
+        public final String getName()
+        {
+            return NAMES[0];
+        }
+
+        public final String[] getNamesRef()
+        {
+            return NAMES;
+        }
+
+        public final String getOid()
+        {
+            return OID;
+        }
+
+        public final boolean isObsolete()
+        {
+            return false;
+        }
+
+        public String getSchema()
+        {
+            return MetaSchemaConstants.SCHEMA_NAME;
+        }
+
+        public void setSchema( String schemaName )
+        {
+            throw new NotImplementedException();
+        }
+    }
+}
diff --git a/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/BootstrapProducer.java b/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/BootstrapProducer.java
new file mode 100755
index 0000000..6546a25
--- /dev/null
+++ b/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/BootstrapProducer.java
@@ -0,0 +1,58 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.bootstrap;
+
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.schema.bootstrap.ProducerTypeEnum;
+import org.apache.directory.server.schema.registries.Registries;
+
+
+/**
+ * A schema object producer which uses a callback to announce object creation
+ * rather than completely returning objects in bulk. This way registries can
+ * be populated while the producer is doing is creating schema objects.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public interface BootstrapProducer
+{
+    /**
+     * Gets the type of producer this is.
+     *
+     * @return the type of the BootstrapProducer as a enum
+     */
+    ProducerTypeEnum getType();
+
+
+    /**
+     * Produces schema objects announcing each one after creation via the
+     * callback before continuing on to create more objects.
+     *
+     * @param registries the registry set used by this producer
+     * @param cb the producer's callback
+     * @throws NamingException callbacks often operate upon registries and can
+     * throw these exceptions so we must throw this as well since
+     * implementations will have to call the callback methods
+     */
+    void produce( Registries registries, ProducerCallback cb ) throws NamingException;
+}
diff --git a/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/BootstrapSchemaLoader.java b/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/BootstrapSchemaLoader.java
new file mode 100755
index 0000000..8dde0da
--- /dev/null
+++ b/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/BootstrapSchemaLoader.java
@@ -0,0 +1,442 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.bootstrap;
+
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Stack;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.constants.MetaSchemaConstants;
+import org.apache.directory.server.schema.bootstrap.SystemSchema;
+import org.apache.directory.server.schema.bootstrap.BootstrapSchema;
+import org.apache.directory.server.schema.bootstrap.ProducerTypeEnum;
+import org.apache.directory.server.schema.bootstrap.AbstractBootstrapProducer.BootstrapAttributeType;
+import org.apache.directory.server.schema.bootstrap.AbstractBootstrapProducer.BootstrapMatchingRule;
+import org.apache.directory.server.schema.bootstrap.AbstractBootstrapProducer.BootstrapObjectClass;
+import org.apache.directory.server.schema.bootstrap.AbstractBootstrapProducer.BootstrapSyntax;
+import org.apache.directory.server.schema.registries.AbstractSchemaLoader;
+import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
+import org.apache.directory.server.schema.registries.ComparatorRegistry;
+import org.apache.directory.server.schema.registries.DefaultRegistries;
+import org.apache.directory.server.schema.registries.MatchingRuleRegistry;
+import org.apache.directory.server.schema.registries.NormalizerRegistry;
+import org.apache.directory.server.schema.registries.ObjectClassRegistry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.server.schema.registries.SyntaxCheckerRegistry;
+import org.apache.directory.server.schema.registries.SyntaxRegistry;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.schema.MatchingRule;
+import org.apache.directory.shared.ldap.schema.Normalizer;
+import org.apache.directory.shared.ldap.schema.ObjectClass;
+import org.apache.directory.shared.ldap.schema.Syntax;
+import org.apache.directory.shared.ldap.schema.syntax.ComparatorDescription;
+import org.apache.directory.shared.ldap.schema.syntax.NormalizerDescription;
+import org.apache.directory.shared.ldap.schema.syntax.SyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.SyntaxCheckerDescription;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * Class which handles bootstrap schema class file loading.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class BootstrapSchemaLoader extends AbstractSchemaLoader
+{
+    private static final Logger log = LoggerFactory.getLogger( BootstrapSchemaLoader.class );
+
+    private ClassLoader cl = getClass().getClassLoader();
+
+    /** stores schemas of producers for callback access */
+    private ThreadLocal<BootstrapSchema> schemas;
+    /** stores registries associated with producers for callback access */
+    private ThreadLocal<Registries> registries;
+    /** the callback that just calls register() */
+    private final ProducerCallback cb = new ProducerCallback()
+    {
+        public void schemaObjectProduced( BootstrapProducer producer, String registryKey, Object schemaObject )
+            throws NamingException
+        {
+            register( producer.getType(), registryKey, schemaObject );
+        }
+    };
+
+
+    /**
+     * Creates a BootstrapSchema loader.
+     */
+    public BootstrapSchemaLoader()
+    {
+        schemas = new ThreadLocal<BootstrapSchema>();
+        registries = new ThreadLocal<Registries>();
+    }
+
+
+    public BootstrapSchemaLoader( ClassLoader cl )
+    {
+        this();
+        this.cl = cl;
+    }
+
+    public final void loadWithDependencies( Schema schema, Registries registries ) throws NamingException
+    {
+        if ( ! ( schema instanceof BootstrapSchema ) )
+        {
+            throw new NamingException( "Expecting schema to be of sub-type BootstrapSchema" );
+        }
+        
+        Map<String, Schema> notLoaded = new HashMap<String, Schema>();
+        notLoaded.put( schema.getSchemaName(), schema );
+        Properties props = new Properties();
+        props.put( "package", ( ( BootstrapSchema ) schema ).getPackageName() );
+        loadDepsFirst( schema, new Stack<String>(), notLoaded, schema, registries, props );
+    }
+
+    
+    /**
+     * Loads a set of schemas by loading and running all producers for each
+     * dependent schema first.
+     *
+     * @param bootstrapSchemas Collection of {@link BootstrapSchema}s to load
+     * @param registries the registries to fill with producer created objects
+     * @throws NamingException if there are any failures during this process
+     */
+    public final void loadWithDependencies( Collection<Schema> bootstrapSchemas, Registries registries ) throws NamingException
+    {
+        BootstrapSchema[] schemas = new BootstrapSchema[bootstrapSchemas.size()];
+        schemas = bootstrapSchemas.toArray( schemas );
+        HashMap<String,Schema> loaded = new HashMap<String,Schema>();
+        HashMap<String,Schema> notLoaded = new HashMap<String,Schema>();
+
+        for ( BootstrapSchema schema:schemas )
+        {
+            notLoaded.put( schema.getSchemaName(), schema );
+        }
+
+        BootstrapSchema schema;
+
+        // Create system schema and kick it off by loading system which
+        // will never depend on anything.
+        schema = new SystemSchema();
+        load( schema, registries, false );
+        notLoaded.remove( schema.getSchemaName() ); // Remove if user specified it.
+        loaded.put( schema.getSchemaName(), schema );
+
+        Iterator list = notLoaded.values().iterator();
+        while ( list.hasNext() )
+        {
+            schema = ( BootstrapSchema ) list.next();
+            Properties props = new Properties();
+            props.put( "package", schema.getPackageName() );
+            loadDepsFirst( schema, new Stack<String>(), notLoaded, schema, registries, props );
+            list = notLoaded.values().iterator();
+        }
+    }
+
+
+    /**
+     * Loads a schema by loading and running all producers for the schema.
+     *
+     * @param schema the schema to load
+     * @param registries the registries to fill with producer created objects
+     * @throws NamingException if there are any failures during this process
+     */
+    public final void load( Schema schema, Registries registries, boolean isDepLoad ) throws NamingException
+    {
+        if ( registries.getLoadedSchemas().containsKey( schema.getSchemaName() ) )
+        {
+            return;
+        }
+        
+        if ( ! ( schema instanceof BootstrapSchema ) )
+        {
+            throw new NamingException( "Expecting schema to be of sub-type BootstrapSchema" );
+        }
+        
+        this.registries.set( registries );
+        this.schemas.set( ( BootstrapSchema ) schema );
+
+        for ( ProducerTypeEnum producerType:ProducerTypeEnum.getList() )
+        {
+            BootstrapProducer producer = getProducer( ( BootstrapSchema ) schema, producerType.getName() );
+            producer.produce( registries, cb );
+        }
+
+        notifyListenerOrRegistries( schema, registries );
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Utility Methods
+    // ------------------------------------------------------------------------
+
+    /**
+     * Registers objects
+     *
+     * @param type the type of the producer which determines the type of object produced
+     * @param id the primary key identifying the created object in a registry
+     * @param schemaObject the object being registered
+     * @throws NamingException if there are problems when registering the object
+     * in any of the registries
+     */
+    private void register( ProducerTypeEnum type, String id, Object schemaObject ) throws NamingException
+    {
+        BootstrapSchema schema = this.schemas.get();
+        DefaultRegistries registries = ( DefaultRegistries ) this.registries.get();
+        List<String> values = new ArrayList<String>(1);
+        values.add( schema.getSchemaName() );
+
+        switch ( type )
+        {
+            case NORMALIZER_PRODUCER :
+                Normalizer normalizer = ( Normalizer ) schemaObject;
+                NormalizerRegistry normalizerRegistry;
+                normalizerRegistry = registries.getNormalizerRegistry();
+                
+                NormalizerDescription normalizerDescription = new NormalizerDescription();
+                normalizerDescription.setNumericOid( id );
+                normalizerDescription.setFqcn( normalizer.getClass().getName() );
+                normalizerDescription.addExtension( MetaSchemaConstants.X_SCHEMA, values );
+                
+                normalizerRegistry.register( normalizerDescription, normalizer );
+                break;
+                
+            case COMPARATOR_PRODUCER :
+                Comparator comparator = ( Comparator ) schemaObject;
+                ComparatorRegistry comparatorRegistry;
+                comparatorRegistry = registries.getComparatorRegistry();
+                
+                ComparatorDescription comparatorDescription = new ComparatorDescription();
+                comparatorDescription.addExtension( MetaSchemaConstants.X_SCHEMA, values );
+                comparatorDescription.setFqcn( comparator.getClass().getName() );
+                comparatorDescription.setNumericOid( id );
+                
+                comparatorRegistry.register( comparatorDescription, comparator );
+                break;
+                
+            case SYNTAX_CHECKER_PRODUCER :
+                SyntaxChecker syntaxChecker = ( SyntaxChecker ) schemaObject;
+                SyntaxCheckerRegistry syntaxCheckerRegistry;
+                syntaxCheckerRegistry = registries.getSyntaxCheckerRegistry();
+                
+                SyntaxCheckerDescription syntaxCheckerDescription = new SyntaxCheckerDescription();
+                syntaxCheckerDescription.addExtension( MetaSchemaConstants.X_SCHEMA, values );
+                syntaxCheckerDescription.setFqcn( syntaxChecker.getClass().getName() );
+                syntaxCheckerDescription.setNumericOid( id );
+                
+                syntaxCheckerRegistry.register( syntaxCheckerDescription, syntaxChecker );
+                break;
+                
+            case SYNTAX_PRODUCER :
+                Syntax syntax = ( Syntax ) schemaObject;
+                
+                if ( schemaObject instanceof BootstrapSyntax )
+                {
+                    ( ( BootstrapSyntax ) syntax ).setSchema( schema.getSchemaName() );
+                }
+
+                SyntaxRegistry syntaxRegistry = registries.getSyntaxRegistry();
+                syntaxRegistry.register( syntax );
+                break;
+                
+            case MATCHING_RULE_PRODUCER :
+                MatchingRule matchingRule = ( MatchingRule ) schemaObject;
+                
+                if ( schemaObject instanceof BootstrapMatchingRule )
+                {
+                    ( ( BootstrapMatchingRule ) matchingRule ).setSchema( schema.getSchemaName() );
+                }
+
+                MatchingRuleRegistry matchingRuleRegistry;
+                matchingRuleRegistry = registries.getMatchingRuleRegistry();
+                matchingRuleRegistry.register( matchingRule );
+                break;
+                
+            case ATTRIBUTE_TYPE_PRODUCER :
+                AttributeType attributeType = ( AttributeType ) schemaObject;
+                
+                if ( attributeType instanceof BootstrapAttributeType )
+                {
+                    ( ( BootstrapAttributeType ) attributeType ).setSchema( schema.getSchemaName() );
+                }
+                
+                AttributeTypeRegistry attributeTypeRegistry;
+                attributeTypeRegistry = registries.getAttributeTypeRegistry();
+                attributeTypeRegistry.register( attributeType );
+                break;
+                
+            case OBJECT_CLASS_PRODUCER :
+                ObjectClass objectClass = ( ObjectClass ) schemaObject;
+                
+                if ( objectClass instanceof BootstrapObjectClass )
+                {
+                    ( ( BootstrapObjectClass ) objectClass ).setSchema( schema.getSchemaName() );
+                }
+                
+                ObjectClassRegistry objectClassRegistry;
+                objectClassRegistry = registries.getObjectClassRegistry();
+                objectClassRegistry.register( objectClass );
+                break;
+                
+            default:
+                throw new IllegalStateException( "ProducerTypeEnum value is invalid: " + type );
+        }
+    }
+
+
+    /**
+     * Attempts first to try to load the target class for the Producer,
+     * then tries for the default if the target load fails.
+     *
+     * @param schema the bootstrap schema
+     * @param producerBase the producer's base name
+     * @throws NamingException if there are failures loading classes
+     */
+    private BootstrapProducer getProducer( BootstrapSchema schema, String producerBase ) throws NamingException
+    {
+        Class<?> clazz = null;
+        boolean failedTargetLoad = false;
+        String defaultClassName;
+        String targetClassName = schema.getBaseClassName() + producerBase;
+
+        try
+        {
+            clazz = Class.forName( targetClassName, true, cl );
+        }
+        catch ( ClassNotFoundException e )
+        {
+            failedTargetLoad = true;
+            log.debug( "Failed to load '" + targetClassName + "'.  Trying the alternative.", e );
+        }
+
+        if ( failedTargetLoad )
+        {
+            defaultClassName = schema.getDefaultBaseClassName() + producerBase;
+
+            try
+            {
+                clazz = Class.forName( defaultClassName, true, cl );
+            }
+            catch ( ClassNotFoundException e )
+            {
+                NamingException ne = new NamingException( "Failed to load " + producerBase + " for "
+                    + schema.getSchemaName() + " schema using following classes: " + targetClassName + ", "
+                    + defaultClassName );
+                ne.setRootCause( e );
+                throw ne;
+            }
+        }
+
+        try
+        {
+            return ( BootstrapProducer ) clazz.newInstance();
+        }
+        catch ( IllegalAccessException e )
+        {
+            NamingException ne = new NamingException( "Failed to create " + clazz );
+            ne.setRootCause( e );
+            throw ne;
+        }
+        catch ( InstantiationException e )
+        {
+            NamingException ne = new NamingException( "Failed to create " + clazz );
+            ne.setRootCause( e );
+            throw ne;
+        }
+    }
+
+
+    public Schema getSchema( String schemaName ) throws NamingException
+    {
+        return getSchema( schemaName, null );
+    }
+    
+    
+    public Schema getSchema( String schemaName, Properties schemaProperties ) throws NamingException
+    {
+        String baseName = schemaName;
+        schemaName = schemaName.toLowerCase();
+        StringBuffer buf = new StringBuffer();
+
+        
+        if ( schemaProperties == null || schemaProperties.getProperty( "package" ) == null )
+        {
+            // first see if we can load a schema object using the default bootstrap package
+            Properties props = new Properties();
+            props.put( "package", "org.apache.directory.server.schema.bootstrap" );
+            
+            try
+            {
+                Schema schema = getSchema( baseName, props );
+                return schema;
+            }
+            catch( NamingException e )
+            {
+                throw new NamingException( "Can't find the bootstrap schema class in the default " +
+                        "\n bootstrap schema package.  I need a package name property with key \"package\"." );
+            }
+        }
+        
+        buf.append( schemaProperties.getProperty( "package" ) );
+        buf.append( '.' );
+        buf.append( Character.toUpperCase( schemaName.charAt( 0 ) ) );
+        buf.append( schemaName.substring( 1 ) );
+        schemaName = buf.toString();
+        
+        Schema schema = null;
+        try
+        {
+            schema = ( Schema ) Class.forName( schemaName, true, cl ).newInstance();
+        }
+        catch ( InstantiationException e )
+        {
+            NamingException ne = new NamingException( "Failed to instantiate schema object: " + schemaName );
+            ne.setRootCause( e );
+            throw ne;
+        }
+        catch ( IllegalAccessException e )
+        {
+            NamingException ne = 
+                new NamingException( "Failed to access default constructor of schema object: " + schemaName );
+            ne.setRootCause( e );
+            throw ne;
+        }
+        catch ( ClassNotFoundException e )
+        {
+            NamingException ne = new NamingException( "Schema class not found: " + schemaName );
+            ne.setRootCause( e );
+            throw ne;
+        }
+        
+        return schema;
+    }
+}
diff --git a/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/NameOrNumericIdComparator.java b/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/NameOrNumericIdComparator.java
new file mode 100644
index 0000000..df92600
--- /dev/null
+++ b/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/NameOrNumericIdComparator.java
@@ -0,0 +1,132 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.bootstrap;
+
+
+import java.io.Serializable;
+import java.util.Comparator;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.schema.registries.OidRegistry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.util.StringTools;
+
+
+/**
+ * A comparator that sorts OIDs based on their numeric id value.  Needs a 
+ * OidRegistry to properly do it's job.  Public method to set the oid 
+ * registry will be used by the server after instantiation in deserialization.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class NameOrNumericIdComparator implements Comparator, Serializable
+{
+    private static final long serialVersionUID = 1L;
+    private transient OidRegistry registry;
+
+    
+    public NameOrNumericIdComparator( OidRegistry registry )
+    {
+        this.registry = registry;
+    }
+    
+    
+    public NameOrNumericIdComparator()
+    {
+    }
+    
+    
+    /* (non-Javadoc)
+     * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
+     */
+    public int compare( Object o1, Object o2 )
+    {
+        String s1 = getNumericIdString( o1 );
+        String s2 = getNumericIdString( o2 );
+
+        if ( s1 == null && s2 == null )
+        {
+            return 0;
+        }
+        
+        if ( s1 == null )
+        {
+            return -1;
+        }
+        
+        if ( s2 == null )
+        {
+            return 1;
+        }
+        
+        return s1.compareTo( s2 );
+    }
+    
+    
+    public void setRegistries( Registries registries )
+    {
+        registry = registries.getOidRegistry();
+    }
+    
+    
+    String getNumericIdString( Object obj )
+    {
+        String strValue;
+
+        if ( obj == null )
+        {
+            return null;
+        }
+        
+        if ( obj instanceof String )
+        {
+            strValue = ( String ) obj;
+        }
+        else if ( obj instanceof byte[] )
+        {
+            strValue = StringTools.utf8ToString( ( byte[] ) obj ); 
+        }
+        else
+        {
+            strValue = obj.toString();
+        }
+        
+        if ( strValue.length() == 0 )
+        {
+            return "";
+        }
+
+        if ( registry.hasOid( strValue ) )
+        {
+            try
+            {
+                return registry.getOid( strValue );
+            }
+            catch ( NamingException e )
+            {
+                throw new RuntimeException( "Failed to lookup OID for " + strValue, e );
+            }
+        }
+        
+        return strValue;
+    }
+}
diff --git a/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/NameOrNumericIdMatch.java b/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/NameOrNumericIdMatch.java
new file mode 100644
index 0000000..73112d1
--- /dev/null
+++ b/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/NameOrNumericIdMatch.java
@@ -0,0 +1,157 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.bootstrap;
+
+import java.util.Comparator;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.schema.registries.OidRegistry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.NotImplementedException;
+import org.apache.directory.shared.ldap.schema.MatchingRule;
+import org.apache.directory.shared.ldap.schema.Normalizer;
+import org.apache.directory.shared.ldap.schema.Syntax;
+
+/**
+ * Document me!
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class NameOrNumericIdMatch implements MatchingRule
+{
+    private static final long serialVersionUID = 1L;
+    
+    private final static String[] NAMES = new String[] { "nameOrNumericIdMatch" }; 
+    private final static String OID = "1.3.6.1.4.1.18060.0.4.0.1.0";
+    private transient Normalizer normalizer;
+    private transient Comparator comparator;
+    private transient Syntax syntax;
+    private final String schema;
+    
+    
+    public NameOrNumericIdMatch( String schema )
+    {
+        this.syntax = new ApachemetaSyntaxProducer.NameOrNumericIdSyntax();
+        this.schema = schema;
+    }
+
+    
+    public NameOrNumericIdMatch( OidRegistry registry, String schema )
+    {
+        this.normalizer = new NameOrNumericIdNormalizer( registry );
+        this.comparator = new NameOrNumericIdComparator( registry );
+        this.syntax = new ApachemetaSyntaxProducer.NameOrNumericIdSyntax();
+        this.schema = schema;
+    }
+    
+
+    public void setRegistries( Registries registries )
+    {
+        this.normalizer = new NameOrNumericIdNormalizer( registries.getOidRegistry() );
+        this.comparator = new NameOrNumericIdComparator( registries.getOidRegistry() );
+        this.syntax = new ApachemetaSyntaxProducer.NameOrNumericIdSyntax();
+    }
+
+    
+    /* (non-Javadoc)
+     * @see org.apache.directory.shared.ldap.schema.MatchingRule#getComparator()
+     */
+    public Comparator getComparator() throws NamingException
+    {
+        return comparator;
+    }
+
+
+    /* (non-Javadoc)
+     * @see org.apache.directory.shared.ldap.schema.MatchingRule#getNormalizer()
+     */
+    public Normalizer getNormalizer() throws NamingException
+    {
+        return normalizer;
+    }
+
+
+    /* (non-Javadoc)
+     * @see org.apache.directory.shared.ldap.schema.MatchingRule#getSyntax()
+     */
+    public Syntax getSyntax() throws NamingException
+    {
+        return syntax;
+    }
+
+
+    /* (non-Javadoc)
+     * @see org.apache.directory.shared.ldap.schema.SchemaObject#getDescription()
+     */
+    public String getDescription()
+    {
+        return "A name or numeric id matchingRule";
+    }
+
+
+    /* (non-Javadoc)
+     * @see org.apache.directory.shared.ldap.schema.SchemaObject#getName()
+     */
+    public String getName()
+    {
+        return NAMES[0];
+    }
+
+
+    /* (non-Javadoc)
+     * @see org.apache.directory.shared.ldap.schema.SchemaObject#getNamesRef()
+     */
+    public String[] getNamesRef()
+    {
+        return NAMES;
+    }
+
+
+    /* (non-Javadoc)
+     * @see org.apache.directory.shared.ldap.schema.SchemaObject#getOid()
+     */
+    public String getOid()
+    {
+        return OID;
+    }
+
+
+    /* (non-Javadoc)
+     * @see org.apache.directory.shared.ldap.schema.SchemaObject#isObsolete()
+     */
+    public boolean isObsolete()
+    {
+        return false;
+    }
+
+
+    public String getSchema()
+    {
+        return schema;
+    }
+
+
+    public void setSchema( String schemaName )
+    {
+        throw new NotImplementedException();
+    }
+}
diff --git a/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/NameOrNumericIdNormalizer.java b/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/NameOrNumericIdNormalizer.java
new file mode 100644
index 0000000..b1035ec
--- /dev/null
+++ b/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/NameOrNumericIdNormalizer.java
@@ -0,0 +1,113 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.bootstrap;
+
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.schema.registries.OidRegistry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.exception.LdapNamingException;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.schema.Normalizer;
+import org.apache.directory.shared.ldap.schema.syntax.NumericOidSyntaxChecker;
+import org.apache.directory.shared.ldap.util.StringTools;
+
+
+/**
+ * A name or numeric id normalizer.  Needs an OID registry to operate properly.
+ * The OID registry is injected into this class after instantiation if a 
+ * setRegistries(Registries) method is exposed.
+ * 
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class NameOrNumericIdNormalizer implements Normalizer
+{
+    private static final long serialVersionUID = 1L;
+    private NumericOidSyntaxChecker checker = new NumericOidSyntaxChecker();
+    private transient OidRegistry registry;
+    
+    
+    public NameOrNumericIdNormalizer( OidRegistry registry )
+    {
+        this.registry = registry;
+    }
+    
+    
+    public NameOrNumericIdNormalizer()
+    {
+    }
+    
+    
+    /* (non-Javadoc)
+     * @see org.apache.directory.shared.ldap.schema.Normalizer#normalize(java.lang.Object)
+     */
+    public Object normalize( Object value ) throws NamingException
+    {
+        String strValue;
+
+        if ( value == null )
+        {
+            return null;
+        }
+        
+        if ( value instanceof String )
+        {
+            strValue = ( String ) value;
+        }
+        else if ( value instanceof byte[] )
+        {
+            strValue = StringTools.utf8ToString( ( byte[] ) value ); 
+        }
+        else
+        {
+            strValue = value.toString();
+        }
+
+        if ( strValue.length() == 0 )
+        {
+            return "";
+        }
+        
+        // if value is a numeric id then return it as is
+        if ( checker.isValidSyntax( strValue ) )
+        {
+            return value;
+        }
+        
+        // if it is a name we need to do a lookup
+        if ( registry.hasOid( strValue ) )
+        {
+            return registry.getOid( strValue );
+        }
+        
+        // if all else fails
+        throw new LdapNamingException( "Encountered name based id of " + value 
+            + " which was not found in the OID registry" , ResultCodeEnum.OTHER );
+    }
+    
+    
+    public void setRegistries( Registries registries )
+    {
+        this.registry = registries.getOidRegistry();
+    }
+}
diff --git a/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/ProducerCallback.java b/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/ProducerCallback.java
new file mode 100755
index 0000000..9c20fda
--- /dev/null
+++ b/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/ProducerCallback.java
@@ -0,0 +1,45 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.bootstrap;
+
+
+import javax.naming.NamingException;
+
+
+/**
+ * A BootstrapProducer's callback used to announce object creation.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public interface ProducerCallback
+{
+    /**
+     * Called to announce the creation of a new schema object by a producer.
+     *
+     * @param producer the producer which created the object
+     * @param registryKey used to uniquely identify the object in registries
+     * @param schemaObject the object that was created by the producer
+     * @throws NamingException if there are problems registering these objects
+     * with bootstrap registries
+     */
+    void schemaObjectProduced( BootstrapProducer producer, String registryKey, Object schemaObject )
+        throws NamingException;
+}
diff --git a/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/SystemComparatorProducer.java b/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/SystemComparatorProducer.java
new file mode 100644
index 0000000..846126a
--- /dev/null
+++ b/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/SystemComparatorProducer.java
@@ -0,0 +1,324 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.bootstrap;
+
+
+import java.util.Comparator;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.schema.DnComparator;
+import org.apache.directory.server.schema.NameAndOptionalUIDComparator;
+import org.apache.directory.server.schema.NameAndOptionalUIDNormalizer;
+import org.apache.directory.server.schema.bootstrap.ProducerTypeEnum;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.schema.ByteArrayComparator;
+import org.apache.directory.shared.ldap.schema.CachingNormalizer;
+import org.apache.directory.shared.ldap.schema.ComparableComparator;
+import org.apache.directory.shared.ldap.schema.DeepTrimNormalizer;
+import org.apache.directory.shared.ldap.schema.DeepTrimToLowerNormalizer;
+import org.apache.directory.shared.ldap.schema.NormalizingComparator;
+import org.apache.directory.shared.ldap.schema.ObjectIdentifierComparator;
+
+
+/**
+ * Document this class.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class SystemComparatorProducer extends AbstractBootstrapProducer
+{
+    public SystemComparatorProducer()
+    {
+        super( ProducerTypeEnum.COMPARATOR_PRODUCER );
+    }
+
+    
+    public static class DeepTrimToLowerCachingNormalizingComparator extends NormalizingComparator
+    {
+        public DeepTrimToLowerCachingNormalizingComparator()        
+        {
+            super( new CachingNormalizer( new DeepTrimToLowerNormalizer() ), new ComparableComparator() );
+        }
+    }
+
+    
+    public static class DeepTrimCachingNormalizingComparator extends NormalizingComparator
+    {
+        public DeepTrimCachingNormalizingComparator()        
+        {
+            super( new CachingNormalizer( new DeepTrimNormalizer() ), new ComparableComparator() );
+        }
+    }
+
+    /**
+     * This caching NormalizingComparator would be a good thing to have,
+     * sadly we can't use it as the registries are not available here ...
+     * 
+     *  TODO Inject the AttributeType registry into the caching normalizer.
+     */
+    public static class NameAndOptionalUIDCachingNormalizingComparator extends NormalizingComparator
+    {
+        public NameAndOptionalUIDCachingNormalizingComparator()        
+        {
+            super( new CachingNormalizer( new NameAndOptionalUIDNormalizer() ), new NameAndOptionalUIDComparator() );
+        }
+    }
+    
+    public void produce( Registries registries, ProducerCallback cb ) throws NamingException
+    {
+        Comparator comparator;
+
+        /*
+         * Straight out of RFC 2252: Section 8
+         * =======================================
+         ( 2.5.13.0 NAME 'objectIdentifierMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )
+         */
+        comparator = new ObjectIdentifierComparator();
+        cb.schemaObjectProduced( this, "2.5.13.0", comparator );
+
+        /*
+         ( 2.5.13.1 NAME 'distinguishedNameMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
+         */
+        comparator = new DnComparator( registries.getAttributeTypeRegistry() );
+        cb.schemaObjectProduced( this, "2.5.13.1", comparator );
+
+        /*
+         ( 2.5.13.2 NAME 'caseIgnoreMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+         */
+        comparator = new DeepTrimToLowerCachingNormalizingComparator();
+        cb.schemaObjectProduced( this, "2.5.13.2", comparator );
+
+        /*
+         ( 2.5.13.3 NAME 'caseIgnoreOrderingMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+         */
+        comparator = new DeepTrimToLowerCachingNormalizingComparator();
+        cb.schemaObjectProduced( this, "2.5.13.3", comparator );
+
+        /*
+         ( 2.5.13.4 NAME 'caseIgnoreSubstringsMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )
+         */
+        comparator = new DeepTrimToLowerCachingNormalizingComparator();
+        cb.schemaObjectProduced( this, "2.5.13.4", comparator );
+
+        /*
+         ( 2.5.13.6 NAME 'caseExactOrderingMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+         */
+        comparator = new ComparableComparator();
+        cb.schemaObjectProduced( this, "2.5.13.6", comparator );
+
+        /*
+         ( 2.5.13.8 NAME 'numericStringMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.36 )
+         */
+        comparator = new ComparableComparator();
+        cb.schemaObjectProduced( this, "2.5.13.8", comparator );
+
+        /*
+         ( 2.5.13.10 NAME 'numericStringSubstringsMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )
+         */
+        comparator = new ComparableComparator();
+        cb.schemaObjectProduced( this, "2.5.13.10", comparator );
+
+        /*
+         ( 2.5.13.11 NAME 'caseIgnoreListMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.41 )
+         */
+        comparator = new DeepTrimToLowerCachingNormalizingComparator();
+        cb.schemaObjectProduced( this, "2.5.13.11", comparator );
+
+        /*
+         ( 2.5.13.14 NAME 'integerMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )
+         */
+        comparator = new ComparableComparator();
+        cb.schemaObjectProduced( this, "2.5.13.14", comparator );
+
+        /*
+         ( 2.5.13.14 NAME 'integerOrderingMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )
+         */
+        comparator = new ComparableComparator();
+        cb.schemaObjectProduced( this, "2.5.13.15", comparator );
+
+        /*
+         ( 2.5.13.16 NAME 'bitStringMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.6 )
+         */
+        comparator = new ComparableComparator();
+        cb.schemaObjectProduced( this, "2.5.13.16", comparator );
+
+        /*
+         ( 2.5.13.17 NAME 'octetStringMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )
+         */
+        comparator = new ByteArrayComparator();
+        cb.schemaObjectProduced( this, "2.5.13.17", comparator );
+
+        /*
+         ( 2.5.13.18 NAME 'octetStringOrderingMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )
+         */
+        comparator = new ByteArrayComparator();
+        cb.schemaObjectProduced( this, "2.5.13.18", comparator );
+
+        /*
+         ( 2.5.13.20 NAME 'telephoneNumberMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.50 )
+         */
+        comparator = new ComparableComparator();
+        cb.schemaObjectProduced( this, "2.5.13.20", comparator );
+
+        /*
+         ( 2.5.13.21 NAME 'telephoneNumberSubstringsMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )
+         */
+        comparator = new ComparableComparator();
+        cb.schemaObjectProduced( this, "2.5.13.21", comparator );
+
+        /*
+         ( 2.5.13.22 NAME 'presentationAddressMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.43 )
+         */
+        comparator = new ComparableComparator();
+        cb.schemaObjectProduced( this, "2.5.13.22", comparator );
+
+        /*
+         ( 2.5.13.23 NAME 'uniqueMemberMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.34 )
+         */
+        comparator = new NameAndOptionalUIDComparator();
+        cb.schemaObjectProduced( this, "2.5.13.23", comparator );
+
+        /*
+         ( 2.5.13.24 NAME 'protocolInformationMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.42 )
+         */
+        comparator = new ComparableComparator();
+        cb.schemaObjectProduced( this, "2.5.13.24", comparator );
+
+        /*
+         ( 2.5.13.27 NAME 'generalizedTimeMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 )
+         */
+        comparator = new ComparableComparator();
+        cb.schemaObjectProduced( this, "2.5.13.27", comparator );
+
+        /*
+         ( 2.5.13.28 NAME 'generalizedTimeOrderingMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 )
+         */
+        comparator = new ComparableComparator();
+        cb.schemaObjectProduced( this, "2.5.13.28", comparator );
+
+        /*
+         ( 2.5.13.29 NAME 'integerFirstComponentMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )
+         */
+        comparator = new ComparableComparator();
+        cb.schemaObjectProduced( this, "2.5.13.29", comparator );
+
+        /*
+         ( 2.5.13.30 NAME 'objectIdentifierFirstComponentMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )
+         */
+        comparator = new ComparableComparator();
+        cb.schemaObjectProduced( this, "2.5.13.30", comparator );
+
+        /*
+         * Straight out of RFC 3698: Section 2.6
+         * http://www.faqs.org/rfcs/rfc3698.html
+         * =======================================
+         * ( 2.5.13.31 NAME 'directoryStringFirstComponentMatch'
+         *   SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+         */
+        comparator = new ComparableComparator();
+        cb.schemaObjectProduced( this, "2.5.13.31", comparator );
+
+        /*
+         ( 1.3.6.1.4.1.1466.109.114.1 NAME 'caseExactIA5Match'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+         */
+        comparator = new DeepTrimCachingNormalizingComparator();
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.109.114.1", comparator );
+
+        /*
+         ( 1.3.6.1.4.1.1466.109.114.2 NAME 'caseIgnoreIA5Match'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+         */
+        comparator = new DeepTrimToLowerCachingNormalizingComparator();
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.109.114.2", comparator );
+
+        /*
+         * MatchingRules from section 2 of http://www.faqs.org/rfcs/rfc3698.html
+         * for Additional MatchingRules
+
+         ( 2.5.13.13 NAME 'booleanMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 )
+
+         */
+
+        comparator = new ComparableComparator();
+        cb.schemaObjectProduced( this, "2.5.13.13", comparator );
+        
+        /*
+         * Straight out of RFC 2798 for InetOrgPerson: Section 9.3.3
+         * =========================================================
+
+         ( 2.5.13.5 NAME 'caseExactMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+
+         ( 2.5.13.7 NAME 'caseExactSubstringsMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )
+
+         ( 2.5.13.12 NAME 'caseIgnoreListSubstringsMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )
+
+         */
+
+        comparator = new DeepTrimCachingNormalizingComparator();
+        cb.schemaObjectProduced( this, "2.5.13.5", comparator );
+
+        comparator = new DeepTrimCachingNormalizingComparator();
+        cb.schemaObjectProduced( this, "2.5.13.7", comparator );
+
+        comparator = new DeepTrimToLowerCachingNormalizingComparator();
+        cb.schemaObjectProduced( this, "2.5.13.12", comparator );
+
+        /*
+         * Straight out of RFC 2798 for InetOrgPerson: Section 9.3.4
+         * =========================================================
+
+         ( 1.3.6.1.4.1.1466.109.114.3 NAME 'caseIgnoreIA5SubstringsMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )
+         */
+
+        comparator = new DeepTrimToLowerCachingNormalizingComparator();
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.109.114.3", comparator );
+    }
+}
diff --git a/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/SystemMatchingRuleProducer.java b/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/SystemMatchingRuleProducer.java
new file mode 100755
index 0000000..1ff0b5a
--- /dev/null
+++ b/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/SystemMatchingRuleProducer.java
@@ -0,0 +1,375 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.bootstrap;
+
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.schema.bootstrap.ProducerTypeEnum;
+import org.apache.directory.server.schema.registries.Registries;
+
+
+/**
+ * A simple maching rule configuration where objects and java code are used
+ * to create matching rules.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class SystemMatchingRuleProducer extends AbstractBootstrapProducer
+{
+    public SystemMatchingRuleProducer()
+    {
+        super( ProducerTypeEnum.MATCHING_RULE_PRODUCER );
+    }
+
+
+    public void produce( Registries registries, ProducerCallback cb ) throws NamingException
+    {
+        BootstrapMatchingRule mrule = null;
+
+        /*
+         * Straight out of RFC 2252: Section 8
+         * =======================================
+         ( 2.5.13.0 NAME 'objectIdentifierMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )
+
+         ( 2.5.13.1 NAME 'distinguishedNameMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
+
+         ( 2.5.13.2 NAME 'caseIgnoreMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+
+         ( 2.5.13.3 NAME 'caseIgnoreOrderingMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+
+         ( 2.5.13.4 NAME 'caseIgnoreSubstringsMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )
+         */
+
+        mrule = new BootstrapMatchingRule( "2.5.13.0", registries );
+        mrule.setNames( new String[]
+            { "objectIdentifierMatch" } );
+        mrule.setSyntaxOid( "1.3.6.1.4.1.1466.115.121.1.38" );
+        cb.schemaObjectProduced( this, mrule.getOid(), mrule );
+
+        mrule = new BootstrapMatchingRule( "2.5.13.1", registries );
+        mrule.setNames( new String[]
+            { "distinguishedNameMatch" } );
+        mrule.setSyntaxOid( "1.3.6.1.4.1.1466.115.121.1.12" );
+        cb.schemaObjectProduced( this, mrule.getOid(), mrule );
+
+        mrule = new BootstrapMatchingRule( "2.5.13.2", registries );
+        mrule.setNames( new String[]
+            { "caseIgnoreMatch" } );
+        mrule.setSyntaxOid( "1.3.6.1.4.1.1466.115.121.1.15" );
+        cb.schemaObjectProduced( this, mrule.getOid(), mrule );
+
+        mrule = new BootstrapMatchingRule( "2.5.13.3", registries );
+        mrule.setNames( new String[]
+            { "caseIgnoreOrderingMatch" } );
+        mrule.setSyntaxOid( "1.3.6.1.4.1.1466.115.121.1.15" );
+        cb.schemaObjectProduced( this, mrule.getOid(), mrule );
+
+        mrule = new BootstrapMatchingRule( "2.5.13.4", registries );
+        mrule.setNames( new String[]
+            { "caseIgnoreSubstringsMatch" } );
+        mrule.setSyntaxOid( "1.3.6.1.4.1.1466.115.121.1.58" );
+        cb.schemaObjectProduced( this, mrule.getOid(), mrule );
+
+        /*
+         * Straight out of RFC 3698: Section 2.3
+         * http://www.faqs.org/rfcs/rfc3698.html
+         * =======================================
+         ( 2.5.13.6 NAME 'caseExactOrderingMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+         */
+
+        mrule = new BootstrapMatchingRule( "2.5.13.6", registries );
+        mrule.setNames( new String[]
+            { "caseExactOrderingMatch" } );
+        mrule.setSyntaxOid( "1.3.6.1.4.1.1466.115.121.1.15" );
+        cb.schemaObjectProduced( this, mrule.getOid(), mrule );
+
+        /*
+         * Straight out of RFC 2252: Section 8
+         * =======================================
+         ( 2.5.13.8 NAME 'numericStringMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.36 )
+
+         ( 2.5.13.10 NAME 'numericStringSubstringsMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )
+
+         ( 2.5.13.11 NAME 'caseIgnoreListMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.41 )
+
+         ( 2.5.13.14 NAME 'integerMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )
+
+         ( 2.5.13.16 NAME 'bitStringMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.6 )
+
+         ( 2.5.13.17 NAME 'octetStringMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )
+         */
+
+        mrule = new BootstrapMatchingRule( "2.5.13.8", registries );
+        mrule.setNames( new String[]
+            { "numericStringMatch" } );
+        mrule.setSyntaxOid( "1.3.6.1.4.1.1466.115.121.1.36" );
+        cb.schemaObjectProduced( this, mrule.getOid(), mrule );
+
+        mrule = new BootstrapMatchingRule( "2.5.13.10", registries );
+        mrule.setNames( new String[]
+            { "numericStringSubstringsMatch" } );
+        mrule.setSyntaxOid( "1.3.6.1.4.1.1466.115.121.1.58" );
+        cb.schemaObjectProduced( this, mrule.getOid(), mrule );
+
+        mrule = new BootstrapMatchingRule( "2.5.13.11", registries );
+        mrule.setNames( new String[]
+            { "caseIgnoreListMatch" } );
+        mrule.setSyntaxOid( "1.3.6.1.4.1.1466.115.121.1.41" );
+        cb.schemaObjectProduced( this, mrule.getOid(), mrule );
+
+        mrule = new BootstrapMatchingRule( "2.5.13.14", registries );
+        mrule.setNames( new String[]
+            { "integerMatch" } );
+        mrule.setSyntaxOid( "1.3.6.1.4.1.1466.115.121.1.27" );
+        cb.schemaObjectProduced( this, mrule.getOid(), mrule );
+
+        /*
+         * Straight out of RFC 3698: Section 2.7
+         * http://www.faqs.org/rfcs/rfc3698.html
+         * =======================================
+         ( 2.5.13.15 NAME 'integerOrderingMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )
+         */
+
+        mrule = new BootstrapMatchingRule( "2.5.13.15", registries );
+        mrule.setNames( new String[]
+            { "integerOrderingMatch" } );
+        mrule.setSyntaxOid( "1.3.6.1.4.1.1466.115.121.1.27" );
+        cb.schemaObjectProduced( this, mrule.getOid(), mrule );
+
+        mrule = new BootstrapMatchingRule( "2.5.13.16", registries );
+        mrule.setNames( new String[]
+            { "bitStringMatch" } );
+        mrule.setSyntaxOid( "1.3.6.1.4.1.1466.115.121.1.6" );
+        cb.schemaObjectProduced( this, mrule.getOid(), mrule );
+
+        mrule = new BootstrapMatchingRule( "2.5.13.17", registries );
+        mrule.setNames( new String[]
+            { "octetStringMatch" } );
+        mrule.setSyntaxOid( "1.3.6.1.4.1.1466.115.121.1.40" );
+        cb.schemaObjectProduced( this, mrule.getOid(), mrule );
+
+        /*
+         * Straight out of RFC 2252: Section 8
+         * =======================================
+         ( 2.5.13.20 NAME 'telephoneNumberMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.50 )
+
+         ( 2.5.13.21 NAME 'telephoneNumberSubstringsMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )
+
+         ( 2.5.13.22 NAME 'presentationAddressMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.43 )
+
+         ( 2.5.13.23 NAME 'uniqueMemberMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.34 )
+
+         ( 2.5.13.24 NAME 'protocolInformationMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.42 )
+         */
+
+        mrule = new BootstrapMatchingRule( "2.5.13.20", registries );
+        mrule.setNames( new String[]
+            { "telephoneNumberMatch" } );
+        mrule.setSyntaxOid( "1.3.6.1.4.1.1466.115.121.1.50" );
+        cb.schemaObjectProduced( this, mrule.getOid(), mrule );
+
+        mrule = new BootstrapMatchingRule( "2.5.13.21", registries );
+        mrule.setNames( new String[]
+            { "telephoneNumberSubstringsMatch" } );
+        mrule.setSyntaxOid( "1.3.6.1.4.1.1466.115.121.1.58" );
+        cb.schemaObjectProduced( this, mrule.getOid(), mrule );
+
+        mrule = new BootstrapMatchingRule( "2.5.13.22", registries );
+        mrule.setNames( new String[]
+            { "presentationAddressMatch" } );
+        mrule.setSyntaxOid( "1.3.6.1.4.1.1466.115.121.1.43" );
+        cb.schemaObjectProduced( this, mrule.getOid(), mrule );
+
+        mrule = new BootstrapMatchingRule( "2.5.13.23", registries );
+        mrule.setNames( new String[]
+            { "uniqueMemberMatch" } );
+        mrule.setSyntaxOid( "1.3.6.1.4.1.1466.115.121.1.34" );
+        cb.schemaObjectProduced( this, mrule.getOid(), mrule );
+
+        mrule = new BootstrapMatchingRule( "2.5.13.24", registries );
+        mrule.setNames( new String[]
+            { "protocolInformationMatch" } );
+        mrule.setSyntaxOid( "1.3.6.1.4.1.1466.115.121.1.42" );
+        cb.schemaObjectProduced( this, mrule.getOid(), mrule );
+
+        /*
+         * Straight out of RFC 2252: Section 8
+         * =======================================
+         ( 2.5.13.27 NAME 'generalizedTimeMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 )
+
+         ( 2.5.13.28 NAME 'generalizedTimeOrderingMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 )
+
+         ( 2.5.13.29 NAME 'integerFirstComponentMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )
+
+         ( 2.5.13.30 NAME 'objectIdentifierFirstComponentMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )
+
+         ( 1.3.6.1.4.1.1466.109.114.1 NAME 'caseExactIA5Match'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+
+         ( 1.3.6.1.4.1.1466.109.114.2 NAME 'caseIgnoreIA5Match'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+
+         */
+
+        mrule = new BootstrapMatchingRule( "2.5.13.27", registries );
+        mrule.setNames( new String[]
+            { "generalizedTimeMatch" } );
+        mrule.setSyntaxOid( "1.3.6.1.4.1.1466.115.121.1.24" );
+        cb.schemaObjectProduced( this, mrule.getOid(), mrule );
+
+        mrule = new BootstrapMatchingRule( "2.5.13.28", registries );
+        mrule.setNames( new String[]
+            { "generalizedTimeOrderingMatch" } );
+        mrule.setSyntaxOid( "1.3.6.1.4.1.1466.115.121.1.24" );
+        cb.schemaObjectProduced( this, mrule.getOid(), mrule );
+
+        mrule = new BootstrapMatchingRule( "2.5.13.29", registries );
+        mrule.setNames( new String[]
+            { "integerFirstComponentMatch" } );
+        mrule.setSyntaxOid( "1.3.6.1.4.1.1466.115.121.1.27" );
+        cb.schemaObjectProduced( this, mrule.getOid(), mrule );
+
+        mrule = new BootstrapMatchingRule( "2.5.13.30", registries );
+        mrule.setNames( new String[]
+            { "objectIdentifierFirstComponentMatch" } );
+        mrule.setSyntaxOid( "1.3.6.1.4.1.1466.115.121.1.38" );
+        cb.schemaObjectProduced( this, mrule.getOid(), mrule );
+
+        /*
+         * Straight out of RFC 3698: Section 2.6
+         * http://www.faqs.org/rfcs/rfc3698.html
+         * =======================================
+         * ( 2.5.13.31 NAME 'directoryStringFirstComponentMatch'
+         *   SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+         */
+
+        mrule = new BootstrapMatchingRule( "2.5.13.31", registries );
+        mrule.setNames( new String[]
+            { "directoryStringFirstComponentMatch" } );
+        mrule.setSyntaxOid( "1.3.6.1.4.1.1466.115.121.1.15" );
+        cb.schemaObjectProduced( this, mrule.getOid(), mrule );
+
+        mrule = new BootstrapMatchingRule( "1.3.6.1.4.1.1466.109.114.1", registries );
+        mrule.setNames( new String[]
+            { "caseExactIA5Match" } );
+        mrule.setSyntaxOid( "1.3.6.1.4.1.1466.115.121.1.26" );
+        cb.schemaObjectProduced( this, mrule.getOid(), mrule );
+
+        mrule = new BootstrapMatchingRule( "1.3.6.1.4.1.1466.109.114.2", registries );
+        mrule.setNames( new String[]
+            { "caseIgnoreIA5Match" } );
+        mrule.setSyntaxOid( "1.3.6.1.4.1.1466.115.121.1.26" );
+        cb.schemaObjectProduced( this, mrule.getOid(), mrule );
+
+        /*
+         * MatchingRules from section 2 of http://www.faqs.org/rfcs/rfc3698.html
+         * for Additional MatchingRules
+
+         ( 2.5.13.13 NAME 'booleanMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 )
+
+         ( 2.5.13.18 NAME 'octetStringOrderingMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )
+
+         */
+
+        mrule = new BootstrapMatchingRule( "2.5.13.13", registries );
+        mrule.setNames( new String[]
+            { "booleanMatch" } );
+        mrule.setSyntaxOid( "1.3.6.1.4.1.1466.115.121.1.7" );
+        cb.schemaObjectProduced( this, mrule.getOid(), mrule );
+
+        mrule = new BootstrapMatchingRule( "2.5.13.18", registries );
+        mrule.setNames( new String[]
+            { "octetStringOrderingMatch" } );
+        mrule.setSyntaxOid( "1.3.6.1.4.1.1466.115.121.1.40" );
+        cb.schemaObjectProduced( this, mrule.getOid(), mrule );
+
+        /*
+         * Straight out of RFC 2798 for InetOrgPerson: Section 9.3.3
+         * =========================================================
+
+         ( 2.5.13.5 NAME 'caseExactMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+
+         ( 2.5.13.7 NAME 'caseExactSubstringsMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )
+
+         ( 2.5.13.12 NAME 'caseIgnoreListSubstringsMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )
+         */
+
+        mrule = new BootstrapMatchingRule( "2.5.13.5", registries );
+        mrule.setNames( new String[]
+            { "caseExactMatch" } );
+        mrule.setSyntaxOid( "1.3.6.1.4.1.1466.115.121.1.15" );
+        cb.schemaObjectProduced( this, mrule.getOid(), mrule );
+
+        mrule = new BootstrapMatchingRule( "2.5.13.7", registries );
+        mrule.setNames( new String[]
+            { "caseExactSubstringsMatch" } );
+        mrule.setSyntaxOid( "1.3.6.1.4.1.1466.115.121.1.58" );
+        cb.schemaObjectProduced( this, mrule.getOid(), mrule );
+
+        mrule = new BootstrapMatchingRule( "2.5.13.12", registries );
+        mrule.setNames( new String[]
+            { "caseIgnoreListSubstringsMatch" } );
+        mrule.setSyntaxOid( "1.3.6.1.4.1.1466.115.121.1.58" );
+        cb.schemaObjectProduced( this, mrule.getOid(), mrule );
+
+        /*
+         * Straight out of RFC 2798 for InetOrgPerson: Section 9.3.4
+         * =========================================================
+
+         ( 1.3.6.1.4.1.1466.109.114.3 NAME 'caseIgnoreIA5SubstringsMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )
+         */
+
+        mrule = new BootstrapMatchingRule( "1.3.6.1.4.1.1466.109.114.3", registries );
+        mrule.setNames( new String[]
+            { "caseIgnoreIA5SubstringsMatch" } );
+        mrule.setSyntaxOid( "1.3.6.1.4.1.1466.115.121.1.58" );
+        cb.schemaObjectProduced( this, mrule.getOid(), mrule );
+    }
+}
diff --git a/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/SystemNormalizerProducer.java b/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/SystemNormalizerProducer.java
new file mode 100644
index 0000000..22534ac
--- /dev/null
+++ b/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/SystemNormalizerProducer.java
@@ -0,0 +1,388 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.bootstrap;
+
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.schema.DnNormalizer;
+import org.apache.directory.server.schema.NameAndOptionalUIDNormalizer;
+import org.apache.directory.server.schema.bootstrap.ProducerTypeEnum;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.schema.CachingNormalizer;
+import org.apache.directory.shared.ldap.schema.DeepTrimNormalizer;
+import org.apache.directory.shared.ldap.schema.DeepTrimToLowerNormalizer;
+import org.apache.directory.shared.ldap.schema.NoOpNormalizer;
+import org.apache.directory.shared.ldap.schema.Normalizer;
+import org.apache.directory.shared.ldap.schema.ObjectIdentifierNormalizer;
+
+
+/**
+ * A bootstrap producer which creates and announces newly created Normalizers
+ * for various matchingRules in the core schema.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class SystemNormalizerProducer extends AbstractBootstrapProducer
+{
+    public SystemNormalizerProducer()
+    {
+        super( ProducerTypeEnum.NORMALIZER_PRODUCER );
+    }
+
+    
+    public static class CachingDeepTrimToLowerNormalizer extends CachingNormalizer
+    {
+        private static final long serialVersionUID = 1L;
+
+        public CachingDeepTrimToLowerNormalizer()
+        {
+            super( new DeepTrimToLowerNormalizer() );
+        }
+    }
+    
+    
+    public static class CachingDeepTrimNormalizer extends CachingNormalizer
+    {
+        private static final long serialVersionUID = 1L;
+
+        public CachingDeepTrimNormalizer()
+        {
+            super( new DeepTrimNormalizer() );
+        }
+    }
+    
+    
+    public static class CachingDnNormalizer extends CachingNormalizer
+    {
+        private static final long serialVersionUID = 1L;
+
+        /** Used for looking up the setRegistries(Registries) method */
+        private final static Class[] parameterTypes = new Class[] { Registries.class };
+
+        
+        public CachingDnNormalizer()
+        {
+            super( new DnNormalizer() );
+        }
+
+        
+        public void setRegistries( Registries registries ) throws NamingException
+        {
+            injectRegistries( super.normalizer, registries );
+        }
+        
+        
+        private void injectRegistries( Object obj, Registries registries ) throws NamingException
+        {
+            String className = obj.getClass().getName();
+            
+            try
+            {
+                Method method = obj.getClass().getMethod( "setRegistries", parameterTypes );
+                
+                if ( method == null )
+                {
+                    return;
+                }
+                
+                Object[] args = new Object[] { registries };
+                method.invoke( obj, args );
+            }
+            catch ( SecurityException e )
+            {
+                NamingException ne = new NamingException( "SyntaxChecker class "+ className 
+                    + " could not have the Registries dependency injected." );
+                ne.setRootCause( e );
+                throw ne;
+            }
+            catch ( NoSuchMethodException e )
+            {
+                // this is ok since not every object may have setRegistries()
+            }
+            catch ( IllegalArgumentException e )
+            {
+                NamingException ne = new NamingException( "SyntaxChecker class "+ className 
+                    + " could not have the Registries dependency injected." );
+                ne.setRootCause( e );
+                throw ne;
+            }
+            catch ( IllegalAccessException e )
+            {
+                NamingException ne = new NamingException( "SyntaxChecker class "+ className 
+                    + " could not have the Registries dependency injected." );
+                ne.setRootCause( e );
+                throw ne;
+            }
+            catch ( InvocationTargetException e )
+            {
+                NamingException ne = new NamingException( "SyntaxChecker class "+ className 
+                    + " could not have the Registries dependency injected." );
+                ne.setRootCause( e );
+                throw ne;
+            }
+        }
+    }
+    
+
+    public void produce( Registries registries, ProducerCallback cb ) throws NamingException
+    {
+        Normalizer normalizer;
+
+        /*
+         * Straight out of RFC 2252: Section 8
+         * =======================================
+
+         ( 2.5.13.1 NAME 'distinguishedNameMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
+         */
+        normalizer = new CachingDnNormalizer();
+        ( ( CachingDnNormalizer ) normalizer ).setRegistries( registries );
+        cb.schemaObjectProduced( this, "2.5.13.1", normalizer );
+
+        /*
+         ( 1.3.6.1.4.1.1466.109.114.2 NAME 'caseIgnoreIA5Match'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+         */
+        normalizer = new CachingDeepTrimToLowerNormalizer();
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.109.114.2", normalizer );
+
+        /*
+         ( 2.5.13.11 NAME 'caseIgnoreListMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.41 )
+         */
+        normalizer = new CachingDeepTrimToLowerNormalizer();
+        cb.schemaObjectProduced( this, "2.5.13.11", normalizer );
+
+        /*
+         ( 2.5.13.2 NAME 'caseIgnoreMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+         */
+        normalizer = new CachingDeepTrimToLowerNormalizer();
+        cb.schemaObjectProduced( this, "2.5.13.2", normalizer );
+
+        /*
+         ( 2.5.13.3 NAME 'caseIgnoreOrderingMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+         */
+        normalizer = new CachingDeepTrimToLowerNormalizer();
+        cb.schemaObjectProduced( this, "2.5.13.3", normalizer );
+
+        /*
+         ( 2.5.13.4 NAME 'caseIgnoreSubstringsMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )
+         */
+        normalizer = new CachingDeepTrimToLowerNormalizer();
+        cb.schemaObjectProduced( this, "2.5.13.4", normalizer );
+
+        /*
+         ( 2.5.13.6 NAME 'caseExactOrderingMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+         */
+        normalizer = new NoOpNormalizer();
+        cb.schemaObjectProduced( this, "2.5.13.6", normalizer );
+
+        /*
+         ( 2.5.13.0 NAME 'objectIdentifierMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )
+         */
+        normalizer = new ObjectIdentifierNormalizer();
+        cb.schemaObjectProduced( this, "2.5.13.0", normalizer );
+
+        /*
+         ( 2.5.13.8 NAME 'numericStringMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.36 )
+         */
+        normalizer = new NoOpNormalizer();
+        cb.schemaObjectProduced( this, "2.5.13.8", normalizer );
+
+        /*
+         ( 2.5.13.10 NAME 'numericStringSubstringsMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )
+         */
+        normalizer = new NoOpNormalizer();
+        cb.schemaObjectProduced( this, "2.5.13.10", normalizer );
+
+        /*
+         ( 2.5.13.14 NAME 'integerMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )
+         */
+        normalizer = new NoOpNormalizer();
+        cb.schemaObjectProduced( this, "2.5.13.14", normalizer );
+
+        /*
+         ( 2.5.13.14 NAME 'integerOrderingMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )
+         */
+        normalizer = new NoOpNormalizer();
+        cb.schemaObjectProduced( this, "2.5.13.15", normalizer );
+
+        /*
+         ( 2.5.13.16 NAME 'bitStringMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.6 )
+         */
+        normalizer = new NoOpNormalizer();
+        cb.schemaObjectProduced( this, "2.5.13.16", normalizer );
+
+        /*
+         ( 2.5.13.17 NAME 'octetStringMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )
+         */
+        normalizer = new NoOpNormalizer();
+        cb.schemaObjectProduced( this, "2.5.13.17", normalizer );
+
+        /*
+         ( 2.5.13.18 NAME 'octetStringOrderingMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )
+         */
+        normalizer = new NoOpNormalizer();
+        cb.schemaObjectProduced( this, "2.5.13.18", normalizer );
+
+        /*
+         ( 2.5.13.20 NAME 'telephoneNumberMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.50 )
+         */
+        normalizer = new NoOpNormalizer();
+        cb.schemaObjectProduced( this, "2.5.13.20", normalizer );
+
+        /*
+         ( 2.5.13.21 NAME 'telephoneNumberSubstringsMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )
+         */
+        normalizer = new NoOpNormalizer();
+        cb.schemaObjectProduced( this, "2.5.13.21", normalizer );
+
+        /*
+         ( 2.5.13.22 NAME 'presentationAddressMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.43 )
+         */
+        normalizer = new NoOpNormalizer();
+        cb.schemaObjectProduced( this, "2.5.13.22", normalizer );
+
+        /*
+         ( 2.5.13.23 NAME 'uniqueMemberMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.34 )
+         */
+        normalizer = new NameAndOptionalUIDNormalizer();
+        cb.schemaObjectProduced( this, "2.5.13.23", normalizer );
+
+        /*
+         ( 2.5.13.24 NAME 'protocolInformationMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.42 )
+         */
+        normalizer = new CachingDeepTrimNormalizer();
+        cb.schemaObjectProduced( this, "2.5.13.24", normalizer );
+
+        /*
+         ( 2.5.13.27 NAME 'generalizedTimeMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 )
+         */
+        normalizer = new CachingDeepTrimNormalizer();
+        cb.schemaObjectProduced( this, "2.5.13.27", normalizer );
+
+        /*
+         ( 2.5.13.28 NAME 'generalizedTimeOrderingMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 )
+         */
+        normalizer = new CachingDeepTrimNormalizer();
+        cb.schemaObjectProduced( this, "2.5.13.28", normalizer );
+
+        /*
+         ( 2.5.13.29 NAME 'integerFirstComponentMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )
+         */
+        normalizer = new NoOpNormalizer();
+        cb.schemaObjectProduced( this, "2.5.13.29", normalizer );
+
+        /*
+         ( 2.5.13.30 NAME 'objectIdentifierFirstComponentMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )
+         */
+        normalizer = new NoOpNormalizer();
+        cb.schemaObjectProduced( this, "2.5.13.30", normalizer );
+
+        /*
+         * Straight out of RFC 3698: Section 2.6
+         * http://www.faqs.org/rfcs/rfc3698.html
+         * =======================================
+         * ( 2.5.13.31 NAME 'directoryStringFirstComponentMatch'
+         *   SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+         */
+        normalizer = new NoOpNormalizer();
+        cb.schemaObjectProduced( this, "2.5.13.31", normalizer );
+
+        /*
+         ( 1.3.6.1.4.1.1466.109.114.1 NAME 'caseExactIA5Match'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+         */
+        normalizer = new CachingDeepTrimNormalizer();
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.109.114.1", normalizer );
+
+        /*
+         * MatchingRules from section 2 of http://www.faqs.org/rfcs/rfc3698.html
+         * for Additional MatchingRules
+
+         ( 2.5.13.13 NAME 'booleanMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 )
+
+         */
+
+        normalizer = new NoOpNormalizer();
+        cb.schemaObjectProduced( this, "2.5.13.13", normalizer );
+
+        /*
+         * Straight out of RFC 2798 for InetOrgPerson: Section 9.3.3
+         * =========================================================
+
+         ( 2.5.13.5 NAME 'caseExactMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+
+         ( 2.5.13.7 NAME 'caseExactSubstringsMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )
+
+         ( 2.5.13.12 NAME 'caseIgnoreListSubstringsMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )
+         */
+
+        normalizer = new CachingDeepTrimNormalizer();
+        cb.schemaObjectProduced( this, "2.5.13.5", normalizer );
+
+        normalizer = new CachingDeepTrimNormalizer();
+        cb.schemaObjectProduced( this, "2.5.13.7", normalizer );
+
+        normalizer = new CachingDeepTrimToLowerNormalizer();
+        cb.schemaObjectProduced( this, "2.5.13.12", normalizer );
+
+        /*
+         * Straight out of RFC 2798 for InetOrgPerson: Section 9.3.4
+         * =========================================================
+
+         ( 1.3.6.1.4.1.1466.109.114.3 NAME 'caseIgnoreIA5SubstringsMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )
+         */
+
+        normalizer = new CachingDeepTrimToLowerNormalizer();
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.109.114.3", normalizer );
+    }
+}
diff --git a/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/SystemSyntaxCheckerProducer.java b/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/SystemSyntaxCheckerProducer.java
new file mode 100644
index 0000000..05ead9a
--- /dev/null
+++ b/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/SystemSyntaxCheckerProducer.java
@@ -0,0 +1,262 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.bootstrap;
+
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.schema.bootstrap.ProducerTypeEnum;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.schema.syntax.ACIItemSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.AcceptAllSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.AccessPointSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.AttributeTypeDescriptionSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.AudioSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.BinarySyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.BitStringSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.BooleanSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.CertificateListSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.CertificatePairSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.CertificateSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.CountrySyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.DITContentRuleDescriptionSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.DITStructureRuleDescriptionSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.DLSubmitPermissionSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.DNSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.DSAQualitySyntaxSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.DSETypeSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.DataQualitySyntaxSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.DeliveryMethodSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.DirectoryStringSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.EnhancedGuideSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.FacsimileTelephoneNumberSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.FaxSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.GeneralizedTimeSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.GuideSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.Ia5StringSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.IntegerSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.JpegSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.LdapSyntaxDescriptionSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.MHSORAddressSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.MailPreferenceSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.MasterAndShadowAccessPointSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.MatchingRuleDescriptionSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.MatchingRuleUseDescriptionSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.NameAndOptionalUIDSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.NameFormDescriptionSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.NumericStringSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.ObjectClassDescriptionSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.OctetStringSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.OidSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.OtherMailboxSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.PostalAddressSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.PresentationAddressSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.PrintableStringSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.ProtocolInformationSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.SubstringAssertionSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.SubtreeSpecificationSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.SupplierAndConsumerSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.SupplierInformationSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.SupplierOrConsumerSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.SupportedAlgorithmSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.TelephoneNumberSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.TeletexTerminalIdentifierSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.TelexNumberSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.UtcTimeSyntaxChecker;
+
+
+/**
+ * A producer of SyntaxCheckers for the core schema.
+ *
+ * @todo now we use do nothing checkers for place holder and will add as we go 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class SystemSyntaxCheckerProducer extends AbstractBootstrapProducer
+{
+    /**
+     * Creates a producer which produces all 58 of the core schema syntax's
+     * SyntaxCheckers.
+     */
+    public SystemSyntaxCheckerProducer()
+    {
+        super( ProducerTypeEnum.SYNTAX_CHECKER_PRODUCER );
+    }
+
+
+    public void produce( Registries registries, ProducerCallback cb ) throws NamingException
+    {
+        /*
+         * We are going to need a syntax checker for each and every one of
+         * these syntaxes.  However right now we're probably not going to be
+         * turning on syntax checking or are not as interested in it.  So we
+         * can put in place simple do nothing syntax checkers - which is really
+         * the binary syntax checker.
+         */
+
+        /*
+         * From RFC 2252 Section 4.3.2. on Syntax Object Identifiers
+         */
+
+        /*
+         * Value being represented        H-R OBJECT IDENTIFIER
+         * ==================================================================
+         * 0 ACI Item                         N  1.3.6.1.4.1.1466.115.121.1.1
+         * 1 Access Point                     Y  1.3.6.1.4.1.1466.115.121.1.2
+         * 2 Attribute Type Description       Y  1.3.6.1.4.1.1466.115.121.1.3
+         * 3 Audio                            N  1.3.6.1.4.1.1466.115.121.1.4
+         * 4 Binary                           N  1.3.6.1.4.1.1466.115.121.1.5
+         * 5 Bit String                       Y  1.3.6.1.4.1.1466.115.121.1.6
+         * 6 Boolean                          Y  1.3.6.1.4.1.1466.115.121.1.7
+         * 7 Certificate                      N  1.3.6.1.4.1.1466.115.121.1.8
+         * 8 Certificate List                 N  1.3.6.1.4.1.1466.115.121.1.9
+         * 9 Certificate Pair                 N  1.3.6.1.4.1.1466.115.121.1.10
+         */
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.115.121.1.1", new ACIItemSyntaxChecker() );
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.115.121.1.2", new AccessPointSyntaxChecker() );
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.115.121.1.3", new AttributeTypeDescriptionSyntaxChecker() );
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.115.121.1.4", new AudioSyntaxChecker() );
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.115.121.1.5", new BinarySyntaxChecker() );
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.115.121.1.6", new BitStringSyntaxChecker() );
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.115.121.1.7", new BooleanSyntaxChecker() );
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.115.121.1.8", new CertificateSyntaxChecker() );
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.115.121.1.9", new CertificateListSyntaxChecker() );
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.115.121.1.10", new CertificatePairSyntaxChecker() );
+
+        /*
+         * 10 Country String                  Y  1.3.6.1.4.1.1466.115.121.1.11
+         * 11 DN                              Y  1.3.6.1.4.1.1466.115.121.1.12
+         * 12 Data Quality Syntax             Y  1.3.6.1.4.1.1466.115.121.1.13
+         * 13 Delivery Method                 Y  1.3.6.1.4.1.1466.115.121.1.14
+         * 14 Directory String                Y  1.3.6.1.4.1.1466.115.121.1.15
+         * 15 DIT Content Rule Description    Y  1.3.6.1.4.1.1466.115.121.1.16
+         * 16 DIT Structure Rule Description  Y  1.3.6.1.4.1.1466.115.121.1.17
+         * 17 DL Submit Permission            Y  1.3.6.1.4.1.1466.115.121.1.18
+         * 18 DSA Quality Syntax              Y  1.3.6.1.4.1.1466.115.121.1.19
+         * 19 DSE Type                        Y  1.3.6.1.4.1.1466.115.121.1.20
+         */
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.115.121.1.11", new CountrySyntaxChecker() );
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.115.121.1.12", new DNSyntaxChecker() );
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.115.121.1.13", new DataQualitySyntaxSyntaxChecker() );
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.115.121.1.14", new DeliveryMethodSyntaxChecker() );
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.115.121.1.15", new DirectoryStringSyntaxChecker() );
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.115.121.1.16", new DITContentRuleDescriptionSyntaxChecker() );
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.115.121.1.17", new DITStructureRuleDescriptionSyntaxChecker() );
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.115.121.1.18", new DLSubmitPermissionSyntaxChecker() );
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.115.121.1.19", new DSAQualitySyntaxSyntaxChecker() );
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.115.121.1.20", new DSETypeSyntaxChecker() );
+
+        /* 20 Enhanced Guide                  Y  1.3.6.1.4.1.1466.115.121.1.21
+         * 21 Facsimile Telephone Number      Y  1.3.6.1.4.1.1466.115.121.1.22
+         * 22 Fax                             N  1.3.6.1.4.1.1466.115.121.1.23
+         * 23 Generalized Time                Y  1.3.6.1.4.1.1466.115.121.1.24
+         * 24 Guide                           Y  1.3.6.1.4.1.1466.115.121.1.25
+         * 25 IA5 String                      Y  1.3.6.1.4.1.1466.115.121.1.26
+         * 26 INTEGER                         Y  1.3.6.1.4.1.1466.115.121.1.27
+         * 27 JPEG                            N  1.3.6.1.4.1.1466.115.121.1.28
+         * 28 Master And Shadow Access Points Y  1.3.6.1.4.1.1466.115.121.1.29
+         * 29 Matching Rule Description       Y  1.3.6.1.4.1.1466.115.121.1.30
+         */
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.115.121.1.21", new EnhancedGuideSyntaxChecker() );
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.115.121.1.22", new FacsimileTelephoneNumberSyntaxChecker() );
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.115.121.1.23", new FaxSyntaxChecker() );
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.115.121.1.24", new GeneralizedTimeSyntaxChecker() );
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.115.121.1.25", new GuideSyntaxChecker() );
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.115.121.1.26", new Ia5StringSyntaxChecker() );
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.115.121.1.27", new IntegerSyntaxChecker() );
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.115.121.1.28", new JpegSyntaxChecker() );
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.115.121.1.29", new MasterAndShadowAccessPointSyntaxChecker() );
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.115.121.1.30", new MatchingRuleDescriptionSyntaxChecker() );
+
+        /* 30 Matching Rule Use Description   Y  1.3.6.1.4.1.1466.115.121.1.31
+         * 31 Mail Preference                 Y  1.3.6.1.4.1.1466.115.121.1.32
+         * 32 MHS OR Address                  Y  1.3.6.1.4.1.1466.115.121.1.33
+         * 33 Name And Optional UID           Y  1.3.6.1.4.1.1466.115.121.1.34
+         * 34 Name Form Description           Y  1.3.6.1.4.1.1466.115.121.1.35
+         * 35 Numeric String                  Y  1.3.6.1.4.1.1466.115.121.1.36
+         * 36 Object Class Description        Y  1.3.6.1.4.1.1466.115.121.1.37
+         * 37 OID                             Y  1.3.6.1.4.1.1466.115.121.1.38
+         * 38 Other Mailbox                   Y  1.3.6.1.4.1.1466.115.121.1.39
+         * 39 Octet String                    Y  1.3.6.1.4.1.1466.115.121.1.40
+         */
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.115.121.1.31", new MatchingRuleUseDescriptionSyntaxChecker() );
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.115.121.1.32", new MailPreferenceSyntaxChecker() );
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.115.121.1.33", new MHSORAddressSyntaxChecker() );
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.115.121.1.34", new NameAndOptionalUIDSyntaxChecker() );
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.115.121.1.35", new NameFormDescriptionSyntaxChecker() );
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.115.121.1.36", new NumericStringSyntaxChecker() );
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.115.121.1.37", new ObjectClassDescriptionSyntaxChecker() );
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.115.121.1.38", new OidSyntaxChecker() );
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.115.121.1.39", new OtherMailboxSyntaxChecker() );
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.115.121.1.40", new OctetStringSyntaxChecker() );
+
+        /*
+         * 40 Postal Address                  Y  1.3.6.1.4.1.1466.115.121.1.41
+         * 41 Protocol Information            Y  1.3.6.1.4.1.1466.115.121.1.42
+         * 42 Presentation Address            Y  1.3.6.1.4.1.1466.115.121.1.43
+         * 43 Printable String                Y  1.3.6.1.4.1.1466.115.121.1.44
+         * 44 Subtree Specification           Y  1.3.6.1.4.1.1466.115.121.1.45
+         * 45 Supplier Information            Y  1.3.6.1.4.1.1466.115.121.1.46
+         * 46 Supplier Or Consumer            Y  1.3.6.1.4.1.1466.115.121.1.47
+         * 47 Supplier And Consumer           Y  1.3.6.1.4.1.1466.115.121.1.48
+         * 48 Supported Algorithm             N  1.3.6.1.4.1.1466.115.121.1.49
+         * 49 Telephone Number                Y  1.3.6.1.4.1.1466.115.121.1.50
+         */
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.115.121.1.41", new PostalAddressSyntaxChecker() );
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.115.121.1.42", new ProtocolInformationSyntaxChecker() );
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.115.121.1.43", new PresentationAddressSyntaxChecker() );
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.115.121.1.44", new PrintableStringSyntaxChecker() );
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.115.121.1.45", new SubtreeSpecificationSyntaxChecker() );
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.115.121.1.46", new SupplierInformationSyntaxChecker() );
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.115.121.1.47", new SupplierOrConsumerSyntaxChecker() );
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.115.121.1.48", new SupplierAndConsumerSyntaxChecker() );
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.115.121.1.49", new SupportedAlgorithmSyntaxChecker() );
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.115.121.1.50", new TelephoneNumberSyntaxChecker() );
+
+        /*
+         * 50 Teletex Terminal Identifier     Y  1.3.6.1.4.1.1466.115.121.1.51
+         * 51 Telex Number                    Y  1.3.6.1.4.1.1466.115.121.1.52
+         * 52 UTC Time                        Y  1.3.6.1.4.1.1466.115.121.1.53
+         * 53 LDAP Syntax Description         Y  1.3.6.1.4.1.1466.115.121.1.54
+         * 54 Modify Rights                   Y  1.3.6.1.4.1.1466.115.121.1.55  (No defined SC yet)
+         * 55 LDAP BootstrapSchema Definition Y  1.3.6.1.4.1.1466.115.121.1.56  (No defined SC yet) 
+         * 56 LDAP BootstrapSchema DescriptionY  1.3.6.1.4.1.1466.115.121.1.57  (No defined SC yet)
+         * 57 Substring Assertion             Y  1.3.6.1.4.1.1466.115.121.1.58
+         */
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.115.121.1.51", new TeletexTerminalIdentifierSyntaxChecker() );
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.115.121.1.52", new TelexNumberSyntaxChecker() );
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.115.121.1.53", new UtcTimeSyntaxChecker() );
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.115.121.1.54", new LdapSyntaxDescriptionSyntaxChecker() );
+
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.115.121.1.55", 
+            new AcceptAllSyntaxChecker( "1.3.6.1.4.1.1466.115.121.1.55" ) );
+        
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.115.121.1.56", 
+            new AcceptAllSyntaxChecker( "1.3.6.1.4.1.1466.115.121.1.56" ) );
+        
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.115.121.1.57", 
+            new AcceptAllSyntaxChecker( "1.3.6.1.4.1.1466.115.121.1.57" ) );
+        
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.115.121.1.58", new SubstringAssertionSyntaxChecker() );
+
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.1466.115.121.1.59", 
+            new AcceptAllSyntaxChecker( "1.3.6.1.4.1.1466.115.121.1.59" ) );
+    }
+}
diff --git a/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/SystemSyntaxProducer.java b/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/SystemSyntaxProducer.java
new file mode 100755
index 0000000..d496f16
--- /dev/null
+++ b/old_trunk/schema-bootstrap/src/main/java/org/apache/directory/server/schema/bootstrap/SystemSyntaxProducer.java
@@ -0,0 +1,573 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.bootstrap;
+
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.schema.bootstrap.ProducerTypeEnum;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.server.schema.registries.SyntaxCheckerRegistry;
+
+
+/**
+ * A simple Syntax factory for the core LDAP schema in Section 4.3.2 of
+ * <a href="http://www.faqs.org/rfcs/rfc2252.html">RFC2252</a>.
+ * The following table reproduced from RFC2252 shows the syntaxes included
+ * within this SyntaxFactory:
+ * <pre>
+ * Index   Value being represented   H-R     OBJECT IDENTIFIER
+ * =====================================================================
+ * 0 ACI Item                         N  1.3.6.1.4.1.1466.115.121.1.1
+ * 1 Access Point                     Y  1.3.6.1.4.1.1466.115.121.1.2
+ * 2 Attribute Type Description       Y  1.3.6.1.4.1.1466.115.121.1.3
+ * 3 Audio                            N  1.3.6.1.4.1.1466.115.121.1.4
+ * 4 Binary                           N  1.3.6.1.4.1.1466.115.121.1.5
+ * 5 Bit String                       Y  1.3.6.1.4.1.1466.115.121.1.6
+ * 6 Boolean                          Y  1.3.6.1.4.1.1466.115.121.1.7
+ * 7 Certificate                      N  1.3.6.1.4.1.1466.115.121.1.8
+ * 8 Certificate List                 N  1.3.6.1.4.1.1466.115.121.1.9
+ * 9 Certificate Pair                 N  1.3.6.1.4.1.1466.115.121.1.10
+ * 10 Country String                  Y  1.3.6.1.4.1.1466.115.121.1.11
+ * 11 DN                              Y  1.3.6.1.4.1.1466.115.121.1.12
+ * 12 Data Quality Syntax             Y  1.3.6.1.4.1.1466.115.121.1.13
+ * 13 Delivery Method                 Y  1.3.6.1.4.1.1466.115.121.1.14
+ * 14 Directory String                Y  1.3.6.1.4.1.1466.115.121.1.15
+ * 15 DIT Content Rule Description    Y  1.3.6.1.4.1.1466.115.121.1.16
+ * 16 DIT Structure Rule Description  Y  1.3.6.1.4.1.1466.115.121.1.17
+ * 17 DL Submit Permission            Y  1.3.6.1.4.1.1466.115.121.1.18
+ * 18 DSA Quality Syntax              Y  1.3.6.1.4.1.1466.115.121.1.19
+ * 19 DSE Type                        Y  1.3.6.1.4.1.1466.115.121.1.20
+ * 20 Enhanced Guide                  Y  1.3.6.1.4.1.1466.115.121.1.21
+ * 21 Facsimile Telephone Number      Y  1.3.6.1.4.1.1466.115.121.1.22
+ * 22 Fax                             N  1.3.6.1.4.1.1466.115.121.1.23
+ * 23 Generalized Time                Y  1.3.6.1.4.1.1466.115.121.1.24
+ * 24 Guide                           Y  1.3.6.1.4.1.1466.115.121.1.25
+ * 25 IA5 String                      Y  1.3.6.1.4.1.1466.115.121.1.26
+ * 26 INTEGER                         Y  1.3.6.1.4.1.1466.115.121.1.27
+ * 27 JPEG                            N  1.3.6.1.4.1.1466.115.121.1.28
+ * 28 Master And Shadow Access Points Y  1.3.6.1.4.1.1466.115.121.1.29
+ * 29 Matching Rule Description       Y  1.3.6.1.4.1.1466.115.121.1.30
+ * 30 Matching Rule Use Description   Y  1.3.6.1.4.1.1466.115.121.1.31
+ * 31 Mail Preference                 Y  1.3.6.1.4.1.1466.115.121.1.32
+ * 32 MHS OR Address                  Y  1.3.6.1.4.1.1466.115.121.1.33
+ * 33 Name And Optional UID           Y  1.3.6.1.4.1.1466.115.121.1.34
+ * 34 Name Form Description           Y  1.3.6.1.4.1.1466.115.121.1.35
+ * 35 Numeric String                  Y  1.3.6.1.4.1.1466.115.121.1.36
+ * 36 Object Class Description        Y  1.3.6.1.4.1.1466.115.121.1.37
+ * 37 OID                             Y  1.3.6.1.4.1.1466.115.121.1.38
+ * 38 Other Mailbox                   Y  1.3.6.1.4.1.1466.115.121.1.39
+ *
+ * 39 Octet String                    Y  1.3.6.1.4.1.1466.115.121.1.40
+ *
+ * This is not going to be followed for OctetString which needs to be treated
+ * as binary data.
+ *
+ * 40 Postal Address                  Y  1.3.6.1.4.1.1466.115.121.1.41
+ * 41 Protocol Information            Y  1.3.6.1.4.1.1466.115.121.1.42
+ * 42 Presentation Address            Y  1.3.6.1.4.1.1466.115.121.1.43
+ * 43 Printable String                Y  1.3.6.1.4.1.1466.115.121.1.44
+ * 44 Subtree Specification           Y  1.3.6.1.4.1.1466.115.121.1.45
+ * 45 Supplier Information            Y  1.3.6.1.4.1.1466.115.121.1.46
+ * 46 Supplier Or Consumer            Y  1.3.6.1.4.1.1466.115.121.1.47
+ * 47 Supplier And Consumer           Y  1.3.6.1.4.1.1466.115.121.1.48
+ * 48 Supported Algorithm             N  1.3.6.1.4.1.1466.115.121.1.49
+ * 49 Telephone Number                Y  1.3.6.1.4.1.1466.115.121.1.50
+ * 50 Teletex Terminal Identifier     Y  1.3.6.1.4.1.1466.115.121.1.51
+ * 51 Telex Number                    Y  1.3.6.1.4.1.1466.115.121.1.52
+ * 52 UTC Time                        Y  1.3.6.1.4.1.1466.115.121.1.53
+ * 53 LDAP Syntax Description         Y  1.3.6.1.4.1.1466.115.121.1.54
+ * 54 Modify Rights                   Y  1.3.6.1.4.1.1466.115.121.1.55
+ * 55 LDAP Schema Definition          Y  1.3.6.1.4.1.1466.115.121.1.56
+ * 56 LDAP Schema Description         Y  1.3.6.1.4.1.1466.115.121.1.57
+ * 57 Substring Assertion             Y  1.3.6.1.4.1.1466.115.121.1.58
+ * </pre>
+ *
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class SystemSyntaxProducer extends AbstractBootstrapProducer
+{
+    public SystemSyntaxProducer()
+    {
+        super( ProducerTypeEnum.SYNTAX_PRODUCER );
+    }
+
+
+    // ------------------------------------------------------------------------
+    // BootstrapProducer Methods
+    // ------------------------------------------------------------------------
+
+    public void produce( Registries registries, ProducerCallback cb ) throws NamingException
+    {
+        BootstrapSyntax syntax;
+        SyntaxCheckerRegistry syntaxCheckerRegistry = registries.getSyntaxCheckerRegistry();
+
+        /*
+         * From RFC 2252 Section 4.3.2. on Syntax Object Identifiers
+         */
+
+        /*
+         * Value being represented        H-R OBJECT IDENTIFIER
+         * ==================================================================
+         * 0 ACI Item                        N  1.3.6.1.4.1.1466.115.121.1.1
+         * 1 Access Point                    Y  1.3.6.1.4.1.1466.115.121.1.2
+         * 2 Attribute Type Description      Y  1.3.6.1.4.1.1466.115.121.1.3
+         * 3 Audio                           N  1.3.6.1.4.1.1466.115.121.1.4
+         * 4 Binary                          N  1.3.6.1.4.1.1466.115.121.1.5
+         * 5 Bit String                      Y  1.3.6.1.4.1.1466.115.121.1.6
+         * 6 Boolean                         Y  1.3.6.1.4.1.1466.115.121.1.7
+         * 7 Certificate                     N  1.3.6.1.4.1.1466.115.121.1.8
+         * 8 Certificate List                N  1.3.6.1.4.1.1466.115.121.1.9
+         * 9 Certificate Pair                N  1.3.6.1.4.1.1466.115.121.1.10
+         */
+        syntax = new BootstrapSyntax( "1.3.6.1.4.1.1466.115.121.1.1", syntaxCheckerRegistry );
+        syntax.setNames( new String[]
+            { "ACI Item" } );
+        // This is in direct conflict with RFC 2252 but for us ACI Item is human readable
+        syntax.setHumanReadable( true );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        syntax = new BootstrapSyntax( "1.3.6.1.4.1.1466.115.121.1.2", syntaxCheckerRegistry );
+        syntax.setNames( new String[]
+            { "Access Point" } );
+        syntax.setHumanReadable( true );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        syntax = new BootstrapSyntax( "1.3.6.1.4.1.1466.115.121.1.3", syntaxCheckerRegistry );
+        syntax.setNames( new String[]
+            { "Attribute Type Description" } );
+        syntax.setHumanReadable( true );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        syntax = new BootstrapSyntax( "1.3.6.1.4.1.1466.115.121.1.4", syntaxCheckerRegistry );
+        syntax.setNames( new String[]
+            { "Audio" } );
+        syntax.setHumanReadable( false );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        syntax = new BootstrapSyntax( "1.3.6.1.4.1.1466.115.121.1.5", syntaxCheckerRegistry );
+        syntax.setNames( new String[]
+            { "Binary" } );
+        syntax.setHumanReadable( false );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        syntax = new BootstrapSyntax( "1.3.6.1.4.1.1466.115.121.1.6", syntaxCheckerRegistry );
+        syntax.setNames( new String[]
+            { "Bit String" } );
+        syntax.setHumanReadable( true );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        syntax = new BootstrapSyntax( "1.3.6.1.4.1.1466.115.121.1.7", syntaxCheckerRegistry );
+        syntax.setNames( new String[]
+            { "Boolean" } );
+        syntax.setHumanReadable( true );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        syntax = new BootstrapSyntax( "1.3.6.1.4.1.1466.115.121.1.8", syntaxCheckerRegistry );
+        syntax.setNames( new String[]
+            { "Certificate" } );
+        syntax.setHumanReadable( false );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        syntax = new BootstrapSyntax( "1.3.6.1.4.1.1466.115.121.1.9", syntaxCheckerRegistry );
+        syntax.setNames( new String[]
+            { "Certificate List" } );
+        syntax.setHumanReadable( false );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        syntax = new BootstrapSyntax( "1.3.6.1.4.1.1466.115.121.1.10", syntaxCheckerRegistry );
+        syntax.setNames( new String[]
+            { "Certificate Pair" } );
+        syntax.setHumanReadable( false );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        /*
+         * Value being represented        H-R OBJECT IDENTIFIER
+         * ===================================================================
+         * 10 Country String                  Y  1.3.6.1.4.1.1466.115.121.1.11
+         * 11 DN                              Y  1.3.6.1.4.1.1466.115.121.1.12
+         * 12 Data Quality Syntax             Y  1.3.6.1.4.1.1466.115.121.1.13
+         * 13 Delivery Method                 Y  1.3.6.1.4.1.1466.115.121.1.14
+         * 14 Directory String                Y  1.3.6.1.4.1.1466.115.121.1.15
+         * 15 DIT Content Rule Description    Y  1.3.6.1.4.1.1466.115.121.1.16
+         * 16 DIT Structure Rule Description  Y  1.3.6.1.4.1.1466.115.121.1.17
+         * 17 DL Submit Permission            Y  1.3.6.1.4.1.1466.115.121.1.18
+         * 18 DSA Quality Syntax              Y  1.3.6.1.4.1.1466.115.121.1.19
+         * 19 DSE Type                        Y  1.3.6.1.4.1.1466.115.121.1.20
+         */
+        syntax = new BootstrapSyntax( "1.3.6.1.4.1.1466.115.121.1.11", syntaxCheckerRegistry );
+        syntax.setNames( new String[]
+            { "Country String" } );
+        syntax.setHumanReadable( true );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        syntax = new BootstrapSyntax( "1.3.6.1.4.1.1466.115.121.1.12", syntaxCheckerRegistry );
+        syntax.setNames( new String[]
+            { "DN" } );
+        syntax.setHumanReadable( true );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        syntax = new BootstrapSyntax( "1.3.6.1.4.1.1466.115.121.1.13", syntaxCheckerRegistry );
+        syntax.setNames( new String[]
+            { "Data Quality Syntax" } );
+        syntax.setHumanReadable( true );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        syntax = new BootstrapSyntax( "1.3.6.1.4.1.1466.115.121.1.14", syntaxCheckerRegistry );
+        syntax.setNames( new String[]
+            { "Delivery Method" } );
+        syntax.setHumanReadable( true );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        syntax = new BootstrapSyntax( "1.3.6.1.4.1.1466.115.121.1.15", syntaxCheckerRegistry );
+        syntax.setNames( new String[]
+            { "Directory String" } );
+        syntax.setHumanReadable( true );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        syntax = new BootstrapSyntax( "1.3.6.1.4.1.1466.115.121.1.16", syntaxCheckerRegistry );
+        syntax.setNames( new String[]
+            { "DIT Content Rule Description" } );
+        syntax.setHumanReadable( true );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        syntax = new BootstrapSyntax( "1.3.6.1.4.1.1466.115.121.1.17", syntaxCheckerRegistry );
+        syntax.setNames( new String[]
+            { "DIT Structure Rule Description" } );
+        syntax.setHumanReadable( true );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        syntax = new BootstrapSyntax( "1.3.6.1.4.1.1466.115.121.1.18", syntaxCheckerRegistry );
+        syntax.setNames( new String[]
+            { "DL Submit Permission" } );
+        syntax.setHumanReadable( true );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        syntax = new BootstrapSyntax( "1.3.6.1.4.1.1466.115.121.1.19", syntaxCheckerRegistry );
+        syntax.setNames( new String[]
+            { "DSA Quality Syntax" } );
+        syntax.setHumanReadable( true );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        syntax = new BootstrapSyntax( "1.3.6.1.4.1.1466.115.121.1.20", syntaxCheckerRegistry );
+        syntax.setNames( new String[]
+            { "DSE Type" } );
+        syntax.setHumanReadable( true );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        /*
+         * Value being represented        H-R OBJECT IDENTIFIER
+         * ===================================================================
+         * 20 Enhanced Guide                  Y  1.3.6.1.4.1.1466.115.121.1.21
+         * 21 Facsimile Telephone Number      Y  1.3.6.1.4.1.1466.115.121.1.22
+         * 22 Fax                             N  1.3.6.1.4.1.1466.115.121.1.23
+         * 23 Generalized Time                Y  1.3.6.1.4.1.1466.115.121.1.24
+         * 24 Guide                           Y  1.3.6.1.4.1.1466.115.121.1.25
+         * 25 IA5 String                      Y  1.3.6.1.4.1.1466.115.121.1.26
+         * 26 INTEGER                         Y  1.3.6.1.4.1.1466.115.121.1.27
+         * 27 JPEG                            N  1.3.6.1.4.1.1466.115.121.1.28
+         * 28 Master And Shadow Access Points Y  1.3.6.1.4.1.1466.115.121.1.29
+         * 29 Matching Rule Description       Y  1.3.6.1.4.1.1466.115.121.1.30
+         */
+        syntax = new BootstrapSyntax( "1.3.6.1.4.1.1466.115.121.1.21", syntaxCheckerRegistry );
+        syntax.setNames( new String[]
+            { "Enhanced Guide" } );
+        syntax.setHumanReadable( true );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        syntax = new BootstrapSyntax( "1.3.6.1.4.1.1466.115.121.1.22", syntaxCheckerRegistry );
+        syntax.setNames( new String[]
+            { "Facsimile Telephone Number" } );
+        syntax.setHumanReadable( true );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        syntax = new BootstrapSyntax( "1.3.6.1.4.1.1466.115.121.1.23", syntaxCheckerRegistry );
+        syntax.setNames( new String[]
+            { "Fax" } );
+        syntax.setHumanReadable( false );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        syntax = new BootstrapSyntax( "1.3.6.1.4.1.1466.115.121.1.24", syntaxCheckerRegistry );
+        syntax.setNames( new String[]
+            { "Generalized Time" } );
+        syntax.setHumanReadable( true );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        syntax = new BootstrapSyntax( "1.3.6.1.4.1.1466.115.121.1.25", syntaxCheckerRegistry );
+        syntax.setNames( new String[]
+            { "Guide" } );
+        syntax.setHumanReadable( true );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        syntax = new BootstrapSyntax( "1.3.6.1.4.1.1466.115.121.1.26", syntaxCheckerRegistry );
+        syntax.setNames( new String[]
+            { "IA5 String" } );
+        syntax.setHumanReadable( true );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        syntax = new BootstrapSyntax( "1.3.6.1.4.1.1466.115.121.1.27", syntaxCheckerRegistry );
+        syntax.setNames( new String[]
+            { "INTEGER" } );
+        syntax.setHumanReadable( true );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        syntax = new BootstrapSyntax( "1.3.6.1.4.1.1466.115.121.1.28", syntaxCheckerRegistry );
+        syntax.setNames( new String[]
+            { "JPEG" } );
+        syntax.setHumanReadable( false );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        syntax = new BootstrapSyntax( "1.3.6.1.4.1.1466.115.121.1.29", syntaxCheckerRegistry );
+        syntax.setNames( new String[]
+            { "Master And Shadow Access Points" } );
+        syntax.setHumanReadable( true );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        syntax = new BootstrapSyntax( "1.3.6.1.4.1.1466.115.121.1.30", syntaxCheckerRegistry );
+        syntax.setNames( new String[]
+            { "Matching Rule Description" } );
+        syntax.setHumanReadable( true );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        /*
+         * Value being represented        H-R OBJECT IDENTIFIER
+         * ==================================================================
+         * 30 Matching Rule Use Description   Y  1.3.6.1.4.1.1466.115.121.1.31
+         * 31 Mail Preference                 Y  1.3.6.1.4.1.1466.115.121.1.32
+         * 32 MHS OR Address                  Y  1.3.6.1.4.1.1466.115.121.1.33
+         * 33 Name And Optional UID           Y  1.3.6.1.4.1.1466.115.121.1.34
+         * 34 Name Form Description           Y  1.3.6.1.4.1.1466.115.121.1.35
+         * 35 Numeric String                  Y  1.3.6.1.4.1.1466.115.121.1.36
+         * 36 Object Class Description        Y  1.3.6.1.4.1.1466.115.121.1.37
+         * 37 OID                             Y  1.3.6.1.4.1.1466.115.121.1.38
+         * 38 Other Mailbox                   Y  1.3.6.1.4.1.1466.115.121.1.39
+         * 39 Octet String                    Y  1.3.6.1.4.1.1466.115.121.1.40
+         */
+        syntax = new BootstrapSyntax( "1.3.6.1.4.1.1466.115.121.1.31", syntaxCheckerRegistry );
+        syntax.setNames( new String[]
+            { "Matching Rule Use Description" } );
+        syntax.setHumanReadable( true );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        syntax = new BootstrapSyntax( "1.3.6.1.4.1.1466.115.121.1.32", syntaxCheckerRegistry );
+        syntax.setNames( new String[]
+            { "Mail Preference" } );
+        syntax.setHumanReadable( true );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        syntax = new BootstrapSyntax( "1.3.6.1.4.1.1466.115.121.1.33", syntaxCheckerRegistry );
+        syntax.setNames( new String[]
+            { "MHS OR Address" } );
+        syntax.setHumanReadable( true );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        syntax = new BootstrapSyntax( "1.3.6.1.4.1.1466.115.121.1.34", syntaxCheckerRegistry );
+        syntax.setNames( new String[]
+            { "Name And Optional UID" } );
+        syntax.setHumanReadable( true );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        syntax = new BootstrapSyntax( "1.3.6.1.4.1.1466.115.121.1.35", syntaxCheckerRegistry );
+        syntax.setNames( new String[]
+            { "Name Form Description" } );
+        syntax.setHumanReadable( true );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        syntax = new BootstrapSyntax( "1.3.6.1.4.1.1466.115.121.1.36", syntaxCheckerRegistry );
+        syntax.setNames( new String[]
+            { "Numeric String" } );
+        syntax.setHumanReadable( true );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        syntax = new BootstrapSyntax( "1.3.6.1.4.1.1466.115.121.1.37", syntaxCheckerRegistry );
+        syntax.setNames( new String[]
+            { "Object Class Description" } );
+        syntax.setHumanReadable( true );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        syntax = new BootstrapSyntax( "1.3.6.1.4.1.1466.115.121.1.38", syntaxCheckerRegistry );
+        syntax.setNames( new String[]
+            { "OID" } );
+        syntax.setHumanReadable( true );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        syntax = new BootstrapSyntax( "1.3.6.1.4.1.1466.115.121.1.39", syntaxCheckerRegistry );
+        syntax.setNames( new String[]
+            { "Other Mailbox" } );
+        syntax.setHumanReadable( true );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        /*
+         * This is where we deviate.  An octet string may or may not be human readable.  Essentially
+         * we are using this property of a syntax to determine if a value should be treated as binary
+         * data or not.  It must be human readable always in order to get this property set to true.
+         *
+         * If we set this to true then userPasswords which implement this syntax are not treated as
+         * binary attributes.  If that happens we can have data corruption due to UTF-8 handling.
+         */
+        syntax = new BootstrapSyntax( "1.3.6.1.4.1.1466.115.121.1.40", syntaxCheckerRegistry );
+        syntax.setNames( new String[]
+            { "Octet String" } );
+        syntax.setHumanReadable( false );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        /*
+         * Value being represented        H-R OBJECT IDENTIFIER
+         * ===================================================================
+         * 40 Postal Address                  Y  1.3.6.1.4.1.1466.115.121.1.41
+         * 41 Protocol Information            Y  1.3.6.1.4.1.1466.115.121.1.42
+         * 42 Presentation Address            Y  1.3.6.1.4.1.1466.115.121.1.43
+         * 43 Printable String                Y  1.3.6.1.4.1.1466.115.121.1.44
+         * 44 Subtree Specification           Y  1.3.6.1.4.1.1466.115.121.1.45
+         * 45 Supplier Information            Y  1.3.6.1.4.1.1466.115.121.1.46
+         * 46 Supplier Or Consumer            Y  1.3.6.1.4.1.1466.115.121.1.47
+         * 47 Supplier And Consumer           Y  1.3.6.1.4.1.1466.115.121.1.48
+         * 48 Supported Algorithm             N  1.3.6.1.4.1.1466.115.121.1.49
+         * 49 Telephone Number                Y  1.3.6.1.4.1.1466.115.121.1.50
+         */
+        syntax = new BootstrapSyntax( "1.3.6.1.4.1.1466.115.121.1.41", syntaxCheckerRegistry );
+        syntax.setNames( new String[]
+            { "Postal Address" } );
+        syntax.setHumanReadable( true );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        syntax = new BootstrapSyntax( "1.3.6.1.4.1.1466.115.121.1.42", syntaxCheckerRegistry );
+        syntax.setNames( new String[]
+            { "Protocol Information" } );
+        syntax.setHumanReadable( true );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        syntax = new BootstrapSyntax( "1.3.6.1.4.1.1466.115.121.1.43", syntaxCheckerRegistry );
+        syntax.setNames( new String[]
+            { "Presentation Address" } );
+        syntax.setHumanReadable( true );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        syntax = new BootstrapSyntax( "1.3.6.1.4.1.1466.115.121.1.44", syntaxCheckerRegistry );
+        syntax.setNames( new String[]
+            { "Printable String" } );
+        syntax.setHumanReadable( true );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        syntax = new BootstrapSyntax( "1.3.6.1.4.1.1466.115.121.1.45", syntaxCheckerRegistry );
+        syntax.setNames( new String[]
+            { "Subtree Specification" } );
+        syntax.setHumanReadable( true );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        syntax = new BootstrapSyntax( "1.3.6.1.4.1.1466.115.121.1.46", syntaxCheckerRegistry );
+        syntax.setNames( new String[]
+            { "Supplier Information" } );
+        syntax.setHumanReadable( true );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        syntax = new BootstrapSyntax( "1.3.6.1.4.1.1466.115.121.1.47", syntaxCheckerRegistry );
+        syntax.setNames( new String[]
+            { "Supplier Or Consumer" } );
+        syntax.setHumanReadable( true );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        syntax = new BootstrapSyntax( "1.3.6.1.4.1.1466.115.121.1.48", syntaxCheckerRegistry );
+        syntax.setNames( new String[]
+            { "Supplier And Consumer" } );
+        syntax.setHumanReadable( true );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        syntax = new BootstrapSyntax( "1.3.6.1.4.1.1466.115.121.1.49", syntaxCheckerRegistry );
+        syntax.setNames( new String[]
+            { "Supported Algorithm" } );
+        syntax.setHumanReadable( false );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        syntax = new BootstrapSyntax( "1.3.6.1.4.1.1466.115.121.1.50", syntaxCheckerRegistry );
+        syntax.setNames( new String[]
+            { "Telephone Number" } );
+        syntax.setHumanReadable( true );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        /*
+         * Value being represented        H-R OBJECT IDENTIFIER
+         * ==================================================================
+         * 50 Teletex Terminal Identifier     Y  1.3.6.1.4.1.1466.115.121.1.51
+         * 51 Telex Number                    Y  1.3.6.1.4.1.1466.115.121.1.52
+         * 52 UTC Time                        Y  1.3.6.1.4.1.1466.115.121.1.53
+         * 53 LDAP Syntax Description         Y  1.3.6.1.4.1.1466.115.121.1.54
+         * 54 Modify Rights                   Y  1.3.6.1.4.1.1466.115.121.1.55
+         * 55 LDAP BootstrapSchema Definition          Y  1.3.6.1.4.1.1466.115.121.1.56
+         * 56 LDAP BootstrapSchema Description         Y  1.3.6.1.4.1.1466.115.121.1.57
+         * 57 Substring Assertion             Y  1.3.6.1.4.1.1466.115.121.1.58
+         */
+        syntax = new BootstrapSyntax( "1.3.6.1.4.1.1466.115.121.1.51", syntaxCheckerRegistry );
+        syntax.setNames( new String[]
+            { "Teletex Terminal Identifier" } );
+        syntax.setHumanReadable( true );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        syntax = new BootstrapSyntax( "1.3.6.1.4.1.1466.115.121.1.52", syntaxCheckerRegistry );
+        syntax.setNames( new String[]
+            { "Telex Number" } );
+        syntax.setHumanReadable( true );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        syntax = new BootstrapSyntax( "1.3.6.1.4.1.1466.115.121.1.53", syntaxCheckerRegistry );
+        syntax.setNames( new String[]
+            { "UTC Time" } );
+        syntax.setHumanReadable( true );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        syntax = new BootstrapSyntax( "1.3.6.1.4.1.1466.115.121.1.54", syntaxCheckerRegistry );
+        syntax.setNames( new String[]
+            { "LDAP Syntax Description" } );
+        syntax.setHumanReadable( true );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        syntax = new BootstrapSyntax( "1.3.6.1.4.1.1466.115.121.1.55", syntaxCheckerRegistry );
+        syntax.setNames( new String[]
+            { "Modify Rights" } );
+        syntax.setHumanReadable( true );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        syntax = new BootstrapSyntax( "1.3.6.1.4.1.1466.115.121.1.56", syntaxCheckerRegistry );
+        syntax.setNames( new String[]
+            { "LDAP BootstrapSchema Definition" } );
+        syntax.setHumanReadable( true );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        syntax = new BootstrapSyntax( "1.3.6.1.4.1.1466.115.121.1.57", syntaxCheckerRegistry );
+        syntax.setNames( new String[]
+            { "LDAP BootstrapSchema Description" } );
+        syntax.setHumanReadable( true );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        syntax = new BootstrapSyntax( "1.3.6.1.4.1.1466.115.121.1.58", syntaxCheckerRegistry );
+        syntax.setNames( new String[]
+            { "Substring Assertion" } );
+        syntax.setHumanReadable( true );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+        
+        syntax = new BootstrapSyntax( "1.3.6.1.4.1.1466.115.121.1.59", syntaxCheckerRegistry );
+        syntax.setNames( new String[]
+            { "Trigger Specification" } );
+        syntax.setHumanReadable( true );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+    }
+}
diff --git a/old_trunk/schema-bootstrap/src/main/schema/apache.schema b/old_trunk/schema-bootstrap/src/main/schema/apache.schema
new file mode 100644
index 0000000..dcd7b4a
--- /dev/null
+++ b/old_trunk/schema-bootstrap/src/main/schema/apache.schema
@@ -0,0 +1,483 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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 Schema
+# =============================================================================
+#
+#              +-----------------------------+-----------------+
+#              |  Apache AttributeType OID   |      name       |
+#              +-----------------------------+-----------------+
+#              | 1.3.6.1.4.1.18060.0.4.1.2.1 | apacheNdn       |
+#              | 1.3.6.1.4.1.18060.0.4.1.2.2 | apacheUpdn      |
+#              | 1.3.6.1.4.1.18060.0.4.1.2.3 | apacheExistance |
+#              | 1.3.6.1.4.1.18060.0.4.1.2.4 | apacheHierarchy |
+#              | 1.3.6.1.4.1.18060.0.4.1.2.5 | apacheOneAlias  |
+#              | 1.3.6.1.4.1.18060.0.4.1.2.6 | apacheSubAlias  |
+#              | 1.3.6.1.4.1.18060.0.4.1.2.7 | apacheAlias     |
+#              +-----------------------------+-----------------+
+# =============================================================================
+
+
+attributetype ( 1.3.6.1.4.1.18060.0.4.1.2.1 NAME 'apacheNdn'
+    DESC 'Index attribute DN whose values are normalized based on schema'
+    EQUALITY exactDnAsStringMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.12
+    SINGLE-VALUE
+    NO-USER-MODIFICATION
+    USAGE dSAOperation )
+
+attributetype ( 1.3.6.1.4.1.18060.0.4.1.2.2 NAME 'apacheUpdn'
+    DESC 'Index attribute for DN whose values are NOT normalized in any way'
+    EQUALITY exactDnAsStringMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.12
+    SINGLE-VALUE
+    NO-USER-MODIFICATION
+    USAGE dSAOperation )
+
+attributetype ( 1.3.6.1.4.1.18060.0.4.1.2.3 NAME 'apacheExistance'
+    DESC 'Index attribute used to track the existence of attributes'
+    SUP name
+    EQUALITY caseIgnoreMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+    NO-USER-MODIFICATION
+    USAGE dSAOperation )
+
+attributetype ( 1.3.6.1.4.1.18060.0.4.1.2.4 NAME 'apacheHierarchy'
+    DESC 'Index attribute used to track the DIT hierarchy'
+    EQUALITY integerMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
+    NO-USER-MODIFICATION
+    USAGE dSAOperation )
+
+attributetype ( 1.3.6.1.4.1.18060.0.4.1.2.5 NAME 'apacheOnealias'
+    DESC 'Index attribute used to track single level aliases'
+    EQUALITY integerMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
+    NO-USER-MODIFICATION
+    USAGE dSAOperation )
+
+attributetype ( 1.3.6.1.4.1.18060.0.4.1.2.6 NAME 'apacheSubalias'
+    DESC 'Index attribute used to track single level aliases'
+    EQUALITY integerMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
+    NO-USER-MODIFICATION
+    USAGE dSAOperation )
+
+attributetype ( 1.3.6.1.4.1.18060.0.4.1.2.7 NAME 'apacheAlias'
+    DESC 'asdf'
+    SUP distinguishedName
+    EQUALITY distinguishedNameMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.12
+    SINGLE-VALUE
+    NO-USER-MODIFICATION
+    USAGE dSAOperation )
+
+attributetype ( 1.3.6.1.4.1.18060.0.4.1.2.9 NAME 'apacheSamType'
+    DESC 'Single-use Authentication Mechanism type/vendor code'
+    EQUALITY integerMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
+    SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.18060.0.4.1.2.8 NAME 'prefNodeName'
+    DESC 'Attribute to describe the name of a Java Preferences API node'
+    EQUALITY caseExactMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+    SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.18060.0.4.1.2.10 NAME 'autonomousAreaSubentry'
+    DESC 'Used to track a subentry associated with an autonomousArea'
+    SUP distinguishedName
+    EQUALITY distinguishedNameMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.12
+    SINGLE-VALUE
+    NO-USER-MODIFICATION
+    USAGE directoryOperation )
+
+attributetype ( 1.3.6.1.4.1.18060.0.4.1.2.11 NAME 'accessControlSubentries'
+    DESC 'Used to track a subentry associated with access control areas'
+    SUP distinguishedName
+    EQUALITY distinguishedNameMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.12
+    NO-USER-MODIFICATION
+    USAGE directoryOperation )
+
+objectclass ( 1.3.6.1.4.1.18060.0.4.1.3.1 NAME 'prefNode'
+    SUP top
+    STRUCTURAL
+    MUST prefNodeName )
+
+#attributetype ( 1.3.6.1.4.1.18060.0.4.1.2.12 NAME 'prescriptiveACI'
+attributetype ( 2.5.24.4 NAME 'prescriptiveACI'
+  DESC 'Access control information that applies to a set of entries'
+  EQUALITY directoryStringFirstComponentMatch
+  SYNTAX 1.3.6.1.4.1.1466.115.121.1.1
+  USAGE directoryOperation )
+
+# was attributetype ( 1.3.6.1.4.1.18060.0.4.1.2.13 NAME 'entryACI' ...
+attributetype ( 2.5.24.5 NAME 'entryACI'
+  DESC 'Access control information that applies to a single entry'
+  EQUALITY directoryStringFirstComponentMatch
+  SYNTAX 1.3.6.1.4.1.1466.115.121.1.1
+  USAGE directoryOperation )
+
+#attributetype ( 1.3.6.1.4.1.18060.0.4.1.2.14 NAME 'subentryACI'
+attributetype ( 2.5.24.6 NAME 'subentryACI'
+  DESC 'Access control information that applies to a single subentry'
+  EQUALITY directoryStringFirstComponentMatch
+  SYNTAX 1.3.6.1.4.1.1466.115.121.1.1
+  USAGE directoryOperation )
+
+objectclass ( 2.5.17.1
+    NAME 'accessControlSubentry'
+    AUXILIARY 
+    MUST prescriptiveACI )
+
+attributetype ( 1.3.6.1.4.1.18060.0.4.1.2.15
+    NAME 'apacheServicePid'
+	DESC 'A string up to 256 characters in length'
+	EQUALITY caseIgnoreIA5Match
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.18060.0.4.1.2.16
+    NAME 'apacheServiceFactoryPid'
+	DESC 'A string up to 256 characters in length'
+	EQUALITY caseIgnoreIA5Match
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} SINGLE-VALUE )
+
+objectclass ( 1.3.6.1.4.1.18060.0.4.1.3.3
+    NAME 'apacheServiceConfiguration'
+    SUP top
+    STRUCTURAL
+    MUST ( cn $ apacheServicePid )
+    MAY ( apacheServiceFactoryPid ) )
+
+objectclass ( 1.3.6.1.4.1.18060.0.4.1.3.4
+    NAME 'apacheFactoryConfiguration'
+    SUP top
+    STRUCTURAL
+    MUST ( cn $ apacheServicePid ) )
+
+attributetype ( 1.3.6.1.4.1.18060.0.4.1.2.17
+    NAME 'apacheCatalogEntryName'
+	DESC 'A string up to 256 characters in length'
+	EQUALITY caseIgnoreIA5Match
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.18060.0.4.1.2.18
+    NAME 'apacheCatalogEntryBaseDn'
+	DESC 'A string up to 256 characters in length'
+	EQUALITY caseIgnoreIA5Match
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} SINGLE-VALUE )
+
+objectclass ( 1.3.6.1.4.1.18060.0.4.1.3.5
+    NAME 'apacheCatalogEntry'
+    SUP top
+    STRUCTURAL
+    MUST ( cn $ apacheCatalogEntryBaseDn )
+    MAY ( apacheCatalogEntryName ) )
+
+attributetype ( 1.3.6.1.4.1.18060.0.4.1.2.19
+    NAME 'windowsFilePath'
+	DESC 'A windows file path where case does not make a difference'
+	EQUALITY caseIgnoreIA5Match
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.18060.0.4.1.2.20
+    NAME 'unixFilePath'
+	DESC 'A UNIX file path where case does make a difference'
+	EQUALITY caseExactIA5Match
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+
+objectclass ( 1.3.6.1.4.1.18060.0.4.1.3.6
+    NAME 'windowsFile'
+    SUP top
+    STRUCTURAL
+    MUST ( windowsFilePath ) )
+
+objectclass ( 1.3.6.1.4.1.18060.0.4.1.3.7
+    NAME 'unixFile'
+    SUP top
+    STRUCTURAL
+    MUST ( unixFilePath ) )
+
+# ===============
+# SP related
+# ===============
+
+attributetype ( 1.3.6.1.4.1.18060.0.4.1.2.21
+    NAME 'fullyQualifiedJavaClassName'
+    DESC 'The fully qualified name for a (Java) class'
+	 EQUALITY caseExactIA5Match
+	 SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.18060.0.4.1.2.22
+    NAME 'javaClassByteCode'
+	 DESC 'The actual byte code for a (Java) class'
+	 SYNTAX 1.3.6.1.4.1.1466.115.121.1.5 SINGLE-VALUE )
+
+objectclass ( 1.3.6.1.4.1.18060.0.4.1.3.8
+    NAME 'javaClass'
+    SUP top
+    STRUCTURAL
+    MUST ( fullyQualifiedJavaClassName $ javaClassByteCode ) )
+
+attributetype ( 1.3.6.1.4.1.18060.0.4.1.2.23
+    NAME 'classLoaderDefaultSearchContext'
+    DESC 'The default search context for the Ldap Class Loader'
+    SUP distinguishedName
+    EQUALITY distinguishedNameMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
+
+# New SP related schema
+
+attributetype ( 1.3.6.1.4.1.18060.0.4.1.5.1
+    NAME 'storedProcLangId'
+    EQUALITY caseExactIA5Match
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+    SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.18060.0.4.1.5.2
+    NAME 'storedProcUnitName'
+    EQUALITY caseExactIA5Match
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+    SINGLE-VALUE )
+
+objectclass ( 1.3.6.1.4.1.18060.0.4.1.5.3
+    NAME 'storedProcUnit'
+    SUP top
+    ABSTRACT
+    MUST ( storedProcLangId $ storedProcUnitName ) )
+
+attributetype ( 1.3.6.1.4.1.18060.0.4.1.5.4
+    NAME 'javaByteCode'
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.5 SINGLE-VALUE )
+
+objectclass ( 1.3.6.1.4.1.18060.0.4.1.5.5
+    NAME 'javaStoredProcUnit'
+    SUP storedProcUnit
+    STRUCTURAL
+    MUST ( javaByteCode ) )
+
+attributetype ( 1.3.6.1.4.1.18060.0.4.1.5.6
+    NAME 'javaxScriptLangId'
+    EQUALITY caseExactIA5Match
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+    SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.18060.0.4.1.5.7
+    NAME 'javaxScriptCode'
+    EQUALITY caseExactIA5Match
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+    SINGLE-VALUE )
+
+objectclass ( 1.3.6.1.4.1.18060.0.4.1.5.8
+    NAME 'javaxScriptStoredProcUnit'
+    SUP storedProcUnit
+    STRUCTURAL
+    MUST ( javaxScriptLangId $ javaxScriptCode ) )
+
+# ===============
+# Trigger related
+# ===============
+
+attributetype ( 1.3.6.1.4.1.18060.0.4.1.2.25 NAME 'prescriptiveTriggerSpecification'
+    DESC 'Trigger specification that applies to a set of entries'
+    EQUALITY directoryStringFirstComponentMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.59
+    USAGE directoryOperation )
+
+attributetype ( 1.3.6.1.4.1.18060.0.4.1.2.26 NAME 'entryTriggerSpecification'
+    DESC 'Trigger specification that applies to a single entry'
+    EQUALITY directoryStringFirstComponentMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.59
+    USAGE directoryOperation )
+
+attributetype ( 1.3.6.1.4.1.18060.0.4.1.2.27 NAME 'triggerExecutionSubentries'
+    DESC 'Used to track subentries associated with a trigger area which an entry falls under'
+    SUP distinguishedName
+    EQUALITY distinguishedNameMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.12
+    NO-USER-MODIFICATION
+    USAGE directoryOperation )
+
+objectclass ( 1.3.6.1.4.1.18060.0.4.1.2.28 NAME 'triggerExecutionSubentry'
+    DESC 'Used to track a subentry associated with trigger areas'
+    AUXILIARY
+    MUST prescriptiveTriggerSpecification )
+
+# ======================
+# End of Trigger related
+# ======================
+
+# ======================
+# Mitosis Related
+# ======================
+
+attributetype ( 1.3.6.1.4.1.18060.0.4.1.2.29
+        NAME 'entryUUID'
+        DESC 'LCUP/LDUP: UUID of the entry'
+        EQUALITY octetStringMatch
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.40{64}
+        SINGLE-VALUE 
+        NO-USER-MODIFICATION 
+        USAGE directoryOperation )
+
+attributetype ( 1.3.6.1.4.1.18060.0.4.1.2.30
+        NAME 'entryCSN'
+        DESC 'LCUP/LDUP: change sequence number of the entry'
+        EQUALITY octetStringMatch
+        ORDERING octetStringOrderingMatch
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.40{64}
+        SINGLE-VALUE 
+        NO-USER-MODIFICATION 
+        USAGE directoryOperation )
+
+attributetype ( 1.3.6.1.4.1.18060.0.4.1.2.31
+        NAME 'entryDeleted'
+        DESC 'Whether or not an entry has been deleted.'
+        EQUALITY booleanMatch
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.7
+        SINGLE-VALUE 
+        NO-USER-MODIFICATION 
+        USAGE directoryOperation )
+
+# ======================
+# End of Mitosis Related
+# ======================
+
+# =======================
+# Schema Subentry Related
+# =======================
+
+#         | 1.3.6.1.4.1.18060.0.4.1.2.32 | comparators                 |
+#         | 1.3.6.1.4.1.18060.0.4.1.2.33 | normalizers                 |
+#         | 1.3.6.1.4.1.18060.0.4.1.2.34 | syntaxCheckers              |
+
+
+#
+# These operational attributes need a syntax!
+#
+
+# --- comparators AttributeType -----------------------------------
+attributetype ( 1.3.6.1.4.1.18060.0.4.1.2.32 NAME 'comparators'
+    DESC 'A multivalued comparator description attribute'
+    EQUALITY caseIgnoreMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+    USAGE directoryOperation
+)
+
+# --- normalizers AttributeType -----------------------------------
+attributetype ( 1.3.6.1.4.1.18060.0.4.1.2.33 NAME 'normalizers'
+    DESC 'A multivalued normalizer description attribute'
+    EQUALITY caseIgnoreMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+    USAGE directoryOperation
+)
+
+# --- syntaxCheckers AttributeType -----------------------------------
+attributetype ( 1.3.6.1.4.1.18060.0.4.1.2.34 NAME 'syntaxCheckers'
+    DESC 'A multivalued syntaxCheckers description attribute'
+    EQUALITY caseIgnoreMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+    USAGE directoryOperation
+)
+
+objectclass ( 1.3.6.1.4.1.18060.0.4.1.3.9 NAME 'apacheSubschema'
+    DESC 'RFC2252 extension for controlling subschema (sub)entry in ApacheDS'
+    SUP subschema
+    AUXILIARY
+    MAY ( comparators $ normalizers $ syntaxCheckers ) )
+
+# ===========================
+# END Schema Subentry Related
+# ===========================
+
+# =============================================
+# Schema Subentry Modification Attribute Schema 
+# =============================================
+
+#         | 1.3.6.1.4.1.18060.0.4.1.2.35 | schemaModifyTimestamp       |
+#         | 1.3.6.1.4.1.18060.0.4.1.2.36 | schemaModifiersName         |
+#         | 1.3.6.1.4.1.18060.0.4.1.2.37 | subschemaSubentryName       |
+
+attributetype ( 1.3.6.1.4.1.18060.0.4.1.2.35 NAME 'schemaModifyTimestamp'
+    DESC 'time which schema was modified'
+    SUP modifyTimestamp )
+
+attributetype ( 1.3.6.1.4.1.18060.0.4.1.2.36 NAME 'schemaModifiersName'
+    DESC 'the DN of the modifier of the schema'
+    SUP modifiersName )
+
+attributetype ( 1.3.6.1.4.1.18060.0.4.1.2.37 NAME 'subschemaSubentryName'
+    DESC 'the DN of the schema subentry the modification info corresponds to'
+    EQUALITY distinguishedNameMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{32768} )
+
+objectclass ( 1.3.6.1.4.1.18060.0.4.1.3.10 NAME 'schemaModificationAttributes'
+        DESC 'a special entry tracking schema modification attributes'
+        SUP top STRUCTURAL
+        MUST ( cn $ subschemaSubentryName $ 
+               schemaModifyTimestamp $ schemaModifiersName ) )
+
+# =================================================
+# END Schema Subentry Modification Attribute Schema 
+# =================================================
+
+# =============================================
+# SSL/TLS Key Management for LDAPS and StartTLS  
+# =============================================
+
+attributetype ( 1.3.6.1.4.1.18060.0.4.1.2.38
+    NAME 'privateKeyFormat'
+    DESC 'The format of the private key used for TLS'
+    EQUALITY caseExactIA5Match
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.18060.0.4.1.2.41
+    NAME 'publicKeyFormat'
+    DESC 'The format of the public key used for TLS'
+    EQUALITY caseExactIA5Match
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.18060.0.4.1.2.39
+    NAME 'keyAlgorithm'
+    DESC 'The algorithm used for the key/pair used by the server for TLS'
+    EQUALITY caseExactIA5Match
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.18060.0.4.1.2.40
+    NAME 'privateKey'
+    DESC 'The private key material used for TLS'
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.5 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.18060.0.4.1.2.42
+    NAME 'publicKey'
+    DESC 'The public key material used for TLS'
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.5 SINGLE-VALUE )
+
+objectclass ( 1.3.6.1.4.1.18060.0.4.1.3.11
+    NAME 'tlsKeyInfo'
+    SUP top
+    AUXILIARY
+    MUST ( privateKeyFormat $ keyAlgorithm $ privateKey $
+           publicKeyFormat $ publicKey ) )
+
+# =================================================
+# END SSL/TLS Key Management for LDAPS and StartTLS 
+# =================================================
+    
diff --git a/old_trunk/schema-bootstrap/src/main/schema/apachemeta.schema b/old_trunk/schema-bootstrap/src/main/schema/apachemeta.schema
new file mode 100644
index 0000000..db2d62c
--- /dev/null
+++ b/old_trunk/schema-bootstrap/src/main/schema/apachemeta.schema
@@ -0,0 +1,576 @@
+# =============================================================================
+#   Licensed to the Apache Software Foundation (ASF) under one
+#   or more contributor license agreements.  See the NOTICE file
+#   distributed with this work for additional information
+#   regarding copyright ownership.  The ASF licenses this file
+#   to you 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.
+#  
+# =============================================================================
+#                                metameta Schema
+# =============================================================================
+#
+#         +------------------------------+-----------------------------+
+#         |          Syntax OID          |            name             |
+#         +------------------------------+-----------------------------+
+#         | 1.3.6.1.4.1.18060.0.4.0.0.1  | objectClassType             |
+#         | 1.3.6.1.4.1.18060.0.4.0.0.2  | NumericOid                  |
+#         | 1.3.6.1.4.1.18060.0.4.0.0.3  | attributeTypeUsage          |
+#         | 1.3.6.1.4.1.18060.0.4.0.0.4  | number                      |
+#         | 1.3.6.1.4.1.18060.0.4.0.0.5  | oidLen                      |
+#         | 1.3.6.1.4.1.18060.0.4.0.0.6  | objectName                  |
+#         +------------------------------+-----------------------------+
+#
+#         +------------------------------+-----------------------------+
+#         |       MatchingRule OID       |            name             |
+#         +------------------------------+-----------------------------+
+#         | 1.3.6.1.4.1.18060.0.4.0.1.0  | nameOrNumericIdMatch        |
+#         | 1.3.6.1.4.1.18060.0.4.0.1.1  | objectClassTypeMatch        |
+#         | 1.3.6.1.4.1.18060.0.4.0.1.2  | numericOidMatch             |
+#         | 1.3.6.1.4.1.18060.0.4.0.1.3  | supDITStructureRuleMatch    |
+#         | 1.3.6.1.4.1.18060.0.4.0.1.4  | ruleIDMatch                 |
+#         +------------------------------+-----------------------------+
+#
+#         +------------------------------+-----------------------------+
+#         |      AttributeType OID       |            name             |
+#         +------------------------------+-----------------------------+
+#         | 1.3.6.1.4.1.18060.0.4.0.2.1  | m-oid                       |
+#         | 1.3.6.1.4.1.18060.0.4.0.2.2  | m-name                      |
+#         | 1.3.6.1.4.1.18060.0.4.0.2.3  | m-description               |
+#         | 1.3.6.1.4.1.18060.0.4.0.2.4  | m-obsolete                  |
+#         | 1.3.6.1.4.1.18060.0.4.0.2.5  | m-supObjectClass            |
+#         | 1.3.6.1.4.1.18060.0.4.0.2.6  | m-must                      |
+#         | 1.3.6.1.4.1.18060.0.4.0.2.7  | m-may                       |
+#         | 1.3.6.1.4.1.18060.0.4.0.2.8  | m-typeObjectClass           |
+#         | 1.3.6.1.4.1.18060.0.4.0.2.10 | m-supAttributeType          |
+#         | 1.3.6.1.4.1.18060.0.4.0.2.11 | m-equality                  |
+#         | 1.3.6.1.4.1.18060.0.4.0.2.12 | m-ordering                  |
+#         | 1.3.6.1.4.1.18060.0.4.0.2.13 | m-substr                    |
+#         | 1.3.6.1.4.1.18060.0.4.0.2.14 | m-syntax                    |
+#         | 1.3.6.1.4.1.18060.0.4.0.2.15 | m-singleValue               |
+#         | 1.3.6.1.4.1.18060.0.4.0.2.16 | m-collective                |
+#         | 1.3.6.1.4.1.18060.0.4.0.2.17 | m-noUserModification        |
+#         | 1.3.6.1.4.1.18060.0.4.0.2.18 | m-usage                     |
+#         | 1.3.6.1.4.1.18060.0.4.0.2.20 | m-ruleId                    |
+#         | 1.3.6.1.4.1.18060.0.4.0.2.21 | m-form                      |
+#         | 1.3.6.1.4.1.18060.0.4.0.2.22 | m-supDITStructureRule       |
+#         | 1.3.6.1.4.1.18060.0.4.0.2.24 | m-oc                        |
+#         | 1.3.6.1.4.1.18060.0.4.0.2.26 | m-aux                       |
+#         | 1.3.6.1.4.1.18060.0.4.0.2.27 | m-not                       |
+#         | 1.3.6.1.4.1.18060.0.4.0.2.29 | m-applies                   |
+#         | 1.3.6.1.4.1.18060.0.4.0.2.31 | m-matchingRuleSyntax        |
+#         | 1.3.6.1.4.1.18060.0.4.0.2.32 | m-fqcn                      |
+#         | 1.3.6.1.4.1.18060.0.4.0.2.33 | m-bytecode                  |
+#         | 1.3.6.1.4.1.18060.0.4.0.2.34 | x-humanReadable             |
+#         | 1.3.6.1.4.1.18060.0.4.0.2.37 | m-disabled                  |
+#         | 1.3.6.1.4.1.18060.0.4.0.2.38 | m-dependencies              |
+#         | 1.3.6.1.4.1.18060.0.4.0.2.39 | m-length                    |
+#         +------------------------------+-----------------------------+
+#
+#         +------------------------------+-----------------------------+
+#         |       Objectclasses OID      |            name             |
+#         +------------------------------+-----------------------------+
+#         | 1.3.6.1.4.1.18060.0.4.0.3.1  | metaTop                     |
+#         | 1.3.6.1.4.1.18060.0.4.0.3.2  | metaObjectClass             |
+#         | 1.3.6.1.4.1.18060.0.4.0.3.3  | metaAttributeType           |
+#         | 1.3.6.1.4.1.18060.0.4.0.3.4  | metaSyntax                  |
+#         | 1.3.6.1.4.1.18060.0.4.0.3.5  | metaMatchingRule            |
+#         | 1.3.6.1.4.1.18060.0.4.0.3.6  | metaDITStructureRule        |
+#         | 1.3.6.1.4.1.18060.0.4.0.3.7  | metaNameForm                |
+#         | 1.3.6.1.4.1.18060.0.4.0.3.8  | metaMatchingRuleUse         |
+#         | 1.3.6.1.4.1.18060.0.4.0.3.9  | metaDITContentRule          |
+#         | 1.3.6.1.4.1.18060.0.4.0.3.10 | metaSyntaxChecker           |
+#         | 1.3.6.1.4.1.18060.0.4.0.3.11 | metaSchema                  |
+#         | 1.3.6.1.4.1.18060.0.4.0.3.12 | metaNormalizer              |
+#         | 1.3.6.1.4.1.18060.0.4.0.3.13 | metaComparator              |
+#         +------------------------------+-----------------------------+
+#
+# =============================================================================
+
+# =============================================================================
+# objectclasses
+# =============================================================================
+
+# --- metaTop objectclass -----------------------------------------------------
+objectclass ( 1.3.6.1.4.1.18060.0.4.0.3.1
+    NAME 'metaTop'
+    DESC 'Top level objectclass of all meta objects'
+    SUP top
+    ABSTRACT
+    MUST m-oid
+    MAY m-description
+)
+
+# --- metaobjectclass objectclass ---------------------------------------------
+# ObjectClassDescription = LPAREN WSP
+#         numericoid                 ; object identifier
+#         [ SP "NAME" SP qdescrs ]   ; short names (descriptors)
+#         [ SP "DESC" SP qdstring ]  ; description
+#         [ SP "OBSOLETE" ]          ; not active
+#         [ SP "SUP" SP oids ]       ; superior object classes
+#         [ SP kind ]                ; kind of class
+#         [ SP "MUST" SP oids ]      ; attribute types
+#         [ SP "MAY" SP oids ]       ; attribute types
+#         extensions WSP RPAREN
+#
+# kind = "ABSTRACT" / "STRUCTURAL" / "AUXILIARY"
+#
+# The numericOid is an m-oid attributeType, it is stored in
+# the metaTop objectClass
+#------------------------------------------------------------------------------
+objectclass ( 1.3.6.1.4.1.18060.0.4.0.3.2
+    NAME 'metaObjectClass'
+    DESC 'meta definition of the objectclass object'
+    SUP metaTop
+    STRUCTURAL
+    MUST m-oid
+    MAY ( m-name $ m-obsolete $ m-supObjectClass $ m-typeObjectClass $ m-must $ 
+	  m-may )
+)
+
+# --- metaAttributeType objectclass -------------------------------------------
+#
+# AttributeTypeDescription = LPAREN WSP
+#     numericoid                    ; object identifier
+#     [ SP "NAME" SP qdescrs ]      ; short names (descriptors)
+#     [ SP "DESC" SP qdstring ]     ; description
+#     [ SP "OBSOLETE" ]             ; not active
+#     [ SP "SUP" SP oid ]           ; supertype
+#     [ SP "EQUALITY" SP oid ]      ; equality matching rule
+#     [ SP "ORDERING" SP oid ]      ; ordering matching rule
+#     [ SP "SUBSTR" SP oid ]        ; substrings matching rule
+#     [ SP "SYNTAX" SP noidlen ]    ; value syntax
+#     [ SP "SINGLE-VALUE" ]         ; single-value
+#     [ SP "COLLECTIVE" ]           ; collective
+#     [ SP "NO-USER-MODIFICATION" ] ; not user modifiable
+#     [ SP "USAGE" SP usage ]       ; usage
+#     extensions WSP RPAREN         ; extensions
+#
+# usage = "userApplications" |  ; user
+#     "directoryOperation"   |  ; directory operational
+#     "distributedOperation" |  ; DSA-shared operational
+#     "dSAOperation"            ; DSA-specific operational
+#
+# IMPORTANT NOTE :
+# ----------------
+# ==> Either m-sup OR m-syntax MUST be present
+#------------------------------------------------------------------------------
+objectclass ( 1.3.6.1.4.1.18060.0.4.0.3.3
+    NAME 'metaAttributeType'
+    DESC 'meta definition of the AttributeType object'
+    SUP metaTop
+    STRUCTURAL
+    MAY ( m-name $ m-obsolete $ m-supAttributeType $ m-equality $ m-ordering $ 
+          m-substr $ m-syntax $ m-singleValue $ m-collective $
+          m-noUserModification $ m-usage $ m-length )
+)
+
+# --- metaSyntax objectclass --------------------------------------------------
+#
+# SyntaxDescription = LPAREN WSP
+#     numericoid                 ; object identifier
+#     [ SP "DESC" SP qdstring ]  ; description
+#     extensions WSP RPAREN      ; extensions
+#
+# Except numericOid, no element is mandatory
+#------------------------------------------------------------------------------
+objectclass ( 1.3.6.1.4.1.18060.0.4.0.3.4
+    NAME 'metaSyntax'
+    DESC 'meta definition of the Syntax object'
+    SUP metaTop
+    STRUCTURAL
+)
+
+# --- metaMatchingRule objectclass --------------------------------------------
+#
+# MatchingRuleDescription = LPAREN WSP
+#      numericoid                 ; object identifier
+#      [ SP "NAME" SP qdescrs ]   ; short names (descriptors)
+#      [ SP "DESC" SP qdstring ]  ; description
+#      [ SP "OBSOLETE" ]          ; not active
+#      SP "SYNTAX" SP numericoid  ; assertion syntax
+#      extensions WSP RPAREN      ; extensions
+#
+#------------------------------------------------------------------------------
+objectclass ( 1.3.6.1.4.1.18060.0.4.0.3.5
+    NAME 'metaMatchingRule'
+    DESC 'meta definition of the MatchingRule object'
+    SUP metaTop
+    STRUCTURAL
+    MUST m-syntax
+    MAY ( m-name $ m-obsolete )
+)
+
+# --- metaDITStructureRule objectclass ----------------------------------------
+#
+# DITStructureRuleDescription = LPAREN WSP
+#     ruleid                     ; rule identifier
+#     [ SP "NAME" SP qdescrs ]   ; short names (descriptors)
+#     [ SP "DESC" SP qdstring ]  ; description
+#     [ SP "OBSOLETE" ]          ; not active
+#     SP "FORM" SP oid           ; NameForm
+#     [ SP "SUP" ruleids ]       ; superior rules
+#     extensions WSP RPAREN      ; extensions
+#
+# IMPORTANT NOTE :
+# ----------------
+# ==> This ObjectClass does not inherit from metaTop, but directly from top.
+#------------------------------------------------------------------------------
+objectclass ( 1.3.6.1.4.1.18060.0.4.0.3.6
+    NAME 'metaDITStructureRule'
+    DESC 'meta definition of the DITStructureRule object'
+    SUP top
+    STRUCTURAL
+    MUST ( m-ruleId $ m-form )
+    MAY ( m-name $ m-obsolete $ m-supDITStructureRule ) 
+)
+
+# --- metaNameForm objectclass ------------------------------------------------
+#
+# NameFormDescription = LPAREN WSP
+#     numericoid                 ; object identifier
+#     [ SP "NAME" SP qdescrs ]   ; short names (descriptors)
+#     [ SP "DESC" SP qdstring ]  ; description
+#     [ SP "OBSOLETE" ]          ; not active
+#     SP "OC" SP oid             ; structural object class
+#     SP "MUST" SP oids          ; attribute types
+#     [ SP "MAY" SP oids ]       ; attribute types
+#     extensions WSP RPAREN      ; extensions
+#------------------------------------------------------------------------------
+objectclass ( 1.3.6.1.4.1.18060.0.4.0.3.7
+    NAME 'metaNameForm'
+    DESC 'meta definition of the NameForm object'
+    SUP metaTop
+    STRUCTURAL
+    MUST ( m-oc $ m-must )
+    MAY ( m-name $ m-obsolete $ m-may )
+)
+
+# --- metaMatchingRuleUse objectclass -----------------------------------------
+#
+# MatchingRuleUseDescription = LPAREN WSP
+#     numericoid                 ; object identifier
+#     [ SP "NAME" SP qdescrs ]   ; short names (descriptors)
+#     [ SP "DESC" SP qdstring ]  ; description
+#     [ SP "OBSOLETE" ]          ; not active
+#     SP "APPLIES" SP oids       ; attribute types
+#     extensions WSP RPAREN      ; extensions
+#------------------------------------------------------------------------------
+objectclass ( 1.3.6.1.4.1.18060.0.4.0.3.8
+    NAME 'metaMatchingRuleUse'
+    DESC 'meta definition of the MatchingRuleUse object'
+    SUP metaTop
+    STRUCTURAL
+    MUST m-applies
+    MAY ( m-name $ m-obsolete )
+)
+
+# --- metaDITContentRule objectclass ------------------------------------------
+# 
+# DITContentRuleDescription = LPAREN WSP
+#     numericoid                 ; object identifier
+#     [ SP "NAME" SP qdescrs ]   ; short names (descriptors)
+#     [ SP "DESC" SP qdstring ]  ; description
+#     [ SP "OBSOLETE" ]          ; not active
+#     [ SP "AUX" SP oids ]       ; auxiliary object classes
+#     [ SP "MUST" SP oids ]      ; attribute types
+#     [ SP "MAY" SP oids ]       ; attribute types
+#     [ SP "NOT" SP oids ]       ; attribute types
+#     extensions WSP RPAREN      ; extensions
+#------------------------------------------------------------------------------
+objectclass ( 1.3.6.1.4.1.18060.0.4.0.3.9
+    NAME 'metaDITContentRule'
+    DESC 'meta definition of the DITContentRule object'
+    SUP metaTop
+    STRUCTURAL
+    MAY ( m-name $ m-obsolete $ m-aux $ m-must $ m-may $ m-not )
+)
+
+# --- metaSyntaxChecker objectclass -------------------------------------------
+objectclass ( 1.3.6.1.4.1.18060.0.4.0.3.10
+    NAME 'metaSyntaxChecker'
+    DESC 'meta definition of the SyntaxChecker object'
+    SUP metaTop
+    STRUCTURAL
+    MUST m-fqcn
+    MAY m-bytecode
+)
+
+# --- metaSchema objectclass --------------------------------------------------
+objectclass ( 1.3.6.1.4.1.18060.0.4.0.3.11
+    NAME 'metaSchema'
+    DESC 'A schema object under which meta schema definitions are found'
+    SUP top
+    STRUCTURAL
+    MUST ( cn )
+    MAY ( m-disabled $ m-dependencies )
+)
+
+# --- metaNormalizer objectclass ----------------------------------------------
+objectclass ( 1.3.6.1.4.1.18060.0.4.0.3.12
+    NAME 'metaNormalizer'
+    DESC 'meta definition of a Normalizer object'
+    SUP metaTop
+    STRUCTURAL
+    MUST m-fqcn
+    MAY m-bytecode
+)
+
+# --- metaComparator objectclass ----------------------------------------------
+objectclass ( 1.3.6.1.4.1.18060.0.4.0.3.13
+    NAME 'metaComparator'
+    DESC 'meta definition of a Comparator object'
+    SUP metaTop
+    STRUCTURAL
+    MUST m-fqcn
+    MAY m-bytecode
+)
+
+# =============================================================================
+# AttributeTypes
+# =============================================================================
+# --- m-oid AttributeType -----------------------------------------------------
+attributetype ( 1.3.6.1.4.1.18060.0.4.0.2.1 NAME 'm-oid'
+    DESC 'The Object Identifier'
+    EQUALITY objectIdentifierMatch
+    SYNTAX 1.3.6.1.4.1.18060.0.4.0.0.2
+    SINGLE-VALUE
+)
+
+# --- m-name AttributeType ----------------------------------------------------
+attributetype ( 1.3.6.1.4.1.18060.0.4.0.2.2 NAME 'm-name'
+    DESC 'The Object name'
+    EQUALITY caseIgnoreMatch
+    SYNTAX 1.3.6.1.4.1.18060.0.4.0.0.6{1024}
+)
+
+# --- m-description AttributeType ---------------------------------------------
+attributetype ( 1.3.6.1.4.1.18060.0.4.0.2.3 NAME 'm-description'
+    DESC 'meta descriptive information'
+    EQUALITY caseIgnoreMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{1024}
+    SINGLE-VALUE
+)
+
+# --- m-obsolete AttributeType ------------------------------------------------
+attributetype ( 1.3.6.1.4.1.18060.0.4.0.2.4 NAME 'm-obsolete'
+    DESC 'The type is obsolete'
+    EQUALITY booleanMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.7
+    SINGLE-VALUE
+)
+
+# --- m-supObjectClass AttributeType ------------------------------------------
+attributetype ( 1.3.6.1.4.1.18060.0.4.0.2.5 NAME 'm-supObjectClass'
+    DESC 'The list of superiors'
+    EQUALITY nameOrNumericIdMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.38
+)
+
+# --- m-must AttributeType ----------------------------------------------------
+attributetype ( 1.3.6.1.4.1.18060.0.4.0.2.6 NAME 'm-must'
+    DESC 'The list of mandatory ATs'
+    EQUALITY nameOrNumericIdMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.38
+)
+
+# --- m-may AttributeType -----------------------------------------------------
+attributetype ( 1.3.6.1.4.1.18060.0.4.0.2.7 NAME 'm-may'
+    DESC 'The list of authorized ATs'
+    EQUALITY nameOrNumericIdMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.38
+)
+
+# --- m-typeObjectClass AttributeType -----------------------------------------
+attributetype ( 1.3.6.1.4.1.18060.0.4.0.2.8 NAME 'm-typeObjectClass'
+    DESC 'The objectclass type'
+    EQUALITY objectClassTypeMatch
+    SYNTAX 1.3.6.1.4.1.18060.0.4.0.0.1
+    SINGLE-VALUE
+)
+
+# --- m-supAttributeType AttributeType ----------------------------------------
+attributetype ( 1.3.6.1.4.1.18060.0.4.0.2.10 NAME 'm-supAttributeType'
+    DESC 'The list of superior'
+    EQUALITY nameOrNumericIdMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.38
+    SINGLE-VALUE
+)
+
+# --- m-equality AttributeType ------------------------------------------------
+attributetype ( 1.3.6.1.4.1.18060.0.4.0.2.11 NAME 'm-equality'
+    DESC 'Equality matching rule'
+    EQUALITY nameOrNumericIdMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.38
+    SINGLE-VALUE
+)
+
+# --- m-ordering AttributeType ------------------------------------------------
+attributetype ( 1.3.6.1.4.1.18060.0.4.0.2.12 NAME 'm-ordering'
+    DESC 'Ordering matching rule'
+    EQUALITY nameOrNumericIdMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.38
+    SINGLE-VALUE
+)
+
+# --- m-substr AttributeType --------------------------------------------------
+attributetype ( 1.3.6.1.4.1.18060.0.4.0.2.13 NAME 'm-substr'
+    DESC 'Substring matching rule'
+    EQUALITY nameOrNumericIdMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.38
+    SINGLE-VALUE
+)
+
+# --- m-syntax AttributeType -------------------------------
+attributetype ( 1.3.6.1.4.1.18060.0.4.0.2.14 NAME 'm-syntax'
+    DESC 'The syntax OID for attributeTypes and matchingRules'
+    EQUALITY nameOrNumericIdMatch
+    SYNTAX 1.3.6.1.4.1.18060.0.4.0.0.2
+    SINGLE-VALUE
+)
+
+# --- m-singleValue AttributeType ---------------------------------------------
+attributetype ( 1.3.6.1.4.1.18060.0.4.0.2.15 NAME 'm-singleValue'
+    DESC 'The attribute is single valued'
+    EQUALITY booleanMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.7
+    SINGLE-VALUE
+)
+
+# --- m-collective AttributeType ----------------------------------------------
+attributetype ( 1.3.6.1.4.1.18060.0.4.0.2.16 NAME 'm-collective'
+    DESC 'The attribute is collective'
+    EQUALITY booleanMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.7
+    SINGLE-VALUE
+)
+
+# --- m-noUserModification AttributeType --------------------------------------
+attributetype ( 1.3.6.1.4.1.18060.0.4.0.2.17 NAME 'm-noUserModification'
+    DESC 'The attribute is protected'
+    EQUALITY booleanMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.7
+    SINGLE-VALUE
+)
+
+# --- m-usage AttributeType -------------------------------------
+attributetype ( 1.3.6.1.4.1.18060.0.4.0.2.18 NAME 'm-usage'
+    DESC 'Usage type of an attributeType'
+    EQUALITY caseIgnoreMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+    SINGLE-VALUE
+)
+
+# --- m-ruleId AttributeType --------------------------------------------------
+attributetype ( 1.3.6.1.4.1.18060.0.4.0.2.20 NAME 'm-ruleId'
+    DESC 'The rule ID'
+    EQUALITY ruleIDMatch
+    SYNTAX 1.3.6.1.4.1.18060.0.4.0.0.4
+)
+
+# --- m-form AttributeType ----------------------------------------------------
+attributetype ( 1.3.6.1.4.1.18060.0.4.0.2.21 NAME 'm-form'
+    DESC 'The name form associated with this DITStructure rule'
+    EQUALITY caseIgnoreMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.38
+)
+
+# --- m-supDITStructureRule AttributeType -------------------------------------
+attributetype ( 1.3.6.1.4.1.18060.0.4.0.2.22 NAME 'm-supDITStructureRule'
+    DESC 'The list of superiors'
+    EQUALITY supDITStructureRuleMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.36
+)
+
+# --- m-oc AttributeType ------------------------------------------------------
+attributetype ( 1.3.6.1.4.1.18060.0.4.0.2.24 NAME 'm-oc'
+    DESC 'The structural ObjectClass'
+    EQUALITY numericOidMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.38
+)
+
+# --- m-aux AttributeType -----------------------------------------------------
+attributetype ( 1.3.6.1.4.1.18060.0.4.0.2.26 NAME 'm-aux'
+    DESC 'List of auxiliary ObjectClasses'
+    EQUALITY numericOidMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.38
+)
+
+# --- m-not AttributeType -----------------------------------------------------
+attributetype ( 1.3.6.1.4.1.18060.0.4.0.2.27 NAME 'm-not'
+    DESC 'List of precluded attribute types'
+    EQUALITY numericOidMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.38
+)
+
+# --- m-applies AttributeType -------------------------------------------------
+attributetype ( 1.3.6.1.4.1.18060.0.4.0.2.29 NAME 'm-applies'
+    DESC 'List of attribute types the matching rule applies to'
+    EQUALITY numericOidMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.38
+)
+
+# --- m-matchingRuleSyntax AttributeType --------------------------------------
+attributetype ( 1.3.6.1.4.1.18060.0.4.0.2.31 NAME 'm-matchingRuleSyntax'
+    DESC 'The matchingRule attribute syntax '
+    EQUALITY numericOidMatch
+    SYNTAX 1.3.6.1.4.1.18060.0.4.0.0.2
+    SINGLE-VALUE
+)
+
+# --- m-fqcn AttributeType ----------------------------------------------------
+attributetype ( 1.3.6.1.4.1.18060.0.4.0.2.32 NAME 'm-fqcn'
+    DESC 'The fully qualified class name of a code based schema entity'
+    EQUALITY caseExactMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+    SINGLE-VALUE
+)
+
+# --- m-bytecode AttributeType ------------------------------------------------
+attributetype ( 1.3.6.1.4.1.18060.0.4.0.2.33 NAME 'm-bytecode'
+    DESC 'The Java bytecode for a code based schema entity'
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.5
+    SINGLE-VALUE
+)
+
+# --- x-humanReadable AttributeType ------------------------------------------------
+attributetype ( 1.3.6.1.4.1.18060.0.4.0.2.34 NAME 'x-humanReadable'
+    DESC 'whether or not a syntax is human readable'
+    EQUALITY booleanMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.7
+    SINGLE-VALUE
+)
+
+# --- m-disabled AttributeType -----------------------------------
+attributetype ( 1.3.6.1.4.1.18060.0.4.0.2.37 NAME 'm-disabled'
+    DESC 'Used as a marker for schemas to enable or disable them.'
+    EQUALITY booleanMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.7
+    SINGLE-VALUE
+)
+
+# --- m-dependencies AttributeType -----------------------------------
+attributetype ( 1.3.6.1.4.1.18060.0.4.0.2.38 NAME 'm-dependencies'
+    DESC 'The dependencies of a schema: other schema names.'
+    EQUALITY caseIgnoreMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+)
+
+# --- m-length AttributeType -----------------------------------
+attributetype ( 1.3.6.1.4.1.18060.0.4.0.2.39 NAME 'm-length'
+    DESC 'The maximum length for an attribute value.'
+    EQUALITY caseIgnoreMatch
+    SYNTAX 1.3.6.1.4.1.18060.0.4.0.0.4
+    SINGLE-VALUE
+)
+
diff --git a/old_trunk/schema-bootstrap/src/main/schema/core.schema b/old_trunk/schema-bootstrap/src/main/schema/core.schema
new file mode 100644
index 0000000..fbf1d59
--- /dev/null
+++ b/old_trunk/schema-bootstrap/src/main/schema/core.schema
@@ -0,0 +1,557 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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. 
+
+
+# $OpenLDAP: pkg/ldap/servers/slapd/schema/core.schema,v 1.64.2.3 2002/09/06 01:13:55 kurt Exp $
+#
+# OpenLDAP Core schema
+#
+# Includes LDAPv3 schema items from:
+#	RFC 2252/2256 (LDAPv3)
+#
+# Select standard track schema items:
+#	RFC 1274 (uid/dc)
+#	RFC 2079 (URI)
+#	RFC 2247 (dc/dcObject)
+#	RFC 2587 (PKI)
+#	RFC 2589 (Dynamic Directory Services)
+#
+# Select informational schema items:
+#	RFC 2377 (uidObject)
+
+#
+# Standard attribute types from RFC 2256
+#
+
+# system schema
+#attributetype ( 2.5.4.0 NAME 'objectClass'
+#	DESC 'RFC2256: object classes of the entity'
+#	EQUALITY objectIdentifierMatch
+#	SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )
+
+# system schema
+#attributetype ( 2.5.4.1 NAME ( 'aliasedObjectName' 'aliasedEntryName' )
+#	DESC 'RFC2256: name of aliased object'
+#	EQUALITY distinguishedNameMatch
+#	SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 SINGLE-VALUE )
+
+attributetype ( 2.5.4.2 NAME 'knowledgeInformation'
+	DESC 'RFC2256: knowledge information'
+	EQUALITY caseIgnoreMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{32768} )
+
+# system schema
+#attributetype ( 2.5.4.3 NAME ( 'cn' 'commonName' )
+#	DESC 'RFC2256: common name(s) for which the entity is known by'
+#	SUP name )
+
+attributetype ( 2.5.4.4 NAME ( 'sn' 'surname' )
+	DESC 'RFC2256: last (family) name(s) for which the entity is known by'
+	SUP name )
+
+attributetype ( 2.5.4.5 NAME 'serialNumber'
+	DESC 'RFC2256: serial number of the entity'
+	EQUALITY caseIgnoreMatch
+	SUBSTR caseIgnoreSubstringsMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.44{64} )
+
+attributetype ( 2.5.4.6 NAME ( 'c' 'countryName' )
+	DESC 'RFC2256: ISO-3166 country 2-letter code'
+	SUP name SINGLE-VALUE )
+
+attributetype ( 2.5.4.7 NAME ( 'l' 'localityName' )
+	DESC 'RFC2256: locality which this object resides in'
+	SUP name )
+
+attributetype ( 2.5.4.8 NAME ( 'st' 'stateOrProvinceName' )
+	DESC 'RFC2256: state or province which this object resides in'
+	SUP name )
+
+attributetype ( 2.5.4.9 NAME ( 'street' 'streetAddress' )
+	DESC 'RFC2256: street address of this object'
+	EQUALITY caseIgnoreMatch
+	SUBSTR caseIgnoreSubstringsMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{128} )
+
+attributetype ( 2.5.4.10 NAME ( 'o' 'organizationName' )
+	DESC 'RFC2256: organization this object belongs to'
+	SUP name )
+
+attributetype ( 2.5.4.11 NAME ( 'ou' 'organizationalUnitName' )
+	DESC 'RFC2256: organizational unit this object belongs to'
+	SUP name )
+
+attributetype ( 2.5.4.12 NAME 'title'
+	DESC 'RFC2256: title associated with the entity'
+	SUP name )
+
+attributetype ( 2.5.4.13 NAME 'description'
+	DESC 'RFC2256: descriptive information'
+	EQUALITY caseIgnoreMatch
+	SUBSTR caseIgnoreSubstringsMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{1024} )
+
+# Obsoleted by enhancedSearchGuide
+attributetype ( 2.5.4.14 NAME 'searchGuide'
+	DESC 'RFC2256: search guide, obsoleted by enhancedSearchGuide'
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.25 )
+
+attributetype ( 2.5.4.15 NAME 'businessCategory'
+	DESC 'RFC2256: business category'
+	EQUALITY caseIgnoreMatch
+	SUBSTR caseIgnoreSubstringsMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{128} )
+
+attributetype ( 2.5.4.16 NAME 'postalAddress'
+	DESC 'RFC2256: postal address'
+	EQUALITY caseIgnoreListMatch
+	SUBSTR caseIgnoreListSubstringsMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.41 )
+
+attributetype ( 2.5.4.17 NAME 'postalCode'
+	DESC 'RFC2256: postal code'
+	EQUALITY caseIgnoreMatch
+	SUBSTR caseIgnoreSubstringsMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{40} )
+
+attributetype ( 2.5.4.18 NAME 'postOfficeBox'
+	DESC 'RFC2256: Post Office Box'
+	EQUALITY caseIgnoreMatch
+	SUBSTR caseIgnoreSubstringsMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{40} )
+
+attributetype ( 2.5.4.19 NAME 'physicalDeliveryOfficeName'
+	DESC 'RFC2256: Physical Delivery Office Name'
+	EQUALITY caseIgnoreMatch
+	SUBSTR caseIgnoreSubstringsMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{128} )
+
+attributetype ( 2.5.4.20 NAME 'telephoneNumber'
+	DESC 'RFC2256: Telephone Number'
+	EQUALITY telephoneNumberMatch
+	SUBSTR telephoneNumberSubstringsMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.50{32} )
+
+attributetype ( 2.5.4.21 NAME 'telexNumber'
+	DESC 'RFC2256: Telex Number'
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.52 )
+
+attributetype ( 2.5.4.22 NAME 'teletexTerminalIdentifier'
+	DESC 'RFC2256: Teletex Terminal Identifier'
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.51 )
+
+attributetype ( 2.5.4.23 NAME ( 'facsimileTelephoneNumber' 'fax' )
+	DESC 'RFC2256: Facsimile (Fax) Telephone Number'
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.22 )
+
+attributetype ( 2.5.4.24 NAME 'x121Address'
+	DESC 'RFC2256: X.121 Address'
+	EQUALITY numericStringMatch
+	SUBSTR numericStringSubstringsMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.36{15} )
+
+attributetype ( 2.5.4.25 NAME 'internationaliSDNNumber'
+	DESC 'RFC2256: international ISDN number'
+	EQUALITY numericStringMatch
+	SUBSTR numericStringSubstringsMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.36{16} )
+
+attributetype ( 2.5.4.26 NAME 'registeredAddress'
+	DESC 'RFC2256: registered postal address'
+	SUP postalAddress
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.41 )
+
+attributetype ( 2.5.4.27 NAME 'destinationIndicator'
+	DESC 'RFC2256: destination indicator'
+	EQUALITY caseIgnoreMatch
+	SUBSTR caseIgnoreSubstringsMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.44{128} )
+
+attributetype ( 2.5.4.28 NAME 'preferredDeliveryMethod'
+	DESC 'RFC2256: preferred delivery method'
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.14
+	SINGLE-VALUE )
+
+attributetype ( 2.5.4.29 NAME 'presentationAddress'
+	DESC 'RFC2256: presentation address'
+	EQUALITY presentationAddressMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.43
+	SINGLE-VALUE )
+
+attributetype ( 2.5.4.30 NAME 'supportedApplicationContext'
+	DESC 'RFC2256: supported application context'
+	EQUALITY objectIdentifierMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )
+
+attributetype ( 2.5.4.31 NAME 'member'
+	DESC 'RFC2256: member of a group'
+	SUP distinguishedName )
+
+attributetype ( 2.5.4.32 NAME 'owner'
+	DESC 'RFC2256: owner (of the object)'
+	SUP distinguishedName )
+
+attributetype ( 2.5.4.33 NAME 'roleOccupant'
+	DESC 'RFC2256: occupant of role'
+	SUP distinguishedName )
+
+attributetype ( 2.5.4.34 NAME 'seeAlso'
+	DESC 'RFC2256: DN of related object'
+	SUP distinguishedName )
+
+# system schema
+#attributetype ( 2.5.4.35 NAME 'userPassword'
+#	DESC 'RFC2256/2307: password of user'
+#	EQUALITY octetStringMatch
+#	SYNTAX 1.3.6.1.4.1.1466.115.121.1.40{128} )
+
+# Must be transferred using ;binary
+attributetype ( 2.5.4.36 NAME 'userCertificate'
+	DESC 'RFC2256: X.509 user certificate, use ;binary'
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.8 )
+
+# Must be transferred using ;binary
+attributetype ( 2.5.4.37 NAME 'cACertificate'
+	DESC 'RFC2256: X.509 CA certificate, use ;binary'
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.8 )
+
+# Must be transferred using ;binary
+attributetype ( 2.5.4.38 NAME 'authorityRevocationList'
+	DESC 'RFC2256: X.509 authority revocation list, use ;binary'
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.9 )
+
+# Must be transferred using ;binary
+attributetype ( 2.5.4.39 NAME 'certificateRevocationList'
+	DESC 'RFC2256: X.509 certificate revocation list, use ;binary'
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.9 )
+
+# Must be stored and requested in the binary form
+attributetype ( 2.5.4.40 NAME 'crossCertificatePair'
+	DESC 'RFC2256: X.509 cross certificate pair, use ;binary'
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.10 )
+
+# 2.5.4.41 is defined above as it's used for subtyping
+#attributetype ( 2.5.4.41 NAME 'name'
+#	EQUALITY caseIgnoreMatch
+#	SUBSTR caseIgnoreSubstringsMatch
+#	SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{32768} )
+
+attributetype ( 2.5.4.42 NAME ( 'givenName' 'gn' )
+	DESC 'RFC2256: first name(s) for which the entity is known by'
+	SUP name )
+
+attributetype ( 2.5.4.43 NAME 'initials'
+	DESC 'RFC2256: initials of some or all of names, but not the surname(s).'
+	SUP name )
+
+attributetype ( 2.5.4.44 NAME 'generationQualifier'
+	DESC 'RFC2256: name qualifier indicating a generation'
+	SUP name )
+
+attributetype ( 2.5.4.45 NAME 'x500UniqueIdentifier'
+	DESC 'RFC2256: X.500 unique identifier'
+	EQUALITY bitStringMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.6 )
+
+attributetype ( 2.5.4.46 NAME 'dnQualifier'
+	DESC 'RFC2256: DN qualifier'
+	EQUALITY caseIgnoreMatch
+	ORDERING caseIgnoreOrderingMatch
+	SUBSTR caseIgnoreSubstringsMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.44 )
+
+attributetype ( 2.5.4.47 NAME 'enhancedSearchGuide'
+	DESC 'RFC2256: enhanced search guide'
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.21 )
+
+attributetype ( 2.5.4.48 NAME 'protocolInformation'
+	DESC 'RFC2256: protocol information'
+	EQUALITY protocolInformationMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.42 )
+
+# 2.5.4.49 is defined above as it's used for subtyping
+#attributetype ( 2.5.4.49 NAME 'distinguishedName'
+#	EQUALITY distinguishedNameMatch
+#	SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
+
+attributetype ( 2.5.4.50 NAME 'uniqueMember'
+	DESC 'RFC2256: unique member of a group'
+	EQUALITY uniqueMemberMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.34 )
+
+attributetype ( 2.5.4.51 NAME 'houseIdentifier'
+	DESC 'RFC2256: house identifier'
+	EQUALITY caseIgnoreMatch
+	SUBSTR caseIgnoreSubstringsMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{32768} )
+
+# Must be transferred using ;binary
+attributetype ( 2.5.4.52 NAME 'supportedAlgorithms'
+	DESC 'RFC2256: supported algorithms'
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.49 )
+
+# Must be transferred using ;binary
+attributetype ( 2.5.4.53 NAME 'deltaRevocationList'
+	DESC 'RFC2256: delta revocation list; use ;binary'
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.9 )
+
+attributetype ( 2.5.4.54 NAME 'dmdName'
+	DESC 'RFC2256: name of DMD'
+	SUP name )
+
+
+# Standard object classes from RFC2256
+
+# system schema
+#objectclass ( 2.5.6.1 NAME 'alias'
+#	DESC 'RFC2256: an alias'
+#	SUP top STRUCTURAL
+#	MUST aliasedObjectName )
+
+objectclass ( 2.5.6.2 NAME 'country'
+	DESC 'RFC2256: a country'
+	SUP top STRUCTURAL
+	MUST c
+	MAY ( searchGuide $ description ) )
+
+objectclass ( 2.5.6.3 NAME 'locality'
+	DESC 'RFC2256: a locality'
+	SUP top STRUCTURAL
+	MAY ( street $ seeAlso $ searchGuide $ st $ l $ description ) )
+
+objectclass ( 2.5.6.4 NAME 'organization'
+	DESC 'RFC2256: an organization'
+	SUP top STRUCTURAL
+	MUST o
+	MAY ( userPassword $ searchGuide $ seeAlso $ businessCategory $
+		x121Address $ registeredAddress $ destinationIndicator $
+		preferredDeliveryMethod $ telexNumber $ teletexTerminalIdentifier $
+		telephoneNumber $ internationaliSDNNumber $
+		facsimileTelephoneNumber $ street $ postOfficeBox $ postalCode $
+		postalAddress $ physicalDeliveryOfficeName $ st $ l $ description ) )
+
+objectclass ( 2.5.6.5 NAME 'organizationalUnit'
+	DESC 'RFC2256: an organizational unit'
+	SUP top STRUCTURAL
+	MUST ou
+	MAY ( userPassword $ searchGuide $ seeAlso $ businessCategory $
+		x121Address $ registeredAddress $ destinationIndicator $
+		preferredDeliveryMethod $ telexNumber $ teletexTerminalIdentifier $
+		telephoneNumber $ internationaliSDNNumber $
+		facsimileTelephoneNumber $ street $ postOfficeBox $ postalCode $
+		postalAddress $ physicalDeliveryOfficeName $ st $ l $ description ) )
+
+objectclass ( 2.5.6.6 NAME 'person'
+	DESC 'RFC2256: a person'
+	SUP top STRUCTURAL
+	MUST ( sn $ cn )
+	MAY ( userPassword $ telephoneNumber $ seeAlso $ description ) )
+
+objectclass ( 2.5.6.7 NAME 'organizationalPerson'
+	DESC 'RFC2256: an organizational person'
+	SUP person STRUCTURAL
+	MAY ( title $ x121Address $ registeredAddress $ destinationIndicator $
+		preferredDeliveryMethod $ telexNumber $ teletexTerminalIdentifier $
+		telephoneNumber $ internationaliSDNNumber $
+		facsimileTelephoneNumber $ street $ postOfficeBox $ postalCode $
+		postalAddress $ physicalDeliveryOfficeName $ ou $ st $ l ) )
+
+objectclass ( 2.5.6.8 NAME 'organizationalRole'
+	DESC 'RFC2256: an organizational role'
+	SUP top STRUCTURAL
+	MUST cn
+	MAY ( x121Address $ registeredAddress $ destinationIndicator $
+		preferredDeliveryMethod $ telexNumber $ teletexTerminalIdentifier $
+		telephoneNumber $ internationaliSDNNumber $ facsimileTelephoneNumber $
+		seeAlso $ roleOccupant $ preferredDeliveryMethod $ street $
+		postOfficeBox $ postalCode $ postalAddress $
+		physicalDeliveryOfficeName $ ou $ st $ l $ description ) )
+
+objectclass ( 2.5.6.9 NAME 'groupOfNames'
+	DESC 'RFC2256: a group of names (DNs)'
+	SUP top STRUCTURAL
+	MUST ( member $ cn )
+	MAY ( businessCategory $ seeAlso $ owner $ ou $ o $ description ) )
+
+objectclass ( 2.5.6.10 NAME 'residentialPerson'
+	DESC 'RFC2256: an residential person'
+	SUP person STRUCTURAL
+	MUST l
+	MAY ( businessCategory $ x121Address $ registeredAddress $
+		destinationIndicator $ preferredDeliveryMethod $ telexNumber $
+		teletexTerminalIdentifier $ telephoneNumber $ internationaliSDNNumber $
+		facsimileTelephoneNumber $ preferredDeliveryMethod $ street $
+		postOfficeBox $ postalCode $ postalAddress $
+		physicalDeliveryOfficeName $ st ) )
+
+objectclass ( 2.5.6.11 NAME 'applicationProcess'
+	DESC 'RFC2256: an application process'
+	SUP top STRUCTURAL
+	MUST cn
+	MAY ( seeAlso $ ou $ l $ description ) )
+
+objectclass ( 2.5.6.12 NAME 'applicationEntity'
+	DESC 'RFC2256: an application entity'
+	SUP top STRUCTURAL
+	MUST ( presentationAddress $ cn )
+	MAY ( supportedApplicationContext $ seeAlso $ ou $ o $ l $
+	description ) )
+
+objectclass ( 2.5.6.13 NAME 'dSA'
+	DESC 'RFC2256: a directory system agent (a server)'
+	SUP applicationEntity STRUCTURAL
+	MAY knowledgeInformation )
+
+objectclass ( 2.5.6.14 NAME 'device'
+	DESC 'RFC2256: a device'
+	SUP top STRUCTURAL
+	MUST cn
+	MAY ( serialNumber $ seeAlso $ owner $ ou $ o $ l $ description ) )
+
+objectclass ( 2.5.6.15 NAME 'strongAuthenticationUser'
+	DESC 'RFC2256: a strong authentication user'
+	SUP top AUXILIARY
+	MUST userCertificate )
+
+objectclass ( 2.5.6.16 NAME 'certificationAuthority'
+	DESC 'RFC2256: a certificate authority'
+	SUP top AUXILIARY
+	MUST ( authorityRevocationList $ certificateRevocationList $
+		cACertificate ) MAY crossCertificatePair )
+
+objectclass ( 2.5.6.17 NAME 'groupOfUniqueNames'
+	DESC 'RFC2256: a group of unique names (DN and Unique Identifier)'
+	SUP top STRUCTURAL
+	MUST ( uniqueMember $ cn )
+	MAY ( businessCategory $ seeAlso $ owner $ ou $ o $ description ) )
+
+objectclass ( 2.5.6.18 NAME 'userSecurityInformation'
+	DESC 'RFC2256: a user security information'
+	SUP top AUXILIARY
+	MAY ( supportedAlgorithms ) )
+
+objectclass ( 2.5.6.16.2 NAME 'certificationAuthority-V2'
+	SUP certificationAuthority
+	AUXILIARY MAY ( deltaRevocationList ) )
+
+objectclass ( 2.5.6.19 NAME 'cRLDistributionPoint'
+	SUP top STRUCTURAL
+	MUST ( cn )
+	MAY ( certificateRevocationList $ authorityRevocationList $
+		deltaRevocationList ) )
+
+objectclass ( 2.5.6.20 NAME 'dmd'
+	SUP top STRUCTURAL
+	MUST ( dmdName )
+	MAY ( userPassword $ searchGuide $ seeAlso $ businessCategory $
+		x121Address $ registeredAddress $ destinationIndicator $
+		preferredDeliveryMethod $ telexNumber $ teletexTerminalIdentifier $
+		telephoneNumber $ internationaliSDNNumber $ facsimileTelephoneNumber $
+		street $ postOfficeBox $ postalCode $ postalAddress $
+		physicalDeliveryOfficeName $ st $ l $ description ) )
+
+#
+# Object Classes from RFC 2587
+#
+objectclass ( 2.5.6.21 NAME 'pkiUser'
+	DESC 'RFC2587: a PKI user'
+	SUP top AUXILIARY
+	MAY userCertificate )
+
+objectclass ( 2.5.6.22 NAME 'pkiCA'
+	DESC 'RFC2587: PKI certificate authority'
+	SUP top AUXILIARY
+	MAY ( authorityRevocationList $ certificateRevocationList $
+		cACertificate $ crossCertificatePair ) )
+
+objectclass ( 2.5.6.23 NAME 'deltaCRL'
+	DESC 'RFC2587: PKI user'
+	SUP top AUXILIARY
+	MAY deltaRevocationList )
+
+#
+# Standard Track URI label schema from RFC 2079
+#
+
+# this is already in system.schema
+#attributetype ( 1.3.6.1.4.1.250.1.57 NAME 'labeledURI'
+#       DESC 'RFC2079: Uniform Resource Identifier with optional label'
+#       EQUALITY caseExactMatch
+#       SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+
+# This was malformed with MAY list before the SUP and AUXILIARY fields
+objectclass ( 1.3.6.1.4.1.250.3.15 NAME 'labeledURIObject'
+	DESC 'RFC2079: object that contains the URI attribute type'
+	SUP top AUXILIARY
+	MAY ( labeledURI ) )
+
+#
+# Derived from RFC 1274, but with new "short names"
+#
+attributetype ( 0.9.2342.19200300.100.1.1
+	NAME ( 'uid' 'userid' )
+	DESC 'RFC1274: user identifier'
+	EQUALITY caseIgnoreMatch
+	SUBSTR caseIgnoreSubstringsMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
+
+attributetype ( 0.9.2342.19200300.100.1.3
+	NAME ( 'mail' 'rfc822Mailbox' )
+	DESC 'RFC1274: RFC822 Mailbox'
+    EQUALITY caseIgnoreIA5Match
+    SUBSTR caseIgnoreIA5SubstringsMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} )
+
+objectclass ( 0.9.2342.19200300.100.4.19 NAME 'simpleSecurityObject'
+	DESC 'RFC1274: simple security object'
+	SUP top AUXILIARY
+	MUST userPassword )
+
+# RFC 1274 + RFC 2247
+attributetype ( 0.9.2342.19200300.100.1.25
+	NAME ( 'dc' 'domainComponent' )
+	DESC 'RFC1274/2247: domain component'
+	EQUALITY caseIgnoreIA5Match
+	SUBSTR caseIgnoreIA5SubstringsMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+
+# RFC 2247
+objectclass ( 1.3.6.1.4.1.1466.344 NAME 'dcObject'
+	DESC 'RFC2247: domain component object'
+	SUP top AUXILIARY MUST dc )
+
+# RFC 2377
+objectclass ( 1.3.6.1.1.3.1 NAME 'uidObject'
+	DESC 'RFC2377: uid object'
+	SUP top AUXILIARY MUST uid )
+
+# From COSINE Pilot
+attributetype ( 0.9.2342.19200300.100.1.37
+	NAME 'associatedDomain'
+	DESC 'RFC1274: domain associated with object'
+	EQUALITY caseIgnoreIA5Match
+	SUBSTR caseIgnoreIA5SubstringsMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+
+# RFC 2459
+attributetype ( 1.2.840.113549.1.9.1
+	NAME ( 'email' 'emailAddress' 'pkcs9email' )
+	DESC 'RFC2459: legacy attribute for email addresses in DNs'
+	EQUALITY caseIgnoreIA5Match
+	SUBSTR caseIgnoreIA5SubstringsMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{128} )
+
diff --git a/old_trunk/schema-bootstrap/src/main/schema/system.schema b/old_trunk/schema-bootstrap/src/main/schema/system.schema
new file mode 100644
index 0000000..0fd2cf7
--- /dev/null
+++ b/old_trunk/schema-bootstrap/src/main/schema/system.schema
@@ -0,0 +1,337 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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. 
+
+# =============================================================================
+#                               System Schema
+#
+# Depends on no other schema!
+# =============================================================================
+
+attributetype ( 2.5.4.0 NAME 'objectClass'
+    DESC 'RFC2256: object classes of the entity'
+    EQUALITY objectIdentifierMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )
+
+attributetype ( 2.5.21.9 NAME 'structuralObjectClass'
+    DESC 'X.500(93): structural object class of entry'
+    EQUALITY objectIdentifierMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.38
+    SINGLE-VALUE
+    NO-USER-MODIFICATION
+    USAGE directoryOperation )
+
+attributetype ( 2.5.18.1 NAME 'createTimestamp'
+    DESC 'RFC2252: time which object was created'
+    EQUALITY generalizedTimeMatch
+    ORDERING generalizedTimeOrderingMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.24
+    SINGLE-VALUE
+    NO-USER-MODIFICATION
+    USAGE directoryOperation )
+
+attributetype ( 2.5.18.2 NAME 'modifyTimestamp'
+    DESC 'RFC2252: time which object was last modified'
+    EQUALITY generalizedTimeMatch
+    ORDERING generalizedTimeOrderingMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.24
+    SINGLE-VALUE
+    NO-USER-MODIFICATION
+    USAGE directoryOperation )
+
+attributetype ( 2.5.18.3 NAME 'creatorsName'
+    DESC 'RFC2252: name of creator'
+    EQUALITY distinguishedNameMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.12
+    SINGLE-VALUE
+    NO-USER-MODIFICATION
+    USAGE directoryOperation )
+
+attributetype ( 2.5.18.4 NAME 'modifiersName'
+    DESC 'RFC2252: name of last modifier'
+    EQUALITY distinguishedNameMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.12
+    SINGLE-VALUE
+    NO-USER-MODIFICATION
+    USAGE directoryOperation )
+
+attributetype ( 2.5.18.9 NAME 'hasSubordinates'
+    DESC 'X.501: entry has children'
+    EQUALITY booleanMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.7
+    SINGLE-VALUE
+    NO-USER-MODIFICATION
+    USAGE directoryOperation )
+
+attributetype ( 2.5.18.10 NAME 'subschemaSubentry'
+    DESC 'RFC2252: name of controlling subschema entry'
+    EQUALITY distinguishedNameMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.12
+    SINGLE-VALUE
+    NO-USER-MODIFICATION
+    USAGE directoryOperation )
+
+attributetype ( 2.5.18.12 NAME 'collectiveAttributeSubentries'
+    EQUALITY distinguishedNameMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.12
+    NO-USER-MODIFICATION
+    USAGE directoryOperation )
+
+attributetype ( 2.5.18.7 NAME 'collectiveExclusions'
+    EQUALITY objectIdentifierMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.38
+    USAGE directoryOperation )
+
+# root DSE attributes
+
+attributetype ( 1.3.6.1.4.1.1466.101.120.6 NAME 'altServer'
+    DESC 'RFC2252: alternative servers'
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+    USAGE dSAOperation )
+
+attributetype ( 1.3.6.1.4.1.1466.101.120.5 NAME 'namingContexts'
+    DESC 'RFC2252: naming contexts'
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.12
+    USAGE dSAOperation )
+
+attributetype ( 1.3.6.1.4.1.1466.101.120.13 NAME 'supportedControl'
+    DESC 'RFC2252: supported controls'
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 USAGE dSAOperation )
+
+attributetype ( 1.3.6.1.4.1.1466.101.120.7 NAME 'supportedExtension'
+    DESC 'RFC2252: supported extended operations'
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.38
+    USAGE dSAOperation )
+
+attributetype ( 1.3.6.1.4.1.1466.101.120.15 NAME 'supportedLDAPVersion'
+    DESC 'RFC2252: supported LDAP versions'
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
+    USAGE dSAOperation )
+
+attributetype ( 1.3.6.1.4.1.1466.101.120.14 NAME 'supportedSASLMechanisms'
+    DESC 'RFC2252: supported SASL mechanisms'
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+    USAGE dSAOperation )
+
+attributetype ( 1.3.6.1.1.4 NAME 'vendorName'
+    DESC 'RFC3045: name of implementation vendor'
+    EQUALITY caseExactMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+    SINGLE-VALUE
+    NO-USER-MODIFICATION
+    USAGE dSAOperation )
+
+attributetype ( 1.3.6.1.1.5 NAME 'vendorVersion'
+    DESC 'RFC3045: version of implementation'
+    EQUALITY caseExactMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+    SINGLE-VALUE NO-USER-MODIFICATION
+    USAGE dSAOperation )
+
+# ===================
+# subentry attributes
+# ===================
+
+attributetype ( 2.5.18.5 NAME 'administrativeRole'
+    EQUALITY objectIdentifierMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.38
+    USAGE directoryOperation )
+
+attributetype ( 2.5.18.6 NAME 'subtreeSpecification'
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.45
+    SINGLE-VALUE
+    USAGE directoryOperation )
+
+# =============================
+# subschema subentry attributes
+# =============================
+
+attributetype ( 2.5.21.1 NAME 'dITStructureRules'
+    DESC 'RFC2252: DIT structure rules'
+    EQUALITY integerFirstComponentMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.17
+    USAGE directoryOperation )
+
+attributetype ( 2.5.21.2 NAME 'dITContentRules'
+    DESC 'RFC2252: DIT content rules'
+    EQUALITY objectIdentifierFirstComponentMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.16
+    USAGE directoryOperation )
+
+attributetype ( 2.5.21.4 NAME 'matchingRules'
+    DESC 'RFC2252: matching rules'
+    EQUALITY objectIdentifierFirstComponentMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.30
+    USAGE directoryOperation )
+
+attributetype ( 2.5.21.5 NAME 'attributeTypes'
+    DESC 'RFC2252: attribute types'
+    EQUALITY objectIdentifierFirstComponentMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.3
+    USAGE directoryOperation )
+
+attributetype ( 2.5.21.6 NAME 'objectClasses'
+    DESC 'RFC2252: object classes'
+    EQUALITY objectIdentifierFirstComponentMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.37
+    USAGE directoryOperation )
+
+attributetype ( 2.5.21.7 NAME 'nameForms'
+    DESC 'RFC2252: name forms '
+    EQUALITY objectIdentifierFirstComponentMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.35
+    USAGE directoryOperation )
+
+attributetype ( 2.5.21.8 NAME 'matchingRuleUse'
+    DESC 'RFC2252: matching rule uses'
+    EQUALITY objectIdentifierFirstComponentMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.31
+    USAGE directoryOperation )
+
+attributetype ( 1.3.6.1.4.1.1466.101.120.16 NAME 'ldapSyntaxes'
+    DESC 'RFC2252: LDAP syntaxes'
+    EQUALITY objectIdentifierFirstComponentMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.54
+    USAGE directoryOperation )
+
+# =====================
+# knowledge information
+# =====================
+
+attributetype ( 2.5.4.1 NAME ( 'aliasedObjectName' 'aliasedEntryName' )
+    DESC 'RFC2256: name of aliased object'
+    EQUALITY distinguishedNameMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.12
+    SINGLE-VALUE )
+
+attributetype ( 2.16.840.1.113730.3.1.34 NAME 'ref'
+    DESC 'namedref: subordinate referral URL'
+    EQUALITY caseExactMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+    USAGE distributedOperation )
+
+attributetype ( 1.3.6.1.4.1.1466.101.119.3 NAME 'entryTtl'
+    DESC 'RFC2589: entry time-to-live'
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
+    SINGLE-VALUE
+    NO-USER-MODIFICATION
+    USAGE dSAOperation )
+
+attributetype ( 1.3.6.1.4.1.1466.101.119.4 NAME 'dynamicSubtrees'
+    DESC 'RFC2589: dynamic subtrees'
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.12
+    NO-USER-MODIFICATION
+    USAGE dSAOperation )
+
+# =============================================================
+# userApplication attributes (which system schema depends upon)
+# =============================================================
+
+attributetype ( 2.5.4.49 NAME 'distinguishedName'
+    DESC 'RFC2256: common supertype of DN attributes'
+    EQUALITY distinguishedNameMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
+
+attributetype ( 2.5.4.41 NAME 'name'
+    DESC 'RFC2256: common supertype of name attributes'
+    EQUALITY caseIgnoreMatch
+    SUBSTR caseIgnoreSubstringsMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{32768} )
+
+attributetype ( 2.5.4.3 NAME ( 'cn' 'commonName' )
+    DESC 'RFC2256: common name(s) for which the entity is known by'
+    SUP name )
+
+attributetype ( 2.5.4.35 NAME 'userPassword'
+    DESC 'RFC2256/2307: password of user'
+    EQUALITY octetStringMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.40{128} )
+
+attributetype (  1.3.6.1.4.1.250.1.57 NAME 'labeledURI'
+    DESC 'RFC2079: Uniform Resource Identifier with optional label'
+    EQUALITY caseExactMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+
+attributetype ( 1.3.6.1.4.1.4203.1.3.5 NAME 'supportedFeatures'
+    EQUALITY objectIdentifierMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.38
+    USAGE dSAOperation )
+
+# ==============
+# objectClasses
+# ==============
+
+
+objectclass ( 2.5.6.0 NAME 'top'
+    DESC 'top of the superclass chain'
+    ABSTRACT
+    MUST objectClass )
+
+
+objectclass ( 1.3.6.1.4.1.1466.101.120.111
+    NAME 'extensibleObject'
+    DESC 'RFC2252: extensible object'
+    SUP top
+    AUXILIARY )
+
+
+objectclass ( 2.5.6.1 NAME 'alias'
+    DESC 'RFC2256: an alias'
+    SUP top
+    STRUCTURAL
+    MUST aliasedObjectName )
+
+
+objectclass ( 2.16.840.1.113730.3.2.6 NAME 'referral'
+    DESC 'namedref: named subordinate referral'
+    SUP top
+    STRUCTURAL
+    MUST ref )
+
+
+objectclass ( 1.3.6.1.4.1.4203.1.4.1
+    NAME ( 'OpenLDAProotDSE' 'LDAProotDSE' )
+    DESC 'OpenLDAP Root DSE object'
+    SUP top
+    STRUCTURAL
+    MAY cn )
+
+
+objectclass ( 2.5.17.0 NAME 'subentry'
+    SUP top
+    STRUCTURAL
+    MUST ( cn $ subtreeSpecification ) )
+
+
+objectclass ( 2.5.20.1 NAME 'subschema'
+    DESC 'RFC2252: controlling subschema (sub)entry'
+    AUXILIARY
+    MAY ( dITStructureRules $ nameForms $ ditContentRules $
+          objectClasses $ attributeTypes $ matchingRules $
+          matchingRuleUse ) )
+
+
+objectclass ( 2.5.17.2
+    NAME 'collectiveAttributeSubentry'
+    AUXILIARY )
+
+
+objectclass ( 1.3.6.1.4.1.1466.101.119.2
+    NAME 'dynamicObject'
+    DESC 'RFC2589: Dynamic Object'
+    SUP top
+    AUXILIARY )
+
diff --git a/old_trunk/schema-bootstrap/src/site/site.xml b/old_trunk/schema-bootstrap/src/site/site.xml
new file mode 100644
index 0000000..9ec7c16
--- /dev/null
+++ b/old_trunk/schema-bootstrap/src/site/site.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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.
+-->
+<!--
+  @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+-->
+<project name="${project.name}">
+    <bannerLeft>
+        <name>${project.name}</name>
+    </bannerLeft>
+    <publishDate position="navigation-bottom" format="dd-MM-yyyy HH:mm" />
+    <body>
+        <menu name="Parent">
+            <item name="Apache Directory ApacheDS" href="../index.html" />
+        </menu>
+        <menu ref="reports" />
+    </body>
+</project>
diff --git a/old_trunk/schema-extras/pom.xml b/old_trunk/schema-extras/pom.xml
new file mode 100644
index 0000000..c08cf40
--- /dev/null
+++ b/old_trunk/schema-extras/pom.xml
@@ -0,0 +1,162 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you 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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.directory.server</groupId>
+    <artifactId>apacheds-parent</artifactId>
+    <version>1.5.4-SNAPSHOT</version>
+  </parent>
+  <artifactId>apacheds-schema-extras</artifactId>
+  <name>ApacheDS Extra Schemas</name>
+  <packaging>jar</packaging>
+
+  <description>
+    A set of additional bootstrap schemas beyond the essential set.
+  </description>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-schema-registries</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-schema-bootstrap</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.directory.server</groupId>
+        <artifactId>apacheds-core-plugin</artifactId>
+        <version>${pom.version}</version>
+        <configuration>
+          <schemaSourcesDir>src/main/schema</schemaSourcesDir>
+          <schemas>
+            <schema>
+              <name>apachedns</name>
+              <dependencies>
+                <dependency>system</dependency>
+                <dependency>core</dependency>
+              </dependencies>
+            </schema>
+            <schema>
+              <name>autofs</name>
+              <dependencies>
+                <dependency>system</dependency>
+                <dependency>core</dependency>
+                <dependency>cosine</dependency>
+              </dependencies>
+            </schema>
+            <schema>
+              <name>collective</name>
+              <dependencies>
+                <dependency>system</dependency>
+                <dependency>core</dependency>
+              </dependencies>
+            </schema>
+            <schema>
+              <name>corba</name>
+              <dependencies>
+                <dependency>system</dependency>
+                <dependency>core</dependency>
+              </dependencies>
+            </schema>
+            <schema>
+              <name>cosine</name>
+              <dependencies>
+                <dependency>system</dependency>
+                <dependency>core</dependency>
+              </dependencies>
+            </schema>
+            <schema>
+              <name>inetorgperson</name>
+              <dependencies>
+                <dependency>system</dependency>
+                <dependency>core</dependency>
+                <dependency>cosine</dependency>
+              </dependencies>
+            </schema>
+            <schema>
+              <name>java</name>
+              <dependencies>
+                <dependency>system</dependency>
+                <dependency>core</dependency>
+              </dependencies>
+            </schema>
+            <schema>
+              <name>krb5kdc</name>
+              <dependencies>
+                <dependency>system</dependency>
+                <dependency>core</dependency>
+              </dependencies>
+            </schema>
+            <schema>
+              <name>nis</name>
+              <dependencies>
+                <dependency>system</dependency>
+                <dependency>core</dependency>
+                <dependency>cosine</dependency>
+              </dependencies>
+            </schema>
+            <schema>
+              <name>mozilla</name>
+              <dependencies>
+                <dependency>system</dependency>
+                <dependency>core</dependency>
+              </dependencies>
+            </schema>
+            <schema>
+              <name>dhcp</name>
+              <dependencies>
+                <dependency>system</dependency>
+                <dependency>core</dependency>
+              </dependencies>
+            </schema>
+            <schema>
+              <name>samba</name>
+              <dependencies>
+                <dependency>system</dependency>
+                <dependency>core</dependency>
+                <dependency>inetorgperson</dependency>
+                <dependency>nis</dependency>
+              </dependencies>
+            </schema>
+          </schemas>
+        </configuration>
+        <executions>
+          <execution>
+            <goals>
+              <goal>generate</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+</project>
+
diff --git a/old_trunk/schema-extras/src/main/java/org/apache/directory/server/schema/bootstrap/NisComparatorProducer.java b/old_trunk/schema-extras/src/main/java/org/apache/directory/server/schema/bootstrap/NisComparatorProducer.java
new file mode 100644
index 0000000..463bd7d
--- /dev/null
+++ b/old_trunk/schema-extras/src/main/java/org/apache/directory/server/schema/bootstrap/NisComparatorProducer.java
@@ -0,0 +1,80 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.bootstrap;
+
+
+import java.util.Comparator;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.schema.bootstrap.AbstractBootstrapProducer;
+import org.apache.directory.server.schema.bootstrap.ProducerCallback;
+import org.apache.directory.server.schema.bootstrap.ProducerTypeEnum;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.schema.CachingNormalizer;
+import org.apache.directory.shared.ldap.schema.ComparableComparator;
+import org.apache.directory.shared.ldap.schema.DeepTrimNormalizer;
+import org.apache.directory.shared.ldap.schema.NormalizingComparator;
+
+
+/**
+ * A producer of Comparator objects for the nis schema.
+ * Modified by hand from generated code
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class NisComparatorProducer extends AbstractBootstrapProducer
+{
+    public NisComparatorProducer()
+    {
+        super( ProducerTypeEnum.COMPARATOR_PRODUCER );
+    }
+
+    
+    public static class DeepTrimCachingNormalizingComparator extends NormalizingComparator
+    {
+        public DeepTrimCachingNormalizingComparator()        
+        {
+            super( new CachingNormalizer( new DeepTrimNormalizer() ), new ComparableComparator() );
+        }
+    }
+
+    
+    // ------------------------------------------------------------------------
+    // BootstrapProducer Methods
+    // ------------------------------------------------------------------------
+
+    /**
+     * @see org.apache.directory.server.schema.bootstrap.BootstrapProducer#produce(Registries, ProducerCallback)
+     */
+    public void produce( Registries registries, ProducerCallback cb ) throws NamingException
+    {
+        Comparator comparator;
+
+        /* Really an openLDAP matching rule but its used in he nis so its here
+         *
+         ( 1.3.6.1.4.1.4203.1.2.1 NAME 'caseExactIA5SubstringsMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+         */
+        comparator = new DeepTrimCachingNormalizingComparator();
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.4203.1.2.1", comparator );
+    }
+}
diff --git a/old_trunk/schema-extras/src/main/java/org/apache/directory/server/schema/bootstrap/NisMatchingRuleProducer.java b/old_trunk/schema-extras/src/main/java/org/apache/directory/server/schema/bootstrap/NisMatchingRuleProducer.java
new file mode 100644
index 0000000..9957a8d
--- /dev/null
+++ b/old_trunk/schema-extras/src/main/java/org/apache/directory/server/schema/bootstrap/NisMatchingRuleProducer.java
@@ -0,0 +1,69 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.bootstrap;
+
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.schema.bootstrap.AbstractBootstrapProducer;
+import org.apache.directory.server.schema.bootstrap.ProducerCallback;
+import org.apache.directory.server.schema.bootstrap.ProducerTypeEnum;
+import org.apache.directory.server.schema.registries.Registries;
+
+
+/**
+ * A producer of MatchingRule objects for the nis schema.
+ * Modified by hand from generated code
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class NisMatchingRuleProducer extends AbstractBootstrapProducer
+{
+    public NisMatchingRuleProducer()
+    {
+        super( ProducerTypeEnum.MATCHING_RULE_PRODUCER );
+    }
+
+
+    // ------------------------------------------------------------------------
+    // BootstrapProducer Methods
+    // ------------------------------------------------------------------------
+
+    /**
+     * @see org.apache.directory.server.schema.bootstrap.BootstrapProducer#produce(org.apache.directory.server.schema.registries.Registries, org.apache.directory.server.schema.bootstrap.ProducerCallback)
+     */
+    public void produce( Registries registries, ProducerCallback cb ) throws NamingException
+    {
+        BootstrapMatchingRule mrule;
+
+        /* Really an openLDAP matching rule but its used in he nis so its here
+         *
+         ( 1.3.6.1.4.1.4203.1.2.1 NAME 'caseExactIA5SubstringsMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+         */
+
+        mrule = new BootstrapMatchingRule( "1.3.6.1.4.1.4203.1.2.1", registries );
+        mrule.setNames( new String[]
+            { "caseExactIA5SubstringsMatch" } );
+        mrule.setSyntaxOid( "1.3.6.1.4.1.1466.115.121.1.26" );
+        cb.schemaObjectProduced( this, mrule.getOid(), mrule );
+    }
+}
diff --git a/old_trunk/schema-extras/src/main/java/org/apache/directory/server/schema/bootstrap/NisNormalizerProducer.java b/old_trunk/schema-extras/src/main/java/org/apache/directory/server/schema/bootstrap/NisNormalizerProducer.java
new file mode 100644
index 0000000..a1f2fc4
--- /dev/null
+++ b/old_trunk/schema-extras/src/main/java/org/apache/directory/server/schema/bootstrap/NisNormalizerProducer.java
@@ -0,0 +1,80 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.bootstrap;
+
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.schema.bootstrap.AbstractBootstrapProducer;
+import org.apache.directory.server.schema.bootstrap.ProducerCallback;
+import org.apache.directory.server.schema.bootstrap.ProducerTypeEnum;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.schema.CachingNormalizer;
+import org.apache.directory.shared.ldap.schema.DeepTrimNormalizer;
+import org.apache.directory.shared.ldap.schema.Normalizer;
+
+
+/**
+ * A producer of Normalizer objects for the nis schema.
+ * Modified by hand from generated code
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class NisNormalizerProducer extends AbstractBootstrapProducer
+{
+    public NisNormalizerProducer()
+    {
+        super( ProducerTypeEnum.NORMALIZER_PRODUCER );
+    }
+
+    
+    public static class CachingDeepTrimNormalizer extends CachingNormalizer
+    {
+        private static final long serialVersionUID = 1L;
+        
+        public CachingDeepTrimNormalizer()
+        {
+            super( new DeepTrimNormalizer() );
+        }
+    }
+    
+        
+    // ------------------------------------------------------------------------
+    // BootstrapProducer Methods
+    // ------------------------------------------------------------------------
+
+    /**
+     * @see org.apache.directory.server.schema.bootstrap.BootstrapProducer#produce(Registries, ProducerCallback)
+     */
+    public void produce( Registries registries, ProducerCallback cb ) throws NamingException
+    {
+        Normalizer normalizer;
+
+        /* Really an openLDAP matching rule but its used in he nis so its here
+         *
+         ( 1.3.6.1.4.1.4203.1.2.1 NAME 'caseExactIA5SubstringsMatch'
+         SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+         */
+        normalizer = new CachingDeepTrimNormalizer();
+        cb.schemaObjectProduced( this, "1.3.6.1.4.1.4203.1.2.1", normalizer );
+
+    }
+}
diff --git a/old_trunk/schema-extras/src/main/java/org/apache/directory/server/schema/bootstrap/NisSyntaxCheckerProducer.java b/old_trunk/schema-extras/src/main/java/org/apache/directory/server/schema/bootstrap/NisSyntaxCheckerProducer.java
new file mode 100644
index 0000000..d599323
--- /dev/null
+++ b/old_trunk/schema-extras/src/main/java/org/apache/directory/server/schema/bootstrap/NisSyntaxCheckerProducer.java
@@ -0,0 +1,75 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.bootstrap;
+
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.schema.bootstrap.AbstractBootstrapProducer;
+import org.apache.directory.server.schema.bootstrap.ProducerCallback;
+import org.apache.directory.server.schema.bootstrap.ProducerTypeEnum;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.schema.syntax.AcceptAllSyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.SyntaxChecker;
+
+
+/**
+ * A producer of SyntaxChecker objects for the nis schema.
+ * Modified by hand from generated code
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class NisSyntaxCheckerProducer extends AbstractBootstrapProducer
+{
+    public NisSyntaxCheckerProducer()
+    {
+        super( ProducerTypeEnum.SYNTAX_CHECKER_PRODUCER );
+    }
+
+
+    // ------------------------------------------------------------------------
+    // BootstrapProducer Methods
+    // ------------------------------------------------------------------------
+
+    /**
+     * @see org.apache.directory.server.schema.bootstrap.BootstrapProducer#produce(Registries, org.apache.directory.server.schema.bootstrap.ProducerCallback)
+     */
+    public void produce( Registries registries, ProducerCallback cb ) throws NamingException
+    {
+        SyntaxChecker syntaxChecker;
+
+        /*
+         * We are going to need a syntax checker for each and every one of
+         * these syntaxes.  However right now we're probably not going to be
+         * turning on syntax checking or are not as interested in it.  So we
+         * can put in place simple do nothing syntax checkers - which is really
+         * the binary syntax checker.
+         */
+
+        // 1.3.6.1.1.1.0.0 - RFC2307 NIS Netgroup Triple
+        syntaxChecker = new AcceptAllSyntaxChecker( "1.3.6.1.1.1.0.0" );
+        cb.schemaObjectProduced( this, syntaxChecker.getSyntaxOid(), syntaxChecker );
+
+        // 1.3.6.1.1.1.0.1 - RFC2307 Boot Parameter Syntax
+        syntaxChecker = new AcceptAllSyntaxChecker( "1.3.6.1.1.1.0.1" );
+        cb.schemaObjectProduced( this, syntaxChecker.getSyntaxOid(), syntaxChecker );
+    }
+}
diff --git a/old_trunk/schema-extras/src/main/java/org/apache/directory/server/schema/bootstrap/NisSyntaxProducer.java b/old_trunk/schema-extras/src/main/java/org/apache/directory/server/schema/bootstrap/NisSyntaxProducer.java
new file mode 100644
index 0000000..e83d253
--- /dev/null
+++ b/old_trunk/schema-extras/src/main/java/org/apache/directory/server/schema/bootstrap/NisSyntaxProducer.java
@@ -0,0 +1,75 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.bootstrap;
+
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.schema.bootstrap.AbstractBootstrapProducer;
+import org.apache.directory.server.schema.bootstrap.ProducerCallback;
+import org.apache.directory.server.schema.bootstrap.ProducerTypeEnum;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.server.schema.registries.SyntaxCheckerRegistry;
+
+
+/**
+ * A producer of Syntax objects for the nis schema.
+ * Modified by hand from generated code
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class NisSyntaxProducer extends AbstractBootstrapProducer
+{
+    public NisSyntaxProducer()
+    {
+        super( ProducerTypeEnum.SYNTAX_PRODUCER );
+    }
+
+
+    // ------------------------------------------------------------------------
+    // BootstrapProducer Methods
+    // ------------------------------------------------------------------------
+
+    /**
+     * @see org.apache.directory.server.schema.bootstrap.BootstrapProducer#produce(org.apache.directory.server.schema.registries.Registries, org.apache.directory.server.schema.bootstrap.ProducerCallback)
+     */
+    public void produce( Registries registries, ProducerCallback cb ) throws NamingException
+    {
+        BootstrapSyntax syntax;
+        SyntaxCheckerRegistry syntaxCheckerRegistry = registries.getSyntaxCheckerRegistry();
+
+        // 1.3.6.1.1.1.0.0 - RFC2307 NIS Netgroup Triple
+        syntax = new BootstrapSyntax( "1.3.6.1.1.1.0.0", syntaxCheckerRegistry );
+        syntax.setDescription( "RFC2307 NIS Netgroup Triple" );
+        syntax.setNames( new String[]
+            { "NIS Netgroup Triple" } );
+        syntax.setHumanReadable( true );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+        // 1.3.6.1.1.1.0.1 - RFC2307 Boot Parameter Syntax
+        syntax = new BootstrapSyntax( "1.3.6.1.1.1.0.1", syntaxCheckerRegistry );
+        syntax.setNames( new String[]
+            { "NIS Boot Parameter" } );
+        syntax.setHumanReadable( true );
+        cb.schemaObjectProduced( this, syntax.getOid(), syntax );
+
+    }
+}
diff --git a/old_trunk/schema-extras/src/main/java/org/apache/directory/server/schema/bootstrap/package-info.java b/old_trunk/schema-extras/src/main/java/org/apache/directory/server/schema/bootstrap/package-info.java
new file mode 100644
index 0000000..472cc74
--- /dev/null
+++ b/old_trunk/schema-extras/src/main/java/org/apache/directory/server/schema/bootstrap/package-info.java
@@ -0,0 +1,122 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.
+ *
+ */
+
+/**
+ * <pre>
+ * <p>
+ * Contains interfaces and classes used for bootstrap schema object loading and
+ * configuration.
+ * </p>
+ * <p>
+ * Briefly, before delving into the reasons behind our choices, we would like to
+ * summarize what is here.  First off each schema entity of significance whether
+ * defined by the protocol or not is introduced into the system using a bootstrap
+ * configuration set class.  This is a class that implements a specific interface
+ * for creating all the needed schema objects to establish a baseline within the
+ * system.  All such schema objects generated will be owned by the directory
+ * administrator.  Furthermore these objects are temporary and can be overridden
+ * in the solid state using altered versions that are authoritative definitions
+ * within the system backend.
+ * </p>
+ * <p>
+ * Just what does all this mean?  Hard coded schema objects in Java classes
+ * which implement a configuration set interface can be loaded into the system
+ * and used while bootstrapping it.  If this is the first time bootstrapping,
+ * meaning these objects are NOT found within the system backend, they will
+ * be persisted into the system backend at the very end of the bootstrap stage so
+ * they can be resolved in the solid state.  Changes to these objects via LDAP
+ * within the system backend by the admin persist across bootstrap sessions.  They
+ * persist because the bootstrap object is never stored in the system backend
+ * if a record for that object already exists.
+ * </p>
+ * <p>
+ * Why are we storing a configuration directly in Java code?  First off the
+ * likelihood of altering schema object fields to bootstrap the system is highly
+ * unlikely.  Secondly most of these objects are published static entities and
+ * changing them would have serious repercussions.  Hence its highly unlikely
+ * that these objects will change and there is no need to get complex to try
+ * to have an external representation for these schema objects.  The
+ * representation of these objects as entries and attributes within the directory
+ * is enough of an external representation.  Schema manipulation tools already
+ * exist for making changes so why have another additional
+ * mechanism/representation as well.
+ * </p>
+ * <hr>
+ * <h3>Notes:</h3>
+ * <ul>
+ * <li>
+ * Baseline schema configuration sets are loaded first.
+ * </li>
+ * <li>
+ * Within a logical schema (e.g. krb5-kdc) the ConfigurationSets of that
+ * schema will be loaded in an order consistent with datatype dependencies.
+ * For example a ConfigurationSet (CS) for SyntaxCheckers will be loaded
+ * before the one for Syntaxes within the same schema.  This is because
+ * Syntaxes depend upon SyntaxCheckers and the proper SyntaxChecker must be
+ * resolvable via the SyntaxChecker registry.
+ * </li>
+ * <li>
+ * A CS shall contain schema objects associated with a specific owner, and a
+ * specific logical schema.  The set of CS schema objects may depend on
+ * Schema objects within other logic schemas, hence other CS objects.  These
+ * dependencies on other logical schemas are exposed by the CS interface.
+ * </li>
+ * <li>
+ * Other user defined CS classes can be loaded by the system as well.  The
+ * system can search on the classpath for classes implementing the CS
+ * interface.  It would then need to analyze dependencies to determine which
+ * schema object sets to process/load first.  This is the most rudimentary
+ * of mechanisms for users to introduce new schema objects into the system.
+ * The other means is during the solid state through LDAP itself.  Changes
+ * can be made along with new schema object introductions via schemaSubentry
+ * changes in the usual LDAP manager where schema objects are attributes in
+ * a massive entry.  Another means is through the alteration of easier to
+ * manage entries for schema objects through the system namespace.  Changes
+ * to either schemaSubEntry attributes or system namespace schema entries both
+ * mirror one another.  A change to one propagates to the other: the same
+ * entity is being manipulated through two different views.  One is a heck of
+ * a lot more manageable.
+ * </li>
+ * <li>
+ * We can add a resolver interface so a resolver can be registered after
+ * bootstrapping with a registry.  In the case of a registry miss we ask the
+ * resolver for the object.  If it cannot find it then well we're out of luck.
+ * Once the resolver is set for solid state operation, registry misses make
+ * the resolver go to disk on the system backend for authoritative copies.
+ * </li>
+ * <li>
+ * The resolver concept mentioned above can itself be another registry.  It's
+ * just a secondary disk registry used when the first misses disk.  So hence
+ * other registries should be able to be registerable with others to allow for
+ * registry delegation like the way classloaders delegate.  This way there is
+ * no need to replace bootstrap registries.  Once they have enough info to
+ * enable disk access, they can tap secondary schema information on disk to
+ * override the bootstrap objects first of all.  Secondly in the solid state
+ * schema objects can be brought into memory from disk.
+ * </li>
+ * </ul>
+ * </hr>
+ * </pre>
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+
+package org.apache.directory.server.schema.bootstrap;
diff --git a/old_trunk/schema-extras/src/main/schema/apachedns.schema b/old_trunk/schema-extras/src/main/schema/apachedns.schema
new file mode 100644
index 0000000..abb20d0
--- /dev/null
+++ b/old_trunk/schema-extras/src/main/schema/apachedns.schema
@@ -0,0 +1,172 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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. 
+
+# =============================================================================
+#                ApacheDS schema for storing DNS zones in LDAP
+# =============================================================================
+#               +---------------------------+-----------------+
+#               |         Apache OID        |   description   |
+#               +---------------------------+-----------------+
+#               +---------------------------+-----------------+
+# =============================================================================
+
+attributetype ( 1.3.6.1.4.1.18060.0.4.2.2.1 NAME 'apacheDnsClass'
+	DESC 'The class of a resource record'
+	EQUALITY caseIgnoreIA5Match
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.18060.0.4.2.2.2 NAME 'apacheDnsTtl'
+	DESC 'An integer denoting time to live'
+	EQUALITY integerMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.18060.0.4.2.2.3 NAME 'apacheDnsDomainName'
+	DESC 'A domain name represented as a sequence of labels'
+	EQUALITY caseIgnoreIA5Match
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.18060.0.4.2.2.4 NAME 'apacheDnsCharacterString'
+	DESC 'A string up to 256 characters in length'
+	EQUALITY caseIgnoreIA5Match
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.18060.0.4.2.2.5 NAME 'apacheDnsIpAddress'
+	DESC 'A 4 octet IP address'
+	EQUALITY caseIgnoreIA5Match
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{128} SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.18060.0.4.2.2.6 NAME 'apacheDnsSoaMName'
+	DESC 'The domain name of the name server that was the primary source of data for this zone'
+	SUP apacheDnsDomainName SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.18060.0.4.2.2.7 NAME 'apacheDnsSoaRName'
+	DESC 'The domain name which specifies the mailbox of the person responsible for this zone'
+	SUP apacheDnsDomainName SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.18060.0.4.2.2.8 NAME 'apacheDnsSoaSerial'
+	DESC 'The unsigned 32 bit version number of the original copy of the zone'
+	EQUALITY integerMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.18060.0.4.2.2.9 NAME 'apacheDnsSoaRefresh'
+	DESC 'A 32 bit time interval before the zone should be refreshed'
+	EQUALITY integerMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.18060.0.4.2.2.10 NAME 'apacheDnsSoaRetry'
+	DESC 'A 32 bit time interval that should elapse before a failed refresh should be retired'
+	EQUALITY integerMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.18060.0.4.2.2.11 NAME 'apacheDnsSoaExpire'
+	DESC 'A 32 bit time value that specifies the upper limit on the time interval that can elapse before the zone is no longer authoritative'
+	EQUALITY integerMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.18060.0.4.2.2.12 NAME 'apacheDnsSoaMinimum'
+	DESC 'The unsigned 32 bit minimum TTL field that should be exported with any RR from this zone.'
+	EQUALITY integerMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.18060.0.4.2.2.13 NAME 'apacheDnsMxPreference'
+	DESC 'An integer denoting the mail exchange preference'
+	EQUALITY integerMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.18060.0.4.2.2.14 NAME 'apacheDnsServicePriority'
+	DESC 'The unsigned 16 bit priority of this target host'
+	EQUALITY integerMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.18060.0.4.2.2.15 NAME 'apacheDnsServiceWeight'
+	DESC 'The unsigned 16 bit weight specifying a relative weight for entries with the same priority'
+	EQUALITY integerMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.18060.0.4.2.2.16 NAME 'apacheDnsServicePort'
+	DESC 'The unsigned 16 bit port on this target host of this service'
+	EQUALITY integerMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+objectclass ( 1.3.6.1.4.1.18060.0.4.2.3.1
+	NAME 'apacheDnsAbstractRecord'
+	DESC 'An abstract DNS record objectClass used to build other specific structural objectclasses for different record types'
+	SUP top
+	ABSTRACT
+	MUST cn
+	MAY ( apacheDnsTtl $ description ) )
+
+objectclass ( 1.3.6.1.4.1.18060.0.4.2.3.2 
+	NAME 'apacheDnsAddressRecord'
+	DESC 'An address A record'
+	SUP apacheDnsAbstractRecord 
+	MUST apacheDnsIpAddress )
+
+objectclass ( 1.3.6.1.4.1.18060.0.4.2.3.3 
+	NAME 'apacheDnsPointerRecord'
+	DESC 'A pointer PTR record'
+	SUP apacheDnsAbstractRecord 
+	MUST apacheDnsDomainName )
+
+objectclass ( 1.3.6.1.4.1.18060.0.4.2.3.4
+	NAME 'apacheDnsNameServerRecord'
+	DESC 'A name server NS record'
+	SUP apacheDnsAbstractRecord 
+	MUST apacheDnsDomainName )
+
+objectclass ( 1.3.6.1.4.1.18060.0.4.2.3.5
+	NAME 'apacheDnsStartOfAuthorityRecord'
+	DESC 'A start of authority SOA record'
+	SUP apacheDnsAbstractRecord
+	MUST ( apacheDnsSoaMName $ apacheDnsSoaRName $ apacheDnsSoaMinimum )
+	MAY ( apacheDnsClass $ apacheDnsSoaSerial $ apacheDnsSoaRefresh $ apacheDnsSoaRetry $ apacheDnsSoaExpire ) )
+
+objectclass ( 1.3.6.1.4.1.18060.0.4.2.3.6
+	NAME 'apacheDnsCanonicalNameRecord'
+	DESC 'A canonical name CNAME record'
+	SUP apacheDnsAbstractRecord 
+	MUST apacheDnsDomainName )
+
+objectclass ( 1.3.6.1.4.1.18060.0.4.2.3.7
+	NAME 'apacheDnsMailExchangeRecord'
+	DESC 'A mail exchange MX record'
+	SUP apacheDnsAbstractRecord 
+	MUST ( apacheDnsMxPreference $ apacheDnsDomainName ) )
+
+objectclass ( 1.3.6.1.4.1.18060.0.4.2.3.8
+	NAME 'apacheDnsTextRecord'
+	DESC 'A text TXT record'
+	SUP apacheDnsAbstractRecord 
+	MUST apacheDnsCharacterString )
+
+objectclass ( 1.3.6.1.4.1.18060.0.4.2.3.9
+	NAME 'apacheDnsServiceRecord'
+	DESC 'A service SRV record'
+	SUP apacheDnsAbstractRecord 
+	MUST ( apacheDnsServicePriority $ apacheDnsServiceWeight $ apacheDnsServicePort $ apacheDnsDomainName ) )
+
+objectclass ( 1.3.6.1.4.1.18060.0.4.2.3.10
+	NAME 'apacheDnsReferralNameServer'
+	DESC 'A non-authoritative referral or delegation name server'
+	SUP apacheDnsAbstractRecord 
+	MUST apacheDnsDomainName )
+
+objectclass ( 1.3.6.1.4.1.18060.0.4.2.3.11
+	NAME 'apacheDnsReferralAddress'
+	DESC 'A non-authoritative referral or glue address record'
+	SUP apacheDnsAbstractRecord 
+	MUST ( apacheDnsDomainName $ apacheDnsIpAddress ) )
diff --git a/old_trunk/schema-extras/src/main/schema/autofs.schema b/old_trunk/schema-extras/src/main/schema/autofs.schema
new file mode 100644
index 0000000..1dab8f7
--- /dev/null
+++ b/old_trunk/schema-extras/src/main/schema/autofs.schema
@@ -0,0 +1,44 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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. 
+
+# Depends upon core.schema and cosine.schema
+
+# OID Base is 1.3.6.1.4.1.2312.4
+#
+# Attribute types are under 1.3.6.1.4.1.2312.4.1
+# Object classes are under 1.3.6.1.4.1.2312.4.2
+# Syntaxes are under 1.3.6.1.4.1.2312.4.3
+
+# Attribute Type Definitions
+
+attributetype ( 1.3.6.1.4.1.2312.4.1.2 NAME 'automountInformation'
+	DESC 'Information used by the autofs automounter'
+	EQUALITY caseExactMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+
+# malformed: superior top and class type STRUCTURAL were in front of DESC
+objectclass ( 1.3.6.1.4.1.2312.4.2.3 NAME 'automount'
+	DESC 'An entry in an automounter map'
+	SUP top STRUCTURAL
+	MUST ( cn $ automountInformation )
+	MAY ( description ) )
+
+# malformed: superior top and class type STRUCTURAL were in front of DESC
+objectclass ( 1.3.6.1.4.1.2312.4.2.2 NAME 'automountMap'
+	DESC 'An group of related automount objects'
+	SUP top STRUCTURAL
+	MUST ( ou ) )
diff --git a/old_trunk/schema-extras/src/main/schema/collective.schema b/old_trunk/schema-extras/src/main/schema/collective.schema
new file mode 100644
index 0000000..a738715
--- /dev/null
+++ b/old_trunk/schema-extras/src/main/schema/collective.schema
@@ -0,0 +1,189 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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. 
+
+# From RFC 3671 [portions trimmed]:
+# 	Collective Attributes in LDAP
+
+#Abstract
+#
+#  X.500 collective attributes allow common characteristics to be shared
+#  between collections of entries.  This document summarizes the X.500
+#  information model for collective attributes and describes use of
+#  collective attributes in LDAP (Lightweight Directory Access Protocol).
+#  This document provides schema definitions for collective attributes
+#  for use in LDAP.
+
+#3. Collective Attribute Types
+#
+#  A userApplications attribute type can be defined to be COLLECTIVE
+#  [RFC2252].  This indicates that the same attribute values will appear
+#  in the entries of an entry collection subject to the use of the
+#  collectiveExclusions attribute and other administrative controls.
+#
+#  Collective attribute types are commonly defined as subtypes of non-
+#  collective attribute types.  By convention, collective attributes are
+#  named by prefixing the name of their non-collective supertype with
+#  "c-".  For example, the collective telephone attribute is named
+#  c-TelephoneNumber after its non-collective supertype telephoneNumber.
+#
+#  Non-collective attributes types SHALL NOT subtype collective
+#  attributes.
+#
+#  Collective attributes SHALL NOT be SINGLE-VALUED.  Collective
+#  attribute types SHALL NOT appear in the attribute types of an object
+#  class definition.
+#
+#  Operational attributes SHALL NOT be defined to be collective.
+#
+#  The remainder of section provides a summary of collective attributes
+#  derived from those defined in [X.520].  Implementations of this
+#  specification SHOULD support the following collective attributes and
+#  MAY support additional collective attributes.
+#
+#
+#3.1. Collective Locality Name
+#
+#  The c-l attribute type specifies a locality name for a collection of
+#  entries.
+#
+attributetype      ( 2.5.4.7.1 NAME 'c-l'
+	SUP l COLLECTIVE )
+#
+#
+#3.2. Collective State or Province Name
+#
+#  The c-st attribute type specifies a state or province name for a
+#  collection of entries.
+#
+attributetype      ( 2.5.4.8.1 NAME 'c-st'
+	SUP st COLLECTIVE )
+#
+#
+#3.3. Collective Street Address
+#
+#  The c-street attribute type specifies a street address for a
+#  collection of entries.
+#
+attributetype      ( 2.5.4.9.1 NAME 'c-street'
+	SUP street COLLECTIVE )
+#
+#
+#3.4. Collective Organization Name
+#
+#  The c-o attribute type specifies an organization name for a collection
+#  of entries.
+#
+attributetype      ( 2.5.4.10.1 NAME 'c-o'
+	SUP o COLLECTIVE )
+#
+#
+#3.5. Collective Organizational Unit Name
+#
+#  The c-ou attribute type specifies an organizational unit name for a
+#  collection of entries.
+#
+attributetype      ( 2.5.4.11.1 NAME 'c-ou'
+	SUP ou COLLECTIVE )
+#
+#
+#3.6. Collective Postal Address
+#
+#  The c-PostalAddress attribute type specifies a postal address for a
+#  collection of entries.
+#
+attributetype      ( 2.5.4.16.1 NAME 'c-PostalAddress'
+	SUP postalAddress COLLECTIVE )
+#
+#
+#3.7. Collective Postal Code
+#
+#  The c-PostalCode attribute type specifies a postal code for a
+#  collection of entries.
+#
+attributetype      ( 2.5.4.17.1 NAME 'c-PostalCode'
+	SUP postalCode COLLECTIVE )
+#
+#
+#3.8. Collective Post Office Box
+#
+#  The c-PostOfficeBox attribute type specifies a post office box for a
+#  collection of entries.
+#
+attributetype ( 2.5.4.18.1 NAME 'c-PostOfficeBox'
+	SUP postOfficeBox COLLECTIVE )
+#
+#
+#3.9. Collective Physical Delivery Office Name
+#
+#  The c-PhysicalDeliveryOfficeName attribute type specifies a physical
+#  delivery office name for a collection of entries.
+#
+attributetype ( 2.5.4.19.1 NAME 'c-PhysicalDeliveryOfficeName'
+	SUP physicalDeliveryOfficeName COLLECTIVE )
+#
+#
+#3.10. Collective Telephone Number
+#
+#  The c-TelephoneNumber attribute type specifies a telephone number for
+#  a collection of entries.
+#
+attributetype ( 2.5.4.20.1 NAME 'c-TelephoneNumber'
+	SUP telephoneNumber COLLECTIVE )
+#
+#
+#3.11. Collective Telex Number
+#
+#  The c-TelexNumber attribute type specifies a telex number for a
+#  collection of entries.
+#
+attributetype ( 2.5.4.21.1 NAME 'c-TelexNumber'
+	SUP telexNumber COLLECTIVE )
+#
+#
+#3.13. Collective Facsimile Telephone Number
+#
+#  The c-FacsimileTelephoneNumber attribute type specifies a facsimile
+#  telephone number for a collection of entries.
+#
+attributetype ( 2.5.4.23.1 NAME 'c-FacsimileTelephoneNumber'
+	SUP facsimileTelephoneNumber COLLECTIVE )
+#
+#
+#3.14. Collective International ISDN Number
+#
+#  The c-InternationalISDNNumber attribute type specifies an
+#  international ISDN number for a collection of entries.
+#
+attributetype ( 2.5.4.25.1 NAME 'c-InternationalISDNNumber'
+	SUP internationalISDNNumber COLLECTIVE )
+
+# Full Copyright
+#
+# Copyright (C) The Internet Society (2003). All Rights Reserved.
+# 
+# This document and translations of it may be copied and furnished
+# to others, and derivative works that comment on or otherwise explain
+# it or assist in its implmentation may be prepared, copied, published
+# and distributed, in whole or in part, without restriction of any
+# kind, provided that the above copyright notice and this paragraph
+# are included on all such copies and derivative works.  However,
+# this document itself may not be modified in any way, such as by
+# removing the copyright notice or references to the Internet Society
+# or other Internet organizations, except as needed for the  purpose
+# of developing Internet standards in which case the procedures for
+# copyrights defined in the Internet Standards process must be followed,
+# or as required to translate it into languages other than English.
diff --git a/old_trunk/schema-extras/src/main/schema/corba.schema b/old_trunk/schema-extras/src/main/schema/corba.schema
new file mode 100644
index 0000000..3fd2745
--- /dev/null
+++ b/old_trunk/schema-extras/src/main/schema/corba.schema
@@ -0,0 +1,239 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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. 
+
+# Corba Object Schema
+# $OpenLDAP: pkg/ldap/servers/slapd/schema/corba.schema,v 1.2 2002/01/04 20:17:55 kurt Exp $
+# depends upon core.schema
+
+# Network Working Group                                            V. Ryan
+# Request for Comments: 2714                                        R. Lee
+# Category: Informational                                      S. Seligman
+#                                                   Sun Microsystems, Inc.
+#                                                             October 1999
+# 
+# 
+#   Schema for Representing CORBA Object References in an LDAP Directory
+# 
+# Status of this Memo
+# 
+#    This memo provides information for the Internet community.  It does
+#    not specify an Internet standard of any kind.  Distribution of this
+#    memo is unlimited.
+# 
+# Copyright Notice
+# 
+#    Copyright (C) The Internet Society (1999).  All Rights Reserved.
+# 
+# Abstract
+# 
+#    CORBA [CORBA] is the Common Object Request Broker Architecture
+#    defined by the Object Management Group. This document defines the
+#    schema for representing CORBA object references in an LDAP directory
+#    [LDAPv3].
+# 
+# [trimmed]
+
+# 3. Attribute Type Definitions
+# 
+#    The following attribute types are defined in this document:
+# 
+#        corbaIor
+#        corbaRepositoryId
+# 
+# 3.1 corbaIor
+# 
+#    This attribute stores the string representation of the interoperable
+#    object reference (IOR) for a CORBA object. An IOR is an opaque handle
+#    for the object which contains the information necessary to locate the
+#    object, even if the object is in another ORB.
+# 
+#    This attribute's syntax is 'IA5 String' and its case is
+#    insignificant.
+# 
+#    ( 1.3.6.1.4.1.42.2.27.4.1.14
+#     NAME 'corbaIor'
+#     DESC 'Stringified interoperable object reference of a CORBA object'
+#     EQUALITY caseIgnoreIA5Match
+#     SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+#     SINGLE-VALUE
+#    )
+# 
+attributetype ( 1.3.6.1.4.1.42.2.27.4.1.14
+	NAME 'corbaIor'
+	DESC 'Stringified interoperable object reference of a CORBA object'
+	EQUALITY caseIgnoreIA5Match
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+	SINGLE-VALUE )
+
+# 3.2 corbaRepositoryId
+# 
+#    Each CORBA interface has a unique "repository id" (also called "type
+#    id") that identifies the interface.  A CORBA object has one or more
+#    repository ids, one for each interface that it implements.
+# 
+#    The format of a repository id can be any string, but the OMG
+#    specifies four standard formats:
+# 
+#       a. IDL-style
+# 
+#        IDL:Prefix/ModuleName/InterfaceName:VersionNumber
+# 
+#    For example, the repository id for the "NamingContext" in OMG's COS
+#    Naming module is:  "IDL:omg.org/CosNaming/NamingContext:1.0".
+# 
+#       b. RMI-style
+# 
+#        RMI:ClassName:HashCode[:SUID]
+# 
+#    This format is used by RMI-IIOP remote objects [RMI-IIOP].
+#    "ClassName" is the fully qualified name of the class (for example,
+#    "java.lang.String"). "HashCode" is the object's hash code (that is,
+#    that obtained by invoking the "hashCode()" method).  "SUID" is the
+#    "stream unique identifier", which is a 64-bit number that uniquely
+#    identifies the serialization version of the class; SUID is optional
+#    in the repository id.
+# 
+#       c. DCE-style
+# 
+#        DCE:UUID
+# 
+#    This format is used for DCE/CORBA interoperability [CORBA-DCE].
+#    "UUID" represents a DCE UUID.
+# 
+#       d. "local"
+# 
+#    This format is defined by the local Object Request Broker (ORB).
+# 
+#    The corbaRepositoryId attribute is a multivalued attribute; each
+#    value records a single repository id of an interface implemented by
+#    the CORBA object.  This attribute need not contain a complete list of
+#    the interfaces implemented by the CORBA object.
+# 
+#    This attribute's syntax is 'Directory String' and its case is
+#    significant.  The values of this attribute are encoded using UTF-8.
+#    Some values may require translation from their native representation
+#    in order to be correctly encoded using UTF-8.
+# 
+#    ( 1.3.6.1.4.1.42.2.27.4.1.15
+#     NAME 'corbaRepositoryId'
+#     DESC 'Repository ids of interfaces implemented by a CORBA object'
+#     EQUALITY caseExactMatch
+#     SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+#    )
+# 
+# 
+attributetype ( 1.3.6.1.4.1.42.2.27.4.1.15
+	NAME 'corbaRepositoryId'
+	DESC 'Repository ids of interfaces implemented by a CORBA object'
+	EQUALITY caseExactMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+
+# 4. Object Class Definitions
+# 
+#    The following object classes are defined in this document:
+# 
+#        corbaContainer
+#        corbaObject
+#        corbaObjectReference
+# 
+# 4.1 corbaContainer
+# 
+#    This structural object class represents a container for a CORBA
+#    object.
+# 
+#    ( 1.3.6.1.4.1.42.2.27.4.2.10
+#     NAME 'corbaContainer'
+#     DESC 'Container for a CORBA object'
+#     SUP top
+#     STRUCTURAL
+#     MUST ( cn )
+#    )
+# 
+objectclass ( 1.3.6.1.4.1.42.2.27.4.2.10
+	NAME 'corbaContainer'
+	DESC 'Container for a CORBA object'
+	SUP top
+	STRUCTURAL
+	MUST cn )
+
+# 4.2 corbaObject
+# 
+#    This abstract object class is the root class for representing a CORBA
+#    object.
+# 
+#    ( 1.3.6.1.4.1.42.2.27.4.2.9
+#     NAME 'corbaObject'
+#     DESC 'CORBA object representation'
+#     SUP top
+#     ABSTRACT
+#     MAY ( corbaRepositoryId $ description )
+#    )
+# 
+objectclass ( 1.3.6.1.4.1.42.2.27.4.2.9
+	NAME 'corbaObject'
+	DESC 'CORBA object representation'
+	SUP top
+	ABSTRACT
+	MAY ( corbaRepositoryId $ description ) )
+
+# 4.3 corbaObjectReference
+# 
+#    This auxiliary object class represents a CORBA object reference.  It
+#    must be mixed in with a structural object class.
+# 
+#    ( 1.3.6.1.4.1.42.2.27.4.2.11
+#     NAME 'corbaObjectReference'
+#     DESC 'CORBA interoperable object reference'
+#     SUP corbaObject
+#     AUXILIARY
+#     MUST ( corbaIor )
+#    )
+# 
+objectclass ( 1.3.6.1.4.1.42.2.27.4.2.11
+	NAME 'corbaObjectReference'
+	DESC 'CORBA interoperable object reference'
+	SUP corbaObject
+	AUXILIARY
+	MUST corbaIor )
+ 
+# 10.  Full Copyright Statement
+# 
+#    Copyright (C) The Internet Society (1999).  All Rights Reserved.
+# 
+#    This document and translations of it may be copied and furnished to
+#    others, and derivative works that comment on or otherwise explain it
+#    or assist in its implementation may be prepared, copied, published
+#    and distributed, in whole or in part, without restriction of any
+#    kind, provided that the above copyright notice and this paragraph are
+#    included on all such copies and derivative works.  However, this
+#    document itself may not be modified in any way, such as by removing
+#    the copyright notice or references to the Internet Society or other
+#    Internet organizations, except as needed for the purpose of
+#    developing Internet standards in which case the procedures for
+#    copyrights defined in the Internet Standards process must be
+#    followed, or as required to translate it into languages other than
+#    English.
+# 
+#    The limited permissions granted above are perpetual and will not be
+#    revoked by the Internet Society or its successors or assigns.
+# 
+#    This document and the information contained herein is provided on an
+#    "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+#    TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+#    BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+#    HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+#    MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
diff --git a/old_trunk/schema-extras/src/main/schema/cosine.schema b/old_trunk/schema-extras/src/main/schema/cosine.schema
new file mode 100644
index 0000000..99704b7
--- /dev/null
+++ b/old_trunk/schema-extras/src/main/schema/cosine.schema
@@ -0,0 +1,2579 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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. 
+
+# $OpenLDAP: pkg/ldap/servers/slapd/schema/cosine.schema,v 1.15 2002/01/11 19:28:06 kurt Exp $
+#
+# RFC1274: Cosine and Internet X.500 schema
+#
+# This file contains LDAPv3 schema derived from X.500 COSINE "pilot"
+# schema.  As this schema was defined for X.500(89), some
+# oddities were introduced in the mapping to LDAPv3.  The
+# mappings were based upon: draft-ietf-asid-ldapv3-attributes-03.txt
+# (a work in progress)
+#
+# Note: It seems that the pilot schema evolved beyond what was
+# described in RFC1274.  However, this document attempts to describes
+# RFC1274 as published.
+#
+# Depends on core.schema
+
+
+# Network Working Group                                          P. Barker
+# Request for Comments: 1274                                      S. Kille
+#                                              University College London
+#                                                          November 1991
+#
+#                 The COSINE and Internet X.500 Schema
+#
+# [trimmed]
+#
+# Abstract
+#
+#  This document suggests an X.500 Directory Schema, or Naming
+#  Architecture, for use in the COSINE and Internet X.500 pilots.  The
+#  schema is independent of any specific implementation.  As well as
+#  indicating support for the standard object classes and attributes, a
+#  large number of generally useful object classes and attributes are
+#  also defined.  An appendix to this document includes a machine
+#  processable version of the schema.
+#
+# [trimmed]
+
+# 7.  Object Identifiers
+#
+#  Some additional object identifiers are defined for this schema.
+#  These are also reproduced in Appendix C.
+#
+#    data OBJECT IDENTIFIER ::= {ccitt 9}
+#    pss OBJECT IDENTIFIER ::= {data 2342}
+#    ucl OBJECT IDENTIFIER ::= {pss 19200300}
+#    pilot OBJECT IDENTIFIER ::= {ucl 100}
+#
+#    pilotAttributeType OBJECT IDENTIFIER ::= {pilot 1}
+#    pilotAttributeSyntax OBJECT IDENTIFIER ::= {pilot 3}
+#    pilotObjectClass OBJECT IDENTIFIER ::= {pilot 4}
+#    pilotGroups OBJECT IDENTIFIER ::= {pilot 10}
+#
+#    iA5StringSyntax OBJECT IDENTIFIER ::= {pilotAttributeSyntax 4}
+#    caseIgnoreIA5StringSyntax OBJECT IDENTIFIER ::=
+#                                          {pilotAttributeSyntax 5}
+#
+# 8.  Object Classes
+# [relocated after 9]
+
+#
+# 9.  Attribute Types
+#
+# 9.1.  X.500 standard attribute types
+#
+#  A number of generally useful attribute types are defined in X.520,
+#  and these are supported.  Refer to that document for descriptions of
+#  the suggested usage of these attribute types.  The ASN.1 for these
+#  attribute types is reproduced for completeness in Appendix C.
+#
+# 9.2.  X.400 standard attribute types
+#
+#  The standard X.400 attribute types are supported.  See X.402 for full
+#  details.  The ASN.1 for these attribute types is reproduced in
+#  Appendix C.
+#
+# 9.3.  COSINE/Internet attribute types
+#
+#  This section describes all the attribute types defined for use in the
+#  COSINE and Internet pilots.  Descriptions are given as to the
+#  suggested usage of these attribute types.  The ASN.1 for these
+#  attribute types is reproduced in Appendix C.
+#
+# 9.3.1.  Userid
+#
+#  The Userid attribute type specifies a computer system login name.
+#
+#    userid ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            caseIgnoreStringSyntax
+#            (SIZE (1 .. ub-user-identifier))
+#    ::= {pilotAttributeType 1}
+#
+#(in core.schema)
+##attributetype ( 0.9.2342.19200300.100.1.1 NAME ( 'uid' 'userid' )
+##	EQUALITY caseIgnoreMatch
+##	SUBSTR caseIgnoreSubstringsMatch
+##	SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
+
+# 9.3.2.  Text Encoded O/R Address
+#
+#  The Text Encoded O/R Address attribute type specifies a text encoding
+#  of an X.400 O/R address, as specified in RFC 987.  The use of this
+#  attribute is deprecated as the attribute is intended for interim use
+#  only.  This attribute will be the first candidate for the attribute
+#  expiry mechanisms!
+#
+#    textEncodedORAddress ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            caseIgnoreStringSyntax
+#        (SIZE (1 .. ub-text-encoded-or-address))
+#    ::= {pilotAttributeType 2}
+#
+attributetype ( 0.9.2342.19200300.100.1.2 NAME 'textEncodedORAddress'
+	EQUALITY caseIgnoreMatch
+	SUBSTR caseIgnoreSubstringsMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
+
+# 9.3.3.  RFC 822 Mailbox
+#
+#  The RFC822 Mailbox attribute type specifies an electronic mailbox
+#  attribute following the syntax specified in RFC 822.  Note that this
+#  attribute should not be used for greybook or other non-Internet order
+#  mailboxes.
+#
+#    rfc822Mailbox ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            caseIgnoreIA5StringSyntax
+#            (SIZE (1 .. ub-rfc822-mailbox))
+#    ::= {pilotAttributeType 3}
+#
+#(in core.schema)
+##attributetype ( 0.9.2342.19200300.100.1.3 NAME ( 'mail' 'rfc822Mailbox' )
+##	EQUALITY caseIgnoreIA5Match
+##	SUBSTR caseIgnoreIA5SubstringsMatch
+##	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} )
+
+# 9.3.4.  Information
+#
+#  The Information attribute type specifies any general information
+#  pertinent to an object.  It is recommended that specific usage of
+#  this attribute type is avoided, and that specific requirements are
+#  met by other (possibly additional) attribute types.
+#
+#    info ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            caseIgnoreStringSyntax
+#            (SIZE (1 .. ub-information))
+#    ::= {pilotAttributeType 4}
+#
+attributetype ( 0.9.2342.19200300.100.1.4 NAME 'info'
+	DESC 'RFC1274: general information'
+	EQUALITY caseIgnoreMatch
+	SUBSTR caseIgnoreSubstringsMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{2048} )
+
+
+# 9.3.5.  Favourite Drink
+#
+#  The Favourite Drink attribute type specifies the favourite drink of
+#  an object (or person).
+#
+#    favouriteDrink ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            caseIgnoreStringSyntax
+#            (SIZE (1 .. ub-favourite-drink))
+#    ::= {pilotAttributeType 5}
+#
+attributetype ( 0.9.2342.19200300.100.1.5
+	NAME ( 'drink' 'favouriteDrink' )
+	DESC 'RFC1274: favorite drink'
+	EQUALITY caseIgnoreMatch
+	SUBSTR caseIgnoreSubstringsMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
+
+# 9.3.6.  Room Number
+#
+#  The Room Number attribute type specifies the room number of an
+#  object.  Note that the commonName attribute should be used for naming
+#  room objects.
+#
+#    roomNumber ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            caseIgnoreStringSyntax
+#            (SIZE (1 .. ub-room-number))
+#    ::= {pilotAttributeType 6}
+#
+attributetype ( 0.9.2342.19200300.100.1.6 NAME 'roomNumber'
+	DESC 'RFC1274: room number'
+	EQUALITY caseIgnoreMatch
+	SUBSTR caseIgnoreSubstringsMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
+
+# 9.3.7.  Photo
+#
+#  The Photo attribute type specifies a "photograph" for an object.
+#  This should be encoded in G3 fax as explained in recommendation T.4,
+#  with an ASN.1 wrapper to make it compatible with an X.400 BodyPart as
+#  defined in X.420.
+#
+#    IMPORT  G3FacsimileBodyPart  FROM  {   mhs-motis   ipms   modules
+#    information-objects }
+#
+#    photo ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            CHOICE {
+#                g3-facsimile [3] G3FacsimileBodyPart
+#                }
+#        (SIZE (1 .. ub-photo))
+#    ::= {pilotAttributeType 7}
+#
+attributetype ( 0.9.2342.19200300.100.1.7 NAME 'photo'
+	DESC 'RFC1274: photo (G3 fax)'
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.23{25000} )
+
+# 9.3.8.  User Class
+#
+#  The User Class attribute type specifies a category of computer user.
+#  The semantics placed on this attribute are for local interpretation.
+#  Examples of current usage od this attribute in academia are
+#  undergraduate student, researcher, lecturer, etc.  Note that the
+#  organizationalStatus attribute may now often be preferred as it makes
+#  no distinction between computer users and others.
+#
+#    userClass ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            caseIgnoreStringSyntax
+#            (SIZE (1 .. ub-user-class))
+#    ::= {pilotAttributeType 8}
+#
+attributetype ( 0.9.2342.19200300.100.1.8 NAME 'userClass'
+	DESC 'RFC1274: categorory of user'
+	EQUALITY caseIgnoreMatch
+	SUBSTR caseIgnoreSubstringsMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
+
+# 9.3.9.  Host
+#
+#  The Host attribute type specifies a host computer.
+#
+#    host ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            caseIgnoreStringSyntax
+#            (SIZE (1 .. ub-host))
+#    ::= {pilotAttributeType 9}
+#
+attributetype ( 0.9.2342.19200300.100.1.9 NAME 'host'
+	DESC 'RFC1274: host computer'
+	EQUALITY caseIgnoreMatch
+	SUBSTR caseIgnoreSubstringsMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
+
+# 9.3.10.  Manager
+#
+#  The Manager attribute type specifies the manager of an object
+#  represented by an entry.
+#
+#    manager ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            distinguishedNameSyntax
+#    ::= {pilotAttributeType 10}
+#
+attributetype ( 0.9.2342.19200300.100.1.10 NAME 'manager'
+	DESC 'RFC1274: DN of manager'
+	EQUALITY distinguishedNameMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
+
+# 9.3.11.  Document Identifier
+#
+#  The Document Identifier attribute type specifies a unique identifier
+#  for a document.
+#
+#    documentIdentifier ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            caseIgnoreStringSyntax
+#            (SIZE (1 .. ub-document-identifier))
+#    ::= {pilotAttributeType 11}
+#
+attributetype ( 0.9.2342.19200300.100.1.11 NAME 'documentIdentifier'
+	DESC 'RFC1274: unique identifier of document'
+	EQUALITY caseIgnoreMatch
+	SUBSTR caseIgnoreSubstringsMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
+
+# 9.3.12.  Document Title
+#
+#  The Document Title attribute type specifies the title of a document.
+#
+#    documentTitle ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            caseIgnoreStringSyntax
+#        (SIZE (1 .. ub-document-title))
+#    ::= {pilotAttributeType 12}
+#
+attributetype ( 0.9.2342.19200300.100.1.12 NAME 'documentTitle'
+	DESC 'RFC1274: title of document'
+	EQUALITY caseIgnoreMatch
+	SUBSTR caseIgnoreSubstringsMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
+
+# 9.3.13.  Document Version
+#
+#  The Document Version attribute type specifies the version number of a
+#  document.
+#
+#    documentVersion ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            caseIgnoreStringSyntax
+#            (SIZE (1 .. ub-document-version))
+#    ::= {pilotAttributeType 13}
+#
+attributetype ( 0.9.2342.19200300.100.1.13 NAME 'documentVersion'
+	DESC 'RFC1274: version of document'
+	EQUALITY caseIgnoreMatch
+	SUBSTR caseIgnoreSubstringsMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
+
+# 9.3.14.  Document Author
+#
+#  The Document Author attribute type specifies the distinguished name
+#  of the author of a document.
+#
+#    documentAuthor ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            distinguishedNameSyntax
+#    ::= {pilotAttributeType 14}
+#
+attributetype ( 0.9.2342.19200300.100.1.14 NAME 'documentAuthor'
+	DESC 'RFC1274: DN of author of document'
+	EQUALITY distinguishedNameMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
+
+# 9.3.15.  Document Location
+#
+#  The Document Location attribute type specifies the location of the
+#  document original.
+#
+#    documentLocation ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            caseIgnoreStringSyntax
+#            (SIZE (1 .. ub-document-location))
+#    ::= {pilotAttributeType 15}
+#
+attributetype ( 0.9.2342.19200300.100.1.15 NAME 'documentLocation'
+	DESC 'RFC1274: location of document original'
+	EQUALITY caseIgnoreMatch
+	SUBSTR caseIgnoreSubstringsMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
+
+# 9.3.16.  Home Telephone Number
+#
+#  The Home Telephone Number attribute type specifies a home telephone
+#  number associated with a person.  Attribute values should follow the
+#  agreed format for international telephone numbers: i.e., "+44 71 123
+#  4567".
+#
+#    homeTelephoneNumber ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            telephoneNumberSyntax
+#    ::= {pilotAttributeType 20}
+#
+
+# This was malformed NAME was after DESC
+attributetype ( 0.9.2342.19200300.100.1.20
+	NAME ( 'homePhone' 'homeTelephoneNumber' )
+	DESC 'RFC1274: home telephone number'
+	EQUALITY telephoneNumberMatch
+	SUBSTR telephoneNumberSubstringsMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.50 )
+
+# 9.3.17.  Secretary
+#
+#  The Secretary attribute type specifies the secretary of a person.
+#  The attribute value for Secretary is a distinguished name.
+#
+#    secretary ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            distinguishedNameSyntax
+#    ::= {pilotAttributeType 21}
+#
+attributetype ( 0.9.2342.19200300.100.1.21 NAME 'secretary'
+	DESC 'RFC1274: DN of secretary'
+	EQUALITY distinguishedNameMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
+
+# 9.3.18.  Other Mailbox
+#
+#  The Other Mailbox attribute type specifies values for electronic
+#  mailbox types other than X.400 and rfc822.
+#
+#    otherMailbox ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            SEQUENCE {
+#                    mailboxType PrintableString, -- e.g. Telemail
+#                    mailbox IA5String  -- e.g. X378:Joe
+#            }
+#    ::= {pilotAttributeType 22}
+#
+attributetype ( 0.9.2342.19200300.100.1.22 NAME 'otherMailbox'
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.39 )
+
+# 9.3.19.  Last Modified Time
+#
+#  The Last Modified Time attribute type specifies the last time, in UTC
+#  time, that an entry was modified.  Ideally, this attribute should be
+#  maintained by the DSA.
+#
+#    lastModifiedTime ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            uTCTimeSyntax
+#    ::= {pilotAttributeType 23}
+#
+## OBSOLETE
+#attributetype ( 0.9.2342.19200300.100.1.23 NAME 'lastModifiedTime'
+#	DESC 'RFC1274: time of last modify, replaced by modifyTimestamp'
+#	OBSOLETE
+#	SYNTAX 1.3.6.1.4.1.1466.115.121.1.53
+#	USAGE directoryOperation )
+
+# 9.3.20.  Last Modified By
+#
+#  The Last Modified By attribute specifies the distinguished name of
+#  the last user to modify the associated entry.  Ideally, this
+#  attribute should be maintained by the DSA.
+#
+#    lastModifiedBy ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            distinguishedNameSyntax
+#    ::= {pilotAttributeType 24}
+#
+## OBSOLETE
+#attributetype ( 0.9.2342.19200300.100.1.24 NAME 'lastModifiedBy'
+#	DESC 'RFC1274: last modifier, replaced by modifiersName'
+#	OBSOLETE
+#	EQUALITY distinguishedNameMatch
+#	SYNTAX 1.3.6.1.4.1.1466.115.121.1.12
+#	USAGE directoryOperation )
+
+# 9.3.21.  Domain Component
+#
+#  The Domain Component attribute type specifies a DNS/NRS domain.  For
+#  example, "uk" or "ac".
+#
+#    domainComponent ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            caseIgnoreIA5StringSyntax
+#            SINGLE VALUE
+#    ::= {pilotAttributeType 25}
+#
+##(in core.schema)
+##attributetype ( 0.9.2342.19200300.100.1.25 NAME ( 'dc' 'domainComponent' )
+##	EQUALITY caseIgnoreIA5Match
+##	SUBSTR caseIgnoreIA5SubstringsMatch
+##	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+
+# 9.3.22.  DNS ARecord
+#
+#  The A Record attribute type specifies a type A (Address) DNS resource
+#  record [6] [7].
+#
+#    aRecord ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            DNSRecordSyntax
+#    ::= {pilotAttributeType 26}
+#
+## incorrect syntax?
+attributetype ( 0.9.2342.19200300.100.1.26 NAME 'aRecord'
+	EQUALITY caseIgnoreIA5Match
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+
+## missing from RFC1274
+## incorrect syntax?
+attributetype ( 0.9.2342.19200300.100.1.27 NAME 'mDRecord'
+	EQUALITY caseIgnoreIA5Match
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+
+# 9.3.23.  MX Record
+#
+#  The MX Record attribute type specifies a type MX (Mail Exchange) DNS
+#  resource record [6] [7].
+#
+#    mXRecord ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            DNSRecordSyntax
+#    ::= {pilotAttributeType 28}
+#
+## incorrect syntax!!
+attributetype ( 0.9.2342.19200300.100.1.28 NAME 'mXRecord'
+	EQUALITY caseIgnoreIA5Match
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+
+# 9.3.24.  NS Record
+#
+#  The NS Record attribute type specifies an NS (Name Server) DNS
+#  resource record [6] [7].
+#
+#    nSRecord ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            DNSRecordSyntax
+#    ::= {pilotAttributeType 29}
+#
+## incorrect syntax!!
+attributetype ( 0.9.2342.19200300.100.1.29 NAME 'nSRecord'
+	EQUALITY caseIgnoreIA5Match
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+
+# 9.3.25.  SOA Record
+#
+#  The SOA Record attribute type specifies a type SOA (Start of
+#  Authority) DNS resorce record [6] [7].
+#
+#    sOARecord ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            DNSRecordSyntax
+#    ::= {pilotAttributeType 30}
+#
+## incorrect syntax!!
+attributetype ( 0.9.2342.19200300.100.1.30 NAME 'sOARecord'
+	EQUALITY caseIgnoreIA5Match
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+
+# 9.3.26.  CNAME Record
+#
+#  The CNAME Record attribute type specifies a type CNAME (Canonical
+#  Name) DNS resource record [6] [7].
+#
+#    cNAMERecord ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            iA5StringSyntax
+#    ::= {pilotAttributeType 31}
+#
+## incorrect syntax!!
+attributetype ( 0.9.2342.19200300.100.1.31 NAME 'cNAMERecord'
+	EQUALITY caseIgnoreIA5Match
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+
+# 9.3.27.  Associated Domain
+#
+#  The Associated Domain attribute type specifies a DNS or NRS domain
+#  which is associated with an object in the DIT. For example, the entry
+#  in the DIT with a distinguished name "C=GB, O=University College
+#  London" would have an associated domain of "UCL.AC.UK.  Note that all
+#  domains should be represented in rfc822 order.  See [3] for more
+#  details of usage of this attribute.
+#
+#    associatedDomain ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            caseIgnoreIA5StringSyntax
+#    ::= {pilotAttributeType 37}
+#
+#attributetype ( 0.9.2342.19200300.100.1.37 NAME 'associatedDomain'
+#	EQUALITY caseIgnoreIA5Match
+#	SUBSTR caseIgnoreIA5SubstringsMatch
+#	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+
+# 9.3.28.  Associated Name
+#
+#  The Associated Name attribute type specifies an entry in the
+#  organisational DIT associated with a DNS/NRS domain.  See [3] for
+#  more details of usage of this attribute.
+#
+#    associatedName ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            distinguishedNameSyntax
+#    ::= {pilotAttributeType 38}
+#
+attributetype ( 0.9.2342.19200300.100.1.38 NAME 'associatedName'
+	DESC 'RFC1274: DN of entry associated with domain'
+	EQUALITY distinguishedNameMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
+
+# 9.3.29.  Home postal address
+#
+#  The Home postal address attribute type specifies a home postal
+#  address for an object.  This should be limited to up to 6 lines of 30
+#  characters each.
+#
+#    homePostalAddress ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            postalAddress
+#            MATCHES FOR EQUALITY
+#    ::= {pilotAttributeType 39}
+#
+attributetype ( 0.9.2342.19200300.100.1.39 NAME 'homePostalAddress'
+	DESC 'RFC1274: home postal address'
+	EQUALITY caseIgnoreListMatch
+	SUBSTR caseIgnoreListSubstringsMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.41 )
+
+# 9.3.30.  Personal Title
+#
+#  The Personal Title attribute type specifies a personal title for a
+#  person. Examples of personal titles are "Ms", "Dr", "Prof" and "Rev".
+#
+#    personalTitle ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            caseIgnoreStringSyntax
+#            (SIZE (1 .. ub-personal-title))
+#    ::= {pilotAttributeType 40}
+#
+attributetype ( 0.9.2342.19200300.100.1.40 NAME 'personalTitle'
+	DESC 'RFC1274: personal title'
+	EQUALITY caseIgnoreMatch
+	SUBSTR caseIgnoreSubstringsMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
+
+# 9.3.31.  Mobile Telephone Number
+#
+#  The Mobile Telephone Number attribute type specifies a mobile
+#  telephone number associated with a person.  Attribute values should
+#  follow the agreed format for international telephone numbers: i.e.,
+#  "+44 71 123 4567".
+#
+#    mobileTelephoneNumber ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            telephoneNumberSyntax
+#    ::= {pilotAttributeType 41}
+#
+attributetype ( 0.9.2342.19200300.100.1.41
+	NAME ( 'mobile' 'mobileTelephoneNumber' )
+	DESC 'RFC1274: mobile telephone number'
+	EQUALITY telephoneNumberMatch
+	SUBSTR telephoneNumberSubstringsMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.50 )
+
+# 9.3.32.  Pager Telephone Number
+#
+#  The Pager Telephone Number attribute type specifies a pager telephone
+#  number for an object. Attribute values should follow the agreed
+#  format for international telephone numbers: i.e., "+44 71 123 4567".
+#
+#    pagerTelephoneNumber ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            telephoneNumberSyntax
+#    ::= {pilotAttributeType 42}
+#
+attributetype ( 0.9.2342.19200300.100.1.42
+	NAME ( 'pager' 'pagerTelephoneNumber' )
+	DESC 'RFC1274: pager telephone number'
+	EQUALITY telephoneNumberMatch
+	SUBSTR telephoneNumberSubstringsMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.50 )
+
+# 9.3.33.  Friendly Country Name
+#
+#  The Friendly Country Name attribute type specifies names of countries
+#  in human readable format.  The standard attribute country name must
+#  be one of the two-letter codes defined in ISO 3166.
+#
+#    friendlyCountryName ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            caseIgnoreStringSyntax
+#    ::= {pilotAttributeType 43}
+#
+attributetype ( 0.9.2342.19200300.100.1.43
+	NAME ( 'co' 'friendlyCountryName' )
+	DESC 'RFC1274: friendly country name'
+	EQUALITY caseIgnoreMatch
+	SUBSTR caseIgnoreSubstringsMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+
+# 9.3.34.  Unique Identifier
+#
+#  The Unique Identifier attribute type specifies a "unique identifier"
+#  for an object represented in the Directory.  The domain within which
+#  the identifier is unique, and the exact semantics of the identifier,
+#  are for local definition.  For a person, this might be an
+#  institution-wide payroll number.  For an organisational unit, it
+#  might be a department code.
+#
+#    uniqueIdentifier ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            caseIgnoreStringSyntax
+#            (SIZE (1 .. ub-unique-identifier))
+#    ::= {pilotAttributeType 44}
+#
+attributetype ( 0.9.2342.19200300.100.1.44 NAME 'uniqueIdentifier'
+	DESC 'RFC1274: unique identifer'
+	EQUALITY caseIgnoreMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
+
+# 9.3.35.  Organisational Status
+#
+#  The Organisational Status attribute type specifies a category by
+#  which a person is often referred to in an organisation.  Examples of
+#  usage in academia might include undergraduate student, researcher,
+#  lecturer, etc.
+#
+#  A Directory administrator should probably consider carefully the
+#  distinctions between this and the title and userClass attributes.
+#
+#    organizationalStatus ATTRIBUTE
+#            WITH ATTRIBUTE-SYNTAX
+#            caseIgnoreStringSyntax
+#            (SIZE (1 .. ub-organizational-status))
+#    ::= {pilotAttributeType 45}
+#
+attributetype ( 0.9.2342.19200300.100.1.45 NAME 'organizationalStatus'
+	DESC 'RFC1274: organizational status'
+	EQUALITY caseIgnoreMatch
+	SUBSTR caseIgnoreSubstringsMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
+
+# 9.3.36.  Janet Mailbox
+#
+#  The Janet Mailbox attribute type specifies an electronic mailbox
+#  attribute following the syntax specified in the Grey Book of the
+#  Coloured Book series.  This attribute is intended for the convenience
+#  of U.K users unfamiliar with rfc822 and little-endian mail addresses.
+#  Entries using this attribute MUST also include an rfc822Mailbox
+#  attribute.
+#
+#    janetMailbox ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            caseIgnoreIA5StringSyntax
+#            (SIZE (1 .. ub-janet-mailbox))
+#    ::= {pilotAttributeType 46}
+#
+attributetype ( 0.9.2342.19200300.100.1.46 NAME 'janetMailbox'
+	DESC 'RFC1274: Janet mailbox'
+	EQUALITY caseIgnoreIA5Match
+	SUBSTR caseIgnoreIA5SubstringsMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} )
+
+# 9.3.37.  Mail Preference Option
+#
+#  An attribute to allow users to indicate a preference for inclusion of
+#  their names on mailing lists (electronic or physical).  The absence
+#  of such an attribute should be interpreted as if the attribute was
+#  present with value "no-list-inclusion".  This attribute should be
+#  interpreted by anyone using the directory to derive mailing lists,
+#  and its value respected.
+#
+#    mailPreferenceOption ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX ENUMERATED {
+#                no-list-inclusion(0),
+#                any-list-inclusion(1),  -- may be added to any lists
+#                professional-list-inclusion(2)
+#                                        -- may be added to lists
+#                                        -- which the list provider
+#                                        -- views as related to the
+#                                        -- users professional inter-
+#                                        -- ests, perhaps evaluated
+#                                        -- from the business of the
+#                                        -- organisation or keywords
+#                                        -- in the entry.
+#                }
+#    ::= {pilotAttributeType 47}
+#
+attributetype ( 0.9.2342.19200300.100.1.47
+	NAME 'mailPreferenceOption'
+	DESC 'RFC1274: mail preference option'
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )
+
+# 9.3.38.  Building Name
+#
+#  The Building Name attribute type specifies the name of the building
+#  where an organisation or organisational unit is based.
+#
+#    buildingName ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            caseIgnoreStringSyntax
+#            (SIZE (1 .. ub-building-name))
+#    ::= {pilotAttributeType 48}
+#
+attributetype ( 0.9.2342.19200300.100.1.48 NAME 'buildingName'
+	DESC 'RFC1274: name of building'
+	EQUALITY caseIgnoreMatch
+	SUBSTR caseIgnoreSubstringsMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
+
+# 9.3.39.  DSA Quality
+#
+#  The DSA Quality attribute type specifies the purported quality of a
+#  DSA.  It allows a DSA manager to indicate the expected level of
+#  availability of the DSA. See [8] for details of the syntax.
+#
+#    dSAQuality ATTRIBUTE
+#            WITH ATTRIBUTE-SYNTAX DSAQualitySyntax
+#            SINGLE VALUE
+#    ::= {pilotAttributeType 49}
+#
+attributetype ( 0.9.2342.19200300.100.1.49 NAME 'dSAQuality'
+	DESC 'RFC1274: DSA Quality'
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.19 SINGLE-VALUE )
+
+# 9.3.40.  Single Level Quality
+#
+#  The Single Level Quality attribute type specifies the purported data
+#  quality at the level immediately below in the DIT.  See [8] for
+#  details of the syntax.
+#
+#    singleLevelQuality ATTRIBUTE
+#            WITH ATTRIBUTE-SYNTAX DataQualitySyntax
+#            SINGLE VALUE
+#    ::= {pilotAttributeType 50}
+#
+attributetype ( 0.9.2342.19200300.100.1.50 NAME 'singleLevelQuality'
+	DESC 'RFC1274: Single Level Quality'
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.13 SINGLE-VALUE )
+
+# 9.3.41.  Subtree Minimum Quality
+#
+#  The Subtree Minimum Quality attribute type specifies the purported
+#  minimum data quality for a DIT subtree.  See [8] for more discussion
+#  and details of the syntax.
+#
+#    subtreeMinimumQuality ATTRIBUTE
+#            WITH ATTRIBUTE-SYNTAX DataQualitySyntax
+#            SINGLE VALUE
+#               -- Defaults to singleLevelQuality
+#    ::= {pilotAttributeType 51}
+#
+attributetype ( 0.9.2342.19200300.100.1.51 NAME 'subtreeMinimumQuality'
+	DESC 'RFC1274: Subtree Mininum Quality'
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.13 SINGLE-VALUE )
+
+# 9.3.42.  Subtree Maximum Quality
+#
+#  The Subtree Maximum Quality attribute type specifies the purported
+#  maximum data quality for a DIT subtree.  See [8] for more discussion
+#  and details of the syntax.
+#
+#    subtreeMaximumQuality ATTRIBUTE
+#            WITH ATTRIBUTE-SYNTAX DataQualitySyntax
+#            SINGLE VALUE
+#               -- Defaults to singleLevelQuality
+#    ::= {pilotAttributeType 52}
+#
+attributetype ( 0.9.2342.19200300.100.1.52 NAME 'subtreeMaximumQuality'
+	DESC 'RFC1274: Subtree Maximun Quality'
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.13 SINGLE-VALUE )
+
+# 9.3.43.  Personal Signature
+#
+#  The Personal Signature attribute type allows for a representation of
+#  a person's signature.  This should be encoded in G3 fax as explained
+#  in recommendation T.4, with an ASN.1 wrapper to make it compatible
+#  with an X.400 BodyPart as defined in X.420.
+#
+#    IMPORT  G3FacsimileBodyPart  FROM  {   mhs-motis   ipms   modules
+#    information-objects }
+#
+#    personalSignature ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            CHOICE {
+#                g3-facsimile [3] G3FacsimileBodyPart
+#                }
+#        (SIZE (1 .. ub-personal-signature))
+#    ::= {pilotAttributeType 53}
+#
+attributetype ( 0.9.2342.19200300.100.1.53 NAME 'personalSignature'
+	DESC 'RFC1274: Personal Signature (G3 fax)'
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.23 )
+
+# 9.3.44.  DIT Redirect
+#
+#  The DIT Redirect attribute type is used to indicate that the object
+#  described by one entry now has a newer entry in the DIT.  The entry
+#  containing the redirection attribute should be expired after a
+#  suitable grace period.  This attribute may be used when an individual
+#  changes his/her place of work, and thus acquires a new organisational
+#  DN.
+#
+#    dITRedirect ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            distinguishedNameSyntax
+#    ::= {pilotAttributeType 54}
+#
+attributetype ( 0.9.2342.19200300.100.1.54 NAME 'dITRedirect'
+	DESC 'RFC1274: DIT Redirect'
+	EQUALITY distinguishedNameMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
+
+# 9.3.45.  Audio
+#
+#  The Audio attribute type allows the storing of sounds in the
+#  Directory.  The attribute uses a u-law encoded sound file as used by
+#  the "play" utility on a Sun 4.  This is an interim format.
+#
+#    audio ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            Audio
+#        (SIZE (1 .. ub-audio))
+#    ::= {pilotAttributeType 55}
+#
+attributetype ( 0.9.2342.19200300.100.1.55 NAME 'audio'
+	DESC 'RFC1274: audio (u-law)'
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.4{25000} )
+
+# 9.3.46.  Publisher of Document
+#
+#
+#  The Publisher of Document attribute is the person and/or organization
+#  that published a document.
+#
+#    documentPublisher ATTRIBUTE
+#            WITH ATTRIBUTE SYNTAX caseIgnoreStringSyntax
+#    ::= {pilotAttributeType 56}
+#
+attributetype ( 0.9.2342.19200300.100.1.56 NAME 'documentPublisher'
+	DESC 'RFC1274: publisher of document'
+	EQUALITY caseIgnoreMatch
+	SUBSTR caseIgnoreSubstringsMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+
+# 9.4.  Generally useful syntaxes
+#
+#    caseIgnoreIA5StringSyntax ATTRIBUTE-SYNTAX
+#            IA5String
+#            MATCHES FOR EQUALITY SUBSTRINGS
+#
+#    iA5StringSyntax ATTRIBUTE-SYNTAX
+#        IA5String
+#        MATCHES FOR EQUALITY SUBSTRINGS
+#
+#
+#    -- Syntaxes to support the DNS attributes
+#
+#    DNSRecordSyntax ATTRIBUTE-SYNTAX
+#            IA5String
+#            MATCHES FOR EQUALITY
+#
+#
+#    NRSInformationSyntax ATTRIBUTE-SYNTAX
+#            NRSInformation
+#            MATCHES FOR EQUALITY
+#
+#
+#    NRSInformation ::=  SET {
+#                    [0] Context,
+#                    [1] Address-space-id,
+#                    routes [2] SEQUENCE OF SEQUENCE {
+#                    Route-cost,
+#                    Addressing-info }
+#            }
+#
+#
+# 9.5.  Upper bounds on length of attribute values
+#
+#
+#    ub-document-identifier INTEGER ::= 256
+#
+#    ub-document-location INTEGER ::= 256
+#
+#    ub-document-title INTEGER ::= 256
+#
+#    ub-document-version INTEGER ::= 256
+#
+#    ub-favourite-drink INTEGER ::= 256
+#
+#    ub-host INTEGER ::= 256
+#
+#    ub-information INTEGER ::= 2048
+#
+#    ub-unique-identifier INTEGER ::= 256
+#
+#    ub-personal-title INTEGER ::= 256
+#
+#    ub-photo INTEGER ::= 250000
+#
+#    ub-rfc822-mailbox INTEGER ::= 256
+#
+#    ub-room-number INTEGER ::= 256
+#
+#    ub-text-or-address INTEGER ::= 256
+#
+#    ub-user-class INTEGER ::= 256
+#
+#    ub-user-identifier INTEGER ::= 256
+#
+#    ub-organizational-status INTEGER ::= 256
+#
+#    ub-janet-mailbox INTEGER ::= 256
+#
+#    ub-building-name INTEGER ::= 256
+#
+#    ub-personal-signature ::= 50000
+#
+#    ub-audio INTEGER ::= 250000
+#
+
+# [back to 8]
+# 8.  Object Classes
+#
+# 8.1.  X.500 standard object classes
+#
+#  A number of generally useful object classes are defined in X.521, and
+#  these are supported.  Refer to that document for descriptions of the
+#  suggested usage of these object classes.  The ASN.1 for these object
+#  classes is reproduced for completeness in Appendix C.
+#
+# 8.2.  X.400 standard object classes
+#
+#  A number of object classes defined in X.400 are supported.  Refer to
+#  X.402 for descriptions of the usage of these object classes.  The
+#  ASN.1 for these object classes is reproduced for completeness in
+#  Appendix C.
+#
+# 8.3.  COSINE/Internet object classes
+#
+#  This section attempts to fuse together the object classes designed
+#  for use in the COSINE and Internet pilot activities.  Descriptions
+#  are given of the suggested usage of these object classes.  The ASN.1
+#  for these object classes is also reproduced in Appendix C.
+#
+# 8.3.1.  Pilot Object
+#
+#  The PilotObject object class is used as a sub-class to allow some
+#  common, useful attributes to be assigned to entries of all other
+#  object classes.
+#
+#    pilotObject OBJECT-CLASS
+#        SUBCLASS OF top
+#        MAY CONTAIN {
+#            info,
+#            photo,
+#            manager,
+#            uniqueIdentifier,
+#            lastModifiedTime,
+#            lastModifiedBy,
+#            dITRedirect,
+#            audio}
+#    ::= {pilotObjectClass 3}
+#
+#objectclass ( 0.9.2342.19200300.100.4.3 NAME 'pilotObject'
+#	DESC 'RFC1274: pilot object'
+#	SUP top AUXILIARY
+#	MAY ( info $ photo $ manager $ uniqueIdentifier $
+#		lastModifiedTime $ lastModifiedBy $ dITRedirect $ audio )
+#	)
+
+# 8.3.2.  Pilot Person
+#
+#  The PilotPerson object class is used as a sub-class of person, to
+#  allow the use of a number of additional attributes to be assigned to
+#  entries of object class person.
+#
+#    pilotPerson OBJECT-CLASS
+#        SUBCLASS OF person
+#        MAY CONTAIN {
+#                    userid,
+#                    textEncodedORAddress,
+#                    rfc822Mailbox,
+#                    favouriteDrink,
+#                    roomNumber,
+#                    userClass,
+#                    homeTelephoneNumber,
+#                    homePostalAddress,
+#                    secretary,
+#                    personalTitle,
+#                    preferredDeliveryMethod,
+#                    businessCategory,
+#                    janetMailbox,
+#                    otherMailbox,
+#                    mobileTelephoneNumber,
+#                    pagerTelephoneNumber,
+#                    organizationalStatus,
+#                    mailPreferenceOption,
+#                    personalSignature}
+#    ::= {pilotObjectClass 4}
+#
+objectclass ( 0.9.2342.19200300.100.4.4
+	NAME ( 'pilotPerson' 'newPilotPerson' )
+	SUP person STRUCTURAL
+	MAY ( userid $ textEncodedORAddress $ rfc822Mailbox $
+		favouriteDrink $ roomNumber $ userClass $
+		homeTelephoneNumber $ homePostalAddress $ secretary $
+		personalTitle $ preferredDeliveryMethod $ businessCategory $
+		janetMailbox $ otherMailbox $ mobileTelephoneNumber $
+		pagerTelephoneNumber $ organizationalStatus $
+		mailPreferenceOption $ personalSignature )
+	)
+
+# 8.3.3.  Account
+#
+#  The Account object class is used to define entries representing
+#  computer accounts.  The userid attribute should be used for naming
+#  entries of this object class.
+#
+#    account OBJECT-CLASS
+#        SUBCLASS OF top
+#        MUST CONTAIN {
+#            userid}
+#        MAY CONTAIN {
+#            description,
+#            seeAlso,
+#            localityName,
+#            organizationName,
+#            organizationalUnitName,
+#            host}
+#    ::= {pilotObjectClass 5}
+#
+objectclass ( 0.9.2342.19200300.100.4.5 NAME 'account'
+	SUP top STRUCTURAL
+	MUST userid
+	MAY ( description $ seeAlso $ localityName $
+		organizationName $ organizationalUnitName $ host )
+	)
+
+# 8.3.4.  Document
+#
+#  The Document object class is used to define entries which represent
+#  documents.
+#
+#    document OBJECT-CLASS
+#        SUBCLASS OF top
+#        MUST CONTAIN {
+#            documentIdentifier}
+#        MAY CONTAIN {
+#            commonName,
+#            description,
+#            seeAlso,
+#            localityName,
+#            organizationName,
+#            organizationalUnitName,
+#            documentTitle,
+#            documentVersion,
+#            documentAuthor,
+#            documentLocation,
+#            documentPublisher}
+#    ::= {pilotObjectClass 6}
+#
+objectclass ( 0.9.2342.19200300.100.4.6 NAME 'document'
+	SUP top STRUCTURAL
+	MUST documentIdentifier
+	MAY ( commonName $ description $ seeAlso $ localityName $
+		organizationName $ organizationalUnitName $
+		documentTitle $ documentVersion $ documentAuthor $
+		documentLocation $ documentPublisher )
+	)
+
+# 8.3.5.  Room
+#
+#  The Room object class is used to define entries representing rooms.
+#  The commonName attribute should be used for naming pentries of this
+#  object class.
+#
+#    room OBJECT-CLASS
+#        SUBCLASS OF top
+#        MUST CONTAIN {
+#            commonName}
+#        MAY CONTAIN {
+#            roomNumber,
+#            description,
+#            seeAlso,
+#            telephoneNumber}
+#    ::= {pilotObjectClass 7}
+#
+objectclass ( 0.9.2342.19200300.100.4.7 NAME 'room'
+	SUP top STRUCTURAL
+	MUST commonName
+	MAY ( roomNumber $ description $ seeAlso $ telephoneNumber )
+	)
+
+# 8.3.6.  Document Series
+#
+#  The Document Series object class is used to define an entry which
+#  represents a series of documents (e.g., The Request For Comments
+#  papers).
+#
+#    documentSeries OBJECT-CLASS
+#        SUBCLASS OF top
+#        MUST CONTAIN {
+#            commonName}
+#        MAY CONTAIN {
+#            description,
+#            seeAlso,
+#            telephoneNumber,
+#            localityName,
+#            organizationName,
+#            organizationalUnitName}
+#    ::= {pilotObjectClass 9}
+#
+objectclass ( 0.9.2342.19200300.100.4.9 NAME 'documentSeries'
+	SUP top STRUCTURAL
+	MUST commonName
+	MAY ( description $ seeAlso $ telephonenumber $
+		localityName $ organizationName $ organizationalUnitName )
+	)
+
+# 8.3.7.  Domain
+#
+#  The Domain object class is used to define entries which represent DNS
+#  or NRS domains.  The domainComponent attribute should be used for
+#  naming entries of this object class.  The usage of this object class
+#  is described in more detail in [3].
+#
+#    domain OBJECT-CLASS
+#        SUBCLASS OF top
+#        MUST CONTAIN {
+#            domainComponent}
+#        MAY CONTAIN {
+#            associatedName,
+#            organizationName,
+#            organizationalAttributeSet}
+#    ::= {pilotObjectClass 13}
+#
+objectclass ( 0.9.2342.19200300.100.4.13 NAME 'domain'
+	SUP top STRUCTURAL
+	MUST domainComponent
+	MAY ( associatedName $ organizationName $ description $
+		businessCategory $ seeAlso $ searchGuide $ userPassword $
+		localityName $ stateOrProvinceName $ streetAddress $
+		physicalDeliveryOfficeName $ postalAddress $ postalCode $
+		postOfficeBox $ streetAddress $
+		facsimileTelephoneNumber $ internationalISDNNumber $
+		telephoneNumber $ teletexTerminalIdentifier $ telexNumber $
+		preferredDeliveryMethod $ destinationIndicator $
+		registeredAddress $ x121Address )
+	)
+
+# 8.3.8.  RFC822 Local Part
+#
+#  The RFC822 Local Part object class is used to define entries which
+#  represent the local part of RFC822 mail addresses.  This treats this
+#  part of an RFC822 address as a domain.  The usage of this object
+#  class is described in more detail in [3].
+#
+#    rFC822localPart OBJECT-CLASS
+#        SUBCLASS OF domain
+#        MAY CONTAIN {
+#            commonName,
+#            surname,
+#            description,
+#            seeAlso,
+#            telephoneNumber,
+#            postalAttributeSet,
+#            telecommunicationAttributeSet}
+#    ::= {pilotObjectClass 14}
+#
+objectclass ( 0.9.2342.19200300.100.4.14 NAME 'RFC822localPart'
+	SUP domain STRUCTURAL
+	MAY ( commonName $ surname $ description $ seeAlso $ telephoneNumber $
+		physicalDeliveryOfficeName $ postalAddress $ postalCode $
+		postOfficeBox $ streetAddress $
+		facsimileTelephoneNumber $ internationalISDNNumber $
+		telephoneNumber $ teletexTerminalIdentifier $
+		telexNumber $ preferredDeliveryMethod $ destinationIndicator $
+		registeredAddress $ x121Address )
+	)
+
+# 8.3.9.  DNS Domain
+#
+#  The DNS Domain (Domain NameServer) object class is used to define
+#  entries for DNS domains.  The usage of this object class is described
+#  in more detail in [3].
+#
+#    dNSDomain OBJECT-CLASS
+#        SUBCLASS OF domain
+#        MAY CONTAIN {
+#            ARecord,
+#            MDRecord,
+#            MXRecord,
+#            NSRecord,
+#            SOARecord,
+#            CNAMERecord}
+#    ::= {pilotObjectClass 15}
+#
+
+# malformed: had quotes around superior domain
+objectclass ( 0.9.2342.19200300.100.4.15 NAME 'dNSDomain'
+	SUP domain STRUCTURAL
+	MAY ( ARecord $ MDRecord $ MXRecord $ NSRecord $
+		SOARecord $ CNAMERecord )
+	)
+
+# 8.3.10.  Domain Related Object
+#
+#  The Domain Related Object object class is used to define entries
+#  which represent DNS/NRS domains which are "equivalent" to an X.500
+#  domain: e.g., an organisation or organisational unit.  The usage of
+#  this object class is described in more detail in [3].
+#
+#    domainRelatedObject OBJECT-CLASS
+#        SUBCLASS OF top
+#        MUST CONTAIN {
+#            associatedDomain}
+#    ::= {pilotObjectClass 17}
+#
+objectclass ( 0.9.2342.19200300.100.4.17 NAME 'domainRelatedObject'
+	DESC 'RFC1274: an object related to an domain'
+	SUP top AUXILIARY
+	MUST associatedDomain )
+
+# 8.3.11.  Friendly Country
+#
+#  The Friendly Country object class is used to define country entries
+#  in the DIT.  The object class is used to allow friendlier naming of
+#  countries than that allowed by the object class country.  The naming
+#  attribute of object class country, countryName, has to be a 2 letter
+#  string defined in ISO 3166.
+#
+#    friendlyCountry OBJECT-CLASS
+#        SUBCLASS OF country
+#        MUST CONTAIN {
+#            friendlyCountryName}
+#    ::= {pilotObjectClass 18}
+#
+objectclass ( 0.9.2342.19200300.100.4.18 NAME 'friendlyCountry'
+	SUP country STRUCTURAL
+	MUST friendlyCountryName )
+
+# 8.3.12.  Simple Security Object
+#
+#  The Simple Security Object object class is used to allow an entry to
+#  have a userPassword attribute when an entry's principal object
+#  classes do not allow userPassword as an attribute type.
+#
+#    simpleSecurityObject OBJECT-CLASS
+#        SUBCLASS OF top
+#        MUST CONTAIN {
+#            userPassword }
+#    ::= {pilotObjectClass 19}
+#
+## (in core.schema)
+## objectclass ( 0.9.2342.19200300.100.4.19 NAME 'simpleSecurityObject'
+##	SUP top AUXILIARY
+##	MUST userPassword )
+
+# 8.3.13.  Pilot Organization
+#
+#  The PilotOrganization object class is used as a sub-class of
+#  organization and organizationalUnit to allow a number of additional
+#  attributes to be assigned to entries of object classes organization
+#  and organizationalUnit.
+#
+#    pilotOrganization OBJECT-CLASS
+#        SUBCLASS OF organization, organizationalUnit
+#        MAY CONTAIN {
+#                    buildingName}
+#    ::= {pilotObjectClass 20}
+#
+objectclass ( 0.9.2342.19200300.100.4.20 NAME 'pilotOrganization'
+	SUP ( organization $ organizationalUnit ) STRUCTURAL
+	MAY buildingName )
+
+# 8.3.14.  Pilot DSA
+#
+#  The PilotDSA object class is used as a sub-class of the dsa object
+#  class to allow additional attributes to be assigned to entries for
+#  DSAs.
+#
+#    pilotDSA OBJECT-CLASS
+#        SUBCLASS OF dsa
+#        MUST CONTAIN {
+#            dSAQuality}
+#    ::= {pilotObjectClass 21}
+#
+objectclass ( 0.9.2342.19200300.100.4.21 NAME 'pilotDSA'
+	SUP dsa STRUCTURAL
+	MAY dSAQuality )
+
+# 8.3.15.  Quality Labelled Data
+#
+#  The Quality Labelled Data object class is used to allow the
+#  assignment of the data quality attributes to subtrees in the DIT.
+#
+#  See [8] for more details.
+#
+#    qualityLabelledData OBJECT-CLASS
+#        SUBCLASS OF top
+#        MUST CONTAIN {
+#            dSAQuality}
+#        MAY CONTAIN {
+#            subtreeMinimumQuality,
+#            subtreeMaximumQuality}
+#    ::= {pilotObjectClass 22}
+objectclass ( 0.9.2342.19200300.100.4.22 NAME 'qualityLabelledData'
+	SUP top AUXILIARY
+	MUST dsaQuality
+	MAY ( subtreeMinimumQuality $ subtreeMaximumQuality )
+	)
+
+
+# References
+#
+#    [1]  CCITT/ISO, "X.500, The Directory - overview of concepts,
+#         models and services, CCITT /ISO IS 9594.
+#
+#    [2]  Kille, S., "The THORN and RARE X.500 Naming Architecture, in
+#         University College London, Department of Computer Science
+#         Research Note 89/48, May 1989.
+#
+#    [3]  Kille, S., "X.500 and Domains", RFC 1279, University College
+#         London, November 1991.
+#
+#    [4]  Rose, M., "PSI/NYSERNet White Pages Pilot Project: Status
+#         Report", Technical Report 90-09-10-1, published by NYSERNet
+#         Inc, 1990.
+#
+#    [5]  Craigie, J., "UK Academic Community Directory Service Pilot
+#         Project, pp. 305-310 in Computer Networks and ISDN Systems
+#         17 (1989), published by North Holland.
+#
+#    [6]  Mockapetris, P., "Domain Names - Concepts and Facilities",
+#         RFC 1034, USC/Information Sciences Institute, November 1987.
+#
+#    [7]  Mockapetris, P., "Domain Names - Implementation and
+#         Specification, RFC 1035, USC/Information Sciences Institute,
+#         November 1987.
+#
+#    [8]  Kille, S., "Handling QOS (Quality of service) in the
+#         Directory," publication in process, March 1991.
+#
+#
+# APPENDIX C - Summary of all Object Classes and Attribute Types
+#
+#    -- Some Important Object Identifiers
+#
+#    data OBJECT IDENTIFIER ::= {ccitt 9}
+#    pss OBJECT IDENTIFIER ::= {data 2342}
+#    ucl OBJECT IDENTIFIER ::= {pss 19200300}
+#    pilot OBJECT IDENTIFIER ::= {ucl 100}
+#
+#    pilotAttributeType OBJECT IDENTIFIER ::= {pilot 1}
+#    pilotAttributeSyntax OBJECT IDENTIFIER ::= {pilot 3}
+#    pilotObjectClass OBJECT IDENTIFIER ::= {pilot 4}
+#    pilotGroups OBJECT IDENTIFIER ::= {pilot 10}
+#
+#    iA5StringSyntax OBJECT IDENTIFIER ::= {pilotAttributeSyntax 4}
+#    caseIgnoreIA5StringSyntax OBJECT IDENTIFIER ::=
+#                                          {pilotAttributeSyntax 5}
+#
+#    -- Standard Object Classes
+#
+#    top OBJECT-CLASS
+#        MUST CONTAIN {
+#            objectClass}
+#    ::= {objectClass 0}
+#
+#
+#    alias OBJECT-CLASS
+#        SUBCLASS OF top
+#        MUST CONTAIN {
+#            aliasedObjectName}
+#    ::= {objectClass 1}
+#
+#
+#    country OBJECT-CLASS
+#        SUBCLASS OF top
+#        MUST CONTAIN {
+#            countryName}
+#        MAY CONTAIN {
+#            description,
+#            searchGuide}
+#    ::= {objectClass 2}
+#
+#
+#    locality OBJECT-CLASS
+#        SUBCLASS OF top
+#        MAY CONTAIN {
+#            description,
+#            localityName,
+#            stateOrProvinceName,
+#            searchGuide,
+#            seeAlso,
+#            streetAddress}
+#    ::= {objectClass 3}
+#
+#
+#    organization OBJECT-CLASS
+#        SUBCLASS OF top
+#        MUST CONTAIN {
+#            organizationName}
+#        MAY CONTAIN {
+#            organizationalAttributeSet}
+#    ::= {objectClass 4}
+#
+#
+#    organizationalUnit OBJECT-CLASS
+#        SUBCLASS OF top
+#        MUST CONTAIN {
+#            organizationalUnitName}
+#        MAY CONTAIN {
+#            organizationalAttributeSet}
+#    ::= {objectClass 5}
+#
+#
+#    person OBJECT-CLASS
+#        SUBCLASS OF top
+#        MUST CONTAIN {
+#            commonName,
+#            surname}
+#        MAY CONTAIN {
+#            description,
+#            seeAlso,
+#            telephoneNumber,
+#            userPassword}
+#    ::= {objectClass 6}
+#
+#
+#    organizationalPerson OBJECT-CLASS
+#        SUBCLASS OF person
+#        MAY CONTAIN {
+#            localeAttributeSet,
+#            organizationalUnitName,
+#            postalAttributeSet,
+#            telecommunicationAttributeSet,
+#            title}
+#    ::= {objectClass 7}
+#
+#
+#    organizationalRole OBJECT-CLASS
+#        SUBCLASS OF top
+#        MUST CONTAIN {
+#            commonName}
+#        MAY CONTAIN {
+#            description,
+#            localeAttributeSet,
+#            organizationalUnitName,
+#            postalAttributeSet,
+#            preferredDeliveryMethod,
+#            roleOccupant,
+#            seeAlso,
+#            telecommunicationAttributeSet}
+#    ::= {objectClass 8}
+#
+#
+#    groupOfNames OBJECT-CLASS
+#        SUBCLASS OF top
+#        MUST CONTAIN {
+#            commonName,
+#            member}
+#        MAY CONTAIN {
+#            description,
+#            organizationName,
+#            organizationalUnitName,
+#            owner,
+#            seeAlso,
+#            businessCategory}
+#    ::= {objectClass 9}
+#
+#
+#    residentialPerson OBJECT-CLASS
+#        SUBCLASS OF person
+#        MUST CONTAIN {
+#            localityName}
+#        MAY CONTAIN {
+#            localeAttributeSet,
+#            postalAttributeSet,
+#            preferredDeliveryMethod,
+#            telecommunicationAttributeSet,
+#            businessCategory}
+#    ::= {objectClass 10}
+#
+#
+#    applicationProcess OBJECT-CLASS
+#        SUBCLASS OF top
+#        MUST CONTAIN {
+#            commonName}
+#        MAY CONTAIN {
+#            description,
+#            localityName,
+#            organizationalUnitName,
+#            seeAlso}
+#    ::= {objectClass 11}
+#
+#
+#    applicationEntity OBJECT-CLASS
+#        SUBCLASS OF top
+#        MUST CONTAIN {
+#            commonName,
+#            presentationAddress}
+#        MAY CONTAIN {
+#            description,
+#            localityName,
+#            organizationName,
+#            organizationalUnitName,
+#            seeAlso,
+#            supportedApplicationContext}
+#    ::= {objectClass 12}
+#
+#
+#    dSA OBJECT-CLASS
+#        SUBCLASS OF applicationEntity
+#        MAY CONTAIN {
+#            knowledgeInformation}
+#    ::= {objectClass 13}
+#
+#
+#    device OBJECT-CLASS
+#        SUBCLASS OF top
+#        MUST CONTAIN {
+#            commonName}
+#        MAY CONTAIN {
+#            description,
+#            localityName,
+#            organizationName,
+#            organizationalUnitName,
+#            owner,
+#            seeAlso,
+#            serialNumber}
+#    ::= {objectClass 14}
+#
+#
+#    strongAuthenticationUser OBJECT-CLASS
+#        SUBCLASS OF top
+#        MUST CONTAIN {
+#            userCertificate}
+#    ::= {objectClass 15}
+#
+#
+#    certificationAuthority OBJECT-CLASS
+#        SUBCLASS OF top
+#        MUST CONTAIN {
+#            cACertificate,
+#            certificateRevocationList,
+#            authorityRevocationList}
+#        MAY CONTAIN {
+#            crossCertificatePair}
+#    ::= {objectClass 16}
+#
+#    -- Standard MHS Object Classes
+#
+#    mhsDistributionList OBJECT-CLASS
+#        SUBCLASS OF top
+#        MUST CONTAIN {
+#            commonName,
+#            mhsDLSubmitPermissions,
+#            mhsORAddresses}
+#        MAY CONTAIN {
+#            description,
+#            organizationName,
+#            organizationalUnitName,
+#            owner,
+#            seeAlso,
+#            mhsDeliverableContentTypes,
+#            mhsdeliverableEits,
+#            mhsDLMembers,
+#            mhsPreferredDeliveryMethods}
+#    ::= {mhsObjectClass 0}
+#
+#
+#    mhsMessageStore OBJECT-CLASS
+#        SUBCLASS OF applicationEntity
+#        MAY CONTAIN {
+#            description,
+#            owner,
+#            mhsSupportedOptionalAttributes,
+#            mhsSupportedAutomaticActions,
+#            mhsSupportedContentTypes}
+#    ::= {mhsObjectClass 1}
+#
+#
+#    mhsMessageTransferAgent OBJECT-CLASS
+#        SUBCLASS OF applicationEntity
+#        MAY CONTAIN {
+#            description,
+#            owner,
+#            mhsDeliverableContentLength}
+#    ::= {mhsObjectClass 2}
+#
+#
+#    mhsOrganizationalUser OBJECT-CLASS
+#        SUBCLASS OF organizationalPerson
+#        MUST CONTAIN {
+#            mhsORAddresses}
+#        MAY CONTAIN {
+#            mhsDeliverableContentLength,
+#            mhsDeliverableContentTypes,
+#            mhsDeliverableEits,
+#            mhsMessageStoreName,
+#            mhsPreferredDeliveryMethods }
+#    ::= {mhsObjectClass 3}
+#
+#
+#    mhsResidentialUser OBJECT-CLASS
+#        SUBCLASS OF residentialPerson
+#        MUST CONTAIN {
+#            mhsORAddresses}
+#        MAY CONTAIN {
+#            mhsDeliverableContentLength,
+#            mhsDeliverableContentTypes,
+#            mhsDeliverableEits,
+#            mhsMessageStoreName,
+#            mhsPreferredDeliveryMethods }
+#    ::= {mhsObjectClass 4}
+#
+#
+#    mhsUserAgent OBJECT-CLASS
+#        SUBCLASS OF applicationEntity
+#        MAY CONTAIN {
+#            mhsDeliverableContentLength,
+#            mhsDeliverableContentTypes,
+#            mhsDeliverableEits,
+#            mhsORAddresses,
+#            owner}
+#    ::= {mhsObjectClass 5}
+#
+#
+#
+#
+#    -- Pilot Object Classes
+#
+#    pilotObject OBJECT-CLASS
+#        SUBCLASS OF top
+#        MAY CONTAIN {
+#            info,
+#            photo,
+#            manager,
+#            uniqueIdentifier,
+#            lastModifiedTime,
+#            lastModifiedBy,
+#            dITRedirect,
+#            audio}
+#    ::= {pilotObjectClass 3}
+#    pilotPerson OBJECT-CLASS
+#        SUBCLASS OF person
+#        MAY CONTAIN {
+#                    userid,
+#                    textEncodedORAddress,
+#                    rfc822Mailbox,
+#                    favouriteDrink,
+#                    roomNumber,
+#                    userClass,
+#                    homeTelephoneNumber,
+#                    homePostalAddress,
+#                    secretary,
+#                    personalTitle,
+#                    preferredDeliveryMethod,
+#                    businessCategory,
+#                    janetMailbox,
+#                    otherMailbox,
+#                    mobileTelephoneNumber,
+#                    pagerTelephoneNumber,
+#                    organizationalStatus,
+#                    mailPreferenceOption,
+#                    personalSignature}
+#    ::= {pilotObjectClass 4}
+#
+#
+#    account OBJECT-CLASS
+#        SUBCLASS OF top
+#        MUST CONTAIN {
+#            userid}
+#        MAY CONTAIN {
+#            description,
+#            seeAlso,
+#            localityName,
+#            organizationName,
+#            organizationalUnitName,
+#            host}
+#    ::= {pilotObjectClass 5}
+#
+#
+#    document OBJECT-CLASS
+#        SUBCLASS OF top
+#        MUST CONTAIN {
+#            documentIdentifier}
+#        MAY CONTAIN {
+#            commonName,
+#            description,
+#            seeAlso,
+#            localityName,
+#            organizationName,
+#            organizationalUnitName,
+#            documentTitle,
+#            documentVersion,
+#            documentAuthor,
+#            documentLocation,
+#            documentPublisher}
+#    ::= {pilotObjectClass 6}
+#
+#
+#    room OBJECT-CLASS
+#        SUBCLASS OF top
+#        MUST CONTAIN {
+#            commonName}
+#        MAY CONTAIN {
+#            roomNumber,
+#            description,
+#            seeAlso,
+#            telephoneNumber}
+#    ::= {pilotObjectClass 7}
+#
+#
+#    documentSeries OBJECT-CLASS
+#        SUBCLASS OF top
+#        MUST CONTAIN {
+#            commonName}
+#        MAY CONTAIN {
+#            description,
+#            seeAlso,
+#            telephoneNumber,
+#            localityName,
+#            organizationName,
+#            organizationalUnitName}
+#    ::= {pilotObjectClass 9}
+#
+#
+#    domain OBJECT-CLASS
+#        SUBCLASS OF top
+#        MUST CONTAIN {
+#            domainComponent}
+#        MAY CONTAIN {
+#            associatedName,
+#            organizationName,
+#            organizationalAttributeSet}
+#    ::= {pilotObjectClass 13}
+#
+#
+#    rFC822localPart OBJECT-CLASS
+#        SUBCLASS OF domain
+#        MAY CONTAIN {
+#            commonName,
+#            surname,
+#            description,
+#            seeAlso,
+#            telephoneNumber,
+#            postalAttributeSet,
+#            telecommunicationAttributeSet}
+#    ::= {pilotObjectClass 14}
+#
+#
+#    dNSDomain OBJECT-CLASS
+#        SUBCLASS OF domain
+#        MAY CONTAIN {
+#            ARecord,
+#            MDRecord,
+#            MXRecord,
+#            NSRecord,
+#            SOARecord,
+#            CNAMERecord}
+#    ::= {pilotObjectClass 15}
+#
+#
+#    domainRelatedObject OBJECT-CLASS
+#        SUBCLASS OF top
+#        MUST CONTAIN {
+#            associatedDomain}
+#    ::= {pilotObjectClass 17}
+#
+#
+#    friendlyCountry OBJECT-CLASS
+#        SUBCLASS OF country
+#        MUST CONTAIN {
+#            friendlyCountryName}
+#    ::= {pilotObjectClass 18}
+#
+#
+#    simpleSecurityObject OBJECT-CLASS
+#        SUBCLASS OF top
+#        MUST CONTAIN {
+#            userPassword }
+#    ::= {pilotObjectClass 19}
+#
+#
+#    pilotOrganization OBJECT-CLASS
+#        SUBCLASS OF organization, organizationalUnit
+#        MAY CONTAIN {
+#                    buildingName}
+#    ::= {pilotObjectClass 20}
+#
+#
+#    pilotDSA OBJECT-CLASS
+#        SUBCLASS OF dsa
+#        MUST CONTAIN {
+#            dSAQuality}
+#    ::= {pilotObjectClass 21}
+#
+#
+#    qualityLabelledData OBJECT-CLASS
+#        SUBCLASS OF top
+#        MUST CONTAIN {
+#            dSAQuality}
+#        MAY CONTAIN {
+#            subtreeMinimumQuality,
+#            subtreeMaximumQuality}
+#    ::= {pilotObjectClass 22}
+#
+#
+#
+#
+#    -- Standard Attribute Types
+#
+#    objectClass ObjectClass
+#        ::= {attributeType 0}
+#
+#
+#    aliasedObjectName AliasedObjectName
+#        ::= {attributeType 1}
+#
+#
+#    knowledgeInformation ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX caseIgnoreString
+#        ::= {attributeType 2}
+#
+#
+#    commonName ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX caseIgnoreStringSyntax
+#        (SIZE (1..ub-common-name))
+#        ::= {attributeType 3}
+#
+#
+#    surname ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX caseIgnoreStringSyntax
+#        (SIZE (1..ub-surname))
+#        ::= {attributeType 4}
+#
+#
+#    serialNumber ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX printableStringSyntax
+#        (SIZE (1..ub-serial-number))
+#        ::= {attributeType 5}
+#
+#
+#    countryName ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX PrintableString
+#        (SIZE (1..ub-country-code))
+#        SINGLE VALUE
+#        ::= {attributeType 6}
+#
+#
+#    localityName ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX caseIgnoreStringSyntax
+#        (SIZE (1..ub-locality-name))
+#        ::= {attributeType 7}
+#
+#
+#    stateOrProvinceName ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX caseIgnoreStringSyntax
+#        (SIZE (1..ub-state-name))
+#        ::= {attributeType 8}
+#
+#
+#    streetAddress ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX caseIgnoreStringSyntax
+#        (SIZE (1..ub-street-address))
+#        ::= {attributeType 9}
+#
+#
+#    organizationName ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX caseIgnoreStringSyntax
+#        (SIZE (1..ub-organization-name))
+#        ::= {attributeType 10}
+#
+#
+#    organizationalUnitName ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX caseIgnoreStringSyntax
+#        (SIZE (1..ub-organizational-unit-name))
+#        ::= {attributeType 11}
+#
+#
+#    title ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX caseIgnoreStringSyntax
+#        (SIZE (1..ub-title))
+#        ::= {attributeType 12}
+#
+#
+#    description ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX caseIgnoreStringSyntax
+#        (SIZE (1..ub-description))
+#        ::= {attributeType 13}
+#
+#
+#    searchGuide ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX Guide
+#        ::= {attributeType 14}
+#
+#
+#    businessCategory ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX caseIgnoreStringSyntax
+#        (SIZE (1..ub-business-category))
+#        ::= {attributeType 15}
+#
+#
+#    postalAddress ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX PostalAddress
+#        MATCHES FOR EQUALITY
+#        ::= {attributeType 16}
+#
+#
+#    postalCode ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX caseIgnoreStringSyntax
+#        (SIZE (1..ub-postal-code))
+#        ::= {attributeType 17}
+#
+#
+#    postOfficeBox ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX caseIgnoreStringSyntax
+#        (SIZE (1..ub-post-office-box))
+#        ::= {attributeType 18}
+#
+#
+#    physicalDeliveryOfficeName ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX caseIgnoreStringSyntax
+#        (SIZE (1..ub-physical-office-name))
+#        ::= {attributeType 19}
+#
+#
+#    telephoneNumber ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX telephoneNumberSyntax
+#        (SIZE (1..ub-telephone-number))
+#        ::= {attributeType 20}
+#
+#
+#    telexNumber ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX TelexNumber
+#        (SIZE (1..ub-telex))
+#        ::= {attributeType 21}
+#
+#
+#    teletexTerminalIdentifier ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX TeletexTerminalIdentifier
+#        (SIZE (1..ub-teletex-terminal-id))
+#        ::= {attributeType 22}
+#
+#
+#    facsimileTelephoneNumber ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX FacsimileTelephoneNumber
+#        ::= {attributeType 23}
+#
+#
+#    x121Address ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX NumericString
+#        (SIZE (1..ub-x121-address))
+#        ::= {attributeType 24}
+#
+#
+#    internationaliSDNNumber ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX NumericString
+#        (SIZE (1..ub-isdn-address))
+#        ::= {attributeType 25}
+#
+#
+#    registeredAddress ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX PostalAddress
+#        ::= {attributeType 26}
+#
+#
+#    destinationIndicator ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX PrintableString
+#        (SIZE (1..ub-destination-indicator))
+#        MATCHES FOR EQUALITY SUBSTRINGS
+#        ::= {attributeType 27}
+#
+#
+#    preferredDeliveryMethod ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX deliveryMethod
+#        ::= {attributeType 28}
+#
+#
+#    presentationAddress ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX PresentationAddress
+#        MATCHES FOR EQUALITY
+#        ::= {attributeType 29}
+#
+#
+#    supportedApplicationContext ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX objectIdentifierSyntax
+#        ::= {attributeType 30}
+#
+#
+#    member ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX distinguishedNameSyntax
+#        ::= {attributeType 31}
+#
+#
+#    owner ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX distinguishedNameSyntax
+#        ::= {attributeType 32}
+#
+#
+#    roleOccupant ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX distinguishedNameSyntax
+#        ::= {attributeType 33}
+#
+#
+#    seeAlso ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX distinguishedNameSyntax
+#        ::= {attributeType 34}
+#
+#
+#    userPassword ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX Userpassword
+#        ::= {attributeType 35}
+#
+#
+#    userCertificate ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX UserCertificate
+#        ::= {attributeType 36}
+#
+#
+#    cACertificate ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX cACertificate
+#        ::= {attributeType 37}
+#
+#
+#    authorityRevocationList ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX AuthorityRevocationList
+#        ::= {attributeType 38}
+#
+#
+#    certificateRevocationList ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX CertificateRevocationList
+#        ::= {attributeType 39}
+#
+#
+#    crossCertificatePair ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX CrossCertificatePair
+#        ::= {attributeType 40}
+#
+#
+#
+#
+#    -- Standard MHS Attribute Types
+#
+#    mhsDeliverableContentLength ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX integer
+#        ::= {mhsAttributeType 0}
+#
+#
+#    mhsDeliverableContentTypes ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX oID
+#        ::= {mhsAttributeType 1}
+#
+#
+#    mhsDeliverableEits ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX oID
+#        ::= {mhsAttributeType 2}
+#
+#
+#    mhsDLMembers ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX oRName
+#        ::= {mhsAttributeType 3}
+#
+#
+#    mhsDLSubmitPermissions ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX dLSubmitPermission
+#        ::= {mhsAttributeType 4}
+#
+#
+#    mhsMessageStoreName ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX dN
+#        ::= {mhsAttributeType 5}
+#
+#
+#    mhsORAddresses ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX oRAddress
+#        ::= {mhsAttributeType 6}
+#
+#
+#    mhsPreferredDeliveryMethods ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX deliveryMethod
+#        ::= {mhsAttributeType 7}
+#
+#
+#    mhsSupportedAutomaticActions ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX oID
+#        ::= {mhsAttributeType 8}
+#
+#
+#    mhsSupportedContentTypes ATTRIBUTE
+#
+#        WITH ATTRIBUTE-SYNTAX oID
+#        ::= {mhsAttributeType 9}
+#
+#
+#    mhsSupportedOptionalAttributes ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX oID
+#        ::= {mhsAttributeType 10}
+#
+#
+#
+#
+#    -- Pilot Attribute Types
+#
+#    userid ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            caseIgnoreStringSyntax
+#            (SIZE (1 .. ub-user-identifier))
+#    ::= {pilotAttributeType 1}
+#
+#
+#    textEncodedORAddress ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            caseIgnoreStringSyntax
+#        (SIZE (1 .. ub-text-encoded-or-address))
+#    ::= {pilotAttributeType 2}
+#
+#
+#    rfc822Mailbox ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            caseIgnoreIA5StringSyntax
+#            (SIZE (1 .. ub-rfc822-mailbox))
+#    ::= {pilotAttributeType 3}
+#
+#
+#    info ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            caseIgnoreStringSyntax
+#            (SIZE (1 .. ub-information))
+#    ::= {pilotAttributeType 4}
+#
+#
+#    favouriteDrink ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            caseIgnoreStringSyntax
+#            (SIZE (1 .. ub-favourite-drink))
+#    ::= {pilotAttributeType 5}
+#
+#
+#    roomNumber ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            caseIgnoreStringSyntax
+#            (SIZE (1 .. ub-room-number))
+#    ::= {pilotAttributeType 6}
+#
+#
+#    photo ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            CHOICE {
+#                g3-facsimile [3] G3FacsimileBodyPart
+#                }
+#        (SIZE (1 .. ub-photo))
+#    ::= {pilotAttributeType 7}
+#
+#
+#    userClass ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            caseIgnoreStringSyntax
+#            (SIZE (1 .. ub-user-class))
+#    ::= {pilotAttributeType 8}
+#
+#
+#    host ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            caseIgnoreStringSyntax
+#            (SIZE (1 .. ub-host))
+#    ::= {pilotAttributeType 9}
+#
+#
+#    manager ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            distinguishedNameSyntax
+#    ::= {pilotAttributeType 10}
+#
+#
+#    documentIdentifier ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            caseIgnoreStringSyntax
+#            (SIZE (1 .. ub-document-identifier))
+#    ::= {pilotAttributeType 11}
+#
+#
+#    documentTitle ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            caseIgnoreStringSyntax
+#        (SIZE (1 .. ub-document-title))
+#    ::= {pilotAttributeType 12}
+#
+#
+#    documentVersion ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            caseIgnoreStringSyntax
+#            (SIZE (1 .. ub-document-version))
+#    ::= {pilotAttributeType 13}
+#
+#
+#    documentAuthor ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            distinguishedNameSyntax
+#    ::= {pilotAttributeType 14}
+#
+#
+#    documentLocation ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            caseIgnoreStringSyntax
+#            (SIZE (1 .. ub-document-location))
+#    ::= {pilotAttributeType 15}
+#
+#
+#    homeTelephoneNumber ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            telephoneNumberSyntax
+#    ::= {pilotAttributeType 20}
+#
+#
+#    secretary ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            distinguishedNameSyntax
+#    ::= {pilotAttributeType 21}
+#
+#
+#    otherMailbox ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            SEQUENCE {
+#                    mailboxType PrintableString, -- e.g. Telemail
+#                    mailbox IA5String  -- e.g. X378:Joe
+#            }
+#    ::= {pilotAttributeType 22}
+#
+#
+#    lastModifiedTime ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            uTCTimeSyntax
+#    ::= {pilotAttributeType 23}
+#
+#
+#    lastModifiedBy ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            distinguishedNameSyntax
+#    ::= {pilotAttributeType 24}
+#
+#
+#    domainComponent ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            caseIgnoreIA5StringSyntax
+#            SINGLE VALUE
+#    ::= {pilotAttributeType 25}
+#
+#
+#    aRecord ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            DNSRecordSyntax
+#    ::= {pilotAttributeType 26}
+#
+#
+#    mXRecord ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            DNSRecordSyntax
+#    ::= {pilotAttributeType 28}
+#
+#
+#    nSRecord ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            DNSRecordSyntax
+#    ::= {pilotAttributeType 29}
+#
+#    sOARecord ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            DNSRecordSyntax
+#    ::= {pilotAttributeType 30}
+#
+#
+#    cNAMERecord ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            iA5StringSyntax
+#    ::= {pilotAttributeType 31}
+#
+#
+#    associatedDomain ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            caseIgnoreIA5StringSyntax
+#    ::= {pilotAttributeType 37}
+#
+#
+#    associatedName ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            distinguishedNameSyntax
+#    ::= {pilotAttributeType 38}
+#
+#
+#    homePostalAddress ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            postalAddress
+#            MATCHES FOR EQUALITY
+#    ::= {pilotAttributeType 39}
+#
+#
+#    personalTitle ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            caseIgnoreStringSyntax
+#            (SIZE (1 .. ub-personal-title))
+#    ::= {pilotAttributeType 40}
+#
+#
+#    mobileTelephoneNumber ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            telephoneNumberSyntax
+#    ::= {pilotAttributeType 41}
+#
+#
+#    pagerTelephoneNumber ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            telephoneNumberSyntax
+#    ::= {pilotAttributeType 42}
+#
+#
+#    friendlyCountryName ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            caseIgnoreStringSyntax
+#    ::= {pilotAttributeType 43}
+#
+#
+#    uniqueIdentifier ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            caseIgnoreStringSyntax
+#            (SIZE (1 .. ub-unique-identifier))
+#    ::= {pilotAttributeType 44}
+#
+#
+#    organizationalStatus ATTRIBUTE
+#            WITH ATTRIBUTE-SYNTAX
+#            caseIgnoreStringSyntax
+#            (SIZE (1 .. ub-organizational-status))
+#    ::= {pilotAttributeType 45}
+#
+#
+#    janetMailbox ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            caseIgnoreIA5StringSyntax
+#            (SIZE (1 .. ub-janet-mailbox))
+#    ::= {pilotAttributeType 46}
+#
+#
+#    mailPreferenceOption ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX ENUMERATED {
+#                no-list-inclusion(0),
+#                any-list-inclusion(1),  -- may be added to any lists
+#                professional-list-inclusion(2)
+#                                        -- may be added to lists
+#                                        -- which the list provider
+#                                        -- views as related to the
+#                                        -- users professional inter-
+#                                        -- ests, perhaps evaluated
+#                                        -- from the business of the
+#                                        -- organisation or keywords
+#                                        -- in the entry.
+#                }
+#    ::= {pilotAttributeType 47}
+#
+#
+#    buildingName ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            caseIgnoreStringSyntax
+#            (SIZE (1 .. ub-building-name))
+#    ::= {pilotAttributeType 48}
+#
+#
+#    dSAQuality ATTRIBUTE
+#            WITH ATTRIBUTE-SYNTAX DSAQualitySyntax
+#            SINGLE VALUE
+#    ::= {pilotAttributeType 49}
+#
+#
+#    singleLevelQuality ATTRIBUTE
+#            WITH ATTRIBUTE-SYNTAX DataQualitySyntax
+#            SINGLE VALUE
+#
+#
+#    subtreeMinimumQuality ATTRIBUTE
+#            WITH ATTRIBUTE-SYNTAX DataQualitySyntax
+#            SINGLE VALUE
+#               -- Defaults to singleLevelQuality
+#    ::= {pilotAttributeType 51}
+#
+#
+#    subtreeMaximumQuality ATTRIBUTE
+#            WITH ATTRIBUTE-SYNTAX DataQualitySyntax
+#            SINGLE VALUE
+#               -- Defaults to singleLevelQuality
+#    ::= {pilotAttributeType 52}
+#
+#
+#    personalSignature ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            CHOICE {
+#                g3-facsimile [3] G3FacsimileBodyPart
+#                }
+#        (SIZE (1 .. ub-personal-signature))
+#    ::= {pilotAttributeType 53}
+#
+#
+#    dITRedirect ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            distinguishedNameSyntax
+#    ::= {pilotAttributeType 54}
+#
+#
+#    audio ATTRIBUTE
+#        WITH ATTRIBUTE-SYNTAX
+#            Audio
+#        (SIZE (1 .. ub-audio))
+#    ::= {pilotAttributeType 55}
+#
+#    documentPublisher ATTRIBUTE
+#            WITH ATTRIBUTE SYNTAX caseIgnoreStringSyntax
+#    ::= {pilotAttributeType 56}
+#
+#
+#
+#    -- Generally useful syntaxes
+#
+#
+#    caseIgnoreIA5StringSyntax ATTRIBUTE-SYNTAX
+#            IA5String
+#            MATCHES FOR EQUALITY SUBSTRINGS
+#
+#
+#    iA5StringSyntax ATTRIBUTE-SYNTAX
+#        IA5String
+#        MATCHES FOR EQUALITY SUBSTRINGS
+#
+#
+#    -- Syntaxes to support the DNS attributes
+#
+#    DNSRecordSyntax ATTRIBUTE-SYNTAX
+#            IA5String
+#            MATCHES FOR EQUALITY
+#
+#
+#    NRSInformationSyntax ATTRIBUTE-SYNTAX
+#            NRSInformation
+#            MATCHES FOR EQUALITY
+#
+#
+#    NRSInformation ::=  SET {
+#                    [0] Context,
+#                    [1] Address-space-id,
+#                    routes [2] SEQUENCE OF SEQUENCE {
+#                    Route-cost,
+#                    Addressing-info }
+#            }
+#
+#
+#    -- Upper bounds on length of attribute values
+#
+#
+#    ub-document-identifier INTEGER ::= 256
+#
+#    ub-document-location INTEGER ::= 256
+#
+#    ub-document-title INTEGER ::= 256
+#
+#    ub-document-version INTEGER ::= 256
+#
+#    ub-favourite-drink INTEGER ::= 256
+#
+#    ub-host INTEGER ::= 256
+#
+#    ub-information INTEGER ::= 2048
+#
+#    ub-unique-identifier INTEGER ::= 256
+#
+#    ub-personal-title INTEGER ::= 256
+#
+#    ub-photo INTEGER ::= 250000
+#
+#    ub-rfc822-mailbox INTEGER ::= 256
+#
+#    ub-room-number INTEGER ::= 256
+#
+#    ub-text-or-address INTEGER ::= 256
+#
+#    ub-user-class INTEGER ::= 256
+#
+#    ub-user-identifier INTEGER ::= 256
+#
+#    ub-organizational-status INTEGER ::= 256
+#
+#    ub-janet-mailbox INTEGER ::= 256
+#
+#    ub-building-name INTEGER ::= 256
+#
+#    ub-personal-signature ::= 50000
+#
+#    ub-audio INTEGER ::= 250000
+#
+# [remainder of memo trimmed]
+
diff --git a/old_trunk/schema-extras/src/main/schema/dhcp.schema b/old_trunk/schema-extras/src/main/schema/dhcp.schema
new file mode 100644
index 0000000..bcafc02
--- /dev/null
+++ b/old_trunk/schema-extras/src/main/schema/dhcp.schema
@@ -0,0 +1,359 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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. 
+
+attributetype ( 2.16.840.1.113719.1.203.4.1 
+	NAME 'dhcpPrimaryDN' 
+	DESC 'The DN of the dhcpServer which is the primary server for the configuration.' 
+	EQUALITY distinguishedNameMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 SINGLE-VALUE )
+
+attributetype ( 2.16.840.1.113719.1.203.4.2 
+	NAME 'dhcpSecondaryDN' 
+	DESC 'The DN of dhcpServer(s) which provide backup service for the configuration.'
+	EQUALITY distinguishedNameMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
+
+attributetype ( 2.16.840.1.113719.1.203.4.3 
+	NAME 'dhcpStatements' 
+	DESC 'Flexible storage for specific data depending on what object this exists in. Like conditional statements, server parameters, etc. This allows the standard to evolve without needing to adjust the schema.' 
+	EQUALITY caseIgnoreIA5Match
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+
+attributetype ( 2.16.840.1.113719.1.203.4.4 
+	NAME 'dhcpRange' 
+	DESC 'The starting & ending IP Addresses in the range (inclusive), separated by a hyphen; if the range only contains one address, then just the address can be specified with no hyphen.  Each range is defined as a separate value.'
+	EQUALITY caseIgnoreIA5Match
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+
+attributetype ( 2.16.840.1.113719.1.203.4.5 
+	NAME 'dhcpPermitList' 
+	DESC 'This attribute contains the permit lists associated with a pool. Each permit list is defined as a separate value.' 
+	EQUALITY caseIgnoreIA5Match
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+
+attributetype ( 2.16.840.1.113719.1.203.4.6 
+	NAME 'dhcpNetMask' 
+	DESC 'The subnet mask length for the subnet.  The mask can be easily computed from this length.' 
+	EQUALITY integerMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 2.16.840.1.113719.1.203.4.7 
+	NAME 'dhcpOption' 
+	DESC 'Encoded option values to be sent to clients.  Each value represents a single option and contains (OptionTag, Length, OptionValue) encoded in the format used by DHCP.' 
+	EQUALITY caseIgnoreIA5Match
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )
+
+attributetype ( 2.16.840.1.113719.1.203.4.8 
+	NAME 'dhcpClassData' 
+	DESC 'Encoded text string or list of bytes expressed in hexadecimal, separated by colons.  Clients match subclasses based on matching the class data with the results of match or spawn with statements in the class name declarations.' 
+	EQUALITY caseIgnoreIA5Match
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+
+attributetype ( 2.16.840.1.113719.1.203.4.9 
+	NAME 'dhcpOptionsDN' 
+	DESC 'The distinguished name(s) of the dhcpOption objects containing the configuration options provided by the server.' 
+	EQUALITY distinguishedNameMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
+
+attributetype ( 2.16.840.1.113719.1.203.4.10 
+	NAME 'dhcpHostDN' 
+	DESC 'the distinguished name(s) of the dhcpHost objects.' 
+	EQUALITY distinguishedNameMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 ) 
+
+attributetype ( 2.16.840.1.113719.1.203.4.11 
+	NAME 'dhcpPoolDN' 
+	DESC 'The distinguished name(s) of pools.' 
+	EQUALITY distinguishedNameMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
+
+attributetype ( 2.16.840.1.113719.1.203.4.12 
+	NAME 'dhcpGroupDN' 
+	DESC 'The distinguished name(s)   of the groups.' 
+	EQUALITY distinguishedNameMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
+
+attributetype ( 2.16.840.1.113719.1.203.4.13 
+	NAME 'dhcpSubnetDN' 
+	DESC 'The distinguished name(s) of the subnets.' 
+	EQUALITY distinguishedNameMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
+
+attributetype ( 2.16.840.1.113719.1.203.4.14 
+	NAME 'dhcpLeaseDN' 
+	DESC 'The distinguished name of a client address.' 
+	EQUALITY distinguishedNameMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 SINGLE-VALUE)
+
+attributetype ( 2.16.840.1.113719.1.203.4.15 NAME 'dhcpLeasesDN' 
+	DESC 'The distinguished name(s) client addresses.' 
+	EQUALITY distinguishedNameMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
+
+attributetype ( 2.16.840.1.113719.1.203.4.16 
+	NAME 'dhcpClassesDN' 
+	DESC 'The distinguished name(s) of a class(es) in a subclass.' 
+	EQUALITY distinguishedNameMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
+
+attributetype ( 2.16.840.1.113719.1.203.4.17 
+	NAME 'dhcpSubclassesDN' 
+	DESC 'The distinguished name(s) of subclass(es).' 
+	EQUALITY distinguishedNameMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
+
+attributetype ( 2.16.840.1.113719.1.203.4.18 
+	NAME 'dhcpSharedNetworkDN' 
+	DESC 'The distinguished name(s) of sharedNetworks.' 
+	EQUALITY distinguishedNameMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
+
+attributetype ( 2.16.840.1.113719.1.203.4.19 
+	NAME 'dhcpServiceDN' 
+	DESC 'The DN of dhcpService object(s)which contain the configuration information. Each dhcpServer object has this attribute identifying the DHCP configuration(s) that the server is associated with.' 
+	EQUALITY distinguishedNameMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
+
+attributetype ( 2.16.840.1.113719.1.203.4.20 
+	NAME 'dhcpVersion' DESC 'The version attribute of this object.' 
+	EQUALITY caseIgnoreIA5Match
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+
+attributetype ( 2.16.840.1.113719.1.203.4.21 
+	NAME 'dhcpImplementation' 
+	DESC 'Description of the DHCP Server implementation e.g. DHCP Servers vendor.' 
+	EQUALITY caseIgnoreIA5Match
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+
+attributetype ( 2.16.840.1.113719.1.203.4.22 
+	NAME 'dhcpAddressState' 
+	DESC 'This stores information about the current binding-status of an address.  For dynamic addresses managed by DHCP, the values should be restricted to the following: "FREE", "ACTIVE", "EXPIRED", "RELEASED", "RESET", "ABANDONED", "BACKUP".  For other addresses, it SHOULD be one of the following: "UNKNOWN", "RESERVED" (an address that is managed by DHCP that is reserved for a specific client), "RESERVED-ACTIVE" (same as reserved, but address is currently in use), "ASSIGNED" (assigned manually or by some other mechanism), "UNASSIGNED", "NOTASSIGNABLE".'
+	EQUALITY caseIgnoreIA5Match
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+
+attributetype ( 2.16.840.1.113719.1.203.4.23 
+	NAME 'dhcpExpirationTime' 
+	DESC 'This is the time the current lease for an address expires.' 
+	EQUALITY integerMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 SINGLE-VALUE )
+
+attributetype ( 2.16.840.1.113719.1.203.4.24 
+	NAME 'dhcpStartTimeOfState' 
+	DESC 'This is the time of the last state change for a leased address.' 
+	EQUALITY integerMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 SINGLE-VALUE )
+
+attributetype ( 2.16.840.1.113719.1.203.4.25 
+	NAME 'dhcpLastTransactionTime' 
+	DESC 'This is the last time a valid DHCP packet was received from the client.'
+	EQUALITY integerMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 SINGLE-VALUE )
+
+attributetype ( 2.16.840.1.113719.1.203.4.26 
+	NAME 'dhcpBootpFlag' 
+	DESC 'This indicates whether the address was assigned via BOOTP.' 
+	EQUALITY integerMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE )
+
+attributetype ( 2.16.840.1.113719.1.203.4.27 
+	NAME 'dhcpDomainName' 
+	DESC 'This is the name of the domain sent to the client by the server.  It is essentially the same as the value for DHCP option 15 sent to the client, and represents only the domain - not the full FQDN.  To obtain the full FQDN assigned to the client you must prepend the "dhcpAssignedHostName" to this value with a ".".' 
+	EQUALITY caseIgnoreIA5Match
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+
+attributetype ( 2.16.840.1.113719.1.203.4.28 
+	NAME 'dhcpDnsStatus' 
+	DESC 'This indicates the status of updating DNS resource records on behalf of the client by the DHCP server for this address.  The value is a 16-bit bitmask.'
+	EQUALITY integerMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 2.16.840.1.113719.1.203.4.29 
+	NAME 'dhcpRequestedHostName' 
+	DESC 'This is the hostname that was requested by the client.' 
+	EQUALITY caseIgnoreIA5Match
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+
+attributetype ( 2.16.840.1.113719.1.203.4.30 
+	NAME 'dhcpAssignedHostName' 
+	DESC 'This is the actual hostname that was assigned to a client. It may not be the name that was requested by the client.  The fully qualified domain name can be determined by appending the value of "dhcpDomainName" (with a dot separator) to this name.' 
+	EQUALITY caseIgnoreIA5Match
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+
+attributetype ( 2.16.840.1.113719.1.203.4.31 
+	NAME 'dhcpReservedForClient' 
+	DESC 'The distinguished name of a "dhcpClient" that an address is reserved for.  This may not be the same as the "dhcpAssignedToClient" attribute if the address is being reassigned but the current lease has not yet expired.'
+	EQUALITY distinguishedNameMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 SINGLE-VALUE )
+
+attributetype ( 2.16.840.1.113719.1.203.4.32 
+	NAME 'dhcpAssignedToClient' 
+	DESC 'This is the distinguished name of a "dhcpClient" that an address is currently assigned to.  This attribute is only present in the class when the address is leased.' 
+	EQUALITY distinguishedNameMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 SINGLE-VALUE )
+
+attributetype ( 2.16.840.1.113719.1.203.4.33 
+	NAME 'dhcpRelayAgentInfo' 
+	DESC 'If the client request was received via a relay agent, this contains information about the relay agent that was available from the DHCP request.  This is a hex-encoded option value.' 
+	EQUALITY caseIgnoreIA5Match
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 SINGLE-VALUE )
+
+attributetype ( 2.16.840.1.113719.1.203.4.34 
+	NAME 'dhcpHWAddress' 
+	DESC 'The clients hardware address that requested this IP address.' 
+	EQUALITY caseIgnoreIA5Match
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 SINGLE-VALUE )
+
+attributetype ( 2.16.840.1.113719.1.203.4.35 
+	NAME 'dhcpHashBucketAssignment' 
+	DESC 'HashBucketAssignment bit map for the DHCP Server, as defined in DHC Load Balancing Algorithm [RFC 3074].' 
+	EQUALITY caseIgnoreIA5Match
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 SINGLE-VALUE )
+
+attributetype ( 2.16.840.1.113719.1.203.4.36 
+	NAME 'dhcpDelayedServiceParameter' 
+	DESC 'Delay in seconds corresponding to Delayed Service Parameter configuration, as defined in  DHC Load Balancing Algorithm [RFC 3074]. '
+	EQUALITY integerMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 2.16.840.1.113719.1.203.4.37 
+	NAME 'dhcpMaxClientLeadTime' 
+	DESC 'Maximum Client Lead Time configuration in seconds, as defined in DHCP Failover Protocol [FAILOVR]' 
+	EQUALITY integerMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 2.16.840.1.113719.1.203.4.38 
+	NAME 'dhcpFailOverEndpointState' 
+	DESC 'Server (Failover Endpoint) state, as defined in DHCP Failover Protocol [FAILOVR]' 
+	EQUALITY integerMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+
+attributetype ( 2.16.840.1.113719.1.203.4.39 
+	NAME 'dhcpErrorLog' 
+	DESC 'Generic error log attribute that allows logging error conditions within a dhcpService or a dhcpSubnet, like no IP addresses available for lease.'
+	EQUALITY caseIgnoreIA5Match
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+
+# Classes
+
+objectclass ( 2.16.840.1.113719.1.203.6.1 
+	NAME 'dhcpService' 
+	DESC 'Service object that represents the actual DHCP Service configuration. This is a container object.' 
+	SUP top 
+	MUST (cn $ dhcpPrimaryDN) 
+	MAY ( dhcpSecondaryDN $ dhcpSharedNetworkDN $ dhcpSubnetDN $ 
+		dhcpGroupDN $ dhcpHostDN $  dhcpClassesDN $ dhcpOptionsDN $ 
+		dhcpStatements ) )
+
+objectclass ( 2.16.840.1.113719.1.203.6.2 
+	NAME 'dhcpSharedNetwork' 
+	DESC 'This stores configuration information for a shared network.' 
+	SUP top 
+	MUST cn 
+	MAY ( dhcpSubnetDN $ dhcpPoolDN $ dhcpOptionsDN $ dhcpStatements) 
+	)
+
+objectclass ( 2.16.840.1.113719.1.203.6.3 
+	NAME 'dhcpSubnet' 
+	DESC 'This class defines a subnet. This is a container object.' 
+	SUP top 
+	MUST ( cn $ dhcpNetMask ) 
+	MAY ( dhcpRange $ dhcpPoolDN $ dhcpGroupDN $ dhcpHostDN $ 
+		dhcpClassesDN $ dhcpLeasesDN $ dhcpOptionsDN $ dhcpStatements) 
+	 )
+
+objectclass ( 2.16.840.1.113719.1.203.6.4 
+	NAME 'dhcpPool' 
+	DESC 'This stores configuration information about a pool.' 
+	SUP top 
+	MUST ( cn $ dhcpRange ) 
+	MAY (dhcpClassesDN $ dhcpPermitList $ dhcpLeasesDN $ dhcpOptionsDN $ 
+		dhcpStatements) 
+	)
+
+objectclass ( 2.16.840.1.113719.1.203.6.5 
+	NAME 'dhcpGroup' 
+	DESC 'Group object that lists host DNs and parameters. This is a container object.' 
+	SUP top 
+	MUST cn 
+	MAY ( dhcpHostDN $ dhcpOptionsDN $ dhcpStatements ) 
+	)
+
+objectclass ( 2.16.840.1.113719.1.203.6.6 
+	NAME 'dhcpHost' 
+	DESC 'This represents information about a particular client' 
+	SUP top 
+	MUST cn 
+	MAY  (dhcpLeaseDN $ dhcpHWAddress $ dhcpOptionsDN $ dhcpStatements) 
+	)
+
+objectclass ( 2.16.840.1.113719.1.203.6.7 
+	NAME 'dhcpClass' 
+	DESC 'Represents information about a collection of related clients.' 
+	SUP top 
+	MUST cn 
+	MAY (dhcpSubClassesDN $ dhcpOptionsDN $ dhcpStatements) 
+	)
+
+objectclass ( 2.16.840.1.113719.1.203.6.8 
+	NAME 'dhcpSubClass' 
+	DESC 'Represents information about a collection of related classes.' 
+	SUP top 
+	MUST cn 
+	MAY (dhcpClassData $ dhcpOptionsDN $ dhcpStatements) 
+	) 
+
+objectclass ( 2.16.840.1.113719.1.203.6.9 
+	NAME 'dhcpOptions' 
+	DESC 'Represents information about a collection of options defined.' 
+	SUP top 
+        AUXILIARY
+	MUST cn 
+	MAY ( dhcpOption ) 
+        )
+
+objectclass ( 2.16.840.1.113719.1.203.6.10 
+	NAME 'dhcpLeases' 
+	DESC 'This class represents an IP Address, which may or may not have been leased.' 
+	SUP top 
+	MUST ( cn $ dhcpAddressState ) 
+	MAY ( dhcpExpirationTime $ dhcpStartTimeOfState $ 
+		dhcpLastTransactionTime $ dhcpBootpFlag $ dhcpDomainName $ 
+		dhcpDnsStatus $ dhcpRequestedHostName $ dhcpAssignedHostName $ 
+		dhcpReservedForClient $ dhcpAssignedToClient $ 
+		dhcpRelayAgentInfo $ dhcpHWAddress ) 
+	)
+
+objectclass ( 2.16.840.1.113719.1.203.6.11 
+	NAME 'dhcpLog' 
+	DESC 'This is the object that holds past information about the IP address. The cn is the time/date stamp when the address was assigned or released, the address state at the time, if the address was assigned or released.' 
+	SUP top 
+	MUST ( cn ) 
+	MAY ( dhcpAddressState $ dhcpExpirationTime $ dhcpStartTimeOfState $ 
+		dhcpLastTransactionTime $ dhcpBootpFlag $ dhcpDomainName $ 
+		dhcpDnsStatus $ dhcpRequestedHostName $ dhcpAssignedHostName $ 
+		dhcpReservedForClient $ dhcpAssignedToClient $ 
+		dhcpRelayAgentInfo $ dhcpHWAddress $ dhcpErrorLog) 
+	)
+
+objectclass ( 2.16.840.1.113719.1.203.6.12 
+	NAME 'dhcpServer' 
+	DESC 'DHCP Server Object' 
+	SUP top 
+	MUST (cn $ dhcpServiceDN) 
+	MAY (dhcpVersion $ dhcpImplementation $ dhcpHashBucketAssignment $ dhcpDelayedServiceParameter $ dhcpMaxClientLeadTime $ dhcpFailOverEndpointState $ dhcpStatements)
+	)
+
diff --git a/old_trunk/schema-extras/src/main/schema/inetorgperson.schema b/old_trunk/schema-extras/src/main/schema/inetorgperson.schema
new file mode 100644
index 0000000..6c333d4
--- /dev/null
+++ b/old_trunk/schema-extras/src/main/schema/inetorgperson.schema
@@ -0,0 +1,159 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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. 
+
+# $OpenLDAP: pkg/ldap/servers/slapd/schema/inetorgperson.schema,v 1.14 2001/12/05 22:16:36 kurt Exp $
+#
+# InetOrgPerson (RFC2798)
+#
+# Depends upon
+#   Definition of an X.500 Attribute Type and an Object Class to Hold
+#   Uniform Resource Identifiers (URIs) [RFC2079]
+#	(core.schema)
+#
+#   A Summary of the X.500(96) User Schema for use with LDAPv3 [RFC2256]
+#	(core.schema)
+#
+#   The COSINE and Internet X.500 Schema [RFC1274] (cosine.schema)
+
+# carLicense
+# This multivalued field is used to record the values of the license or
+# registration plate associated with an individual.
+attributetype ( 2.16.840.1.113730.3.1.1
+	NAME 'carLicense'
+	DESC 'RFC2798: vehicle license or registration plate'
+	EQUALITY caseIgnoreMatch
+	SUBSTR caseIgnoreSubstringsMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+
+# departmentNumber
+# Code for department to which a person belongs.  This can also be
+# strictly numeric (e.g., 1234) or alphanumeric (e.g., ABC/123).
+attributetype ( 2.16.840.1.113730.3.1.2
+	NAME 'departmentNumber'
+	DESC 'RFC2798: identifies a department within an organization'
+	EQUALITY caseIgnoreMatch
+	SUBSTR caseIgnoreSubstringsMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+
+# displayName
+# When displaying an entry, especially within a one-line summary list, it
+# is useful to be able to identify a name to be used.  Since other attri-
+# bute types such as 'cn' are multivalued, an additional attribute type is
+# needed.  Display name is defined for this purpose.
+attributetype ( 2.16.840.1.113730.3.1.241
+	NAME 'displayName'
+	DESC 'RFC2798: preferred name to be used when displaying entries'
+	EQUALITY caseIgnoreMatch
+	SUBSTR caseIgnoreSubstringsMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+	SINGLE-VALUE )
+
+# employeeNumber
+# Numeric or alphanumeric identifier assigned to a person, typically based
+# on order of hire or association with an organization.  Single valued.
+attributetype ( 2.16.840.1.113730.3.1.3
+	NAME 'employeeNumber'
+	DESC 'RFC2798: numerically identifies an employee within an organization'
+	EQUALITY caseIgnoreMatch
+	SUBSTR caseIgnoreSubstringsMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+	SINGLE-VALUE )
+
+# employeeType
+# Used to identify the employer to employee relationship.  Typical values
+# used will be "Contractor", "Employee", "Intern", "Temp", "External", and
+# "Unknown" but any value may be used.
+attributetype ( 2.16.840.1.113730.3.1.4
+	NAME 'employeeType'
+	DESC 'RFC2798: type of employment for a person'
+	EQUALITY caseIgnoreMatch
+	SUBSTR caseIgnoreSubstringsMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+
+# jpegPhoto
+# Used to store one or more images of a person using the JPEG File
+# Interchange Format [JFIF].
+# Note that the jpegPhoto attribute type was defined for use in the
+# Internet X.500 pilots but no referencable definition for it could be
+# located.
+attributetype ( 0.9.2342.19200300.100.1.60
+	NAME 'jpegPhoto'
+	DESC 'RFC2798: a JPEG image'
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.28 )
+
+# preferredLanguage
+# Used to indicate an individual's preferred written or spoken
+# language.  This is useful for international correspondence or human-
+# computer interaction.  Values for this attribute type MUST conform to
+# the definition of the Accept-Language header field defined in
+# [RFC2068] with one exception:  the sequence "Accept-Language" ":"
+# should be omitted.  This is a single valued attribute type.
+attributetype ( 2.16.840.1.113730.3.1.39
+	NAME 'preferredLanguage'
+	DESC 'RFC2798: preferred written or spoken language for a person'
+	EQUALITY caseIgnoreMatch
+	SUBSTR caseIgnoreSubstringsMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+	SINGLE-VALUE )
+
+# userSMIMECertificate
+# A PKCS#7 [RFC2315] SignedData, where the content that is signed is
+# ignored by consumers of userSMIMECertificate values.  It is
+# recommended that values have a `contentType' of data with an absent
+# `content' field.  Values of this attribute contain a person's entire
+# certificate chain and an smimeCapabilities field [RFC2633] that at a
+# minimum describes their SMIME algorithm capabilities.  Values for
+# this attribute are to be stored and requested in binary form, as
+# 'userSMIMECertificate;binary'.  If available, this attribute is
+# preferred over the userCertificate attribute for S/MIME applications.
+## OpenLDAP note: ";binary" transfer should NOT be used as syntax is binary
+attributetype ( 2.16.840.1.113730.3.1.40
+	NAME 'userSMIMECertificate'
+	DESC 'RFC2798: PKCS#7 SignedData used to support S/MIME'
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.5 )
+
+# userPKCS12
+# PKCS #12 [PKCS12] provides a format for exchange of personal identity
+# information.  When such information is stored in a directory service,
+# the userPKCS12 attribute should be used. This attribute is to be stored
+# and requested in binary form, as 'userPKCS12;binary'.  The attribute
+# values are PFX PDUs stored as binary data.
+## OpenLDAP note: ";binary" transfer should NOT be used as syntax is binary
+attributetype ( 2.16.840.1.113730.3.1.216
+	NAME 'userPKCS12'
+	DESC 'RFC2798: personal identity information, a PKCS #12 PFX'
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.5 )
+
+
+# inetOrgPerson
+# The inetOrgPerson represents people who are associated with an
+# organization in some way.  It is a structural class and is derived
+# from the organizationalPerson which is defined in X.521 [X521].
+objectclass	( 2.16.840.1.113730.3.2.2
+    NAME 'inetOrgPerson'
+	DESC 'RFC2798: Internet Organizational Person'
+    SUP organizationalPerson
+    STRUCTURAL
+	MAY (
+		audio $ businessCategory $ carLicense $ departmentNumber $
+		displayName $ employeeNumber $ employeeType $ givenName $
+		homePhone $ homePostalAddress $ initials $ jpegPhoto $
+		labeledURI $ mail $ manager $ mobile $ o $ pager $
+		photo $ roomNumber $ secretary $ uid $ userCertificate $
+		x500uniqueIdentifier $ preferredLanguage $
+		userSMIMECertificate $ userPKCS12 )
+	)
diff --git a/old_trunk/schema-extras/src/main/schema/java.schema b/old_trunk/schema-extras/src/main/schema/java.schema
new file mode 100644
index 0000000..58c9f1f
--- /dev/null
+++ b/old_trunk/schema-extras/src/main/schema/java.schema
@@ -0,0 +1,405 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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. 
+
+# Java Object Schema
+# $OpenLDAP: pkg/ldap/servers/slapd/schema/java.schema,v 1.3 2002/01/04 20:17:55 kurt Exp $
+# depends upon core.schema
+
+# Network Working Group                                            V. Ryan
+# Request for Comments: 2713                                   S. Seligman
+# Category: Informational                                           R. Lee
+#                                                   Sun Microsystems, Inc.
+#                                                             October 1999
+# 
+# 
+#      Schema for Representing Java(tm) Objects in an LDAP Directory
+# 
+# Status of this Memo
+# 
+#    This memo provides information for the Internet community.  It does
+#    not specify an Internet standard of any kind.  Distribution of this
+#    memo is unlimited.
+# 
+# Copyright Notice
+# 
+#    Copyright (C) The Internet Society (1999).  All Rights Reserved.
+# 
+# Abstract
+# 
+#    This document defines the schema for representing Java(tm) objects in
+#    an LDAP directory [LDAPv3].  It defines schema elements to represent
+#    a Java serialized object [Serial], a Java marshalled object [RMI], a
+#    Java remote object [RMI], and a JNDI reference [JNDI].
+# 
+
+# [trimmed]
+
+# 3 Attribute Type Definitions
+# 
+#    The following attribute types are defined in this document:
+# 
+#        javaClassName
+#        javaClassNames
+#        javaCodebase
+#        javaSerializedData
+#        javaFactory
+#        javaReferenceAddress
+#        javaDoc
+# 
+# 3.1 javaClassName
+# 
+#    This attribute stores the fully qualified name of the Java object's
+#    "distinguished" class or interface (for example, "java.lang.String").
+#    It is a single-valued attribute. This attribute's syntax is '
+#    Directory String' and its case is significant.
+# 
+#        ( 1.3.6.1.4.1.42.2.27.4.1.6
+#          NAME 'javaClassName'
+#          DESC 'Fully qualified name of distinguished Java class or
+#                interface'
+#          EQUALITY caseExactMatch
+#          SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+#          SINGLE-VALUE
+#        )
+# 
+attributetype ( 1.3.6.1.4.1.42.2.27.4.1.6
+	NAME 'javaClassName'
+	DESC 'Fully qualified name of distinguished Java class or interface'
+	EQUALITY caseExactMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+	SINGLE-VALUE )
+ 
+# 3.2 javaCodebase
+# 
+#    This attribute stores the Java class definition's locations.  It
+#    specifies the locations from which to load the class definition for
+#    the class specified by the javaClassName attribute.  Each value of
+#    the attribute contains an ordered list of URLs, separated by spaces.
+#    For example, a value of "url1 url2 url3" means that the three
+#    (possibly interdependent) URLs (url1, url2, and url3) form the
+#    codebase for loading in the Java class definition.
+# 
+#    If the javaCodebase attribute contains more than one value, each
+#    value is an independent codebase. That is, there is no relationship
+#    between the URLs in one value and those in another; each value can be
+#    viewed as an alternate source for loading the Java class definition.
+#    See [Java] for information regarding class loading.
+# 
+#    This attribute's syntax is 'IA5 String' and its case is significant.
+# 
+#        ( 1.3.6.1.4.1.42.2.27.4.1.7
+#          NAME 'javaCodebase'
+#          DESC 'URL(s) specifying the location of class definition'
+#          EQUALITY caseExactIA5Match
+#          SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+#        )
+# 
+attributetype ( 1.3.6.1.4.1.42.2.27.4.1.7
+	NAME 'javaCodebase'
+	DESC 'URL(s) specifying the location of class definition'
+	EQUALITY caseExactIA5Match
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+
+# 3.3 javaClassNames
+# 
+#    This attribute stores the Java object's fully qualified class or
+#    interface names (for example, "java.lang.String").  It is a
+#    multivalued attribute. When more than one value is present, each is
+#    the name of a class or interface, or ancestor class or interface, of
+#    this object.
+# 
+#    This attribute's syntax is 'Directory String' and its case is
+#    significant.
+# 
+#        ( 1.3.6.1.4.1.42.2.27.4.1.13
+#          NAME 'javaClassNames'
+#          DESC 'Fully qualified Java class or interface name'
+#          EQUALITY caseExactMatch
+#          SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+#        )
+# 
+# 
+attributetype ( 1.3.6.1.4.1.42.2.27.4.1.13
+	NAME 'javaClassNames'
+	DESC 'Fully qualified Java class or interface name'
+	EQUALITY caseExactMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+ 
+# 3.4 javaSerializedData
+# 
+#    This attribute stores the serialized form of a Java object.  The
+#    serialized form is described in [Serial].
+# 
+#    This attribute's syntax is 'Octet String'.
+# 
+#        ( 1.3.6.1.4.1.42.2.27.4.1.8
+#          NAME 'javaSerializedData
+#          DESC 'Serialized form of a Java object'
+#          SYNTAX 1.3.6.1.4.1.1466.115.121.1.40
+#          SINGLE-VALUE
+#        )
+# 
+attributetype ( 1.3.6.1.4.1.42.2.27.4.1.8
+	NAME 'javaSerializedData'
+	DESC 'Serialized form of a Java object'
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.40
+	SINGLE-VALUE )
+
+# 3.5 javaFactory
+# 
+#    This attribute stores the fully qualified class name of the object
+#    factory (for example, "com.wiz.jndi.WizObjectFactory") that can be
+#    used to create an instance of the object identified by the
+#    javaClassName attribute.
+# 
+#    This attribute's syntax is 'Directory String' and its case is
+#    significant.
+# 
+#        ( 1.3.6.1.4.1.42.2.27.4.1.10
+#          NAME 'javaFactory'
+#          DESC 'Fully qualified Java class name of a JNDI object factory'
+#          EQUALITY caseExactMatch
+#          SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+#          SINGLE-VALUE
+#        )
+# 
+attributetype ( 1.3.6.1.4.1.42.2.27.4.1.10
+	NAME 'javaFactory'
+	DESC 'Fully qualified Java class name of a JNDI object factory'
+	EQUALITY caseExactMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+	SINGLE-VALUE )
+
+# 3.6 javaReferenceAddress
+# 
+#    This attribute represents the sequence of addresses of a JNDI
+#    reference.  Each of its values represents one address, a Java object
+#    of type javax.naming.RefAddr.  Its value is a concatenation of the
+#    address type and address contents, preceded by a sequence number (the
+#    order of addresses in a JNDI reference is significant).  For example:
+# 
+#        #0#TypeA#ValA
+#        #1#TypeB#ValB
+#        #2#TypeC##rO0ABXNyABpq...
+# 
+#    In more detail, the value is encoded as follows:
+# 
+#    The delimiter is the first character of the value.  For readability
+#    the character '#' is recommended when it is not otherwise used
+#    anywhere in the value, but any character may be used subject to
+#    restrictions given below.
+# 
+#    The first delimiter is followed by the sequence number.  The sequence
+#    number of an address is its position in the JNDI reference, with the
+#    first address being numbered 0.  It is represented by its shortest
+#    string form, in decimal notation.
+# 
+#    The sequence number is followed by a delimiter, then by the address
+#    type, and then by another delimiter.  If the address is of Java class
+#    javax.naming.StringRefAddr, then this delimiter is followed by the
+#    value of the address contents (which is a string).  Otherwise, this
+#    delimiter is followed immediately by another delimiter, and then by
+#    the Base64 encoding of the serialized form of the entire address.
+# 
+#    The delimiter may be any character other than a digit or a character
+#    contained in the address type.  In addition, if the address contents
+#    is a string, the delimiter may not be the first character of that
+#    string.
+# 
+#    This attribute's syntax is 'Directory String' and its case is
+#    significant.  It can contain multiple values.
+# 
+#        ( 1.3.6.1.4.1.42.2.27.4.1.11
+#          NAME 'javaReferenceAddress'
+#          DESC 'Addresses associated with a JNDI Reference'
+#          EQUALITY caseExactMatch
+#          SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+#        )
+# 
+attributetype ( 1.3.6.1.4.1.42.2.27.4.1.11
+	NAME 'javaReferenceAddress'
+	DESC 'Addresses associated with a JNDI Reference'
+	EQUALITY caseExactMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+
+# 3.7 javaDoc
+# 
+#    This attribute stores a pointer to the Java documentation for the
+#    class.  It's value is a URL. For example, the following URL points to
+#    the specification of the java.lang.String class:
+#    http://java.sun.com/products/jdk/1.2/docs/api/java/lang/String.html
+# 
+#    This attribute's syntax is 'IA5 String' and its case is significant.
+# 
+#        ( 1.3.6.1.4.1.42.2.27.4.1.12
+#          NAME 'javaDoc'
+#          DESC 'The Java documentation for the class'
+#          EQUALITY caseExactIA5Match
+#          SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+#        )
+# 
+attributetype ( 1.3.6.1.4.1.42.2.27.4.1.12
+	NAME 'javaDoc'
+	DESC 'The Java documentation for the class'
+	EQUALITY caseExactIA5Match
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+
+# 4 Object Class Definitions
+# 
+#    The following object classes are defined in this document:
+# 
+#        javaContainer
+#        javaObject
+#        javaSerializedObject
+#        javaMarshalledObject
+#        javaNamingReference
+# 
+# 4.1 javaContainer
+# 
+#    This structural object class represents a container for a Java
+#    object.
+# 
+#        ( 1.3.6.1.4.1.42.2.27.4.2.1
+#          NAME 'javaContainer'
+#          DESC 'Container for a Java object'
+#          SUP top
+#          STRUCTURAL
+#          MUST ( cn )
+#        )
+# 
+objectclass ( 1.3.6.1.4.1.42.2.27.4.2.1
+	NAME 'javaContainer'
+	DESC 'Container for a Java object'
+	SUP top
+	STRUCTURAL
+	MUST cn )
+
+# 4.2 javaObject
+# 
+#    This abstract object class represents a Java object.  A javaObject
+#    cannot exist in the directory; only auxiliary or structural
+#    subclasses of it can exist in the directory.
+# 
+#        ( 1.3.6.1.4.1.42.2.27.4.2.4
+#          NAME 'javaObject'
+#          DESC 'Java object representation'
+#          SUP top
+#          ABSTRACT
+#          MUST ( javaClassName )
+#          MAY ( javaClassNames $
+#                javaCodebase $
+#                javaDoc $
+#                description )
+#        )
+# 
+objectclass ( 1.3.6.1.4.1.42.2.27.4.2.4
+	NAME 'javaObject'
+	DESC 'Java object representation'
+	SUP top
+	ABSTRACT
+	MUST javaClassName
+	MAY ( javaClassNames $ javaCodebase $
+		javaDoc $ description ) )
+
+# 4.3 javaSerializedObject
+# 
+#    This auxiliary object class represents a Java serialized object.  It
+#    must be mixed in with a structural object class.
+# 
+#        ( 1.3.6.1.4.1.42.2.27.4.2.5
+#          NAME 'javaSerializedObject'
+#          DESC 'Java serialized object'
+#          SUP javaObject
+#          AUXILIARY
+#          MUST ( javaSerializedData )
+#        )
+# 
+objectclass ( 1.3.6.1.4.1.42.2.27.4.2.5
+	NAME 'javaSerializedObject'
+	DESC 'Java serialized object'
+	SUP javaObject
+	AUXILIARY
+	MUST javaSerializedData )
+ 
+# 4.4 javaMarshalledObject
+# 
+#    This auxiliary object class represents a Java marshalled object.  It
+#    must be mixed in with a structural object class.
+# 
+#        ( 1.3.6.1.4.1.42.2.27.4.2.8
+#          NAME 'javaMarshalledObject'
+#          DESC 'Java marshalled object'
+#          SUP javaObject
+#          AUXILIARY
+#          MUST ( javaSerializedData )
+#        )
+# 
+objectclass ( 1.3.6.1.4.1.42.2.27.4.2.8
+	NAME 'javaMarshalledObject'
+	DESC 'Java marshalled object'
+	SUP javaObject
+	AUXILIARY
+	MUST javaSerializedData )
+
+# 4.5 javaNamingReference
+# 
+#    This auxiliary object class represents a JNDI reference.  It must be
+#    mixed in with a structural object class.
+# 
+#        ( 1.3.6.1.4.1.42.2.27.4.2.7
+#          NAME 'javaNamingReference'
+#          DESC 'JNDI reference'
+#          SUP javaObject
+#          AUXILIARY
+#          MAY ( javaReferenceAddress $
+#                javaFactory )
+#        )
+# 
+objectclass ( 1.3.6.1.4.1.42.2.27.4.2.7
+	NAME 'javaNamingReference'
+	DESC 'JNDI reference'
+	SUP javaObject
+	AUXILIARY
+	MAY ( javaReferenceAddress $ javaFactory ) )
+ 
+# Full Copyright Statement
+# 
+#    Copyright (C) The Internet Society (1999).  All Rights Reserved.
+# 
+#    This document and translations of it may be copied and furnished to
+#    others, and derivative works that comment on or otherwise explain it
+#    or assist in its implementation may be prepared, copied, published
+#    and distributed, in whole or in part, without restriction of any
+#    kind, provided that the above copyright notice and this paragraph are
+#    included on all such copies and derivative works.  However, this
+#    document itself may not be modified in any way, such as by removing
+#    the copyright notice or references to the Internet Society or other
+#    Internet organizations, except as needed for the purpose of
+#    developing Internet standards in which case the procedures for
+#    copyrights defined in the Internet Standards process must be
+#    followed, or as required to translate it into languages other than
+#    English.
+# 
+#    The limited permissions granted above are perpetual and will not be
+#    revoked by the Internet Society or its successors or assigns.
+# 
+#    This document and the information contained herein is provided on an
+#    "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+#    TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+#    BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+#    HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+#    MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
diff --git a/old_trunk/schema-extras/src/main/schema/krb5kdc.schema b/old_trunk/schema-extras/src/main/schema/krb5kdc.schema
new file mode 100644
index 0000000..3338eff
--- /dev/null
+++ b/old_trunk/schema-extras/src/main/schema/krb5kdc.schema
@@ -0,0 +1,169 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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. 
+
+# $Id: krb5-kdc.schema,v 1.1 2004/03/22 17:25:05 quanah Exp $
+# Definitions for a Kerberos V KDC schema
+
+# OID Base is iso(1) org(3) dod(6) internet(1) private(4) enterprise(1) padl(5322) kdcSchema(10)
+#
+# Syntaxes are under 1.3.6.1.4.1.5322.10.0
+# Attributes types are under 1.3.6.1.4.1.5322.10.1
+# Object classes are under 1.3.6.1.4.1.5322.10.2
+
+# Syntax definitions
+
+#krb5KDCFlagsSyntax SYNTAX ::= {
+#   WITH SYNTAX            INTEGER
+#--        initial(0),             -- require as-req
+#--        forwardable(1),         -- may issue forwardable
+#--        proxiable(2),           -- may issue proxiable
+#--        renewable(3),           -- may issue renewable
+#--        postdate(4),            -- may issue postdatable
+#--        server(5),              -- may be server
+#--        client(6),              -- may be client
+#--        invalid(7),             -- entry is invalid
+#--        require-preauth(8),     -- must use preauth
+#--        change-pw(9),           -- change password service
+#--        require-hwauth(10),     -- must use hwauth
+#--        ok-as-delegate(11),     -- as in TicketFlags
+#--        user-to-user(12),       -- may use user-to-user auth
+#--        immutable(13)           -- may not be deleted         
+#   ID                     { 1.3.6.1.4.1.5322.10.0.1 }
+#}
+
+#krb5PrincipalNameSyntax SYNTAX ::= {
+#   WITH SYNTAX            OCTET STRING
+#-- String representations of distinguished names as per RFC1510
+#   ID                     { 1.3.6.1.4.1.5322.10.0.2 }
+#}
+
+# Attribute type definitions
+ 
+attributetype ( 1.3.6.1.4.1.5322.10.1.1
+	NAME 'krb5PrincipalName'
+	DESC 'The unparsed Kerberos principal name'
+	EQUALITY caseExactIA5Match
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 
+	SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.5322.10.1.2
+	NAME 'krb5KeyVersionNumber'
+	EQUALITY integerMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
+	SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.5322.10.1.3
+	NAME 'krb5MaxLife'
+	EQUALITY integerMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 
+	SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.5322.10.1.4
+	NAME 'krb5MaxRenew'
+	EQUALITY integerMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 
+	SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.5322.10.1.5
+	NAME 'krb5KDCFlags'
+	EQUALITY integerMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 
+	SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.5322.10.1.6
+	NAME 'krb5EncryptionType'
+	EQUALITY integerMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )
+
+attributetype ( 1.3.6.1.4.1.5322.10.1.7
+	NAME 'krb5ValidStart'
+	EQUALITY generalizedTimeMatch
+	ORDERING generalizedTimeOrderingMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.24
+	SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.5322.10.1.8
+	NAME 'krb5ValidEnd'
+	EQUALITY generalizedTimeMatch
+	ORDERING generalizedTimeOrderingMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.24
+	SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.5322.10.1.9
+	NAME 'krb5PasswordEnd'
+	EQUALITY generalizedTimeMatch
+	ORDERING generalizedTimeOrderingMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.24
+	SINGLE-VALUE )
+
+# this is temporary; keys will eventually
+# be child entries or compound attributes.
+attributetype ( 1.3.6.1.4.1.5322.10.1.10
+	NAME 'krb5Key'
+	DESC 'Encoded ASN1 Key as an octet string'
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.5 )
+
+attributetype ( 1.3.6.1.4.1.5322.10.1.11
+	NAME 'krb5PrincipalRealm'
+	DESC 'Distinguished name of krb5Realm entry'
+	SUP distinguishedName )
+
+attributetype ( 1.3.6.1.4.1.5322.10.1.12
+	NAME 'krb5RealmName'
+	EQUALITY caseExactIA5Match
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+
+attributetype ( 1.3.6.1.4.1.5322.10.1.13
+  NAME 'krb5AccountDisabled'
+  EQUALITY booleanMatch
+  SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.5322.10.1.14
+  NAME 'krb5AccountLockedOut'
+  EQUALITY booleanMatch
+  SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.5322.10.1.15
+  NAME 'krb5AccountExpirationTime'
+  EQUALITY generalizedTimeMatch
+  SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 SINGLE-VALUE )
+
+# Object class definitions
+
+objectclass ( 1.3.6.1.4.1.5322.10.2.1
+	NAME 'krb5Principal'
+	SUP top
+	AUXILIARY
+	MUST ( krb5PrincipalName )
+	MAY ( cn $ krb5PrincipalRealm ) )
+
+objectclass ( 1.3.6.1.4.1.5322.10.2.2
+	NAME 'krb5KDCEntry'
+	SUP krb5Principal
+	AUXILIARY
+	MUST ( krb5KeyVersionNumber )
+	MAY ( krb5ValidStart $ krb5ValidEnd $ krb5PasswordEnd $
+              krb5MaxLife $ krb5MaxRenew $ krb5KDCFlags $
+              krb5EncryptionType $ krb5Key $ krb5AccountDisabled $
+              krb5AccountLockedOut $ krb5AccountExpirationTime ) )
+
+objectclass ( 1.3.6.1.4.1.5322.10.2.3
+	NAME 'krb5Realm'
+	SUP top
+	AUXILIARY
+	MUST ( krb5RealmName ) )
+
diff --git a/old_trunk/schema-extras/src/main/schema/misc.schema b/old_trunk/schema-extras/src/main/schema/misc.schema
new file mode 100644
index 0000000..67cfd33
--- /dev/null
+++ b/old_trunk/schema-extras/src/main/schema/misc.schema
@@ -0,0 +1,76 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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. 
+
+# $OpenLDAP: pkg/ldap/servers/slapd/schema/misc.schema,v 1.23.2.1 2003/02/15 15:44:08 kurt Exp $
+#
+# Assorted definitions from several sources, including
+# ''works in progress''.  Contents of this file are
+# subject to change (including deletion) without notice.
+#
+# Not recommended for production use!
+# Use with extreme caution!
+
+#
+# draft-lachman-laser-ldap-mail-routing-02.txt !!!EXPIRED!!!
+#
+attributetype ( 2.16.840.1.113730.3.1.13
+	NAME 'mailLocalAddress'
+	DESC 'RFC822 email address of this recipient'
+	EQUALITY caseIgnoreIA5Match
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} )
+
+attributetype ( 2.16.840.1.113730.3.1.18
+	NAME 'mailHost'
+	DESC 'FQDN of the SMTP/MTA of this recipient'
+	EQUALITY caseIgnoreIA5Match
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256}
+	SINGLE-VALUE )
+
+attributetype ( 2.16.840.1.113730.3.1.47
+	NAME 'mailRoutingAddress'
+	DESC 'RFC822 routing address of this recipient'
+	EQUALITY caseIgnoreIA5Match
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256}
+	SINGLE-VALUE )
+
+# I-D leaves this OID TBD.
+# iPlanet uses 2.16.840.1.113.730.3.2.147 but that is an
+# improperly delegated OID.  A typo is likely.
+objectclass ( 2.16.840.1.113730.3.2.147
+	NAME 'inetLocalMailRecipient'
+	DESC 'Internet local mail recipient'
+	SUP top AUXILIARY
+	MAY	( mailLocalAddress $ mailHost $ mailRoutingAddress ) )
+
+#
+# draft-srivastava-ldap-mail-00.txt !!!EXPIRED!!!
+#
+attributetype ( 1.3.6.1.4.1.42.2.27.2.1.15
+	NAME 'rfc822MailMember'
+	DESC 'rfc822 mail address of group member(s)'
+	EQUALITY caseIgnoreIA5Match
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+
+# 
+# !!!no I-D!!!
+#
+objectclass ( 1.3.6.1.4.1.42.2.27.1.2.5
+	NAME 'nisMailAlias'
+	DESC 'NIS mail alias'
+	SUP top STRUCTURAL
+	MUST cn
+	MAY rfc822MailMember )
diff --git a/old_trunk/schema-extras/src/main/schema/mozilla.schema b/old_trunk/schema-extras/src/main/schema/mozilla.schema
new file mode 100644
index 0000000..e63027a
--- /dev/null
+++ b/old_trunk/schema-extras/src/main/schema/mozilla.schema
@@ -0,0 +1,161 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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. 
+
+# created mozillaAbPersonObsolete schema
+# from mozillaOrgPerson schema v.0.6
+# for OpenLDAP Directory servers
+#
+# Changes from v.0.6
+# 
+# - Add additional attritute names to match those currently exported 
+#   from mozilla, except for 'mozilla_AimScreenName' which is invalid.
+#   I have found that mozilla does not find them otherwise.
+# - Set both versions of 'nsAIMid' to use the mozilla OID space.
+# - Make 'inetOrgPerson' the SUP for 'mozillaAbPersonObsolete'.
+
+# req. core
+# req. cosine
+# req. inetorgperson
+
+# attribute defs
+
+attributetype ( 1.3.6.1.4.1.13769.2.1.1 
+	NAME ( 'xmozillanickname' 'mozillaNickname' ) 
+	SUP name )
+
+attributetype ( 1.3.6.1.4.1.13769.2.1.2 
+	NAME ( 'xmozillausehtmlmail' 'mozillaUseHtmlMail' ) 
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 
+	SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.13769.2.1.3
+	NAME 'mozillaSecondEmail' 
+	EQUALITY caseIgnoreIA5Match
+	SUBSTR caseIgnoreIA5SubstringsMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} )
+
+attributetype ( 1.3.6.1.4.1.13769.2.1.4
+	NAME 'mozillaHomeLocalityName' 
+	EQUALITY caseIgnoreMatch
+	SUBSTR caseIgnoreSubstringsMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{128} )
+
+attributetype ( 1.3.6.1.4.1.13769.2.1.5 
+	NAME 'mozillaPostalAddress2'
+	EQUALITY caseIgnoreListMatch
+	SUBSTR caseIgnoreListSubstringsMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.41 )
+
+attributetype ( 1.3.6.1.4.1.13769.2.1.6 
+	NAME 'mozillaHomePostalAddress2'
+	EQUALITY caseIgnoreListMatch
+	SUBSTR caseIgnoreListSubstringsMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.41 )
+
+attributetype ( 1.3.6.1.4.1.13769.2.1.7 
+	NAME ( 'mozillaHomeState' ) SUP name )
+
+attributetype ( 1.3.6.1.4.1.13769.2.1.8 
+	NAME 'mozillaHomePostalCode'
+	EQUALITY caseIgnoreMatch
+	SUBSTR caseIgnoreSubstringsMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{40} )
+
+attributetype ( 1.3.6.1.4.1.13769.2.1.9 
+	NAME ( 'mozillaHomeCountryName' ) 
+	SUP name SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.13769.2.1.10
+	NAME ( 'mozillaHomeFriendlyCountryName' )
+	EQUALITY caseIgnoreMatch
+	SUBSTR caseIgnoreSubstringsMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+
+attributetype ( 1.3.6.1.4.1.13769.2.1.11
+	NAME ( 'homeurl' 'mozillaHomeUrl' )
+	EQUALITY caseIgnoreIA5Match
+	SUBSTR caseIgnoreIA5SubstringsMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} )
+
+attributetype ( 1.3.6.1.4.1.13769.2.1.12
+	NAME ( 'workurl' 'mozillaWorkUrl' )
+	EQUALITY caseIgnoreIA5Match
+	SUBSTR caseIgnoreIA5SubstringsMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} )
+
+# un-comment for all LDAP server NOT supporting SYNTAX 2.16.840.1.113730.3.7.1
+attributetype ( 1.3.6.1.4.1.13769.2.1.13
+	NAME ( 'nsAIMid' )
+	DESC 'AOL Instant Messenger (AIM) Identity'
+	EQUALITY telephoneNumberMatch
+	SUBSTR telephoneNumberSubstringsMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.50 )
+
+# un-comment for ... LDAP server supporting SYNTAX 2.16.840.1.113730.3.7.1
+#attributetype ( 1.3.6.1.4.1.13769.2.1.13
+#	NAME ( 'nsAIMid' )
+#	DESC 'AOL Instant Messenger (AIM) Identity'
+#	SYNTAX 2.16.840.1.113730.3.7.1 )
+
+attributetype ( 1.3.6.1.4.1.13769.2.1.96
+	NAME ( 'custom1' 'mozillaCustom1' )
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+	SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.13769.2.1.97
+	NAME ( 'custom2' 'mozillaCustom2' )
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+	SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.13769.2.1.98
+	NAME ( 'custom3' 'mozillaCustom3' )
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+	SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.13769.2.1.99
+	NAME ( 'custom4' 'mozillaCustom4' )
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+	SINGLE-VALUE )
+ 
+
+# objectClass defs 
+
+objectclass ( 1.3.6.1.4.1.13769.2.2.1
+	NAME 'mozillaAbPersonObsolete'
+	SUP inetOrgPerson
+	STRUCTURAL
+	MAY (
+	mozillaNickname $
+	mozillaUseHtmlMail $
+	mozillaSecondEmail $
+	mozillaPostalAddress2 $
+	mozillaHomePostalAddress2 $
+	mozillaHomeLocalityName $
+	mozillaHomeState $
+	mozillaHomePostalCode $
+	mozillaHomeCountryName $
+	mozillaHomeFriendlyCountryName $
+	mozillaHomeUrl $
+	mozillaWorkUrl $
+	mozillaCustom1 $
+	mozillaCustom2 $
+	mozillaCustom3 $
+	mozillaCustom4 $
+	nsAIMid $
+	c $
+	co ) )
+
diff --git a/old_trunk/schema-extras/src/main/schema/nis.schema b/old_trunk/schema-extras/src/main/schema/nis.schema
new file mode 100644
index 0000000..e79091c
--- /dev/null
+++ b/old_trunk/schema-extras/src/main/schema/nis.schema
@@ -0,0 +1,252 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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. 
+
+# $OpenLDAP: /servers/slapd/schema/nis.schema,v 1.15.2.3 2008/02/11 23:26:49 kurt Exp $
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2008 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+# Definitions from RFC2307 (Experimental)
+#	An Approach for Using LDAP as a Network Information Service
+
+# Depends upon core.schema and cosine.schema
+
+# Note: The definitions in RFC2307 are given in syntaxes closely related
+# to those in RFC2252, however, some liberties are taken that are not
+# supported by RFC2252.  This file has been written following RFC2252
+# strictly.
+
+# OID Base is iso(1) org(3) dod(6) internet(1) directory(1) nisSchema(1).
+# i.e. nisSchema in RFC2307 is 1.3.6.1.1.1
+#
+# Syntaxes are under 1.3.6.1.1.1.0 (two new syntaxes are defined)
+#	validaters for these syntaxes are incomplete, they only
+#	implement printable string validation (which is good as the
+#	common use of these syntaxes violates the specification).
+# Attribute types are under 1.3.6.1.1.1.1
+# Object classes are under 1.3.6.1.1.1.2
+
+# Attribute Type Definitions
+
+attributetype ( 1.3.6.1.1.1.1.0 NAME 'uidNumber'
+	DESC 'An integer uniquely identifying a user in an administrative domain'
+	EQUALITY integerMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.1.1.1.1 NAME 'gidNumber'
+	DESC 'An integer uniquely identifying a group in an administrative domain'
+	EQUALITY integerMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.1.1.1.2 NAME 'gecos'
+	DESC 'The GECOS field; the common name'
+	EQUALITY caseIgnoreIA5Match
+	SUBSTR caseIgnoreIA5SubstringsMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.1.1.1.3 NAME 'homeDirectory'
+	DESC 'The absolute path to the home directory'
+	EQUALITY caseExactIA5Match
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.1.1.1.4 NAME 'loginShell'
+	DESC 'The path to the login shell'
+	EQUALITY caseExactIA5Match
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.1.1.1.5 NAME 'shadowLastChange'
+	EQUALITY integerMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.1.1.1.6 NAME 'shadowMin'
+	EQUALITY integerMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.1.1.1.7 NAME 'shadowMax'
+	EQUALITY integerMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.1.1.1.8 NAME 'shadowWarning'
+	EQUALITY integerMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.1.1.1.9 NAME 'shadowInactive'
+	EQUALITY integerMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.1.1.1.10 NAME 'shadowExpire'
+	EQUALITY integerMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.1.1.1.11 NAME 'shadowFlag'
+	EQUALITY integerMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.1.1.1.12 NAME 'memberUid'
+	EQUALITY caseExactIA5Match
+	SUBSTR caseExactIA5SubstringsMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+
+attributetype ( 1.3.6.1.1.1.1.13 NAME 'memberNisNetgroup'
+	EQUALITY caseExactIA5Match
+	SUBSTR caseExactIA5SubstringsMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+
+attributetype ( 1.3.6.1.1.1.1.14 NAME 'nisNetgroupTriple'
+	DESC 'Netgroup triple'
+	SYNTAX 1.3.6.1.1.1.0.0 )
+
+attributetype ( 1.3.6.1.1.1.1.15 NAME 'ipServicePort'
+	EQUALITY integerMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.1.1.1.16 NAME 'ipServiceProtocol'
+	SUP name )
+
+attributetype ( 1.3.6.1.1.1.1.17 NAME 'ipProtocolNumber'
+	EQUALITY integerMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.1.1.1.18 NAME 'oncRpcNumber'
+	EQUALITY integerMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.1.1.1.19 NAME 'ipHostNumber'
+	DESC 'IP address'
+	EQUALITY caseIgnoreIA5Match
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{128} )
+
+attributetype ( 1.3.6.1.1.1.1.20 NAME 'ipNetworkNumber'
+	DESC 'IP network'
+	EQUALITY caseIgnoreIA5Match
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{128} SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.1.1.1.21 NAME 'ipNetmaskNumber'
+	DESC 'IP netmask'
+	EQUALITY caseIgnoreIA5Match
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{128} SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.1.1.1.22 NAME 'macAddress'
+	DESC 'MAC address'
+	EQUALITY caseIgnoreIA5Match
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{128} )
+
+attributetype ( 1.3.6.1.1.1.1.23 NAME 'bootParameter'
+	DESC 'rpc.bootparamd parameter'
+	SYNTAX 1.3.6.1.1.1.0.1 )
+
+attributetype ( 1.3.6.1.1.1.1.24 NAME 'bootFile'
+	DESC 'Boot image name'
+	EQUALITY caseExactIA5Match
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+
+attributetype ( 1.3.6.1.1.1.1.26 NAME 'nisMapName'
+	SUP name )
+
+attributetype ( 1.3.6.1.1.1.1.27 NAME 'nisMapEntry'
+	EQUALITY caseExactIA5Match
+	SUBSTR caseExactIA5SubstringsMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{1024} SINGLE-VALUE )
+
+# Object Class Definitions
+
+objectclass ( 1.3.6.1.1.1.2.0 NAME 'posixAccount'
+	DESC 'Abstraction of an account with POSIX attributes'
+	SUP top AUXILIARY
+	MUST ( cn $ uid $ uidNumber $ gidNumber $ homeDirectory )
+	MAY ( userPassword $ loginShell $ gecos $ description ) )
+
+objectclass ( 1.3.6.1.1.1.2.1 NAME 'shadowAccount'
+	DESC 'Additional attributes for shadow passwords'
+	SUP top AUXILIARY
+	MUST uid
+	MAY ( userPassword $ shadowLastChange $ shadowMin $
+	      shadowMax $ shadowWarning $ shadowInactive $
+	      shadowExpire $ shadowFlag $ description ) )
+
+objectclass ( 1.3.6.1.1.1.2.2 NAME 'posixGroup'
+	DESC 'Abstraction of a group of accounts'
+	SUP top STRUCTURAL
+	MUST ( cn $ gidNumber )
+	MAY ( userPassword $ memberUid $ description ) )
+
+objectclass ( 1.3.6.1.1.1.2.3 NAME 'ipService'
+	DESC 'Abstraction an Internet Protocol service'
+	SUP top STRUCTURAL
+	MUST ( cn $ ipServicePort $ ipServiceProtocol )
+	MAY ( description ) )
+
+objectclass ( 1.3.6.1.1.1.2.4 NAME 'ipProtocol'
+	DESC 'Abstraction of an IP protocol'
+	SUP top STRUCTURAL
+	MUST ( cn $ ipProtocolNumber )
+	MAY description )
+
+objectclass ( 1.3.6.1.1.1.2.5 NAME 'oncRpc'
+	DESC 'Abstraction of an ONC/RPC binding'
+	SUP top STRUCTURAL
+	MUST ( cn $ oncRpcNumber )
+	MAY description )
+
+objectclass ( 1.3.6.1.1.1.2.6 NAME 'ipHost'
+	DESC 'Abstraction of a host, an IP device'
+	SUP top AUXILIARY
+	MUST ( cn $ ipHostNumber )
+	MAY ( l $ description $ manager ) )
+
+objectclass ( 1.3.6.1.1.1.2.7 NAME 'ipNetwork'
+	DESC 'Abstraction of an IP network'
+	SUP top STRUCTURAL
+	MUST ( cn $ ipNetworkNumber )
+	MAY ( ipNetmaskNumber $ l $ description $ manager ) )
+
+objectclass ( 1.3.6.1.1.1.2.8 NAME 'nisNetgroup'
+	DESC 'Abstraction of a netgroup'
+	SUP top STRUCTURAL
+	MUST cn
+	MAY ( nisNetgroupTriple $ memberNisNetgroup $ description ) )
+
+objectclass ( 1.3.6.1.1.1.2.9 NAME 'nisMap'
+	DESC 'A generic abstraction of a NIS map'
+	SUP top STRUCTURAL
+	MUST nisMapName
+	MAY description )
+
+objectclass ( 1.3.6.1.1.1.2.10 NAME 'nisObject'
+	DESC 'An entry in a NIS map'
+	SUP top STRUCTURAL
+	MUST ( cn $ nisMapEntry $ nisMapName )
+	MAY description )
+
+objectclass ( 1.3.6.1.1.1.2.11 NAME 'ieee802Device'
+	DESC 'A device with a MAC address'
+	SUP top AUXILIARY
+	MAY macAddress )
+
+objectclass ( 1.3.6.1.1.1.2.12 NAME 'bootableDevice'
+	DESC 'A device with boot parameters'
+	SUP top AUXILIARY
+	MAY ( bootFile $ bootParameter ) )
diff --git a/old_trunk/schema-extras/src/main/schema/samba.schema b/old_trunk/schema-extras/src/main/schema/samba.schema
new file mode 100644
index 0000000..634a4e8
--- /dev/null
+++ b/old_trunk/schema-extras/src/main/schema/samba.schema
@@ -0,0 +1,505 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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. 
+
+##
+## schema file for OpenLDAP 2.x
+## Schema for storing Samba user accounts and group maps in LDAP
+## OIDs are owned by the Samba Team
+##
+## Prerequisite schemas - uid         (cosine.schema)
+##                      - displayName (inetorgperson.schema)
+##                      - gidNumber   (nis.schema)
+##
+## 1.3.6.1.4.1.7165.2.1.x - attributetypes
+## 1.3.6.1.4.1.7165.2.2.x - objectclasses
+##
+## Printer support
+## 1.3.6.1.4.1.7165.2.3.1.x - attributetypes
+## 1.3.6.1.4.1.7165.2.3.2.x - objectclasses
+##
+## ----- READ THIS WHEN ADDING A NEW ATTRIBUTE OR OBJECT CLASS ------
+##
+## Run the 'get_next_oid' bash script in this directory to find the
+## next available OID for attribute type and object classes.
+##
+##   $ ./get_next_oid
+##   attributetype ( 1.3.6.1.4.1.7165.2.1.XX NAME ....
+##   objectclass ( 1.3.6.1.4.1.7165.2.2.XX NAME ....
+##
+## Also ensure that new entries adhere to the declaration style
+## used throughout this file
+##
+##    <attributetype|objectclass> ( 1.3.6.1.4.1.7165.2.XX.XX NAME ....
+##                               ^ ^                        ^
+##
+## The spaces are required for the get_next_oid script (and for
+## readability).
+##
+## ------------------------------------------------------------------
+
+# objectIdentifier SambaRoot 1.3.6.1.4.1.7165
+# objectIdentifier Samba3 SambaRoot:2
+# objectIdentifier Samba3Attrib Samba3:1
+# objectIdentifier Samba3ObjectClass Samba3:2
+
+########################################################################
+##                            HISTORICAL                              ##
+########################################################################
+
+##
+## Password hashes
+##
+#attributetype ( 1.3.6.1.4.1.7165.2.1.1 NAME 'lmPassword'
+#	DESC 'LanManager Passwd'
+#	EQUALITY caseIgnoreIA5Match
+#	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{32} SINGLE-VALUE )
+
+#attributetype ( 1.3.6.1.4.1.7165.2.1.2 NAME 'ntPassword'
+#	DESC 'NT Passwd'
+#	EQUALITY caseIgnoreIA5Match
+#	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{32} SINGLE-VALUE )
+
+##
+## Account flags in string format ([UWDX     ])
+##
+#attributetype ( 1.3.6.1.4.1.7165.2.1.4 NAME 'acctFlags'
+#	DESC 'Account Flags'
+#	EQUALITY caseIgnoreIA5Match
+#	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{16} SINGLE-VALUE )
+
+##
+## Password timestamps & policies
+##
+#attributetype ( 1.3.6.1.4.1.7165.2.1.3 NAME 'pwdLastSet'
+#	DESC 'NT pwdLastSet'
+#	EQUALITY integerMatch
+#	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+#attributetype ( 1.3.6.1.4.1.7165.2.1.5 NAME 'logonTime'
+#	DESC 'NT logonTime'
+#	EQUALITY integerMatch
+#	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+#attributetype ( 1.3.6.1.4.1.7165.2.1.6 NAME 'logoffTime'
+#	DESC 'NT logoffTime'
+#	EQUALITY integerMatch
+#	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+#attributetype ( 1.3.6.1.4.1.7165.2.1.7 NAME 'kickoffTime'
+#	DESC 'NT kickoffTime'
+#	EQUALITY integerMatch
+#	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+#attributetype ( 1.3.6.1.4.1.7165.2.1.8 NAME 'pwdCanChange'
+#	DESC 'NT pwdCanChange'
+#	EQUALITY integerMatch
+#	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+#attributetype ( 1.3.6.1.4.1.7165.2.1.9 NAME 'pwdMustChange'
+#	DESC 'NT pwdMustChange'
+#	EQUALITY integerMatch
+#	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+##
+## string settings
+##
+#attributetype ( 1.3.6.1.4.1.7165.2.1.10 NAME 'homeDrive'
+#	DESC 'NT homeDrive'
+#	EQUALITY caseIgnoreIA5Match
+#	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{4} SINGLE-VALUE )
+
+#attributetype ( 1.3.6.1.4.1.7165.2.1.11 NAME 'scriptPath'
+#	DESC 'NT scriptPath'
+#	EQUALITY caseIgnoreIA5Match
+#	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{255} SINGLE-VALUE )
+
+#attributetype ( 1.3.6.1.4.1.7165.2.1.12 NAME 'profilePath'
+#	DESC 'NT profilePath'
+#	EQUALITY caseIgnoreIA5Match
+#	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{255} SINGLE-VALUE )
+
+#attributetype ( 1.3.6.1.4.1.7165.2.1.13 NAME 'userWorkstations'
+#	DESC 'userWorkstations'
+#	EQUALITY caseIgnoreIA5Match
+#	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{255} SINGLE-VALUE )
+
+#attributetype ( 1.3.6.1.4.1.7165.2.1.17 NAME 'smbHome'
+#	DESC 'smbHome'
+#	EQUALITY caseIgnoreIA5Match
+#	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{128} )
+
+#attributetype ( 1.3.6.1.4.1.7165.2.1.18 NAME 'domain'
+#	DESC 'Windows NT domain to which the user belongs'
+#	EQUALITY caseIgnoreIA5Match
+#	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{128} )
+
+##
+## user and group RID
+##
+#attributetype ( 1.3.6.1.4.1.7165.2.1.14 NAME 'rid'
+#	DESC 'NT rid'
+#	EQUALITY integerMatch
+#	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+#attributetype ( 1.3.6.1.4.1.7165.2.1.15 NAME 'primaryGroupID'
+#	DESC 'NT Group RID'
+#	EQUALITY integerMatch
+#	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+##
+## The smbPasswordEntry objectclass has been depreciated in favor of the
+## sambaAccount objectclass
+##
+#objectclass ( 1.3.6.1.4.1.7165.2.2.1 NAME 'smbPasswordEntry' SUP top AUXILIARY
+#        DESC 'Samba smbpasswd entry'
+#        MUST ( uid $ uidNumber )
+#        MAY  ( lmPassword $ ntPassword $ pwdLastSet $ acctFlags ))
+
+#objectclass ( 1.3.6.1.4.1.7165.2.2.2 NAME 'sambaAccount' SUP top STRUCTURAL
+#	DESC 'Samba Account'
+#	MUST ( uid $ rid )
+#	MAY  ( cn $ lmPassword $ ntPassword $ pwdLastSet $ logonTime $
+#               logoffTime $ kickoffTime $ pwdCanChange $ pwdMustChange $ acctFlags $
+#               displayName $ smbHome $ homeDrive $ scriptPath $ profilePath $
+#               description $ userWorkstations $ primaryGroupID $ domain ))
+
+#objectclass ( 1.3.6.1.4.1.7165.2.2.3 NAME 'sambaAccount' SUP top AUXILIARY
+#	DESC 'Samba Auxiliary Account'
+#	MUST ( uid $ rid )
+#	MAY  ( cn $ lmPassword $ ntPassword $ pwdLastSet $ logonTime $
+#              logoffTime $ kickoffTime $ pwdCanChange $ pwdMustChange $ acctFlags $
+#              displayName $ smbHome $ homeDrive $ scriptPath $ profilePath $
+#              description $ userWorkstations $ primaryGroupID $ domain ))
+
+########################################################################
+##                        END OF HISTORICAL                           ##
+########################################################################
+
+#######################################################################
+##                Attributes used by Samba 3.0 schema                ##
+#######################################################################
+
+##
+## Password hashes
+##
+attributetype ( 1.3.6.1.4.1.7165.2.1.24 NAME 'sambaLMPassword'
+	DESC 'LanManager Password'
+	EQUALITY caseIgnoreIA5Match
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{32} SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.25 NAME 'sambaNTPassword'
+	DESC 'MD4 hash of the unicode password'
+	EQUALITY caseIgnoreIA5Match
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{32} SINGLE-VALUE )
+
+##
+## Account flags in string format ([UWDX     ])
+##
+attributetype ( 1.3.6.1.4.1.7165.2.1.26 NAME 'sambaAcctFlags'
+	DESC 'Account Flags'
+	EQUALITY caseIgnoreIA5Match
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{16} SINGLE-VALUE )
+
+##
+## Password timestamps & policies
+##
+attributetype ( 1.3.6.1.4.1.7165.2.1.27 NAME 'sambaPwdLastSet'
+	DESC 'Timestamp of the last password update'
+	EQUALITY integerMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.28 NAME 'sambaPwdCanChange'
+	DESC 'Timestamp of when the user is allowed to update the password'
+	EQUALITY integerMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.29 NAME 'sambaPwdMustChange'
+	DESC 'Timestamp of when the password will expire'
+	EQUALITY integerMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.30 NAME 'sambaLogonTime'
+	DESC 'Timestamp of last logon'
+	EQUALITY integerMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.31 NAME 'sambaLogoffTime'
+	DESC 'Timestamp of last logoff'
+	EQUALITY integerMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.32 NAME 'sambaKickoffTime'
+	DESC 'Timestamp of when the user will be logged off automatically'
+	EQUALITY integerMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.48 NAME 'sambaBadPasswordCount'
+	DESC 'Bad password attempt count'
+	EQUALITY integerMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.49 NAME 'sambaBadPasswordTime'
+	DESC 'Time of the last bad password attempt'
+	EQUALITY integerMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.55 NAME 'sambaLogonHours'
+	DESC 'Logon Hours'
+	EQUALITY caseIgnoreIA5Match
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{42} SINGLE-VALUE )
+
+##
+## string settings
+##
+attributetype ( 1.3.6.1.4.1.7165.2.1.33 NAME 'sambaHomeDrive'
+	DESC 'Driver letter of home directory mapping'
+	EQUALITY caseIgnoreIA5Match
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{4} SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.34 NAME 'sambaLogonScript'
+	DESC 'Logon script path'
+	EQUALITY caseIgnoreMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{255} SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.35 NAME 'sambaProfilePath'
+	DESC 'Roaming profile path'
+	EQUALITY caseIgnoreMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{255} SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.36 NAME 'sambaUserWorkstations'
+	DESC 'List of user workstations the user is allowed to logon to'
+	EQUALITY caseIgnoreMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{255} SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.37 NAME 'sambaHomePath'
+	DESC 'Home directory UNC path'
+	EQUALITY caseIgnoreMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{128} )
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.38 NAME 'sambaDomainName'
+	DESC 'Windows NT domain to which the user belongs'
+	EQUALITY caseIgnoreMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{128} )
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.47 NAME 'sambaMungedDial'
+	EQUALITY caseExactMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{1050} )
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.54 NAME 'sambaPasswordHistory'
+	DESC 'Concatenated MD4 hashes of the unicode passwords used on this account'
+	EQUALITY caseIgnoreIA5Match
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{32} )
+
+##
+## SID, of any type
+##
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.20 NAME 'sambaSID'
+	DESC 'Security ID'
+	EQUALITY caseIgnoreIA5Match
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{64} SINGLE-VALUE )
+
+
+##
+## Primary group SID, compatible with ntSid
+##
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.23 NAME 'sambaPrimaryGroupSID'
+	DESC 'Primary Group Security ID'
+	EQUALITY caseIgnoreIA5Match
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{64} SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.51 NAME 'sambaSIDList'
+	DESC 'Security ID List'
+	EQUALITY caseIgnoreIA5Match
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{64} )
+
+##
+## group mapping attributes
+##
+attributetype ( 1.3.6.1.4.1.7165.2.1.19 NAME 'sambaGroupType'
+	DESC 'NT Group Type'
+	EQUALITY integerMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+##
+## Store info on the domain
+##
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.21 NAME 'sambaNextUserRid'
+	DESC 'Next NT rid to give our for users'
+	EQUALITY integerMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.22 NAME 'sambaNextGroupRid'
+	DESC 'Next NT rid to give out for groups'
+	EQUALITY integerMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.39 NAME 'sambaNextRid'
+	DESC 'Next NT rid to give out for anything'
+	EQUALITY integerMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.40 NAME 'sambaAlgorithmicRidBase'
+	DESC 'Base at which the samba RID generation algorithm should operate'
+	EQUALITY integerMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.41 NAME 'sambaShareName'
+	DESC 'Share Name'
+	EQUALITY caseIgnoreMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.42 NAME 'sambaOptionName'
+	DESC 'Option Name'
+	EQUALITY caseIgnoreMatch
+	SUBSTR caseIgnoreSubstringsMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.43 NAME 'sambaBoolOption'
+	DESC 'A boolean option'
+	EQUALITY booleanMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.44 NAME 'sambaIntegerOption'
+	DESC 'An integer option'
+	EQUALITY integerMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.45 NAME 'sambaStringOption'
+	DESC 'A string option'
+	EQUALITY caseExactIA5Match
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.46 NAME 'sambaStringListOption'
+	DESC 'A string list option'
+	EQUALITY caseIgnoreMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.50 NAME 'sambaPrivName'
+	SUP name )
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.52 NAME 'sambaPrivilegeList'
+	DESC 'Privileges List'
+	EQUALITY caseIgnoreIA5Match
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{64} )
+
+attributetype ( 1.3.6.1.4.1.7165.2.1.53 NAME 'sambaTrustFlags'
+	DESC 'Trust Password Flags'
+	EQUALITY caseIgnoreIA5Match
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+
+
+#######################################################################
+##              objectClasses used by Samba 3.0 schema               ##
+#######################################################################
+
+## The X.500 data model (and therefore LDAPv3) says that each entry can
+## only have one structural objectclass.  OpenLDAP 2.0 does not enforce
+## this currently but will in v2.1
+
+##
+## added new objectclass (and OID) for 3.0 to help us deal with backwards
+## compatibility with 2.2 installations (e.g. ldapsam_compat)  --jerry
+##
+objectclass ( 1.3.6.1.4.1.7165.2.2.6 NAME 'sambaSamAccount'
+	DESC 'Samba 3.0 Auxilary SAM Account'
+    SUP top AUXILIARY
+	MUST ( uid $ sambaSID )
+	MAY  ( cn $ sambaLMPassword $ sambaNTPassword $ sambaPwdLastSet $
+	       sambaLogonTime $ sambaLogoffTime $ sambaKickoffTime $
+	       sambaPwdCanChange $ sambaPwdMustChange $ sambaAcctFlags $
+               displayName $ sambaHomePath $ sambaHomeDrive $ sambaLogonScript $
+	       sambaProfilePath $ description $ sambaUserWorkstations $
+	       sambaPrimaryGroupSID $ sambaDomainName $ sambaMungedDial $
+	       sambaBadPasswordCount $ sambaBadPasswordTime $
+	       sambaPasswordHistory $ sambaLogonHours))
+
+##
+## Group mapping info
+##
+objectclass ( 1.3.6.1.4.1.7165.2.2.4 NAME 'sambaGroupMapping'
+	DESC 'Samba Group Mapping'
+    SUP top AUXILIARY
+	MUST ( gidNumber $ sambaSID $ sambaGroupType )
+	MAY  ( displayName $ description $ sambaSIDList ))
+
+##
+## Trust password for trust relationships (any kind)
+##
+objectclass ( 1.3.6.1.4.1.7165.2.2.14 NAME 'sambaTrustPassword'
+	DESC 'Samba Trust Password'
+    SUP top STRUCTURAL
+	MUST ( sambaDomainName $ sambaNTPassword $ sambaTrustFlags )
+	MAY ( sambaSID $ sambaPwdLastSet ))
+
+##
+## Whole-of-domain info
+##
+objectclass ( 1.3.6.1.4.1.7165.2.2.5 NAME 'sambaDomain'
+	DESC 'Samba Domain Information'
+    SUP top STRUCTURAL
+	MUST ( sambaDomainName $
+	       sambaSID )
+	MAY ( sambaNextRid $ sambaNextGroupRid $ sambaNextUserRid $
+	      sambaAlgorithmicRidBase ) )
+
+##
+## used for idmap_ldap module
+##
+objectclass ( 1.3.6.1.4.1.7165.2.2.7 NAME 'sambaUnixIdPool'
+        DESC 'Pool for allocating UNIX uids/gids'
+        SUP top AUXILIARY
+        MUST ( uidNumber $ gidNumber ) )
+
+objectclass ( 1.3.6.1.4.1.7165.2.2.8 NAME 'sambaIdmapEntry'
+        DESC 'Mapping from a SID to an ID'
+        SUP top AUXILIARY
+        MUST ( sambaSID )
+	MAY ( uidNumber $ gidNumber ) )
+
+objectclass ( 1.3.6.1.4.1.7165.2.2.9 NAME 'sambaSidEntry'
+	DESC 'Structural Class for a SID'
+    SUP top STRUCTURAL
+	MUST ( sambaSID ) )
+
+objectclass ( 1.3.6.1.4.1.7165.1.2.2.10 NAME 'sambaConfig'
+	DESC 'Samba Configuration Section'
+    SUP top AUXILIARY
+	MAY ( description ) )
+
+objectclass ( 1.3.6.1.4.1.7165.2.2.11 NAME 'sambaShare'
+	DESC 'Samba Share Section'
+    SUP top STRUCTURAL
+	MUST ( sambaShareName )
+	MAY ( description ) )
+
+objectclass ( 1.3.6.1.4.1.7165.2.2.12 NAME 'sambaConfigOption'
+	DESC 'Samba Configuration Option'
+    SUP top STRUCTURAL
+	MUST ( sambaOptionName )
+	MAY ( sambaBoolOption $ sambaIntegerOption $ sambaStringOption $
+	      sambaStringListoption $ description ) )
+
+objectclass ( 1.3.6.1.4.1.7165.2.2.13 NAME 'sambaPrivilege'
+	DESC 'Samba Privilege'
+    SUP top AUXILIARY
+	MUST ( sambaSID )
+	MAY ( sambaPrivilegeList ) )
+
diff --git a/old_trunk/schema-extras/src/site/site.xml b/old_trunk/schema-extras/src/site/site.xml
new file mode 100644
index 0000000..9ec7c16
--- /dev/null
+++ b/old_trunk/schema-extras/src/site/site.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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.
+-->
+<!--
+  @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+-->
+<project name="${project.name}">
+    <bannerLeft>
+        <name>${project.name}</name>
+    </bannerLeft>
+    <publishDate position="navigation-bottom" format="dd-MM-yyyy HH:mm" />
+    <body>
+        <menu name="Parent">
+            <item name="Apache Directory ApacheDS" href="../index.html" />
+        </menu>
+        <menu ref="reports" />
+    </body>
+</project>
diff --git a/old_trunk/schema-extras/src/test/java/org/apache/directory/server/schema/bootstrap/ExtraSchemaLoadTest.java b/old_trunk/schema-extras/src/test/java/org/apache/directory/server/schema/bootstrap/ExtraSchemaLoadTest.java
new file mode 100644
index 0000000..40c9c4c
--- /dev/null
+++ b/old_trunk/schema-extras/src/test/java/org/apache/directory/server/schema/bootstrap/ExtraSchemaLoadTest.java
@@ -0,0 +1,293 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.bootstrap;
+
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.naming.NamingException;
+
+import junit.framework.TestCase;
+
+import org.apache.directory.server.schema.bootstrap.ApacheSchema;
+import org.apache.directory.server.schema.bootstrap.BootstrapSchemaLoader;
+import org.apache.directory.server.schema.bootstrap.CoreSchema;
+import org.apache.directory.server.schema.bootstrap.SystemSchema;
+import org.apache.directory.server.schema.registries.DefaultOidRegistry;
+import org.apache.directory.server.schema.registries.DefaultRegistries;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+
+
+/**
+ * A unit test case for the BootstrapSchemaLoader class.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 437012 $
+ */
+public class ExtraSchemaLoadTest extends TestCase
+{
+    DefaultRegistries registries;
+
+
+    protected void setUp() throws Exception
+    {
+        super.setUp();
+        registries = new DefaultRegistries( "bootstrap", new BootstrapSchemaLoader(), new DefaultOidRegistry() );
+    }
+
+
+    protected void tearDown() throws Exception
+    {
+        super.tearDown();
+        registries = null;
+    }
+
+
+    public void testLoadAll() throws NamingException
+    {
+        BootstrapSchemaLoader loader = new BootstrapSchemaLoader();
+        Set<Schema> schemas = new HashSet<Schema>();
+        schemas.add( new CoreSchema() );
+        schemas.add( new ApacheSchema() );
+        schemas.add( new ApachednsSchema() );
+        schemas.add( new SystemSchema() );
+        schemas.add( new JavaSchema() );
+        schemas.add( new CorbaSchema() );
+        schemas.add( new CosineSchema() );
+        schemas.add( new DhcpSchema() );
+        schemas.add( new InetorgpersonSchema() );
+        schemas.add( new MozillaSchema() );
+        schemas.add( new CollectiveSchema() );
+        schemas.add( new AutofsSchema() );
+        schemas.add( new NisSchema() );
+        schemas.add( new SambaSchema() );
+        schemas.add( new Krb5kdcSchema() );
+
+        loader.loadWithDependencies( schemas, registries );
+        AttributeType type;
+
+        // from autofs.schema
+        type = registries.getAttributeTypeRegistry().lookup( "automountInformation" );
+        assertNotNull( type );
+
+        // from core.schema
+        type = registries.getAttributeTypeRegistry().lookup( "knowledgeInformation" );
+        assertNotNull( type );
+
+        // from cosine.schema
+        type = registries.getAttributeTypeRegistry().lookup( "textEncodedORAddress" );
+        assertNotNull( type );
+
+        // from corba.schema
+        type = registries.getAttributeTypeRegistry().lookup( "corbaRepositoryId" );
+        assertNotNull( type );
+
+        // from eve.schema
+        type = registries.getAttributeTypeRegistry().lookup( "apacheAlias" );
+        assertNotNull( type );
+
+        // from inetorgperson.schema
+        type = registries.getAttributeTypeRegistry().lookup( "carLicense" );
+        assertNotNull( type );
+
+        // from java.schema
+        type = registries.getAttributeTypeRegistry().lookup( "javaClassName" );
+        assertNotNull( type );
+
+        // from krb5kdc.schema
+        type = registries.getAttributeTypeRegistry().lookup( "krb5PrincipalName" );
+        assertNotNull( type );
+
+        // from nis.schema
+        type = registries.getAttributeTypeRegistry().lookup( "homeDirectory" );
+        assertNotNull( type );
+
+        // from system.schema
+        type = registries.getAttributeTypeRegistry().lookup( "distinguishedName" );
+        assertNotNull( type );
+
+    }
+
+
+    public void testApacheSchemaLoad() throws NamingException
+    {
+        ApacheSchema apacheSchema = new ApacheSchema();
+        BootstrapSchemaLoader loader = new BootstrapSchemaLoader();
+        loader.load( new SystemSchema(), registries, false );
+        loader.load( apacheSchema, registries, false );
+
+        AttributeType type;
+        type = registries.getAttributeTypeRegistry().lookup( "apacheNdn" );
+        assertNotNull( type );
+
+        type = registries.getAttributeTypeRegistry().lookup( "apacheAlias" );
+        assertNotNull( type );
+
+        type = registries.getAttributeTypeRegistry().lookup( "apacheUpdn" );
+        assertNotNull( type );
+    }
+
+
+    public void testDepsSchemaLoad() throws NamingException
+    {
+        BootstrapSchemaLoader loader = new BootstrapSchemaLoader();
+
+        Set<Schema> schemas = new HashSet<Schema>();
+        schemas.add( new ApacheSchema() );
+        schemas.add( new SystemSchema() );
+
+        loader.loadWithDependencies( schemas, registries );
+        AttributeType type;
+        type = registries.getAttributeTypeRegistry().lookup( "apacheNdn" );
+        assertNotNull( type );
+
+        type = registries.getAttributeTypeRegistry().lookup( "apacheAlias" );
+        assertNotNull( type );
+
+        type = registries.getAttributeTypeRegistry().lookup( "apacheUpdn" );
+        assertNotNull( type );
+    }
+
+
+    public void testCoreSchemaLoad() throws NamingException
+    {
+        CoreSchema coreSchema = new CoreSchema();
+        BootstrapSchemaLoader loader = new BootstrapSchemaLoader();
+        loader.load( new SystemSchema(), registries, false );
+        loader.load( coreSchema, registries, false );
+
+        AttributeType type;
+        type = registries.getAttributeTypeRegistry().lookup( "knowledgeInformation" );
+        assertNotNull( type );
+
+        type = registries.getAttributeTypeRegistry().lookup( "countryName" );
+        assertNotNull( type );
+
+        type = registries.getAttributeTypeRegistry().lookup( "serialNumber" );
+        assertNotNull( type );
+    }
+
+
+    public void testJavaSchemaLoad() throws NamingException
+    {
+        testCoreSchemaLoad();
+
+        JavaSchema javaSchema = new JavaSchema();
+        BootstrapSchemaLoader loader = new BootstrapSchemaLoader();
+        loader.load( javaSchema, registries, false );
+
+        AttributeType type;
+        type = registries.getAttributeTypeRegistry().lookup( "javaFactory" );
+        assertNotNull( type );
+
+        type = registries.getAttributeTypeRegistry().lookup( "javaSerializedData" );
+        assertNotNull( type );
+
+        type = registries.getAttributeTypeRegistry().lookup( "javaClassNames" );
+        assertNotNull( type );
+    }
+
+
+    public void testJavaDepsSchemaLoad() throws NamingException
+    {
+        BootstrapSchemaLoader loader = new BootstrapSchemaLoader();
+
+        Set<Schema> schemas = new HashSet<Schema>();
+        schemas.add( new CoreSchema() );
+        schemas.add( new JavaSchema() );
+        schemas.add( new SystemSchema() );
+
+        loader.loadWithDependencies( schemas, registries );
+        AttributeType type;
+        type = registries.getAttributeTypeRegistry().lookup( "javaFactory" );
+        assertNotNull( type );
+
+        type = registries.getAttributeTypeRegistry().lookup( "javaSerializedData" );
+        assertNotNull( type );
+
+        type = registries.getAttributeTypeRegistry().lookup( "javaClassNames" );
+        assertNotNull( type );
+    }
+
+
+    public void testApacheAndJavaDepsSchemaLoad() throws NamingException
+    {
+        BootstrapSchemaLoader loader = new BootstrapSchemaLoader();
+
+        Set<Schema> schemas = new HashSet<Schema>();
+        schemas.add( new ApacheSchema() );
+        schemas.add( new CoreSchema() );
+        schemas.add( new JavaSchema() );
+        schemas.add( new SystemSchema() );
+
+        loader.loadWithDependencies( schemas, registries );
+        AttributeType type;
+        type = registries.getAttributeTypeRegistry().lookup( "apacheAlias" );
+        assertNotNull( type );
+
+        type = registries.getAttributeTypeRegistry().lookup( "apacheNdn" );
+        assertNotNull( type );
+
+        type = registries.getAttributeTypeRegistry().lookup( "apacheUpdn" );
+        assertNotNull( type );
+    }
+
+
+    /**
+     * Attempts to resolve the dependent schema objects of all entities that
+     * refer to other objects within the registries.
+     *
+     * @throws NamingException if there are problems.
+     */
+    public void testReferentialIntegrity() throws NamingException
+    {
+        if ( System.getProperties().containsKey( "ignore.ref.integ.test" ) )
+        {
+            System.err.println( "REFERENTIAL INTEGRITY TESTS BYPASSED!!!" );
+            return;
+        }
+
+        testLoadAll();
+        List errors = registries.checkRefInteg();
+        assertNotNull( errors );
+
+        StringBuffer buf = new StringBuffer();
+
+        if ( !errors.isEmpty() )
+        {
+            buf.append( "expected empty erorrs but got " ).append( errors.size() ).append( " errors:\n" );
+            for ( int ii = 0; ii < errors.size(); ii++ )
+            {
+                buf.append( '\t' ).append( errors.get( ii ).toString() ).append( '\n' );
+            }
+
+            StringWriter out = new StringWriter();
+            Exception e = ( Exception ) errors.get( 0 );
+            e.printStackTrace( new PrintWriter( out ) );
+            buf.append( "\nfirst exception trace:\n" + out.getBuffer().toString() );
+        }
+
+        assertTrue( buf.toString(), errors.isEmpty() );
+    }
+}
diff --git a/old_trunk/schema-registries/pom.xml b/old_trunk/schema-registries/pom.xml
new file mode 100644
index 0000000..a0571fe
--- /dev/null
+++ b/old_trunk/schema-registries/pom.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you 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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.directory.server</groupId>
+    <artifactId>apacheds-parent</artifactId>
+    <version>1.5.4-SNAPSHOT</version>
+  </parent>
+  <artifactId>apacheds-schema-registries</artifactId>
+  <name>ApacheDS Schema Registries</name>
+
+  <description>
+    Interfaces for schema entity registries are contained here.
+  </description>
+
+  <packaging>jar</packaging>
+
+  <dependencies>
+    <dependency>
+      <groupId>${pom.groupId}</groupId>
+      <artifactId>apacheds-core-shared</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
+  </dependencies>
+</project>
+
diff --git a/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/ConcreteNameComponentNormalizer.java b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/ConcreteNameComponentNormalizer.java
new file mode 100644
index 0000000..6a87352
--- /dev/null
+++ b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/ConcreteNameComponentNormalizer.java
@@ -0,0 +1,228 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema;
+
+
+import java.io.UnsupportedEncodingException;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
+import org.apache.directory.server.schema.registries.OidRegistry;
+import org.apache.directory.shared.ldap.name.NameComponentNormalizer;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.schema.MatchingRule;
+import org.apache.directory.shared.ldap.schema.NoOpNormalizer;
+import org.apache.directory.shared.ldap.schema.Normalizer;
+import org.apache.directory.shared.ldap.util.StringTools;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * A DN Name component Normalizer which uses the bootstrap registries to find
+ * the appropriate normalizer for the attribute of the name component with which
+ * to normalize the name component value.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class ConcreteNameComponentNormalizer implements NameComponentNormalizer
+{
+    /** The LoggerFactory used by this Interceptor */
+    private static Logger LOG = LoggerFactory.getLogger( ConcreteNameComponentNormalizer.class );
+
+    /** the at registry used to dynamically resolve Normalizers */
+    private final AttributeTypeRegistry attributeRegistry;
+    
+    /** the oid registry used to dynamically resolve aliases to OIDs */
+    private final OidRegistry oidRegistry;
+
+
+    /**
+     * Creates a DN Name component Normalizer which uses the bootstrap
+     * registries to find the appropriate normalizer for the attribute of the
+     * name component with which to normalize the name component value.
+     *
+     * @param registry the at registry used to dynamically resolve Normalizers
+     */
+    public ConcreteNameComponentNormalizer( AttributeTypeRegistry registry, OidRegistry oidRegistry )
+    {
+        this.attributeRegistry = registry;
+        this.oidRegistry = oidRegistry;
+    }
+
+    
+    private String unescape( String value )
+    {
+        char[] newVal = new char[value.length()];
+        int escaped = 0;
+        char high = 0;
+        char low = 0;
+        int pos = 0;
+        
+        for ( char c:value.toCharArray() )
+        {
+            switch ( escaped )
+            {
+                case 0 :
+                    if ( c == '\\' )
+                    {
+                        escaped = 1;
+                    }
+                    else
+                    {
+                        newVal[pos++] = c;
+                    }
+                    
+                    break;
+
+                case 1 :
+                    escaped++;
+                    high = c;
+                    break;
+                    
+                case 2 :
+                    escaped=0;
+                    low = c;
+                    newVal[pos++] = (char)StringTools.getHexValue( high, low );
+                    
+            }
+        }
+        
+        return new String( newVal, 0, pos );
+    }
+
+    /**
+     * @see NameComponentNormalizer#normalizeByName(String, String)
+     */
+    public Object normalizeByName( String name, String value ) throws NamingException
+    {
+        AttributeType attributeType = attributeRegistry.lookup( name );
+        
+        if ( attributeType.getSyntax().isHumanReadable() )
+        {
+            return lookup( name ).normalize( value );
+        }
+        else
+        {
+            try
+            {
+                String unescaped = unescape( value );
+                byte[] valBytes = unescaped.getBytes( "UTF-8" );
+                
+                return lookup( name ).normalize( valBytes ); 
+            }
+            catch ( UnsupportedEncodingException uee )
+            {
+                String message = "The value stored in a non Human Readable attribute as a String should be convertible to a byte[]";
+                LOG.error( message );
+                throw new NamingException( message );
+            }
+        }
+        
+    }
+
+
+    /**
+     * @see NameComponentNormalizer#normalizeByName(String, String)
+     */
+    public Object normalizeByName( String name, byte[] value ) throws NamingException
+    {
+        AttributeType attributeType = attributeRegistry.lookup( name );
+        
+        if ( !attributeType.getSyntax().isHumanReadable() )
+        {
+            return lookup( name ).normalize( value );
+        }
+        else
+        {
+            try
+            {
+                String valStr = new String( value, "UTF-8" );
+                return lookup( name ).normalize( valStr ); 
+            }
+            catch ( UnsupportedEncodingException uee )
+            {
+                String message = "The value stored in an Human Readable attribute as a byte[] should be convertible to a String";
+                LOG.error( message );
+                throw new NamingException( message );
+            }
+        }
+    }
+
+
+    /**
+     * @see NameComponentNormalizer#normalizeByOid(String, String)
+     */
+    public Object normalizeByOid( String oid, String value ) throws NamingException
+    {
+        return lookup( oid ).normalize( value );
+    }
+
+
+    /**
+     * @see NameComponentNormalizer#normalizeByOid(String, String)
+     */
+    public Object normalizeByOid( String oid, byte[] value ) throws NamingException
+    {
+        return lookup( oid ).normalize( value );
+    }
+
+
+    /**
+     * Looks up the Normalizer to use for a name component using the attributeId
+     * for the name component.  First the attribute is resolved, then its
+     * equality matching rule is looked up.  The normalizer of that matching
+     * rule is returned.
+     *
+     * @param id the name or oid of the attribute in the name component to
+     * normalize the value of
+     * @return the Normalizer to use for normalizing the value of the attribute
+     * @throws NamingException if there are failures resolving the Normalizer
+     */
+    private Normalizer lookup( String id ) throws NamingException
+    {
+        AttributeType type = attributeRegistry.lookup( id );
+        MatchingRule mrule = type.getEquality();
+        
+        if ( mrule == null )
+        {
+            return NoOpNormalizer.INSTANCE;
+        }
+        
+        return type.getEquality().getNormalizer();
+    }
+
+
+    /**
+     * @see NameComponentNormalizer#isDefined(String)
+     */
+    public boolean isDefined( String id )
+    {
+        return attributeRegistry.hasAttributeType( id );
+    }
+
+
+    public String normalizeName( String attributeName ) throws NamingException
+    {
+        return oidRegistry.getOid( attributeName );
+    }
+}
diff --git a/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/DnComparator.java b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/DnComparator.java
new file mode 100644
index 0000000..f611814
--- /dev/null
+++ b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/DnComparator.java
@@ -0,0 +1,110 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema;
+
+
+import java.util.Comparator;
+
+import javax.naming.Name;
+import javax.naming.NamingException;
+
+import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+
+/**
+ * 
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class DnComparator implements Comparator
+{
+    // @TODO you'll need this to fix the way normalization is done
+    private AttributeTypeRegistry attrRegistry;
+    
+    
+    public DnComparator( AttributeTypeRegistry attrRegistry )
+    {
+        this.attrRegistry = attrRegistry;
+    }
+    
+    
+    public DnComparator()
+    {
+    }
+
+    
+    public void setRegistries( Registries registries )
+    {
+        attrRegistry = registries.getAttributeTypeRegistry();
+    }
+    
+    
+    public int compare( Object obj0, Object obj1 ) 
+    {
+        LdapDN dn0 = null;
+        LdapDN dn1 = null;
+        
+        try 
+        {
+            dn0 = getDn( obj0 );
+            dn1 = getDn( obj1 );
+        }
+        catch ( NamingException e )
+        {
+            // -- what do we do here ?
+            return -1;
+        }
+        
+        return dn0.compareTo( dn1 );
+    }
+
+
+    public LdapDN getDn( Object obj ) throws NamingException
+    {
+        LdapDN dn = null;
+        
+        if ( obj instanceof LdapDN )
+        {
+            dn = (LdapDN)obj;
+            
+            dn = ( dn.isNormalized() ? dn : LdapDN.normalize( dn, attrRegistry.getNormalizerMapping() ) );
+        }
+        else if ( obj instanceof Name )
+        {
+            dn = new LdapDN( ( Name ) obj );
+            dn.normalize( attrRegistry.getNormalizerMapping() );
+        }
+        else if ( obj instanceof String )
+        {
+            dn = new LdapDN( ( String ) obj );
+            dn.normalize( attrRegistry.getNormalizerMapping() );
+        }
+        else
+        {
+            throw new IllegalStateException( "I do not know how to handle dn comparisons with objects of class: " 
+                + (obj == null ? null : obj.getClass() ) );
+        }
+        
+        return dn;
+    }
+}
diff --git a/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/DnNormalizer.java b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/DnNormalizer.java
new file mode 100644
index 0000000..174ee0e
--- /dev/null
+++ b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/DnNormalizer.java
@@ -0,0 +1,88 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema;
+
+
+import javax.naming.Name;
+import javax.naming.NamingException;
+
+import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.schema.Normalizer;
+
+
+/**
+ * 
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class DnNormalizer implements Normalizer
+{
+    private static final long serialVersionUID = 1L;
+
+    // @TODO use this later for seting up normalization
+    private AttributeTypeRegistry attrRegistry;
+    
+    
+    public DnNormalizer( AttributeTypeRegistry attrRegistry )
+    {
+        this.attrRegistry = attrRegistry;
+    }
+    
+    
+    public DnNormalizer()
+    {
+    }
+ 
+    
+    public void setRegistries( Registries registries )
+    {
+        this.attrRegistry = registries.getAttributeTypeRegistry();
+    }
+    
+
+    public Object normalize( Object value ) throws NamingException
+    {
+        LdapDN dn = null;
+        
+        if ( value instanceof LdapDN )
+        {
+            dn = ( LdapDN ) ( ( LdapDN ) value ).clone();
+        }
+        else if ( value instanceof Name )
+        {
+            dn = new LdapDN( ( Name ) value );
+        }
+        else if ( value instanceof String )
+        {
+            dn = new LdapDN( ( String ) value );
+        }
+        else
+        {
+            throw new IllegalStateException( "I do not know how to handle dn normalization with objects of class: " 
+                + (value == null ? null : value.getClass() ) );
+        }
+        
+        dn.normalize( attrRegistry.getNormalizerMapping() );
+        return dn.getNormName();
+    }
+}
diff --git a/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/NameAndOptionalUIDComparator.java b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/NameAndOptionalUIDComparator.java
new file mode 100644
index 0000000..c8e14f1
--- /dev/null
+++ b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/NameAndOptionalUIDComparator.java
@@ -0,0 +1,199 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema;
+
+
+import java.util.Comparator;
+
+import javax.naming.Name;
+import javax.naming.NamingException;
+
+import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+
+/**
+ * A comparator for the uniqueMember match
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class NameAndOptionalUIDComparator implements Comparator
+{
+    // @TODO you'll need this to fix the way normalization is done
+    private AttributeTypeRegistry attrRegistry;
+    
+    
+    public NameAndOptionalUIDComparator( AttributeTypeRegistry attrRegistry )
+    {
+        this.attrRegistry = attrRegistry;
+    }
+    
+    
+    public NameAndOptionalUIDComparator()
+    {
+    }
+
+    
+    public void setRegistries( Registries registries )
+    {
+        attrRegistry = registries.getAttributeTypeRegistry();
+    }
+    
+    /**
+     * Comparing two uniqueMember is a matter of following this algorithm:
+     * - if they are only DN, then the values should be equal
+     * - otherwise, both element should contain the same DN and 
+     *   * if they both have an UID, they should be equals.
+     */
+    public int compare( Object obj0, Object obj1 )
+    {
+        String dnstr0 = null;
+        String dnstr1 = null;
+        
+        if ( ( obj0 instanceof String ) && ( obj1 instanceof String) )
+        {
+            dnstr0 = (String)obj0;
+            dnstr1 = (String)obj1;
+            
+            int dash0 = dnstr0.lastIndexOf( '#' );
+            int dash1 = dnstr1.lastIndexOf( '#' );
+            
+            if ( ( dash0 == -1 ) && ( dash1 == -1 ) )
+            {
+                // no UID part
+                try
+                {
+                    return getDn( dnstr0 ).compareTo( getDn ( dnstr1 ) );
+                }
+                catch ( NamingException ne )
+                {
+                    return -1;
+                }
+            }
+            else
+            {
+                // Now, check that we don't have another '#'
+                if ( dnstr0.indexOf( '#' ) != dash0 )
+                {
+                    // Yes, we have one : this is not allowed, it should have been
+                    // escaped.
+                    return -1;
+                }
+                
+                if ( dnstr1.indexOf( '#' ) != dash0 )
+                {
+                    // Yes, we have one : this is not allowed, it should have been
+                    // escaped.
+                    return 1;
+                }
+                
+                LdapDN dn0 = null;
+                LdapDN dn1 = null;
+                
+                // This is an UID if the '#' is immediatly
+                // followed by a BitString, except if the '#' is
+                // on the last position
+                String uid0 = dnstr0.substring( dash0 + 1 );
+                
+                if ( dash0 > 0 )
+                {
+                    try
+                    {
+                        dn0 = new LdapDN( dnstr0.substring( 0, dash0 ) );
+                    }
+                    catch ( NamingException ne )
+                    {
+                        return -1;
+                    }
+                }
+                else
+                {
+                    return -1;
+                }
+                
+                // This is an UID if the '#' is immediatly
+                // followed by a BitString, except if the '#' is
+                // on the last position
+                String uid1 = dnstr1.substring( dash1 + 1 );
+                
+                if ( dash1 > 0 )
+                {
+                    try
+                    {
+                        dn1 = new LdapDN( dnstr0.substring( 0, dash1 ) );
+                    }
+                    catch ( NamingException ne )
+                    {
+                        return 1;
+                    }
+                }
+                else
+                {
+                    return 1;
+                }
+                
+                int dnComp = dn0.compareTo( dn1 );
+                
+                if ( dnComp != 0 )
+                {
+                    return dnComp;
+                }
+                
+                return uid0.compareTo( uid1 );
+            }
+        }
+        else
+        {
+            return -1;
+        }
+    }
+
+
+    public LdapDN getDn( Object obj ) throws NamingException
+    {
+        LdapDN dn = null;
+        
+        if ( obj instanceof LdapDN )
+        {
+            dn = (LdapDN)obj;
+            
+            dn = ( dn.isNormalized() ? dn : LdapDN.normalize( dn, attrRegistry.getNormalizerMapping() ) );
+        }
+        else if ( obj instanceof Name )
+        {
+            dn = new LdapDN( ( Name ) obj );
+            dn.normalize( attrRegistry.getNormalizerMapping() );
+        }
+        else if ( obj instanceof String )
+        {
+            dn = new LdapDN( ( String ) obj );
+            dn.normalize( attrRegistry.getNormalizerMapping() );
+        }
+        else
+        {
+            throw new IllegalStateException( "I do not know how to handle dn comparisons with objects of class: " 
+                + (obj == null ? null : obj.getClass() ) );
+        }
+        
+        return dn;
+    }
+}
diff --git a/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/NameAndOptionalUIDNormalizer.java b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/NameAndOptionalUIDNormalizer.java
new file mode 100644
index 0000000..8751b0d
--- /dev/null
+++ b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/NameAndOptionalUIDNormalizer.java
@@ -0,0 +1,123 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema;
+
+
+import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.schema.Normalizer;
+import org.apache.directory.shared.ldap.util.StringTools;
+
+import javax.naming.NamingException;
+
+
+/**
+ * A noirmalizer for UniqueMember
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class NameAndOptionalUIDNormalizer implements Normalizer
+{
+    private static final long serialVersionUID = 1L;
+
+    private AttributeTypeRegistry attrRegistry;
+    
+    
+    public NameAndOptionalUIDNormalizer( AttributeTypeRegistry attrRegistry )
+    {
+        this.attrRegistry = attrRegistry;
+    }
+    
+    
+    public NameAndOptionalUIDNormalizer()
+    {
+    }
+ 
+    
+    public void setRegistries( Registries registries )
+    {
+        this.attrRegistry = registries.getAttributeTypeRegistry();
+    }
+    
+
+    public Object normalize( Object value ) throws NamingException
+    {
+        if ( value instanceof byte[] )
+        {
+            value = StringTools.utf8ToString( ( byte[] ) value );
+        }
+
+        if ( value instanceof String )
+        {
+            String nameAndUid = (String)value;
+            
+            if ( nameAndUid.length() == 0 )
+            {
+                return false;
+            }
+            
+            // Let's see if we have an UID part
+            int sharpPos = nameAndUid.lastIndexOf( '#' );
+            
+            if ( sharpPos != -1 )
+            {
+                // Now, check that we don't have another '#'
+                if ( nameAndUid.indexOf( '#' ) != sharpPos )
+                {
+                    // Yes, we have one : this is not allowed, it should have been
+                    // escaped.
+                    return false;
+                }
+                
+                // This is an UID if the '#' is immediatly
+                // followed by a BitString, except if the '#' is
+                // on the last position
+                String uid = nameAndUid.substring( sharpPos + 1 );
+                
+                if ( sharpPos > 0 )
+                {
+                    LdapDN dn = new LdapDN( nameAndUid.substring( 0, sharpPos ) );
+                    
+                    dn.normalize( attrRegistry.getNormalizerMapping() );
+                    
+                    return dn.getNormName() + '#' + uid;
+                }
+                else
+                {
+                    throw new IllegalStateException( "I do not know how to handle NameAndOptionalUID normalization with objects of class: " 
+                        + (value == null ? null : value.getClass() ) );
+                }
+            }
+            else
+            {
+                // No UID, the strValue is a DN
+                // Return the normalized DN
+                return new LdapDN( nameAndUid ).getNormName();
+            }
+        }
+        else
+        {
+            throw new IllegalStateException( "I do not know how to handle NameAndOptionalUID normalization with objects of class: " 
+                + (value == null ? null : value.getClass() ) );
+        }
+    }
+}
diff --git a/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/SerializableComparator.java b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/SerializableComparator.java
new file mode 100644
index 0000000..d1cf1fe
--- /dev/null
+++ b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/SerializableComparator.java
@@ -0,0 +1,100 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema;
+
+
+import java.io.Serializable;
+import java.util.Comparator;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.schema.registries.ComparatorRegistry;
+
+
+/**
+ * A serializable wrapper around a Comparator which uses delayed initialization
+ * of the underlying wrapped comparator which is JIT resolved from a static
+ * global registry.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class SerializableComparator implements Comparator, Serializable
+{
+    private static final long serialVersionUID = 3257566226288162870L;
+
+    /** the system global Comparator registry */
+    private static ComparatorRegistry registry = null;
+    /** the OID of the matchingRule for this comparator */
+    private String matchingRuleOid;
+    /** the transient wrapped comparator */
+    private transient Comparator wrapped;
+
+
+    // ------------------------------------------------------------------------
+    // S T A T I C   M E T H O D S
+    // ------------------------------------------------------------------------
+
+    /**
+     * Sets the global Comparator registry for comparator lookups.
+     *
+     * @param registry the comparator registry to use for Comparator lookups
+     */
+    public static void setRegistry( ComparatorRegistry registry )
+    {
+        SerializableComparator.registry = registry;
+    }
+
+
+    // ------------------------------------------------------------------------
+    // C O N T R U C T O R S
+    // ------------------------------------------------------------------------
+
+    public SerializableComparator( String matchingRuleOid )
+    {
+        this.matchingRuleOid = matchingRuleOid;
+    }
+
+
+    // ------------------------------------------------------------------------
+    // C O M P A R A T O R   I M P L E M E N T A T I O N S
+    // ------------------------------------------------------------------------
+
+    /**
+     * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
+     */
+    @SuppressWarnings("unchecked")
+    public int compare( Object o1, Object o2 )
+    {
+        if ( wrapped == null )
+        {
+            try
+            {
+                wrapped = registry.lookup( matchingRuleOid );
+            }
+            catch ( NamingException e )
+            {
+                throw new RuntimeException( "Matching rule not found: " + matchingRuleOid );
+            }
+        }
+
+        return wrapped.compare( o1, o2 );
+    }
+}
diff --git a/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/AbstractSchemaLoader.java b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/AbstractSchemaLoader.java
new file mode 100644
index 0000000..999621f
--- /dev/null
+++ b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/AbstractSchemaLoader.java
@@ -0,0 +1,166 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.registries;
+
+
+import java.util.Map;
+import java.util.Properties;
+import java.util.Stack;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.schema.bootstrap.Schema;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * An abstract class with a utility method and setListener() implemented.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public abstract class AbstractSchemaLoader implements SchemaLoader
+{
+    /** static class logger */
+    private static final Logger LOG = LoggerFactory.getLogger( AbstractSchemaLoader.class );
+    
+    protected SchemaLoaderListener listener;
+    
+    
+    public void setListener( SchemaLoaderListener listener )
+    {
+        this.listener = listener;
+    }
+    
+    
+    protected final void notifyListenerOrRegistries( Schema schema, Registries registries )
+    {
+        if ( listener != null )
+        {
+            listener.schemaLoaded( schema );
+        }
+        
+        if ( registries instanceof SchemaLoaderListener )
+        {
+            if ( registries != listener )
+            {
+                SchemaLoaderListener listener = ( SchemaLoaderListener ) registries;
+                listener.schemaLoaded( schema );
+            }
+        }
+    }
+    
+    
+    /**
+     * Recursive method which loads schema's with their dependent schemas first
+     * and tracks what schemas it has seen so the recursion does not go out of
+     * control with depenency cycle detection.
+     *
+     * @param rootAncestor the triggering schema load request: the root ancestor of dependency chain
+     * @param beenthere stack of schema names we have visited and have yet to load
+     * @param notLoaded hash of schemas keyed by name which have yet to be loaded
+     * @param schema the current schema we are attempting to load
+     * @param registries the set of registries to use while loading
+     * @param props to use while trying to resolve other schemas
+     * @throws NamingException if there is a cycle detected and/or another
+     * failure results while loading, producing and or registering schema objects
+     */
+    protected final void loadDepsFirst( Schema rootAncestor, Stack<String> beenthere, Map<String,Schema> notLoaded, Schema schema,
+        Registries registries, Properties props ) throws NamingException
+    {
+        if ( registries.getLoadedSchemas().containsKey( schema.getSchemaName() ) )
+        {
+            LOG.warn( "{} schema has already been loaded" + schema.getSchemaName() );
+            return;
+        }
+        
+        beenthere.push( schema.getSchemaName() );
+        String[] deps = schema.getDependencies();
+
+        // if no deps then load this guy and return
+        if ( deps == null || deps.length == 0 )
+        {
+            if ( rootAncestor == schema )
+            {
+                load( schema, registries, false );
+            }
+            else
+            {
+                load( schema, registries, true );
+            }
+            
+            notLoaded.remove( schema.getSchemaName() );
+            beenthere.pop();
+            return;
+        }
+
+        /*
+         * We got deps and need to load them before this schema.  We go through
+         * all deps loading them with their deps first if they have not been
+         * loaded.
+         */
+        for ( String depName : deps )
+        {
+            // @todo if a dependency is not loaded it's not in this list
+            // @todo why is it not in this list?  Without being in this list
+            // @todo this for loop is absolutely useless - we will not load
+            // @todo any disabled dependencies at all.  I'm shocked that the
+            // @todo samba schema is actually loading when the nis dependency
+            // @todo is not loaded.
+
+            if ( !notLoaded.containsKey( depName ) )
+            {
+                continue;
+            }
+
+            Schema dep = notLoaded.get( depName );
+
+            // dep is not in the set of schema objects we need to try to resolve it
+            if ( dep == null )
+            {
+                // try to load dependency with the provided properties default
+                dep = getSchema( depName, props );
+            }
+
+            if ( beenthere.contains( dep.getSchemaName() ) )
+            {
+                // push again so we show the cycle in output
+                beenthere.push( dep.getSchemaName() );
+                throw new NamingException( "schema dependency cycle detected: " + beenthere );
+            }
+
+            loadDepsFirst( rootAncestor, beenthere, notLoaded, dep, registries, props );
+        }
+
+        // We have loaded all our deps so we can load this schema
+        if ( rootAncestor == schema )
+        {
+            load( schema, registries, false );
+        }
+        else
+        {
+            load( schema, registries, true );
+        }
+        
+        notLoaded.remove( schema.getSchemaName() );
+        beenthere.pop();
+    }
+}
diff --git a/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/AttributeTypeRegistry.java b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/AttributeTypeRegistry.java
new file mode 100755
index 0000000..0f87d98
--- /dev/null
+++ b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/AttributeTypeRegistry.java
@@ -0,0 +1,121 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.registries;
+
+
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.schema.OidNormalizer;
+
+import javax.naming.NamingException;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+
+/**
+ * An AttributeType registry service interface.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public interface AttributeTypeRegistry extends SchemaObjectRegistry
+{
+    /**
+     * Registers a new AttributeType with this registry.
+     *
+     * @param attributeType the AttributeType to register
+     * @throws NamingException if the AttributeType is already registered or
+     * the registration operation is not supported
+     */
+    void register( AttributeType attributeType ) throws NamingException;
+
+
+    /**
+     * Gets a set of Strings representing the aliases, and numeric identifiers of
+     * all binary attributes.  The set will contain all the aliases for a binary
+     * attributeType (one whose syntax is not human readible) along with its numeric
+     * identifier.
+     *
+     * @return set of aliases and numeric ids for binary attributeTypes
+     * @throws NamingException if there are issues resolving type information
+     */
+    Set<String> getBinaryAttributes() throws NamingException;
+
+    /**
+     * Looks up an AttributeType by its unique Object Identifier or by its
+     * unique name.
+     * 
+     * @param id the object identifier or name of the AttributeType
+     * @return the AttributeType instance for the oid
+     * @throws NamingException if the AttributeType does not exist
+     */
+    AttributeType lookup( String id ) throws NamingException;
+
+
+    /**
+     * Checks to see if an AttributeType exists.
+     * 
+     * @param id the object identifier or name of the AttributeType
+     * @return true if an AttributeType definition exists for the oid, false
+     * otherwise
+     */
+    boolean hasAttributeType( String id );
+
+
+    /**
+     * Gets an Iterator over the AttributeTypes within this registry.
+     *
+     * @return an iterator over all AttributeTypes in registry
+     */
+    Iterator<AttributeType> iterator();
+    
+    
+    /**
+     * Gets an oid/name to normalizer mapping used to normalize distinguished 
+     * names.
+     *
+     * @return a map of OID Strings to OidNormalizer instances
+     * @throws NamingException if for some reason this information cannot be returned
+     */
+    Map<String, OidNormalizer> getNormalizerMapping() throws NamingException; 
+    
+    /**
+     * Quick lookup to see if an attribute has descendants.
+     * 
+     * @param ancestorId the name alias or OID for an attributeType
+     * @return an Iterator over the AttributeTypes which have the ancestor
+     * within their superior chain to the top
+     * @throws NamingException if the ancestor attributeType cannot be 
+     * discerned from the ancestorId supplied
+     */
+    boolean hasDescendants( String ancestorId ) throws NamingException;
+    
+    /**
+     * Get's an iterator over the set of descendant attributeTypes for
+     * some ancestor's name alias or their OID.
+     * 
+     * @param ancestorId the name alias or OID for an attributeType
+     * @return an Iterator over the AttributeTypes which have the ancestor
+     * within their superior chain to the top
+     * @throws NamingException if the ancestor attributeType cannot be 
+     * discerned from the ancestorId supplied
+     */
+    Iterator<AttributeType> descendants( String ancestorId ) throws NamingException;
+}
diff --git a/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/ComparatorRegistry.java b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/ComparatorRegistry.java
new file mode 100644
index 0000000..bcb1c66
--- /dev/null
+++ b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/ComparatorRegistry.java
@@ -0,0 +1,124 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.registries;
+
+
+import java.util.Comparator;
+import java.util.Iterator;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.shared.ldap.schema.syntax.ComparatorDescription;
+
+
+/**
+ * Comparator registry component's service interface.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public interface ComparatorRegistry
+{
+    /**
+     * Gets the name of the schema this schema object is associated with.
+     *
+     * @param oid the object identifier
+     * @return the schema name
+     * @throws NamingException if the schema object does not exist 
+     */
+    String getSchemaName( String oid ) throws NamingException;
+
+
+    /**
+     * Registers a Comparator with this registry.
+     * 
+     * @param description the comparatorDescription for the comparator to register
+     * @param comparator the Comparator to register
+     * @throws NamingException if the Comparator is already registered or the 
+     *      registration operation is not supported
+     */
+    void register( ComparatorDescription description, Comparator comparator ) throws NamingException;
+
+
+    /**
+     * Looks up a Comparator by its unique Object Identifier.
+     * 
+     * @param oid the object identifier
+     * @return the Comparator for the oid
+     * @throws NamingException if there is a backing store failure or the 
+     *      Comparator does not exist.
+     */
+    Comparator lookup( String oid ) throws NamingException;
+
+
+    /**
+     * Checks to see if a Comparator exists.  Backing store failures simply 
+     * return false.
+     * 
+     * @param oid the object identifier
+     * @return true if a Comparator definition exists for the oid, false 
+     *      otherwise
+     */
+    boolean hasComparator( String oid );
+
+
+    /**
+     * Iterates over the numeric OID strings of this registry.
+     * 
+     * @return Iterator of numeric OID strings 
+     */
+    Iterator<String> oidIterator();
+
+    
+    /**
+     * Iterates over the numeric OID strings of this registry.
+     * 
+     * @return Iterator of numeric OID strings 
+     */
+    Iterator<ComparatorDescription> comparatorDescriptionIterator();
+
+    
+    /**
+     * Removes a registered comparator from this registry.
+     * 
+     * @param oid the numeric oid of the comparator to remove.
+     * @throws NamingException if the oid is not a numeric id
+     */
+    void unregister( String oid ) throws NamingException;
+    
+    
+    /**
+     * Unregisters comparators from this registry associated with a schema.
+     *
+     * @param schemaName the name of the schema whose comparators are removed 
+     * from this registry
+     */
+    void unregisterSchemaElements( String schemaName );
+    
+    
+    /**
+     * Renames the schemaName associated with entities within this 
+     * registry to a new schema name.
+     * 
+     * @param originalSchemaName the original schema name
+     * @param newSchemaName the new name to give to the schema
+     */
+    void renameSchema( String originalSchemaName, String newSchemaName );
+}
diff --git a/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/DITContentRuleRegistry.java b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/DITContentRuleRegistry.java
new file mode 100755
index 0000000..ca5b5a8
--- /dev/null
+++ b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/DITContentRuleRegistry.java
@@ -0,0 +1,73 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.registries;
+
+
+import java.util.Iterator;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.shared.ldap.schema.DITContentRule;
+
+
+/**
+ * An DITContentRule registry's service interface.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public interface DITContentRuleRegistry extends SchemaObjectRegistry
+{
+    /**
+     * Registers a DITContentRule with this registry.
+     * 
+     * @param dITContentRule the DITContentRule to register
+     * @throws NamingException if the DITContentRule is already registered or
+     * the registration operation is not supported
+     */
+    void register( DITContentRule dITContentRule ) throws NamingException;
+
+
+    /**
+     * Looks up a DITContentRule by its object identifier or by its name.
+     * 
+     * @param id the object identifier or name of the DITContentRule
+     * @return the DITContentRule instance for the id
+     * @throws NamingException if the DITContentRule does not exist
+     */
+    DITContentRule lookup( String id ) throws NamingException;
+
+
+    /**
+     * Checks to see if a DITContentRule exists.
+     * 
+     * @param id the object identifier or name of the DITContentRule
+     * @return true if a DITContentRule definition exists for the id, false
+     * otherwise
+     */
+    boolean hasDITContentRule( String id );
+    
+    /**
+     * Lists the DITContentRules registered in this registry.
+     *
+     * @return an Iterator of DITContentRules
+     */
+    Iterator<DITContentRule> iterator();
+}
diff --git a/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/DITStructureRuleRegistry.java b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/DITStructureRuleRegistry.java
new file mode 100755
index 0000000..1dbee46
--- /dev/null
+++ b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/DITStructureRuleRegistry.java
@@ -0,0 +1,119 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.registries;
+
+
+import java.util.Iterator;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.shared.ldap.schema.DITStructureRule;
+
+
+/**
+ * An DITStructureRule registry service interface.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public interface DITStructureRuleRegistry extends SchemaObjectRegistry
+{
+    /**
+     * Registers a DITStructureRule with this registry.
+     * 
+     * @param dITStructureRule the dITStructureRule to register
+     * @throws NamingException if the DITStructureRule is already registered
+     * or the registration operation is not supported
+     */
+    void register( DITStructureRule dITStructureRule ) throws NamingException;
+
+
+    /**
+     * Looks up an dITStructureRule using a composite key composed of the
+     * nameForm object identifier with a DOT and the rule id of the 
+     * DITStructureRule appended to it.  If the name form object identifier
+     * is 1.2.3.4 and the rule identifier is 5 then the OID used for the 
+     * lookup is 1.2.3.4.5.
+     * 
+     * @param id the nameForm object identifier with rule identifier appended
+     * @return the DITStructureRule instance for the id
+     * @throws NamingException if the DITStructureRule does not exist
+     */
+    DITStructureRule lookup( String id ) throws NamingException;
+
+
+    /**
+     * Looks up an dITStructureRule by its unique Object IDentifier or by its
+     * name.
+     * 
+     * @param ruleId the rule identifier for the DITStructureRule
+     * @return the DITStructureRule instance for rule identifier
+     * @throws NamingException if the DITStructureRule does not exist
+     */
+    DITStructureRule lookup( Integer ruleId ) throws NamingException;
+
+
+    /**
+     * Checks to see if an dITStructureRule exists using the object identifier
+     * of the nameForm appended with the rule identifier of the DITStructureRule.
+     * 
+     * @param id the object identifier of the nameForm with the rule Id appended
+     * @return true if an dITStructureRule definition exists for the id, false
+     * otherwise
+     */
+    boolean hasDITStructureRule( String id );
+
+
+    /**
+     * Checks to see if an dITStructureRule exists using the rule identifier.
+     * 
+     * @param ruleId the rule identifier for the DITStructureRule.
+     * @return true if an dITStructureRule definition exists for the id, false
+     * otherwise
+     */
+    boolean hasDITStructureRule( Integer ruleId );
+
+    
+    /**
+     * Unregisters a DITStructureRule using it's rule identifier. 
+     * 
+     * @param ruleId the rule identifier for the DITStructureRule to unregister
+     * @throws NamingException if no such DITStructureRule exists
+     */
+    void unregister( Integer ruleId ) throws NamingException;
+    
+    
+    /**
+     * Gets the schema name for a DITStructureRule using the rule identifier. 
+     * 
+     * @param ruleId the rule identifier for the DITStructureRule
+     * @return the schema name for the DITStructureRule
+     * @throws NamingException if no such rule could be found
+     */
+    String getSchemaName( Integer ruleId ) throws NamingException;
+
+
+    /**
+     * Lists all the DITStructureRules within this registry.
+     *
+     * @return an Iterator over all the DITStructureRules within this registry
+     */
+    Iterator<DITStructureRule> iterator();
+}
diff --git a/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/DefaultAttributeTypeRegistry.java b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/DefaultAttributeTypeRegistry.java
new file mode 100755
index 0000000..b853464
--- /dev/null
+++ b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/DefaultAttributeTypeRegistry.java
@@ -0,0 +1,349 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.registries;
+
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.schema.MatchingRule;
+import org.apache.directory.shared.ldap.schema.NoOpNormalizer;
+import org.apache.directory.shared.ldap.schema.OidNormalizer;
+import org.apache.directory.shared.ldap.util.StringTools;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * A plain old java object implementation of an AttributeTypeRegistry.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class DefaultAttributeTypeRegistry implements AttributeTypeRegistry
+{
+    /** static class logger */
+    private static final Logger LOG = LoggerFactory.getLogger( DefaultAttributeTypeRegistry.class );
+
+    /** Speedup for DEBUG mode */
+    private static final boolean IS_DEBUG = LOG.isDebugEnabled();
+    
+    /** maps an OID to an AttributeType */
+    private final Map<String,AttributeType> byOid;
+    /** maps OIDs to a Set of descendants for that OID */
+    private final Map<String,Set<AttributeType>> oidToDescendantSet;
+    /** the registry used to resolve names to OIDs */
+    private final OidRegistry oidRegistry;
+    /** cached normalizer mapping */
+    private transient Map<String, OidNormalizer> mapping;
+    
+
+    // ------------------------------------------------------------------------
+    // C O N S T R U C T O R S
+    // ------------------------------------------------------------------------
+
+
+    /**
+     * Creates an empty DefaultAttributeTypeRegistry.
+     *
+     * @param oidRegistry used by this registry for OID to name resolution of
+     * dependencies and to automatically register and unregister it's aliases and OIDs
+     */
+    public DefaultAttributeTypeRegistry( OidRegistry oidRegistry )
+    {
+        this.byOid = new HashMap<String,AttributeType>();
+        this.oidToDescendantSet= new HashMap<String,Set<AttributeType>>();
+        this.oidRegistry = oidRegistry;
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Service Methods
+    // ------------------------------------------------------------------------
+
+    
+    public void register( AttributeType attributeType ) throws NamingException
+    {
+        if ( byOid.containsKey( attributeType.getOid() ) )
+        {
+            throw new NamingException( "attributeType w/ OID " + attributeType.getOid()
+                + " has already been registered!" );
+        }
+
+        String[] names = attributeType.getNamesRef();
+        for ( String name : names )
+        {
+            oidRegistry.register( name, attributeType.getOid() );
+        }
+        oidRegistry.register( attributeType.getOid(), attributeType.getOid() );
+
+        if ( mapping != null )
+        {
+            addMappingFor( attributeType );
+        }
+
+        registerDescendants( attributeType );
+        byOid.put( attributeType.getOid(), attributeType );
+        
+        if ( IS_DEBUG )
+        {
+            LOG.debug( "registed attributeType: " + attributeType );
+        }
+    }
+
+
+    public Set<String> getBinaryAttributes() throws NamingException
+    {
+        Set<String> binaries = new HashSet<String>();
+        Iterator<AttributeType> list = iterator();
+        while ( list.hasNext() )
+        {
+            AttributeType type = list.next();
+
+            if ( ! type.getSyntax().isHumanReadable() )
+            {
+                // add the OID for the attributeType
+                binaries.add( type.getOid() );
+
+                // add the lowercased name for the names for the attributeType
+                String[] names = type.getNamesRef();
+
+                for ( String name : names )
+                {
+                    // @TODO do we really need to lowercase strings here?
+                    binaries.add( StringTools.lowerCaseAscii( StringTools.trim( name ) ) );
+                }
+            }
+        }
+
+        return binaries;
+    }
+
+
+    public void registerDescendants( AttributeType attributeType ) throws NamingException
+    {
+        // add/create the descendent set for this attribute
+        oidToDescendantSet.put( attributeType.getOid(), new HashSet<AttributeType>( 5 ) );
+        
+        // add this attribute to descendant list of other attributes in superior chain
+        onRegisterAddToAncestorDescendants( attributeType, attributeType.getSuperior() );
+    }
+    
+    
+    /**
+     * Recursively adds a new attributeType to the descendant's list of all ancestors
+     * until top is reached.  Top will not have the new type added.
+     * 
+     * @param newType the new attributeType being added
+     * @param ancestor some anscestor from superior up to and including top
+     * @throws NamingException if there are resolution failures
+     */
+    protected void onRegisterAddToAncestorDescendants( AttributeType newType, AttributeType ancestor ) 
+        throws NamingException
+    {
+        if ( ancestor == null )
+        {
+            return;
+        }
+        
+        if ( ancestor.getName() != null && ancestor.getName().equals( SchemaConstants.TOP_OC ) )
+        {
+            return;
+        }
+        
+        Set<AttributeType> descendants = oidToDescendantSet.get( ancestor.getOid() );
+        if ( descendants == null )
+        {
+            descendants = new HashSet<AttributeType>( 5 );
+            oidToDescendantSet.put( ancestor.getOid(), descendants );
+        }
+        descendants.add( newType );
+        onRegisterAddToAncestorDescendants( newType, ancestor.getSuperior() );
+    }
+    
+
+    public AttributeType lookup( String id ) throws NamingException
+    {
+        String oid = oidRegistry.getOid( id );
+
+        if ( !byOid.containsKey( oid ) )
+        {
+            throw new NamingException( "attributeType w/ OID " + oid + " not registered!" );
+        }
+
+        AttributeType attributeType = byOid.get( oid );
+        
+        if ( IS_DEBUG )
+        {
+            LOG.debug( "lookup with id" + oid + "' of attributeType: " + attributeType );
+        }
+        
+        return attributeType;
+    }
+
+
+    public boolean hasAttributeType( String id )
+    {
+        if ( oidRegistry.hasOid( id ) )
+        {
+            try
+            {
+                return byOid.containsKey( oidRegistry.getOid( id ) );
+            }
+            catch ( NamingException e )
+            {
+                return false;
+            }
+        }
+
+        return false;
+    }
+
+
+    public String getSchemaName( String id ) throws NamingException
+    {
+        id = oidRegistry.getOid( id );
+        AttributeType at = byOid.get( id );
+        
+        if ( at != null )
+        {
+            return at.getSchema();
+        }
+
+        throw new NamingException( "OID " + id + " not found in oid to " + "AttributeType map!" );
+    }
+
+
+    public Iterator list()
+    {
+        return byOid.values().iterator();
+    }
+
+
+    private void removeMappingFor( AttributeType type ) throws NamingException
+    {
+        if ( type == null )
+        {
+            return;
+        }
+        
+        MatchingRule matchingRule = type.getEquality();
+        mapping.remove( type.getOid() );
+        String[] aliases = type.getNamesRef();
+        for ( String aliase : aliases )
+        {
+            mapping.remove( aliase );
+            mapping.remove( aliase.toLowerCase() );
+        }
+    }
+
+
+    private void addMappingFor( AttributeType type ) throws NamingException
+    {
+        MatchingRule matchingRule = type.getEquality();
+        OidNormalizer oidNormalizer;
+
+        if ( matchingRule == null )
+        {
+            LOG.debug( "Attribute " + type.getName() + " does not have normalizer : using NoopNormalizer" );
+            oidNormalizer = new OidNormalizer( type.getOid(), new NoOpNormalizer() );
+        }
+        else
+        {
+            oidNormalizer = new OidNormalizer( type.getOid(), matchingRule.getNormalizer() );
+        }
+
+        mapping.put( type.getOid(), oidNormalizer );
+        String[] aliases = type.getNamesRef();
+        for ( String aliase : aliases )
+        {
+            mapping.put( aliase, oidNormalizer );
+            mapping.put( aliase.toLowerCase(), oidNormalizer );
+        }
+    }
+
+    
+    public Map<String,OidNormalizer> getNormalizerMapping() throws NamingException
+    {
+        if ( mapping == null )
+        {
+            mapping = new HashMap<String,OidNormalizer>( byOid.size() << 1 );
+            for ( AttributeType type : byOid.values() )
+            {
+                addMappingFor( type );
+            }
+        }
+        
+        return Collections.unmodifiableMap( mapping );
+    }
+
+
+    public Iterator<AttributeType> descendants( String ancestorId ) throws NamingException
+    {
+        String oid = oidRegistry.getOid( ancestorId );
+        Set<AttributeType> descendants = oidToDescendantSet.get( oid );
+        if ( descendants == null )
+        {
+            //noinspection unchecked
+            return Collections.EMPTY_SET.iterator();
+        }
+        return descendants.iterator();
+    }
+
+
+    public boolean hasDescendants( String ancestorId ) throws NamingException
+    {
+        String oid = oidRegistry.getOid( ancestorId );
+        Set descendants = oidToDescendantSet.get( oid );
+        return descendants != null && !descendants.isEmpty();
+    }
+
+
+    public Iterator<AttributeType> iterator()
+    {
+        return byOid.values().iterator();
+    }
+    
+    
+    public void unregister( String numericOid ) throws NamingException
+    {
+        if ( ! Character.isDigit( numericOid.charAt( 0 ) ) )
+        {
+            throw new NamingException( "Looks like the arg is not a numeric OID" );
+        }
+
+        if ( mapping != null )
+        {
+            removeMappingFor( byOid.get( numericOid ));
+        }
+
+        byOid.remove( numericOid );
+        oidToDescendantSet.remove( numericOid );
+    }
+}
diff --git a/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/DefaultComparatorRegistry.java b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/DefaultComparatorRegistry.java
new file mode 100755
index 0000000..078e094
--- /dev/null
+++ b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/DefaultComparatorRegistry.java
@@ -0,0 +1,204 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.registries;
+
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.shared.ldap.schema.syntax.ComparatorDescription;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * A simple POJO implementation of the ComparatorRegistry service interface.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class DefaultComparatorRegistry implements ComparatorRegistry
+{
+    /** static class logger */
+    private static final Logger LOG = LoggerFactory.getLogger( DefaultComparatorRegistry.class );
+    
+    /** the comparators in this registry */
+    private final Map<String,Comparator> byOid;
+    
+    /** maps oids to a comparator description */
+    private final Map<String,ComparatorDescription> oidToDescription;
+
+    /** A speedup for debug */
+    private static final boolean DEBUG = LOG.isDebugEnabled();
+
+    // ------------------------------------------------------------------------
+    // C O N S T R U C T O R S
+    // ------------------------------------------------------------------------
+
+
+    /**
+     * Creates a DefaultComparatorRegistry by initializing the map and the
+     * montior.
+     */
+    public DefaultComparatorRegistry()
+    {
+        this.byOid = new HashMap<String, Comparator>();
+        this.oidToDescription = new HashMap<String,ComparatorDescription>();
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Service Methods
+    // ------------------------------------------------------------------------
+
+    
+    public void register( ComparatorDescription description, Comparator comparator ) throws NamingException
+    {
+        if ( byOid.containsKey( description.getNumericOid() ) )
+        {
+            throw new NamingException( "Comparator with OID " + description.getNumericOid() 
+                + " already registered!" );
+        }
+
+        oidToDescription.put( description.getNumericOid(), description );
+        byOid.put( description.getNumericOid(), comparator );
+        
+        if ( DEBUG )
+        {
+            LOG.debug( "registed comparator with OID: " + description.getNumericOid() );
+        }
+    }
+
+    
+    private static String getSchema( ComparatorDescription desc )
+    {
+        List values = desc.getExtensions().get( "X-SCHEMA" );
+        
+        if ( values == null || values.size() == 0 )
+        {
+            return "other";
+        }
+        
+        return desc.getExtensions().get( "X-SCHEMA" ).get( 0 );
+    }
+    
+
+    public Comparator lookup( String oid ) throws NamingException
+    {
+        if ( byOid.containsKey( oid ) )
+        {
+            Comparator c = byOid.get( oid );
+            
+            if ( DEBUG )
+            {
+                LOG.debug( "looked up comparator with OID: " + oid );
+            }
+            
+            return c;
+        }
+
+        throw new NamingException( "Comparator not found for OID: " + oid );
+    }
+
+
+    public boolean hasComparator( String oid )
+    {
+        return byOid.containsKey( oid );
+    }
+
+
+    public String getSchemaName( String oid ) throws NamingException
+    {
+        if ( ! Character.isDigit( oid.charAt( 0 ) ) )
+        {
+            throw new NamingException( "OID " + oid + " is not a numeric OID" );
+        }
+
+        if ( oidToDescription.containsKey( oid ) )
+        {
+            return getSchema( oidToDescription.get( oid ) );
+        }
+
+        throw new NamingException( "OID " + oid + " not found in oid to " + "description map!" );
+    }
+
+
+    public Iterator<String> oidIterator()
+    {
+        return byOid.keySet().iterator();
+    }
+
+
+    public void unregister( String oid ) throws NamingException
+    {
+        if ( ! Character.isDigit( oid.charAt( 0 ) ) )
+        {
+            throw new NamingException( "OID " + oid + " is not a numeric OID" );
+        }
+
+        this.byOid.remove( oid );
+        this.oidToDescription.remove( oid );
+    }
+    
+    
+    public void unregisterSchemaElements( String schemaName )
+    {
+        List<String> oids = new ArrayList<String>( byOid.keySet() );
+        for ( String oid : oids )
+        {
+            ComparatorDescription description = oidToDescription.get( oid );
+            String schemaNameForOid = getSchema( description );
+            if ( schemaNameForOid.equalsIgnoreCase( schemaName ) )
+            {
+                byOid.remove( oid );
+                oidToDescription.remove( oid );
+            }
+        }
+    }
+
+
+    public void renameSchema( String originalSchemaName, String newSchemaName )
+    {
+        List<String> oids = new ArrayList<String>( byOid.keySet() );
+        for ( String oid : oids )
+        {
+            ComparatorDescription description = oidToDescription.get( oid );
+            String schemaNameForOid = getSchema( description );
+            if ( schemaNameForOid.equalsIgnoreCase( originalSchemaName ) )
+            {
+                List<String> schemaExt = description.getExtensions().get( "X-SCHEMA" );
+                schemaExt.clear();
+                schemaExt.add( newSchemaName );
+            }
+        }
+    }
+
+
+    public Iterator<ComparatorDescription> comparatorDescriptionIterator()
+    {
+        return oidToDescription.values().iterator();
+    }
+}
diff --git a/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/DefaultDitContentRuleRegistry.java b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/DefaultDitContentRuleRegistry.java
new file mode 100755
index 0000000..7c46eed
--- /dev/null
+++ b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/DefaultDitContentRuleRegistry.java
@@ -0,0 +1,153 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.registries;
+
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.shared.ldap.schema.DITContentRule;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * A plain old java object implementation of an DITContentRuleRegistry.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class DefaultDitContentRuleRegistry implements DITContentRuleRegistry
+{
+    /** static class logger */
+    private static final Logger LOG = LoggerFactory.getLogger( DefaultDitContentRuleRegistry.class );
+    /** maps an OID to an DITContentRule */
+    private final Map<String,DITContentRule> byOid;
+    /** the registry used to resolve names to OIDs */
+    private final OidRegistry oidRegistry;
+
+
+    // ------------------------------------------------------------------------
+    // C O N S T R U C T O R S
+    // ------------------------------------------------------------------------
+
+    /**
+     * Creates an empty DefaultDitContentRuleRegistry.
+     *
+     * @param oidRegistry used by this registry for OID to name resolution of
+     * dependencies and to automatically register and unregister it's aliases and OIDs
+     */
+    public DefaultDitContentRuleRegistry( OidRegistry oidRegistry )
+    {
+        this.byOid = new HashMap<String,DITContentRule>();
+        this.oidRegistry = oidRegistry;
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Service Methods
+    // ------------------------------------------------------------------------
+
+    
+    public void register( DITContentRule dITContentRule ) throws NamingException
+    {
+        if ( byOid.containsKey( dITContentRule.getOid() ) )
+        {
+            throw new NamingException( "dITContentRule w/ OID " + dITContentRule.getOid()
+                + " has already been registered!" );
+        }
+
+        oidRegistry.register( dITContentRule.getName(), dITContentRule.getOid() );
+        byOid.put( dITContentRule.getOid(), dITContentRule );
+        if ( LOG.isDebugEnabled() )
+        {
+            LOG.debug( "registed dITContentRule: " + dITContentRule );
+        }
+    }
+
+
+    public DITContentRule lookup( String id ) throws NamingException
+    {
+        id = oidRegistry.getOid( id );
+
+        if ( !byOid.containsKey( id ) )
+        {
+            throw new NamingException( "dITContentRule w/ OID " + id + " not registered!" );
+        }
+
+        DITContentRule dITContentRule = byOid.get( id );
+        if ( LOG.isDebugEnabled() )
+        {
+            LOG.debug( "lookup with id '" + id + "' of dITContentRule: " + dITContentRule );
+        }
+        return dITContentRule;
+    }
+
+
+    public boolean hasDITContentRule( String id )
+    {
+        if ( oidRegistry.hasOid( id ) )
+        {
+            try
+            {
+                return byOid.containsKey( oidRegistry.getOid( id ) );
+            }
+            catch ( NamingException e )
+            {
+                return false;
+            }
+        }
+
+        return false;
+    }
+
+
+    public String getSchemaName( String id ) throws NamingException
+    {
+        id = oidRegistry.getOid( id );
+        DITContentRule dcr = byOid.get( id );
+        if ( dcr != null )
+        {
+            return dcr.getSchema();
+        }
+
+        throw new NamingException( "OID " + id + " not found in oid to " + "DITContentRule map!" );
+    }
+
+
+    public Iterator<DITContentRule> iterator()
+    {
+        return byOid.values().iterator();
+    }
+
+
+    public void unregister( String numericOid ) throws NamingException
+    {
+        if ( ! Character.isDigit( numericOid.charAt( 0 ) ) )
+        {
+            throw new NamingException( "Looks like the arg is not a numeric OID" );
+        }
+
+        byOid.remove( numericOid );
+    }
+}
diff --git a/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/DefaultDitStructureRuleRegistry.java b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/DefaultDitStructureRuleRegistry.java
new file mode 100755
index 0000000..647882a
--- /dev/null
+++ b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/DefaultDitStructureRuleRegistry.java
@@ -0,0 +1,213 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.registries;
+
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.shared.ldap.exception.LdapNamingException;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.schema.DITStructureRule;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * A plain old java object implementation of an DITStructureRuleRegistry.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class DefaultDitStructureRuleRegistry implements DITStructureRuleRegistry
+{
+    /** static class logger */
+    private static final Logger LOG = LoggerFactory.getLogger( DefaultDitStructureRuleRegistry.class );
+    /** maps an OID to an DITStructureRule */
+    private final Map<String,DITStructureRule> byOid;
+    /** maps an OID to an DITStructureRule */
+    private final Map<Integer,DITStructureRule> byRuleId;
+    /** the registry used to resolve names to OIDs */
+    private final OidRegistry oidRegistry;
+
+
+    // ------------------------------------------------------------------------
+    // C O N S T R U C T O R S
+    // ------------------------------------------------------------------------
+
+    
+    /**
+     * Creates an empty DefaultDitStructureRuleRegistry.
+     *
+     * @param oidRegistry used by this registry for OID to name resolution of
+     * dependencies and to automatically register and unregister it's aliases and OIDs
+     */
+    public DefaultDitStructureRuleRegistry( OidRegistry oidRegistry )
+    {
+        this.byRuleId = new HashMap<Integer,DITStructureRule>();
+        this.byOid = new HashMap<String,DITStructureRule>();
+        this.oidRegistry = oidRegistry;
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Service Methods
+    // ------------------------------------------------------------------------
+
+    public void register( DITStructureRule dITStructureRule ) throws NamingException
+    {
+        if ( byOid.containsKey( dITStructureRule.getOid() ) )
+        {
+            throw new NamingException( "dITStructureRule w/ OID " + dITStructureRule.getOid()
+                + " has already been registered!" );
+        }
+
+        oidRegistry.register( dITStructureRule.getName(), dITStructureRule.getOid() );
+        byOid.put( dITStructureRule.getOid(), dITStructureRule );
+        byRuleId.put( dITStructureRule.getRuleId(), dITStructureRule );
+        if ( LOG.isDebugEnabled() )
+        {
+            LOG.debug( "registered dITStructureRule: " + dITStructureRule );
+        }
+    }
+
+
+    public DITStructureRule lookup( String id ) throws NamingException
+    {
+        id = oidRegistry.getOid( id );
+
+        if ( !byOid.containsKey( id ) )
+        {
+            throw new NamingException( "dITStructureRule w/ OID " + id + " not registered!" );
+        }
+
+        DITStructureRule dITStructureRule = byOid.get( id );
+        if ( LOG.isDebugEnabled() )
+        {
+            LOG.debug( "lookup with id '" + id + "' for dITStructureRule: " + dITStructureRule );
+        }
+        return dITStructureRule;
+    }
+
+
+    public boolean hasDITStructureRule( String id )
+    {
+        if ( oidRegistry.hasOid( id ) )
+        {
+            try
+            {
+                return byOid.containsKey( oidRegistry.getOid( id ) );
+            }
+            catch ( NamingException e )
+            {
+                return false;
+            }
+        }
+
+        return false;
+    }
+
+
+    public String getSchemaName( String id ) throws NamingException
+    {
+        id = oidRegistry.getOid( id );
+        DITStructureRule dsr = byOid.get( id );
+        if ( dsr != null )
+        {
+            return dsr.getSchema();
+        }
+
+        throw new NamingException( "OID " + id + " not found in oid to " + "DITStructureRule map!" );
+    }
+
+
+    public String getSchemaName( Integer ruleId ) throws NamingException
+    {
+        DITStructureRule dsr = byRuleId.get( ruleId );
+        if ( dsr != null )
+        {
+            return dsr.getSchema();
+        }
+
+        throw new NamingException( "A DitStructureRule with ruleId " + ruleId 
+            + " not found in the DITStructureRule map!" );
+    }
+
+
+    public Iterator<DITStructureRule> iterator()
+    {
+        return byOid.values().iterator();
+    }
+
+    
+    public void unregister( Integer ruleId ) throws NamingException
+    {
+        DITStructureRule dsr = byRuleId.remove( ruleId );
+        
+        if ( dsr == null )
+        {
+            if ( dsr == null )
+            {
+                throw new LdapNamingException(
+                    "No such DITStructureRule for rule identifier: " + ruleId,
+                    ResultCodeEnum.OTHER );
+            }
+        }
+        
+        byOid.remove( dsr.getOid() );
+    }
+    
+    
+    public void unregister( String numericOid ) throws NamingException
+    {
+        if ( ! Character.isDigit( numericOid.charAt( 0 ) ) )
+        {
+            throw new NamingException( "Looks like the arg is not a numeric OID" );
+        }
+
+        DITStructureRule dsr = byOid.remove( numericOid );
+        byRuleId.remove( dsr.getRuleId() );
+    }
+
+
+    public boolean hasDITStructureRule( Integer ruleId )
+    {
+        DITStructureRule dsr = byRuleId.get( ruleId );
+        return dsr != null;
+    }
+
+
+    public DITStructureRule lookup( Integer ruleId ) throws NamingException
+    {
+        DITStructureRule dsr = byRuleId.get( ruleId );
+        
+        if ( dsr == null )
+        {
+            throw new LdapNamingException(
+                "No such DITStructureRule for rule identifier: " + ruleId,
+                ResultCodeEnum.OTHER );
+        }
+
+        return dsr;
+    }
+}
diff --git a/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/DefaultMatchingRuleRegistry.java b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/DefaultMatchingRuleRegistry.java
new file mode 100644
index 0000000..67d562a
--- /dev/null
+++ b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/DefaultMatchingRuleRegistry.java
@@ -0,0 +1,171 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.registries;
+
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.shared.ldap.schema.MatchingRule;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * A MatchingRuleRegistry service used to lookup matching rules by OID.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class DefaultMatchingRuleRegistry implements MatchingRuleRegistry
+{
+    /** static class logger */
+    private static final Logger LOG = LoggerFactory.getLogger( DefaultMatchingRuleRegistry.class );
+    /** a map using an OID for the key and a MatchingRule for the value */
+    private final Map<String,MatchingRule> byOid;
+    /** the registry used to resolve names to OIDs */
+    private final OidRegistry oidRegistry;
+
+
+    // ------------------------------------------------------------------------
+    // C O N S T R U C T O R S
+    // ------------------------------------------------------------------------
+
+    
+    /**
+     * Creates a DefaultMatchingRuleRegistry using existing MatchingRulees
+     * for lookups.
+     *
+     * @param oidRegistry used by this registry for OID to name resolution of
+     * dependencies and to automatically register and unregister it's aliases and OIDs
+     */
+    public DefaultMatchingRuleRegistry( OidRegistry oidRegistry )
+    {
+        this.oidRegistry = oidRegistry;
+        this.byOid = new HashMap<String,MatchingRule>();
+    }
+
+
+    // ------------------------------------------------------------------------
+    // MatchingRuleRegistry interface methods
+    // ------------------------------------------------------------------------
+
+    /**
+     * @see org.apache.directory.server.schema.registries.MatchingRuleRegistry#lookup(String)
+     */
+    public MatchingRule lookup( String id ) throws NamingException
+    {
+        id = oidRegistry.getOid( id );
+
+        if ( byOid.containsKey( id ) )
+        {
+            MatchingRule matchingRule = byOid.get( id );
+            if ( LOG.isDebugEnabled() )
+            {
+                LOG.debug( "lookup with id '"+id+"' of matchingRule: " + matchingRule );
+            }
+            return matchingRule;
+        }
+
+        throw new NamingException( "Unknown MatchingRule OID " + id );
+    }
+
+
+    /**
+     * @see MatchingRuleRegistry#register(MatchingRule)
+     */
+    public void register( MatchingRule matchingRule ) throws NamingException
+    {
+        if ( byOid.containsKey( matchingRule.getOid() ) )
+        {
+            throw new NamingException( "matchingRule w/ OID " + matchingRule.getOid()
+                + " has already been registered!" );
+        }
+
+        String[] names = matchingRule.getNamesRef();
+        
+        for ( String name : names )
+        {
+            oidRegistry.register( name, matchingRule.getOid() );
+        }
+        oidRegistry.register( matchingRule.getOid(), matchingRule.getOid() );
+
+        byOid.put( matchingRule.getOid(), matchingRule );
+        if ( LOG.isDebugEnabled() )
+        {
+            LOG.debug( "registed matchingRule: " + matchingRule);
+        }
+    }
+
+
+    /**
+     * @see org.apache.directory.server.schema.registries.MatchingRuleRegistry#hasMatchingRule(String)
+     */
+    public boolean hasMatchingRule( String id )
+    {
+        if ( oidRegistry.hasOid( id ) )
+        {
+            try
+            {
+                return byOid.containsKey( oidRegistry.getOid( id ) );
+            }
+            catch ( NamingException e )
+            {
+                return false;
+            }
+        }
+
+        return false;
+    }
+
+
+    public String getSchemaName( String id ) throws NamingException
+    {
+        id = oidRegistry.getOid( id );
+        MatchingRule mr = byOid.get( id );
+        if ( mr != null )
+        {
+            return mr.getSchema();
+        }
+
+        throw new NamingException( "OID " + id + " not found in oid to " + "MatchingRule name map!" );
+    }
+
+
+    public Iterator<MatchingRule> iterator()
+    {
+        return byOid.values().iterator();
+    }
+    
+    
+    public void unregister( String numericOid ) throws NamingException
+    {
+        if ( ! Character.isDigit( numericOid.charAt( 0 ) ) )
+        {
+            throw new NamingException( "Looks like the arg is not a numeric OID" );
+        }
+
+        byOid.remove( numericOid );
+    }
+}
diff --git a/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/DefaultMatchingRuleUseRegistry.java b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/DefaultMatchingRuleUseRegistry.java
new file mode 100755
index 0000000..2d7cd99
--- /dev/null
+++ b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/DefaultMatchingRuleUseRegistry.java
@@ -0,0 +1,128 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.registries;
+
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.shared.ldap.schema.MatchingRuleUse;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * A plain old java object implementation of an MatchingRuleUseRegistry.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class DefaultMatchingRuleUseRegistry implements MatchingRuleUseRegistry
+{
+    /** static class logger */
+    private static final Logger LOG = LoggerFactory.getLogger( DefaultMatchingRuleUseRegistry.class );
+    /** maps a name to an MatchingRuleUse */
+    private final Map<String,MatchingRuleUse> byName;
+
+
+    // ------------------------------------------------------------------------
+    // C O N S T R U C T O R S
+    // ------------------------------------------------------------------------
+
+    
+    /**
+     * Creates an empty DefaultMatchingRuleUseRegistry.
+     */
+    public DefaultMatchingRuleUseRegistry()
+    {
+        this.byName = new HashMap<String,MatchingRuleUse>();
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Service Methods
+    // ------------------------------------------------------------------------
+
+    
+    public void register( MatchingRuleUse matchingRuleUse ) throws NamingException
+    {
+        if ( byName.containsKey( matchingRuleUse.getName() ) )
+        {
+            throw new NamingException( "matchingRuleUse w/ name " + matchingRuleUse.getName()
+                + " has already been registered!" );
+        }
+
+        byName.put( matchingRuleUse.getName(), matchingRuleUse );
+        if ( LOG.isDebugEnabled() )
+        {
+            LOG.debug( "registed matchingRuleUse: " + matchingRuleUse );
+        }
+    }
+
+
+    public MatchingRuleUse lookup( String name ) throws NamingException
+    {
+        if ( !byName.containsKey( name ) )
+        {
+            throw new NamingException( "matchingRuleUse w/ name " + name + " not registered!" );
+        }
+
+        MatchingRuleUse matchingRuleUse = byName.get( name );
+        if ( LOG.isDebugEnabled() )
+        {
+            LOG.debug( "lookup with name '"+ name + "' of matchingRuleUse: " + matchingRuleUse );
+        }
+        return matchingRuleUse;
+    }
+
+
+    public boolean hasMatchingRuleUse( String name )
+    {
+        return byName.containsKey( name );
+    }
+
+
+    public String getSchemaName( String id ) throws NamingException
+    {
+        MatchingRuleUse mru = byName.get( id );
+        if ( mru != null )
+        {
+            return mru.getSchema();
+        }
+
+        throw new NamingException( "Name " + id + " not found in name to " + "MatchingRuleUse map!" );
+    }
+
+
+    public Iterator<MatchingRuleUse> iterator()
+    {
+        return byName.values().iterator();
+    }
+    
+    
+    public void unregister( String name ) throws NamingException
+    {
+        byName.remove( name );
+    }
+}
diff --git a/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/DefaultNameFormRegistry.java b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/DefaultNameFormRegistry.java
new file mode 100755
index 0000000..d84a9e8
--- /dev/null
+++ b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/DefaultNameFormRegistry.java
@@ -0,0 +1,153 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.registries;
+
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.shared.ldap.schema.NameForm;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * A plain old java object implementation of an NameFormRegistry.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class DefaultNameFormRegistry implements NameFormRegistry
+{
+    /** static class logger */
+    private static final Logger LOG = LoggerFactory.getLogger( DefaultNameFormRegistry.class );
+    /** maps an OID to an NameForm */
+    private final Map<String,NameForm> byOid;
+    /** the registry used to resolve names to OIDs */
+    private final OidRegistry oidRegistry;
+
+
+    // ------------------------------------------------------------------------
+    // C O N S T R U C T O R S
+    // ------------------------------------------------------------------------
+
+
+    /**
+     * Creates an empty DefaultNameFormRegistry.
+     *
+     * @param oidRegistry used by this registry for OID to name resolution of
+     * dependencies and to automatically register and unregister it's aliases and OIDs
+     */
+    public DefaultNameFormRegistry( OidRegistry oidRegistry )
+    {
+        this.byOid = new HashMap<String,NameForm>();
+        this.oidRegistry = oidRegistry;
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Service Methods
+    // ------------------------------------------------------------------------
+
+    public void register( NameForm nameForm ) throws NamingException
+    {
+        if ( byOid.containsKey( nameForm.getOid() ) )
+        {
+            throw new NamingException( "nameForm w/ OID " + nameForm.getOid()
+                + " has already been registered!" );
+        }
+
+        oidRegistry.register( nameForm.getName(), nameForm.getOid() );
+        byOid.put( nameForm.getOid(), nameForm );
+        if ( LOG.isDebugEnabled() )
+        {
+            LOG.debug( "registered nameForm: " + nameForm );
+        }
+    }
+
+
+    public NameForm lookup( String id ) throws NamingException
+    {
+        id = oidRegistry.getOid( id );
+
+        if ( !byOid.containsKey( id ) )
+        {
+            throw new NamingException( "nameForm w/ OID " + id + " not registered!" );
+        }
+
+        NameForm nameForm = byOid.get( id );
+        if ( LOG.isDebugEnabled() )
+        {
+            LOG.debug( "lookup with id '"+ id + "' of nameForm: " + nameForm );
+        }
+        return nameForm;
+    }
+
+
+    public boolean hasNameForm( String id )
+    {
+        if ( oidRegistry.hasOid( id ) )
+        {
+            try
+            {
+                return byOid.containsKey( oidRegistry.getOid( id ) );
+            }
+            catch ( NamingException e )
+            {
+                return false;
+            }
+        }
+
+        return false;
+    }
+
+
+    public String getSchemaName( String id ) throws NamingException
+    {
+        id = oidRegistry.getOid( id );
+        NameForm nf = byOid.get( id );
+        if ( nf != null )
+        {
+            return nf.getSchema();
+        }
+
+        throw new NamingException( "OID " + id + " not found in oid to " + "NameForm map!" );
+    }
+
+
+    public Iterator<NameForm> iterator()
+    {
+        return byOid.values().iterator();
+    }
+    
+    
+    public void unregister( String numericOid ) throws NamingException
+    {
+        if ( ! Character.isDigit( numericOid.charAt( 0 ) ) )
+        {
+            throw new NamingException( "Looks like the arg is not a numeric OID" );
+        }
+
+        byOid.remove( numericOid );
+    }
+}
diff --git a/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/DefaultNormalizerRegistry.java b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/DefaultNormalizerRegistry.java
new file mode 100755
index 0000000..f90ebd3
--- /dev/null
+++ b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/DefaultNormalizerRegistry.java
@@ -0,0 +1,205 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.registries;
+
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.shared.ldap.schema.Normalizer;
+import org.apache.directory.shared.ldap.schema.syntax.NormalizerDescription;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * The POJO implementation for the NormalizerRegistry service.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class DefaultNormalizerRegistry implements NormalizerRegistry
+{
+    /** static class logger */
+    private static final Logger LOG = LoggerFactory.getLogger( DefaultNormalizerRegistry.class );
+    
+    /** A speedup for debug */
+    private static final boolean DEBUG = LOG.isDebugEnabled();
+    
+    /** a map of Normalizers looked up by OID */
+    private final Map<String,Normalizer> byOid;
+    
+    /** maps an OID to a normalizerDescription */
+    private final Map<String,NormalizerDescription> oidToDescription;
+
+
+    // ------------------------------------------------------------------------
+    // C O N S T R U C T O R S
+    // ------------------------------------------------------------------------
+
+
+    /**
+     * Creates a new default DefaultNormalizerRegistry.
+     */
+    public DefaultNormalizerRegistry()
+    {
+        this.byOid = new HashMap<String, Normalizer>();
+        this.oidToDescription = new HashMap<String, NormalizerDescription>();
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Service Methods
+    // ------------------------------------------------------------------------
+
+    
+    public void register( NormalizerDescription description, Normalizer normalizer ) throws NamingException
+    {
+        String oid = description.getNumericOid();
+        if ( byOid.containsKey( oid ) )
+        {
+            throw new NamingException( "Normalizer already " + "registered for OID " + oid );
+        }
+
+        oidToDescription.put( oid, description );
+        byOid.put( oid, normalizer );
+        
+        if ( DEBUG )
+        {
+            LOG.debug( "registered normalizer with oid: " + oid );
+        }
+    }
+
+
+    public Normalizer lookup( String oid ) throws NamingException
+    {
+        if ( !byOid.containsKey( oid ) )
+        {
+            throw new NamingException( "Normalizer for OID " + oid + " does not exist!" );
+        }
+
+        Normalizer normalizer = byOid.get( oid );
+        
+        if ( DEBUG )
+        {
+            LOG.debug( "registered normalizer with oid: " + oid );
+        }
+        
+        return normalizer;
+    }
+
+
+    public boolean hasNormalizer( String oid )
+    {
+        return byOid.containsKey( oid );
+    }
+
+
+    public String getSchemaName( String oid ) throws NamingException
+    {
+        if ( ! Character.isDigit( oid.charAt( 0 ) ) )
+        {
+            throw new NamingException( "Looks like the arg is not a numeric OID" );
+        }
+
+        if ( oidToDescription.containsKey( oid ) )
+        {
+            return getSchema( oidToDescription.get( oid ) );
+        }
+
+        throw new NamingException( "OID " + oid + " not found in oid to " + "schema name map!" );
+    }
+
+
+    private static String getSchema( NormalizerDescription desc )
+    {
+        List values = desc.getExtensions().get( "X-SCHEMA" );
+        
+        if ( values == null || values.size() == 0 )
+        {
+            return "other";
+        }
+        
+        return desc.getExtensions().get( "X-SCHEMA" ).get( 0 );
+    }
+    
+
+    public Iterator<String> oidIterator()
+    {
+        return byOid.keySet().iterator();
+    }
+
+
+    public void unregister( String oid ) throws NamingException
+    {
+        if ( ! Character.isDigit( oid.charAt( 0 ) ) )
+        {
+            throw new NamingException( "OID " + oid + " is not a numeric OID" );
+        }
+
+        this.byOid.remove( oid );
+        this.oidToDescription.remove( oid );
+    }
+    
+    
+    public void unregisterSchemaElements( String schemaName )
+    {
+        List<String> oids = new ArrayList<String>( byOid.keySet() );
+        for ( String oid : oids )
+        {
+            NormalizerDescription description = oidToDescription.get( oid );
+            String schemaNameForOid = getSchema( description );
+            if ( schemaNameForOid.equalsIgnoreCase( schemaName ) )
+            {
+                byOid.remove( oid );
+                oidToDescription.remove( oid );
+            }
+        }
+    }
+
+
+    public void renameSchema( String originalSchemaName, String newSchemaName )
+    {
+        List<String> oids = new ArrayList<String>( byOid.keySet() );
+        for ( String oid : oids )
+        {
+            NormalizerDescription description = oidToDescription.get( oid );
+            String schemaNameForOid = getSchema( description );
+            if ( schemaNameForOid.equalsIgnoreCase( originalSchemaName ) )
+            {
+                List<String> schemaExt = description.getExtensions().get( "X-SCHEMA" );
+                schemaExt.clear();
+                schemaExt.add( newSchemaName );
+            }
+        }
+    }
+
+
+    public Iterator<NormalizerDescription> normalizerDescriptionIterator()
+    {
+        return oidToDescription.values().iterator();
+    }
+}
diff --git a/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/DefaultObjectClassRegistry.java b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/DefaultObjectClassRegistry.java
new file mode 100755
index 0000000..66a5ca8
--- /dev/null
+++ b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/DefaultObjectClassRegistry.java
@@ -0,0 +1,173 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.registries;
+
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.shared.ldap.schema.ObjectClass;
+import org.apache.directory.shared.ldap.util.StringTools;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * A plain old java object implementation of an ObjectClassRegistry.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class DefaultObjectClassRegistry implements ObjectClassRegistry
+{
+    /** static class logger */
+    private static final Logger LOG = LoggerFactory.getLogger( DefaultObjectClassRegistry.class );
+    
+    /** Speedup for DEBUG mode */
+    private static final boolean IS_DEBUG = LOG.isDebugEnabled();
+    
+    /** maps an OID to an ObjectClass */
+    private final Map<String,ObjectClass> byOid;
+    /** the registry used to resolve names to OIDs */
+    private final OidRegistry oidRegistry;
+
+
+    // ------------------------------------------------------------------------
+    // C O N S T R U C T O R S
+    // ------------------------------------------------------------------------
+
+    /**
+     * Creates an empty DefaultObjectClassRegistry.
+     *
+     * @param oidRegistry used by this registry for OID to name resolution of
+     * dependencies and to automatically register and unregister it's aliases and OIDs
+     */
+    public DefaultObjectClassRegistry( OidRegistry oidRegistry )
+    {
+        this.byOid = new HashMap<String,ObjectClass>();
+        this.oidRegistry = oidRegistry;
+    }
+
+    
+    // ------------------------------------------------------------------------
+    // Service Methods
+    // ------------------------------------------------------------------------
+
+    
+    public void register( ObjectClass objectClass ) throws NamingException
+    {
+        if ( byOid.containsKey( objectClass.getOid() ) )
+        {
+            throw new NamingException( "objectClass w/ OID " + objectClass.getOid()
+                + " has already been registered!" );
+        }
+
+        if ( objectClass.getNamesRef() != null && objectClass.getNamesRef().length > 0 )
+        {
+            oidRegistry.register( objectClass.getName(), objectClass.getOid() );
+        }
+        else
+        {
+            oidRegistry.register( objectClass.getOid(), objectClass.getOid() );
+        }
+        
+        byOid.put( objectClass.getOid(), objectClass );
+        
+        if ( IS_DEBUG )
+        {
+            LOG.debug( "registered objectClass: " + objectClass );
+        }
+    }
+
+
+    public ObjectClass lookup( String id ) throws NamingException
+    {
+        if ( StringTools.isEmpty( id ) )
+        {
+            throw new NamingException( "name should not be empty" );
+        }
+        
+        String oid = oidRegistry.getOid( id.toLowerCase() );
+
+        if ( !byOid.containsKey( oid ) )
+        {
+            throw new NamingException( "objectClass w/ OID " + oid + " not registered!" );
+        }
+
+        ObjectClass objectClass = byOid.get( oid );
+        
+        if ( IS_DEBUG )
+        {
+            LOG.debug( "looked objectClass with OID '" + oid + "' and got back " + objectClass );
+        }
+        return objectClass;
+    }
+
+
+    public boolean hasObjectClass( String id )
+    {
+        if ( oidRegistry.hasOid( id ) )
+        {
+            try
+            {
+                return byOid.containsKey( oidRegistry.getOid( id ) );
+            }
+            catch ( NamingException e )
+            {
+                return false;
+            }
+        }
+
+        return false;
+    }
+
+
+    public String getSchemaName( String id ) throws NamingException
+    {
+        id = oidRegistry.getOid( id );
+        ObjectClass oc = byOid.get( id );
+        if ( oc != null )
+        {
+            return oc.getSchema();
+        }
+
+        throw new NamingException( "OID " + id + " not found in oid to " + "ObjectClass map!" );
+    }
+
+
+    public Iterator<ObjectClass> iterator()
+    {
+        return byOid.values().iterator();
+    }
+    
+    
+    public void unregister( String numericOid ) throws NamingException
+    {
+        if ( ! Character.isDigit( numericOid.charAt( 0 ) ) )
+        {
+            throw new NamingException( "Looks like the arg is not a numeric OID" );
+        }
+
+        byOid.remove( numericOid );
+    }
+}
diff --git a/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/DefaultOidRegistry.java b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/DefaultOidRegistry.java
new file mode 100644
index 0000000..1df1bd5
--- /dev/null
+++ b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/DefaultOidRegistry.java
@@ -0,0 +1,300 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.registries;
+
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.naming.NamingException;
+import javax.naming.directory.NoSuchAttributeException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.directory.shared.asn1.primitives.OID;
+import org.apache.directory.shared.ldap.util.StringTools;
+
+
+/**
+ * Default OID registry implementation used to resolve a schema object OID 
+ * to a name and vice-versa.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class DefaultOidRegistry implements OidRegistry
+{
+    /** static class logger */
+    private static final Logger LOG = LoggerFactory.getLogger( DefaultOidRegistry.class );
+
+    /** Speedup for DEBUG mode */
+    private static final boolean IS_DEBUG = LOG.isDebugEnabled();
+    
+    /** Maps OID to a name or a list of names if more than one name exists */
+    private Map<String, List<String>> byOid = new HashMap<String, List<String>>();
+    
+    /** Maps several names to an OID */
+    private Map<String,String> byName = new HashMap<String,String>();
+
+
+    /**
+     * @see org.apache.directory.server.schema.registries.OidRegistry#getOid(java.lang.String)
+     */
+    public String getOid( String name ) throws NamingException
+    {
+        if ( StringTools.isEmpty( name ) )
+        {
+            throw new NamingException( "name should not be empty" );
+        }
+        
+        /* If name is an OID then we return it back since inherently the
+         * OID is another name for the object referred to by OID and the
+         * caller does not know that the argument is an OID String.
+         */
+        if ( StringTools.isDigit( name.charAt( 0 ) ) )
+        {
+            return name;
+        }
+
+        // If name is mapped to a OID already return OID
+        if ( byName.containsKey( name ) )
+        {
+            String oid = byName.get( name );
+            
+            if ( IS_DEBUG )
+            {
+                LOG.debug( "looked up OID '" + oid + "' with id '" + name + "'" );
+            }
+            
+            return oid;
+        }
+
+        /*
+         * As a last resort we check if name is not normalized and if the
+         * normalized version used as a key returns an OID.  If the normalized
+         * name works add the normalized name as a key with its OID to the
+         * byName lookup.  BTW these normalized versions of the key are not
+         * returned on a getNameSet.
+         */
+        String lowerCase = name.trim().toLowerCase();
+        
+        String oid = byName.get( lowerCase );
+        
+        if ( oid != null )
+        {
+            if ( IS_DEBUG )
+            {
+                LOG.debug( "looked up OID '" + oid + "' with id '" + name + "'" );
+            }
+
+            return oid;
+        }
+
+        NamingException fault = new NoSuchAttributeException( "OID for name '" + name + "' was not "
+            + "found within the OID registry" );
+        LOG.error( fault.getMessage() );
+        throw fault;
+    }
+
+
+    /**
+     * @see org.apache.directory.server.schema.registries.OidRegistry#hasOid(java.lang.String)
+     */
+    public boolean hasOid( String name )
+    {
+        if ( StringTools.isEmpty( name ) )
+        {
+            return false;
+        }
+        
+        String normalized = name.trim().toLowerCase();
+        
+        return byName.containsKey( normalized );
+    }
+
+
+    /**
+     * @see org.apache.directory.server.schema.registries.OidRegistry#getPrimaryName(java.lang.String)
+     */
+    public String getPrimaryName( String oid ) throws NamingException
+    {
+        List<String> value = byOid.get( oid );
+
+        if ( null == value )
+        {
+            throw new NamingException( "OID '" + oid + "' was not found within the OID registry" );
+        }
+
+        String name = value.get( 0 );
+        
+        if ( IS_DEBUG )
+        {
+            LOG.debug( "looked up primary name '" + name + "' with OID '" + oid + "'" );
+        }
+        
+        return name;
+    }
+
+
+    /**
+     * @see org.apache.directory.server.schema.registries.OidRegistry#getNameSet(java.lang.String)
+     */
+    public List<String> getNameSet( String oid ) throws NamingException
+    {
+        List<String> value = byOid.get( oid );
+
+        if ( null == value )
+        {
+            throw new NamingException( "OID '" + oid + "' was not found within the OID registry" );
+        }
+
+        if ( IS_DEBUG )
+        {
+            LOG.debug( "looked up names '" + value + "' for OID '" + oid + "'" );
+        }
+        
+        return value;
+    }
+
+
+    /**
+     * @see org.apache.directory.server.schema.registries.OidRegistry#list()
+     */
+    @SuppressWarnings("unchecked")
+    public Iterator list()
+    {
+        return Collections.unmodifiableSet( byOid.keySet() ).iterator();
+    }
+
+
+    /**
+     * Get the map of all the oids by their name
+     * @return The Map that contains all the oids
+     */
+    public Map<String, String> getOidByName()
+    {
+        return byName;
+    }
+
+
+    /**
+     * Get the map of all the oids by their name
+     * @return The Map that contains all the oids
+     */
+    public Map<String, List<String>> getNameByOid()
+    {
+        return byOid;
+    }
+
+
+    /**
+     * @see org.apache.directory.server.schema.registries.OidRegistry#register(String, String)
+     */
+    @SuppressWarnings("unchecked")
+    public void register( String name, String oid ) throws NamingException
+    {
+        if ( !OID.isOID( oid ) )
+        {
+            String message = "Swap the parameter order: the oid " + 
+            "does not start with a digit, or is not an OID!";
+            
+            LOG.debug( message );
+            throw new NamingException( message );
+        }
+        
+        if ( StringTools.isEmpty( name ) )
+        {
+            String message = "The name is empty";
+            LOG.error( message );
+            throw new NamingException( message );
+        }
+
+        /*
+         * Add the entry for the given name as is and its lowercased version if
+         * the lower cased name is different from the given name name.  
+         */
+        String lowerCase = name.trim().toLowerCase();
+
+        // Put both the name and the oid as names
+        byName.put( lowerCase, oid );
+        byName.put( oid, oid );
+
+        /*
+         * Update OID Map
+         * 
+         * 1). Check if we already have a value[s] stored
+         *      1a). Value is a single value and is a String
+         *          Replace value with list containing old and new values
+         *      1b). More than one value stored in a list
+         *          Add new value to the list
+         * 2). If we do not have a value then we just add it as a String
+         */
+        List<String> value;
+        
+        if ( !byOid.containsKey( oid ) )
+        {
+            value = new ArrayList<String>( 1 );
+            value.add( lowerCase );
+        }
+        else
+        {
+            value = byOid.get( oid );
+            
+            if ( value.contains( lowerCase ) )
+            {
+                return;
+            }
+            else
+            {
+                value.add( lowerCase );
+            }
+        }
+
+        byOid.put( oid, value );
+        
+        if ( IS_DEBUG )
+        {
+            LOG.debug( "registed name '" + name + "' with OID: " + oid );
+        }
+    }
+
+
+    public void unregister( String numericOid ) throws NamingException
+    {
+        // First, remove the <OID, names> from the byOID map
+        List<String> names = byOid.remove( numericOid );
+        
+        // Then remove all the <name, OID> from the byName map
+        if ( names != null )
+        {
+            for ( String name:names )
+            {
+                byName.remove( name );
+            }
+        }
+
+        // Last, remove the <OID, OID> from the byName map
+        byName.remove( numericOid );
+    }
+}
diff --git a/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/DefaultRegistries.java b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/DefaultRegistries.java
new file mode 100755
index 0000000..c7654f6
--- /dev/null
+++ b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/DefaultRegistries.java
@@ -0,0 +1,574 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.registries;
+
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.schema.bootstrap.Schema;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.schema.MatchingRule;
+import org.apache.directory.shared.ldap.schema.ObjectClass;
+import org.apache.directory.shared.ldap.schema.SchemaObject;
+import org.apache.directory.shared.ldap.schema.Syntax;
+
+
+/**
+ * A set of boostrap registries used to fire up the server.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class DefaultRegistries implements Registries
+{
+    private DefaultAttributeTypeRegistry attributeTypeRegistry;
+    private DefaultComparatorRegistry comparatorRegistry;
+    private DefaultDitContentRuleRegistry ditContentRuleRegistry;
+    private DefaultDitStructureRuleRegistry ditStructureRuleRegistry;
+    private DefaultMatchingRuleRegistry matchingRuleRegistry;
+    private DefaultMatchingRuleUseRegistry matchingRuleUseRegistry;
+    private DefaultNameFormRegistry nameFormRegistry;
+    private DefaultNormalizerRegistry normalizerRegistry;
+    private DefaultObjectClassRegistry objectClassRegistry;
+    private OidRegistry oidRegistry;
+    private DefaultSyntaxCheckerRegistry syntaxCheckerRegistry;
+    private DefaultSyntaxRegistry syntaxRegistry;
+    private Map<String,Schema> loadedByName = new HashMap<String, Schema>();
+    private final SchemaLoader schemaLoader;
+    private final String name;
+
+
+    public DefaultRegistries( String name, SchemaLoader schemaLoader, OidRegistry registry )
+    {
+        this.name = name;
+        this.schemaLoader = schemaLoader;
+        this.schemaLoader.setListener( new SchemaLoaderListener() {
+            public void schemaLoaded( Schema schema )
+            {
+                loadedByName.put( schema.getSchemaName(), schema );
+            }
+        });
+        oidRegistry = registry;
+        normalizerRegistry = new DefaultNormalizerRegistry();
+        comparatorRegistry = new DefaultComparatorRegistry();
+        syntaxCheckerRegistry = new DefaultSyntaxCheckerRegistry();
+        syntaxRegistry = new DefaultSyntaxRegistry( oidRegistry );
+        matchingRuleRegistry = new DefaultMatchingRuleRegistry( oidRegistry );
+        attributeTypeRegistry = new DefaultAttributeTypeRegistry( oidRegistry );
+        objectClassRegistry = new DefaultObjectClassRegistry( oidRegistry );
+        ditContentRuleRegistry = new DefaultDitContentRuleRegistry( oidRegistry );
+        ditStructureRuleRegistry = new DefaultDitStructureRuleRegistry( oidRegistry );
+        matchingRuleUseRegistry = new DefaultMatchingRuleUseRegistry();
+        nameFormRegistry = new DefaultNameFormRegistry( oidRegistry );
+    }
+
+
+    public String getName()
+    {
+        return name;
+    }
+    
+    
+    public AttributeTypeRegistry getAttributeTypeRegistry()
+    {
+        return attributeTypeRegistry;
+    }
+
+
+    public ComparatorRegistry getComparatorRegistry()
+    {
+        return comparatorRegistry;
+    }
+
+
+    public DITContentRuleRegistry getDitContentRuleRegistry()
+    {
+        return ditContentRuleRegistry;
+    }
+
+
+    public DITStructureRuleRegistry getDitStructureRuleRegistry()
+    {
+        return ditStructureRuleRegistry;
+    }
+
+
+    public MatchingRuleRegistry getMatchingRuleRegistry()
+    {
+        return matchingRuleRegistry;
+    }
+
+
+    public MatchingRuleUseRegistry getMatchingRuleUseRegistry()
+    {
+        return matchingRuleUseRegistry;
+    }
+
+
+    public NameFormRegistry getNameFormRegistry()
+    {
+        return nameFormRegistry;
+    }
+
+
+    public NormalizerRegistry getNormalizerRegistry()
+    {
+        return normalizerRegistry;
+    }
+
+
+    public ObjectClassRegistry getObjectClassRegistry()
+    {
+        return objectClassRegistry;
+    }
+
+
+    public OidRegistry getOidRegistry()
+    {
+        return oidRegistry;
+    }
+
+
+    public SyntaxCheckerRegistry getSyntaxCheckerRegistry()
+    {
+        return syntaxCheckerRegistry;
+    }
+
+
+    public SyntaxRegistry getSyntaxRegistry()
+    {
+        return syntaxRegistry;
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Code used to sanity check the resolution of entities in registries
+    // ------------------------------------------------------------------------
+
+    /**
+     * Attempts to resolve the dependent schema objects of all entities that
+     * refer to other objects within the registries.  Null references will be
+     * handed appropriately.
+     *
+     * @return a list of exceptions encountered while resolving entities
+     */
+    public List<Throwable> checkRefInteg()
+    {
+        ArrayList<Throwable> errors = new ArrayList<Throwable>();
+
+        Iterator list = objectClassRegistry.iterator();
+        while ( list.hasNext() )
+        {
+            ObjectClass oc = ( ObjectClass ) list.next();
+            resolve( oc, errors );
+        }
+
+        list = attributeTypeRegistry.list();
+        while ( list.hasNext() )
+        {
+            AttributeType at = ( AttributeType ) list.next();
+            resolve( at, errors );
+        }
+
+        list = matchingRuleRegistry.iterator();
+        while ( list.hasNext() )
+        {
+            MatchingRule mr = ( MatchingRule ) list.next();
+            resolve( mr, errors );
+        }
+
+        list = syntaxRegistry.iterator();
+        while ( list.hasNext() )
+        {
+            Syntax syntax = ( Syntax ) list.next();
+            resolve( syntax, errors );
+        }
+
+        return errors;
+    }
+
+
+    /**
+     * Attempts to resolve the SyntaxChecker associated with a Syntax.
+     *
+     * @param syntax the Syntax to resolve the SyntaxChecker of
+     * @param errors the list of errors to add exceptions to
+     * @return true if it succeeds, false otherwise
+     */
+    private boolean resolve( Syntax syntax, List<Throwable> errors )
+    {
+        if ( syntax == null )
+        {
+            return true;
+        }
+
+        try
+        {
+            syntax.getSyntaxChecker();
+            return true;
+        }
+        catch ( NamingException e )
+        {
+            errors.add( e );
+            return false;
+        }
+    }
+
+
+    private boolean resolve( MatchingRule mr, List<Throwable> errors )
+    {
+        boolean isSuccess = true;
+
+        if ( mr == null )
+        {
+            return true;
+        }
+
+        try
+        {
+            if ( mr.getComparator() == null )
+            {
+                String schema = matchingRuleRegistry.getSchemaName( mr.getOid() );
+                errors.add( new NullPointerException( "matchingRule " + mr.getName() + " in schema " + schema
+                    + " with OID " + mr.getOid() + " has a null comparator" ) );
+                isSuccess = false;
+            }
+        }
+        catch ( NamingException e )
+        {
+            errors.add( e );
+            isSuccess = false;
+        }
+
+        try
+        {
+            if ( mr.getNormalizer() == null )
+            {
+                String schema = matchingRuleRegistry.getSchemaName( mr.getOid() );
+                errors.add( new NullPointerException( "matchingRule " + mr.getName() + " in schema " + schema
+                    + " with OID " + mr.getOid() + " has a null normalizer" ) );
+                isSuccess = false;
+            }
+        }
+        catch ( NamingException e )
+        {
+            errors.add( e );
+            isSuccess = false;
+        }
+
+        try
+        {
+            isSuccess &= resolve( mr.getSyntax(), errors );
+
+            if ( mr.getSyntax() == null )
+            {
+                String schema = matchingRuleRegistry.getSchemaName( mr.getOid() );
+                errors.add( new NullPointerException( "matchingRule " + mr.getName() + " in schema " + schema
+                    + " with OID " + mr.getOid() + " has a null Syntax" ) );
+                isSuccess = false;
+            }
+        }
+        catch ( NamingException e )
+        {
+            errors.add( e );
+            isSuccess = false;
+        }
+
+        return isSuccess;
+    }
+
+
+    private boolean resolve( AttributeType at, List<Throwable> errors )
+    {
+        boolean isSuccess = true;
+
+        boolean hasMatchingRule = false;
+
+        if ( at == null )
+        {
+            return true;
+        }
+
+        try
+        {
+            isSuccess &= resolve( at.getSuperior(), errors );
+        }
+        catch ( NamingException e )
+        {
+            errors.add( e );
+
+            isSuccess = false;
+        }
+
+        try
+        {
+            isSuccess &= resolve( at.getEquality(), errors );
+
+            if ( at.getEquality() != null )
+            {
+                hasMatchingRule |= true;
+            }
+        }
+        catch ( NamingException e )
+        {
+            errors.add( e );
+
+            isSuccess = false;
+        }
+
+        try
+        {
+            isSuccess &= resolve( at.getOrdering(), errors );
+
+            if ( at.getOrdering() != null )
+            {
+                hasMatchingRule |= true;
+            }
+        }
+        catch ( NamingException e )
+        {
+            errors.add( e );
+
+            isSuccess = false;
+        }
+
+        try
+        {
+            isSuccess &= resolve( at.getSubstr(), errors );
+
+            if ( at.getSubstr() != null )
+            {
+                hasMatchingRule |= true;
+            }
+        }
+        catch ( NamingException e )
+        {
+            errors.add( e );
+
+            isSuccess = false;
+        }
+
+        try
+        {
+            isSuccess &= resolve( at.getSyntax(), errors );
+
+            if ( at.getSyntax() == null )
+            {
+                String schema = attributeTypeRegistry.getSchemaName( at.getOid() );
+
+                errors.add( new NullPointerException( "attributeType " + at.getName() + " in schema " + schema
+                    + " with OID " + at.getOid() + " has a null Syntax" ) );
+
+                isSuccess = false;
+            }
+        }
+        catch ( NamingException e )
+        {
+            errors.add( e );
+
+            isSuccess = false;
+        }
+
+        //        try
+        //        {
+        //            String schema = attributeTypeRegistry.getSchemaName( at.getOid() );
+        //            if ( ! hasMatchingRule && at.getSyntax().isHumanReadable() )
+        //            {
+        //                errors.add( new NullPointerException( "attributeType "
+        //                        + at.getName() + " in schema " + schema + " with OID "
+        //                        + at.getOid() + " has a no matchingRules defined" ) );
+        //                isSuccess = false;
+        //            }
+        //        }
+        //        catch ( NamingException e )
+        //        {
+        //            errors.add( e );
+        //            isSuccess = false;
+        //        }
+
+        return isSuccess;
+    }
+
+
+    private boolean resolve( ObjectClass oc, List<Throwable> errors )
+    {
+        boolean isSuccess = true;
+
+        if ( oc == null )
+        {
+            return true;
+        }
+
+        ObjectClass[] superiors = new org.apache.directory.shared.ldap.schema.ObjectClass[0];
+
+        try
+        {
+            superiors = oc.getSuperClasses();
+        }
+        catch ( NamingException e )
+        {
+            superiors = new ObjectClass[0];
+            isSuccess = false;
+            errors.add( e );
+        }
+
+        for ( int ii = 0; ii < superiors.length; ii++ )
+        {
+            isSuccess &= resolve( superiors[ii], errors );
+        }
+
+        AttributeType[] mayList = new org.apache.directory.shared.ldap.schema.AttributeType[0];
+
+        try
+        {
+            mayList = oc.getMayList();
+        }
+        catch ( NamingException e )
+        {
+            mayList = new AttributeType[0];
+            isSuccess = false;
+            errors.add( e );
+        }
+
+        for ( int ii = 0; ii < mayList.length; ii++ )
+        {
+            isSuccess &= resolve( mayList[ii], errors );
+        }
+
+        AttributeType[] mustList = new org.apache.directory.shared.ldap.schema.AttributeType[0];
+
+        try
+        {
+            mustList = oc.getMustList();
+        }
+        catch ( NamingException e )
+        {
+            mustList = new AttributeType[0];
+            isSuccess = false;
+            errors.add( e );
+        }
+
+        for ( int ii = 0; ii < mustList.length; ii++ )
+        {
+            isSuccess &= resolve( mustList[ii], errors );
+        }
+
+        return isSuccess;
+    }
+
+    
+    /**
+     * Alterations to the returned map of schema names to schema objects does not 
+     * change the map returned from this method.  The returned map is however mutable.
+     */
+    public Map<String, Schema> getLoadedSchemas()
+    {
+        return new HashMap<String,Schema>( loadedByName );
+    }
+
+
+    public void load( String schemaName ) throws NamingException
+    {
+        load( schemaName, new Properties() );
+    }
+
+
+    public void load( String schemaName, Properties schemaProperties ) throws NamingException
+    {
+        Schema schema = schemaLoader.getSchema( schemaName, schemaProperties );
+        
+        if ( schema.isDisabled() )
+        {
+            throw new NamingException( "Disabled schemas cannot be loaded into registries." );
+        }
+        
+        loadedByName.put( schema.getSchemaName(), schema );
+        schemaLoader.load( schema, this, false );
+    }
+    
+    
+    public void unload( String schemaName ) throws NamingException
+    {
+        disableSchema( ditStructureRuleRegistry, schemaName );
+        disableSchema( ditContentRuleRegistry, schemaName );
+        disableSchema( matchingRuleUseRegistry, schemaName );
+        disableSchema( nameFormRegistry, schemaName );
+        disableSchema( objectClassRegistry, schemaName );
+        disableSchema( attributeTypeRegistry, schemaName );
+        disableSchema( matchingRuleRegistry, schemaName );
+        disableSchema( syntaxRegistry, schemaName );
+
+        normalizerRegistry.unregisterSchemaElements( schemaName );
+        comparatorRegistry.unregisterSchemaElements( schemaName );
+        syntaxCheckerRegistry.unregisterSchemaElements( schemaName );
+        loadedByName.remove( schemaName );
+    }
+
+
+    private void disableSchema( SchemaObjectRegistry registry, String schemaName ) throws NamingException
+    {
+        Iterator<? extends SchemaObject> objects = registry.iterator();
+        List<String> unregistered = new ArrayList<String>();
+        while ( objects.hasNext() )
+        {
+            SchemaObject obj = objects.next();
+            if ( obj.getSchema().equalsIgnoreCase( schemaName ) )
+            {
+                unregistered.add( obj.getOid() );
+            }
+        }
+        
+        for ( String oid : unregistered )
+        {
+            registry.unregister( oid );
+        }
+    }
+    
+
+    public SchemaLoader setSchemaLoader()
+    {
+        return schemaLoader;
+    }
+
+
+    public Schema getSchema( String schemaName )
+    {
+        return this.loadedByName.get( schemaName );
+    }
+
+
+    public void addToLoadedSet( Schema schema )
+    {
+        loadedByName.put( schema.getSchemaName(), schema );
+    }
+
+
+    public void removeFromLoadedSet( String schemaName )
+    {
+        loadedByName.remove( schemaName );
+    }
+}
diff --git a/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/DefaultSyntaxCheckerRegistry.java b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/DefaultSyntaxCheckerRegistry.java
new file mode 100755
index 0000000..cc7f8de
--- /dev/null
+++ b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/DefaultSyntaxCheckerRegistry.java
@@ -0,0 +1,196 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.registries;
+
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.shared.ldap.schema.syntax.SyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.SyntaxCheckerDescription;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * The POJO implementation for the SyntaxCheckerRegistry service.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class DefaultSyntaxCheckerRegistry implements SyntaxCheckerRegistry
+{
+    /** static class logger */
+    private static final Logger LOG = LoggerFactory.getLogger( DefaultSyntaxCheckerRegistry.class );
+    /** a map by OID of SyntaxCheckers */
+    private final Map<String, SyntaxChecker> byOid;
+    /** maps an OID to a syntaxCheckerDescription */
+    private final Map<String, SyntaxCheckerDescription> oidToDescription;
+
+
+    // ------------------------------------------------------------------------
+    // C O N S T R U C T O R S
+    // ------------------------------------------------------------------------
+
+
+    /**
+     * Creates an instance of a DefaultSyntaxRegistry.
+     */
+    public DefaultSyntaxCheckerRegistry()
+    {
+        this.byOid = new HashMap<String, SyntaxChecker>();
+        this.oidToDescription = new HashMap<String, SyntaxCheckerDescription>();
+    }
+
+
+    // ------------------------------------------------------------------------
+    // Service Methods
+    // ------------------------------------------------------------------------
+
+    
+    public void register( SyntaxCheckerDescription syntaxCheckerDescription, SyntaxChecker syntaxChecker ) throws NamingException
+    {
+        if ( byOid.containsKey( syntaxChecker.getSyntaxOid() ) )
+        {
+            throw new NamingException( "SyntaxChecker with OID " + syntaxChecker.getSyntaxOid()
+                + " already registered!" );
+        }
+
+        byOid.put( syntaxChecker.getSyntaxOid(), syntaxChecker );
+        oidToDescription.put( syntaxChecker.getSyntaxOid(), syntaxCheckerDescription );
+        if ( LOG.isDebugEnabled() )
+        {
+            LOG.debug( "registered syntaxChecher for OID " + syntaxChecker.getSyntaxOid() );
+        }
+    }
+
+
+    public SyntaxChecker lookup( String oid ) throws NamingException
+    {
+        if ( !byOid.containsKey( oid ) )
+        {
+            throw new NamingException( "SyntaxChecker for OID " + oid + " not found!" );
+        }
+
+        SyntaxChecker syntaxChecker = byOid.get( oid );
+        if ( LOG.isDebugEnabled() )
+        {
+            LOG.debug( "looked up syntaxChecher with OID " + oid );
+        }
+        return syntaxChecker;
+    }
+
+
+    public boolean hasSyntaxChecker( String oid )
+    {
+        return byOid.containsKey( oid );
+    }
+
+
+    public String getSchemaName( String oid ) throws NamingException
+    {
+        if ( ! Character.isDigit( oid.charAt( 0 ) ) )
+        {
+            throw new NamingException( "Looks like the arg is not a numeric OID" );
+        }
+
+        if ( oidToDescription.containsKey( oid ) )
+        {
+            return getSchema( oidToDescription.get( oid ) );
+        }
+
+        throw new NamingException( "OID " + oid + " not found in oid to " + "schema name map!" );
+    }
+    
+    
+    private static String getSchema( SyntaxCheckerDescription desc ) 
+    {
+        List<String> ext = desc.getExtensions().get( "X-SCHEMA" );
+        
+        if ( ext == null || ext.size() == 0 )
+        {
+            return "other";
+        }
+        
+        return ext.get( 0 );
+    }
+
+
+    public Iterator<SyntaxChecker> iterator()
+    {
+        return byOid.values().iterator();
+    }
+
+
+    public void unregister( String numericOid ) throws NamingException
+    {
+        if ( ! Character.isDigit( numericOid.charAt( 0 ) ) )
+        {
+            throw new NamingException( "Looks like the arg is not a numeric OID" );
+        }
+
+        byOid.remove( numericOid );
+        oidToDescription.remove( numericOid );
+    }
+    
+    
+    public void unregisterSchemaElements( String schemaName )
+    {
+        List<String> oids = new ArrayList<String>( byOid.keySet() );
+        for ( String oid : oids )
+        {
+            SyntaxCheckerDescription description = oidToDescription.get( oid );
+            String schemaNameForOid = getSchema( description );
+            if ( schemaNameForOid.equalsIgnoreCase( schemaName ) )
+            {
+                byOid.remove( oid );
+                oidToDescription.remove( oid );
+            }
+        }
+    }
+
+
+    public void renameSchema( String originalSchemaName, String newSchemaName )
+    {
+        List<String> oids = new ArrayList<String>( byOid.keySet() );
+        for ( String oid : oids )
+        {
+            SyntaxCheckerDescription description = oidToDescription.get( oid );
+            String schemaNameForOid = getSchema( description );
+            if ( schemaNameForOid.equalsIgnoreCase( originalSchemaName ) )
+            {
+                List<String> values = description.getExtensions().get( "X-SCHEMA" );
+                values.clear();
+                values.add( newSchemaName );
+            }
+        }
+    }
+
+
+    public Iterator<SyntaxCheckerDescription> syntaxCheckerDescriptionIterator()
+    {
+        return oidToDescription.values().iterator();
+    }
+}
diff --git a/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/DefaultSyntaxRegistry.java b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/DefaultSyntaxRegistry.java
new file mode 100644
index 0000000..06a2cf6
--- /dev/null
+++ b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/DefaultSyntaxRegistry.java
@@ -0,0 +1,174 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.registries;
+
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.shared.ldap.schema.Syntax;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * A SyntaxRegistry service available during server startup when other resources
+ * like a syntax backing store is unavailable.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class DefaultSyntaxRegistry implements SyntaxRegistry
+{
+    /** static class logger */
+    private static final Logger LOG = LoggerFactory.getLogger( DefaultSyntaxRegistry.class );
+    
+    /** Speedup for DEBUG mode */
+    private static final boolean IS_DEBUG = LOG.isDebugEnabled();
+    
+    /** a map of entries using an OID for the key and a Syntax for the value */
+    private final Map<String,Syntax> byOid;
+    /** the OID oidRegistry this oidRegistry uses to register new syntax OIDs */
+    private final OidRegistry oidRegistry;
+
+
+    // ------------------------------------------------------------------------
+    // C O N S T R U C T O R S
+    // ------------------------------------------------------------------------
+
+    /**
+     * Creates a DefaultSyntaxRegistry.
+     *
+     * @param registry used by this registry for OID to name resolution of
+     * dependencies and to automatically register and unregister it's aliases and OIDs
+     */
+    public DefaultSyntaxRegistry( OidRegistry registry )
+    {
+        this.oidRegistry = registry;
+        this.byOid = new HashMap<String,Syntax>();
+    }
+
+
+    // ------------------------------------------------------------------------
+    // SyntaxRegistry interface methods
+    // ------------------------------------------------------------------------
+
+    
+    public Syntax lookup( String id ) throws NamingException
+    {
+        id = oidRegistry.getOid( id );
+
+        if ( byOid.containsKey( id ) )
+        {
+            Syntax syntax = byOid.get( id );
+            
+            if ( IS_DEBUG )
+            {
+                LOG.debug( "looked up using id '" + id + "': " + syntax );
+            }
+            
+            return syntax;
+        }
+
+        throw new NamingException( "Unknown syntax OID " + id );
+    }
+
+
+    public void register( Syntax syntax ) throws NamingException
+    {
+        if ( byOid.containsKey( syntax.getOid() ) )
+        {
+            throw new NamingException( "syntax w/ OID " + syntax.getOid()
+                + " has already been registered!" );
+        }
+
+        if ( syntax.getName() != null )
+        {
+            oidRegistry.register( syntax.getName(), syntax.getOid() );
+        }
+        else
+        {
+            oidRegistry.register( syntax.getOid(), syntax.getOid() );
+        }
+
+        byOid.put( syntax.getOid(), syntax );
+        
+        if ( IS_DEBUG )
+        {
+            LOG.debug( "registered syntax: " + syntax );
+        }
+    }
+
+
+    public boolean hasSyntax( String id )
+    {
+        if ( oidRegistry.hasOid( id ) )
+        {
+            try
+            {
+                return byOid.containsKey( oidRegistry.getOid( id ) );
+            }
+            catch ( NamingException e )
+            {
+                return false;
+            }
+        }
+
+        return false;
+    }
+
+
+    public String getSchemaName( String id ) throws NamingException
+    {
+        if ( ! Character.isDigit( id.charAt( 0 ) ) )
+        {
+            throw new NamingException( "Looks like the arg is not a numeric OID" );
+        }
+
+        id = oidRegistry.getOid( id );
+        Syntax syntax = byOid.get( id );
+        if ( syntax != null )
+        {
+            return syntax.getSchema();
+        }
+
+        throw new NamingException( "OID " + id + " not found in oid to " + "Syntax map!" );
+    }
+
+
+    public Iterator<Syntax> iterator()
+    {
+        return byOid.values().iterator();
+    }
+
+
+    public void unregister( String numericOid ) throws NamingException
+    {
+        if ( ! Character.isDigit( numericOid.charAt( 0 ) ) )
+        {
+            throw new NamingException( "Looks like the arg is not a numeric OID" );
+        }
+
+        byOid.remove( numericOid );
+    }
+}
diff --git a/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/MatchingRuleRegistry.java b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/MatchingRuleRegistry.java
new file mode 100644
index 0000000..6db73dd
--- /dev/null
+++ b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/MatchingRuleRegistry.java
@@ -0,0 +1,76 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.registries;
+
+
+import java.util.Iterator;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.shared.ldap.schema.MatchingRule;
+
+
+/**
+ * A registry used to track system matchingRules.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public interface MatchingRuleRegistry extends SchemaObjectRegistry
+{
+    /**
+     * Registers a MatchingRule with this registry.
+     * 
+     * @param matchingRule the MatchingRule to register
+     * @throws NamingException if the matchingRule is already registered or the 
+     * registration operation is not supported
+     */
+    void register( MatchingRule matchingRule ) throws NamingException;
+
+
+    /**
+     * Looks up a MatchingRule by its unique Object Identifier or by name.
+     * 
+     * @param id the object identifier or the name identifier
+     * @return the MatchingRule for the id
+     * @throws NamingException if there is a backing store failure or the 
+     * MatchingRule does not exist.
+     */
+    MatchingRule lookup( String id ) throws NamingException;
+
+
+    /**
+     * Checks to see if a MatchingRule exists.  Backing store failures simply 
+     * return false.
+     * 
+     * @param oid the object identifier
+     * @return true if a MatchingRule definition exists for the oid, false 
+     * otherwise
+     */
+    boolean hasMatchingRule( String oid );
+
+
+    /**
+     * Gets an Iterator over the MatchingRules within this registry.
+     *
+     * @return an iterator over all MatchingRules in registry
+     */
+    Iterator<MatchingRule> iterator();
+}
diff --git a/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/MatchingRuleUseRegistry.java b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/MatchingRuleUseRegistry.java
new file mode 100755
index 0000000..5a6b700
--- /dev/null
+++ b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/MatchingRuleUseRegistry.java
@@ -0,0 +1,76 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.registries;
+
+
+import java.util.Iterator;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.shared.ldap.schema.MatchingRuleUse;
+
+
+/**
+ * A MatchingRuleUse registry service interface.  MatchingRuleUse objects are
+ * special in that they do not have unique OID's specifically assigned to them.
+ * Their OID is really the OID of the MatchingRule they refer to.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public interface MatchingRuleUseRegistry extends SchemaObjectRegistry
+{
+    /**
+     * Registers a MatchingRuleUse with this registry.
+     *
+     * @param matchingRuleUse the matchingRuleUse to register
+     * @throws NamingException if the MatchingRuleUse is already registered or
+     * the registration operation is not supported
+     */
+    void register( MatchingRuleUse matchingRuleUse ) throws NamingException;
+
+
+    /**
+     * Looks up an matchingRuleUse by its name.
+     * 
+     * @param name the name of the matchingRuleUse
+     * @return the MatchingRuleUse instance for the name
+     * @throws NamingException if the MatchingRuleUse does not exist
+     */
+    MatchingRuleUse lookup( String name ) throws NamingException;
+
+    
+    /**
+     * Checks to see if an matchingRuleUse exists.
+     * 
+     * @param name the name of the matchingRuleUse
+     * @return true if an matchingRuleUse definition exists for the name, false
+     * otherwise
+     */
+    boolean hasMatchingRuleUse( String name );
+
+
+    /**
+     * Lists all the MatchingRuleUses within this registry.
+     *
+     * @return an Iterator over all the MatchingRuleUses within this registry
+     */
+    Iterator<MatchingRuleUse> iterator();
+}
diff --git a/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/NameFormRegistry.java b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/NameFormRegistry.java
new file mode 100755
index 0000000..930b01c
--- /dev/null
+++ b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/NameFormRegistry.java
@@ -0,0 +1,74 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.registries;
+
+
+import java.util.Iterator;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.shared.ldap.schema.NameForm;
+
+
+/**
+ * An NameForm registry service interface.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public interface NameFormRegistry extends SchemaObjectRegistry
+{
+    /**
+     * Registers a NameForm with this registry.
+     * 
+     * @param nameForm the nameForm to register
+     * @throws NamingException if the NameForm is already registered or the
+     * registration operation is not supported
+     */
+    void register( NameForm nameForm ) throws NamingException;
+
+
+    /**
+     * Looks up a nameForm by its unique Object Identifier or by name.
+     * 
+     * @param id the object identifier or name
+     * @return the NameForm instance for the id
+     * @throws NamingException if the NameForm does not exist
+     */
+    NameForm lookup( String id ) throws NamingException;
+
+
+    /**
+     * Checks to see if an nameForm exists.
+     * 
+     * @param id the object identifier or name
+     * @return true if an nameForm definition exists for the oid, false
+     * otherwise
+     */
+    boolean hasNameForm( String id );
+
+
+    /**
+     * Lists all the NameForms within this registry.
+     *
+     * @return an Iterator over all the NameForms within this registry
+     */
+    Iterator<NameForm> iterator();
+}
diff --git a/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/NormalizerRegistry.java b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/NormalizerRegistry.java
new file mode 100644
index 0000000..449d86e
--- /dev/null
+++ b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/NormalizerRegistry.java
@@ -0,0 +1,125 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.registries;
+
+
+import java.util.Iterator;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.shared.ldap.schema.Normalizer;
+import org.apache.directory.shared.ldap.schema.syntax.NormalizerDescription;
+
+
+/**
+ * Normalizer registry service interface.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public interface NormalizerRegistry
+{
+    /**
+     * Registers a Normalizer with this registry.
+     * 
+     * @param normalizer the Normalizer to register
+     * @throws NamingException if the Normalizer is already registered or the
+     *      registration operation is not supported
+     */
+    void register( NormalizerDescription description, Normalizer normalizer ) throws NamingException;
+
+
+    /**
+     * Looks up a Normalizer by its unique Object Identifier.
+     * 
+     * @param oid the object identifier
+     * @return the Normalizer for the oid
+     * @throws NamingException if there is a backing store failure or the 
+     *      Normalizer does not exist.
+     */
+    Normalizer lookup( String oid ) throws NamingException;
+
+
+    /**
+     * Gets the name of the schema this schema object is associated with.
+     *
+     * @param oid the object identifier
+     * @return the schema name
+     * @throws NamingException if the schema object does not exist
+     */
+    String getSchemaName( String oid ) throws NamingException;
+
+
+    /**
+     * Checks to see if a Normalizer exists.  Backing store failures simply 
+     * return false.
+     * 
+     * @param oid the object identifier
+     * @return true if a Normalizer definition exists for the oid, false 
+     *      otherwise
+     */
+    boolean hasNormalizer( String oid );
+    
+    
+    /**
+     * Used to iterate through all normalizers.  We have to iterate over the
+     * OID String keys because these objects do not associate a matchingRule OID
+     * with them as a class member.
+     *  
+     * @return an Iterator over the set of OID Strings in this registry
+     */
+    Iterator<String> oidIterator();
+
+    
+    /**
+     * Used to iterate through all normalizerDescriptions.
+     *  
+     * @return an Iterator over the set of NormalizerDescriptions in this registry
+     */
+    Iterator<NormalizerDescription> normalizerDescriptionIterator();
+
+    
+    /**
+     * Unregisters a normalizer from this registry by OID.
+     * 
+     * @param oid the numeric OID of the matchingRule the normalizer is for
+     * @throws NamingException if the provided argument is not a numeric OID
+     */
+    void unregister( String oid ) throws NamingException;
+    
+    
+    /**
+     * Unregisters normalizers from this registry associated with a schema.
+     *
+     * @param schemaName the name of the schema whose normalizers are 
+     * removed from this registry
+     */
+    void unregisterSchemaElements( String schemaName );
+
+    
+    /**
+     * Renames the schemaName associated with entities within this 
+     * registry to a new schema name.
+     * 
+     * @param originalSchemaName the original schema name
+     * @param newSchemaName the new name to give to the schema
+     */
+    void renameSchema( String originalSchemaName, String newSchemaName );
+}
diff --git a/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/ObjectClassRegistry.java b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/ObjectClassRegistry.java
new file mode 100755
index 0000000..23a10d0
--- /dev/null
+++ b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/ObjectClassRegistry.java
@@ -0,0 +1,74 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.registries;
+
+
+import java.util.Iterator;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.shared.ldap.schema.ObjectClass;
+
+
+/**
+ * ObjectClass registry service interface.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public interface ObjectClassRegistry extends SchemaObjectRegistry
+{
+    /**
+     * Registers an ObjectClass with this registry.
+     *
+     * @param objectClass the objectClass to register
+     * @throws NamingException if the ObjectClass is already registered or the
+     * registration operation is not supported
+     */
+    void register( ObjectClass objectClass ) throws NamingException;
+
+
+    /**
+     * Looks up an objectClass by its unique Object Identifier or by name.
+     *
+     * @param id the object identifier or name
+     * @return the ObjectClass instance for the id
+     * @throws NamingException if the ObjectClass does not exist
+     */
+    ObjectClass lookup( String id ) throws NamingException;
+
+
+    /**
+     * Checks to see if an objectClass exists.
+     *
+     * @param id the object identifier or name
+     * @return true if an objectClass definition exists for the id, false
+     * otherwise
+     */
+    boolean hasObjectClass( String id );
+
+
+    /**
+     * Gets an Iterator over the ObjectClasses within this ObjectClassRegistry.
+     *
+     * @return an iterator over all ObjectClasses in registry
+     */
+    Iterator<ObjectClass> iterator();
+}
diff --git a/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/OidRegistry.java b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/OidRegistry.java
new file mode 100644
index 0000000..3e98a02
--- /dev/null
+++ b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/OidRegistry.java
@@ -0,0 +1,122 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.registries;
+
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.naming.NamingException;
+
+
+/**
+ * Object identifier registry.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public interface OidRegistry
+{
+    /**
+     * Gets the object identifier for a common name or returns the argument
+     * as-is if it is an object identifier.
+     * 
+     * @param name the name to lookup an OID for
+     * @return the OID string associated with a name
+     * @throws NamingException if name does not map to an OID
+     */
+    String getOid( String name ) throws NamingException;
+
+
+    /**
+     * Checks to see if an identifier, oid or name exists within this registry.
+     *
+     * @param id the oid or name to look for
+     * @return true if the id exists false otherwise
+     */
+    boolean hasOid( String id );
+
+
+    /**
+     * Gets the primary name associated with an OID.  The primary name is the
+     * first name specified for the OID.
+     * 
+     * @param oid the object identifier
+     * @return the primary name
+     * @throws NamingException if oid does not exist
+     */
+    String getPrimaryName( String oid ) throws NamingException;
+
+
+    /**
+     * Gets the names associated with an OID.  An OID is unique however it may 
+     * have many names used to refer to it.  A good example is the cn and
+     * commonName attribute names for OID 2.5.4.3.  Within a server one name 
+     * within the set must be chosen as the primary name.  This is used to
+     * name certain things within the server internally.  If there is more than
+     * one name then the first name is taken to be the primary.
+     * 
+     * @param oid the OID for which we return the set of common names
+     * @return a sorted set of names
+     * @throws NamingException if oid does not exist
+     */
+    List getNameSet( String oid ) throws NamingException;
+
+
+    /**
+     * Lists all the OIDs within the registry.  This may be a really big list.
+     * 
+     * @return all the OIDs registered
+     */
+    Iterator list();
+
+
+    /**
+     * Adds an OID name pair to the registry.
+     * 
+     * @param name the name to associate with the OID
+     * @param oid the OID to add or associate a new name with
+     */
+    void register( String name, String oid ) throws NamingException;
+
+
+    /**
+     * Get the map of all the oids by their name
+     * @return The Map that contains all the oids
+     */
+    public Map getOidByName();
+
+
+    /**
+     * Get the map of all the oids by their name
+     * @return The Map that contains all the oids
+     */
+    public Map getNameByOid();
+
+
+    /**
+     * Removes an oid from this registry.
+     *
+     * @param numericOid the numeric identifier for the object
+     * @throws NamingException if the identifier is not numeric
+     */
+    void unregister( String numericOid ) throws NamingException;
+}
diff --git a/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/Registries.java b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/Registries.java
new file mode 100644
index 0000000..82404ce
--- /dev/null
+++ b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/Registries.java
@@ -0,0 +1,98 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.registries;
+
+
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.schema.bootstrap.Schema;
+
+
+/**
+ * Document this class.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public interface Registries
+{
+    String getName();
+    
+    Map<String,Schema> getLoadedSchemas();
+    
+    void load( String schemaName ) throws NamingException;
+    
+    void load( String schemaName, Properties props ) throws NamingException;
+
+    void unload( String schemaName ) throws NamingException;
+    
+    SchemaLoader setSchemaLoader();
+    
+    AttributeTypeRegistry getAttributeTypeRegistry();
+    
+    ComparatorRegistry getComparatorRegistry();
+
+    DITContentRuleRegistry getDitContentRuleRegistry();
+
+    DITStructureRuleRegistry getDitStructureRuleRegistry();
+
+    MatchingRuleRegistry getMatchingRuleRegistry();
+
+    MatchingRuleUseRegistry getMatchingRuleUseRegistry();
+
+    NameFormRegistry getNameFormRegistry();
+
+    NormalizerRegistry getNormalizerRegistry();
+
+    ObjectClassRegistry getObjectClassRegistry();
+
+    OidRegistry getOidRegistry();
+
+    SyntaxCheckerRegistry getSyntaxCheckerRegistry();
+
+    SyntaxRegistry getSyntaxRegistry();
+
+    List<Throwable> checkRefInteg();
+
+    Schema getSchema( String schemaName );
+
+    /**
+     * Removes a schema from the loaded set without unloading the schema.
+     * This should be used ONLY when an enabled schema is deleted.
+     * 
+     * @param schemaName the name of the schema to remove
+     */
+    void removeFromLoadedSet( String schemaName );
+    
+    /**
+     * Adds a schema to the loaded set but does not load the schema in 
+     * question.  This may be a temporary fix for new schemas being added
+     * which are enabled yet do not have any schema entities associated 
+     * with them to load.  In this case all objects added under this 
+     * schema will load when added instead of in bulk.
+     * 
+     * @param schema the schema object to add to the loaded set.
+     */
+    void addToLoadedSet( Schema schema );
+}
diff --git a/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/SchemaLoader.java b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/SchemaLoader.java
new file mode 100644
index 0000000..4f50193
--- /dev/null
+++ b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/SchemaLoader.java
@@ -0,0 +1,94 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.registries;
+
+
+import java.util.Collection;
+import java.util.Properties;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.schema.bootstrap.Schema;
+
+
+/**
+ * Loads schemas into registres.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public interface SchemaLoader
+{
+    /**
+     * Sets listener used to notify of newly loaded schemas.
+     * 
+     * @param listener the listener to notify (only one is enough for us)
+     * @note probably should have used the observer pattern here 
+     */
+    public void setListener( SchemaLoaderListener listener );
+    
+    /**
+     * Gets a schema object based on it's name.
+     * 
+     * @param schemaName the name of the schema to load
+     * @return the Schema object associated with the name
+     * @throws NamingException if any problems while trying to find the associated Schema
+     */
+    Schema getSchema( String schemaName ) throws NamingException;
+    
+    /**
+     * Gets a schema object based on it's name and some properties.
+     * 
+     * @param schemaName the name of the schema to load
+     * @param schemaProperties the properties associated with that schema to facilitate locating/loading it
+     * @return the Schema object associated with the name
+     * @throws NamingException if any problems while trying to find the associated Schema
+     */
+    Schema getSchema( String schemaName, Properties schemaProperties ) throws NamingException;
+    
+    /**
+     * Loads a collection of schemas.  A best effort should be made to load the dependended 
+     * schemas that these schemas may rely on even if they are not included in the collection.
+     * 
+     * @param schemas the collection of schemas to load
+     * @param registries the registries to populate with these schemas
+     * @throws NamingException if any kind of problems are encountered during the load
+     */
+    void loadWithDependencies( Collection<Schema> schemas, Registries registries ) throws NamingException;
+    
+    /**
+     * Loads a single schema at least and possibly it's dependencies.  
+     * 
+     * @param schemas the schema to load
+     * @param registries the registries to populate with these schemas
+     * @throws NamingException if any kind of problems are encountered during the load
+     */
+    void loadWithDependencies( Schema schemas, Registries registries ) throws NamingException;
+    
+    /**
+     * Loads a single schema.  Do not try to resolve dependencies while implementing this method.
+     * 
+     * @param schema the schema to load
+     * @param registries the registries to populate with these schemas
+     * @param isDepLoad tells the loader if this load request is to satisfy a dependency
+     * @throws NamingException if any kind of problems are encountered during the load
+     */
+    void load( Schema schema, Registries registries, boolean isDepLoad ) throws NamingException;
+}
diff --git a/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/SchemaLoaderListener.java b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/SchemaLoaderListener.java
new file mode 100644
index 0000000..73fbd92
--- /dev/null
+++ b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/SchemaLoaderListener.java
@@ -0,0 +1,34 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.registries;
+
+import org.apache.directory.server.schema.bootstrap.Schema;
+
+
+/**
+ * A listener to the schema loader for events like a new schema being loaded.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public interface SchemaLoaderListener
+{
+    void schemaLoaded( Schema schema );
+}
diff --git a/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/SchemaObjectRegistry.java b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/SchemaObjectRegistry.java
new file mode 100644
index 0000000..32e141a
--- /dev/null
+++ b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/SchemaObjectRegistry.java
@@ -0,0 +1,63 @@
+/*
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *   or more contributor license agreements.  See the NOTICE file
+ *   distributed with this work for additional information
+ *   regarding copyright ownership.  The ASF licenses this file
+ *   to you 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.
+ *
+ */
+package org.apache.directory.server.schema.registries;
+
+
+import java.util.Iterator;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.shared.ldap.schema.SchemaObject;
+
+
+/**
+ * Common schema object registry interface.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public interface SchemaObjectRegistry
+{
+    /**
+     * Gets the name of the schema this schema object is associated with.
+     *
+     * @param id the object identifier or the name
+     * @return the schema name
+     * @throws NamingException if the schema object does not exist
+     */
+    String getSchemaName( String id ) throws NamingException;
+
+    
+    /**
+     * Gets an iterator over the registered schema objects in the registry.
+     *
+     * @return an Iterator of homogenious schema objects
+     */
+    Iterator<? extends SchemaObject> iterator();
+
+
+    /**
+     * Removes the SchemaObject registered with this registry.
+     * 
+     * @param numericOid the numeric identifier
+     * @throws NamingException if the numeric identifier is invalid
+     */
+    void unregister( String numericOid ) throws NamingException;
+}
diff --git a/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/SyntaxCheckerRegistry.java b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/SyntaxCheckerRegistry.java
new file mode 100644
index 0000000..817249b
--- /dev/null
+++ b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/SyntaxCheckerRegistry.java
@@ -0,0 +1,124 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.registries;
+
+
+import java.util.Iterator;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.shared.ldap.schema.syntax.SyntaxChecker;
+import org.apache.directory.shared.ldap.schema.syntax.SyntaxCheckerDescription;
+
+
+/**
+ * SyntaxChecker registry component's service interface.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public interface SyntaxCheckerRegistry
+{
+    /**
+     * Registers a SyntaxChecker with this registry.
+     * 
+     * @param description the syntaxCheckerDescription for this syntaxChecker
+     * @param syntaxChecker the SyntaxChecker to register
+     * @throws NamingException if the SyntaxChecker is already registered or the
+     *      registration operation is not supported
+     */
+    void register( SyntaxCheckerDescription description, SyntaxChecker syntaxChecker ) throws NamingException;
+
+
+    /**
+     * Looks up a SyntaxChecker by its unique Object Identifier.
+     * 
+     * @param oid the object identifier
+     * @return the SyntaxChecker for the oid
+     * @throws NamingException if there is a backing store failure or the 
+     *      SyntaxChecker does not exist.
+     */
+    SyntaxChecker lookup( String oid ) throws NamingException;
+
+
+    /**
+     * Gets the name of the schema this schema object is associated with.
+     *
+     * @param oid the object identifier
+     * @return the schema name
+     * @throws NamingException if the schema object does not exist
+     */
+    String getSchemaName( String oid ) throws NamingException;
+
+
+    /**
+     * Checks to see if a SyntaxChecker exists.  Backing store failures simply 
+     * return false.
+     * 
+     * @param oid the object identifier
+     * @return true if a SyntaxChecker definition exists for the oid, false 
+     *      otherwise
+     */
+    boolean hasSyntaxChecker( String oid );
+    
+
+    /**
+     * Get's an iterator over all the syntaxCheckers associated with this registry.
+     * 
+     * @return an Iterator over all the syntaxCheckers
+     */
+    Iterator<SyntaxChecker> iterator();
+
+
+    /**
+     * Get's an iterator over all the syntaxCheckerDescriptions associated with this registry.
+     * 
+     * @return an Iterator over all the syntaxCheckerDescriptions
+     */
+    Iterator<SyntaxCheckerDescription> syntaxCheckerDescriptionIterator();
+
+
+    /**
+     * Unregisters a registered syntaxChecker from this registry.
+     * 
+     * @param numericOid the numeric oid of the syntax this checker is associated with
+     * @throws NamingException if the numericOid is not valid
+     */
+    void unregister( String numericOid ) throws NamingException;
+    
+    
+    /**
+     * Unregisters all syntaxCheckers defined for a specific schema from
+     * this registry.
+     * 
+     * @param schemaName the name of the schema whose syntaxCheckers will be removed
+     */
+    void unregisterSchemaElements( String schemaName );
+
+
+    /**
+     * Renames the schemaName associated with entities within this 
+     * registry to a new schema name.
+     * 
+     * @param originalSchemaName the original schema name
+     * @param newSchemaName the new name to give to the schema
+     */
+    void renameSchema( String originalSchemaName, String newSchemaName );
+}
diff --git a/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/SyntaxRegistry.java b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/SyntaxRegistry.java
new file mode 100644
index 0000000..31dfa05
--- /dev/null
+++ b/old_trunk/schema-registries/src/main/java/org/apache/directory/server/schema/registries/SyntaxRegistry.java
@@ -0,0 +1,75 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.schema.registries;
+
+
+import java.util.Iterator;
+
+import javax.naming.NamingException;
+
+import org.apache.directory.shared.ldap.schema.Syntax;
+
+
+/**
+ * Manages the lookup and registration of Syntaxes within the system by OID.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public interface SyntaxRegistry extends SchemaObjectRegistry
+{
+    /**
+     * Looks up a Syntax by its unique Object Identifier or by name.
+     * 
+     * @param id the object identifier or name
+     * @return the Syntax for the id
+     * @throws NamingException if there is a backing store failure or the Syntax
+     * does not exist.
+     */
+    Syntax lookup( String id ) throws NamingException;
+
+
+    /**
+     * Registers a Syntax with this registry.  
+     * 
+     * @param syntax the Syntax to register
+     * @throws NamingException if the syntax is already registered or the 
+     * registration operation is not supported
+     */
+    void register( Syntax syntax ) throws NamingException;
+
+
+    /**
+     * Checks to see if a Syntax exists.  Backing store failures simply return
+     * false.
+     * 
+     * @param id the object identifier or name
+     * @return true if a Syntax definition exists for the id, false otherwise
+     */
+    boolean hasSyntax( String id );
+
+
+    /**
+     * Lists all the Syntaxes within this registry.
+     *
+     * @return an Iterator over all the Syntaxes within this registry
+     */
+    Iterator<Syntax> iterator();
+}
diff --git a/old_trunk/schema-registries/src/site/site.xml b/old_trunk/schema-registries/src/site/site.xml
new file mode 100644
index 0000000..9ec7c16
--- /dev/null
+++ b/old_trunk/schema-registries/src/site/site.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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.
+-->
+<!--
+  @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+-->
+<project name="${project.name}">
+    <bannerLeft>
+        <name>${project.name}</name>
+    </bannerLeft>
+    <publishDate position="navigation-bottom" format="dd-MM-yyyy HH:mm" />
+    <body>
+        <menu name="Parent">
+            <item name="Apache Directory ApacheDS" href="../index.html" />
+        </menu>
+        <menu ref="reports" />
+    </body>
+</project>
diff --git a/old_trunk/server-jndi/pom.xml b/old_trunk/server-jndi/pom.xml
new file mode 100644
index 0000000..9321193
--- /dev/null
+++ b/old_trunk/server-jndi/pom.xml
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you 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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.directory.server</groupId>
+    <artifactId>apacheds-parent</artifactId>
+    <version>1.5.4-SNAPSHOT</version>
+  </parent>
+  <artifactId>apacheds-server-jndi</artifactId>
+  <name>ApacheDS Server JNDI</name>
+
+  <description>
+    The JNDI provider which launches the core and associated network 
+    services: Changepw, Kerberos, LDAP, and NTP if all are configured.
+    By default only LDAP is configured to startup.
+  </description>
+
+  <packaging>jar</packaging>  
+
+  <dependencies>
+<!--
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-protocol-changepw</artifactId>
+      <version>${pom.version}</version>
+    </dependency>  
+-->
+
+<!--
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-protocol-ntp</artifactId>
+      <version>${pom.version}</version>
+    </dependency>  
+-->
+
+<!--
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-protocol-kerberos</artifactId>
+      <version>${pom.version}</version>
+    </dependency>  
+-->
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-protocol-ldap</artifactId>
+      <version>${pom.version}</version>
+    </dependency>  
+
+<!--
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-protocol-dns</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
+-->
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-core</artifactId>
+      <version>${pom.version}</version>
+    </dependency>  
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-source-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>attach-sources</id>
+            <phase>verify</phase>
+            <goals>
+              <goal>jar</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+
+</project>
+
diff --git a/old_trunk/server-jndi/src/main/java/org/apache/directory/server/configuration/ApacheDS.java b/old_trunk/server-jndi/src/main/java/org/apache/directory/server/configuration/ApacheDS.java
new file mode 100644
index 0000000..170511e
--- /dev/null
+++ b/old_trunk/server-jndi/src/main/java/org/apache/directory/server/configuration/ApacheDS.java
@@ -0,0 +1,475 @@
+/*

+ *  Licensed to the Apache Software Foundation (ASF) under one

+ *  or more contributor license agreements.  See the NOTICE file

+ *  distributed with this work for additional information

+ *  regarding copyright ownership.  The ASF licenses this file

+ *  to you 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.

+ *

+ */

+package org.apache.directory.server.configuration;

+

+

+import org.apache.commons.lang.StringUtils;

+import org.apache.directory.server.constants.ApacheSchemaConstants;

+import org.apache.directory.server.constants.ServerDNConstants;

+import org.apache.directory.server.core.DefaultDirectoryService;

+import org.apache.directory.server.core.DirectoryService;

+import org.apache.directory.server.core.authn.LdapPrincipal;

+import org.apache.directory.server.ldap.LdapServer;

+import org.apache.directory.server.protocol.shared.store.LdifFileLoader;

+import org.apache.directory.server.protocol.shared.store.LdifLoadFilter;

+import org.apache.directory.server.schema.registries.AttributeTypeRegistry;

+import org.apache.directory.shared.ldap.constants.AuthenticationLevel;

+import org.apache.directory.shared.ldap.constants.SchemaConstants;

+import org.apache.directory.shared.ldap.message.AttributesImpl;

+import org.apache.directory.shared.ldap.name.LdapDN;

+import org.apache.directory.shared.ldap.util.StringTools;

+import org.apache.mina.common.ByteBuffer;

+import org.apache.mina.common.SimpleByteBufferAllocator;

+import org.slf4j.Logger;

+import org.slf4j.LoggerFactory;

+

+import javax.naming.NamingException;

+import javax.naming.directory.Attributes;

+import javax.naming.directory.DirContext;

+

+import java.io.File;

+import java.io.FileFilter;

+import java.io.IOException;

+import java.util.ArrayList;

+import java.util.List;

+

+

+/**

+ * Apache Directory Server top level.

+ *

+ * @org.apache.xbean.XBean

+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>

+ * @version $Rev$

+ */

+public class ApacheDS

+{

+    private static final Logger LOG = LoggerFactory.getLogger( ApacheDS.class.getName() );

+    

+    /** Default delay between two flushes to the backend */

+    private static final long DEFAULT_SYNC_PERIOD_MILLIS = 20000;

+

+    /** Wainting period between two flushes to the backend */

+    private long synchPeriodMillis = DEFAULT_SYNC_PERIOD_MILLIS;

+

+    /** Directory where are stored the LDIF files to be loaded at startup */

+    private File ldifDirectory;

+    

+    private final List<LdifLoadFilter> ldifFilters = new ArrayList<LdifLoadFilter>();

+

+    /** The LDAP server protocol handler */

+    private final LdapServer ldapServer;

+    

+    /** The LDAPS server protocol handler */

+    private final LdapServer ldapsServer;

+    

+    /** The directory service */

+    private final DirectoryService directoryService;

+

+

+    /**

+     * Creates a new instance of the ApacheDS server

+     *  

+     * @param directoryService 

+     * @param ldapServer

+     * @param ldapsServer

+     */

+    public ApacheDS( DirectoryService directoryService, LdapServer ldapServer, LdapServer ldapsServer )

+    {

+        LOG.info(  "Starting the Apache Directory Server" );

+

+        if ( directoryService == null )

+        {

+            this.directoryService = new DefaultDirectoryService();

+        }

+        else

+        {        

+            this.directoryService = directoryService;

+        }

+        

+        this.ldapServer = ldapServer;

+        this.ldapsServer = ldapsServer;

+        ByteBuffer.setAllocator( new SimpleByteBufferAllocator() );

+        ByteBuffer.setUseDirectBuffers( false );

+    }

+

+

+    /**

+     * Start the server :

+     *  <li>initialize the DirectoryService</li>

+     *  <li>start the LDAP server</li>

+     *  <li>start the LDAPS server</li>

+     *  

+     * @throws NamingException If the server cannot be started

+     * @throws IOException If an IO error occured while reading some file

+     */

+    public void startup() throws NamingException, IOException

+    {

+        LOG.debug( "Starting the server" );

+        

+        // Start the directory service if not started yet

+        if ( ! directoryService.isStarted() )

+        {

+            LOG.debug( "1. Starting the DirectoryService" );

+            directoryService.startup();

+        }

+

+        // Load the LDIF files - if any - into the server

+        loadLdifs();

+

+        // Start the LDAP server

+        if ( ldapServer != null && ! ldapServer.isStarted() )

+        {

+            LOG.debug( "3. Starting the LDAP server" );

+            ldapServer.start();

+        }

+

+        // Start the LDAPS  server

+        if ( ldapsServer != null && ! ldapsServer.isStarted() )

+        {

+            LOG.debug(  "4. Starting the LDAPS server" );

+            ldapsServer.start();

+        }

+        

+        LOG.debug( "Server successfully started" );

+    }

+

+

+    public boolean isStarted()

+    {

+        if ( ldapServer != null || ldapsServer != null )

+        {

+             return ( ldapServer != null && ldapServer.isStarted() )

+                     || ( ldapsServer != null && ldapsServer.isStarted() );

+        }

+        

+        return directoryService.isStarted();

+    }

+    

+

+    public void shutdown() throws NamingException

+    {

+        if ( ldapServer != null && ldapServer.isStarted() )

+        {

+            ldapServer.stop();

+        }

+

+        if ( ldapsServer != null && ldapsServer.isStarted() )

+        {

+            ldapsServer.stop();

+        }

+

+        directoryService.shutdown();

+    }

+

+

+    public LdapServer getLdapServer()

+    {

+        return ldapServer;

+    }

+

+

+    public LdapServer getLdapsServer()

+    {

+        return ldapsServer;

+    }

+

+

+    public DirectoryService getDirectoryService()

+    {

+        return directoryService;

+    }

+

+

+    public long getSynchPeriodMillis()

+    {

+        return synchPeriodMillis;

+    }

+

+

+    public void setSynchPeriodMillis( long synchPeriodMillis )

+    {

+        LOG.info( "Set the synchPeriodMillis to {}", synchPeriodMillis );

+        this.synchPeriodMillis = synchPeriodMillis;

+    }

+

+    

+    /**

+     * Get the directory where 

+     * @return

+     */

+    public File getLdifDirectory()

+    {

+        return ldifDirectory;

+    }

+

+

+    public void setAllowAnonymousAccess( boolean allowAnonymousAccess )

+    {

+        LOG.info( "Set the allowAnonymousAccess flag to {}", allowAnonymousAccess );

+        

+        directoryService.setAllowAnonymousAccess( allowAnonymousAccess );

+        

+        if ( ldapServer != null )

+        {

+            ldapServer.setAllowAnonymousAccess( allowAnonymousAccess );

+        }

+        

+        if ( ldapsServer != null )

+        {

+            ldapsServer.setAllowAnonymousAccess( allowAnonymousAccess );

+        }

+    }

+

+

+    public void setLdifDirectory( File ldifDirectory )

+    {

+        LOG.info( "The LDIF directory file is {}", ldifDirectory.getAbsolutePath() );

+        this.ldifDirectory = ldifDirectory;

+    }

+    

+    

+    // ----------------------------------------------------------------------

+    // From CoreContextFactory: presently in intermediate step but these

+    // methods will be moved to the appropriate protocol service eventually.

+    // This is here simply to start to remove the JNDI dependency then further

+    // refactoring will be needed to place these where they belong.

+    // ----------------------------------------------------------------------

+

+

+    /**

+     * Check that the entry where are stored the loaded Ldif files is created.

+     * 

+     * If not, create it.

+     * 

+     * The files are stored in ou=loadedLdifFiles,ou=configuration,ou=system

+     */

+    private void ensureLdifFileBase( DirContext root )

+    {

+        Attributes entry = new AttributesImpl( SchemaConstants.OU_AT, "loadedLdifFiles", true );

+        entry.put( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC );

+        entry.get( SchemaConstants.OBJECT_CLASS_AT ).add( SchemaConstants.ORGANIZATIONAL_UNIT_OC );

+

+        try

+        {

+            root.createSubcontext( ServerDNConstants.LDIF_FILES_DN, entry );

+            LOG.info( "Creating " + ServerDNConstants.LDIF_FILES_DN );

+        }

+        catch ( NamingException e )

+        {

+            LOG.info( ServerDNConstants.LDIF_FILES_DN + " exists" );

+        }

+    }

+

+

+    /**

+     * Create a string containing a hex dump of the loaded ldif file name.

+     * 

+     * It is associated with the attributeType wrt to the underlying system.

+     */

+    private String buildProtectedFileEntry( File ldif )

+    {

+        String fileSep = File.separatorChar == '\\' ? 

+                ApacheSchemaConstants.WINDOWS_FILE_AT : 

+                ApacheSchemaConstants.UNIX_FILE_AT;

+

+        return  fileSep + 

+                "=" + 

+                StringTools.dumpHexPairs( StringTools.getBytesUtf8( getCanonical( ldif ) ) ) +

+                "," + 

+                ServerDNConstants.LDIF_FILES_DN; 

+    }

+

+    

+    private void addFileEntry( DirContext root, File ldif ) throws NamingException

+    {

+        String rdnAttr = File.separatorChar == '\\' ? 

+            ApacheSchemaConstants.WINDOWS_FILE_AT : 

+            ApacheSchemaConstants.UNIX_FILE_AT;

+        String oc = File.separatorChar == '\\' ? ApacheSchemaConstants.WINDOWS_FILE_OC : ApacheSchemaConstants.UNIX_FILE_OC;

+

+        Attributes entry = new AttributesImpl( rdnAttr, getCanonical( ldif ), true );

+        entry.put( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC );

+        entry.get( SchemaConstants.OBJECT_CLASS_AT ).add( oc );

+        root.createSubcontext( buildProtectedFileEntry( ldif ), entry );

+    }

+

+

+    /**

+     * 

+     * @param root

+     * @param ldif

+     * @return

+     */

+    private Attributes getLdifFileEntry( DirContext root, File ldif )

+    {

+        try

+        {

+            return root.getAttributes( buildProtectedFileEntry( ldif ), new String[]

+                { SchemaConstants.CREATE_TIMESTAMP_AT } );

+        }

+        catch ( NamingException e )

+        {

+            return null;

+        }

+    }

+

+

+    private String getCanonical( File file )

+    {

+        String canonical;

+

+        try

+        {

+            canonical = file.getCanonicalPath();

+        }

+        catch ( IOException e )

+        {

+            LOG.error( "could not get canonical path", e );

+            return null;

+        }

+

+        return StringUtils.replace( canonical, "\\", "\\\\" );

+    }

+

+

+    /**

+     * Load a ldif into the directory.

+     *  

+     * @param root The context in which we will inject the entries

+     * @param ldifFile The ldif file to read

+     * @throws NamingException If something went wrong while loading the entries

+     */

+    private void loadLdif( DirContext root, File ldifFile ) throws NamingException

+    {

+        Attributes fileEntry = getLdifFileEntry( root, ldifFile );

+

+        if ( fileEntry != null )

+        {

+            String time = ( String ) fileEntry.get( SchemaConstants.CREATE_TIMESTAMP_AT ).get();

+            LOG.info( "Load of LDIF file '" + getCanonical( ldifFile )

+                    + "' skipped.  It has already been loaded on " + time + "." );

+        }

+        else

+        {

+            LdifFileLoader loader = new LdifFileLoader( root, ldifFile, ldifFilters );

+            int count = loader.execute();

+            LOG.info( "Loaded " + count + " entries from LDIF file '" + getCanonical( ldifFile ) + "'" );

+            addFileEntry( root, ldifFile );

+        }

+    }

+    

+    

+    /**

+     * Load the ldif files if there are some

+     */

+    public void loadLdifs() throws NamingException

+    {

+        // LOG and bail if property not set

+        if ( ldifDirectory == null )

+        {

+            LOG.info( "LDIF load directory not specified.  No LDIF files will be loaded." );

+            return;

+        }

+

+        // LOG and bail if LDIF directory does not exists

+        if ( ! ldifDirectory.exists() )

+        {

+            LOG.warn( "LDIF load directory '{}' does not exist.  No LDIF files will be loaded.",

+                getCanonical( ldifDirectory ) );

+            return;

+        }

+

+

+        LdapDN dn = new LdapDN( ServerDNConstants.ADMIN_SYSTEM_DN );

+        

+        // Must normalize the dn or - IllegalStateException!

+        AttributeTypeRegistry reg = directoryService.getRegistries().getAttributeTypeRegistry();

+        dn.normalize( reg.getNormalizerMapping() );

+        

+        LdapPrincipal admin = new LdapPrincipal( dn, AuthenticationLevel.STRONG );

+        

+        

+        DirContext root = directoryService.getJndiContext( admin );

+        ensureLdifFileBase( root );

+

+        // if ldif directory is a file try to load it

+        if ( ldifDirectory.isFile() )

+        {

+            if ( LOG.isInfoEnabled() )

+            {

+                LOG.info( "LDIF load directory '{}' is a file. Will attempt to load as LDIF.",

+                    getCanonical( ldifDirectory ) );

+            }

+

+            try

+            {

+                loadLdif( root, ldifDirectory );

+            }

+            catch ( NamingException ne )

+            {

+                // If the file can't be read, log the error, and stop

+                // loading LDIFs.

+                LOG.error( "Cannot load the ldif file '{}', error : ",

+                    ldifDirectory.getAbsolutePath(), 

+                    ne.getMessage() );

+                throw ne;

+            }

+        }

+        else

+        {

+            // get all the ldif files within the directory (should be sorted alphabetically)

+            File[] ldifFiles = ldifDirectory.listFiles( new FileFilter()

+            {

+                public boolean accept( File pathname )

+                {

+                    boolean isLdif = pathname.getName().toLowerCase().endsWith( ".ldif" );

+                    return pathname.isFile() && pathname.canRead() && isLdif;

+                }

+            } );

+    

+            // LOG and bail if we could not find any LDIF files

+            if ( ( ldifFiles == null ) || ( ldifFiles.length == 0 ) )

+            {

+                LOG.warn( "LDIF load directory '{}' does not contain any LDIF files. No LDIF files will be loaded.", 

+                    getCanonical( ldifDirectory ) );

+                return;

+            }

+    

+            // load all the ldif files and load each one that is loaded

+            for ( File ldifFile : ldifFiles )

+            {

+                try

+                {

+                    LOG.info(  "Loading LDIF file '{}'", ldifFile.getName() );

+                    loadLdif( root, ldifFile );

+                }

+                catch ( NamingException ne )

+                {

+                    // If the file can't be read, log the error, and stop

+                    // loading LDIFs.

+                    LOG.error( "Cannot load the ldif file '{}', error : {}", 

+                        ldifFile.getAbsolutePath(), 

+                        ne.getMessage() );

+                    throw ne;

+                }

+            }

+        }

+    }

+}

diff --git a/old_trunk/server-jndi/src/site/site.xml b/old_trunk/server-jndi/src/site/site.xml
new file mode 100644
index 0000000..9ec7c16
--- /dev/null
+++ b/old_trunk/server-jndi/src/site/site.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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.
+-->
+<!--
+  @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+-->
+<project name="${project.name}">
+    <bannerLeft>
+        <name>${project.name}</name>
+    </bannerLeft>
+    <publishDate position="navigation-bottom" format="dd-MM-yyyy HH:mm" />
+    <body>
+        <menu name="Parent">
+            <item name="Apache Directory ApacheDS" href="../index.html" />
+        </menu>
+        <menu ref="reports" />
+    </body>
+</project>
diff --git a/old_trunk/server-replication/pom.xml b/old_trunk/server-replication/pom.xml
new file mode 100644
index 0000000..41706ff
--- /dev/null
+++ b/old_trunk/server-replication/pom.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you 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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.directory.server</groupId>
+    <artifactId>apacheds-parent</artifactId>
+    <version>1.5.4-SNAPSHOT</version>
+  </parent>
+  <artifactId>apacheds-server-replication</artifactId>
+  <name>ApacheDS Server Replication Service</name>
+  <description>
+    A multi-master replication service for replicating information
+    across ApacheDS instances.  This service is modeled as an interceptor.
+  </description>
+  <packaging>jar</packaging>
+</project>
+
diff --git a/old_trunk/server-replication/src/main/java/org/apache/directory/server/replication/configuration/NotificationScheme.java b/old_trunk/server-replication/src/main/java/org/apache/directory/server/replication/configuration/NotificationScheme.java
new file mode 100644
index 0000000..008728d
--- /dev/null
+++ b/old_trunk/server-replication/src/main/java/org/apache/directory/server/replication/configuration/NotificationScheme.java
@@ -0,0 +1,31 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.replication.configuration;
+
+/**
+ * Document me!
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class NotificationScheme
+{
+
+}
diff --git a/old_trunk/server-replication/src/main/java/org/apache/directory/server/replication/configuration/Replica.java b/old_trunk/server-replication/src/main/java/org/apache/directory/server/replication/configuration/Replica.java
new file mode 100644
index 0000000..78b0313
--- /dev/null
+++ b/old_trunk/server-replication/src/main/java/org/apache/directory/server/replication/configuration/Replica.java
@@ -0,0 +1,60 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.replication.configuration;
+
+
+import java.net.InetSocketAddress;
+
+
+/**
+ * Document me!
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class Replica
+{
+    private ReplicaId id;
+    private InetSocketAddress address;
+    
+    
+    public void setId( ReplicaId id )
+    {
+        this.id = id;
+    }
+    
+    
+    public ReplicaId getId()
+    {
+        return id;
+    }
+
+
+    public void setAddress( InetSocketAddress address )
+    {
+        this.address = address;
+    }
+
+
+    public InetSocketAddress getAddress()
+    {
+        return address;
+    }
+}
diff --git a/old_trunk/server-replication/src/main/java/org/apache/directory/server/replication/configuration/ReplicaId.java b/old_trunk/server-replication/src/main/java/org/apache/directory/server/replication/configuration/ReplicaId.java
new file mode 100644
index 0000000..19b62ed
--- /dev/null
+++ b/old_trunk/server-replication/src/main/java/org/apache/directory/server/replication/configuration/ReplicaId.java
@@ -0,0 +1,152 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.replication.configuration;
+
+
+import java.io.Serializable;
+import java.util.regex.Pattern;
+
+import org.apache.directory.shared.ldap.util.StringTools;
+
+
+/**
+ * Store a replica ID after having normalized it.
+ * 
+ * The normalization proces checks that the submitted id is valid, ie
+ * contains only this char set : { '-', '_', 'a..z', 'A..Z', '0..9' }
+ * and its length is between 1 and 16. 
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class ReplicaId implements Comparable, Serializable
+{
+    /**
+     * Declares the Serial Version Uid.
+     */
+    private static final long serialVersionUID = 1L;
+
+    /** The replica pattern. */
+    private static final Pattern REPLICA_ID_PATTERN = Pattern.compile( "[-_A-Z0-9]{1,16}" );
+
+    /** The formated replicaId */
+    private String id;
+
+
+    /**
+     * Creates a new instance of ReplicaId. The id must be a String 
+     * which respect the pattern :
+     * 
+     * [-_a-zA-Z0-9]*
+     * 
+     * and must be between 1 and 16 chars length
+     *
+     * @param id The replica pattern
+     */
+    public ReplicaId( String id )
+    {
+        if ( StringTools.isEmpty( id ) )
+        {
+            throw new IllegalArgumentException( "Empty ID: " + id );
+        }
+
+        String tmpId = id.trim().toUpperCase();
+
+        if ( !REPLICA_ID_PATTERN.matcher( tmpId ).matches() )
+        {
+            throw new IllegalArgumentException( "Invalid replica ID: " + id );
+        }
+
+        this.id = id;
+    }
+
+
+    /**
+     * @return The replicaId
+     */
+    public String getId()
+    {
+        return id;
+    }
+
+
+    /**
+     * Returns a hash code value for the object.
+     * 
+     * @return a hash code value for this object.
+     */
+    public int hashCode()
+    {
+        return id.hashCode();
+    }
+
+
+    /**
+     * Indicates whether some other object is "equal to" this one
+     * 
+     * @param o the reference object with which to compare.
+     * @return <code>true</code> if this object is the same as the obj argument; 
+     * <code>false</code> otherwise.
+     */
+    public boolean equals( Object o )
+    {
+        if ( o == null )
+        {
+            return false;
+        }
+
+        if ( o == this )
+        {
+            return true;
+        }
+
+        if ( o instanceof ReplicaId )
+        {
+            return this.id.equals( ( ( ReplicaId ) o ).id );
+        }
+        else
+        {
+            return false;
+        }
+    }
+
+
+    /**
+     * Compares this object with the specified object for order.  Returns a
+     * negative integer, zero, or a positive integer as this object is less
+     * than, equal to, or greater than the specified object.<p>
+     * 
+     * @param   o the Object to be compared.
+     * @return  a negative integer, zero, or a positive integer as this object
+     *      is less than, equal to, or greater than the specified object.
+     */
+    public int compareTo( Object o )
+    {
+        return this.id.compareTo( ( ( ReplicaId ) o ).id );
+    }
+
+
+    /**
+     * @return the Replica Id
+     */
+    public String toString()
+    {
+        return id;
+    }
+}
diff --git a/old_trunk/server-replication/src/main/java/org/apache/directory/server/replication/configuration/ReplicationAgreement.java b/old_trunk/server-replication/src/main/java/org/apache/directory/server/replication/configuration/ReplicationAgreement.java
new file mode 100644
index 0000000..aecba6e
--- /dev/null
+++ b/old_trunk/server-replication/src/main/java/org/apache/directory/server/replication/configuration/ReplicationAgreement.java
@@ -0,0 +1,103 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.replication.configuration;
+
+
+import java.util.List;
+import java.util.Set;
+
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.subtree.SubtreeSpecification;
+
+
+/**
+ * Document me!
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class ReplicationAgreement
+{
+    /**
+     * Each replication agreement has a unique identifier.
+     */
+    String id;
+    
+    /** 
+     * The set of critical attributes: those that trigger the immediate 
+     * initiation of a replication cycle.  
+     * 
+     * These are numeric attribute OIDs so the structure can be used for 
+     * fast lookups.
+     */
+    Set<String> criticalAttributes;
+    
+    /** 
+     * The set of attributes that are not replicated: note that attributes
+     * of type dSAOperation and collective attributes are not replicated.
+     * 
+     * These are numeric attribute OIDs so the structure can be used for 
+     * fast lookups.
+     */
+    Set<String> exclusions;
+    
+    /** 
+     * The set of attributes that are replicated.  Some attributes may be
+     * dSAOperation attributes and may still need to be replicated.
+     * 
+     * These are numeric attribute OIDs so the structure can be used for 
+     * fast lookups.
+     */
+    Set<String> inclusions;
+    
+    /**
+     * The replication group (list of replicas) composing this agreement. 
+     */
+    List<Replica> replicationGroup;
+    
+    /**
+     * The replicatin area defined as a subtreeSpecification.
+     */
+    SubtreeSpecification area;
+    
+    /**
+     * The administrative point for the replication specific autonomous area.
+     */
+    LdapDN replicationBase;
+    
+    /**
+     * The schedule to use for initiating replication cycles.  You can have more than
+     * one trigger assigned to initiate replication.  This may be:
+     * <ul>
+     *   <li>periodic</li>
+     *   <li>manual</li>
+     *   <li>singular</li>
+     *   <li>on change with optional delay</li>
+     *   <li>after meeting a threshold of changes</li>
+     *   <li>minimum time after the last replication cycle</li>
+     * </ul> 
+     */
+    Set<ReplicationTrigger> replicationSchedule;
+    
+    /**
+     * Who to notify, why and how.
+     */
+    NotificationScheme notificationScheme;
+}
diff --git a/old_trunk/server-replication/src/main/java/org/apache/directory/server/replication/configuration/ReplicationTrigger.java b/old_trunk/server-replication/src/main/java/org/apache/directory/server/replication/configuration/ReplicationTrigger.java
new file mode 100644
index 0000000..3c49598
--- /dev/null
+++ b/old_trunk/server-replication/src/main/java/org/apache/directory/server/replication/configuration/ReplicationTrigger.java
@@ -0,0 +1,31 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.replication.configuration;
+
+
+/**
+ * Document me!
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public interface ReplicationTrigger
+{
+}
diff --git a/old_trunk/server-replication/src/site/site.xml b/old_trunk/server-replication/src/site/site.xml
new file mode 100644
index 0000000..9ec7c16
--- /dev/null
+++ b/old_trunk/server-replication/src/site/site.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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.
+-->
+<!--
+  @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+-->
+<project name="${project.name}">
+    <bannerLeft>
+        <name>${project.name}</name>
+    </bannerLeft>
+    <publishDate position="navigation-bottom" format="dd-MM-yyyy HH:mm" />
+    <body>
+        <menu name="Parent">
+            <item name="Apache Directory ApacheDS" href="../index.html" />
+        </menu>
+        <menu ref="reports" />
+    </body>
+</project>
diff --git a/old_trunk/server-sar/pom.xml b/old_trunk/server-sar/pom.xml
new file mode 100644
index 0000000..42cfcbe
--- /dev/null
+++ b/old_trunk/server-sar/pom.xml
@@ -0,0 +1,158 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you 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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.directory.server</groupId>
+    <artifactId>apacheds-parent</artifactId>
+    <version>1.5.3-SNAPSHOT</version>
+  </parent>
+  <artifactId>apacheds-server-sar</artifactId>
+  <name>ApacheDS Server Sar (JBoss 3.x)</name>
+
+  <description>
+    A server archive file for ApacheDS to run within a JBoss 3.x instance
+  </description>
+
+  <packaging>sar</packaging>  
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-server-ssl</artifactId>
+      <version>${pom.version}</version>
+    </dependency>  
+
+    <dependency>
+      <groupId>org.apache.directory.daemon</groupId>
+      <artifactId>daemon-bootstrappers</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>jboss</groupId>
+      <artifactId>jboss-system</artifactId>
+      <version>3.2.3</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>nlog4j</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-core</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-beans</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-context</artifactId>
+    </dependency>
+  </dependencies>
+
+  <repositories>
+    <repository>
+      <id>mojo.snapshots</id>
+      <name>Codehaus Snapshot Repository</name>
+      <url>http://snapshots.maven.codehaus.org/maven2</url>
+    </repository>
+  </repositories>
+
+  <build>
+    <plugins>
+      <plugin>
+        <extensions>true</extensions>
+        <groupId>org.apache.directory.server</groupId>
+        <artifactId>apacheds-sar-plugin</artifactId>
+        <configuration>
+          <excludes>
+            <exclude>
+aopalliance:aopalliance
+            </exclude>
+            <exclude>
+jasperreports:jasperreports
+            </exclude>
+            <exclude>
+asm:asm-util
+            </exclude>
+            <exclude>
+freemarker:freemarker
+            </exclude>
+            <exclude>
+asm:asm
+            </exclude>
+            <exclude>
+com.jamonapi:jamon
+            </exclude>
+            <exclude>
+velocity:velocity-dep
+            </exclude>
+            <exclude>
+commons-attributes:commons-attributes-compiler
+            </exclude>
+            <exclude>
+commons-attributes:commons-attributes-api
+            </exclude>
+            <exclude>
+cglib:cglib
+            </exclude>
+            <exclude>
+qdox:qdox
+            </exclude>
+            <exclude>
+oro:oro
+            </exclude>
+            <exclude>
+org.springframework:spring-aop
+            </exclude>
+            <exclude>
+velocity:velocity
+            </exclude>
+            <exclude>
+aspectwerkz:aspectwerkz-core
+            </exclude>
+            <exclude>
+xml-apis:xml-apis
+            </exclude>
+            <exclude>
+commons-pool:commons-pool
+            </exclude>
+            <exclude>
+xerces:xerces
+            </exclude>
+            <exclude>
+ant:ant
+            </exclude>
+            <exclude>
+jboss:jboss-system
+            </exclude>
+          </excludes>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/old_trunk/server-sar/src/main/java/org/apache/directory/server/sar/DirectoryService.java b/old_trunk/server-sar/src/main/java/org/apache/directory/server/sar/DirectoryService.java
new file mode 100644
index 0000000..9daa1e9
--- /dev/null
+++ b/old_trunk/server-sar/src/main/java/org/apache/directory/server/sar/DirectoryService.java
@@ -0,0 +1,996 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.sar;
+
+
+import org.apache.directory.server.configuration.MutableServerStartupConfiguration;
+import org.apache.directory.server.core.configuration.Configuration;
+import org.apache.directory.server.core.configuration.MutablePartitionConfiguration;
+import org.apache.directory.server.core.configuration.ShutdownConfiguration;
+import org.apache.directory.server.core.configuration.SyncConfiguration;
+import org.apache.directory.server.jndi.ServerContextFactory;
+
+import org.jboss.system.ServiceMBeanSupport;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import java.io.File;
+
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Properties;
+import java.util.Set;
+
+import javax.management.MBeanRegistration;
+
+import javax.naming.Context;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.BasicAttribute;
+import javax.naming.directory.BasicAttributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.InitialDirContext;
+import javax.naming.directory.ModificationItem;
+
+
+/**
+ * JBoss 3.x Mbean for embedded and remote directory server support
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class DirectoryService extends ServiceMBeanSupport implements DirectoryServiceMBean, MBeanRegistration
+{
+    // ~ Static fields/initializers
+    // ---------------------------------------------
+
+    private static final Logger LOG = LoggerFactory.getLogger( DirectoryService.class );
+
+    /** Default LDAP Listen Port */
+    public static final int DEFAULT_LDAP_PORT = 389;
+
+    /** Default LDAPS (SSL) Port */
+    public static final int DEFAULT_LDAPS_PORT = 636;
+
+    // ~ Instance fields
+    // --------------------------------------------------------
+
+    private boolean embeddedServerEnabled = true;
+
+    private String wkDir = ".";
+
+    private String ldifDir = "./ldif";
+
+    private int ldapPort = DEFAULT_LDAP_PORT;
+
+    private int ldapsPort = DEFAULT_LDAPS_PORT;
+
+    private String customRootPartitionName = "com";
+
+    private String contextProviderURL = "uid=admin,ou=system";
+
+    private String securityAuthentication = "simple";
+
+    private String securityCredentials = "secret";
+
+    private String securityPrincipal = "uid=admin,ou=system";
+
+    private boolean anonymousAccess = false;
+
+    private boolean ldapNetworkingSupport = false;
+
+    private String contextFactory = ServerContextFactory.class.getName();
+
+    private Element additionalEnv = null;
+
+    private Element customSchema = null;
+
+    private Element ldifFilters = null;
+
+    private boolean accessControlEnabled = false;
+
+    private boolean enableNtp = false;
+
+    private boolean enableKerberos = false;
+
+    private boolean enableChangePassword = false;
+
+
+    // ~ Methods
+    // ----------------------------------------------------------------
+
+    protected void startService() throws Exception
+    {
+        // Build the properties from bean attributes
+        Hashtable env = createContextEnv();
+
+        if ( embeddedServerEnabled )
+        {
+            if ( LOG.isInfoEnabled() )
+            {
+                LOG.info( "Starting Embedded Directory Server..." );
+            }
+
+            // Create the baseline configuration
+            MutableServerStartupConfiguration cfg = new MutableServerStartupConfiguration();
+
+            /*
+             * *************** Update the baseline configuration
+             * *****************
+             */
+            // Access Control
+            cfg.setAccessControlEnabled( this.accessControlEnabled );
+            cfg.setAllowAnonymousAccess( this.anonymousAccess );
+
+            // Wire protocols
+            cfg.setEnableNetworking( this.ldapNetworkingSupport );
+            cfg.setLdapPort( this.ldapPort );
+            cfg.setLdapsPort( this.ldapsPort );
+
+            cfg.setEnableNtp( enableNtp );
+            cfg.setEnableKerberos( enableKerberos );
+            cfg.setEnableChangePassword( enableChangePassword );
+
+            // Work folder
+            cfg.setWorkingDirectory( new File( this.wkDir ) );
+
+            // LDIF import
+            cfg.setLdifDirectory( new File( this.ldifDir ) );
+            cfg.setLdifFilters( addCustomLdifFilters() );
+
+            // Addditional bootstrap schema
+            cfg.setBootstrapSchemas( addCustomBootstrapSchema( cfg.getBootstrapSchemas() ) );
+
+            // Single custom partition
+            if ( ( null != this.customRootPartitionName ) && ( this.customRootPartitionName.length() > 0 ) )
+            {
+                if ( LOG.isDebugEnabled() )
+                {
+                    LOG.debug( "Adding custom root partition name: " + this.customRootPartitionName );
+                }
+
+                Set pcfgs = addCustomPartition();
+                cfg.setContextPartitionConfigurations( pcfgs );
+            }
+
+            // Put the configuration instruction to the environment variable.
+            env.putAll( cfg.toJndiEnvironment() );
+
+            new InitialDirContext( env );
+        }
+        else
+        {
+            if ( LOG.isWarnEnabled() )
+            {
+                LOG
+                    .warn( "No Embedded directory server requested.  All directory access will be via remote LDAP interface." );
+            }
+        }
+
+        if ( LOG.isDebugEnabled() )
+        {
+            LOG.debug( "Directory Environment:" );
+
+            Enumeration en = env.keys();
+
+            while ( en.hasMoreElements() )
+            {
+                Object key = en.nextElement();
+                LOG.debug( "    " + key + ":" + env.get( key ) );
+            }
+        }
+    }
+
+
+    private List addCustomLdifFilters()
+    {
+        List filters = new ArrayList();
+
+        Hashtable ht = getPropertiesFromElement( ldifFilters );
+        Enumeration en = ht.elements();
+        Class clazz = null;
+
+        while ( en.hasMoreElements() )
+        {
+            try
+            {
+                clazz = Class.forName( ( String ) en.nextElement() );
+                filters.add( clazz.newInstance() );
+            }
+            catch ( Exception e )
+            {
+                if ( LOG.isErrorEnabled() )
+                {
+                    LOG.error( e.toString() );
+                }
+            }
+        }
+
+        return filters;
+    }
+
+
+    private Set addCustomBootstrapSchema( Set schema )
+    {
+        Hashtable ht = getPropertiesFromElement( customSchema );
+        Enumeration en = ht.elements();
+        Class clazz = null;
+
+        while ( en.hasMoreElements() )
+        {
+            try
+            {
+                clazz = Class.forName( ( String ) en.nextElement() );
+                schema.add( clazz.newInstance() );
+            }
+            catch ( Exception e )
+            {
+                if ( LOG.isErrorEnabled() )
+                {
+                    LOG.error( e.toString() );
+                }
+            }
+        }
+
+        return schema;
+    }
+
+
+    private void addAdditionalEnv( Hashtable env )
+    {
+        Hashtable ht = getPropertiesFromElement( additionalEnv );
+        Enumeration en = ht.keys();
+        String key = null;
+
+        while ( en.hasMoreElements() )
+        {
+            key = ( String ) en.nextElement();
+            env.put( key, ht.get( key ) );
+        }
+    }
+
+
+    private Hashtable createContextEnv()
+    {
+        Hashtable env = new Properties();
+
+        addAdditionalEnv( env );
+
+        env.put( Context.PROVIDER_URL, this.contextProviderURL );
+        env.put( Context.INITIAL_CONTEXT_FACTORY, this.contextFactory );
+
+        env.put( Context.SECURITY_AUTHENTICATION, this.securityAuthentication );
+        env.put( Context.SECURITY_PRINCIPAL, this.securityPrincipal );
+        env.put( Context.SECURITY_CREDENTIALS, this.securityCredentials );
+
+        if ( this.isEmbeddedServerEnabled() )
+        {
+            // This is bug-or-wierdness workaround for in-VM access to the
+            // DirContext of ApacheDS
+            env.put( Configuration.JNDI_KEY, new SyncConfiguration() );
+        }
+
+        return env;
+    }
+
+
+    private Set addCustomPartition() throws NamingException
+    {
+        BasicAttributes attrs;
+        Set indexedAttrs;
+        BasicAttribute attr;
+        Set pcfgs = new HashSet();
+        MutablePartitionConfiguration pcfg;
+        pcfg = new MutablePartitionConfiguration();
+
+        pcfg.setName( this.customRootPartitionName );
+        pcfg.setSuffix( "dc=" + this.customRootPartitionName );
+
+        indexedAttrs = new HashSet();
+        indexedAttrs.add( "ou" );
+        indexedAttrs.add( "dc" );
+        indexedAttrs.add( "objectClass" );
+        pcfg.setIndexedAttributes( indexedAttrs );
+
+        attrs = new BasicAttributes( true );
+
+        attr = new BasicAttribute( "objectClass" );
+        attr.add( "top" );
+        attr.add( "domain" );
+        attr.add( "extensibleObject" );
+        attrs.put( attr );
+
+        attr = new BasicAttribute( "dc" );
+        attr.add( this.customRootPartitionName );
+        attrs.put( attr );
+
+        pcfg.setContextEntry( attrs );
+
+        pcfgs.add( pcfg );
+
+        return pcfgs;
+    }
+
+
+    protected void stopService() throws Exception
+    {
+        if ( embeddedServerEnabled )
+        {
+            if ( LOG.isInfoEnabled() )
+            {
+                LOG.info( "Stopping Embedded Directory Server..." );
+            }
+
+            // Create a configuration instruction.
+            ShutdownConfiguration cfg = new ShutdownConfiguration();
+
+            // Build the properties from bean attributes
+            Hashtable env = createContextEnv();
+
+            // Put the configuration instruction to the environment variable.
+            env.putAll( cfg.toJndiEnvironment() );
+
+            new InitialDirContext( env );
+        }
+    }
+
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.apache.directory.server.jmx.DirectoryServiceMBean#getContextProviderURL()
+     */
+    public String getContextProviderURL()
+    {
+        return this.contextProviderURL;
+    }
+
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.apache.directory.server.jmx.DirectoryServiceMBean#getContextSecurityAuthentication()
+     */
+    public String getContextSecurityAuthentication()
+    {
+        return this.securityAuthentication;
+    }
+
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.apache.directory.server.jmx.DirectoryServiceMBean#getContextSecurityCredentials()
+     */
+    public String getContextSecurityCredentials()
+    {
+        return this.securityCredentials;
+    }
+
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.apache.directory.server.jmx.DirectoryServiceMBean#getContextSecurityPrincipal()
+     */
+    public String getContextSecurityPrincipal()
+    {
+        return this.securityPrincipal;
+    }
+
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.apache.directory.server.jmx.DirectoryServiceMBean#getEmbeddedCustomRootPartitionName()
+     */
+    public String getEmbeddedCustomRootPartitionName()
+    {
+        return this.customRootPartitionName;
+    }
+
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.apache.directory.server.jmx.DirectoryServiceMBean#getEmbeddedLDAPPort()
+     */
+    public int getEmbeddedLDAPPort()
+    {
+        return this.ldapPort;
+    }
+
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.apache.directory.server.jmx.DirectoryServiceMBean#getEmbeddedLDAPSPort()
+     */
+    public int getEmbeddedLDAPSPort()
+    {
+        return this.ldapsPort;
+    }
+
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.apache.directory.server.jmx.DirectoryServiceMBean#getEmbeddedLDIFdir()
+     */
+    public String getEmbeddedLDIFdir()
+    {
+        return this.ldifDir;
+    }
+
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.apache.directory.server.jmx.DirectoryServiceMBean#getEmbeddedWkdir()
+     */
+    public String getEmbeddedWkdir()
+    {
+        return this.wkDir;
+    }
+
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.apache.directory.server.jmx.DirectoryServiceMBean#isEmbeddedAnonymousAccess()
+     */
+    public boolean isEmbeddedAnonymousAccess()
+    {
+        return this.anonymousAccess;
+    }
+
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.apache.directory.server.jmx.DirectoryServiceMBean#isEmbeddedLDAPNetworkingSupport()
+     */
+    public boolean isEmbeddedLDAPNetworkingSupport()
+    {
+        return this.ldapNetworkingSupport;
+    }
+
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.apache.directory.server.jmx.DirectoryServiceMBean#isEmbeddedServerEnabled()
+     */
+    public boolean isEmbeddedServerEnabled()
+    {
+        return this.embeddedServerEnabled;
+    }
+
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.apache.directory.server.jmx.DirectoryServiceMBean#openDirContext()
+     */
+    public DirContext openDirContext() throws NamingException
+    {
+        Hashtable env = createContextEnv();
+
+        return new InitialDirContext( env );
+    }
+
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.apache.directory.server.jmx.DirectoryServiceMBean#setContextProviderURL(java.lang.String)
+     */
+    public void setContextProviderURL( String providerURL )
+    {
+        this.contextProviderURL = providerURL;
+    }
+
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.apache.directory.server.jmx.DirectoryServiceMBean#setContextSecurityAuthentication(java.lang.String)
+     */
+    public void setContextSecurityAuthentication( String securityAuthentication )
+    {
+        this.securityAuthentication = securityAuthentication;
+    }
+
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.apache.directory.server.jmx.DirectoryServiceMBean#setContextSecurityCredentials(java.lang.String)
+     */
+    public void setContextSecurityCredentials( String securityCredentials )
+    {
+        this.securityCredentials = securityCredentials;
+    }
+
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.apache.directory.server.jmx.DirectoryServiceMBean#setContextSecurityprincipal(java.lang.String)
+     */
+    public void setContextSecurityPrincipal( String securityPrincipal )
+    {
+        this.securityPrincipal = securityPrincipal;
+    }
+
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.apache.directory.server.jmx.DirectoryServiceMBean#setEmbeddedAnonymousAccess(boolean)
+     */
+    public void setEmbeddedAnonymousAccess( boolean anonymousAccess )
+    {
+        this.anonymousAccess = anonymousAccess;
+    }
+
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.apache.directory.server.jmx.DirectoryServiceMBean#setEmbeddedCustomRootPartitionName(java.lang.String)
+     */
+    public void setEmbeddedCustomRootPartitionName( String rootPartitianName )
+    {
+        this.customRootPartitionName = rootPartitianName;
+    }
+
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.apache.directory.server.jmx.DirectoryServiceMBean#setEmbeddedLDAPNetworkingSupport(boolean)
+     */
+    public void setEmbeddedLDAPNetworkingSupport( boolean ldapNetworkingSupport )
+    {
+        this.ldapNetworkingSupport = ldapNetworkingSupport;
+    }
+
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.apache.directory.server.jmx.DirectoryServiceMBean#setEmbeddedLDAPPort(int)
+     */
+    public void setEmbeddedLDAPPort( int ldapPort )
+    {
+        this.ldapPort = ldapPort;
+    }
+
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.apache.directory.server.jmx.DirectoryServiceMBean#setEmbeddedLDAPSPort(int)
+     */
+    public void setEmbeddedLDAPSPort( int ldapsPort )
+    {
+        this.ldapsPort = ldapsPort;
+    }
+
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.apache.directory.server.jmx.DirectoryServiceMBean#setEmbeddedLDIFdir(java.lang.String)
+     */
+    public void setEmbeddedLDIFdir( String LDIFdir )
+    {
+        this.ldifDir = LDIFdir;
+    }
+
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.apache.directory.server.jmx.DirectoryServiceMBean#setEmbeddedServerEnabled(boolean)
+     */
+    public void setEmbeddedServerEnabled( boolean enabled )
+    {
+        this.embeddedServerEnabled = enabled;
+    }
+
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.apache.directory.server.jmx.DirectoryServiceMBean#setEmbeddedWkdir(java.lang.String)
+     */
+    public void setEmbeddedWkdir( String wkdir )
+    {
+        this.wkDir = wkdir;
+    }
+
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.apache.directory.server.jmx.DirectoryServiceMBean#getContextFactory()
+     */
+    public String getContextFactory()
+    {
+        return this.contextFactory;
+    }
+
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.apache.directory.server.jmx.DirectoryServiceMBean#setContextFactory(java.lang.String)
+     */
+    public void setContextFactory( String factoryClass )
+    {
+        this.contextFactory = factoryClass;
+    }
+
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.apache.directory.server.jmx.DirectoryServiceMBean#changedEmbeddedAdminPassword(java.lang.String,
+     *      java.lang.String)
+     */
+    public String changedEmbeddedAdminPassword( String oldPassword, String newPassword )
+    {
+        if ( embeddedServerEnabled )
+        {
+            if ( this.securityCredentials.equals( oldPassword ) )
+            {
+                ModificationItem[] mods = new ModificationItem[1];
+                Attribute password = new BasicAttribute( "userpassword", newPassword );
+                mods[0] = new ModificationItem( DirContext.REPLACE_ATTRIBUTE, password );
+
+                try
+                {
+                    DirContext dc = openDirContext();
+
+                    dc.modifyAttributes( "", mods );
+                    dc.close();
+                }
+                catch ( NamingException e )
+                {
+                    String msg = "Failed modifying directory password attribute: " + e;
+
+                    if ( LOG.isErrorEnabled() )
+                    {
+                        LOG.error( msg );
+                    }
+
+                    return msg;
+                }
+
+                this.securityCredentials = newPassword;
+
+                return "Password change successful.";
+            }
+            else
+            {
+                return "Invalid oldPassword given.";
+            }
+        }
+        else
+        {
+            String msg = "Unable to change password as embedded server is not enabled.";
+
+            if ( LOG.isWarnEnabled() )
+            {
+                LOG.warn( msg );
+            }
+
+            return msg;
+        }
+    }
+
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.apache.directory.server.jmx.DirectoryServiceMBean#flushEmbeddedServerData()
+     */
+    public boolean flushEmbeddedServerData()
+    {
+        if ( embeddedServerEnabled )
+        {
+            try
+            {
+                if ( LOG.isInfoEnabled() )
+                {
+                    LOG.info( "Syncing Embedded Directory Server..." );
+                }
+
+                // Create a configuration instruction.
+                SyncConfiguration cfg = new SyncConfiguration();
+
+                // Build the properties from bean attributes
+                Hashtable env = createContextEnv();
+
+                // Put the configuration instruction to the environment
+                // variable.
+                env.putAll( cfg.toJndiEnvironment() );
+
+                if ( LOG.isDebugEnabled() )
+                {
+                    LOG.info( "Directory Properties:" );
+
+                    Enumeration en = env.keys();
+
+                    while ( en.hasMoreElements() )
+                    {
+                        Object key = en.nextElement();
+                        LOG.debug( "    " + key + ":" + env.get( key ) );
+                    }
+                }
+
+                new InitialDirContext( env );
+
+                return true;
+            }
+            catch ( NamingException e )
+            {
+                if ( LOG.isErrorEnabled() )
+                {
+                    LOG.error( e.toString() );
+                }
+            }
+        }
+        else
+        {
+            if ( LOG.isWarnEnabled() )
+            {
+                LOG.warn( "Unable to flush as embedded server is not enabled." );
+            }
+        }
+
+        return false;
+    }
+
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.apache.directory.server.jmx.DirectoryServiceMBean#getEmbeddedAdditionalEnvProperties()
+     */
+    public Element getEmbeddedAdditionalEnvProperties()
+    {
+        return this.additionalEnv;
+    }
+
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.apache.directory.server.jmx.DirectoryServiceMBean#getEmbeddedCustomBootstrapSchemas()
+     */
+    public Element getEmbeddedCustomBootstrapSchema()
+    {
+        return this.customSchema;
+    }
+
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.apache.directory.server.jmx.DirectoryServiceMBean#setEmbeddedAdditionalEnvProperties(java.util.Properties)
+     */
+    public void setEmbeddedAdditionalEnvProperties( Element env )
+    {
+        this.additionalEnv = env;
+    }
+
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.apache.directory.server.jmx.DirectoryServiceMBean#setEmbeddedCustomBootstrapSchemas(java.util.Properties)
+     */
+    public void setEmbeddedCustomBootstrapSchema( Element cfg )
+    {
+        this.customSchema = cfg;
+    }
+
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.apache.directory.server.jmx.DirectoryServiceMBean#isEmbeddedAccessControlEnabled()
+     */
+    public boolean isEmbeddedAccessControlEnabled()
+    {
+        return this.accessControlEnabled;
+    }
+
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.apache.directory.server.jmx.DirectoryServiceMBean#isEmbeddedEnableChangePassword()
+     */
+    public boolean isEmbeddedEnableChangePassword()
+    {
+        return this.enableChangePassword;
+    }
+
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.apache.directory.server.jmx.DirectoryServiceMBean#isEmbeddedEnableKerberos()
+     */
+    public boolean isEmbeddedEnableKerberos()
+    {
+        return this.enableKerberos;
+    }
+
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.apache.directory.server.jmx.DirectoryServiceMBean#isEmbeddedEnableNtp()
+     */
+    public boolean isEmbeddedEnableNtp()
+    {
+        return this.enableNtp;
+    }
+
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.apache.directory.server.jmx.DirectoryServiceMBean#setEmbeddedAccessControlEnabled(boolean)
+     */
+    public void setEmbeddedAccessControlEnabled( boolean enabled )
+    {
+        this.accessControlEnabled = enabled;
+    }
+
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.apache.directory.server.jmx.DirectoryServiceMBean#setEmbeddedEnableChangePassword(boolean)
+     */
+    public void setEmbeddedEnableChangePassword( boolean enabled )
+    {
+        this.enableChangePassword = enabled;
+    }
+
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.apache.directory.server.jmx.DirectoryServiceMBean#setEmbeddedEnableKerberos(boolean)
+     */
+    public void setEmbeddedEnableKerberos( boolean enabled )
+    {
+        this.enableKerberos = enabled;
+    }
+
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.apache.directory.server.jmx.DirectoryServiceMBean#setEmbeddedEnableNtp(boolean)
+     */
+    public void setEmbeddedEnableNtp( boolean enabled )
+    {
+        this.enableNtp = enabled;
+    }
+
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.apache.directory.server.jmx.DirectoryServiceMBean#getEmbeddedLDIFFilters()
+     */
+    public Element getEmbeddedLDIFFilters()
+    {
+        return this.ldifFilters;
+    }
+
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.apache.directory.server.jmx.DirectoryServiceMBean#setEmbeddedLDIFFilters(org.w3c.dom.Element)
+     */
+    public void setEmbeddedLDIFFilters( Element fil )
+    {
+        this.ldifFilters = fil;
+    }
+
+
+    // Embedded lists inside the Mbean service definition are made available as
+    // DOM elements
+    // and are parsed into a java collection before use
+    private Hashtable getPropertiesFromElement( Element element )
+    {
+        Hashtable ht = new Hashtable();
+
+        if ( null != element )
+        {
+            if ( LOG.isInfoEnabled() )
+            {
+                LOG.info( "Adding custom configuration elements:" );
+            }
+
+            NodeList nl = element.getChildNodes();
+            Node el = null;
+
+            for ( int ii = 0; ii < nl.getLength(); ii++ )
+            {
+                el = nl.item( ii );
+
+                String val = null;
+                String name = null;
+
+                if ( el.getNodeType() == Node.ELEMENT_NODE )
+                {
+                    name = el.getAttributes().getNamedItem( "name" ).getNodeValue();
+
+                    NodeList vnl = el.getChildNodes();
+
+                    for ( int jj = 0; jj < vnl.getLength(); jj++ )
+                    {
+                        el = vnl.item( jj );
+
+                        if ( el.getNodeType() == Node.TEXT_NODE )
+                        {
+                            val = el.getNodeValue();
+
+                            break;
+                        }
+                    }
+
+                    if ( ( null != name ) && ( null != val ) )
+                    {
+                        if ( LOG.isInfoEnabled() )
+                        {
+                            LOG.info( "    " + name + ": " + val );
+                        }
+
+                        ht.put( name, val );
+
+                        break;
+                    }
+                }
+            }
+        }
+
+        return ht;
+    }
+}
diff --git a/old_trunk/server-sar/src/main/java/org/apache/directory/server/sar/DirectoryServiceMBean.java b/old_trunk/server-sar/src/main/java/org/apache/directory/server/sar/DirectoryServiceMBean.java
new file mode 100644
index 0000000..f424858
--- /dev/null
+++ b/old_trunk/server-sar/src/main/java/org/apache/directory/server/sar/DirectoryServiceMBean.java
@@ -0,0 +1,403 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.sar;
+
+
+import org.w3c.dom.Element;
+
+import javax.naming.NamingException;
+import javax.naming.directory.DirContext;
+
+
+/**
+ * JBoss 3.x Mbean interface for embedded and remote directory server support
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory
+ *         Project</a>
+ * @version $Rev$, $Date$
+ */
+public interface DirectoryServiceMBean extends org.jboss.system.ServiceMBean
+{
+    //~ Methods ----------------------------------------------------------------
+
+    /**
+     * Gets the root element of the XML properties list of defined LDIF filters
+     *
+     * @return The root DOM element
+     */
+    public Element getEmbeddedLDIFFilters();
+
+
+    /**
+     * Sets the root element of the XML properties list of defined LDIF filters
+     *
+     * @param fil The root DOM element
+     */
+    public void setEmbeddedLDIFFilters( Element fil );
+
+
+    /**
+     * Gets the root element of the XML properties list of additional
+     * environment properties
+     *
+     * @return The root DOM element
+     */
+    public Element getEmbeddedAdditionalEnvProperties();
+
+
+    /**
+     * Sets the root element of the XML properties list of additional
+     * environment properties
+     *
+     * @param env The root DOM element
+     */
+    public void setEmbeddedAdditionalEnvProperties( Element env );
+
+
+    /**
+     * Gets the root element of the XML properties list of custom bootstrap
+     * schema properties
+     *
+     * @return The root DOM element
+     */
+    public Element getEmbeddedCustomBootstrapSchema();
+
+
+    /**
+     * Sets the root element of the XML properties list of custom bootstrap
+     * schema properties
+     *
+     * @param cfg The root DOM element
+     */
+    public void setEmbeddedCustomBootstrapSchema( Element cfg );
+
+
+    /**
+     * Test to see if the directory service to use is embedded in this VM
+     *
+     * @return True if embedded else false
+     */
+    public boolean isEmbeddedServerEnabled();
+
+
+    /**
+     * Set if the directory service to use is embedded in this VM
+     *
+     * @param enabled True if embedded else false
+     */
+    public void setEmbeddedServerEnabled( boolean enabled );
+
+
+    /**
+     * Gets the name-to-object binding for Context INITIAL_CONTEXT_FACTORY
+     *
+     * @return Context.INITIAL_CONTEXT_FACTORY
+     */
+    public String getContextFactory();
+
+
+    /**
+     * Sets the name-to-object binding for Context INITIAL_CONTEXT_FACTORY
+     *
+     * @param factoryClass Context.INITIAL_CONTEXT_FACTORY value
+     */
+    public void setContextFactory( String factoryClass );
+
+
+    /**
+     * Gets the name-to-object binding for Context PROVIDER_URL
+     *
+     * @return Context.PROVIDER_URL
+     */
+    public String getContextProviderURL();
+
+
+    /**
+     * Sets the name-to-object binding for Context PROVIDER_URL
+     *
+     * @param providerURL Context.PROVIDER_URL value
+     */
+    public void setContextProviderURL( String providerURL );
+
+
+    /**
+     * Gets the name-to-object binding for Context SECURITY_AUTHENTICATION
+     *
+     * @return Context.SECURITY_AUTHENTICATION
+     */
+    public String getContextSecurityAuthentication();
+
+
+    /**
+     * Sets the name-to-object binding for Context SECURITY_AUTHENTICATION
+     *
+     * @param securityAuthentication Context.SECURITY_AUTHENTICATION value
+     */
+    public void setContextSecurityAuthentication( String securityAuthentication );
+
+
+    /**
+     * Gets the name-to-object binding for Context SECURITY_PRINCIPAL
+     *
+     * @return Context.SECURITY_PRINCIPAL
+     */
+    public String getContextSecurityPrincipal();
+
+
+    /**
+     * Sets the name-to-object binding for Context SECURITY_PRINCIPAL
+     *
+     * @param securityPrincipal Context.SECURITY_PRINCIPAL value
+     */
+    public void setContextSecurityPrincipal( String securityPrincipal );
+
+
+    /**
+     * Gets the name-to-object binding for Context SECURITY_CREDENTIALS
+     *
+     * @return Context.SECURITY_CREDENTIALS
+     */
+    public String getContextSecurityCredentials();
+
+
+    /**
+     * Sets the name-to-object binding for Context SECURITY_CREDENTIALS
+     *
+     * @param securityCredentials Context.SECURITY_CREDENTIALS value
+     */
+    public void setContextSecurityCredentials( String securityCredentials );
+
+
+    /**
+     * Opens a directory context based on the currently assigned name-to-object
+     * bindings
+     *
+     * @return A valid directory context or null on error
+     */
+    public DirContext openDirContext() throws NamingException;
+
+
+    /**
+     * Embedded server only - Flushes out any I/O buffer or write cache
+     *
+     * @return True if flush succeeded else false
+     */
+    public boolean flushEmbeddedServerData();
+
+
+    /**
+     * Embedded server only - Changes the current password of the uid=admin
+     * user
+     *
+     * @param oldPassword Old password for verification
+     * @param newPassword New password to use
+     *
+     * @return Confirmation message for UI display
+     */
+    public String changedEmbeddedAdminPassword( String oldPassword, String newPassword );
+
+
+    /**
+     * Embedded server only - tests if anonymous access is permitted
+     *
+     * @return True if permitted else false
+     */
+    public boolean isEmbeddedAnonymousAccess();
+
+
+    /**
+     * Embedded server only - sests if anonymous access is permitted
+     *
+     * @param anonymousAccess True to allow else false
+     */
+    public void setEmbeddedAnonymousAccess( boolean anonymousAccess );
+
+
+    /**
+     * Embedded server only - tests if LDAP wire protocol handler is to be
+     * started
+     *
+     * @return True if LDAP wire protocol in use else false
+     */
+    public boolean isEmbeddedLDAPNetworkingSupport();
+
+
+    /**
+     * Embedded server only - sests if LDAP wire protocol handler is to be
+     * started
+     *
+     * @param ldapNetworkingSupport True to install LDAP support else false
+     */
+    public void setEmbeddedLDAPNetworkingSupport( boolean ldapNetworkingSupport );
+
+
+    /**
+     * Embedded server only - gets the LDAP listen port
+     *
+     * @return LDAP listen port
+     */
+    public int getEmbeddedLDAPPort();
+
+
+    /**
+     * Embedded server only - sets the LDAP listen port
+     *
+     * @param ldapPort The LDAP port listened on
+     */
+    public void setEmbeddedLDAPPort( int ldapPort );
+
+
+    /**
+     * Embedded server only - gets the LDAPSSL) listen port (!NOT YET
+     * SUPPORTED!)
+     *
+     * @return LDAPS listen port
+     */
+    public int getEmbeddedLDAPSPort();
+
+
+    /**
+     * Embedded server only - sets the LDAPS (SSL) listen port (!NOT YET
+     * SUPPORTED!)
+     *
+     * @param ldapsPort The LDAPS port listened on
+     */
+    public void setEmbeddedLDAPSPort( int ldapsPort );
+
+
+    /**
+     * Embedded server only - Gets the name of the root partion which was
+     * automatically created on server startup
+     *
+     * @return The name of the custom root partition (null for no custom
+     *         partition)
+     */
+    public String getEmbeddedCustomRootPartitionName();
+
+
+    /**
+     * Embedded server only - Sets the name of the root partion which is
+     * automatically created on server startup
+     *
+     * @param rootPartitianName The name of the custom root partition (null for
+     *        no partition)
+     */
+    public void setEmbeddedCustomRootPartitionName( String rootPartitianName );
+
+
+    /**
+     * Embedded server only - Gets the name of the workfile folder used by the
+     * server
+     *
+     * @return Folder name
+     */
+    public String getEmbeddedWkdir();
+
+
+    /**
+     * Embedded server only - Sets the name of the workfile folder used by the
+     * server
+     *
+     * @param wkdir Folder name
+     */
+    public void setEmbeddedWkdir( String wkdir );
+
+
+    /**
+     * Embedded server only - Gets the name of the LDIF import folder used by
+     * the server
+     *
+     * @return LDIF import folder
+     */
+    public String getEmbeddedLDIFdir();
+
+
+    /**
+     * Embedded server only - Sets the name of the LDIF import folder used by
+     * the server
+     *
+     * @param LDIFdir LDIF import folder
+     */
+    public void setEmbeddedLDIFdir( String LDIFdir );
+
+
+    /**
+     * Embedded server only - test if access control is enabled
+     *
+     * @return True is enabled else false
+     */
+    public boolean isEmbeddedAccessControlEnabled();
+
+
+    /**
+     * Embedded server only - Set if access control is enabled
+     *
+     * @param enabled True to enable else false
+     */
+    public void setEmbeddedAccessControlEnabled( boolean enabled );
+
+
+    /**
+     * Embedded server only - test if NTP wire protocol is enabled
+     *
+     * @return True is enabled else false
+     */
+    public boolean isEmbeddedEnableNtp();
+
+
+    /**
+     * Embedded server only - set if NTP wire protocol is enabled
+     *
+     * @param enabled True to enable else false
+     */
+    public void setEmbeddedEnableNtp( boolean enabled );
+
+
+    /**
+     * Embedded server only - test if Kerberos wire protocol is enabled
+     *
+     * @return True is enabled else false
+     */
+    public boolean isEmbeddedEnableKerberos();
+
+
+    /**
+     * Embedded server only - set if Kerberos wire protocol is enabled
+     *
+     * @param enabled True to enable else false
+     */
+    public void setEmbeddedEnableKerberos( boolean enabled );
+
+
+    /**
+     * Embedded server only - test if Change Password wire protocol is enabled
+     *
+     * @return True is enabled else false
+     */
+    public boolean isEmbeddedEnableChangePassword();
+
+
+    /**
+     * Embedded server only - set if Change Password wire protocol is enabled
+     *
+     * @param enabled True to enable else false
+     */
+    public void setEmbeddedEnableChangePassword( boolean enabled );
+}
diff --git a/old_trunk/server-sar/src/main/resources/META-INF/jboss-service.xml b/old_trunk/server-sar/src/main/resources/META-INF/jboss-service.xml
new file mode 100644
index 0000000..1354389
--- /dev/null
+++ b/old_trunk/server-sar/src/main/resources/META-INF/jboss-service.xml
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you 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.
+-->
+
+<!DOCTYPE server>
+
+<server>
+	<mbean code="org.apache.directory.server.sar.DirectoryService"
+		name="apacheds.system:service=Directory">
+
+		<attribute name="EmbeddedServerEnabled">true</attribute>
+		<attribute name="ContextProviderURL">
+			uid=admin,ou=system
+		</attribute>
+		<attribute name="ContextSecurityAuthentication">
+			simple
+		</attribute>
+		<attribute name="ContextSecurityCredentials">secret</attribute>
+		<attribute name="ContextSecurityPrincipal">
+			uid=admin,ou=system
+		</attribute>
+
+		<!-- ###################################################################
+			Embedded Apache Directory
+			################################################################### -->
+
+		<attribute name="EmbeddedWkdir">
+			${reims.jboss.dir}/conf/apacheds-store
+		</attribute>
+
+		<attribute name="EmbeddedLDIFdir">
+			${reims.jboss.dir}/conf/apacheds-ldif
+		</attribute>
+
+		<attribute name="EmbeddedLDIFFilters">
+			<!--
+				Entries will optionally be filtered using LdifLoadFilters in the
+				order specified.  The example included Krb5KdcEntryFilter will filter
+				kerberos principals creating keys for them using their
+				userPassword attribute if present.
+			-->
+			<xml-properties>
+				<config-property name="Krb5KdcEntryFilter">
+					org.apache.directory.server.protocol.common.store.Krb5KdcEntryFilter
+				</config-property>
+			</xml-properties>
+		</attribute>
+
+		<!-- Access control                    -->
+
+		<attribute name="EmbeddedAccessControlEnabled">false</attribute>
+		<attribute name="EmbeddedAnonymousAccess">false</attribute>
+
+		<!-- Wire protocols                    -->
+		<attribute name="EmbeddedEnableNtp">false</attribute>
+		<attribute name="EmbeddedEnableKerberos">false</attribute>
+		<attribute name="EmbeddedEnableChangePassword">false</attribute>
+		<attribute name="EmbeddedLDAPNetworkingSupport">true</attribute>
+		<attribute name="EmbeddedLDAPPort">389</attribute>
+		<attribute name="EmbeddedLDAPSPort">636</attribute>
+
+	</mbean>
+</server>
+
diff --git a/old_trunk/server-sar/src/site/site.xml b/old_trunk/server-sar/src/site/site.xml
new file mode 100644
index 0000000..9ec7c16
--- /dev/null
+++ b/old_trunk/server-sar/src/site/site.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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.
+-->
+<!--
+  @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+-->
+<project name="${project.name}">
+    <bannerLeft>
+        <name>${project.name}</name>
+    </bannerLeft>
+    <publishDate position="navigation-bottom" format="dd-MM-yyyy HH:mm" />
+    <body>
+        <menu name="Parent">
+            <item name="Apache Directory ApacheDS" href="../index.html" />
+        </menu>
+        <menu ref="reports" />
+    </body>
+</project>
diff --git a/old_trunk/server-tools/pom.xml b/old_trunk/server-tools/pom.xml
new file mode 100644
index 0000000..9a0cb2b
--- /dev/null
+++ b/old_trunk/server-tools/pom.xml
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you 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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.directory.server</groupId>
+    <artifactId>apacheds-parent</artifactId>
+    <version>1.5.4-SNAPSHOT</version>
+  </parent>
+  <artifactId>apacheds-server-tools</artifactId>
+  <name>ApacheDS Server Tools</name>
+
+  <description>
+    Contained within this executable jar are various commandline utilities
+    for apacheds.
+  </description>
+
+  <packaging>jar</packaging>  
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-server-xml</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-bootstrap-partition</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-server-jndi</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.xbean</groupId>
+      <artifactId>xbean-spring</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-core</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-beans</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-context</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.directory.daemon</groupId>
+      <artifactId>daemon-bootstrappers</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>commons-cli</groupId>
+      <artifactId>commons-cli</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>commons-lang</groupId>
+      <artifactId>commons-lang</artifactId>
+    </dependency>
+
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+        <configuration>
+          <archive>
+            <manifestFile>src/main/manifest/MANIFEST.MF</manifestFile>
+            <manifest>
+              <mainClass>org.apache.directory.server.tools.ApachedsTools</mainClass>
+            </manifest>
+          </archive>
+        </configuration>
+      </plugin>
+    </plugins>
+    <resources>
+      <resource>
+        <directory>src/main/resources</directory>
+        <filtering>true</filtering>
+      </resource>
+    </resources>
+  </build>
+</project>
+
diff --git a/old_trunk/server-tools/src/main/java/org/apache/directory/server/tools/ApachedsTools.java b/old_trunk/server-tools/src/main/java/org/apache/directory/server/tools/ApachedsTools.java
new file mode 100644
index 0000000..855469f
--- /dev/null
+++ b/old_trunk/server-tools/src/main/java/org/apache/directory/server/tools/ApachedsTools.java
@@ -0,0 +1,148 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.tools;
+
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Properties;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.directory.server.configuration.ApacheDS;
+import org.springframework.context.ApplicationContext;
+import org.apache.xbean.spring.context.FileSystemXmlApplicationContext;
+
+
+/**
+ * The main() application which executes command targets.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class ApachedsTools
+{
+    public static void main( String[] args ) throws Exception
+    {
+        BaseCommand tools = getInstance();
+
+        if ( !BaseCommand.hasBannerOption( args ) )
+        {
+            tools.printBanner();
+        }
+
+        if ( args.length == 0 )
+        {
+            System.err.println( "Type " + tools.getProductCommand() + " help for usage." );
+            System.exit( 1 );
+        }
+
+        // help is a special command 
+        String command = args[0].toLowerCase();
+        if ( "help".equals( command ) )
+        {
+            CommandLine cmdline = tools.getCommandLine( command, args );
+            if ( cmdline.getArgs().length > 1 )
+            {
+                tools.helpOnCommand( cmdline.getArgs()[1] );
+                System.exit( 0 );
+            }
+            else
+            {
+                tools.printUsage();
+                System.exit( 0 );
+            }
+        }
+        else if ( command.equals( "-version" ) )
+        {
+            System.out.println( tools.getProductCommand() + " version " + tools.getProductVersion() );
+            System.exit( 0 );
+        }
+
+        ToolCommand cmd = ( ToolCommand ) tools.getCommands().get( command );
+        if ( cmd == null )
+        {
+            System.err.println( "Unknown command: " + args[0] );
+            System.err.println( "Type " + tools.getProductCommand() + " help for usage." );
+            System.exit( 1 );
+        }
+
+        CommandLine cmdline = tools.getCommandLine( command, args );
+        if ( cmdline.hasOption( 'd' ) )
+        {
+            cmd.setDebugEnabled( true );
+            BaseCommand.dumpArgs( "raw command line arguments: ", args );
+            BaseCommand.dumpArgs( "parsed arguments: ", cmdline.getArgs() );
+        }
+
+        cmd.setQuietEnabled( cmdline.hasOption( 'q' ) );
+        cmd.setDebugEnabled( cmdline.hasOption( 'd' ) );
+        cmd.setVerboseEnabled( cmdline.hasOption( 'v' ) );
+        cmd.setVersion( tools.getProductVersion() );
+        if ( cmdline.getOptionValue( 'i' ) != null )
+        {
+            cmd.setLayout( cmdline.getOptionValue( 'i' ) );
+            if ( !cmd.isQuietEnabled() )
+            {
+                System.out.println( "loading settings from: " + cmd.getLayout().getConfigurationFile() );
+            }
+            ApplicationContext factory = null;
+            URL configUrl = cmd.getLayout().getConfigurationFile().toURL();
+            factory = new FileSystemXmlApplicationContext( configUrl.toString() );
+            cmd.setApacheDS( ( ApacheDS ) factory.getBean( "apacheDS" ) );
+        }
+        else if ( cmdline.hasOption( 'c' ) )
+        {
+            System.err.println( "forced configuration load (-c) requires the -i option" );
+            System.exit( 1 );
+        }
+
+        cmd.execute( cmdline );
+    }
+
+
+    public static BaseCommand getInstance() throws InstantiationException, IllegalAccessException,
+        ClassNotFoundException
+    {
+        Properties props = new Properties();
+        try
+        {
+            props.load( BaseCommand.class.getResourceAsStream( "product.properties" ) );
+        }
+        catch ( IOException e )
+        {
+            e.printStackTrace();
+        }
+
+        String productVersion = props.getProperty( "product.version", "UNKNOWN" );
+        String productUrl = props.getProperty( "product.url", "http://directory.apache.org" );
+        String productDisplayName = props.getProperty( "product.display.name", "Apache Directory Server" );
+        String productCommand = props.getProperty( "product.command", "apacheds-tools" );
+        String productBanner = props.getProperty( "product.banner", BaseCommand.BANNER );
+        String productClass = props.getProperty( "product.class", "org.apache.directory.server.tools.BaseCommand" );
+
+        BaseCommand baseCommand = ( BaseCommand ) Class.forName( productClass ).newInstance();
+        baseCommand.setProductBanner( productBanner );
+        baseCommand.setProductDisplayName( productDisplayName );
+        baseCommand.setProductUrl( productUrl );
+        baseCommand.setProductVersion( productVersion );
+        baseCommand.setProductCommand( productCommand );
+        return baseCommand;
+    }
+}
diff --git a/old_trunk/server-tools/src/main/java/org/apache/directory/server/tools/BaseCommand.java b/old_trunk/server-tools/src/main/java/org/apache/directory/server/tools/BaseCommand.java
new file mode 100644
index 0000000..a913257
--- /dev/null
+++ b/old_trunk/server-tools/src/main/java/org/apache/directory/server/tools/BaseCommand.java
@@ -0,0 +1,358 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.tools;
+
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.cli.AlreadySelectedException;
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.MissingArgumentException;
+import org.apache.commons.cli.MissingOptionException;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+import org.apache.commons.cli.PosixParser;
+import org.apache.commons.cli.UnrecognizedOptionException;
+
+
+/**
+ * The primary command base class.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class BaseCommand
+{
+    private Map commands = new HashMap();
+
+    private List commandsOrdered = new ArrayList();
+
+    private Options global = new Options();
+
+    private String productCommand;
+
+    private String productVersion;
+
+    private String productDisplayName;
+
+    private String productUrl;
+
+    private String productBanner;
+
+
+    public BaseCommand()
+    {
+        init();
+    }
+
+
+    protected void init()
+    {
+        ToolCommand command;
+
+        command = new DiagnosticCommand();
+        commands.put( command.getName(), command );
+        commandsOrdered.add( command.getName() );
+
+        command = new DisconnectNotificationCommand();
+        commands.put( command.getName(), command );
+        commandsOrdered.add( command.getName() );
+
+        command = new DumpCommand();
+        commands.put( command.getName(), command );
+        commandsOrdered.add( command.getName() );
+
+        command = new CapacityTestCommand();
+        commands.put( command.getName(), command );
+        commandsOrdered.add( command.getName() );
+
+        command = new GracefulShutdownCommand();
+        commands.put( command.getName(), command );
+        commandsOrdered.add( command.getName() );
+
+        command = new ImportCommand();
+        commands.put( command.getName(), command );
+        commandsOrdered.add( command.getName() );
+
+        command = new IndexCommand();
+        commands.put( command.getName(), command );
+        commandsOrdered.add( command.getName() );
+
+        Option op = new Option( "i", "install-path", true, "path to installation directory" );
+        getGlobal().addOption( op );
+        op = new Option( "b", "banner", false, "suppress banner print outs" );
+        getGlobal().addOption( op );
+        op = new Option( "d", "debug", false, "toggle debug mode" );
+        getGlobal().addOption( op );
+        op = new Option( "v", "verbose", false, "toggle verbose debugging" );
+        getGlobal().addOption( op );
+        op = new Option( "q", "quiet", false, "keep the noise down to a minimum" );
+        getGlobal().addOption( op );
+        op = new Option( "c", "configuration", false, "force loading the server.xml (requires -i)" );
+        getGlobal().addOption( op );
+        op = new Option( "version", false, "print the version information and exit" );
+        getGlobal().addOption( op );
+    }
+
+
+    public static boolean hasBannerOption( String[] args )
+    {
+        for ( int ii = 0; ii < args.length; ii++ )
+        {
+            if ( args[ii].equals( "-b" ) || args[ii].equals( "-banner" ) )
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+
+    public CommandLine getCommandLine( String command, String[] args )
+    {
+        Options all = allOptions( command );
+        CommandLineParser parser = new PosixParser();
+        CommandLine cmdline = null;
+        try
+        {
+            cmdline = parser.parse( all, args );
+        }
+        catch ( AlreadySelectedException ase )
+        {
+            System.err.println( "Command line parsing failed for " + command + ".  Reason: already selected "
+                + ase.getMessage() );
+            System.exit( 1 );
+        }
+        catch ( MissingArgumentException mae )
+        {
+            System.err.println( "Command line parsing failed for " + command + ".  Reason: missing argument "
+                + mae.getMessage() );
+            System.exit( 1 );
+        }
+        catch ( MissingOptionException moe )
+        {
+            System.err.println( "Command line parsing failed for " + command + ".  Reason: missing option "
+                + moe.getMessage() );
+            System.exit( 1 );
+        }
+        catch ( UnrecognizedOptionException uoe )
+        {
+            System.err.println( "Command line parsing failed for " + command + ".  Reason: unrecognized option"
+                + uoe.getMessage() );
+            System.exit( 1 );
+        }
+        catch ( ParseException pe )
+        {
+            System.err.println( "Command line parsing failed for " + command + ".  Reason: " + pe.getClass() );
+            System.exit( 1 );
+        }
+
+        return cmdline;
+    }
+
+
+    public Options allOptions( String command )
+    {
+        if ( command.equals( "help" ) )
+        {
+            return getGlobal();
+        }
+
+        Options all = new Options();
+        ToolCommand cmd = ( ToolCommand ) getCommands().get( command );
+
+        for ( Iterator ii = getGlobal().getOptions().iterator(); ii.hasNext(); )
+        {
+            all.addOption( ( Option ) ii.next() );
+        }
+
+        for ( Iterator ii = cmd.getOptions().getOptions().iterator(); ii.hasNext(); )
+        {
+            all.addOption( ( Option ) ii.next() );
+        }
+        return all;
+    }
+
+
+    public static void dumpArgs( String msg, String[] args )
+    {
+        if ( args.length == 0 )
+        {
+            System.out.println( msg );
+            System.out.println( "\t NONE" );
+            return;
+        }
+
+        StringBuffer buf = new StringBuffer();
+        buf.append( msg ).append( "\n" );
+
+        for ( int ii = 0; ii < args.length; ii++ )
+        {
+            buf.append( "\targs[" + ii + "] = " ).append( args[ii] ).append( "\n" );
+        }
+        System.out.println( buf );
+    }
+
+
+    public void helpOnCommand( String command )
+    {
+        if ( command.equals( "help" ) )
+        {
+            printUsage();
+            System.exit( 0 );
+        }
+
+        if ( getCommands().containsKey( command ) )
+        {
+            ToolCommand cmd = ( ToolCommand ) getCommands().get( command );
+            HelpFormatter formatter = new HelpFormatter();
+            formatter.printHelp( getProductCommand() + " " + cmd + " [options]", cmd.getOptions() );
+        }
+        else
+        {
+            System.err.println( command + ": unknown command" );
+            System.exit( 1 );
+        }
+    }
+
+
+    public void printUsage()
+    {
+        HelpFormatter formatter = new HelpFormatter();
+        formatter.printHelp( getProductCommand() + " <command> [options]", "\nGlobal options:", getGlobal(),
+            "\nType \"" + getProductCommand() + " help <command>\" for help on a command." );
+        System.out.println( "\nAvailable commands:" );
+
+        Iterator it = commandsOrdered.iterator();
+        System.out.println( "\thelp" );
+
+        while ( it.hasNext() )
+        {
+            System.out.println( "\t" + it.next() );
+        }
+
+        System.out.println( "\nThese tools are used to manage " + getProductDisplayName() + "." );
+        System.out.println( "For additional information, see " + getProductUrl() );
+    }
+
+    static final String BANNER = "       _                     _          ____  ____    _____           _      \n"
+        + "      / \\   _ __   __ _  ___| |__   ___|  _ \\/ ___|  |_   _|__   ___ | |___  \n"
+        + "     / _ \\ | '_ \\ / _` |/ __| '_ \\ / _ \\ | | \\___ \\    | |/ _ \\ / _ \\| / __| \n"
+        + "    / ___ \\| |_) | (_| | (__| | | |  __/ |_| |___) |   | | (_) | (_) | \\__ \\ \n"
+        + "   /_/   \\_\\ .__/ \\__,_|\\___|_| |_|\\___|____/|____/    |_|\\___/ \\___/|_|___/ \n"
+        + "           |_|                                                               \n";
+
+
+    public void printBanner()
+    {
+        System.out.println( getProductBanner() );
+    }
+
+
+    public void setProductCommand( String productCommand )
+    {
+        this.productCommand = productCommand;
+    }
+
+
+    public String getProductCommand()
+    {
+        return productCommand;
+    }
+
+
+    public void setProductVersion( String productVersion )
+    {
+        this.productVersion = productVersion;
+    }
+
+
+    public String getProductVersion()
+    {
+        return productVersion;
+    }
+
+
+    public void setProductDisplayName( String productDisplayName )
+    {
+        this.productDisplayName = productDisplayName;
+    }
+
+
+    public String getProductDisplayName()
+    {
+        return productDisplayName;
+    }
+
+
+    public void setProductUrl( String productUrl )
+    {
+        this.productUrl = productUrl;
+    }
+
+
+    public String getProductUrl()
+    {
+        return productUrl;
+    }
+
+
+    public void setProductBanner( String productBanner )
+    {
+        this.productBanner = productBanner;
+    }
+
+
+    public String getProductBanner()
+    {
+        return productBanner;
+    }
+
+
+    public void setCommands( Map commands )
+    {
+        this.commands = commands;
+    }
+
+
+    public Map getCommands()
+    {
+        return commands;
+    }
+
+
+    public void setGlobal( Options global )
+    {
+        this.global = global;
+    }
+
+
+    public Options getGlobal()
+    {
+        return global;
+    }
+}
diff --git a/old_trunk/server-tools/src/main/java/org/apache/directory/server/tools/CapacityTestCommand.java b/old_trunk/server-tools/src/main/java/org/apache/directory/server/tools/CapacityTestCommand.java
new file mode 100644
index 0000000..e7930f2
--- /dev/null
+++ b/old_trunk/server-tools/src/main/java/org/apache/directory/server/tools/CapacityTestCommand.java
@@ -0,0 +1,304 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.tools;
+
+
+import java.io.FileWriter;
+import java.io.PrintWriter;
+import java.util.Hashtable;
+
+import javax.naming.NameAlreadyBoundException;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.ldap.InitialLdapContext;
+import javax.naming.ldap.LdapContext;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.Options;
+import org.apache.commons.lang.RandomStringUtils;
+import org.apache.directory.daemon.AvailablePortFinder;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+
+
+/**
+ * A capacity testing tool.  This command will generate bogus user
+ * entries and add them under a base DN.  It will output a table 
+ * of values mapping the capacity of the partition to the time it
+ * took to add an entry to it.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class CapacityTestCommand extends ToolCommand
+{
+    public static final String PORT_RANGE = "(" + AvailablePortFinder.MIN_PORT_NUMBER + ", "
+        + AvailablePortFinder.MAX_PORT_NUMBER + ")";
+
+    private int port = 10389;
+    private String host = "localhost";
+    private String password = "secret";
+    private String baseDn = "ou=users,dc=example,dc=com";
+
+
+    public CapacityTestCommand()
+    {
+        super( "capacity" );
+    }
+
+
+    public void execute( CommandLine cmdline ) throws Exception
+    {
+        processOptions( cmdline );
+        getLayout().verifyInstallation();
+        String outputFile = cmdline.getOptionValue( 'f' );
+        PrintWriter out = null;
+
+        if ( outputFile == null )
+        {
+            out = new PrintWriter( System.out );
+        }
+        else
+        {
+            out = new PrintWriter( new FileWriter( outputFile ) );
+        }
+
+        if ( isDebugEnabled() )
+        {
+            out.println( "Parameters for capacity extended request:" );
+            out.println( "port = " + port );
+            out.println( "host = " + host );
+            out.println( "password = " + password );
+        }
+
+        Hashtable env = new Hashtable();
+        env.put( "java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory" );
+        env.put( "java.naming.provider.url", "ldap://" + host + ":" + port );
+        env.put( "java.naming.security.principal", "uid=admin,ou=system" );
+        env.put( "java.naming.security.credentials", password );
+        env.put( "java.naming.security.authentication", "simple" );
+
+        LdapContext ctx = new InitialLdapContext( env, null );
+
+        // create the base dn if it does not exist
+        createBase( ctx );
+
+        StringBuffer dnBuf = new StringBuffer();
+        StringBuffer outBuf = new StringBuffer();
+        int counter = 0;
+        if ( cmdline.hasOption( "s" ) )
+        {
+            counter = Integer.parseInt( cmdline.getOptionValue( 's' ) ) - 1;
+        }
+        int end = Integer.MAX_VALUE;
+        if ( cmdline.hasOption( "e" ) )
+        {
+            end = Integer.parseInt( cmdline.getOptionValue( 'e' ) );
+        }
+
+        while ( counter < end )
+        {
+            counter++;
+            Attributes attrs = generateLdif( counter );
+            dnBuf.setLength( 0 );
+            dnBuf.append( "uid=user." ).append( counter ).append( "," ).append( baseDn );
+
+            long startTime = System.currentTimeMillis();
+            ctx.createSubcontext( dnBuf.toString(), attrs );
+
+            outBuf.setLength( 0 );
+            outBuf.append( counter ).append( " " ).append( System.currentTimeMillis() - startTime );
+            out.println( outBuf.toString() );
+            out.flush();
+        }
+    }
+
+
+    private boolean createBase( LdapContext ctx ) throws NamingException
+    {
+        Attributes attrs = new AttributesImpl( "objectClass", "organizationalUnit", true );
+        attrs.put( "ou", "users" );
+
+        try
+        {
+            ctx.createSubcontext( "ou=users,dc=example,dc=com", attrs );
+            return true;
+        }
+        catch ( NameAlreadyBoundException e )
+        {
+            return false;
+        }
+    }
+
+
+    private Attributes generateLdif( int counter )
+    {
+        Attributes attrs = new AttributesImpl( "objectClass", "top", true );
+        Attribute oc = attrs.get( "objectClass" );
+        oc.add( "person" );
+        oc.add( "organizationalPerson" );
+        oc.add( "inetOrgPerson" );
+
+        attrs.put( "givenName", RandomStringUtils.randomAlphabetic( 6 ) );
+        attrs.put( "sn", RandomStringUtils.randomAlphabetic( 9 ) );
+        attrs.put( "cn", RandomStringUtils.randomAlphabetic( 15 ) );
+        attrs.put( "initials", RandomStringUtils.randomAlphabetic( 2 ) );
+        attrs.put( "mail", RandomStringUtils.randomAlphabetic( 15 ) );
+        attrs.put( "userPassword", "password" );
+        attrs.put( "telephoneNumber", RandomStringUtils.randomNumeric( 10 ) );
+        attrs.put( "homePhone", RandomStringUtils.randomNumeric( 10 ) );
+        attrs.put( "pager", RandomStringUtils.randomNumeric( 10 ) );
+        attrs.put( "mobile", RandomStringUtils.randomNumeric( 10 ) );
+        attrs.put( "employeeNumber", String.valueOf( counter ) );
+        attrs.put( "street", RandomStringUtils.randomAlphabetic( 20 ) );
+        attrs.put( "l", RandomStringUtils.randomAlphabetic( 10 ) );
+        attrs.put( "st", RandomStringUtils.randomAlphabetic( 2 ) );
+        attrs.put( "postalCode", RandomStringUtils.randomAlphabetic( 5 ) );
+        attrs.put( "postalAddress", RandomStringUtils.randomAlphabetic( 20 ) );
+        attrs.put( "description", RandomStringUtils.randomAlphabetic( 20 ) );
+        return attrs;
+    }
+
+
+    private void processOptions( CommandLine cmd )
+    {
+        if ( isDebugEnabled() )
+        {
+            System.out.println( "Processing options for capacity test ..." );
+        }
+
+        // -------------------------------------------------------------------
+        // figure out and error check the port value
+        // -------------------------------------------------------------------
+
+        if ( cmd.hasOption( 'p' ) ) // - user provided port w/ -p takes precedence
+        {
+            String val = cmd.getOptionValue( 'p' );
+            try
+            {
+                port = Integer.parseInt( val );
+            }
+            catch ( NumberFormatException e )
+            {
+                System.err.println( "port value of '" + val + "' is not a number" );
+                System.exit( 1 );
+            }
+
+            if ( port > AvailablePortFinder.MAX_PORT_NUMBER )
+            {
+                System.err.println( "port value of '" + val + "' is larger than max port number: "
+                    + AvailablePortFinder.MAX_PORT_NUMBER );
+                System.exit( 1 );
+            }
+            else if ( port < AvailablePortFinder.MIN_PORT_NUMBER )
+            {
+                System.err.println( "port value of '" + val + "' is smaller than the minimum port number: "
+                    + AvailablePortFinder.MIN_PORT_NUMBER );
+                System.exit( 1 );
+            }
+
+            if ( isDebugEnabled() )
+            {
+                System.out.println( "port overriden by -p option: " + port );
+            }
+        }
+        else if ( getApacheDS() != null )
+        {
+            port = getApacheDS().getLdapServer().getIpPort();
+
+            if ( isDebugEnabled() )
+            {
+                System.out.println( "port overriden by server.xml configuration: " + port );
+            }
+        }
+        else if ( isDebugEnabled() )
+        {
+            System.out.println( "port set to default: " + port );
+        }
+
+        // -------------------------------------------------------------------
+        // figure out the host value
+        // -------------------------------------------------------------------
+
+        if ( cmd.hasOption( 'h' ) )
+        {
+            host = cmd.getOptionValue( 'h' );
+
+            if ( isDebugEnabled() )
+            {
+                System.out.println( "host overriden by -h option: " + host );
+            }
+        }
+        else if ( isDebugEnabled() )
+        {
+            System.out.println( "host set to default: " + host );
+        }
+
+        // -------------------------------------------------------------------
+        // figure out the password value
+        // -------------------------------------------------------------------
+
+        if ( cmd.hasOption( 'w' ) )
+        {
+            password = cmd.getOptionValue( 'w' );
+
+            if ( isDebugEnabled() )
+            {
+                System.out.println( "password overriden by -w option: " + password );
+            }
+        }
+        else if ( isDebugEnabled() )
+        {
+            System.out.println( "password set to default: " + password );
+        }
+    }
+
+
+    public Options getOptions()
+    {
+        Options opts = new Options();
+        Option op = new Option( "f", "file", true, "file to output the stats to" );
+        op.setRequired( false );
+        opts.addOption( op );
+        op = new Option( "i", "install-path", true, "path to apacheds installation directory" );
+        op.setRequired( true );
+        opts.addOption( op );
+        op = new Option( "h", "host", true, "server host: defaults to localhost" );
+        op.setRequired( false );
+        opts.addOption( op );
+        op = new Option( "p", "port", true, "server port: defaults to 10389 or server.xml specified port" );
+        op.setRequired( false );
+        opts.addOption( op );
+        op = new Option( "w", "password", true, "the apacheds administrator's password: defaults to secret" );
+        op.setRequired( false );
+        opts.addOption( op );
+
+        op = new Option( "s", "start", true, "start on id: number to start on (user.start)" );
+        op.setRequired( false );
+        opts.addOption( op );
+
+        op = new Option( "e", "end", true, "end on id: number to end on (user.end)" );
+        op.setRequired( false );
+        opts.addOption( op );
+
+        return opts;
+    }
+}
diff --git a/old_trunk/server-tools/src/main/java/org/apache/directory/server/tools/DiagnosticCommand.java b/old_trunk/server-tools/src/main/java/org/apache/directory/server/tools/DiagnosticCommand.java
new file mode 100644
index 0000000..5c317cf
--- /dev/null
+++ b/old_trunk/server-tools/src/main/java/org/apache/directory/server/tools/DiagnosticCommand.java
@@ -0,0 +1,200 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.tools;
+
+
+import java.util.Hashtable;
+import javax.naming.ldap.InitialLdapContext;
+import javax.naming.ldap.LdapContext;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.Options;
+import org.apache.directory.daemon.AvailablePortFinder;
+import org.apache.directory.shared.ldap.message.extended.LaunchDiagnosticUiRequest;
+
+
+/**
+ * A command to send an extened request which launches a diagnostic UI 
+ * on the server's console.  This may not work unless the display is set 
+ * and access is granted to the display (via xhost +).  This is especially
+ * the case when running the server in daemon mode.  Usually when running
+ * the server in debug mode is when you want the diagnostics turned on.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 434420 $
+ */
+public class DiagnosticCommand extends ToolCommand
+{
+    public static final String PORT_RANGE = "(" + AvailablePortFinder.MIN_PORT_NUMBER + ", "
+        + AvailablePortFinder.MAX_PORT_NUMBER + ")";
+
+    private int port = 10389;
+    private String host = "localhost";
+    private String password = "secret";
+
+
+    protected DiagnosticCommand()
+    {
+        super( "diagnostic" );
+    }
+
+
+    public void execute( CommandLine cmd ) throws Exception
+    {
+        processOptions( cmd );
+
+        if ( isDebugEnabled() )
+        {
+            System.out.println( "Parameters for LaunchDiagnosticUI extended request:" );
+            System.out.println( "port = " + port );
+            System.out.println( "host = " + host );
+            System.out.println( "password = " + password );
+        }
+
+        Hashtable env = new Hashtable();
+        env.put( "java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory" );
+        env.put( "java.naming.provider.url", "ldap://" + host + ":" + port );
+        env.put( "java.naming.security.principal", "uid=admin,ou=system" );
+        env.put( "java.naming.security.credentials", password );
+        env.put( "java.naming.security.authentication", "simple" );
+
+        LdapContext ctx = new InitialLdapContext( env, null );
+        if ( isDebugEnabled() )
+        {
+            System.out.println( "Connection to the server established.\n" + "Sending extended request ... " );
+        }
+        ctx.extendedOperation( new LaunchDiagnosticUiRequest( 3 ) );
+        ctx.close();
+    }
+
+
+    private void processOptions( CommandLine cmd )
+    {
+        if ( isDebugEnabled() )
+        {
+            System.out.println( "Processing options for launching diagnostic UI ..." );
+        }
+
+        // -------------------------------------------------------------------
+        // figure out and error check the port value
+        // -------------------------------------------------------------------
+
+        if ( cmd.hasOption( 'p' ) ) // - user provided port w/ -p takes precedence
+        {
+            String val = cmd.getOptionValue( 'p' );
+            try
+            {
+                port = Integer.parseInt( val );
+            }
+            catch ( NumberFormatException e )
+            {
+                System.err.println( "port value of '" + val + "' is not a number" );
+                System.exit( 1 );
+            }
+
+            if ( port > AvailablePortFinder.MAX_PORT_NUMBER )
+            {
+                System.err.println( "port value of '" + val + "' is larger than max port number: "
+                    + AvailablePortFinder.MAX_PORT_NUMBER );
+                System.exit( 1 );
+            }
+            else if ( port < AvailablePortFinder.MIN_PORT_NUMBER )
+            {
+                System.err.println( "port value of '" + val + "' is smaller than the minimum port number: "
+                    + AvailablePortFinder.MIN_PORT_NUMBER );
+                System.exit( 1 );
+            }
+
+            if ( isDebugEnabled() )
+            {
+                System.out.println( "port overriden by -p option: " + port );
+            }
+        }
+        else if ( getApacheDS() != null )
+        {
+            port = getApacheDS().getLdapServer().getIpPort();
+
+            if ( isDebugEnabled() )
+            {
+                System.out.println( "port overriden by server.xml configuration: " + port );
+            }
+        }
+        else if ( isDebugEnabled() )
+        {
+            System.out.println( "port set to default: " + port );
+        }
+
+        // -------------------------------------------------------------------
+        // figure out the host value
+        // -------------------------------------------------------------------
+
+        if ( cmd.hasOption( 'h' ) )
+        {
+            host = cmd.getOptionValue( 'h' );
+
+            if ( isDebugEnabled() )
+            {
+                System.out.println( "host overriden by -h option: " + host );
+            }
+        }
+        else if ( isDebugEnabled() )
+        {
+            System.out.println( "host set to default: " + host );
+        }
+
+        // -------------------------------------------------------------------
+        // figure out the password value
+        // -------------------------------------------------------------------
+
+        if ( cmd.hasOption( 'w' ) )
+        {
+            password = cmd.getOptionValue( 'w' );
+
+            if ( isDebugEnabled() )
+            {
+                System.out.println( "password overriden by -w option: " + password );
+            }
+        }
+        else if ( isDebugEnabled() )
+        {
+            System.out.println( "password set to default: " + password );
+        }
+    }
+
+
+    public Options getOptions()
+    {
+        Options opts = new Options();
+        Option op = new Option( "h", "host", true, "server host: defaults to localhost" );
+        op.setRequired( false );
+        opts.addOption( op );
+        op = new Option( "p", "port", true, "server port: defaults to 10389 or server.xml specified port" );
+        op.setRequired( false );
+        opts.addOption( op );
+        op = new Option( "w", "password", true, "the apacheds administrator's password: defaults to secret" );
+        op.setRequired( false );
+        opts.addOption( op );
+        op = new Option( "i", "install-path", true, "path to apacheds installation directory" );
+        op.setRequired( false );
+        opts.addOption( op );
+        return opts;
+    }
+}
diff --git a/old_trunk/server-tools/src/main/java/org/apache/directory/server/tools/DisconnectNotificationCommand.java b/old_trunk/server-tools/src/main/java/org/apache/directory/server/tools/DisconnectNotificationCommand.java
new file mode 100644
index 0000000..0f64f50
--- /dev/null
+++ b/old_trunk/server-tools/src/main/java/org/apache/directory/server/tools/DisconnectNotificationCommand.java
@@ -0,0 +1,319 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.tools;
+
+
+import java.util.Hashtable;
+
+import javax.naming.directory.SearchControls;
+import javax.naming.event.EventContext;
+import javax.naming.event.NamingExceptionEvent;
+import javax.naming.ldap.InitialLdapContext;
+import javax.naming.ldap.LdapContext;
+import javax.naming.ldap.UnsolicitedNotification;
+import javax.naming.ldap.UnsolicitedNotificationEvent;
+import javax.naming.ldap.UnsolicitedNotificationListener;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.Options;
+import org.apache.directory.daemon.AvailablePortFinder;
+import org.apache.directory.shared.asn1.codec.DecoderException;
+import org.apache.directory.shared.ldap.message.extended.GracefulDisconnect;
+import org.apache.directory.shared.ldap.message.extended.NoticeOfDisconnect;
+
+
+/**
+ * Responds to unsolicited notifications by launching an external process.  Also 
+ * reconnects to the server an launches another process to notify that the server
+ * is back up.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 434420 $
+ */
+public class DisconnectNotificationCommand extends ToolCommand implements UnsolicitedNotificationListener
+{
+    UnsolicitedNotification notification;
+    boolean canceled = false;
+    private String host = "localhost";
+    private int port = 10389;
+    private String bindDn = "uid=admin,ou=system";
+    private String password = "secret";
+
+
+    //    private String shutdownCommand = "echo"; 
+    //    private String[] shutdownCommandArgs = new String[] { 
+    //        "server $HOST:$PORT will shutdown for $OFFLINE minutes in $DELAY seconds" };
+
+    protected DisconnectNotificationCommand()
+    {
+        super( "notifications" );
+    }
+
+
+    public void notificationReceived( UnsolicitedNotificationEvent evt )
+    {
+        notification = evt.getNotification();
+
+        if ( notification.getID().equals( NoticeOfDisconnect.EXTENSION_OID ) )
+        {
+            System.out.println( "\nRecieved NoticeOfDisconnect: " + NoticeOfDisconnect.EXTENSION_OID );
+            System.out.println( "Expect to loose this connection without further information." );
+            canceled = true;
+        }
+        else if ( notification.getID().equals( GracefulDisconnect.EXTENSION_OID ) )
+        {
+            System.out.println( "Recieved GracefulDisconnect: " + GracefulDisconnect.EXTENSION_OID );
+            GracefulDisconnect gd = null;
+
+            try
+            {
+                gd = new GracefulDisconnect( notification.getEncodedValue() );
+            }
+            catch ( DecoderException de )
+            {
+                // TODO Auto-generated catch block
+                de.printStackTrace();
+            }
+
+            System.out.println( "LDAP server will shutdown in " + gd.getDelay() + " seconds." );
+            System.out.println( "LDAP server will be back online in " + gd.getTimeOffline() + " minutes." );
+
+            if ( gd.getDelay() > 0 )
+            {
+                Thread t = new Thread( new Counter( gd.getDelay() ) );
+                t.start();
+            }
+        }
+        else
+        {
+            System.out.println( "Unknown event recieved with OID: " + evt.getNotification().getID() );
+        }
+    }
+
+
+    public void namingExceptionThrown( NamingExceptionEvent evt )
+    {
+        canceled = true;
+        System.out.println( "Got an excption event: " + evt.getException().getMessage() );
+        System.out.println( "Process shutting down abruptly." );
+        System.exit( 1 );
+    }
+
+    class Counter implements Runnable
+    {
+        int delay;
+
+
+        Counter( int delay )
+        {
+            this.delay = delay;
+        }
+
+
+        public void run()
+        {
+            System.out.println( "Starting countdown until server shutdown:" );
+            System.out.print( "[" );
+            long delayMillis = delay * 1000 - 1000; // 1000 is for setup costs
+            long startTime = System.currentTimeMillis();
+            while ( System.currentTimeMillis() - startTime < delayMillis && !canceled )
+            {
+                try
+                {
+                    Thread.sleep( 1000 );
+                }
+                catch ( InterruptedException e )
+                {
+                }
+                System.out.print( "." );
+            }
+
+            if ( canceled )
+            {
+                System.out.println( " -- countdown canceled -- " );
+            }
+            else
+            {
+                System.out.println( "]" );
+                System.out.println( "Client shutting down gracefully." );
+                System.exit( 0 );
+            }
+        }
+    }
+
+
+    public void execute( CommandLine cmd ) throws Exception
+    {
+        processOptions( cmd );
+
+        Hashtable env = new Hashtable();
+        env.put( "java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory" );
+        env.put( "java.naming.provider.url", "ldap://" + host + ":" + port );
+        env.put( "java.naming.security.principal", bindDn );
+        env.put( "java.naming.security.credentials", password );
+        env.put( "java.naming.security.authentication", "simple" );
+
+        LdapContext ctx = new InitialLdapContext( env, null );
+        ctx = ctx.newInstance( null );
+        UnsolicitedNotificationListener listener = new DisconnectNotificationCommand();
+        ( ( EventContext ) ctx ).addNamingListener( "", SearchControls.SUBTREE_SCOPE, listener );
+
+        System.out.println( "Listening for notifications." );
+        System.out.println( "Press any key to terminate." );
+        System.in.read();
+        ctx.close();
+        System.out.println( "Process terminated!!!" );
+    }
+
+
+    private void processOptions( CommandLine cmd )
+    {
+        if ( isDebugEnabled() )
+        {
+            System.out.println( "Processing options for disconnect notifications ..." );
+        }
+
+        // -------------------------------------------------------------------
+        // figure out and error check the port value
+        // -------------------------------------------------------------------
+
+        if ( cmd.hasOption( 'p' ) ) // - user provided port w/ -p takes precedence
+        {
+            String val = cmd.getOptionValue( 'p' );
+            try
+            {
+                port = Integer.parseInt( val );
+            }
+            catch ( NumberFormatException e )
+            {
+                System.err.println( "port value of '" + val + "' is not a number" );
+                System.exit( 1 );
+            }
+
+            if ( port > AvailablePortFinder.MAX_PORT_NUMBER )
+            {
+                System.err.println( "port value of '" + val + "' is larger than max port number: "
+                    + AvailablePortFinder.MAX_PORT_NUMBER );
+                System.exit( 1 );
+            }
+            else if ( port < AvailablePortFinder.MIN_PORT_NUMBER )
+            {
+                System.err.println( "port value of '" + val + "' is smaller than the minimum port number: "
+                    + AvailablePortFinder.MIN_PORT_NUMBER );
+                System.exit( 1 );
+            }
+
+            if ( isDebugEnabled() )
+            {
+                System.out.println( "port overriden by -p option: " + port );
+            }
+        }
+        else if ( getApacheDS() != null )
+        {
+            port = getApacheDS().getLdapServer().getIpPort();
+
+            if ( isDebugEnabled() )
+            {
+                System.out.println( "port overriden by server.xml configuration: " + port );
+            }
+        }
+        else if ( isDebugEnabled() )
+        {
+            System.out.println( "port set to default: " + port );
+        }
+
+        // -------------------------------------------------------------------
+        // figure out the host value
+        // -------------------------------------------------------------------
+
+        if ( cmd.hasOption( 'h' ) )
+        {
+            host = cmd.getOptionValue( 'h' );
+
+            if ( isDebugEnabled() )
+            {
+                System.out.println( "host overriden by -h option: " + host );
+            }
+        }
+        else if ( isDebugEnabled() )
+        {
+            System.out.println( "host set to default: " + host );
+        }
+
+        // -------------------------------------------------------------------
+        // figure out the password value
+        // -------------------------------------------------------------------
+
+        if ( cmd.hasOption( 'w' ) )
+        {
+            password = cmd.getOptionValue( 'w' );
+
+            if ( isDebugEnabled() )
+            {
+                System.out.println( "password overriden by -w option: " + password );
+            }
+        }
+        else if ( isDebugEnabled() )
+        {
+            System.out.println( "password set to default: " + password );
+        }
+
+        // -------------------------------------------------------------------
+        // figure out the binddn value
+        // -------------------------------------------------------------------
+
+        if ( cmd.hasOption( 'u' ) )
+        {
+            bindDn = cmd.getOptionValue( 'u' );
+
+            if ( isDebugEnabled() )
+            {
+                System.out.println( "binddn overriden by -u option: " + bindDn );
+            }
+        }
+        else if ( isDebugEnabled() )
+        {
+            System.out.println( "binddn set to default: " + bindDn );
+        }
+    }
+
+
+    public Options getOptions()
+    {
+        Options opts = new Options();
+        Option op = new Option( "h", "host", true, "server host: defaults to localhost" );
+        op.setRequired( false );
+        opts.addOption( op );
+        op = new Option( "p", "port", true, "server port: defaults to 10389 or server.xml specified port" );
+        op.setRequired( false );
+        opts.addOption( op );
+        op = new Option( "w", "password", true, "the apacheds administrator's password: defaults to secret" );
+        op.setRequired( false );
+        opts.addOption( op );
+        op = new Option( "u", "binddn", true, "an apacheds user's dn: defaults to " + bindDn );
+        op.setRequired( false );
+        opts.addOption( op );
+        op = new Option( "i", "install-path", true, "path to apacheds installation directory" );
+        op.setRequired( false );
+        opts.addOption( op );
+        return opts;
+    }
+}
diff --git a/old_trunk/server-tools/src/main/java/org/apache/directory/server/tools/DumpCommand.java b/old_trunk/server-tools/src/main/java/org/apache/directory/server/tools/DumpCommand.java
new file mode 100644
index 0000000..f3be2f4
--- /dev/null
+++ b/old_trunk/server-tools/src/main/java/org/apache/directory/server/tools/DumpCommand.java
@@ -0,0 +1,349 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.tools;
+
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+
+import jdbm.helper.MRU;
+import jdbm.recman.BaseRecordManager;
+import jdbm.recman.CacheRecordManager;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.Options;
+import org.apache.directory.server.constants.ServerDNConstants;
+import org.apache.directory.server.core.DefaultDirectoryService;
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.entry.DefaultServerEntry;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.partition.impl.btree.Index;
+import org.apache.directory.server.core.partition.impl.btree.Tuple;
+import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmIndex;
+import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmMasterTable;
+import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmPartition;
+import org.apache.directory.server.core.schema.PartitionSchemaLoader;
+import org.apache.directory.server.schema.SerializableComparator;
+import org.apache.directory.server.schema.bootstrap.ApacheSchema;
+import org.apache.directory.server.schema.bootstrap.ApachemetaSchema;
+import org.apache.directory.server.schema.bootstrap.BootstrapSchemaLoader;
+import org.apache.directory.server.schema.bootstrap.CoreSchema;
+import org.apache.directory.server.schema.bootstrap.Schema;
+import org.apache.directory.server.schema.bootstrap.SystemSchema;
+import org.apache.directory.server.schema.bootstrap.partition.DbFileListing;
+import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
+import org.apache.directory.server.schema.registries.DefaultOidRegistry;
+import org.apache.directory.server.schema.registries.DefaultRegistries;
+import org.apache.directory.server.schema.registries.OidRegistry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.exception.LdapConfigurationException;
+import org.apache.directory.shared.ldap.exception.LdapNamingException;
+import org.apache.directory.shared.ldap.ldif.LdifUtils;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.schema.UsageEnum;
+import org.apache.directory.shared.ldap.util.Base64;
+
+
+/**
+ * Simple tool used to dump the contents of a jdbm based partition.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 493916 $
+ */
+public class DumpCommand extends ToolCommand
+{
+    private Registries bootstrapRegistries = new DefaultRegistries( "bootstrap", new BootstrapSchemaLoader(),
+        new DefaultOidRegistry() );
+    private Set exclusions = new HashSet();
+    private boolean includeOperational = false;
+
+
+    public DumpCommand()
+    {
+        super( "dump" );
+    }
+
+
+    private Registries loadRegistries() throws Exception
+    {
+        // --------------------------------------------------------------------
+        // Load the bootstrap schemas to start up the schema partition
+        // --------------------------------------------------------------------
+
+        // setup temporary loader and temp registry 
+        BootstrapSchemaLoader loader = new BootstrapSchemaLoader();
+        OidRegistry oidRegistry = new DefaultOidRegistry();
+        final Registries registries = new DefaultRegistries( "bootstrap", loader, oidRegistry );
+
+        // load essential bootstrap schemas 
+        Set<Schema> bootstrapSchemas = new HashSet<Schema>();
+        bootstrapSchemas.add( new ApachemetaSchema() );
+        bootstrapSchemas.add( new ApacheSchema() );
+        bootstrapSchemas.add( new CoreSchema() );
+        bootstrapSchemas.add( new SystemSchema() );
+        loader.loadWithDependencies( bootstrapSchemas, registries );
+
+        // run referential integrity tests
+        List<Throwable> errors = registries.checkRefInteg();
+
+        if ( !errors.isEmpty() )
+        {
+            NamingException e = new NamingException();
+            e.setRootCause( ( Throwable ) errors.get( 0 ) );
+            throw e;
+        }
+
+        SerializableComparator.setRegistry( registries.getComparatorRegistry() );
+
+        // --------------------------------------------------------------------
+        // Initialize schema partition or bomb out if we cannot find it on disk
+        // --------------------------------------------------------------------
+
+        // If not present then we need to abort 
+        File schemaDirectory = new File( getLayout().getPartitionsDirectory(), "schema" );
+        if ( !schemaDirectory.exists() )
+        {
+            throw new LdapConfigurationException( "The following schema directory from "
+                + "the installation layout could not be found:\n\t" + schemaDirectory );
+        }
+
+        JdbmPartition schemaPartition = new JdbmPartition();
+        schemaPartition.setId( "schema" );
+        schemaPartition.setCacheSize( 1000 );
+
+        DbFileListing listing;
+        try
+        {
+            listing = new DbFileListing();
+        }
+        catch ( IOException e )
+        {
+            throw new LdapNamingException( "Got IOException while trying to read DBFileListing: " + e.getMessage(),
+                ResultCodeEnum.OTHER );
+        }
+
+        Set<Index> indexedAttributes = new HashSet<Index>();
+
+        for ( String attributeId : listing.getIndexedAttributes() )
+        {
+            indexedAttributes.add( new JdbmIndex( attributeId ) );
+        }
+
+        schemaPartition.setIndexedAttributes( indexedAttributes );
+        schemaPartition.setSuffix( ServerDNConstants.OU_SCHEMA_DN );
+
+        ServerEntry systemEntry = new DefaultServerEntry( registries, new LdapDN( "ou=schema" ) );
+        systemEntry.put( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC,
+            SchemaConstants.ORGANIZATIONAL_UNIT_OC );
+        systemEntry.put( SchemaConstants.OU_AT, "schema" );
+        schemaPartition.setContextEntry( systemEntry );
+
+        DirectoryService directoryService = new DefaultDirectoryService();
+        schemaPartition.init( directoryService );
+
+        // --------------------------------------------------------------------
+        // Initialize schema subsystem and reset registries
+        // --------------------------------------------------------------------
+
+        PartitionSchemaLoader schemaLoader = new PartitionSchemaLoader( schemaPartition, registries );
+        Registries globalRegistries = new DefaultRegistries( "global", schemaLoader, oidRegistry );
+        schemaLoader.loadEnabled( globalRegistries );
+        SerializableComparator.setRegistry( globalRegistries.getComparatorRegistry() );
+        return globalRegistries;
+    }
+
+
+    public void execute( CommandLine cmdline ) throws Exception
+    {
+        getLayout().verifyInstallation();
+        bootstrapRegistries = loadRegistries();
+
+        includeOperational = cmdline.hasOption( 'o' );
+        String[] partitions = cmdline.getOptionValues( 'p' );
+        String outputFile = cmdline.getOptionValue( 'f' );
+        PrintWriter out = null;
+
+        String[] excludedAttributes = cmdline.getOptionValues( 'e' );
+        if ( excludedAttributes != null )
+        {
+            AttributeTypeRegistry registry = bootstrapRegistries.getAttributeTypeRegistry();
+            for ( int ii = 0; ii < excludedAttributes.length; ii++ )
+            {
+                AttributeType type = registry.lookup( excludedAttributes[ii] );
+                exclusions.add( type.getName() );
+            }
+        }
+
+        if ( outputFile == null )
+        {
+            out = new PrintWriter( System.out );
+        }
+        else
+        {
+            out = new PrintWriter( new FileWriter( outputFile ) );
+        }
+
+        for ( int ii = 0; ii < partitions.length; ii++ )
+        {
+            File partitionDirectory = new File( getLayout().getPartitionsDirectory(), partitions[ii] );
+            out.println( "\n\n" );
+            dump( partitionDirectory, out );
+        }
+    }
+
+
+    private void dump( File partitionDirectory, PrintWriter out ) throws Exception
+    {
+        if ( !partitionDirectory.exists() )
+        {
+            System.err.println( "Partition directory " + partitionDirectory + " does not exist!" );
+            System.exit( 1 );
+        }
+
+        out.println( "# ========================================================================" );
+        out.println( "# ApacheDS Tools Version: " + getVersion() );
+        out.println( "# Partition Directory: " + partitionDirectory );
+        out.println( "# ========================================================================\n\n" );
+
+        String path = partitionDirectory.getPath() + File.separator + "master";
+        BaseRecordManager base = new BaseRecordManager( path );
+        base.disableTransactions();
+        CacheRecordManager recMan = new CacheRecordManager( base, new MRU( 1000 ) );
+
+        JdbmMasterTable master = new JdbmMasterTable( recMan, bootstrapRegistries );
+        AttributeType attributeType = bootstrapRegistries.getAttributeTypeRegistry().lookup( "apacheUpdn" );
+        JdbmIndex idIndex = new JdbmIndex();
+        idIndex.setAttributeId( attributeType.getName() );
+        idIndex.setWkDirPath( partitionDirectory );
+        idIndex.setCacheSize( 1000 );
+        idIndex.setNumDupLimit( 1000 );
+        idIndex.init( attributeType, partitionDirectory );
+
+        out.println( "#---------------------" );
+        NamingEnumeration list = master.listTuples();
+        StringBuffer buf = new StringBuffer();
+        while ( list.hasMore() )
+        {
+            Tuple tuple = ( Tuple ) list.next();
+            BigInteger id = ( BigInteger ) tuple.getKey();
+            String dn = ( String ) idIndex.reverseLookup( id );
+            Attributes entry = ( Attributes ) tuple.getValue();
+
+            filterAttributes( dn, entry );
+
+            buf.append( "# Entry: " ).append( id ).append( "\n#---------------------\n\n" );
+            if ( !LdifUtils.isLDIFSafe( dn ) )
+            {
+                // If the DN isn't LdifSafe, it needs to be Base64 encoded.
+
+                buf.append( "dn:: " ).append( new String( Base64.encode( dn.getBytes() ) ) );
+            }
+            else
+            {
+                buf.append( "dn: " ).append( dn );
+            }
+            buf.append( "\n" ).append( LdifUtils.convertToLdif( entry ) );
+            if ( list.hasMore() )
+            {
+                buf.append( "\n\n#---------------------\n" );
+            }
+            out.print( buf.toString() );
+            out.flush();
+            buf.setLength( 0 );
+        }
+    }
+
+
+    private void filterAttributes( String dn, Attributes entry ) throws NamingException
+    {
+        List toRemove = new ArrayList();
+        AttributeTypeRegistry registry = bootstrapRegistries.getAttributeTypeRegistry();
+        NamingEnumeration attrs = entry.getAll();
+        while ( attrs.hasMore() )
+        {
+            Attribute attr = ( Attribute ) attrs.next();
+            if ( !registry.hasAttributeType( attr.getID() ) )
+            {
+                if ( !isQuietEnabled() )
+                {
+                    System.out
+                        .println( "# Cannot properly filter unrecognized attribute " + attr.getID() + " in " + dn );
+                }
+                continue;
+            }
+
+            AttributeType type = registry.lookup( attr.getID() );
+            boolean isOperational = type.getUsage() != UsageEnum.USER_APPLICATIONS;
+            if ( exclusions.contains( attr.getID() ) || ( isOperational && ( !includeOperational ) ) )
+            {
+                toRemove.add( attr.getID() );
+            }
+        }
+        for ( int ii = 0; ii < toRemove.size(); ii++ )
+        {
+            String id = ( String ) toRemove.get( ii );
+            entry.remove( id );
+            if ( isDebugEnabled() )
+            {
+                System.out.println( "# Excluding attribute " + id + " in " + dn );
+            }
+        }
+    }
+
+
+    public Options getOptions()
+    {
+        Options opts = new Options();
+        Option op = new Option( "f", "file", true, "file to output the dump to" );
+        op.setRequired( false );
+        opts.addOption( op );
+        op = new Option( "p", "partitions", true, "the partitions to dump" );
+        op.setRequired( true );
+        op.setValueSeparator( File.pathSeparatorChar );
+        opts.addOption( op );
+        op = new Option( "e", "excluded-attributes", true, "the attributes to exclude" );
+        op.setRequired( false );
+        op.setValueSeparator( File.pathSeparatorChar );
+        opts.addOption( op );
+        op = new Option( "o", "include-operational", false, "include operational attributes: defaults to false" );
+        op.setRequired( false );
+        opts.addOption( op );
+        op = new Option( "i", "install-path", true, "path to apacheds installation directory" );
+        op.setRequired( true );
+        opts.addOption( op );
+        return opts;
+    }
+}
diff --git a/old_trunk/server-tools/src/main/java/org/apache/directory/server/tools/GracefulShutdownCommand.java b/old_trunk/server-tools/src/main/java/org/apache/directory/server/tools/GracefulShutdownCommand.java
new file mode 100644
index 0000000..300bf89
--- /dev/null
+++ b/old_trunk/server-tools/src/main/java/org/apache/directory/server/tools/GracefulShutdownCommand.java
@@ -0,0 +1,376 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.tools;
+
+
+import java.util.Hashtable;
+
+import javax.naming.CommunicationException;
+import javax.naming.ldap.InitialLdapContext;
+import javax.naming.ldap.LdapContext;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.Options;
+import org.apache.directory.daemon.AvailablePortFinder;
+import org.apache.directory.shared.ldap.message.extended.GracefulShutdownRequest;
+
+
+/**
+ * A command used to send a graceful disconnect to established clients 
+ * while allowing them time to complete operations already in progress.
+ * 
+ * @see <a href="http://docs.safehaus.org/display/APACHEDS/LDAP+Extensions+for+Graceful+Shutdown">
+ * Graceful Shutdown</a>
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 434420 $
+ */
+public class GracefulShutdownCommand extends ToolCommand
+{
+    public static final String PORT_RANGE = "(" + AvailablePortFinder.MIN_PORT_NUMBER + ", "
+        + AvailablePortFinder.MAX_PORT_NUMBER + ")";
+
+    private static final int DELAY_MAX = 86400;
+
+    private static final int TIME_OFFLINE_MAX = 720;
+
+    private int port = 10389;
+    private String host = "localhost";
+    private String password = "secret";
+    private int delay;
+    private int timeOffline;
+
+
+    protected GracefulShutdownCommand()
+    {
+        super( "graceful" );
+    }
+
+    private boolean isWaiting;
+    private boolean isSuccess = false;
+    private Thread executeThread = null;
+
+
+    public void execute( CommandLine cmd ) throws Exception
+    {
+        executeThread = Thread.currentThread();
+        processOptions( cmd );
+
+        if ( isDebugEnabled() )
+        {
+            System.out.println( "Parameters for GracefulShutdown extended request:" );
+            System.out.println( "port = " + port );
+            System.out.println( "host = " + host );
+            System.out.println( "password = " + password );
+            System.out.println( "delay = " + delay );
+            System.out.println( "timeOffline = " + timeOffline );
+        }
+
+        Hashtable env = new Hashtable();
+        env.put( "java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory" );
+        env.put( "java.naming.provider.url", "ldap://" + host + ":" + port );
+        env.put( "java.naming.security.principal", "uid=admin,ou=system" );
+        env.put( "java.naming.security.credentials", password );
+        env.put( "java.naming.security.authentication", "simple" );
+
+        LdapContext ctx = new InitialLdapContext( env, null );
+        if ( !isQuietEnabled() )
+        {
+            System.out.println( "Connection to the server established.\n"
+                + "Sending extended request and blocking for shutdown:" );
+            isWaiting = true;
+            Thread t = new Thread( new Ticker() );
+            t.start();
+        }
+        try
+        {
+            ctx.extendedOperation( new GracefulShutdownRequest( 0, timeOffline, delay ) );
+            isSuccess = true;
+        }
+        catch ( Throwable t )
+        {
+            /*
+             * Sometimes because of timing issues we show a failure when the 
+             * shutdown has succeeded so we should check if the server is up
+             * before we set success to false.
+             */
+            try
+            {
+                new InitialLdapContext( env, null );
+                isSuccess = false;
+                System.err.print( "shutdown request failed with error: " + t.getMessage() );
+            }
+            catch ( CommunicationException e )
+            {
+                isSuccess = true;
+            }
+        }
+        isWaiting = false;
+        ctx.close();
+    }
+
+    class Ticker implements Runnable
+    {
+        public void run()
+        {
+            if ( !isQuietEnabled() )
+                System.out.print( "[waiting for shutdown] " );
+            while ( isWaiting )
+            {
+                try
+                {
+                    Thread.sleep( 1000 );
+                }
+                catch ( InterruptedException e )
+                {
+                    // TODO Auto-generated catch block
+                    e.printStackTrace();
+                }
+                if ( !isQuietEnabled() )
+                    System.out.print( "." );
+            }
+            if ( isSuccess )
+            {
+                if ( !isQuietEnabled() )
+                    System.out.println( "\n[shutdown complete]" );
+                try
+                {
+                    executeThread.join( 1000 );
+                }
+                catch ( InterruptedException e )
+                {
+                    e.printStackTrace();
+                }
+                System.exit( 0 );
+            }
+            else
+            {
+                if ( !isQuietEnabled() )
+                    System.out.println( "\n[shutdown failed]" );
+                try
+                {
+                    executeThread.join( 1000 );
+                }
+                catch ( InterruptedException e )
+                {
+                    e.printStackTrace();
+                }
+                System.exit( 1 );
+            }
+        }
+    }
+
+
+    private void processOptions( CommandLine cmd )
+    {
+        if ( isDebugEnabled() )
+        {
+            System.out.println( "Processing options for graceful shutdown ..." );
+        }
+
+        // -------------------------------------------------------------------
+        // figure out and error check the port value
+        // -------------------------------------------------------------------
+
+        if ( cmd.hasOption( 'p' ) ) // - user provided port w/ -p takes precedence
+        {
+            String val = cmd.getOptionValue( 'p' );
+            try
+            {
+                port = Integer.parseInt( val );
+            }
+            catch ( NumberFormatException e )
+            {
+                System.err.println( "port value of '" + val + "' is not a number" );
+                System.exit( 1 );
+            }
+
+            if ( port > AvailablePortFinder.MAX_PORT_NUMBER )
+            {
+                System.err.println( "port value of '" + val + "' is larger than max port number: "
+                    + AvailablePortFinder.MAX_PORT_NUMBER );
+                System.exit( 1 );
+            }
+            else if ( port < AvailablePortFinder.MIN_PORT_NUMBER )
+            {
+                System.err.println( "port value of '" + val + "' is smaller than the minimum port number: "
+                    + AvailablePortFinder.MIN_PORT_NUMBER );
+                System.exit( 1 );
+            }
+
+            if ( isDebugEnabled() )
+            {
+                System.out.println( "port overriden by -p option: " + port );
+            }
+        }
+        else if ( getApacheDS() != null )
+        {
+            port = getApacheDS().getLdapServer().getIpPort();
+
+            if ( isDebugEnabled() )
+            {
+                System.out.println( "port overriden by server.xml configuration: " + port );
+            }
+        }
+        else if ( isDebugEnabled() )
+        {
+            System.out.println( "port set to default: " + port );
+        }
+
+        // -------------------------------------------------------------------
+        // figure out the host value
+        // -------------------------------------------------------------------
+
+        if ( cmd.hasOption( 'h' ) )
+        {
+            host = cmd.getOptionValue( 'h' );
+
+            if ( isDebugEnabled() )
+            {
+                System.out.println( "host overriden by -h option: " + host );
+            }
+        }
+        else if ( isDebugEnabled() )
+        {
+            System.out.println( "host set to default: " + host );
+        }
+
+        // -------------------------------------------------------------------
+        // figure out the password value
+        // -------------------------------------------------------------------
+
+        if ( cmd.hasOption( 'w' ) )
+        {
+            password = cmd.getOptionValue( 'w' );
+
+            if ( isDebugEnabled() )
+            {
+                System.out.println( "password overriden by -w option: " + password );
+            }
+        }
+        else if ( isDebugEnabled() )
+        {
+            System.out.println( "password set to default: " + password );
+        }
+
+        // -------------------------------------------------------------------
+        // figure out the delay value
+        // -------------------------------------------------------------------
+
+        if ( cmd.hasOption( 'e' ) )
+        {
+            String val = cmd.getOptionValue( 'e' );
+            try
+            {
+                delay = Integer.parseInt( val );
+            }
+            catch ( NumberFormatException e )
+            {
+                System.err.println( "delay value of '" + val + "' is not a number" );
+                System.exit( 1 );
+            }
+
+            if ( delay > DELAY_MAX )
+            {
+                System.err.println( "delay value of '" + val + "' is larger than max delay (seconds) allowed: "
+                    + DELAY_MAX );
+                System.exit( 1 );
+            }
+            else if ( delay < 0 )
+            {
+                System.err.println( "delay value of '" + val + "' is less than zero and makes no sense" );
+                System.exit( 1 );
+            }
+
+            if ( isDebugEnabled() )
+            {
+                System.out.println( "delay seconds overriden by -e option: " + delay );
+            }
+        }
+        else if ( isDebugEnabled() )
+        {
+            System.out.println( "Using default delay value of " + delay );
+        }
+
+        // -------------------------------------------------------------------
+        // figure out the timeOffline value
+        // -------------------------------------------------------------------
+
+        if ( cmd.hasOption( 't' ) )
+        {
+            String val = cmd.getOptionValue( 't' );
+            try
+            {
+                timeOffline = Integer.parseInt( val );
+            }
+            catch ( NumberFormatException e )
+            {
+                System.err.println( "timeOffline value of '" + val + "' is not a number" );
+                System.exit( 1 );
+            }
+
+            if ( timeOffline > TIME_OFFLINE_MAX )
+            {
+                System.err.println( "timeOffline value of '" + val
+                    + "' is larger than max timeOffline (minutes) allowed: " + TIME_OFFLINE_MAX );
+                System.exit( 1 );
+            }
+            else if ( timeOffline < 0 )
+            {
+                System.err.println( "timeOffline value of '" + val + "' is less than zero and makes no sense" );
+                System.exit( 1 );
+            }
+
+            if ( isDebugEnabled() )
+            {
+                System.out.println( "timeOffline seconds overriden by -t option: " + timeOffline );
+            }
+        }
+        else if ( isDebugEnabled() )
+        {
+            System.out.println( "Using default timeOffline value of " + delay );
+        }
+    }
+
+
+    public Options getOptions()
+    {
+        Options opts = new Options();
+        Option op = new Option( "h", "host", true, "server host: defaults to localhost" );
+        op.setRequired( false );
+        opts.addOption( op );
+        op = new Option( "p", "port", true, "server port: defaults to 10389 or server.xml specified port" );
+        op.setRequired( false );
+        opts.addOption( op );
+        op = new Option( "e", "delay", true, "delay (seconds) before shutdown: defaults to 0" );
+        op.setRequired( false );
+        opts.addOption( op );
+        op = new Option( "w", "password", true, "the apacheds administrator's password: defaults to secret" );
+        op.setRequired( false );
+        opts.addOption( op );
+        op = new Option( "t", "time-offline", true, "server offline time (minutes): defaults to 0 (indefinate)" );
+        op.setRequired( false );
+        opts.addOption( op );
+        op = new Option( "i", "install-path", true, "path to apacheds installation directory" );
+        op.setRequired( false );
+        opts.addOption( op );
+        return opts;
+    }
+}
diff --git a/old_trunk/server-tools/src/main/java/org/apache/directory/server/tools/ImportCommand.java b/old_trunk/server-tools/src/main/java/org/apache/directory/server/tools/ImportCommand.java
new file mode 100644
index 0000000..250d5d9
--- /dev/null
+++ b/old_trunk/server-tools/src/main/java/org/apache/directory/server/tools/ImportCommand.java
@@ -0,0 +1,987 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.tools;
+
+
+import java.io.File;
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.net.UnknownHostException;
+import java.nio.ByteBuffer;
+import java.nio.channels.SocketChannel;
+import java.util.Iterator;
+
+import javax.naming.InvalidNameException;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.Options;
+import org.apache.directory.daemon.AvailablePortFinder;
+import org.apache.directory.shared.asn1.ber.Asn1Decoder;
+import org.apache.directory.shared.asn1.ber.IAsn1Container;
+import org.apache.directory.shared.asn1.ber.tlv.TLVStateEnum;
+import org.apache.directory.shared.asn1.codec.DecoderException;
+import org.apache.directory.shared.asn1.codec.EncoderException;
+import org.apache.directory.shared.ldap.codec.LdapConstants;
+import org.apache.directory.shared.ldap.codec.LdapDecoder;
+import org.apache.directory.shared.ldap.codec.LdapMessage;
+import org.apache.directory.shared.ldap.codec.LdapMessageContainer;
+import org.apache.directory.shared.ldap.codec.LdapResult;
+import org.apache.directory.shared.ldap.codec.add.AddRequest;
+import org.apache.directory.shared.ldap.codec.bind.BindRequest;
+import org.apache.directory.shared.ldap.codec.bind.BindResponse;
+import org.apache.directory.shared.ldap.codec.bind.LdapAuthentication;
+import org.apache.directory.shared.ldap.codec.bind.SimpleAuthentication;
+import org.apache.directory.shared.ldap.codec.del.DelRequest;
+import org.apache.directory.shared.ldap.codec.extended.ExtendedResponse;
+import org.apache.directory.shared.ldap.codec.modify.ModifyRequest;
+import org.apache.directory.shared.ldap.codec.modifyDn.ModifyDNRequest;
+import org.apache.directory.shared.ldap.codec.unbind.UnBindRequest;
+import org.apache.directory.shared.ldap.ldif.ChangeType;
+import org.apache.directory.shared.ldap.ldif.LdifEntry;
+import org.apache.directory.shared.ldap.ldif.LdifReader;
+import org.apache.directory.shared.ldap.message.ModificationItemImpl;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.name.Rdn;
+import org.apache.directory.shared.ldap.util.StringTools;
+
+
+/**
+ * A command to import data into a server. The data to be imported must be
+ * stored in a Ldif File, and they could be added entries or modified entries.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 406112 $
+ */
+public class ImportCommand extends ToolCommand
+{
+    public static final String PORT_RANGE = "(" + AvailablePortFinder.MIN_PORT_NUMBER + ", "
+        + AvailablePortFinder.MAX_PORT_NUMBER + ")";
+
+    private int port = 10389;
+
+    private String host = "localhost";
+
+    private String password = "secret";
+
+    private String user = "uid=admin,ou=system";
+
+    private String auth = "simple";
+
+    private File ldifFile;
+
+    private String logs;
+
+    private boolean ignoreErrors = false;
+
+    private static final int IMPORT_ERROR = -1;
+    private static final int IMPORT_SUCCESS = 0;
+
+    /**
+     * Socket used to connect to the server
+     */
+    private SocketChannel channel;
+
+    private SocketAddress serverAddress;
+
+    private IAsn1Container ldapMessageContainer = new LdapMessageContainer();
+
+    private Asn1Decoder ldapDecoder = new LdapDecoder();
+
+
+    /**
+     * The constructor save the command's name into it's super class
+     * 
+     */
+    protected ImportCommand()
+    {
+        super( "import" );
+    }
+
+
+    /**
+     * Connect to the LDAP server through a socket and establish the Input and
+     * Output Streams. All the required information for the connection should be
+     * in the options from the command line, or the default values.
+     * 
+     * @throws UnknownHostException
+     *             The hostname or the Address of server could not be found
+     * @throws IOException
+     *             There was a error opening or establishing the socket
+     */
+    private void connect() throws UnknownHostException, IOException
+    {
+        serverAddress = new InetSocketAddress( host, port );
+        channel = SocketChannel.open( serverAddress );
+        channel.configureBlocking( true );
+    }
+
+
+    private void sendMessage( ByteBuffer bb ) throws IOException
+    {
+        channel.write( bb );
+        bb.clear();
+    }
+
+
+    private LdapMessage readResponse( ByteBuffer bb ) throws IOException, DecoderException, NamingException
+    {
+
+        LdapMessage messageResp = null;
+
+        while ( true )
+        {
+            int nbRead = channel.read( bb );
+
+            if ( nbRead == -1 )
+            {
+                break;
+            }
+            else
+            {
+                bb.flip();
+
+                // Decode the PDU
+                ldapDecoder.decode( bb, ldapMessageContainer );
+
+                if ( ldapMessageContainer.getState() == TLVStateEnum.PDU_DECODED )
+                {
+                    messageResp = ( ( LdapMessageContainer ) ldapMessageContainer ).getLdapMessage();
+
+                    if ( messageResp instanceof BindResponse )
+                    {
+                        BindResponse resp = ( ( LdapMessageContainer ) ldapMessageContainer ).getLdapMessage()
+                            .getBindResponse();
+
+                        if ( resp.getLdapResult().getResultCode() != ResultCodeEnum.SUCCESS )
+                        {
+                            System.out.println( "Error : " + resp.getLdapResult().getErrorMessage() );
+                        }
+                    }
+                    else if ( messageResp instanceof ExtendedResponse )
+                    {
+                        ExtendedResponse resp = ( ( LdapMessageContainer ) ldapMessageContainer ).getLdapMessage()
+                            .getExtendedResponse();
+
+                        if ( resp.getLdapResult().getResultCode() != ResultCodeEnum.SUCCESS )
+                        {
+                            System.out.println( "Error : " + resp.getLdapResult().getErrorMessage() );
+                        }
+                    }
+
+                    ( ( LdapMessageContainer ) ldapMessageContainer ).clean();
+                    break;
+                }
+                else
+                {
+                    bb.flip();
+                }
+            }
+        }
+
+        return messageResp;
+
+    }
+
+
+    /**
+     * Send the entry to the encoder, then wait for a
+     * reponse from the LDAP server on the results of the operation.
+     * 
+     * @param entry
+     *            The entry to add
+     * @param msgId
+     *            message id number
+     */
+    private int addEntry( LdifEntry entry, int messageId ) throws IOException, DecoderException, InvalidNameException,
+        NamingException, EncoderException
+    {
+        AddRequest addRequest = new AddRequest();
+
+        String dn = entry.getDn();
+
+        if ( isDebugEnabled() )
+        {
+            System.out.println( "Adding entry " + dn );
+        }
+
+        Attributes attributes = entry.getAttributes();
+
+        addRequest.setEntry( new LdapDN( dn ) );
+
+        // Copy the attributes
+        addRequest.initAttributes();
+
+        for ( NamingEnumeration attrs = attributes.getAll(); attrs.hasMoreElements(); )
+        {
+            Attribute attribute = ( Attribute ) attrs.nextElement();
+
+            addRequest.addAttributeType( attribute.getID() );
+
+            for ( NamingEnumeration values = attribute.getAll(); values.hasMoreElements(); )
+            {
+                Object value = values.nextElement();
+                addRequest.addAttributeValue( value );
+            }
+        }
+
+        LdapMessage message = new LdapMessage();
+
+        message.setProtocolOP( addRequest );
+        message.setMessageId( messageId );
+
+        // Encode and send the addRequest message
+        ByteBuffer bb = message.encode( null );
+        bb.flip();
+
+        sendMessage( bb );
+
+        bb.clear();
+
+        // Get the response
+        LdapMessage response = readResponse( bb );
+
+        LdapResult result = response.getAddResponse().getLdapResult();
+
+        if ( result.getResultCode() == ResultCodeEnum.SUCCESS )
+        {
+            if ( isDebugEnabled() )
+            {
+                System.out.println( "Add of Entry " + entry.getDn() + " was successful" );
+            }
+
+            return IMPORT_SUCCESS;
+        }
+        else
+        {
+            System.err.println( "Add of entry " + entry.getDn()
+                + " failed for the following reasons provided by the server:\n" + result.getErrorMessage() );
+
+            return IMPORT_ERROR;
+        }
+    }
+
+
+    /**
+     * Send the entry to the encoder, then wait for a
+     * reponse from the LDAP server on the results of the operation.
+     * 
+     * @param entry
+     *            The entry to delete
+     * @param msgId
+     *            message id number
+     */
+    private int deleteEntry( LdifEntry entry, int messageId ) throws IOException, DecoderException,
+        InvalidNameException, NamingException, EncoderException
+    {
+        DelRequest delRequest = new DelRequest();
+
+        String dn = entry.getDn();
+
+        if ( isDebugEnabled() )
+        {
+            System.out.println( "Deleting entry " + dn );
+        }
+
+        delRequest.setEntry( new LdapDN( dn ) );
+
+        LdapMessage message = new LdapMessage();
+
+        message.setProtocolOP( delRequest );
+        message.setMessageId( messageId );
+
+        // Encode and send the delete request
+        ByteBuffer bb = message.encode( null );
+        bb.flip();
+
+        sendMessage( bb );
+
+        bb.clear();
+
+        // Get the response
+        LdapMessage response = readResponse( bb );
+
+        LdapResult result = response.getDelResponse().getLdapResult();
+
+        if ( result.getResultCode() == ResultCodeEnum.SUCCESS )
+        {
+            if ( isDebugEnabled() )
+            {
+                System.out.println( "Delete of Entry " + entry.getDn() + " was successful" );
+            }
+
+            return IMPORT_SUCCESS;
+        }
+        else
+        {
+            System.err.println( "Delete of entry " + entry.getDn()
+                + " failed for the following reasons provided by the server:\n" + result.getErrorMessage() );
+            return IMPORT_ERROR;
+        }
+    }
+
+
+    /**
+     * Send the entry to the encoder, then wait for a
+     * reponse from the LDAP server on the results of the operation.
+     * 
+     * @param entry
+     *            The entry to modify
+     * @param msgId
+     *            message id number
+     */
+    private int changeModRDNEntry( LdifEntry entry, int messageId ) throws IOException, DecoderException,
+        InvalidNameException, NamingException, EncoderException
+    {
+        ModifyDNRequest modifyDNRequest = new ModifyDNRequest();
+
+        String dn = entry.getDn();
+
+        if ( isDebugEnabled() )
+        {
+            System.out.println( "Modify DN of entry " + dn );
+        }
+
+        modifyDNRequest.setEntry( new LdapDN( dn ) );
+        modifyDNRequest.setDeleteOldRDN( entry.isDeleteOldRdn() );
+        modifyDNRequest.setNewRDN( new Rdn( entry.getNewRdn() ) );
+
+        if ( StringTools.isEmpty( entry.getNewSuperior() ) == false )
+        {
+            modifyDNRequest.setNewSuperior( new LdapDN( entry.getNewSuperior() ) );
+        }
+
+        LdapMessage message = new LdapMessage();
+
+        message.setProtocolOP( modifyDNRequest );
+        message.setMessageId( messageId );
+
+        // Encode and send the delete request
+        ByteBuffer bb = message.encode( null );
+        bb.flip();
+
+        sendMessage( bb );
+
+        bb.clear();
+
+        // Get the response
+        LdapMessage response = readResponse( bb );
+
+        LdapResult result = response.getModifyDNResponse().getLdapResult();
+
+        if ( result.getResultCode() == ResultCodeEnum.SUCCESS )
+        {
+            if ( isDebugEnabled() )
+            {
+                System.out.println( "ModifyDn of Entry " + entry.getDn() + " was successful" );
+            }
+
+            return IMPORT_SUCCESS;
+        }
+        else
+        {
+            System.err.println( "ModifyDn of entry " + entry.getDn()
+                + " failed for the following reasons provided by the server:\n" + result.getErrorMessage() );
+            return IMPORT_ERROR;
+        }
+    }
+
+
+    /**
+     * Send the entry to the encoder, then wait for a
+     * reponse from the LDAP server on the results of the operation.
+     * 
+     * @param entry
+     *            The entry to modify
+     * @param msgId
+     *            message id number
+     */
+    private int changeModifyEntry( LdifEntry entry, int messageId ) throws IOException, DecoderException,
+        InvalidNameException, NamingException, EncoderException
+    {
+        ModifyRequest modifyRequest = new ModifyRequest();
+
+        String dn = entry.getDn();
+
+        if ( isDebugEnabled() )
+        {
+            System.out.println( "Modify of entry " + dn );
+        }
+
+        modifyRequest.setObject( new LdapDN( dn ) );
+        modifyRequest.initModifications();
+
+        Iterator modifications = entry.getModificationItems().iterator();
+
+        while ( modifications.hasNext() )
+        {
+            ModificationItemImpl modification = ( ModificationItemImpl ) modifications.next();
+
+            switch ( modification.getModificationOp() )
+            {
+                case DirContext.ADD_ATTRIBUTE:
+                    modifyRequest.setCurrentOperation( LdapConstants.OPERATION_ADD );
+                    break;
+
+                case DirContext.REMOVE_ATTRIBUTE:
+                    modifyRequest.setCurrentOperation( LdapConstants.OPERATION_DELETE );
+                    break;
+
+                case DirContext.REPLACE_ATTRIBUTE:
+                    modifyRequest.setCurrentOperation( LdapConstants.OPERATION_REPLACE );
+                    break;
+
+                default:
+                    System.err.println( "Unknown modify operation for DN " + dn );
+            }
+
+            modifyRequest.addAttributeTypeAndValues( modification.getAttribute().getID() );
+
+            for ( NamingEnumeration values = modification.getAttribute().getAll(); values.hasMoreElements(); )
+            {
+                Object value = values.nextElement();
+                modifyRequest.addAttributeValue( value );
+            }
+        }
+
+        LdapMessage message = new LdapMessage();
+
+        message.setProtocolOP( modifyRequest );
+        message.setMessageId( messageId );
+
+        // Encode and send the delete request
+        ByteBuffer bb = message.encode( null );
+        bb.flip();
+
+        sendMessage( bb );
+
+        bb.clear();
+
+        // Get the response
+        LdapMessage response = readResponse( bb );
+
+        LdapResult result = response.getModifyResponse().getLdapResult();
+
+        if ( result.getResultCode() == ResultCodeEnum.SUCCESS )
+        {
+            if ( isDebugEnabled() )
+            {
+                System.out.println( "Modify of Entry " + entry.getDn() + " was successful" );
+            }
+
+            return IMPORT_SUCCESS;
+        }
+        else
+        {
+            System.err.println( "Modify of entry " + entry.getDn()
+                + " failed for the following reasons provided by the server:\n" + result.getErrorMessage() );
+            return IMPORT_ERROR;
+        }
+    }
+
+
+    /**
+     * Send the change operation to the encoder, then wait for a
+     * reponse from the LDAP server on the results of the operation.
+     * 
+     * @param entry
+     *            The entry to add
+     * @param msgId
+     *            message id number
+     */
+    private int changeEntry( LdifEntry entry, int messageId ) throws IOException, DecoderException,
+        InvalidNameException, NamingException, EncoderException
+    {
+        switch ( entry.getChangeType().getChangeType() )
+        {
+            case ChangeType.ADD_ORDINAL:
+                // No difference with the injection of new entries
+                return addEntry( entry, messageId );
+
+            case ChangeType.DELETE_ORDINAL:
+                return deleteEntry( entry, messageId );
+
+            case ChangeType.MODIFY_ORDINAL:
+                return changeModifyEntry( entry, messageId );
+
+            case ChangeType.MODDN_ORDINAL:
+            case ChangeType.MODRDN_ORDINAL:
+                return changeModRDNEntry( entry, messageId );
+
+            default:
+                return IMPORT_ERROR;
+        }
+    }
+
+
+    /**
+     * Bind to the ldap server
+     * 
+     * @param messageId The message Id
+     */
+    private void bind( int messageId ) throws NamingException, EncoderException, DecoderException, IOException
+    {
+        BindRequest bindRequest = new BindRequest();
+        LdapMessage message = new LdapMessage();
+        LdapAuthentication authentication = null;
+
+        if ( "simple".equals( auth ) )
+        {
+            authentication = new SimpleAuthentication();
+            ( ( SimpleAuthentication ) authentication ).setSimple( StringTools.getBytesUtf8( password ) );
+        }
+
+        bindRequest.setAuthentication( authentication );
+        bindRequest.setName( new LdapDN( user ) );
+        bindRequest.setVersion( 3 );
+
+        message.setProtocolOP( bindRequest );
+        message.setMessageId( messageId );
+
+        // Encode and send the bind request
+        ByteBuffer bb = message.encode( null );
+        bb.flip();
+
+        connect();
+        sendMessage( bb );
+
+        bb.clear();
+
+        // Get the bind response
+        LdapMessage response = readResponse( bb );
+
+        LdapResult result = response.getBindResponse().getLdapResult();
+
+        if ( result.getResultCode() == ResultCodeEnum.SUCCESS )
+        {
+            if ( isDebugEnabled() )
+            {
+                System.out.println( "Binding of user " + user + " was successful" );
+            }
+        }
+        else
+        {
+            System.err.println( "Binding of user " + user
+                + " failed for the following reasons provided by the server:\n" + result.getErrorMessage() );
+            System.exit( 1 );
+        }
+    }
+
+
+    /**
+     * Unbind from the server
+     * 
+     * @param messageId
+     *            The message Id
+     * @throws InvalidNameException
+     * @throws EncoderException
+     * @throws DecoderException
+     * @throws IOException
+     */
+    private void unbind( int messageId ) throws InvalidNameException, EncoderException, DecoderException, IOException
+    {
+        UnBindRequest unbindRequest = new UnBindRequest();
+        LdapMessage message = new LdapMessage();
+
+        message.setProtocolOP( unbindRequest );
+        message.setMessageId( messageId );
+        ByteBuffer bb = message.encode( null );
+        bb.flip();
+
+        sendMessage( bb );
+
+        if ( isDebugEnabled() )
+        {
+            System.out.println( "Unbinding of user " + user + " was successful" );
+        }
+    }
+
+
+    /**
+     * Execute the command
+     * 
+     * @param cmd
+     *            The command to be executed
+     */
+    public void execute( CommandLine cmd ) throws Exception
+    {
+        processOptions( cmd );
+
+        if ( isDebugEnabled() )
+        {
+            System.out.println( "Parameters for Ldif import request:" );
+            System.out.println( "port = " + port );
+            System.out.println( "host = " + host );
+            System.out.println( "user = " + user );
+            System.out.println( "auth type = " + auth );
+            System.out.println( "file = " + ldifFile );
+            System.out.println( "logs = " + logs );
+        }
+
+        int messageId = 0;
+
+        // Login to the server
+        bind( messageId++ );
+
+        if ( isDebugEnabled() )
+        {
+            System.out.println( "Connection to the server established.\n" + "Importing data ... " );
+        }
+
+        LdifReader ldifReader = new LdifReader( ldifFile );
+
+        if ( ldifReader.containsEntries() )
+        {
+            // Parse the file and inject every entry
+            long t0 = System.currentTimeMillis();
+            int nbAdd = 0;
+
+            for ( LdifEntry entry:ldifReader )
+            {
+                // Check if we have had some error, has next() does not throw any exception
+                if ( ldifReader.hasError() )
+                {
+                    System.err
+                        .println( "Found an error while persing an entry : " + ldifReader.getError().getMessage() );
+
+                    if ( ignoreErrors == false )
+                    {
+                        unbind( messageId );
+
+                        System.err.println( "Import failed..." );
+                        System.exit( 1 );
+                    }
+                }
+
+                if ( ( addEntry( entry, messageId++ ) == IMPORT_ERROR ) && ( ignoreErrors == false ) )
+                {
+                    unbind( messageId );
+
+                    System.err.println( "Import failed..." );
+                    System.exit( 1 );
+                }
+
+                nbAdd++;
+
+                if ( nbAdd % 10 == 0 )
+                {
+                    System.out.print( '.' );
+                }
+
+                if ( nbAdd % 500 == 0 )
+                {
+                    System.out.println( nbAdd );
+                }
+            }
+
+            long t1 = System.currentTimeMillis();
+
+            System.out.println( "Done!" );
+            System.out.println( nbAdd + " users added in " + ( ( t1 - t0 ) / 1000 ) + " seconds" );
+        }
+        else
+        {
+            // Parse the file and inject every modification
+            long t0 = System.currentTimeMillis();
+            int nbMod = 0;
+
+            for ( LdifEntry entry:ldifReader )
+            {
+                // Check if we have had some error, has next() does not throw any exception
+                if ( ldifReader.hasError() )
+                {
+                    System.err
+                        .println( "Found an error while persing an entry : " + ldifReader.getError().getMessage() );
+
+                    if ( ignoreErrors == false )
+                    {
+                        unbind( messageId );
+
+                        System.err.println( "Import failed..." );
+                        System.exit( 1 );
+                    }
+                }
+
+                if ( ( changeEntry( entry, messageId++ ) == IMPORT_ERROR ) && ( ignoreErrors == false ) )
+                {
+                    unbind( messageId );
+
+                    System.err.println( "Import failed..." );
+                    System.exit( 1 );
+                }
+
+                nbMod++;
+
+                if ( nbMod % 10 == 0 )
+                {
+                    System.out.print( '.' );
+                }
+
+                if ( nbMod % 500 == 0 )
+                {
+                    System.out.println( nbMod );
+                }
+            }
+
+            long t1 = System.currentTimeMillis();
+
+            System.out.println( "Done!" );
+            System.out.println( nbMod + " users changed in " + ( ( t1 - t0 ) / 1000 ) + " seconds" );
+        }
+
+        // Logout to the server
+        unbind( messageId++ );
+
+    }
+
+
+    /**
+     * Read the command line and get the options : 'h' : host 'p' : port 'u' :
+     * user 'w' : password 'a' : authentication type 'i' : ignore errors 'f' :
+     * ldif file to import
+     * 
+     * @param cmd
+     *            The command line
+     */
+    private void processOptions( CommandLine cmd )
+    {
+        if ( isDebugEnabled() )
+        {
+            System.out.println( "Processing options for launching diagnostic UI ..." );
+        }
+
+        // -------------------------------------------------------------------
+        // figure out the host value
+        // -------------------------------------------------------------------
+
+        if ( cmd.hasOption( 'h' ) )
+        {
+            host = cmd.getOptionValue( 'h' );
+
+            if ( isDebugEnabled() )
+            {
+                System.out.println( "ignore-errors overriden by -i option: true" );
+            }
+        }
+        else if ( isDebugEnabled() )
+        {
+            System.out.println( "ignore-errors set to default: false" );
+        }
+
+        // -------------------------------------------------------------------
+        // figure out and error check the port value
+        // -------------------------------------------------------------------
+
+        if ( cmd.hasOption( 'p' ) ) // - user provided port w/ -p takes
+        // precedence
+        {
+            String val = cmd.getOptionValue( 'p' );
+
+            try
+            {
+                port = Integer.parseInt( val );
+            }
+            catch ( NumberFormatException e )
+            {
+                System.err.println( "port value of '" + val + "' is not a number" );
+                System.exit( 1 );
+            }
+
+            if ( port > AvailablePortFinder.MAX_PORT_NUMBER )
+            {
+                System.err.println( "port value of '" + val + "' is larger than max port number: "
+                    + AvailablePortFinder.MAX_PORT_NUMBER );
+                System.exit( 1 );
+            }
+            else if ( port < AvailablePortFinder.MIN_PORT_NUMBER )
+            {
+                System.err.println( "port value of '" + val + "' is smaller than the minimum port number: "
+                    + AvailablePortFinder.MIN_PORT_NUMBER );
+                System.exit( 1 );
+            }
+
+            if ( isDebugEnabled() )
+            {
+                System.out.println( "port overriden by -p option: " + port );
+            }
+        }
+        else if ( getApacheDS() != null )
+        {
+            port = getApacheDS().getLdapServer().getIpPort();
+
+            if ( isDebugEnabled() )
+            {
+                System.out.println( "port overriden by server.xml configuration: " + port );
+            }
+        }
+        else if ( isDebugEnabled() )
+        {
+            System.out.println( "port set to default: " + port );
+        }
+
+        // -------------------------------------------------------------------
+        // figure out the user value
+        // -------------------------------------------------------------------
+
+        if ( cmd.hasOption( 'u' ) )
+        {
+            user = cmd.getOptionValue( 'u' );
+
+            if ( isDebugEnabled() )
+            {
+                System.out.println( "user overriden by -u option: " + user );
+            }
+        }
+        else if ( isDebugEnabled() )
+        {
+            System.out.println( "user set to default: " + user );
+        }
+
+        // -------------------------------------------------------------------
+        // figure out the password value
+        // -------------------------------------------------------------------
+
+        if ( cmd.hasOption( 'w' ) )
+        {
+            password = cmd.getOptionValue( 'w' );
+
+            if ( isDebugEnabled() )
+            {
+                System.out.println( "password overriden by -w option: " + password );
+            }
+        }
+        else if ( isDebugEnabled() )
+        {
+            System.out.println( "password set to default: " + password );
+        }
+
+        // -------------------------------------------------------------------
+        // figure out the authentication type
+        // -------------------------------------------------------------------
+
+        if ( cmd.hasOption( 'a' ) )
+        {
+            auth = cmd.getOptionValue( 'a' );
+
+            if ( isDebugEnabled() )
+            {
+                System.out.println( "authentication type overriden by -a option: " + auth );
+            }
+        }
+        else if ( isDebugEnabled() )
+        {
+            System.out.println( "authentication type set to default: " + auth );
+        }
+
+        // -------------------------------------------------------------------
+        // figure out the 'ignore-errors' flag
+        // -------------------------------------------------------------------
+
+        if ( cmd.hasOption( 'e' ) )
+        {
+            ignoreErrors = true;
+
+            if ( isDebugEnabled() )
+            {
+                System.out.println( "authentication type overriden by -a option: " + auth );
+            }
+        }
+        else if ( isDebugEnabled() )
+        {
+            System.out.println( "authentication type set to default: " + auth );
+        }
+
+        // -------------------------------------------------------------------
+        // figure out the ldif file to import
+        // -------------------------------------------------------------------
+
+        if ( cmd.hasOption( 'f' ) )
+        {
+            String ldifFileName = cmd.getOptionValue( 'f' );
+
+            ldifFile = new File( ldifFileName );
+
+            if ( ldifFile.exists() == false )
+            {
+                System.err.println( "ldif file '" + ldifFileName + "' does not exist" );
+                System.exit( 1 );
+            }
+
+            if ( ldifFile.canRead() == false )
+            {
+                System.err.println( "ldif file '" + ldifFileName + "' can't be read" );
+                System.exit( 1 );
+            }
+
+            if ( isDebugEnabled() )
+            {
+                try
+                {
+                    System.out.println( "ldif file to import: " + ldifFile.getCanonicalPath() );
+                }
+                catch ( IOException ioe )
+                {
+                    System.out.println( "ldif file to import: " + ldifFileName );
+                }
+            }
+        }
+        else
+        {
+            System.err.println( "ldif file name must be provided" );
+            System.exit( 1 );
+        }
+    }
+
+
+    public Options getOptions()
+    {
+        Options opts = new Options();
+        Option op = new Option( "h", "host", true, "server host: defaults to localhost" );
+        op.setRequired( false );
+        opts.addOption( op );
+        op = new Option( "p", "port", true, "server port: defaults to 10389 or server.xml specified port" );
+        op.setRequired( false );
+        opts.addOption( op );
+        op = new Option( "u", "user", true, "the user: default to uid=admin, ou=system" );
+        op.setRequired( false );
+        opts.addOption( op );
+        op = new Option( "w", "password", true, "the apacheds administrator's password: defaults to secret" );
+        op.setRequired( false );
+        opts.addOption( op );
+        op = new Option( "a", "auth", true, "the authentication mode: defaults to 'simple'" );
+        op.setRequired( false );
+        opts.addOption( op );
+        op = new Option( "f", "file", true, "the ldif file to import" );
+        op.setRequired( true );
+        opts.addOption( op );
+        op = new Option( "e", "ignore", false, "continue to process the file even if errors are encountered " );
+        op.setRequired( false );
+        opts.addOption( op );
+
+        return opts;
+    }
+}
diff --git a/old_trunk/server-tools/src/main/java/org/apache/directory/server/tools/IndexCommand.java b/old_trunk/server-tools/src/main/java/org/apache/directory/server/tools/IndexCommand.java
new file mode 100644
index 0000000..4f97177
--- /dev/null
+++ b/old_trunk/server-tools/src/main/java/org/apache/directory/server/tools/IndexCommand.java
@@ -0,0 +1,258 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.tools;
+
+
+import java.io.File;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+
+import jdbm.helper.MRU;
+import jdbm.recman.BaseRecordManager;
+import jdbm.recman.CacheRecordManager;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.Options;
+import org.apache.directory.server.constants.ServerDNConstants;
+import org.apache.directory.server.core.DefaultDirectoryService;
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.entry.DefaultServerEntry;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.partition.impl.btree.Index;
+import org.apache.directory.server.core.partition.impl.btree.Tuple;
+import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmIndex;
+import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmMasterTable;
+import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmPartition;
+import org.apache.directory.server.core.schema.PartitionSchemaLoader;
+import org.apache.directory.server.schema.SerializableComparator;
+import org.apache.directory.server.schema.bootstrap.ApacheSchema;
+import org.apache.directory.server.schema.bootstrap.ApachemetaSchema;
+import org.apache.directory.server.schema.bootstrap.BootstrapSchemaLoader;
+import org.apache.directory.server.schema.bootstrap.CoreSchema;
+import org.apache.directory.server.schema.bootstrap.Schema;
+import org.apache.directory.server.schema.bootstrap.SystemSchema;
+import org.apache.directory.server.schema.bootstrap.partition.DbFileListing;
+import org.apache.directory.server.schema.registries.DefaultOidRegistry;
+import org.apache.directory.server.schema.registries.DefaultRegistries;
+import org.apache.directory.server.schema.registries.OidRegistry;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.exception.LdapConfigurationException;
+import org.apache.directory.shared.ldap.exception.LdapNamingException;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.util.AttributeUtils;
+
+
+/**
+ * Simple tool used to dump the contents of a jdbm based partition.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 442600 $
+ */
+public class IndexCommand extends ToolCommand
+{
+    private Registries bootstrapRegistries = new DefaultRegistries( "bootstrap", new BootstrapSchemaLoader(),
+        new DefaultOidRegistry() );
+
+
+    public IndexCommand()
+    {
+        super( "index" );
+    }
+
+
+    private Registries loadRegistries() throws Exception
+    {
+        // --------------------------------------------------------------------
+        // Load the bootstrap schemas to start up the schema partition
+        // --------------------------------------------------------------------
+
+        // setup temporary loader and temp registry 
+        BootstrapSchemaLoader loader = new BootstrapSchemaLoader();
+        OidRegistry oidRegistry = new DefaultOidRegistry();
+        final Registries registries = new DefaultRegistries( "bootstrap", loader, oidRegistry );
+
+        // load essential bootstrap schemas 
+        Set<Schema> bootstrapSchemas = new HashSet<Schema>();
+        bootstrapSchemas.add( new ApachemetaSchema() );
+        bootstrapSchemas.add( new ApacheSchema() );
+        bootstrapSchemas.add( new CoreSchema() );
+        bootstrapSchemas.add( new SystemSchema() );
+        loader.loadWithDependencies( bootstrapSchemas, registries );
+
+        // run referential integrity tests
+        List<Throwable> errors = registries.checkRefInteg();
+
+        if ( !errors.isEmpty() )
+        {
+            NamingException e = new NamingException();
+            e.setRootCause( ( Throwable ) errors.get( 0 ) );
+            throw e;
+        }
+
+        SerializableComparator.setRegistry( registries.getComparatorRegistry() );
+
+        // --------------------------------------------------------------------
+        // Initialize schema partition or bomb out if we cannot find it on disk
+        // --------------------------------------------------------------------
+
+        // If not present then we need to abort 
+        File schemaDirectory = new File( getLayout().getPartitionsDirectory(), "schema" );
+        if ( !schemaDirectory.exists() )
+        {
+            throw new LdapConfigurationException( "The following schema directory from "
+                + "the installation layout could not be found:\n\t" + schemaDirectory );
+        }
+
+        JdbmPartition schemaPartition = new JdbmPartition();
+        schemaPartition.setId( "schema" );
+        schemaPartition.setCacheSize( 1000 );
+
+        DbFileListing listing;
+        try
+        {
+            listing = new DbFileListing();
+        }
+        catch ( IOException e )
+        {
+            throw new LdapNamingException( "Got IOException while trying to read DBFileListing: " + e.getMessage(),
+                ResultCodeEnum.OTHER );
+        }
+
+        Set<Index> indexedAttributes = new HashSet<Index>();
+
+        for ( String attributeId : listing.getIndexedAttributes() )
+        {
+            indexedAttributes.add( new JdbmIndex( attributeId ) );
+        }
+
+        schemaPartition.setIndexedAttributes( indexedAttributes );
+        schemaPartition.setSuffix( ServerDNConstants.OU_SCHEMA_DN );
+
+        ServerEntry systemEntry = new DefaultServerEntry( registries, new LdapDN( "ou=schema" ) );
+        systemEntry.put( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC,
+            SchemaConstants.ORGANIZATIONAL_UNIT_OC );
+        systemEntry.put( SchemaConstants.OU_AT, "schema" );
+        schemaPartition.setContextEntry( systemEntry );
+
+        DirectoryService directoryService = new DefaultDirectoryService();
+        schemaPartition.init( directoryService );
+
+        // --------------------------------------------------------------------
+        // Initialize schema subsystem and reset registries
+        // --------------------------------------------------------------------
+
+        PartitionSchemaLoader schemaLoader = new PartitionSchemaLoader( schemaPartition, registries );
+        Registries globalRegistries = new DefaultRegistries( "global", schemaLoader, oidRegistry );
+        schemaLoader.loadEnabled( globalRegistries );
+        SerializableComparator.setRegistry( globalRegistries.getComparatorRegistry() );
+        return globalRegistries;
+    }
+
+
+    public void execute( CommandLine cmdline ) throws Exception
+    {
+        getLayout().verifyInstallation();
+        bootstrapRegistries = loadRegistries();
+
+        String[] partitions = cmdline.getOptionValues( 'p' );
+        String attribute = cmdline.getOptionValue( 'a' );
+
+        for ( int ii = 0; ii < partitions.length; ii++ )
+        {
+            File partitionDirectory = new File( getLayout().getPartitionsDirectory(), partitions[ii] );
+            buildIndex( partitionDirectory, bootstrapRegistries.getAttributeTypeRegistry().lookup( attribute ) );
+        }
+    }
+
+
+    private void buildIndex( File partitionDirectory, AttributeType attributeType ) throws Exception
+    {
+        if ( !partitionDirectory.exists() )
+        {
+            System.err.println( "Partition directory " + partitionDirectory + " does not exist!" );
+            System.exit( 1 );
+        }
+
+        String path = partitionDirectory.getPath() + File.separator + "master";
+        BaseRecordManager base = new BaseRecordManager( path );
+        base.disableTransactions();
+        CacheRecordManager recMan = new CacheRecordManager( base, new MRU( 1000 ) );
+
+        JdbmMasterTable master = new JdbmMasterTable( recMan, bootstrapRegistries );
+        JdbmIndex index = new JdbmIndex();
+        index.setAttributeId( attributeType.getName() );
+        index.setWkDirPath( partitionDirectory );
+        index.setCacheSize( 1000 );
+        index.setNumDupLimit( 1000 );
+        index.init( attributeType, partitionDirectory );
+
+        NamingEnumeration list = master.listTuples();
+        while ( list.hasMore() )
+        {
+            Tuple tuple = ( Tuple ) list.next();
+            BigInteger id = ( BigInteger ) tuple.getKey();
+            Attributes entry = ( Attributes ) tuple.getValue();
+
+            Attribute attr = AttributeUtils.getAttribute( entry, attributeType );
+            if ( attr == null )
+            {
+                continue;
+            }
+
+            for ( int ii = 0; ii < attr.size(); ii++ )
+            {
+                index.add( attr.get( ii ), id );
+            }
+        }
+
+        index.sync();
+    }
+
+
+    public Options getOptions()
+    {
+        Options opts = new Options();
+        Option op = null;
+        op = new Option( "p", "partitions", true, "the partitions to add the attribute indices to" );
+        op.setRequired( true );
+        op.setValueSeparator( File.pathSeparatorChar );
+        opts.addOption( op );
+        op = new Option( "a", "attributes", true, "the attribute to index" );
+        op.setRequired( true );
+        op.setValueSeparator( File.pathSeparatorChar );
+        opts.addOption( op );
+        op = new Option( "i", "install-path", true, "path to apacheds installation directory" );
+        op.setRequired( true );
+        opts.addOption( op );
+        return opts;
+    }
+}
diff --git a/old_trunk/server-tools/src/main/java/org/apache/directory/server/tools/ToolCommand.java b/old_trunk/server-tools/src/main/java/org/apache/directory/server/tools/ToolCommand.java
new file mode 100644
index 0000000..ad46cf1
--- /dev/null
+++ b/old_trunk/server-tools/src/main/java/org/apache/directory/server/tools/ToolCommand.java
@@ -0,0 +1,154 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.tools;
+
+
+import java.io.File;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.Options;
+import org.apache.directory.daemon.InstallationLayout;
+import org.apache.directory.server.configuration.ApacheDS;
+
+
+/**
+ * Simple base class for tool commands.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public abstract class ToolCommand
+{
+    private final String name;
+    private boolean debugEnabled = false;
+    private boolean verboseEnabled = false;
+    private boolean quietEnabled = false;
+    private String version;
+    private InstallationLayout layout;
+    private ApacheDS apacheDS;
+
+
+    protected ToolCommand( String name )
+    {
+        this.name = name;
+    }
+
+
+    public abstract void execute( CommandLine cmd ) throws Exception;
+
+
+    public abstract Options getOptions();
+
+
+    public String getName()
+    {
+        return this.name;
+    }
+
+
+    public void setLayout( File installationDirectory )
+    {
+        this.layout = new InstallationLayout( installationDirectory );
+    }
+
+
+    public void setLayout( String installationPath )
+    {
+        this.layout = new InstallationLayout( installationPath );
+    }
+
+
+    public void setLayout( InstallationLayout layout )
+    {
+        this.layout = layout;
+    }
+
+
+    public InstallationLayout getLayout()
+    {
+        return layout;
+    }
+
+
+    public void setApacheDS( ApacheDS apacheDS )
+    {
+        this.apacheDS = apacheDS;
+    }
+
+
+    public ApacheDS getApacheDS()
+    {
+        return apacheDS;
+    }
+
+
+    public void setVersion( String version )
+    {
+        this.version = version;
+    }
+
+
+    public String getVersion()
+    {
+        return version;
+    }
+
+
+    public String toString()
+    {
+        return getName();
+    }
+
+
+    public void setDebugEnabled( boolean debugEnabled )
+    {
+        this.debugEnabled = debugEnabled;
+    }
+
+
+    public boolean isDebugEnabled()
+    {
+        return debugEnabled;
+    }
+
+
+    public void setVerboseEnabled( boolean verboseEnabled )
+    {
+        this.verboseEnabled = verboseEnabled;
+    }
+
+
+    public boolean isVerboseEnabled()
+    {
+        return verboseEnabled;
+    }
+
+
+    public void setQuietEnabled( boolean quietEnabled )
+    {
+        this.quietEnabled = quietEnabled;
+    }
+
+
+    public boolean isQuietEnabled()
+    {
+        return quietEnabled;
+    }
+}
diff --git a/old_trunk/server-tools/src/main/manifest/MANIFEST.MF b/old_trunk/server-tools/src/main/manifest/MANIFEST.MF
new file mode 100644
index 0000000..226f389
--- /dev/null
+++ b/old_trunk/server-tools/src/main/manifest/MANIFEST.MF
@@ -0,0 +1,48 @@
+Manifest-Version: 1.0
+Main-Class: org.apache.directory.server.tools.ApachedsTools
+Class-Path: ../lib/logger.jar ../lib/daemon.jar ../lib/bootstrapper.jar 
+ ../lib/antlr-2.7.7.jar 
+ ../lib/apacheds-kerberos-shared-1.5.3.jar 
+ ../lib/apacheds-interceptor-kerberos-1.5.3.jar 
+ ../lib/apacheds-protocol-changepw-1.5.3.jar 
+ ../lib/apacheds-protocol-shared-1.5.3.jar 
+ ../lib/apacheds-protocol-kerberos-1.5.3.jar 
+ ../lib/apacheds-protocol-ldap-1.5.3.jar 
+ ../lib/apacheds-protocol-ntp-1.5.3.jar 
+ ../lib/apacheds-protocol-dns-1.5.3.jar 
+ ../lib/apacheds-protocol-dhcp-1.5.3.jar 
+ ../lib/apacheds-core-1.5.3.jar 
+ ../lib/apacheds-core-constants-1.5.3.jar 
+ ../lib/apacheds-core-entry-1.5.3.jar 
+ ../lib/apacheds-core-shared-1.5.3.jar 
+ ../lib/apacheds-jdbm-1.5.3.jar 
+ ../lib/apacheds-jdbm-store-1.5.3.jar 
+ ../lib/apacheds-btree-base-1.5.3.jar 
+ ../lib/apacheds-server-jndi-1.5.3.jar 
+ ../lib/apacheds-server-replication-1.5.3.jar 
+ ../lib/apacheds-utils-1.5.3.jar 
+ ../lib/apacheds-xbean-spring-1.5.3.jar 
+ ../lib/apacheds-schema-registries-1.5.3.jar 
+ ../lib/apacheds-schema-bootstrap-1.5.3.jar 
+ ../lib/apacheds-schema-extras-1.5.3.jar 
+ ../lib/apacheds-bootstrap-partition-1.5.3.jar 
+ ../lib/apacheds-bootstrap-extract-1.5.3.jar 
+ ../lib/mitosis-1.5.3.jar 
+ ../lib/shared-asn1-codec-0.9.11.jar 
+ ../lib/shared-asn1-0.9.11.jar 
+ ../lib/shared-ldap-0.9.11.jar 
+ ../lib/mina-core-1.1.6.jar 
+ ../lib/mina-filter-ssl-1.1.6.jar 
+ ../lib/commons-collections-3.2.jar 
+ ../lib/commons-lang-2.3.jar 
+ ../lib/commons-cli-1.1.jar 
+ ../lib/xbean-spring-3.3.jar 
+ ../lib/spring-beans-2.0.6.jar 
+ ../lib/spring-context-2.0.6.jar 
+ ../lib/spring-core-2.0.6.jar 
+ ../lib/jcl104-over-slf4j-1.4.3.jar 
+ ../lib/slf4j-api-1.4.3.jar 
+ ../lib/slf4j-log4j12-1.4.3.jar 
+ ../lib/log4j-1.2.14.jar 
+Specification-Title: apacheds-tools
+Specification-Version: 1.0
diff --git a/old_trunk/server-tools/src/main/resources/org/apache/directory/server/tools/product.properties b/old_trunk/server-tools/src/main/resources/org/apache/directory/server/tools/product.properties
new file mode 100644
index 0000000..0fd8106
--- /dev/null
+++ b/old_trunk/server-tools/src/main/resources/org/apache/directory/server/tools/product.properties
@@ -0,0 +1,18 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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.  
+
+product.version=${pom.version}
diff --git a/old_trunk/server-tools/src/site/site.xml b/old_trunk/server-tools/src/site/site.xml
new file mode 100644
index 0000000..9ec7c16
--- /dev/null
+++ b/old_trunk/server-tools/src/site/site.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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.
+-->
+<!--
+  @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+-->
+<project name="${project.name}">
+    <bannerLeft>
+        <name>${project.name}</name>
+    </bannerLeft>
+    <publishDate position="navigation-bottom" format="dd-MM-yyyy HH:mm" />
+    <body>
+        <menu name="Parent">
+            <item name="Apache Directory ApacheDS" href="../index.html" />
+        </menu>
+        <menu ref="reports" />
+    </body>
+</project>
diff --git a/old_trunk/server-tools/src/test/resources/org/apache/directory/server/tools/commands/exportcmd/10_entries.ldif b/old_trunk/server-tools/src/test/resources/org/apache/directory/server/tools/commands/exportcmd/10_entries.ldif
new file mode 100644
index 0000000..b586095
--- /dev/null
+++ b/old_trunk/server-tools/src/test/resources/org/apache/directory/server/tools/commands/exportcmd/10_entries.ldif
@@ -0,0 +1,90 @@
+#
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you 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.
+#
+#   EXAMPLE.COM is reserved for testing according to this RFC:
+#
+#   http://www.rfc-editor.org/rfc/rfc2606.txt
+#
+
+dn: o=neworganization, dc=example,dc=com
+objectclass: organization
+objectclass: top
+o: neworganization
+
+dn: cn=newperson1, o=neworganization, dc=example,dc=com
+cn: newperson1
+objectclass: top
+objectclass: person
+telephoneNumber: 0101010101
+sn: aValue
+
+dn: cn=newperson2, o=neworganization, dc=example,dc=com
+cn: newperson2
+objectclass: top
+objectclass: person
+telephoneNumber: 0202020202
+sn: aValue
+
+dn: cn=newperson3, o=neworganization, dc=example,dc=com
+cn: newperson3
+objectclass: top
+objectclass: person
+telephoneNumber: 0303030303
+sn: aValue
+
+dn: cn=newperson4, o=neworganization, dc=example,dc=com
+cn: newperson4
+objectclass: top
+objectclass: person
+telephoneNumber: 0404040404
+sn: aValue
+
+dn: cn=newperson5, o=neworganization, dc=example,dc=com
+cn: newperson5
+objectclass: top
+objectclass: person
+telephoneNumber: 0505050505
+sn: aValue
+
+dn: cn=newperson6, o=neworganization, dc=example,dc=com
+cn: newperson6
+objectclass: top
+objectclass: person
+telephoneNumber: 0606060606
+sn: aValue
+
+dn: cn=newperson7, o=neworganization, dc=example,dc=com
+cn: newperson7
+objectclass: top
+objectclass: person
+telephoneNumber: 0707070707
+sn: aValue
+
+dn: cn=newperson8, o=neworganization, dc=example,dc=com
+cn: newperson8
+objectclass: top
+objectclass: person
+telephoneNumber: 0808080808
+sn: aValue
+
+dn: cn=newperson9, o=neworganization, dc=example,dc=com
+cn: newperson9
+objectclass: top
+objectclass: person
+telephoneNumber: 0909090909
+sn: aValue
diff --git a/old_trunk/server-tools/src/test/resources/org/apache/directory/server/tools/commands/importcmd/RFC2849Sample1.ldif b/old_trunk/server-tools/src/test/resources/org/apache/directory/server/tools/commands/importcmd/RFC2849Sample1.ldif
new file mode 100644
index 0000000..67b2031
--- /dev/null
+++ b/old_trunk/server-tools/src/test/resources/org/apache/directory/server/tools/commands/importcmd/RFC2849Sample1.ldif
@@ -0,0 +1,50 @@
+#
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you 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.
+#
+#   EXAMPLE.COM is reserved for testing according to this RFC:
+#
+#   http://www.rfc-editor.org/rfc/rfc2606.txt
+#
+
+dn: ou=Product Development, dc=example, dc=com
+ou: Product Development
+objectclass: top
+
+dn: ou=Accounting, dc=example, dc=com 
+ou: Accounting
+objectclass: top
+
+dn: cn=Barbara Jensen, ou=Product Development, dc=example, dc=com
+objectclass: top
+objectclass: person
+objectclass: organizationalPerson
+cn: Barbara Jensen
+cn: Barbara J Jensen
+cn: Babs Jensen 
+sn: Jensen
+uid: bjensen
+telephonenumber: +1 408 555 1212
+description: A big sailing fan.
+
+dn: cn=Bjorn Jensen, ou=Accounting, dc=example, dc=com 
+objectclass: top
+objectclass: person
+objectclass: organizationalPerson
+cn: Bjorn Jensen 
+sn: Jensen
+telephonenumber: +1 408 555 1212
diff --git a/old_trunk/server-tools/src/test/resources/org/apache/directory/server/tools/commands/importcmd/RFC2849Sample2.ldif b/old_trunk/server-tools/src/test/resources/org/apache/directory/server/tools/commands/importcmd/RFC2849Sample2.ldif
new file mode 100644
index 0000000..6c49521
--- /dev/null
+++ b/old_trunk/server-tools/src/test/resources/org/apache/directory/server/tools/commands/importcmd/RFC2849Sample2.ldif
@@ -0,0 +1,44 @@
+#
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you 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.
+#
+#   EXAMPLE.COM is reserved for testing according to this RFC:
+#
+#   http://www.rfc-editor.org/rfc/rfc2606.txt
+#
+
+dn: ou=Product Development, dc=example, dc=com
+ou: Product Development
+objectclass: top
+
+dn: ou=Accounting, dc=example, dc=com 
+ou: Accounting
+objectclass: top
+
+dn: cn=Barbara Jensen, ou=Product Development, dc=example, dc=com
+objectclass: top
+objectclass: person
+objectclass: organizationalPerson
+cn: Barbara Jensen
+cn: Barbara J Jensen
+cn: Babs Jensen
+sn: Jensen
+uid: bjensen
+telephonenumber: +1 408 555 1212
+description:Babs is a big sailing fan, and travels extensively in sea
+ rch of perfect sailing conditions.
+title:Product Manager, Rod and Reel Division
diff --git a/old_trunk/server-tools/src/test/resources/org/apache/directory/server/tools/commands/importcmd/RFC2849Sample3.ldif b/old_trunk/server-tools/src/test/resources/org/apache/directory/server/tools/commands/importcmd/RFC2849Sample3.ldif
new file mode 100644
index 0000000..97d4ea1
--- /dev/null
+++ b/old_trunk/server-tools/src/test/resources/org/apache/directory/server/tools/commands/importcmd/RFC2849Sample3.ldif
@@ -0,0 +1,40 @@
+#
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you 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.
+#
+#   EXAMPLE.COM is reserved for testing according to this RFC:
+#
+#   http://www.rfc-editor.org/rfc/rfc2606.txt
+#
+
+dn: ou=Product Testing, dc=example, dc=com
+ou: Product Development
+objectclass: top
+
+dn: cn=Gern Jensen, ou=Product Testing, dc=example, dc=com
+objectclass: top
+objectclass: person
+objectclass: organizationalPerson
+cn: Gern Jensen
+cn: Gern O Jensen
+sn: Jensen
+uid: gernj
+telephonenumber: +1 408 555 1212
+description:: V2hhdCBhIGNhcmVmdWwgcmVhZGVyIHlvdSBhcmUhICBUaGlzIHZhbHVl
+ IGlzIGJhc2UtNjQtZW5jb2RlZCBiZWNhdXNlIGl0IGhhcyBhIGNvbnRyb2wgY2hhcmFjdG
+ VyIGluIGl0IChhIENSKS4NICBCeSB0aGUgd2F5LCB5b3Ugc2hvdWxkIHJlYWxseSBnZXQg
+ b3V0IG1vcmUu
diff --git a/old_trunk/server-tools/src/test/resources/org/apache/directory/server/tools/commands/importcmd/RFC2849Sample3VariousSpacing.ldif b/old_trunk/server-tools/src/test/resources/org/apache/directory/server/tools/commands/importcmd/RFC2849Sample3VariousSpacing.ldif
new file mode 100644
index 0000000..99934e4
--- /dev/null
+++ b/old_trunk/server-tools/src/test/resources/org/apache/directory/server/tools/commands/importcmd/RFC2849Sample3VariousSpacing.ldif
@@ -0,0 +1,40 @@
+#
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you 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.
+#
+#   EXAMPLE.COM is reserved for testing according to this RFC:
+#
+#   http://www.rfc-editor.org/rfc/rfc2606.txt
+#
+
+dn: ou=Product Testing, dc=example, dc=com
+ou: Product Development
+objectclass: top
+
+dn:cn=Gern Jensen, ou=Product Testing, dc=example, dc=com  
+objectclass:top
+objectclass:   person   
+objectclass:organizationalPerson
+cn:Gern Jensen
+cn:Gern O Jensen
+sn:Jensen
+uid:gernj
+telephonenumber:+1 408 555 1212  
+description::  V2hhdCBhIGNhcmVmdWwgcmVhZGVyIHlvdSBhcmUhICBUaGlzIHZhbHVl
+ IGlzIGJhc2UtNjQtZW5jb2RlZCBiZWNhdXNlIGl0IGhhcyBhIGNvbnRyb2wgY2hhcmFjdG
+ VyIGluIGl0IChhIENSKS4NICBCeSB0aGUgd2F5LCB5b3Ugc2hvdWxkIHJlYWxseSBnZXQg
+ b3V0IG1vcmUu  
diff --git a/old_trunk/server-tools/src/test/resources/org/apache/directory/server/tools/commands/importcmd/import_10_entries.ldif b/old_trunk/server-tools/src/test/resources/org/apache/directory/server/tools/commands/importcmd/import_10_entries.ldif
new file mode 100644
index 0000000..475ae9e
--- /dev/null
+++ b/old_trunk/server-tools/src/test/resources/org/apache/directory/server/tools/commands/importcmd/import_10_entries.ldif
@@ -0,0 +1,82 @@
+#
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you 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.
+#
+#   EXAMPLE.COM is reserved for testing according to this RFC:
+#
+#   http://www.rfc-editor.org/rfc/rfc2606.txt
+#
+
+dn: o=neworganization, dc=example,dc=com
+objectclass: organization
+objectclass: top
+o: neworganization
+
+dn: cn=newperson1, o=neworganization, dc=example,dc=com
+cn: newperson1
+objectclass: top
+objectclass: person
+sn: aValue
+
+dn: cn=newperson2, o=neworganization, dc=example,dc=com
+cn: newperson2
+objectclass: top
+objectclass: person
+sn: aValue
+
+dn: cn=newperson3, o=neworganization, dc=example,dc=com
+cn: newperson3
+objectclass: top
+objectclass: person
+sn: aValue
+
+dn: cn=newperson4, o=neworganization, dc=example,dc=com
+cn: newperson4
+objectclass: top
+objectclass: person
+sn: aValue
+
+dn: cn=newperson5, o=neworganization, dc=example,dc=com
+cn: newperson5
+objectclass: top
+objectclass: person
+sn: aValue
+
+dn: cn=newperson6, o=neworganization, dc=example,dc=com
+cn: newperson6
+objectclass: top
+objectclass: person
+sn: aValue
+
+dn: cn=newperson7, o=neworganization, dc=example,dc=com
+cn: newperson7
+objectclass: top
+objectclass: person
+sn: aValue
+
+dn: cn=newperson8, o=neworganization, dc=example,dc=com
+cn: newperson8
+objectclass: top
+objectclass: person
+sn: aValue
+
+dn: cn=newperson9, o=neworganization, dc=example,dc=com
+cn: newperson9
+objectclass: top
+objectclass: person
+sn: aValue
+
diff --git a/old_trunk/server-tools/src/test/resources/org/apache/directory/server/tools/commands/importcmd/import_1_entry.ldif b/old_trunk/server-tools/src/test/resources/org/apache/directory/server/tools/commands/importcmd/import_1_entry.ldif
new file mode 100644
index 0000000..1b9b5ca
--- /dev/null
+++ b/old_trunk/server-tools/src/test/resources/org/apache/directory/server/tools/commands/importcmd/import_1_entry.ldif
@@ -0,0 +1,28 @@
+#
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you 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.
+#
+#   EXAMPLE.COM is reserved for testing according to this RFC:
+#
+#   http://www.rfc-editor.org/rfc/rfc2606.txt
+#
+
+dn: o=neworganization, dc=example,dc=com
+objectclass: organization
+objectclass: top
+o: neworganization
+sn: aValue
diff --git a/old_trunk/server-tools/src/test/resources/org/apache/directory/server/tools/commands/importcmd/import_1_entry_on_error.ldif b/old_trunk/server-tools/src/test/resources/org/apache/directory/server/tools/commands/importcmd/import_1_entry_on_error.ldif
new file mode 100644
index 0000000..6a2a5d6
--- /dev/null
+++ b/old_trunk/server-tools/src/test/resources/org/apache/directory/server/tools/commands/importcmd/import_1_entry_on_error.ldif
@@ -0,0 +1,24 @@
+#
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you 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.
+#
+#   EXAMPLE.COM is reserved for testing according to this RFC:
+#
+#   http://www.rfc-editor.org/rfc/rfc2606.txt
+#
+
+objectclass: top
diff --git a/old_trunk/server-tools/src/test/resources/org/apache/directory/server/tools/commands/importcmd/import_2_entries_error_and_ok.ldif b/old_trunk/server-tools/src/test/resources/org/apache/directory/server/tools/commands/importcmd/import_2_entries_error_and_ok.ldif
new file mode 100644
index 0000000..64f34af
--- /dev/null
+++ b/old_trunk/server-tools/src/test/resources/org/apache/directory/server/tools/commands/importcmd/import_2_entries_error_and_ok.ldif
@@ -0,0 +1,38 @@
+#
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you 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.
+#
+#   EXAMPLE.COM is reserved for testing according to this RFC:
+#
+#   http://www.rfc-editor.org/rfc/rfc2606.txt
+#
+
+dn: o=neworganization, dc=example,dc=com
+objectclass: organization
+objectclass: top
+o: neworganization
+
+dn: bn=newperson1, o=neworganization, dc=example,dc=com
+bn: newperson1
+azerty: top
+azerty: person
+
+dn: cn=newperson2, o=neworganization, dc=example,dc=com
+cn: newperson2
+objectclass: top
+objectclass: person
+sn: aValue
diff --git a/old_trunk/server-unit/pom.xml b/old_trunk/server-unit/pom.xml
new file mode 100644
index 0000000..cd2eab5
--- /dev/null
+++ b/old_trunk/server-unit/pom.xml
@@ -0,0 +1,174 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you 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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.directory.server</groupId>
+    <artifactId>apacheds-parent</artifactId>
+    <version>1.5.4-SNAPSHOT</version>
+  </parent>
+  <artifactId>apacheds-server-unit</artifactId>
+  <name>ApacheDS Server Unit</name>
+  <packaging>jar</packaging>  
+
+  <description>
+    Unit testing framework for ApacheDS Server JNDI Provider
+  </description>
+
+  <dependencies>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>provided</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>ldapsdk</groupId>
+      <artifactId>ldapsdk</artifactId>
+      <scope>test</scope>
+    </dependency>  
+
+    <dependency>
+      <groupId>commons-net</groupId>
+      <artifactId>commons-net</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>dnsjava</groupId>
+      <artifactId>dnsjava</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-server-jndi</artifactId>
+      <version>${pom.version}</version>
+    </dependency>  
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-interceptor-kerberos</artifactId>
+      <version>${pom.version}</version>
+    </dependency>  
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-core-unit</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
+    
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-bootstrap-partition</artifactId>
+      <version>${pom.version}</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <configuration>
+          <excludes>
+            <exclude>**/Abstract*</exclude>
+          </excludes>
+          <argLine>-Xmx1024m</argLine>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+
+  <profiles>
+    <profile>
+      <id>no-integration-tests</id>
+      <activation>
+        <activeByDefault>true</activeByDefault>
+      </activation>
+      <build>
+        <plugins>
+            <plugin>
+              <artifactId>maven-surefire-plugin</artifactId>
+              <configuration>
+                <excludes>
+                  <!-- TODO
+                     Need to rename all integration tests to have Integration
+                     in the name of the test so we can change the exclude below
+                     to only refer to these tests rather than all tests like so:
+                
+                     <exclude>**/*Integration*Test*</exclude>
+                   -->
+                  <exclude>**/*Test.java</exclude>
+                </excludes>
+                <argLine>-Xmx1024m</argLine>
+              </configuration>
+            </plugin>
+
+            <plugin>
+              <artifactId>maven-antrun-plugin</artifactId>
+              <executions>
+                <execution>
+                  <phase>validate</phase>
+                  <configuration>
+                    <tasks>
+                      <echo>
+=================================================================
+                          W A R N I N G
+                          -------------
+                  
+Integration tests have been disabled.  To enable integration 
+tests run maven with the -Dintegration switch.
+=================================================================
+                      </echo>
+                    </tasks>
+                  </configuration>
+                  <goals>
+                    <goal>run</goal>
+                  </goals>
+                </execution>
+              </executions>
+            </plugin>
+         </plugins>
+       </build>
+    </profile>
+
+    <profile>
+      <id>integration</id>
+      <activation>
+        <property><name>integration</name></property>
+      </activation>
+      <build>
+        <plugins>
+          <plugin>
+            <artifactId>maven-surefire-plugin</artifactId>
+            <configuration>
+              <forkMode>pertest</forkMode>
+              <!-- <argLine>-agentlib:yjpagent</argLine> -->
+              <argLine>-Xmx1024m</argLine>
+            </configuration>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+  </profiles>
+</project>
diff --git a/old_trunk/server-unit/src/main/java/org/apache/directory/server/unit/AbstractServerTest.java b/old_trunk/server-unit/src/main/java/org/apache/directory/server/unit/AbstractServerTest.java
new file mode 100644
index 0000000..4851906
--- /dev/null
+++ b/old_trunk/server-unit/src/main/java/org/apache/directory/server/unit/AbstractServerTest.java
@@ -0,0 +1,465 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.unit;
+
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+
+import javax.naming.Context;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.ldap.InitialLdapContext;
+import javax.naming.ldap.LdapContext;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.TestCase;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.directory.server.constants.ServerDNConstants;
+import org.apache.directory.server.core.DefaultDirectoryService;
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.jndi.CoreContextFactory;
+import org.apache.directory.server.ldap.LdapServer;
+import org.apache.directory.server.ldap.handlers.bind.CramMd5MechanismHandler;
+import org.apache.directory.server.ldap.handlers.bind.DigestMd5MechanismHandler;
+import org.apache.directory.server.ldap.handlers.bind.GssapiMechanismHandler;
+import org.apache.directory.server.ldap.handlers.bind.MechanismHandler;
+import org.apache.directory.server.ldap.handlers.bind.SimpleMechanismHandler;
+import org.apache.directory.server.ldap.handlers.bind.ntlm.NtlmMechanismHandler;
+import org.apache.directory.server.ldap.handlers.extended.StartTlsHandler;
+import org.apache.directory.server.ldap.handlers.extended.StoredProcedureExtendedOperationHandler;
+import org.apache.directory.server.protocol.shared.SocketAcceptor;
+import org.apache.directory.shared.ldap.constants.SupportedSaslMechanisms;
+import org.apache.directory.shared.ldap.exception.LdapConfigurationException;
+import org.apache.directory.shared.ldap.ldif.LdifEntry;
+import org.apache.directory.shared.ldap.ldif.LdifReader;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.mina.util.AvailablePortFinder;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * A simple testcase for testing JNDI provider functionality.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public abstract class AbstractServerTest extends TestCase
+{
+    private static final Logger LOG = LoggerFactory.getLogger( AbstractServerTest.class );
+    private static final List<LdifEntry> EMPTY_LIST = Collections.unmodifiableList( new ArrayList<LdifEntry>( 0 ) );
+    private static final String CTX_FACTORY = "com.sun.jndi.ldap.LdapCtxFactory";
+
+    /** the context root for the system partition */
+    protected LdapContext sysRoot;
+
+    /** the context root for the rootDSE */
+    protected LdapContext rootDSE;
+
+    /** the context root for the schema */
+    protected LdapContext schemaRoot;
+
+    /** flag whether to delete database files for each test or not */
+    protected boolean doDelete = true;
+
+//    protected ApacheDS apacheDS = new ApacheDS();
+
+    protected int port = -1;
+
+    private static int start;
+    private static long t0;
+    protected static int nbTests = 10000;
+    protected DirectoryService directoryService;
+    protected SocketAcceptor socketAcceptor;
+    protected LdapServer ldapServer;
+
+
+    /**
+     * If there is an LDIF file with the same name as the test class 
+     * but with the .ldif extension then it is read and the entries 
+     * it contains are added to the server.  It appears as though the
+     * administor adds these entries to the server.
+     *
+     * @param verifyEntries whether or not all entry additions are checked
+     * to see if they were in fact correctly added to the server
+     * @return a list of entries added to the server in the order they were added
+     * @throws NamingException of the load fails
+     */
+    protected List<LdifEntry> loadTestLdif( boolean verifyEntries ) throws NamingException
+    {
+        return loadLdif( getClass().getResourceAsStream( getClass().getSimpleName() + ".ldif" ), verifyEntries );
+    }
+
+
+    /**
+     * Loads an LDIF from an input stream and adds the entries it contains to 
+     * the server.  It appears as though the administrator added these entries
+     * to the server.
+     *
+     * @param in the input stream containing the LDIF entries to load
+     * @param verifyEntries whether or not all entry additions are checked
+     * to see if they were in fact correctly added to the server
+     * @return a list of entries added to the server in the order they were added
+     * @throws NamingException of the load fails
+     */
+    protected List<LdifEntry> loadLdif( InputStream in, boolean verifyEntries ) throws NamingException
+    {
+        if ( in == null )
+        {
+            return EMPTY_LIST;
+        }
+        
+        LdifReader ldifReader = new LdifReader( in );
+        List<LdifEntry> entries = new ArrayList<LdifEntry>();
+
+        for ( LdifEntry entry:ldifReader )
+        {
+            rootDSE.createSubcontext( entry.getDn(), entry.getAttributes() );
+            
+            if ( verifyEntries )
+            {
+                verify( entry );
+                LOG.info( "Successfully verified addition of entry {}", entry.getDn() );
+            }
+            else
+            {
+                LOG.info( "Added entry {} without verification", entry.getDn() );
+            }
+            
+            entries.add( entry );
+        }
+        
+        return entries;
+    }
+    
+
+    /**
+     * Verifies that an entry exists in the directory with the 
+     * specified attributes.
+     *
+     * @param entry the entry to verify
+     * @throws NamingException if there are problems accessing the entry
+     */
+    protected void verify( LdifEntry entry ) throws NamingException
+    {
+        Attributes readAttributes = rootDSE.getAttributes( entry.getDn() );
+        NamingEnumeration<String> readIds = entry.getAttributes().getIDs();
+        while ( readIds.hasMore() )
+        {
+            String id = readIds.next();
+            Attribute readAttribute = readAttributes.get( id );
+            Attribute origAttribute = entry.getAttributes().get( id );
+            
+            for ( int ii = 0; ii < origAttribute.size(); ii++ )
+            {
+                if ( ! readAttribute.contains( origAttribute.get( ii ) ) )
+                {
+                    LOG.error( "Failed to verify entry addition of {}. {} attribute in original " +
+                            "entry missing from read entry.", entry.getDn(), id );
+                    throw new AssertionFailedError( "Failed to verify entry addition of " + entry.getDn()  );
+                }
+            }
+        }
+    }
+    
+
+    /**
+     * Common code to get an initial context via a simple bind to the 
+     * server over the wire using the SUN JNDI LDAP provider. Do not use 
+     * this method until after the setUp() method is called to start the
+     * server otherwise it will fail. 
+     *
+     * @return an LDAP context as the the administrator to the rootDSE
+     * @throws NamingException if the server cannot be contacted
+     */
+    protected LdapContext getWiredContext() throws NamingException
+    {
+        return getWiredContext( ServerDNConstants.ADMIN_SYSTEM_DN, "secret" );
+    }
+    
+    
+    /**
+     * Common code to get an initial context via a simple bind to the 
+     * server over the wire using the SUN JNDI LDAP provider. Do not use 
+     * this method until after the setUp() method is called to start the
+     * server otherwise it will fail.
+     *
+     * @param bindPrincipalDn the DN of the principal to bind as
+     * @param password the password of the bind principal
+     * @return an LDAP context as the the administrator to the rootDSE
+     * @throws NamingException if the server cannot be contacted
+     */
+    protected LdapContext getWiredContext( String bindPrincipalDn, String password ) throws NamingException
+    {
+//        if ( ! apacheDS.isStarted() )
+//        {
+//            throw new ConfigurationException( "The server is not online! Cannot connect to it." );
+//        }
+        
+        Hashtable<String, String> env = new Hashtable<String, String>();
+        env.put( Context.INITIAL_CONTEXT_FACTORY, CTX_FACTORY );
+        env.put( Context.PROVIDER_URL, "ldap://localhost:" + port );
+        env.put( Context.SECURITY_PRINCIPAL, bindPrincipalDn );
+        env.put( Context.SECURITY_CREDENTIALS, password );
+        env.put( Context.SECURITY_AUTHENTICATION, "simple" );
+        return new InitialLdapContext( env, null );
+    }
+    
+    
+    /**
+     * Get's the initial context factory for the provider's ou=system context
+     * root.
+     *
+     * @see junit.framework.TestCase#setUp()
+     */
+    protected void setUp() throws Exception
+    {
+        super.setUp();
+        
+        if ( start == 0 )
+        {
+            t0 = System.currentTimeMillis();
+        }
+
+        start++;
+        directoryService = new DefaultDirectoryService();
+        directoryService.setShutdownHookEnabled( false );
+        socketAcceptor = new SocketAcceptor( null );
+        ldapServer = new LdapServer();
+        ldapServer.setSocketAcceptor( socketAcceptor );
+        ldapServer.setDirectoryService( directoryService );
+        ldapServer.setIpPort( port = AvailablePortFinder.getNextAvailable( 1024 ) );
+
+        setupSaslMechanisms( ldapServer );
+
+        doDelete( directoryService.getWorkingDirectory() );
+        configureDirectoryService();
+        directoryService.startup();
+
+        configureLdapServer();
+
+        // TODO shouldn't this be before calling configureLdapServer() ???
+        ldapServer.addExtendedOperationHandler( new StartTlsHandler() );
+        ldapServer.addExtendedOperationHandler( new StoredProcedureExtendedOperationHandler() );
+
+        ldapServer.start();
+        setContexts( ServerDNConstants.ADMIN_SYSTEM_DN, "secret" );
+    }
+
+
+    private void setupSaslMechanisms( LdapServer server )
+    {
+        Map<String, MechanismHandler> mechanismHandlerMap = new HashMap<String,MechanismHandler>();
+
+        mechanismHandlerMap.put( SupportedSaslMechanisms.PLAIN, new SimpleMechanismHandler() );
+
+        CramMd5MechanismHandler cramMd5MechanismHandler = new CramMd5MechanismHandler();
+        cramMd5MechanismHandler.setDirectoryService( directoryService );
+        mechanismHandlerMap.put( SupportedSaslMechanisms.CRAM_MD5, cramMd5MechanismHandler );
+
+        DigestMd5MechanismHandler digestMd5MechanismHandler = new DigestMd5MechanismHandler();
+        digestMd5MechanismHandler.setDirectoryService( directoryService );
+        mechanismHandlerMap.put( SupportedSaslMechanisms.DIGEST_MD5, digestMd5MechanismHandler );
+
+        GssapiMechanismHandler gssapiMechanismHandler = new GssapiMechanismHandler();
+        gssapiMechanismHandler.setDirectoryService( directoryService );
+        mechanismHandlerMap.put( SupportedSaslMechanisms.GSSAPI, gssapiMechanismHandler );
+
+        NtlmMechanismHandler ntlmMechanismHandler = new NtlmMechanismHandler();
+        // TODO - set some sort of default NtlmProvider implementation here
+        // ntlmMechanismHandler.setNtlmProvider( provider );
+        // TODO - or set FQCN of some sort of default NtlmProvider implementation here
+        // ntlmMechanismHandler.setNtlmProviderFqcn( "com.foo.BarNtlmProvider" );
+        mechanismHandlerMap.put( SupportedSaslMechanisms.NTLM, ntlmMechanismHandler );
+        mechanismHandlerMap.put( SupportedSaslMechanisms.GSS_SPNEGO, ntlmMechanismHandler );
+
+        ldapServer.setSaslMechanismHandlers( mechanismHandlerMap );
+    }
+
+
+    protected void configureDirectoryService() throws NamingException
+    {
+    }
+
+
+    protected void configureLdapServer()
+    {
+    }
+
+
+    protected void setAllowAnonymousAccess( boolean anonymousAccess )
+    {
+        directoryService.setAllowAnonymousAccess( anonymousAccess );
+        ldapServer.setAllowAnonymousAccess( anonymousAccess );
+    }
+
+    
+    /**
+     * Deletes the Eve working directory.
+     * @param wkdir the directory to delete
+     * @throws IOException if the directory cannot be deleted
+     */
+    protected void doDelete( File wkdir ) throws IOException
+    {
+        if ( doDelete )
+        {
+            if ( wkdir.exists() )
+            {
+                FileUtils.deleteDirectory( wkdir );
+            }
+
+            if ( wkdir.exists() )
+            {
+                throw new IOException( "Failed to delete: " + wkdir );
+            }
+        }
+    }
+
+
+    /**
+     * Sets the contexts for this base class.  Values of user and password used to
+     * set the respective JNDI properties.  These values can be overriden by the
+     * overrides properties.
+     *
+     * @param user the username for authenticating as this user
+     * @param passwd the password of the user
+     * @throws NamingException if there is a failure of any kind
+     */
+    protected void setContexts( String user, String passwd ) throws NamingException
+    {
+        Hashtable<String, Object> env = new Hashtable<String, Object>();
+        env.put( DirectoryService.JNDI_KEY, directoryService );
+        env.put( Context.SECURITY_PRINCIPAL, user );
+        env.put( Context.SECURITY_CREDENTIALS, passwd );
+        env.put( Context.SECURITY_AUTHENTICATION, "simple" );
+        env.put( Context.INITIAL_CONTEXT_FACTORY, CoreContextFactory.class.getName() );
+        setContexts( env );
+    }
+
+
+    /**
+     * Sets the contexts of this class taking into account the extras and overrides
+     * properties.  
+     *
+     * @param env an environment to use while setting up the system root.
+     * @throws NamingException if there is a failure of any kind
+     */
+    protected void setContexts( Hashtable<String, Object> env ) throws NamingException
+    {
+        Hashtable<String, Object> envFinal = new Hashtable<String, Object>( env );
+        envFinal.put( Context.PROVIDER_URL, ServerDNConstants.SYSTEM_DN );
+        sysRoot = new InitialLdapContext( envFinal, null );
+
+        envFinal.put( Context.PROVIDER_URL, "" );
+        rootDSE = new InitialLdapContext( envFinal, null );
+
+        envFinal.put( Context.PROVIDER_URL, ServerDNConstants.OU_SCHEMA_DN );
+        schemaRoot = new InitialLdapContext( envFinal, null );
+    }
+
+
+    /**
+     * Sets the system context root to null.
+     *
+     * @see junit.framework.TestCase#tearDown()
+     */
+    protected void tearDown() throws Exception
+    {
+        super.tearDown();
+        ldapServer.stop();
+        try
+        {
+            directoryService.shutdown();
+        }
+        catch ( Exception e )
+        {
+            e.printStackTrace();
+        }
+
+        sysRoot = null;
+//        apacheDS = new ApacheDS();
+        
+        if ( start >= nbTests )
+        {
+            System.out.println( "Delta = " + ( System.currentTimeMillis() - t0 ) );
+        }
+    }
+
+
+    /**
+     * Imports the LDIF entries packaged with the Eve JNDI provider jar into
+     * the newly created system partition to prime it up for operation.  Note
+     * that only ou=system entries will be added - entries for other partitions
+     * cannot be imported and will blow chunks.
+     *
+     * @throws NamingException if there are problems reading the ldif file and
+     * adding those entries to the system partition
+     * @param in the input stream with the ldif
+     */
+    protected void importLdif( InputStream in ) throws NamingException
+    {
+        try
+        {
+            for ( LdifEntry ldifEntry:new LdifReader( in ) )
+            {
+                LdapDN dn = new LdapDN( ldifEntry.getDn() );
+                rootDSE.createSubcontext( dn, ldifEntry.getAttributes() );
+            }
+        }
+        catch ( Exception e )
+        {
+            String msg = "failed while trying to parse system ldif file";
+            NamingException ne = new LdapConfigurationException( msg );
+            ne.setRootCause( e );
+            throw ne;
+        }
+    }
+    
+    /**
+     * Inject an ldif String into the server. DN must be relative to the
+     * root.
+     * @param ldif the entries to inject
+     * @throws NamingException if the entries cannot be added
+     */
+    protected void injectEntries( String ldif ) throws NamingException
+    {
+        LdifReader reader = new LdifReader();
+        List<LdifEntry> entries = reader.parseLdif( ldif );
+
+        for ( LdifEntry entry : entries )
+        {
+            rootDSE.createSubcontext( new LdapDN( entry.getDn() ), entry.getAttributes() );
+        }
+    }
+    
+    
+}
diff --git a/old_trunk/server-unit/src/site/site.xml b/old_trunk/server-unit/src/site/site.xml
new file mode 100644
index 0000000..9ec7c16
--- /dev/null
+++ b/old_trunk/server-unit/src/site/site.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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.
+-->
+<!--
+  @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+-->
+<project name="${project.name}">
+    <bannerLeft>
+        <name>${project.name}</name>
+    </bannerLeft>
+    <publishDate position="navigation-bottom" format="dd-MM-yyyy HH:mm" />
+    <body>
+        <menu name="Parent">
+            <item name="Apache Directory ApacheDS" href="../index.html" />
+        </menu>
+        <menu ref="reports" />
+    </body>
+</project>
diff --git a/old_trunk/server-unit/src/test/java/org/apache/directory/server/AddITest.java b/old_trunk/server-unit/src/test/java/org/apache/directory/server/AddITest.java
new file mode 100644
index 0000000..0be626c
--- /dev/null
+++ b/old_trunk/server-unit/src/test/java/org/apache/directory/server/AddITest.java
@@ -0,0 +1,536 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server;
+
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.InitialDirContext;
+import javax.naming.directory.InvalidAttributeValueException;
+import javax.naming.directory.SchemaViolationException;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+
+import netscape.ldap.LDAPAttribute;
+import netscape.ldap.LDAPAttributeSet;
+import netscape.ldap.LDAPConnection;
+import netscape.ldap.LDAPEntry;
+import netscape.ldap.LDAPException;
+
+import org.apache.directory.server.unit.AbstractServerTest;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+
+import java.util.Hashtable;
+
+
+/**
+ * Various add scenario tests.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class AddITest extends AbstractServerTest
+{
+    private static final String RDN = "cn=The Person";
+
+    private DirContext ctx;
+
+
+    /**
+     * Create an entry for a person.
+     */
+    public void setUp() throws Exception
+    {
+        super.setUp();
+
+        Hashtable<String, String> env = new Hashtable<String,String>();
+        env.put( "java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory" );
+        env.put( "java.naming.provider.url", "ldap://localhost:" + port + "/ou=system" );
+        env.put( "java.naming.security.principal", "uid=admin,ou=system" );
+        env.put( "java.naming.security.credentials", "secret" );
+        env.put( "java.naming.security.authentication", "simple" );
+        ctx = new InitialDirContext( env );
+
+        // Create a person
+        Attributes attributes = new AttributesImpl( true );
+        Attribute attribute = new AttributeImpl( "objectClass" );
+        attribute.add( "top" );
+        attribute.add( "person" );
+        attributes.put( attribute );
+        attributes.put( "cn", "The Person" );
+        attributes.put( "sn", "Person" );
+        attributes.put( "description", "this is a person" );
+        DirContext person = ctx.createSubcontext( RDN, attributes );
+
+        assertNotNull( person );
+    }
+
+
+    /**
+     * Remove the person.
+     */
+    public void tearDown() throws Exception
+    {
+        ctx.unbind( RDN );
+        ctx.close();
+        ctx = null;
+        super.tearDown();
+    }
+
+
+    /**
+     * Just a little test to check wether the person is created correctly after
+     * setup.
+     * 
+     * @throws NamingException if we cannot connect to the server
+     */
+    public void testSetUpTearDown() throws NamingException
+    {
+        DirContext person = ( DirContext ) ctx.lookup( RDN );
+        assertNotNull( person );
+
+        // Check object classes
+
+        Attributes attributes = person.getAttributes( "" );
+        Attribute ocls = attributes.get( "objectClass" );
+
+        String[] expectedOcls = { "top", "person" };
+        for ( String name : expectedOcls )
+        {
+            assertTrue( "object class " + name + " is NOT present when it should be!", ocls.contains( name ) );
+        }
+    }
+
+
+    /**
+     * This is the original defect as in JIRA DIREVE-216.
+     * 
+     * @throws NamingException if we cannot connect and perform add operations
+     */
+    public void testAddObjectClasses() throws NamingException
+    {
+
+        // modify object classes, add two more
+        Attributes attributes = new AttributesImpl( true );
+        Attribute ocls = new AttributeImpl( "objectClass" );
+        ocls.add( "organizationalPerson" );
+        ocls.add( "inetOrgPerson" );
+        attributes.put( ocls );
+
+        DirContext person = ( DirContext ) ctx.lookup( RDN );
+        person.modifyAttributes( "", DirContext.ADD_ATTRIBUTE, attributes );
+
+        // Read again from directory
+        person = ( DirContext ) ctx.lookup( RDN );
+        attributes = person.getAttributes( "" );
+        Attribute newOcls = attributes.get( "objectClass" );
+
+        String[] expectedOcls = { "top", "person", "organizationalPerson", "inetOrgPerson" };
+        for ( String name : expectedOcls )
+        {
+            assertTrue( "object class " + name + " is present", newOcls.contains( name ) );
+        }
+    }
+
+
+    /**
+     * This changes a single attribute value. Just as a reference.
+     * 
+     * @throws NamingException if we cannot connect and modify the description
+     */
+    public void testModifyDescription() throws NamingException
+    {
+        String newDescription = "More info on the user ...";
+
+        // modify object classes, add two more
+        Attributes attributes = new AttributesImpl( true );
+        Attribute desc = new AttributeImpl( "description", newDescription );
+        attributes.put( desc );
+
+        DirContext person = ( DirContext ) ctx.lookup( RDN );
+        person.modifyAttributes( "", DirContext.REPLACE_ATTRIBUTE, attributes );
+
+        // Read again from directory
+        person = ( DirContext ) ctx.lookup( RDN );
+        attributes = person.getAttributes( "" );
+        Attribute newDesc = attributes.get( "description" );
+
+        assertTrue( "new Description", newDesc.contains( newDescription ) );
+    }
+
+
+    /**
+     * Try to add entry with required attribute missing.
+     * 
+     * @throws NamingException if we fail to connect
+     */
+    public void testAddWithMissingRequiredAttributes() throws NamingException
+    {
+        // person without sn
+        Attributes attrs = new AttributesImpl();
+        Attribute ocls = new AttributeImpl( "objectClass" );
+        ocls.add( "top" );
+        ocls.add( "person" );
+        attrs.put( ocls );
+        attrs.put( "cn", "Fiona Apple" );
+
+        try
+        {
+            ctx.createSubcontext( "cn=Fiona Apple", attrs );
+            fail( "creation of entry should fail" );
+        }
+        catch ( SchemaViolationException e )
+        {
+            // expected
+        }
+    }
+    
+    
+
+    static final String HOST = "localhost";
+    static final String USER = "uid=admin,ou=system";
+    static final String PASSWORD = "secret";
+    static final String BASE = "ou=system";
+
+
+    /**
+     * Testcase to demonstrate DIRSERVER-643 ("Netscape SDK: Adding an entry with
+     * two description attributes does not combine values."). Uses Sun ONE Directory
+     * SDK for Java 4.1 , or comparable (Netscape, Mozilla).
+     * 
+     * @throws LDAPException if we fail to connect and add entries
+     */
+    public void testAddEntryWithTwoDescriptions() throws LDAPException
+    {
+        LDAPConnection con = new LDAPConnection();
+        con.connect( 3, HOST, super.port, USER, PASSWORD );
+        LDAPAttributeSet attrs = new LDAPAttributeSet();
+        LDAPAttribute ocls = new LDAPAttribute( "objectclass", new String[]
+            { "top", "person" } );
+        attrs.add( ocls );
+        attrs.add( new LDAPAttribute( "sn", "Bush" ) );
+        attrs.add( new LDAPAttribute( "cn", "Kate Bush" ) );
+
+        String descr[] =
+            { "a British singer-songwriter with an expressive four-octave voice",
+                "one of the most influential female artists of the twentieth century" };
+
+        attrs.add( new LDAPAttribute( "description", descr ) );
+
+        String dn = "cn=Kate Bush," + BASE;
+        LDAPEntry kate = new LDAPEntry( dn, attrs );
+
+        con.add( kate );
+
+        // Analyze entry and description attribute
+        LDAPEntry kateReloaded = con.read( dn );
+        assertNotNull( kateReloaded );
+        LDAPAttribute attr = kateReloaded.getAttribute( "description" );
+        assertNotNull( attr );
+        assertEquals( 2, attr.getStringValueArray().length );
+
+        // Remove entry
+        con.delete( dn );
+        con.disconnect();
+    }
+
+
+    /**
+     * Testcase to demonstrate DIRSERVER-643 ("Netscape SDK: Adding an entry with
+     * two description attributes does not combine values."). Uses Sun ONE Directory
+     * SDK for Java 4.1 , or comparable (Netscape, Mozilla).
+     * 
+     * @throws LDAPException if we fail to connect and add entries
+     */
+    public void testAddEntryWithTwoDescriptionsVariant() throws LDAPException
+    {
+        LDAPConnection con = new LDAPConnection();
+        con.connect( 3, HOST, super.port, USER, PASSWORD );
+        LDAPAttributeSet attrs = new LDAPAttributeSet();
+        LDAPAttribute ocls = new LDAPAttribute( "objectclass", new String[]
+            { "top", "person" } );
+        attrs.add( ocls );
+        attrs.add( new LDAPAttribute( "sn", "Bush" ) );
+        attrs.add( new LDAPAttribute( "cn", "Kate Bush" ) );
+
+        String descr[] =
+            { "a British singer-songwriter with an expressive four-octave voice",
+                "one of the most influential female artists of the twentieth century" };
+
+        attrs.add( new LDAPAttribute( "description", descr[0] ) );
+        attrs.add( new LDAPAttribute( "description", descr[1] ) );
+
+        String dn = "cn=Kate Bush," + BASE;
+        LDAPEntry kate = new LDAPEntry( dn, attrs );
+
+        con.add( kate );
+
+        // Analyze entry and description attribute
+        LDAPEntry kateReloaded = con.read( dn );
+        assertNotNull( kateReloaded );
+        LDAPAttribute attr = kateReloaded.getAttribute( "description" );
+        assertNotNull( attr );
+        assertEquals( 2, attr.getStringValueArray().length );
+
+        // Remove entry
+        con.delete( dn );
+        con.disconnect();
+    }
+
+
+    /**
+     * Testcase to demonstrate DIRSERVER-643 ("Netscape SDK: Adding an entry with
+     * two description attributes does not combine values."). Uses Sun ONE Directory
+     * SDK for Java 4.1 , or comparable (Netscape, Mozilla).
+     * 
+     * @throws LDAPException if we fail to connect and add entries
+     */
+    public void testAddEntryWithTwoDescriptionsSecondVariant() throws LDAPException
+    {
+        LDAPConnection con = new LDAPConnection();
+        con.connect( 3, HOST, super.port, USER, PASSWORD );
+        LDAPAttributeSet attrs = new LDAPAttributeSet();
+        LDAPAttribute ocls = new LDAPAttribute( "objectclass", new String[]
+            { "top", "person" } );
+        attrs.add( ocls );
+        attrs.add( new LDAPAttribute( "sn", "Bush" ) );
+
+        String descr[] =
+            { "a British singer-songwriter with an expressive four-octave voice",
+                "one of the most influential female artists of the twentieth century" };
+
+        attrs.add( new LDAPAttribute( "description", descr[0] ) );
+        attrs.add( new LDAPAttribute( "cn", "Kate Bush" ) );
+        attrs.add( new LDAPAttribute( "description", descr[1] ) );
+
+        String dn = "cn=Kate Bush," + BASE;
+        LDAPEntry kate = new LDAPEntry( dn, attrs );
+
+        con.add( kate );
+
+        // Analyze entry and description attribute
+        LDAPEntry kateReloaded = con.read( dn );
+        assertNotNull( kateReloaded );
+        LDAPAttribute attr = kateReloaded.getAttribute( "description" );
+        assertNotNull( attr );
+        assertEquals( 2, attr.getStringValueArray().length );
+
+        // Remove entry
+        con.delete( dn );
+        con.disconnect();
+    }
+    
+    /**
+     * Try to add entry with invalid number of values for a single-valued atribute
+     * 
+     * @throws NamingException if we fail to connect and add entries
+     * @see <a href="http://issues.apache.org/jira/browse/DIRSERVER-614">DIRSERVER-614</a>
+     */
+    public void testAddWithInvalidNumberOfAttributeValues() throws NamingException
+    {
+        // add inetOrgPerson with two displayNames
+        Attributes attrs = new AttributesImpl();
+        Attribute ocls = new AttributeImpl( "objectClass" );
+        ocls.add( "top" );
+        ocls.add( "inetOrgPerson" );
+        attrs.put( ocls );
+        attrs.put( "cn", "Fiona Apple" );
+        attrs.put( "sn", "Apple" );
+        Attribute displayName = new AttributeImpl( "displayName" );
+        displayName.add( "Fiona" );
+        displayName.add( "Fiona A." );
+        attrs.put( displayName );
+
+        try
+        {
+            ctx.createSubcontext( "cn=Fiona Apple", attrs );
+            fail( "creation of entry should fail" );
+        }
+        catch ( InvalidAttributeValueException e )
+        {
+            
+        }
+    }
+
+
+    /**
+     * Try to add entry and an alias to it. Afterwards, remove it.
+     * 
+     * @throws NamingException if we fail to connect and add entries
+     */
+    public void testAddAlias() throws NamingException
+    {
+
+        // Create entry
+        Attributes entry = new AttributesImpl();
+        Attribute entryOcls = new AttributeImpl( SchemaConstants.OBJECT_CLASS_AT );
+        entryOcls.add( SchemaConstants.TOP_OC );
+        entryOcls.add( SchemaConstants.ORGANIZATIONAL_UNIT_OC );
+        entry.put( entryOcls );
+        entry.put( SchemaConstants.OU_AT, "favorite" );
+        String entryRdn = "ou=favorite";
+        ctx.createSubcontext( entryRdn, entry );
+
+        // Create Alias
+        String aliasedObjectName = entryRdn + "," + ctx.getNameInNamespace();
+        Attributes alias = new AttributesImpl();
+        Attribute aliasOcls = new AttributeImpl( SchemaConstants.OBJECT_CLASS_AT );
+        aliasOcls.add( SchemaConstants.TOP_OC );
+        aliasOcls.add( SchemaConstants.EXTENSIBLE_OBJECT_OC );
+        aliasOcls.add( SchemaConstants.ALIAS_OC );
+        alias.put( aliasOcls );
+        alias.put( SchemaConstants.OU_AT, "bestFruit" );
+        alias.put( SchemaConstants.ALIASED_OBJECT_NAME_AT, aliasedObjectName );
+        String rdnAlias = "ou=bestFruit";
+        ctx.createSubcontext( rdnAlias, alias );
+
+        // Remove alias and entry
+        ctx.destroySubcontext( rdnAlias );
+        ctx.destroySubcontext( entryRdn );
+    }
+
+
+    /**
+     * Try to add entry and an alias to it. Afterwards, remove it. This version
+     * cretes a container entry before the operations.
+     * 
+     * @throws NamingException if we fail to connect and add entries
+     */
+    public void testAddAliasInContainer() throws NamingException
+    {
+        // Create container
+        Attributes container = new AttributesImpl();
+        Attribute containerOcls = new AttributeImpl( SchemaConstants.OBJECT_CLASS_AT );
+        containerOcls.add( SchemaConstants.TOP_OC );
+        containerOcls.add( SchemaConstants.ORGANIZATIONAL_UNIT_OC );
+        container.put( containerOcls );
+        container.put( SchemaConstants.OU_AT, "Fruits" );
+        String containerRdn = "ou=Fruits";
+        DirContext containerCtx = ctx.createSubcontext( containerRdn, container );
+
+        // Create entry
+        Attributes entry = new AttributesImpl();
+        Attribute entryOcls = new AttributeImpl( SchemaConstants.OBJECT_CLASS_AT );
+        entryOcls.add( SchemaConstants.TOP_OC );
+        entryOcls.add( SchemaConstants.ORGANIZATIONAL_UNIT_OC );
+        entry.put( entryOcls );
+        entry.put( SchemaConstants.OU_AT, "favorite" );
+        String entryRdn = "ou=favorite";
+        containerCtx.createSubcontext( entryRdn, entry );
+
+        // Create alias ou=bestFruit,ou=Fruits to entry ou=favorite,ou=Fruits
+        String aliasedObjectName = entryRdn + "," + containerCtx.getNameInNamespace();
+        Attributes alias = new AttributesImpl();
+        Attribute aliasOcls = new AttributeImpl( SchemaConstants.OBJECT_CLASS_AT );
+        aliasOcls.add( SchemaConstants.TOP_OC );
+        aliasOcls.add( SchemaConstants.EXTENSIBLE_OBJECT_OC );
+        aliasOcls.add( SchemaConstants.ALIAS_OC );
+        alias.put( aliasOcls );
+        alias.put( SchemaConstants.OU_AT, "bestFruit" );
+        alias.put( SchemaConstants.ALIASED_OBJECT_NAME_AT, aliasedObjectName );
+        String rdnAlias = "ou=bestFruit";
+        containerCtx.createSubcontext( rdnAlias, alias );
+
+        // search one level scope for alias 
+        SearchControls controls = new SearchControls();
+        controls.setDerefLinkFlag( true );
+        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+        containerCtx.addToEnvironment( "java.naming.ldap.derefAliases", "never" );
+        NamingEnumeration<SearchResult> ne = containerCtx.search( "", "(objectClass=*)", controls );
+        assertTrue( ne.hasMore() );
+        SearchResult sr = ne.next();
+        assertEquals( "ou=favorite", sr.getName() );
+        assertTrue( ne.hasMore() );
+        sr = ne.next();
+        assertEquals( "ou=bestFruit", sr.getName() );
+        
+        // search one level with dereferencing turned on
+        controls = new SearchControls();
+        controls.setDerefLinkFlag( true );
+        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+        containerCtx.addToEnvironment( "java.naming.ldap.derefAliases", "always" );
+        ne = containerCtx.search( "", "(objectClass=*)", controls );
+        assertTrue( ne.hasMore() );
+        sr = ne.next();
+        assertEquals( "ou=favorite", sr.getName() );
+        assertFalse( ne.hasMore() );
+        
+        // search with base set to alias and dereferencing turned on
+        controls = new SearchControls();
+        controls.setDerefLinkFlag( false );
+        controls.setSearchScope( SearchControls.OBJECT_SCOPE );
+        containerCtx.addToEnvironment( "java.naming.ldap.derefAliases", "always" );
+        ne = containerCtx.search( "ou=bestFruit", "(objectClass=*)", controls );
+        assertTrue( ne.hasMore() );
+        sr = ne.next();
+        assertEquals( "ldap://localhost:"+super.port+"/ou=favorite,ou=Fruits,ou=system", sr.getName() );
+        assertFalse( ne.hasMore() );
+        
+        // Remove alias and entry
+        containerCtx.destroySubcontext( rdnAlias );
+        containerCtx.destroySubcontext( entryRdn );
+
+        // Remove container
+        ctx.destroySubcontext( containerRdn );
+    }
+    
+    
+    /**
+     * Try to add entry and an alias to it. Afterwards, remove it.  Taken from
+     * DIRSERVER-1157 test contribution.
+     * 
+     * @see https://issues.apache.org/jira/browse/DIRSERVER-1157
+     * @throws Exception
+     */
+    public void testAddDeleteAlias() throws Exception
+    {
+        // Create entry ou=favorite,dc=example,dc=com
+        Attributes entry = new AttributesImpl();
+        Attribute entryOcls = new AttributeImpl( SchemaConstants.OBJECT_CLASS_AT );
+        entryOcls.add( SchemaConstants.TOP_OC );
+        entryOcls.add( SchemaConstants.ORGANIZATIONAL_UNIT_OC );
+        entry.put( entryOcls );
+        entry.put( SchemaConstants.OU_AT, "favorite" );
+        String entryRdn = "ou=favorite";
+        ctx.createSubcontext( entryRdn, entry );
+
+        // Create Alias ou=bestFruit,dc=example,dc=com to ou=favorite
+        String aliasedObjectName = entryRdn + "," + ctx.getNameInNamespace();
+        Attributes alias = new AttributesImpl();
+        Attribute aliasOcls = new AttributeImpl( SchemaConstants.OBJECT_CLASS_AT );
+        aliasOcls.add( SchemaConstants.TOP_OC );
+        aliasOcls.add( SchemaConstants.EXTENSIBLE_OBJECT_OC );
+        aliasOcls.add( SchemaConstants.ALIAS_OC );
+        alias.put( aliasOcls );
+        alias.put( SchemaConstants.OU_AT, "bestFruit" );
+        alias.put( SchemaConstants.ALIASED_OBJECT_NAME_AT, aliasedObjectName );
+        String rdnAlias = "ou=bestFruit";
+        ctx.createSubcontext( rdnAlias, alias );
+
+        // Remove alias and entry
+        ctx.destroySubcontext( rdnAlias ); //Waiting for Connection.reply()
+        ctx.destroySubcontext( entryRdn );
+    }
+}
diff --git a/old_trunk/server-unit/src/test/java/org/apache/directory/server/AddingEntriesWithSpecialCharactersInRDNTest.java b/old_trunk/server-unit/src/test/java/org/apache/directory/server/AddingEntriesWithSpecialCharactersInRDNTest.java
new file mode 100644
index 0000000..ffe8879
--- /dev/null
+++ b/old_trunk/server-unit/src/test/java/org/apache/directory/server/AddingEntriesWithSpecialCharactersInRDNTest.java
@@ -0,0 +1,325 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server;
+
+
+import java.util.Hashtable;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.InitialDirContext;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+
+import org.apache.directory.server.unit.AbstractServerTest;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+
+
+/**
+ * Test case to demonstrate DIRSERVER-631 ("Creation of entry with special (and
+ * escaped) character in RDN leads to wrong attribute value").
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class AddingEntriesWithSpecialCharactersInRDNTest extends AbstractServerTest {
+    private DirContext ctx = null;
+
+
+    /**
+     * Create an entry for a person.
+     */
+    public void setUp() throws Exception
+    {
+        super.setUp();
+
+        Hashtable<String, Object> env = new Hashtable<String, Object>();
+        env.put( "java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory" );
+        env.put( "java.naming.provider.url", "ldap://localhost:" + port + "/ou=system" );
+        env.put( "java.naming.security.principal", "uid=admin,ou=system" );
+        env.put( "java.naming.security.credentials", "secret" );
+        env.put( "java.naming.security.authentication", "simple" );
+        ctx = new InitialDirContext( env );
+    }
+
+    /**
+     * Remove the person.
+     */
+    public void tearDown() throws Exception
+    {
+        ctx.close();
+        ctx = null;
+        super.tearDown();
+    }
+
+   protected Attributes getPersonAttributes(String sn, String cn) {
+
+       Attributes attrs = new AttributesImpl();
+       Attribute ocls = new AttributeImpl("objectClass");
+       ocls.add("top");
+       ocls.add("person");
+       attrs.put(ocls);
+       attrs.put("cn", cn);
+       attrs.put("sn", sn);
+
+       return attrs;
+   }
+
+   protected Attributes getOrgUnitAttributes(String ou) {
+
+       Attributes attrs = new AttributesImpl();
+       Attribute ocls = new AttributeImpl("objectClass");
+       ocls.add("top");
+       ocls.add("organizationalUnit");
+       attrs.put(ocls);
+       attrs.put("ou", ou);
+
+       return attrs;
+   }
+
+   /**
+    * adding an entry with hash sign (#) in RDN.
+    * 
+    * @throws NamingException 
+    */
+   public void testAddingWithHashRdn() throws NamingException {
+       Attributes attrs = getPersonAttributes("Bush", "Kate#Bush");
+       String rdn = "cn=Kate\\#Bush";
+       ctx.createSubcontext(rdn, attrs);
+
+       SearchControls sctls = new SearchControls();
+       sctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
+
+       NamingEnumeration<SearchResult> enm = ctx.search("", "(cn=Kate\\#Bush)", sctls);
+       assertEquals("entry found", true, enm.hasMore());
+       
+       while (enm.hasMore()) 
+       {
+           SearchResult sr = enm.next();
+           attrs = sr.getAttributes();
+           Attribute cn = sr.getAttributes().get("cn");
+           assertNotNull(cn);
+           assertTrue(cn.contains("Kate#Bush"));
+       }
+       
+       ctx.destroySubcontext(rdn);
+   }
+
+   /**
+    * adding an entry with comma sign (,) in RDN.
+    * 
+    * @throws NamingException 
+    */
+   public void testAddingWithCommaInRdn() throws NamingException {
+
+       Attributes attrs = getPersonAttributes("Bush", "Bush, Kate");
+       String rdn = "cn=Bush\\, Kate";
+       ctx.createSubcontext(rdn, attrs);
+
+       SearchControls sctls = new SearchControls();
+       sctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
+
+       NamingEnumeration<SearchResult> enm = ctx.search("", "(cn=Bush, Kate)", sctls);
+       assertEquals("entry found", true, enm.hasMore());
+       
+       while (enm.hasMore()) 
+       {
+           SearchResult sr = enm.next();
+           attrs = sr.getAttributes();
+           Attribute cn = sr.getAttributes().get("cn");
+           assertNotNull(cn);
+           assertTrue(cn.contains("Bush, Kate"));
+           assertEquals( "cn=Bush\\, Kate", sr.getName() );
+       }
+
+       ctx.destroySubcontext(rdn);
+   }
+
+   /**
+    * adding an entry with quotes (") in RDN.
+    */
+/*   public void testAddingWithQuotesInRdn() throws NamingException {
+
+       Attributes attrs = getPersonAttributes("Messer",
+               "Mackie \\\\\\\"The Knife\" Messer");
+       String rdn = "cn=Mackie \\\"The Knife\" Messer";
+       ctx.createSubcontext(rdn, attrs);
+
+       SearchControls sctls = new SearchControls();
+       sctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
+
+       NamingEnumeration enm = ctx.search("",
+               "(cn=Mackie \"The Knife\" Messer)", sctls);
+       assertEquals("entry found", true, enm.hasMore());
+       while (enm.hasMore()) {
+           SearchResult sr = (SearchResult) enm.next();
+           attrs = sr.getAttributes();
+           Attribute cn = sr.getAttributes().get("cn");
+           assertNotNull(cn);
+           assertTrue(cn.contains("Mackie \"The Knife\" Messer"));
+       }
+
+       ctx.destroySubcontext(rdn);
+   }
+*/
+   /**
+    * adding an entry with backslash (\) in RDN.
+    */
+/*   public void testAddingWithBackslashInRdn() throws NamingException {
+
+       Attributes attrs = getOrgUnitAttributes("AC\\DC");
+       String rdn = "ou=AC\\\\DC";
+       ctx.createSubcontext(rdn, attrs);
+
+       SearchControls sctls = new SearchControls();
+       sctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
+
+       NamingEnumeration enm = ctx.search("", "(ou=AC\\DC)", sctls);
+       assertEquals("entry found", true, enm.hasMore());
+       while (enm.hasMore()) {
+           SearchResult sr = (SearchResult) enm.next();
+           attrs = sr.getAttributes();
+           Attribute ou = sr.getAttributes().get("ou");
+           assertNotNull(ou);
+           assertTrue(ou.contains("AC\\DC"));
+       }
+
+       ctx.destroySubcontext(rdn);
+   }
+*/
+   /**
+    * adding an entry with greater sign (>) in RDN.
+    * 
+    * @throws NamingException 
+    */
+   public void testAddingWithGreaterSignInRdn() throws NamingException {
+
+       Attributes attrs = getOrgUnitAttributes("East -> West");
+       String rdn = "ou=East -\\> West";
+       ctx.createSubcontext(rdn, attrs);
+
+       SearchControls sctls = new SearchControls();
+       sctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
+
+       NamingEnumeration<SearchResult> enm = ctx.search("", "(ou=East -> West)", sctls);
+       assertEquals("entry found", true, enm.hasMore());
+       
+       while (enm.hasMore()) 
+       {
+           SearchResult sr = enm.next();
+           attrs = sr.getAttributes();
+           Attribute ou = sr.getAttributes().get("ou");
+           assertNotNull(ou);
+           assertTrue(ou.contains("East -> West"));
+       }
+
+       ctx.destroySubcontext(rdn);
+   }
+
+   /**
+    * adding an entry with less sign (<) in RDN.
+    * 
+    * @throws NamingException 
+    */
+   public void testAddingWithLessSignInRdn() throws NamingException {
+
+       Attributes attrs = getOrgUnitAttributes("Scissors 8<");
+       String rdn = "ou=Scissors 8\\<";
+       ctx.createSubcontext(rdn, attrs);
+
+       SearchControls sctls = new SearchControls();
+       sctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
+
+       NamingEnumeration<SearchResult> enm = ctx.search("", "(ou=Scissors 8<)", sctls);
+       assertEquals("entry found", true, enm.hasMore());
+       
+       while (enm.hasMore()) 
+       {
+           SearchResult sr = enm.next();
+           attrs = sr.getAttributes();
+           Attribute ou = sr.getAttributes().get("ou");
+           assertNotNull(ou);
+           assertTrue(ou.contains("Scissors 8<"));
+       }
+
+       ctx.destroySubcontext(rdn);
+   }
+
+   /**
+    * adding an entry with semicolon (;) in RDN.
+    * 
+    * @throws NamingException 
+    */
+   public void testAddingWithSemicolonInRdn() throws NamingException {
+
+       Attributes attrs = getOrgUnitAttributes("semicolon group;");
+       String rdn = "ou=semicolon group\\;";
+       ctx.createSubcontext(rdn, attrs);
+
+       SearchControls sctls = new SearchControls();
+       sctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
+
+       NamingEnumeration<SearchResult> enm = ctx.search("", "(ou=semicolon group;)", sctls);
+       assertEquals("entry found", true, enm.hasMore());
+       
+       while (enm.hasMore()) 
+       {
+           SearchResult sr = enm.next();
+           attrs = sr.getAttributes();
+           Attribute ou = sr.getAttributes().get("ou");
+           assertNotNull(ou);
+           assertTrue(ou.contains("semicolon group;"));
+       }
+
+       ctx.destroySubcontext(rdn);
+   }
+
+   /**
+    * adding an entry with equals sign (=) in RDN.
+    * 
+    * @throws NamingException 
+    */
+   public void testAddingWithEqualsInRdn() throws NamingException {
+
+       Attributes attrs = getOrgUnitAttributes("nomen=omen");
+       String rdn = "ou=nomen\\=omen";
+       ctx.createSubcontext(rdn, attrs);
+
+       SearchControls sctls = new SearchControls();
+       sctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
+
+       NamingEnumeration<SearchResult> enm = ctx.search("", "(ou=nomen=omen)", sctls);
+       assertEquals("entry found", true, enm.hasMore());
+       
+       while (enm.hasMore()) 
+       {
+           SearchResult sr = enm.next();
+           attrs = sr.getAttributes();
+           Attribute ou = sr.getAttributes().get("ou");
+           assertNotNull(ou);
+           assertTrue(ou.contains("nomen=omen"));
+       }
+
+       ctx.destroySubcontext(rdn);
+   }
+}
\ No newline at end of file
diff --git a/old_trunk/server-unit/src/test/java/org/apache/directory/server/BadDnTest.java b/old_trunk/server-unit/src/test/java/org/apache/directory/server/BadDnTest.java
new file mode 100644
index 0000000..c53809a
--- /dev/null
+++ b/old_trunk/server-unit/src/test/java/org/apache/directory/server/BadDnTest.java
@@ -0,0 +1,112 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server;
+
+
+import java.util.Hashtable;
+
+import javax.naming.AuthenticationException;
+import javax.naming.ldap.InitialLdapContext;
+import javax.naming.ldap.LdapContext;
+
+import org.apache.directory.server.unit.AbstractServerTest;
+
+
+/**
+ * Tests the effects of using a bad dn in various operations.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class BadDnTest extends AbstractServerTest
+{
+    /**
+     * Bind as a user.
+     * 
+     * @param bindDn 
+     * @param password 
+     * @return The {@link LdapContext} for the bound user. 
+     * @throws Exception 
+     */
+    public LdapContext bind( String bindDn, String password ) throws Exception
+    {
+        Hashtable<String, Object> env = new Hashtable<String, Object>();
+        env.put( "java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory" );
+        env.put( "java.naming.provider.url", "ldap://localhost:" + port + "/ou=system" );
+        env.put( "java.naming.security.principal", bindDn );
+        env.put( "java.naming.security.credentials", password );
+        env.put( "java.naming.security.authentication", "simple" );
+
+        LdapContext ctx = new InitialLdapContext( env, null );
+        assertNotNull( ctx );
+        return ctx;
+    }
+
+
+    /**
+     * Test with bindDn which is not even found under any namingContext of the
+     * server.
+     * 
+     * @throws Exception 
+     */
+    public void testBadBindDnNotInContext() throws Exception
+    {
+        try
+        {
+            bind( "cn=bogus", "blah" );
+            fail( "should never get here due to a " );
+        }
+        catch ( AuthenticationException e )
+        {
+        }
+    }
+
+
+    /**
+     * Test with bindDn that is under a naming context but points to non-existant user.
+     * @todo make this pass: see http://issues.apache.org/jira/browse/DIREVE-339
+     */
+    //    public void testBadBindDnMalformed() throws Exception
+    //    {
+    //        try
+    //        {
+    //            bind( "system", "blah" );
+    //            fail( "should never get here due to a " );
+    //        }
+    //        catch ( InvalidNameException e ){}
+    //    }
+
+    /**
+     * Test with bindDn that is under a naming context but points to non-existant user.
+     * 
+     * @throws Exception 
+     */
+    public void testBadBindDnInContext() throws Exception
+    {
+        try
+        {
+            bind( "cn=bogus,ou=system", "blah" );
+            fail( "should never get here due to a " );
+        }
+        catch ( AuthenticationException e )
+        {
+        }
+    }
+}
diff --git a/old_trunk/server-unit/src/test/java/org/apache/directory/server/BinarySearchTest.java b/old_trunk/server-unit/src/test/java/org/apache/directory/server/BinarySearchTest.java
new file mode 100644
index 0000000..9423a3a
--- /dev/null
+++ b/old_trunk/server-unit/src/test/java/org/apache/directory/server/BinarySearchTest.java
@@ -0,0 +1,136 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server;
+
+
+import java.util.Hashtable;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.InitialLdapContext;
+import javax.naming.ldap.LdapContext;
+
+import org.apache.directory.server.unit.AbstractServerTest;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+
+
+/**
+ * For DIRSERVER-715 and part of DIRSERVER-169.  May include other tests
+ * for binary attribute based searching.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class BinarySearchTest extends AbstractServerTest
+{
+    private LdapContext ctx = null;
+
+
+    /**
+     * Creation of required attributes of a person entry.
+     */
+    protected Attributes getPersonAttributes( String sn, String cn )
+    {
+        Attributes attributes = new AttributesImpl();
+        Attribute attribute = new AttributeImpl( "objectClass" );
+        attribute.add( "top" );
+        attribute.add( "person" );
+        attributes.put( attribute );
+        attributes.put( "cn", cn );
+        attributes.put( "sn", sn );
+
+        return attributes;
+    }
+
+
+    /**
+     * Create context and a person entry.
+     */
+    public void setUp() throws Exception
+    {
+        super.setUp();
+
+        Hashtable<String, Object> env = new Hashtable<String, Object>();
+        env.put( "java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory" );
+        env.put( "java.naming.provider.url", "ldap://localhost:" + port + "/ou=system" );
+        env.put( "java.naming.security.principal", "uid=admin,ou=system" );
+        env.put( "java.naming.security.credentials", "secret" );
+        env.put( "java.naming.security.authentication", "simple" );
+
+        ctx = new InitialLdapContext( env, null );
+        assertNotNull( ctx );
+    }
+
+
+    /**
+     * Remove person entry and close context.
+     */
+    public void tearDown() throws Exception
+    {
+        ctx.close();
+        ctx = null;
+        super.tearDown();
+    }
+    
+    
+    /**
+     * TODO re-enable this test after fixing binary attribute searches.
+     * 
+     * @throws Exception
+     */
+    public void testSearchByBinaryAttribute() throws Exception 
+    {
+        byte[] certData = new byte[] { 0x34, 0x56, 0x4e, 0x5f };
+        
+        // First let's add a some binary data representing a userCertificate
+        Attributes attrs = getPersonAttributes( "Bush", "Kate Bush" );
+        Attribute objectClasses = attrs.get( "objectClass" );
+        objectClasses.add( "strongAuthenticationUser" );
+
+        attrs.put( "userCertificate", certData );
+        ctx.createSubcontext( "cn=Kate Bush", attrs );
+        
+        // Search for kate by cn first
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+        NamingEnumeration enm = ctx.search( "", "(cn=Kate Bush)", controls );
+        assertTrue( enm.hasMore() );
+        SearchResult sr = ( SearchResult ) enm.next();
+        assertNotNull( sr );
+        assertFalse( enm.hasMore() );
+        assertEquals( "cn=Kate Bush", sr.getName() );
+
+        // TODO enable this test here
+        // Failing here below this due to the frontend interpretting the byte[]
+        // as a String value.  I see the value sent to the SearchHandler of the
+        // filter to be a String looking like: "[B@17210a5".
+        
+        enm = ctx.search( "", "(userCertificate={0})", new Object[] {certData}, controls );
+        assertTrue( enm.hasMore() );
+        sr = ( SearchResult ) enm.next();
+        assertNotNull( sr );
+        assertFalse( enm.hasMore() );
+        assertEquals( "cn=Kate Bush", sr.getName() );
+    }
+}
diff --git a/old_trunk/server-unit/src/test/java/org/apache/directory/server/CascadeControlITest.java b/old_trunk/server-unit/src/test/java/org/apache/directory/server/CascadeControlITest.java
new file mode 100644
index 0000000..da37c63
--- /dev/null
+++ b/old_trunk/server-unit/src/test/java/org/apache/directory/server/CascadeControlITest.java
@@ -0,0 +1,105 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server;
+
+
+import java.util.Hashtable;
+
+import javax.naming.NamingException;
+import javax.naming.ldap.Control;
+import javax.naming.ldap.InitialLdapContext;
+import javax.naming.ldap.LdapContext;
+
+import org.apache.directory.server.unit.AbstractServerTest;
+import org.apache.directory.shared.ldap.message.CascadeControl;
+
+
+/**
+ * A set of tests to make sure the Cascade Control is working 
+ * properly.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class CascadeControlITest extends AbstractServerTest
+{
+    private LdapContext ctx = null;
+
+
+    /**
+     * Creates context.
+     */
+    protected void setUp() throws Exception
+    {
+        super.setUp();
+
+        Hashtable<String, Object> env = new Hashtable<String, Object>();
+        env.put( "java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory" );
+        env.put( "java.naming.provider.url", "ldap://localhost:" + port + "/" );
+        env.put( "java.naming.security.principal", "uid=admin,ou=system" );
+        env.put( "java.naming.security.credentials", "secret" );
+        env.put( "java.naming.security.authentication", "simple" );
+
+        ctx = new InitialLdapContext( env, null );
+        assertNotNull( ctx );
+    }
+
+
+    /**
+    * Closes context.
+    */
+    protected void tearDown() throws Exception
+    {
+        ctx.close();
+        ctx = null;
+        super.tearDown();
+    }
+
+
+    /**
+     * Tests the behavior of the server with a SearchRequest including
+     * the CascadeControl.
+     */
+    public void testCascadeControl() throws NamingException
+    {
+        LdapContext containerCtx = ( LdapContext ) ctx.lookup( "ou=attributeTypes, cn=apachemeta, ou=schema" );
+        
+        try
+        {
+            containerCtx.setRequestControls( new Control[] { new CascadeControl() } );
+        }
+        catch ( NamingException e )
+        {
+            fail( e.getMessage() );
+        }
+
+        String oldRdn = "m-oid=1.3.6.1.4.1.18060.0.4.0.2.1";
+        String newRdn = "m-oid=1.3.6.1.4.1.18060.0.4.0.2.1000";
+        try
+        {
+            containerCtx.rename( oldRdn, newRdn );
+        }
+        catch ( NamingException e )
+        {
+            // Hiding this fail() until the server has the correct mechanism to handle this.
+            //            fail( e.getMessage() );
+        }
+    }
+}
diff --git a/old_trunk/server-unit/src/test/java/org/apache/directory/server/ChangeListener.java b/old_trunk/server-unit/src/test/java/org/apache/directory/server/ChangeListener.java
new file mode 100644
index 0000000..20c8eb0
--- /dev/null
+++ b/old_trunk/server-unit/src/test/java/org/apache/directory/server/ChangeListener.java
@@ -0,0 +1,141 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server;
+
+
+import java.util.Hashtable;
+
+import javax.naming.Context;
+import javax.naming.NamingEnumeration;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.Control;
+import javax.naming.ldap.HasControls;
+import javax.naming.ldap.InitialLdapContext;
+
+import org.apache.directory.shared.ldap.codec.search.controls.EntryChangeControlCodec;
+import org.apache.directory.shared.ldap.codec.search.controls.EntryChangeControlDecoder;
+import org.apache.directory.shared.ldap.message.PersistentSearchControl;
+
+
+/**
+ * A simple change listener application that prints out changes returned using
+ * the psearch control.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class ChangeListener
+{
+    public static void main( String[] args ) throws Exception
+    {
+        Hashtable<String, Object> env = new Hashtable<String, Object>();
+        env.put( "java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory" );
+        env.put( "java.naming.provider.url", "ldap://localhost:10389/ou=system" );
+        env.put( "java.naming.security.principal", "uid=admin,ou=system" );
+        env.put( "java.naming.security.credentials", "secret" );
+        env.put( "java.naming.security.authentication", "simple" );
+        InitialLdapContext ctx = new InitialLdapContext( env, null );
+        Runtime.getRuntime().addShutdownHook( new Thread( new ShutdownHook( ctx ) ) );
+        PersistentSearchControl control = new PersistentSearchControl();
+        control.setChangesOnly( false );
+        control.setReturnECs( true );
+        control.setCritical( true );
+        control.setChangeTypes( PersistentSearchControl.ALL_CHANGES );
+        Control[] ctxCtls = new Control[]
+            { control };
+
+        try
+        {
+            Control[] respCtls;
+            ctx.setRequestControls( ctxCtls );
+            EntryChangeControlCodec ecCtl = null;
+            NamingEnumeration list = ctx.search( "", "objectClass=*", null );
+            while ( list.hasMore() )
+            {
+                SearchResult result = ( SearchResult ) list.next();
+                if ( result instanceof HasControls )
+                {
+                    respCtls = ( ( HasControls ) result ).getControls();
+                    if ( respCtls != null )
+                    {
+                        for ( int ii = 0; ii < respCtls.length; ii++ )
+                        {
+                            if ( respCtls[ii].getID().equals(
+                                org.apache.directory.shared.ldap.message.EntryChangeControl.CONTROL_OID ) )
+                            {
+                                EntryChangeControlDecoder decoder = new EntryChangeControlDecoder();
+                                ecCtl = ( EntryChangeControlCodec ) decoder.decode( respCtls[ii].getEncodedValue() );
+                            }
+                        }
+                    }
+                }
+
+                StringBuffer buf = new StringBuffer();
+                buf.append( "DN: " ).append( result.getName() ).append( "\n" );
+                if ( ecCtl != null )
+                {
+                    System.out.println( "================ NOTIFICATION ================" );
+                    buf.append( "    EntryChangeControl =\n" );
+                    buf.append( "        changeType   : " ).append( ecCtl.getChangeType() ).append( "\n" );
+                    buf.append( "        previousDN   : " ).append( ecCtl.getPreviousDn() ).append( "\n" );
+                    buf.append( "        changeNumber : " ).append( ecCtl.getChangeNumber() ).append( "\n" );
+                }
+
+                System.out.println( buf.toString() );
+
+                if ( ecCtl != null )
+                {
+                    System.out.println( "==============================================" );
+                }
+            }
+        }
+        catch ( Exception e )
+        {
+            e.printStackTrace();
+        }
+    }
+
+    static class ShutdownHook implements Runnable
+    {
+        final Context ctx;
+
+
+        ShutdownHook(Context ctx)
+        {
+            this.ctx = ctx;
+        }
+
+
+        public void run()
+        {
+            if ( ctx != null )
+            {
+                try
+                {
+                    ctx.close();
+                }
+                catch ( Exception e )
+                {
+                    e.printStackTrace();
+                }
+            }
+        }
+    }
+}
diff --git a/old_trunk/server-unit/src/test/java/org/apache/directory/server/DIRSERVER951ITest.java b/old_trunk/server-unit/src/test/java/org/apache/directory/server/DIRSERVER951ITest.java
new file mode 100644
index 0000000..fba14d3
--- /dev/null
+++ b/old_trunk/server-unit/src/test/java/org/apache/directory/server/DIRSERVER951ITest.java
@@ -0,0 +1,160 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server;
+
+
+import org.apache.directory.server.core.entry.DefaultServerEntry;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.partition.Oid;
+import org.apache.directory.server.core.partition.impl.btree.Index;
+import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmIndex;
+import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmPartition;
+import org.apache.directory.server.unit.AbstractServerTest;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.util.DateUtils;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.LdapContext;
+import java.util.HashSet;
+import java.util.Set;
+
+
+/**
+ * A set of tests to make sure the negation operator is working 
+ * properly when included in search filters. Created in response
+ * to JIRA issue 
+ * <a href="https://issues.apache.org/jira/browse/DIRSERVER-951">DIRSERVER-951</a>.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class DIRSERVER951ITest extends AbstractServerTest
+{
+    private LdapContext ctx;
+
+
+    /**
+     * Create context and entries for tests.
+     */
+    public void setUp() throws Exception
+    {
+        super.setUp();
+        super.loadTestLdif( true );
+        ctx = getWiredContext();
+        assertNotNull( ctx );
+    }
+
+
+    @Override
+    protected void configureDirectoryService() throws NamingException
+    {
+        JdbmPartition systemCfg = new JdbmPartition();
+        systemCfg.setId( "system" );
+
+        // @TODO need to make this configurable for the system partition
+        systemCfg.setCacheSize( 500 );
+
+        systemCfg.setSuffix( "ou=system" );
+
+        // Add indexed attributes for system partition
+        Set<Index> indexedAttrs = new HashSet<Index>();
+        indexedAttrs.add( new JdbmIndex( Oid.ALIAS ) );
+        indexedAttrs.add( new JdbmIndex( Oid.EXISTANCE ) );
+        indexedAttrs.add( new JdbmIndex( Oid.HIERARCHY ) );
+        indexedAttrs.add( new JdbmIndex( Oid.NDN ) );
+        indexedAttrs.add( new JdbmIndex( Oid.ONEALIAS ) );
+        indexedAttrs.add( new JdbmIndex( Oid.SUBALIAS ) );
+        indexedAttrs.add( new JdbmIndex( Oid.UPDN ) );
+        indexedAttrs.add( new JdbmIndex( "objectClass" ) );
+        indexedAttrs.add( new JdbmIndex( "ou" ) );
+        systemCfg.setIndexedAttributes( indexedAttrs );
+
+        // Add context entry for system partition
+        LdapDN systemDn = new LdapDN( "ou=system" );
+        ServerEntry systemEntry = new DefaultServerEntry( directoryService.getRegistries(), systemDn );
+        
+        systemEntry.put( "objectClass", "top", "account" );
+        systemEntry.put( "creatorsName", "uid=admin,ou=system" );
+        systemEntry.put( "createTimestamp", DateUtils.getGeneralizedTime() );
+        systemEntry.put( "ou", "system" );
+        systemEntry.put( "uid", "testUid" );
+        systemCfg.setContextEntry( systemEntry );
+
+        directoryService.setSystemPartition( systemCfg );
+    }
+
+    /**
+     * Closes context and destroys server.
+     */
+    public void tearDown() throws Exception
+    {
+        ctx.close();
+        ctx = null;
+        super.tearDown();
+    }
+    
+
+    /**
+     * Tests to make sure a negated search for OU of "test1" returns
+     * those entries that do not have the OU attribute or do not have
+     * a "test1" value for OU if the attribute exists.
+     * 
+     * @throws Exception on failure to search
+     */
+    public void testSearchNotOU() throws Exception
+    {
+        Set<SearchResult> results = getResults( "(!(ou=test1))" );
+        assertFalse( contains( "uid=test1,ou=test,ou=system", results ) );
+        assertTrue( contains( "uid=test2,ou=test,ou=system", results ) );
+        assertTrue( contains( "uid=testNoOU,ou=test,ou=system", results ) );
+    }
+
+    
+    boolean contains( String dn, Set<SearchResult> results )
+    {
+        for ( SearchResult result : results )
+        {
+            if ( result.getNameInNamespace().equals( dn ) )
+            {
+                return true;
+            }
+        }
+        
+        return false;
+    }
+    
+    
+    Set<SearchResult> getResults( String filter ) throws NamingException
+    {
+        Set<SearchResult> results = new HashSet<SearchResult>();
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        NamingEnumeration<SearchResult> namingEnumeration = ctx.search( "ou=system", filter, controls );
+        while( namingEnumeration.hasMore() )
+        {
+            results.add( namingEnumeration.next() );
+        }
+        
+        return results;
+    }
+}
diff --git a/old_trunk/server-unit/src/test/java/org/apache/directory/server/DITUtilitiesSP.java b/old_trunk/server-unit/src/test/java/org/apache/directory/server/DITUtilitiesSP.java
new file mode 100644
index 0000000..2b28258
--- /dev/null
+++ b/old_trunk/server-unit/src/test/java/org/apache/directory/server/DITUtilitiesSP.java
@@ -0,0 +1,64 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server;
+
+import javax.naming.Name;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.LdapContext;
+
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+public class DITUtilitiesSP
+{
+    private static final Logger log = LoggerFactory.getLogger( DITUtilitiesSP.class );
+    
+    /**
+     * Recursively deletes a subtree including the apex given.
+     * 
+     * If you do not want to wait for the developers to implement the
+     * following RFC
+     * http://kahuna.telstra.net/ietf/all-ids/draft-armijo-ldap-treedelete-02.txt
+     * you can do it yourself!
+     * 
+     * @param ctx an LDAP context to perform operations on
+     * @param rdn ctx relative name of the entry which is root of
+     *        the subtree to be deleted
+     * @throws NamingException
+     */
+    public static void deleteSubtree( LdapContext ctx, Name rdn ) throws NamingException
+    {
+        NamingEnumeration results = ctx.search( rdn, "(objectClass=*)", new SearchControls() );
+        while ( results.hasMore() )
+        {
+            SearchResult result = ( SearchResult ) results.next();
+            Name childRdn = new LdapDN( result.getName() );
+            childRdn.remove( 0 );
+            deleteSubtree( ctx, childRdn );
+        }
+        ctx.destroySubcontext( rdn );
+        log.info( "Deleted: " + rdn );
+    }
+}
diff --git a/old_trunk/server-unit/src/test/java/org/apache/directory/server/DelITest.java b/old_trunk/server-unit/src/test/java/org/apache/directory/server/DelITest.java
new file mode 100644
index 0000000..c26bbf6
--- /dev/null
+++ b/old_trunk/server-unit/src/test/java/org/apache/directory/server/DelITest.java
@@ -0,0 +1,88 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server;
+
+
+import netscape.ldap.LDAPConnection;
+import netscape.ldap.LDAPException;
+
+import org.apache.directory.server.unit.AbstractServerTest;
+
+
+/**
+ * Various del scenario tests.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 433031 $
+ */
+public class DelITest extends AbstractServerTest
+{
+    static final String HOST = "localhost";
+    static final String USER = "uid=admin,ou=system";
+    static final String PASSWORD = "secret";
+    static final String BASE = "dc=example,dc=com";
+    
+    private LDAPConnection con = null;
+
+    protected void setUp() throws LDAPException, Exception {
+        super.setUp();
+        
+        con = new LDAPConnection();
+        con.connect(3, HOST, port, USER, PASSWORD);
+    }
+
+    protected void tearDown() throws LDAPException, Exception {
+        con.disconnect();
+        super.tearDown();
+    }
+
+    /**
+     * Try to delete a non existing entry. Expected result code is 32
+     * (NO_SUCH_OBJECT).
+     */
+    public void testDeleteNotExisting() 
+    {
+        nbTests = 2;
+        
+        try 
+        {
+            con.delete("cn=This does not exist" + "," + BASE);
+            fail("deletion should fail");
+        } 
+        catch (LDAPException e) 
+        {
+            assertTrue(e.getLDAPResultCode() == LDAPException.NO_SUCH_OBJECT);
+        }
+    }
+
+    /**
+     * Try to delete an entry with invalid DN. Expected result code is 32
+     * (NO_SUCH_OBJECT) or 34 (INVALID_DN_SYNTAX).
+     */
+    public void testDeleteWithIllegalName() {
+        try {
+            con.delete("This is an illegal name" + "," + BASE);
+            fail("deletion should fail");
+        } catch (LDAPException e) {
+            assertTrue(e.getLDAPResultCode() == LDAPException.INVALID_DN_SYNTAX
+                    || e.getLDAPResultCode() == LDAPException.NO_SUCH_OBJECT);
+        }
+    }
+}
diff --git a/old_trunk/server-unit/src/test/java/org/apache/directory/server/DeleteIllegalDNITest.java b/old_trunk/server-unit/src/test/java/org/apache/directory/server/DeleteIllegalDNITest.java
new file mode 100644
index 0000000..dcba1fc
--- /dev/null
+++ b/old_trunk/server-unit/src/test/java/org/apache/directory/server/DeleteIllegalDNITest.java
@@ -0,0 +1,90 @@
+/*
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *   or more contributor license agreements.  See the NOTICE file
+ *   distributed with this work for additional information
+ *   regarding copyright ownership.  The ASF licenses this file
+ *   to you 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.
+ *
+ */
+
+package org.apache.directory.server;
+
+import javax.naming.InvalidNameException;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.LdapContext;
+
+import org.apache.directory.server.unit.AbstractServerTest;
+
+/**
+ * Test reading an illegal DN followed by some other operation.
+ * 
+ * This test will never complete due to
+ * <a href="https://issues.apache.org/jira/browse/DIRSERVER-942">DIRSERVER-942</a>.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class DeleteIllegalDNITest extends AbstractServerTest
+{
+    
+    public void testSearch() throws Exception
+    {
+        LdapContext ctx = getWiredContext();
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.OBJECT_SCOPE );
+        controls.setTimeLimit( 10 );
+        
+        try 
+        {
+            ctx.search( "myBadDN", "(objectClass=*)", controls );
+
+            fail(); // We should get an exception here
+        } 
+        catch ( InvalidNameException ine ) 
+        {
+            // Expected.
+        } 
+        catch ( NamingException ne )
+        {
+            fail();
+        }
+        catch( Exception e )
+        {
+            fail();
+        }
+        
+        try
+        {
+            controls = new SearchControls();
+            controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+            controls.setTimeLimit( 10 );
+            
+            NamingEnumeration<SearchResult> result = ctx.search( "ou=system", "(objectClass=*)", controls );
+
+            assertTrue( result.hasMore() ); 
+        } 
+        catch ( InvalidNameException ine ) 
+        {
+            fail();
+            // Expected.
+        } 
+        catch ( NamingException ne )
+        {
+            fail();
+        }
+    }
+}
diff --git a/old_trunk/server-unit/src/test/java/org/apache/directory/server/HelloWorldProcedure.java b/old_trunk/server-unit/src/test/java/org/apache/directory/server/HelloWorldProcedure.java
new file mode 100644
index 0000000..a37a533
--- /dev/null
+++ b/old_trunk/server-unit/src/test/java/org/apache/directory/server/HelloWorldProcedure.java
@@ -0,0 +1,34 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server;
+
+
+public class HelloWorldProcedure
+{
+    public static String sayHello()
+    {
+        return "Hello World!";
+    }
+    
+    public static String sayHelloTo( String name )
+    {
+        return "Hello " + name + "!";
+    }
+}
diff --git a/old_trunk/server-unit/src/test/java/org/apache/directory/server/IllegalLDAPVersionBindITest.java b/old_trunk/server-unit/src/test/java/org/apache/directory/server/IllegalLDAPVersionBindITest.java
new file mode 100644
index 0000000..cadb18c
--- /dev/null
+++ b/old_trunk/server-unit/src/test/java/org/apache/directory/server/IllegalLDAPVersionBindITest.java
@@ -0,0 +1,68 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server;
+
+
+import netscape.ldap.LDAPConnection;
+import netscape.ldap.LDAPException;
+
+import org.apache.directory.server.unit.AbstractServerTest;
+
+
+/**
+ * If one tries to connect with an illegal LDAP protocol version, 
+ * no error occurs but should.  This is for 
+ * <a href="http://issues.apache.org/jira/browse/DIRSERVER-632">DIRSERVER-632</a>.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: $
+ */
+public class IllegalLDAPVersionBindITest extends AbstractServerTest
+{
+    static final String HOST = "localhost";
+    static final String USER = "uid=admin,ou=system";
+    static final String PASSWORD = "secret";
+
+    private LDAPConnection con = null;
+
+
+    public void testConnectWithIllegalLDAPVersion() throws LDAPException
+    {
+        int LDAP_VERSION = 4; // illegal
+
+        try
+        {
+            con = new LDAPConnection();
+            con.connect( LDAP_VERSION, HOST, port, USER, PASSWORD );
+            fail( "try to connect with illegal version number should fail" );
+        }
+        catch ( LDAPException e )
+        {
+            assertEquals( "statuscode", LDAPException.PROTOCOL_ERROR, e.getLDAPResultCode() );
+        }
+        finally
+        {
+            if ( con.isConnected() )
+            {
+                con.disconnect();
+            }
+        }
+    }
+}
diff --git a/old_trunk/server-unit/src/test/java/org/apache/directory/server/IllegalModificationITest.java b/old_trunk/server-unit/src/test/java/org/apache/directory/server/IllegalModificationITest.java
new file mode 100644
index 0000000..d260fcc
--- /dev/null
+++ b/old_trunk/server-unit/src/test/java/org/apache/directory/server/IllegalModificationITest.java
@@ -0,0 +1,136 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server;
+
+
+import org.apache.directory.server.unit.AbstractServerTest;
+
+import netscape.ldap.LDAPAttribute;
+import netscape.ldap.LDAPAttributeSet;
+import netscape.ldap.LDAPConnection;
+import netscape.ldap.LDAPEntry;
+import netscape.ldap.LDAPException;
+import netscape.ldap.LDAPModification;
+
+
+/** 
+ * A test taken from DIRSERVER-630: If one tries to add an attribute to an 
+ * entry, and does not provide a value, it is assumed that the server does 
+ * not modify the entry. We have a situation here using Sun ONE Directory 
+ * SDK for Java, where adding a description attribute without value to a 
+ * person entry like this,
+ * <code>
+ * dn: cn=Kate Bush,dc=example,dc=com
+ * objectclass: person
+ * objectclass: top
+ * sn: Bush
+ * cn: Kate Bush
+ * </code> 
+ * does not fail (modify call does not result in an exception). Instead, a 
+ * description attribute is created within the entry. At least the new 
+ * attribute is readable with Netscape SDK (it is not visible to most UIs, 
+ * because it is invalid ...). 
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: $
+ */
+public class IllegalModificationITest extends AbstractServerTest
+{
+    static final String DN = "cn=Kate Bush,ou=system";
+    static final String USER = "uid=admin,ou=system";
+    static final String PASSWORD = "secret";
+    static final String HOST = "localhost";
+
+    private LDAPConnection con = null;
+
+
+    protected void setUp() throws Exception
+    {
+        super.setUp();
+        con = new LDAPConnection();
+        con.connect( 3, HOST, super.port, USER, PASSWORD );
+
+        // Create a person entry
+        LDAPAttributeSet attrs = new LDAPAttributeSet();
+        attrs.add( new LDAPAttribute( "sn", "Bush" ) );
+        attrs.add( new LDAPAttribute( "cn", "Kate Bush" ) );
+        LDAPAttribute oc = new LDAPAttribute( "objectClass" );
+        oc.addValue( "top" );
+        oc.addValue( "person" );
+        attrs.add( oc );
+        LDAPEntry entry = new LDAPEntry( DN, attrs );
+        con.add( entry );
+    }
+
+
+    protected void tearDown() throws Exception
+    {
+        // Remove the person entry and disconnect
+        con.delete( DN );
+        con.disconnect();
+        super.tearDown();
+    }
+
+
+    public void testIllegalModification() throws LDAPException
+    {
+        LDAPAttribute attr = new LDAPAttribute( "description" );
+        LDAPModification mod = new LDAPModification( LDAPModification.ADD, attr );
+
+        try
+        {
+            con.modify( "cn=Kate Bush,ou=system", mod );
+            fail( "error expected due to empty attribute value" );
+        }
+        catch ( LDAPException e )
+        {
+            // expected
+        }
+
+        // Check whether entry is unmodified, i.e. no description
+        LDAPEntry entry = con.read( DN );
+        assertEquals( "description exists?", null, entry.getAttribute( "description" ) );
+    }
+    
+    
+    public void testIllegalModification2() throws LDAPException
+    {
+        // first a valid attribute
+        LDAPAttribute attr = new LDAPAttribute( "description", "The description" );
+        LDAPModification mod = new LDAPModification( LDAPModification.ADD, attr );
+        // then an invalid one without any value
+        attr = new LDAPAttribute( "displayName" );
+        LDAPModification mod2 = new LDAPModification( LDAPModification.ADD, attr );
+
+        try
+        {
+            con.modify( "cn=Kate Bush,ou=system", new LDAPModification[] { mod, mod2 } );
+            fail( "error expected due to empty attribute value" );
+        }
+        catch ( LDAPException e )
+        {
+            // expected
+        }
+
+        // Check whether entry is unmodified, i.e. no displayName
+        LDAPEntry entry = con.read( DN );
+        assertEquals( "displayName exists?", null, entry.getAttribute( "displayName" ) );
+    }
+}
diff --git a/old_trunk/server-unit/src/test/java/org/apache/directory/server/KeyDerivationServiceITest.java b/old_trunk/server-unit/src/test/java/org/apache/directory/server/KeyDerivationServiceITest.java
new file mode 100644
index 0000000..d374f7f
--- /dev/null
+++ b/old_trunk/server-unit/src/test/java/org/apache/directory/server/KeyDerivationServiceITest.java
@@ -0,0 +1,667 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server;
+
+
+import org.apache.directory.server.core.entry.DefaultServerEntry;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.interceptor.Interceptor;
+import org.apache.directory.server.core.kerberos.KeyDerivationInterceptor;
+import org.apache.directory.server.core.partition.Partition;
+import org.apache.directory.server.core.partition.impl.btree.Index;
+import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmIndex;
+import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmPartition;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
+import org.apache.directory.server.kerberos.shared.io.decoder.EncryptionKeyDecoder;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+import org.apache.directory.server.kerberos.shared.store.KerberosAttribute;
+import org.apache.directory.server.unit.AbstractServerTest;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.message.ModificationItemImpl;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+import javax.crypto.spec.DESKeySpec;
+import javax.naming.Context;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.InitialDirContext;
+import java.io.IOException;
+import java.security.InvalidKeyException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+
+/**
+ * An {@link AbstractServerTest} testing the (@link {@link KeyDerivationInterceptor}'s
+ * ability to derive Kerberos symmetric keys based on userPassword and principal
+ * name and to generate random keys when the special keyword "randomKey" is used.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class KeyDerivationServiceITest extends AbstractServerTest
+{
+    private static final String RDN = "uid=hnelson,ou=users,dc=example,dc=com";
+
+    private DirContext ctx;
+
+
+    /**
+     * Set up a partition for EXAMPLE.COM, add the Key Derivation interceptor, enable
+     * the krb5kdc schema, and add a user principal to test authentication with.
+     */
+    public void setUp() throws Exception
+    {
+        super.setUp();
+
+        Attributes attrs;
+
+
+        setContexts( "uid=admin,ou=system", "secret" );
+
+        // -------------------------------------------------------------------
+        // Enable the krb5kdc schema
+        // -------------------------------------------------------------------
+
+        // check if krb5kdc is disabled
+        Attributes krb5kdcAttrs = schemaRoot.getAttributes( "cn=Krb5kdc" );
+        boolean isKrb5KdcDisabled = false;
+        
+        if ( krb5kdcAttrs.get( "m-disabled" ) != null )
+        {
+            isKrb5KdcDisabled = ( ( String ) krb5kdcAttrs.get( "m-disabled" ).get() ).equalsIgnoreCase( "TRUE" );
+        }
+
+        // if krb5kdc is disabled then enable it
+        if ( isKrb5KdcDisabled )
+        {
+            Attribute disabled = new AttributeImpl( "m-disabled" );
+            ModificationItemImpl[] mods = new ModificationItemImpl[]
+                { new ModificationItemImpl( DirContext.REMOVE_ATTRIBUTE, disabled ) };
+            schemaRoot.modifyAttributes( "cn=Krb5kdc", mods );
+        }
+
+        ctx =   directoryService.getJndiContext( "dc=example,dc=com" );
+
+        attrs = getOrgUnitAttributes( "users" );
+        DirContext users = ctx.createSubcontext( "ou=users", attrs );
+
+        attrs = getPersonAttributes( "Nelson", "Horatio Nelson", "hnelson", "secret", "hnelson@EXAMPLE.COM" );
+        users.createSubcontext( "uid=hnelson", attrs );
+    }
+
+    protected void configureDirectoryService() throws NamingException
+    {
+        ServerEntry serverEntry;
+        Set<Partition> partitions = new HashSet<Partition>();
+
+        JdbmPartition partition;
+
+        // Add partition 'example'
+        partition = new JdbmPartition();
+        partition.setId( "example" );
+        partition.setSuffix( "dc=example,dc=com" );
+
+        Set<Index> indexedAttrs = new HashSet<Index>();
+        indexedAttrs.add( new JdbmIndex( "ou" ) );
+        indexedAttrs.add( new JdbmIndex( "dc" ) );
+        indexedAttrs.add( new JdbmIndex( "objectClass" ) );
+        partition.setIndexedAttributes( indexedAttrs );
+
+        LdapDN exampleDn = new LdapDN( "dc=example,dc=com" );
+        serverEntry = new DefaultServerEntry( directoryService.getRegistries(), exampleDn );
+        serverEntry.put( "objectClass", "top", "domain" );
+        serverEntry.put( "dc", "example" );
+        partition.setContextEntry( serverEntry );
+
+        partitions.add( partition );
+        directoryService.setPartitions( partitions );
+
+        List<Interceptor> list = directoryService.getInterceptors();
+
+        list.add( new KeyDerivationInterceptor() );
+        directoryService.setInterceptors( list );
+    }
+
+
+    /**
+     * Tests that the addition of an entry caused keys to be derived and added.
+     * 
+     * @throws NamingException failure to perform LDAP operations
+     * @throws IOException on network errors
+     */
+    public void testAddDerivedKeys() throws NamingException, IOException
+    {
+        Hashtable<String, String> env = new Hashtable<String, String>();
+        env.put( Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory" );
+        env.put( Context.PROVIDER_URL, "ldap://localhost:" + port );
+
+        env.put( Context.SECURITY_AUTHENTICATION, "simple" );
+        env.put( Context.SECURITY_PRINCIPAL, "uid=hnelson,ou=users,dc=example,dc=com" );
+        env.put( Context.SECURITY_CREDENTIALS, "secret" );
+        env.put( "java.naming.ldap.attributes.binary", "krb5key" );
+
+        DirContext ctx = new InitialDirContext( env );
+
+        String[] attrIDs =
+            { "uid", "userPassword", KerberosAttribute.KRB5_KEY_AT, KerberosAttribute.KRB5_KEY_VERSION_NUMBER_AT };
+
+        Attributes attributes = ctx.getAttributes( RDN, attrIDs );
+
+        String uid = null;
+
+        if ( attributes.get( "uid" ) != null )
+        {
+            uid = ( String ) attributes.get( "uid" ).get();
+        }
+
+        assertEquals( uid, "hnelson" );
+
+        byte[] userPassword = null;
+
+        if ( attributes.get( "userPassword" ) != null )
+        {
+            userPassword = ( byte[] ) attributes.get( "userPassword" ).get();
+        }
+
+        // Could be 4 or 5 depending on whether AES-256 is enabled or not.
+        assertTrue( "Number of keys", attributes.get( "krb5key" ).size() > 3 );
+
+        byte[] testPasswordBytes =
+            { ( byte ) 0x73, ( byte ) 0x65, ( byte ) 0x63, ( byte ) 0x72, ( byte ) 0x65, ( byte ) 0x74 };
+        assertTrue( Arrays.equals( userPassword, testPasswordBytes ) );
+
+        Attribute krb5key = attributes.get( KerberosAttribute.KRB5_KEY_AT );
+        Map<EncryptionType, EncryptionKey> map = reconstituteKeyMap( krb5key );
+        EncryptionKey encryptionKey = map.get( EncryptionType.DES_CBC_MD5 );
+
+        byte[] testKeyBytes =
+            { ( byte ) 0xF4, ( byte ) 0xA7, ( byte ) 0x13, ( byte ) 0x64, ( byte ) 0x8A, ( byte ) 0x61, ( byte ) 0xCE,
+                ( byte ) 0x5B };
+
+        assertTrue( Arrays.equals( encryptionKey.getKeyValue(), testKeyBytes ) );
+        assertEquals( EncryptionType.DES_CBC_MD5, encryptionKey.getKeyType() );
+
+        int keyVersionNumber = -1;
+
+        if ( attributes.get( KerberosAttribute.KRB5_KEY_VERSION_NUMBER_AT ) != null )
+        {
+            keyVersionNumber = Integer.valueOf( ( String ) attributes.get( KerberosAttribute.KRB5_KEY_VERSION_NUMBER_AT ).get() );
+        }
+
+        assertEquals( "Key version number", 0, keyVersionNumber );
+    }
+
+
+    /**
+     * Tests that the modification of an entry caused keys to be derived and modified.  The
+     * modify request contains both the 'userPassword' and the 'krb5PrincipalName'.
+     * 
+     * @throws NamingException failure to perform LDAP operations
+     * @throws IOException on network errors
+     */
+    public void testModifyDerivedKeys() throws NamingException, IOException
+    {
+        Hashtable<String, String> env = new Hashtable<String, String>();
+        env.put( Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory" );
+        env.put( Context.PROVIDER_URL, "ldap://localhost:" + port );
+
+        env.put( Context.SECURITY_AUTHENTICATION, "simple" );
+        env.put( Context.SECURITY_PRINCIPAL, "uid=hnelson,ou=users,dc=example,dc=com" );
+        env.put( Context.SECURITY_CREDENTIALS, "secret" );
+        env.put( "java.naming.ldap.attributes.binary", "krb5key" );
+
+        DirContext ctx = new InitialDirContext( env );
+
+        String newPrincipalName = "hnelson@EXAMPLE.COM";
+        String newUserPassword = "secretsecret";
+
+        // Modify password.
+        Attributes attributes = new AttributesImpl( true );
+        Attribute attr = new AttributeImpl( "userPassword", newUserPassword );
+        attributes.put( attr );
+        attr = new AttributeImpl( KerberosAttribute.KRB5_PRINCIPAL_NAME_AT, newPrincipalName );
+        attributes.put( attr );
+
+        DirContext person = ( DirContext ) ctx.lookup( RDN );
+        person.modifyAttributes( "", DirContext.REPLACE_ATTRIBUTE, attributes );
+
+        // Read again from directory.
+        person = ( DirContext ) ctx.lookup( RDN );
+
+        attributes = person.getAttributes( "" );
+
+        byte[] userPassword = null;
+
+        if ( attributes.get( "userPassword" ) != null )
+        {
+            userPassword = ( byte[] ) attributes.get( "userPassword" ).get();
+        }
+
+        // Could be 4 or 5 depending on whether AES-256 is enabled or not.
+        assertTrue( "Number of keys", attributes.get( "krb5key" ).size() > 3 );
+
+        byte[] testBytes =
+            { 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74 };
+        assertTrue( Arrays.equals( userPassword, testBytes ) );
+
+        Attribute krb5key = attributes.get( "krb5key" );
+        Map<EncryptionType, EncryptionKey> map = reconstituteKeyMap( krb5key );
+        EncryptionKey encryptionKey = map.get( EncryptionType.DES_CBC_MD5 );
+
+        byte[] testKeyBytes =
+            { ( byte ) 0x16, ( byte ) 0x4A, ( byte ) 0x6D, ( byte ) 0x89, ( byte ) 0x5D, ( byte ) 0x76, ( byte ) 0x0E,
+                ( byte ) 0x23 };
+
+        assertTrue( Arrays.equals( encryptionKey.getKeyValue(), testKeyBytes ) );
+        assertEquals( EncryptionType.DES_CBC_MD5, encryptionKey.getKeyType() );
+
+        int keyVersionNumber = -1;
+
+        if ( attributes.get( KerberosAttribute.KRB5_KEY_VERSION_NUMBER_AT ) != null )
+        {
+            keyVersionNumber = Integer.valueOf( ( String ) attributes.get( KerberosAttribute.KRB5_KEY_VERSION_NUMBER_AT ).get() );
+        }
+
+        assertEquals( "Key version number", 1, keyVersionNumber );
+
+        newUserPassword = "secretsecretsecret";
+
+        // Modify password.
+        attributes = new AttributesImpl( true );
+        attr = new AttributeImpl( "userPassword", newUserPassword );
+        attributes.put( attr );
+        attr = new AttributeImpl( KerberosAttribute.KRB5_PRINCIPAL_NAME_AT, newPrincipalName );
+        attributes.put( attr );
+
+        person = ( DirContext ) ctx.lookup( RDN );
+        person.modifyAttributes( "", DirContext.REPLACE_ATTRIBUTE, attributes );
+
+        // Read again from directory.
+        person = ( DirContext ) ctx.lookup( RDN );
+
+        attributes = person.getAttributes( "" );
+
+        if ( attributes.get( "userPassword" ) != null )
+        {
+            userPassword = ( byte[] ) attributes.get( "userPassword" ).get();
+        }
+
+        assertEquals( "password length", 18, userPassword.length );
+
+        if ( attributes.get( KerberosAttribute.KRB5_KEY_VERSION_NUMBER_AT ) != null )
+        {
+            keyVersionNumber = Integer.valueOf( ( String ) attributes.get( KerberosAttribute.KRB5_KEY_VERSION_NUMBER_AT ).get() );
+        }
+
+        assertEquals( "Key version number", 2, keyVersionNumber );
+
+        newUserPassword = "secretsecretsecretsecret";
+
+        // Modify password.
+        attributes = new AttributesImpl( true );
+        attr = new AttributeImpl( "userPassword", newUserPassword );
+        attributes.put( attr );
+        attr = new AttributeImpl( KerberosAttribute.KRB5_PRINCIPAL_NAME_AT, newPrincipalName );
+        attributes.put( attr );
+
+        person = ( DirContext ) ctx.lookup( RDN );
+        person.modifyAttributes( "", DirContext.REPLACE_ATTRIBUTE, attributes );
+
+        // Read again from directory.
+        person = ( DirContext ) ctx.lookup( RDN );
+
+        attributes = person.getAttributes( "" );
+
+        if ( attributes.get( "userPassword" ) != null )
+        {
+            userPassword = ( byte[] ) attributes.get( "userPassword" ).get();
+        }
+
+        assertEquals( "password length", 24, userPassword.length );
+
+        if ( attributes.get( KerberosAttribute.KRB5_KEY_VERSION_NUMBER_AT ) != null )
+        {
+            keyVersionNumber = Integer.valueOf( ( String ) attributes.get( KerberosAttribute.KRB5_KEY_VERSION_NUMBER_AT ).get() );
+        }
+
+        assertEquals( "Key version number", 3, keyVersionNumber );
+    }
+
+
+    /**
+     * Tests that the modification of an entry caused keys to be derived and modified.  The
+     * modify request contains only the 'userPassword'.  The 'krb5PrincipalName' is to be
+     * obtained from the initial add of the user principal entry.
+     * 
+     * @throws NamingException failure to perform LDAP operations
+     * @throws IOException on network errors
+     */
+    public void testModifyDerivedKeysWithoutPrincipalName() throws NamingException, IOException
+    {
+        Hashtable<String, String> env = new Hashtable<String, String>();
+        env.put( Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory" );
+        env.put( Context.PROVIDER_URL, "ldap://localhost:" + port );
+
+        env.put( Context.SECURITY_AUTHENTICATION, "simple" );
+        env.put( Context.SECURITY_PRINCIPAL, "uid=hnelson,ou=users,dc=example,dc=com" );
+        env.put( Context.SECURITY_CREDENTIALS, "secret" );
+        env.put( "java.naming.ldap.attributes.binary", "krb5key" );
+
+        DirContext ctx = new InitialDirContext( env );
+
+        String newUserPassword = "secretsecret";
+
+        // Modify password.
+        Attributes attributes = new AttributesImpl( true );
+        Attribute attr = new AttributeImpl( "userPassword", newUserPassword );
+        attributes.put( attr );
+
+        DirContext person = ( DirContext ) ctx.lookup( RDN );
+        person.modifyAttributes( "", DirContext.REPLACE_ATTRIBUTE, attributes );
+
+        // Read again from directory.
+        person = ( DirContext ) ctx.lookup( RDN );
+
+        attributes = person.getAttributes( "" );
+
+        byte[] userPassword = null;
+
+        if ( attributes.get( "userPassword" ) != null )
+        {
+            userPassword = ( byte[] ) attributes.get( "userPassword" ).get();
+        }
+
+        // Could be 4 or 5 depending on whether AES-256 is enabled or not.
+        assertTrue( "Number of keys", attributes.get( "krb5key" ).size() > 3 );
+
+        byte[] testBytes =
+            { 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74 };
+        assertTrue( Arrays.equals( userPassword, testBytes ) );
+
+        Attribute krb5key = attributes.get( "krb5key" );
+        Map<EncryptionType, EncryptionKey> map = reconstituteKeyMap( krb5key );
+        EncryptionKey encryptionKey = map.get( EncryptionType.DES_CBC_MD5 );
+
+        byte[] testKeyBytes =
+            { ( byte ) 0x16, ( byte ) 0x4A, ( byte ) 0x6D, ( byte ) 0x89, ( byte ) 0x5D, ( byte ) 0x76, ( byte ) 0x0E,
+                ( byte ) 0x23 };
+
+        assertTrue( Arrays.equals( encryptionKey.getKeyValue(), testKeyBytes ) );
+        assertEquals( EncryptionType.DES_CBC_MD5, encryptionKey.getKeyType() );
+
+        int keyVersionNumber = -1;
+
+        if ( attributes.get( KerberosAttribute.KRB5_KEY_VERSION_NUMBER_AT ) != null )
+        {
+            keyVersionNumber = Integer.valueOf( ( String ) attributes.get( KerberosAttribute.KRB5_KEY_VERSION_NUMBER_AT ).get() );
+        }
+
+        assertEquals( "Key version number", 1, keyVersionNumber );
+
+        newUserPassword = "secretsecretsecret";
+
+        // Modify password.
+        attributes = new AttributesImpl( true );
+        attr = new AttributeImpl( "userPassword", newUserPassword );
+        attributes.put( attr );
+
+        person = ( DirContext ) ctx.lookup( RDN );
+        person.modifyAttributes( "", DirContext.REPLACE_ATTRIBUTE, attributes );
+
+        // Read again from directory.
+        person = ( DirContext ) ctx.lookup( RDN );
+
+        attributes = person.getAttributes( "" );
+
+        if ( attributes.get( "userPassword" ) != null )
+        {
+            userPassword = ( byte[] ) attributes.get( "userPassword" ).get();
+        }
+
+        assertEquals( "password length", 18, userPassword.length );
+
+        if ( attributes.get( KerberosAttribute.KRB5_KEY_VERSION_NUMBER_AT ) != null )
+        {
+            keyVersionNumber = Integer.valueOf( ( String ) attributes.get( KerberosAttribute.KRB5_KEY_VERSION_NUMBER_AT ).get() );
+        }
+
+        assertEquals( "Key version number", 2, keyVersionNumber );
+
+        newUserPassword = "secretsecretsecretsecret";
+
+        // Modify password.
+        attributes = new AttributesImpl( true );
+        attr = new AttributeImpl( "userPassword", newUserPassword );
+        attributes.put( attr );
+
+        person = ( DirContext ) ctx.lookup( RDN );
+        person.modifyAttributes( "", DirContext.REPLACE_ATTRIBUTE, attributes );
+
+        // Read again from directory.
+        person = ( DirContext ) ctx.lookup( RDN );
+
+        attributes = person.getAttributes( "" );
+
+        if ( attributes.get( "userPassword" ) != null )
+        {
+            userPassword = ( byte[] ) attributes.get( "userPassword" ).get();
+        }
+
+        assertEquals( "password length", 24, userPassword.length );
+
+        if ( attributes.get( KerberosAttribute.KRB5_KEY_VERSION_NUMBER_AT ) != null )
+        {
+            keyVersionNumber = Integer.valueOf( ( String ) attributes.get( KerberosAttribute.KRB5_KEY_VERSION_NUMBER_AT ).get() );
+        }
+
+        assertEquals( "Key version number", 3, keyVersionNumber );
+    }
+
+
+    /**
+     * Tests that the addition of an entry caused random keys to be derived and added.
+     * 
+     * @throws NamingException failure to perform LDAP operations
+     * @throws IOException on network errors
+     * @throws InvalidKeyException if the incorrect key results
+     */
+    public void testAddRandomKeys() throws NamingException, IOException, InvalidKeyException
+    {
+        Hashtable<String, String> env = new Hashtable<String, String>();
+        env.put( "java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory" );
+        env.put( "java.naming.provider.url", "ldap://localhost:" + port + "/ou=users,dc=example,dc=com" );
+        env.put( "java.naming.security.principal", "uid=admin,ou=system" );
+        env.put( "java.naming.security.credentials", "secret" );
+        env.put( "java.naming.security.authentication", "simple" );
+        env.put( "java.naming.ldap.attributes.binary", "krb5key" );
+        ctx = new InitialDirContext( env );
+
+        Attributes attrs = getPersonAttributes( "Quist", "Thomas Quist", "tquist", "randomKey", "tquist@EXAMPLE.COM" );
+        ctx.createSubcontext( "uid=tquist", attrs );
+
+        attrs = getPersonAttributes( "Fryer", "John Fryer", "jfryer", "randomKey", "jfryer@EXAMPLE.COM" );
+        ctx.createSubcontext( "uid=jfryer", attrs );
+
+        String[] attrIDs =
+            { "uid", "userPassword", "krb5Key" };
+
+        Attributes tquistAttrs = ctx.getAttributes( "uid=tquist", attrIDs );
+        Attributes jfryerAttrs = ctx.getAttributes( "uid=jfryer", attrIDs );
+
+        String uid = null;
+        byte[] userPassword = null;
+
+        if ( tquistAttrs.get( "uid" ) != null )
+        {
+            uid = ( String ) tquistAttrs.get( "uid" ).get();
+        }
+
+        assertEquals( "tquist", uid );
+
+        if ( tquistAttrs.get( "userPassword" ) != null )
+        {
+            userPassword = ( byte[] ) tquistAttrs.get( "userPassword" ).get();
+        }
+
+        // Bytes for "randomKey."
+        byte[] testPasswordBytes =
+            { ( byte ) 0x72, ( byte ) 0x61, ( byte ) 0x6E, ( byte ) 0x64, ( byte ) 0x6F, ( byte ) 0x6D, ( byte ) 0x4B,
+                ( byte ) 0x65, ( byte ) 0x79 };
+        assertTrue( Arrays.equals( testPasswordBytes, userPassword ) );
+
+        if ( jfryerAttrs.get( "uid" ) != null )
+        {
+            uid = ( String ) jfryerAttrs.get( "uid" ).get();
+        }
+
+        assertEquals( "jfryer", uid );
+
+        if ( jfryerAttrs.get( "userPassword" ) != null )
+        {
+            userPassword = ( byte[] ) jfryerAttrs.get( "userPassword" ).get();
+        }
+
+        assertTrue( Arrays.equals( testPasswordBytes, userPassword ) );
+
+        byte[] testKeyBytes =
+            { ( byte ) 0xF4, ( byte ) 0xA7, ( byte ) 0x13, ( byte ) 0x64, ( byte ) 0x8A, ( byte ) 0x61, ( byte ) 0xCE,
+                ( byte ) 0x5B };
+
+        Attribute krb5key = tquistAttrs.get( "krb5key" );
+        Map<EncryptionType, EncryptionKey> map = reconstituteKeyMap( krb5key );
+        EncryptionKey encryptionKey = map.get( EncryptionType.DES_CBC_MD5 );
+        byte[] tquistKey = encryptionKey.getKeyValue();
+
+        assertEquals( EncryptionType.DES_CBC_MD5, encryptionKey.getKeyType() );
+
+        krb5key = jfryerAttrs.get( "krb5key" );
+        map = reconstituteKeyMap( krb5key );
+        encryptionKey = map.get( EncryptionType.DES_CBC_MD5 );
+        byte[] jfryerKey = encryptionKey.getKeyValue();
+
+        assertEquals( EncryptionType.DES_CBC_MD5, encryptionKey.getKeyType() );
+
+        assertEquals( "Key length", 8, tquistKey.length );
+        assertEquals( "Key length", 8, jfryerKey.length );
+
+        assertFalse( Arrays.equals( testKeyBytes, tquistKey ) );
+        assertFalse( Arrays.equals( testKeyBytes, jfryerKey ) );
+        assertFalse( Arrays.equals( jfryerKey, tquistKey ) );
+
+        byte[] tquistDerivedKey =
+            { ( byte ) 0xFD, ( byte ) 0x7F, ( byte ) 0x6B, ( byte ) 0x83, ( byte ) 0xA4, ( byte ) 0x76, ( byte ) 0xC1,
+                ( byte ) 0xEA };
+        byte[] jfryerDerivedKey =
+            { ( byte ) 0xA4, ( byte ) 0x10, ( byte ) 0x3B, ( byte ) 0x49, ( byte ) 0xCE, ( byte ) 0x0B, ( byte ) 0xB5,
+                ( byte ) 0x07 };
+
+        assertFalse( Arrays.equals( tquistDerivedKey, tquistKey ) );
+        assertFalse( Arrays.equals( jfryerDerivedKey, jfryerKey ) );
+
+        assertTrue( DESKeySpec.isParityAdjusted( tquistKey, 0 ) );
+        assertTrue( DESKeySpec.isParityAdjusted( jfryerKey, 0 ) );
+    }
+
+
+    /**
+     * Tear down.
+     */
+    public void tearDown() throws Exception
+    {
+        ctx.close();
+        ctx = null;
+        super.tearDown();
+    }
+
+
+    /**
+     * Convenience method for creating a person.
+     *
+     * @param cn the commonName of the person
+     * @param sn the surName of the person
+     * @param uid the unique id of the person
+     * @param userPassword the password of the person
+     * @param principal the kerberos principal name for the person
+     * @return the attributes of the person entry
+     */
+    protected Attributes getPersonAttributes( String sn, String cn, String uid, String userPassword, String principal )
+    {
+        Attributes attrs = new AttributesImpl();
+        Attribute ocls = new AttributeImpl( "objectClass" );
+        ocls.add( "top" );
+        ocls.add( "person" ); // sn $ cn
+        ocls.add( "inetOrgPerson" ); // uid
+        ocls.add( "krb5principal" );
+        ocls.add( "krb5kdcentry" );
+        attrs.put( ocls );
+        attrs.put( "cn", cn );
+        attrs.put( "sn", sn );
+        attrs.put( "uid", uid );
+        attrs.put( "userPassword", userPassword );
+        attrs.put( KerberosAttribute.KRB5_PRINCIPAL_NAME_AT, principal );
+        attrs.put( KerberosAttribute.KRB5_KEY_VERSION_NUMBER_AT, "0" );
+
+        return attrs;
+    }
+
+
+    /**
+     * Convenience method for creating an organizational unit.
+     *
+     * @param ou the organizational unit to create
+     * @return the attributes of the organizationalUnit
+     */
+    protected Attributes getOrgUnitAttributes( String ou )
+    {
+        Attributes attrs = new AttributesImpl();
+        Attribute ocls = new AttributeImpl( "objectClass" );
+        ocls.add( "top" );
+        ocls.add( "organizationalUnit" );
+        attrs.put( ocls );
+        attrs.put( "ou", ou );
+
+        return attrs;
+    }
+
+
+    private Map<EncryptionType, EncryptionKey> reconstituteKeyMap( Attribute krb5key ) throws NamingException,
+        IOException
+    {
+        Map<EncryptionType, EncryptionKey> map = new HashMap<EncryptionType, EncryptionKey>();
+
+        for ( int ii = 0; ii < krb5key.size(); ii++ )
+        {
+            byte[] encryptionKeyBytes = ( byte[] ) krb5key.get( ii );
+            EncryptionKey encryptionKey = EncryptionKeyDecoder.decode( encryptionKeyBytes );
+            map.put( encryptionKey.getKeyType(), encryptionKey );
+        }
+
+        return map;
+    }
+}
diff --git a/old_trunk/server-unit/src/test/java/org/apache/directory/server/ManyServersITest.java b/old_trunk/server-unit/src/test/java/org/apache/directory/server/ManyServersITest.java
new file mode 100644
index 0000000..247c907
--- /dev/null
+++ b/old_trunk/server-unit/src/test/java/org/apache/directory/server/ManyServersITest.java
@@ -0,0 +1,221 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server;
+
+
+import javax.naming.Context;
+import javax.naming.ldap.InitialLdapContext;
+import javax.naming.ldap.LdapContext;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.directory.server.core.DefaultDirectoryService;
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.ldap.LdapServer;
+import org.apache.directory.server.protocol.shared.SocketAcceptor;
+import org.apache.mina.util.AvailablePortFinder;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Hashtable;
+
+import junit.framework.TestCase;
+
+
+/**
+ * Various add scenario tests.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class ManyServersITest extends TestCase
+{
+    private static final String CTX_FACTORY = "com.sun.jndi.ldap.LdapCtxFactory";
+
+    protected DirectoryService directoryService0;
+    protected SocketAcceptor socketAcceptor0;
+    protected LdapServer ldapServer0;
+    protected int port0;
+    
+    protected DirectoryService directoryService1;
+    protected SocketAcceptor socketAcceptor1;
+    protected LdapServer ldapServer1;
+    protected int port1;
+    
+    
+    /**
+     * Starts two separate LdapServers.
+     */
+    protected void setUp() throws Exception
+    {
+        super.setUp();
+        
+        /*
+         * Start the first LDAP server.
+         */
+        
+        directoryService0 = new DefaultDirectoryService();
+        directoryService0.setShutdownHookEnabled( false );
+        socketAcceptor0 = new SocketAcceptor( null );
+        ldapServer0 = new LdapServer();
+        ldapServer0.setSocketAcceptor( socketAcceptor0 );
+        ldapServer0.setDirectoryService( directoryService0 );
+        ldapServer0.setIpPort( port0 = AvailablePortFinder.getNextAvailable( 1024 ) );
+
+        doDelete( directoryService0.getWorkingDirectory() );
+        directoryService0.startup();
+
+        ldapServer0.start();
+
+        /*
+         * Start the second LDAP server.
+         */
+        
+        directoryService1 = new DefaultDirectoryService();
+        directoryService1.setShutdownHookEnabled( false );
+        socketAcceptor1 = new SocketAcceptor( null );
+        ldapServer1 = new LdapServer();
+        ldapServer1.setSocketAcceptor( socketAcceptor1 );
+        ldapServer1.setDirectoryService( directoryService1 );
+        ldapServer1.setIpPort( port1 = AvailablePortFinder.getNextAvailable( 1024 ) );
+
+        doDelete( directoryService1.getWorkingDirectory() );
+        directoryService1.startup();
+
+        ldapServer1.start();
+    }
+
+
+    /**
+     * Sets the system context root to null.
+     *
+     * @see junit.framework.TestCase#tearDown()
+     */
+    protected void tearDown() throws Exception
+    {
+        super.tearDown();
+        
+        try
+        {
+            ldapServer0.stop();
+        }
+        catch ( Exception e )
+        {
+            e.printStackTrace();
+        }
+
+        try
+        {
+            directoryService0.shutdown();
+        }
+        catch ( Exception e )
+        {
+            e.printStackTrace();
+        }
+
+        try
+        {
+            ldapServer1.stop();
+        }
+        catch ( Exception e )
+        {
+            e.printStackTrace();
+        }
+
+        try
+        {
+            directoryService1.shutdown();
+        }
+        catch ( Exception e )
+        {
+            e.printStackTrace();
+        }
+    }
+
+
+    /**
+     * Deletes the Eve working directory.
+     * @param wkdir the directory to delete
+     * @throws IOException if the directory cannot be deleted
+     */
+    protected void doDelete( File wkdir ) throws IOException
+    {
+        if ( wkdir.exists() )
+        {
+            FileUtils.deleteDirectory( wkdir );
+        }
+
+        if ( wkdir.exists() )
+        {
+            throw new IOException( "Failed to delete: " + wkdir );
+        }
+    }
+
+
+    /**
+     * Try to start two servers in the same vm: for DIRSERVER-1151.
+     * 
+     * @see https://issues.apache.org/jira/browse/DIRSERVER-1151
+     */
+    public void testBothServers() throws Exception
+    {
+        LdapContext ctx0 = null;
+        try
+        {
+            ctx0 = getLdapContext( port0 );
+            assertNotNull( ctx0 );
+            assertNotNull( ctx0.getAttributes( "" ) );
+        }
+        finally
+        {
+            if ( ctx0 != null )
+            {
+                ctx0.close();
+            }
+        }
+
+        LdapContext ctx1 = null;
+        
+        try
+        {
+            ctx1 = getLdapContext( port1 );
+            assertNotNull( ctx1 );
+            assertNotNull( ctx1.getAttributes( "" ) );
+        }
+        finally
+        {
+            if ( ctx1 != null )
+            {
+                ctx1.close();
+            }
+        }
+    }
+    
+    
+    public LdapContext getLdapContext( int port ) throws Exception
+    {
+        Hashtable<String, String> env = new Hashtable<String, String>();
+        env.put( Context.INITIAL_CONTEXT_FACTORY, CTX_FACTORY );
+        env.put( Context.PROVIDER_URL, "ldap://localhost:" + port );
+        env.put( Context.SECURITY_PRINCIPAL, "uid=admin,ou=system" );
+        env.put( Context.SECURITY_CREDENTIALS, "secret" );
+        env.put( Context.SECURITY_AUTHENTICATION, "simple" );
+        return new InitialLdapContext( env, null );
+    }
+}
diff --git a/old_trunk/server-unit/src/test/java/org/apache/directory/server/MatchingRuleCompareTest.java b/old_trunk/server-unit/src/test/java/org/apache/directory/server/MatchingRuleCompareTest.java
new file mode 100644
index 0000000..2530a52
--- /dev/null
+++ b/old_trunk/server-unit/src/test/java/org/apache/directory/server/MatchingRuleCompareTest.java
@@ -0,0 +1,273 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server;
+
+
+import java.util.Hashtable;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.SearchControls;
+import javax.naming.ldap.InitialLdapContext;
+import javax.naming.ldap.LdapContext;
+
+import org.apache.directory.server.unit.AbstractServerTest;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+
+
+/**
+ * Tests with compare operations on attributes which use different matching
+ * rules. Created to demonstrate JIRA DIREVE-243 ("Compare operation does not
+ * adhere to some matching rules").
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class MatchingRuleCompareTest extends AbstractServerTest
+{
+    private LdapContext ctx = null;
+
+    public static final String PERSON_CN = "Tori Amos";
+    public static final String PERSON_SN = "Amos";
+    public static final String PERSON_RDN = "cn=" + PERSON_CN;
+    public static final String PERSON_TELEPHONE = "1234567890";
+    public static final String PERSON_PWD = "Secret1!";
+
+    public static final String GROUP_CN = "Artists";
+    public static final String GROUP_RDN = "cn=" + GROUP_CN;
+
+
+    protected Attributes getPersonAttributes( String sn, String cn )
+    {
+        Attributes attributes = new AttributesImpl();
+        Attribute attribute = new AttributeImpl( "objectClass" );
+        attribute.add( "top" );
+        attribute.add( "person" );
+        attributes.put( attribute );
+        attributes.put( "cn", cn );
+        attributes.put( "sn", sn );
+
+        return attributes;
+    }
+
+
+    protected Attributes getGroupOfNamesAttributes( String cn, String member )
+    {
+        Attributes attributes = new AttributesImpl();
+        Attribute attribute = new AttributeImpl( "objectClass" );
+        attribute.add( "top" );
+        attribute.add( "groupOfNames" );
+        attributes.put( attribute );
+        attributes.put( "cn", cn );
+        attributes.put( "member", member );
+
+        return attributes;
+    }
+
+
+    /**
+     * Create context, a person entry and a group.
+     */
+    public void setUp() throws Exception
+    {
+        super.setUp();
+
+        Hashtable<String, Object> env = new Hashtable<String, Object>();
+        env.put( "java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory" );
+        env.put( "java.naming.provider.url", "ldap://localhost:" + port + "/ou=system" );
+        env.put( "java.naming.security.principal", "uid=admin,ou=system" );
+        env.put( "java.naming.security.credentials", "secret" );
+        env.put( "java.naming.security.authentication", "simple" );
+
+        ctx = new InitialLdapContext( env, null );
+        assertNotNull( ctx );
+
+        // Create a person
+        Attributes attributes = this.getPersonAttributes( PERSON_SN, PERSON_CN );
+        attributes.put( "telephoneNumber", PERSON_TELEPHONE );
+        attributes.put( "userPassword", PERSON_PWD );
+        ctx.createSubcontext( PERSON_RDN, attributes );
+
+        // Create a group
+        DirContext member = ( DirContext ) ctx.lookup( PERSON_RDN );
+        attributes = this.getGroupOfNamesAttributes( GROUP_CN, member.getNameInNamespace() );
+        ctx.createSubcontext( GROUP_RDN, attributes );
+    }
+
+
+    /**
+     * Remove entries and close context.
+     */
+    public void tearDown() throws Exception
+    {
+        ctx.unbind( PERSON_RDN );
+        ctx.unbind( GROUP_RDN );
+
+        ctx.close();
+
+        super.tearDown();
+    }
+
+
+    /**
+     * Compare with caseIgnoreMatch matching rule.
+     * 
+     * @throws NamingException
+     */
+    public void testCaseIgnoreMatch() throws NamingException
+    {
+        // Setting up search controls for compare op
+        SearchControls ctls = new SearchControls();
+        ctls.setReturningAttributes( new String[]
+            {} ); // no attributes
+        ctls.setSearchScope( SearchControls.OBJECT_SCOPE );
+
+        String[] values =
+            { PERSON_SN, PERSON_SN.toUpperCase(), PERSON_SN.toLowerCase(), PERSON_SN + "X" };
+        boolean[] expected =
+            { true, true, true, false };
+
+        for ( int i = 0; i < values.length; i++ )
+        {
+            String value = values[i];
+
+            NamingEnumeration enumeration = ctx.search( PERSON_RDN, "sn={0}", new String[]
+                { value }, ctls );
+            boolean result = enumeration.hasMore();
+
+            assertEquals( "compare sn value '" + PERSON_SN + "' with '" + value + "'", expected[i], result );
+
+            enumeration.close();
+        }
+    }
+
+
+    //
+
+    /**
+     * Compare with telephoneNumberMatch matching rule.
+     * 
+     * @throws NamingException
+     */
+
+    // Comment this out until we have the telephone number match working.
+    //    public void testTelephoneNumberMatch() throws NamingException
+    //    {
+    //        // Setting up search controls for compare op
+    //        SearchControls ctls = new SearchControls();
+    //        ctls.setReturningAttributes(new String[] {}); // no attributes
+    //        ctls.setSearchScope(SearchControls.OBJECT_SCOPE);
+    //
+    //        String[] values = { "", "1234567890abc", "   1234567890 A B C", "123 456 7890 abc", "123-456-7890 abC",
+    //                "123456-7890 A bc" };
+    //        boolean[] expected = { false, true, true, true, true, true };
+    //
+    //        for (int i = 0; i < values.length; i++) {
+    //            String value = values[i];
+    //
+    //            NamingEnumeration enumeration = ctx.search(PERSON_RDN, "telephoneNumber={0}", new String[] { value }, ctls);
+    //            boolean result = enumeration.hasMore();
+    //
+    //            assertEquals("compare '" + PERSON_TELEPHONE + "' with '" + value + "'", expected[i], result);
+    //
+    //            enumeration.close();
+    //        }
+    //    }
+    /**
+     * Compare with octetStringMatch matching rule.
+     * 
+     * @throws NamingException
+     */
+
+// Cannot search the directory using binary attributes.  I don't know if this 
+// is valid according to specifications but this is the case with respect to apacheds
+// and userPassword is a binary attribute type.  This is why we get class cast 
+// exceptions for this search which fails.  To make this succeed some simple changes
+// are needed.
+    
+//    public void testOctetStringMatch() throws NamingException
+//    {
+//        // Setting up search controls for compare op
+//        SearchControls ctls = new SearchControls();
+//        ctls.setReturningAttributes( new String[]
+//            {} ); // no attributes
+//        ctls.setSearchScope( SearchControls.OBJECT_SCOPE );
+//
+//        String[] values =
+//            { "", PERSON_PWD, PERSON_PWD.toUpperCase(), PERSON_PWD.toLowerCase(), PERSON_PWD + "X" };
+//        boolean[] expected =
+//            { false, true, false, false, false };
+//
+//        for ( int i = 0; i < values.length; i++ )
+//        {
+//            String value = values[i];
+//
+//            NamingEnumeration enumeration = ctx.search( PERSON_RDN, "userPassword={0}", new String[]
+//                { value }, ctls );
+//            boolean result = enumeration.hasMore();
+//
+//            assertEquals( "compare '" + PERSON_PWD + "' with '" + value + "'", expected[i], result );
+//
+//            enumeration.close();
+//        }
+//    }
+
+
+    /**
+     * Compare with distinguishedNameMatch matching rule.
+     * 
+     * @throws NamingException
+     */
+    public void testDistinguishedNameMatch() throws NamingException
+    {
+        // determine member DN of person
+        DirContext member = ( DirContext ) ctx.lookup( PERSON_RDN );
+        String memberDN = member.getNameInNamespace();
+
+        // Setting up search controls for compare op
+        SearchControls ctls = new SearchControls();
+        ctls.setReturningAttributes( new String[]
+            {} ); // no attributes
+        ctls.setSearchScope( SearchControls.OBJECT_SCOPE );
+
+        String[] values =
+            { "", memberDN, "cn=nobody", memberDN, PERSON_RDN + " , " + ctx.getNameInNamespace() };
+        boolean[] expected =
+            { false, true, false, true, true };
+
+        for ( int i = 0; i < values.length; i++ )
+        {
+            String value = values[i];
+
+            NamingEnumeration enumeration = ctx.search( GROUP_RDN, "member={0}", new Object[]
+                { value }, ctls );
+            boolean result = enumeration.hasMore();
+
+            assertEquals( "compare '" + memberDN + "' with '" + value + "'", expected[i], result );
+
+            enumeration.close();
+        }
+    }
+
+}
diff --git a/old_trunk/server-unit/src/test/java/org/apache/directory/server/MiscTest.java b/old_trunk/server-unit/src/test/java/org/apache/directory/server/MiscTest.java
new file mode 100644
index 0000000..3a0e21a
--- /dev/null
+++ b/old_trunk/server-unit/src/test/java/org/apache/directory/server/MiscTest.java
@@ -0,0 +1,481 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server;
+
+
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Set;
+
+import javax.naming.Context;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.NoPermissionException;
+import javax.naming.OperationNotSupportedException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.InitialDirContext;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.InitialLdapContext;
+
+import netscape.ldap.LDAPAttribute;
+import netscape.ldap.LDAPConnection;
+import netscape.ldap.LDAPException;
+
+import org.apache.directory.server.core.entry.DefaultServerEntry;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.entry.ServerEntryUtils;
+import org.apache.directory.server.core.partition.Partition;
+import org.apache.directory.server.core.partition.impl.btree.Index;
+import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmIndex;
+import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmPartition;
+import org.apache.directory.server.unit.AbstractServerTest;
+import org.apache.directory.shared.asn1.util.Asn1StringUtils;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.message.MutableControl;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.util.ArrayUtils;
+import org.apache.directory.shared.ldap.util.EmptyEnumeration;
+
+
+/**
+ * A set of miscellanous tests.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class MiscTest extends AbstractServerTest
+{
+    /**
+     * Cleans up old database files on creation.
+     */
+    public MiscTest()
+    {
+    }
+
+
+    /**
+     * Customizes setup for each test case.
+     *
+     * @throws Exception
+     */
+    public void setUp() throws Exception
+    {
+        super.setUp();
+        if ( this.getName().equals( "testDisableAnonymousBinds" ) ||
+                this.getName().equals( "testCompareWithoutAuthentication" ) ||
+                this.getName().equals( "testEnableAnonymousBindsOnRootDSE" ) )
+        {
+            setAllowAnonymousAccess( false );
+        } else if ( this.getName().equals( "testAnonymousBindsEnabledBaseSearch" ) )
+        {
+            setAllowAnonymousAccess( true );
+        }
+    }
+
+
+    @Override
+    protected void configureDirectoryService() throws NamingException
+    {
+        if ( this.getName().equals( "testUserAuthOnMixedCaseSuffix" ) )
+        {
+            Set<Partition> partitions = new HashSet<Partition>();
+            partitions.addAll( directoryService.getPartitions() );
+            JdbmPartition partition = new JdbmPartition();
+            partition.setSuffix( "dc=aPache,dc=org" );
+            
+            LdapDN apacheDn = new LdapDN( "dc=aPache,dc=org" );
+            ServerEntry serverEntry = new DefaultServerEntry( directoryService.getRegistries(), apacheDn );
+            serverEntry.put( "dc", "aPache" );
+            serverEntry.put( "objectClass", "top", "domain" );
+
+            partition.setId( "apache" );
+            partition.setContextEntry( serverEntry );
+            Set<Index> indexedAttributes = new HashSet<Index>();
+            indexedAttributes.add( new JdbmIndex( "dc" ) );
+            partition.setIndexedAttributes( indexedAttributes );
+            partitions.add( partition );
+            directoryService.setPartitions( partitions );
+        } 
+        else if ( this.getName().equals( "testAnonymousBindsEnabledBaseSearch" ) )
+        {
+            // create a partition to search
+            Set partitions = new HashSet();
+            partitions.addAll( directoryService.getPartitions() );
+            JdbmPartition partition = new JdbmPartition();
+            partition.setSuffix( "dc=apache,dc=org" );
+            
+            LdapDN apacheDn = new LdapDN( "dc=apache,dc=org" );
+            ServerEntry serverEntry = new DefaultServerEntry( directoryService.getRegistries(), apacheDn );
+            serverEntry.put( "dc", "apache" );
+            serverEntry.put( "objectClass", "top", "domain" );
+            
+            partition.setId( "apache" );
+            partition.setContextEntry( serverEntry );
+            Set<Index> indexedAttributes = new HashSet<Index>();
+            indexedAttributes.add( new JdbmIndex( "dc" ) );
+            partition.setIndexedAttributes( indexedAttributes );
+            partitions.add( partition );
+            directoryService.setPartitions( partitions );
+        }
+    }
+
+    public void testCompareWithoutAuthentication() throws LDAPException
+    {
+        LDAPConnection conn = new LDAPConnection();
+        conn.connect( "localhost", super.port );
+        LDAPAttribute attr = new LDAPAttribute( "uid", "admin" );
+        try
+        {
+            conn.compare( "uid=admin,ou=system", attr );
+            fail( "Compare success without authentication" );
+        }
+        catch ( LDAPException e )
+        {
+            assertEquals( "no permission exception", 50, e.getLDAPResultCode() );
+        }
+    }
+
+
+    /**
+     * Test to make sure anonymous binds are disabled when going through
+     * the wire protocol.
+     *
+     * @throws Exception if anything goes wrong
+     */
+    public void testDisableAnonymousBinds() throws Exception
+    {
+        // Use the SUN JNDI provider to hit server port and bind as anonymous
+        InitialDirContext ic = null;
+        final Hashtable<String, Object> env = new Hashtable<String, Object>();
+
+        env.put( Context.PROVIDER_URL, "ldap://localhost:" + port + "/ou=system" );
+        env.put( Context.SECURITY_AUTHENTICATION, "none" );
+        env.put( Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory" );
+
+        boolean connected = false;
+        while ( !connected )
+        {
+            try
+            {
+                ic = new InitialDirContext( env );
+                connected = true;
+            }
+            catch ( Exception e )
+            {
+            }
+        }
+
+        try
+        {
+            ic.search( "", "(objectClass=*)", new SearchControls() );
+            fail( "If anonymous binds are disabled we should never get here!" );
+        }
+        catch ( NoPermissionException e )
+        {
+        }
+
+        Attributes attrs = new AttributesImpl( true );
+        Attribute oc = new AttributeImpl( "objectClass" );
+        attrs.put( oc );
+        oc.add( "top" );
+        oc.add( "organizationalUnit" );
+
+        try
+        {
+            ic.createSubcontext( "ou=blah", attrs );
+        }
+        catch ( NoPermissionException e )
+        {
+        }
+    }
+
+
+    /**
+     * Test to make sure anonymous binds are allowed on the RootDSE even when disabled
+     * in general when going through the wire protocol.
+     *
+     * @throws Exception if anything goes wrong
+     */
+    public void testEnableAnonymousBindsOnRootDSE() throws Exception
+    {
+        // Use the SUN JNDI provider to hit server port and bind as anonymous
+
+        final Hashtable env = new Hashtable();
+
+        env.put( Context.PROVIDER_URL, "ldap://localhost:" + port + "/" );
+        env.put( Context.SECURITY_AUTHENTICATION, "none" );
+        env.put( Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory" );
+
+        InitialDirContext ctx = new InitialDirContext( env );
+        SearchControls cons = new SearchControls();
+        cons.setSearchScope( SearchControls.OBJECT_SCOPE );
+        NamingEnumeration list = ctx.search( "", "(objectClass=*)", cons );
+        SearchResult result = null;
+        if ( list.hasMore() )
+        {
+            result = ( SearchResult ) list.next();
+        }
+        assertFalse( list.hasMore() );
+        list.close();
+
+        assertNotNull( result );
+        assertEquals( "", result.getName().trim() );
+    }
+
+
+    /**
+     * Test to make sure that if anonymous binds are allowed a user may search
+     * within a a partition.
+     *
+     * @throws Exception if anything goes wrong
+     */
+    public void testAnonymousBindsEnabledBaseSearch() throws Exception
+    {
+        // Use the SUN JNDI provider to hit server port and bind as anonymous
+
+        final Hashtable env = new Hashtable();
+
+        env.put( Context.PROVIDER_URL, "ldap://localhost:" + port + "/" );
+        env.put( Context.SECURITY_AUTHENTICATION, "none" );
+        env.put( Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory" );
+
+        InitialDirContext ctx = new InitialDirContext( env );
+        SearchControls cons = new SearchControls();
+        cons.setSearchScope( SearchControls.OBJECT_SCOPE );
+        NamingEnumeration list = ctx.search( "dc=apache,dc=org", "(objectClass=*)", cons );
+        SearchResult result = null;
+        if ( list.hasMore() )
+        {
+            result = ( SearchResult ) list.next();
+        }
+        assertFalse( list.hasMore() );
+        list.close();
+
+        assertNotNull( result );
+        assertNotNull( result.getAttributes().get( "dc" ) );
+    }
+
+
+    /**
+     * Reproduces the problem with
+     * <a href="http://issues.apache.org/jira/browse/DIREVE-239">DIREVE-239</a>.
+     *
+     * @throws Exception if anything goes wrong
+     */
+    public void testAdminAccessBug() throws Exception
+    {
+        // Use the SUN JNDI provider to hit server port and bind as anonymous
+
+        final Hashtable env = new Hashtable();
+
+        env.put( Context.PROVIDER_URL, "ldap://localhost:" + port );
+        env.put( "java.naming.ldap.version", "3" );
+        env.put( Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory" );
+
+        Attributes attributes = new AttributesImpl();
+        Attribute objectClass = new AttributeImpl( "objectClass" );
+        objectClass.add( "top" );
+        objectClass.add( "organizationalUnit" );
+        attributes.put( objectClass );
+        attributes.put( "ou", "blah" );
+        InitialDirContext ctx = new InitialDirContext( env );
+        ctx.createSubcontext( "ou=blah,ou=system", attributes );
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.OBJECT_SCOPE );
+        controls.setReturningAttributes( new String[]
+                {"+"} );
+        NamingEnumeration list = ctx.search( "ou=blah,ou=system", "(objectClass=*)", controls );
+        SearchResult result = ( SearchResult ) list.next();
+        list.close();
+        Attribute creatorsName = result.getAttributes().get( "creatorsName" );
+        assertEquals( "", creatorsName.get() );
+    }
+
+
+    /**
+     * Test case for <a href="http://issues.apache.org/jira/browse/DIREVE-284" where users in
+     * mixed case partitions were not able to authenticate properly.  This test case creates
+     * a new partition under dc=aPache,dc=org, it then creates the example user in the JIRA
+     * issue and attempts to authenticate as that user.
+     *
+     * @throws Exception if the user cannot authenticate or test fails
+     */
+    public void testUserAuthOnMixedCaseSuffix() throws Exception
+    {
+        final Hashtable env = new Hashtable();
+
+        env.put( Context.PROVIDER_URL, "ldap://localhost:" + port + "/dc=aPache,dc=org" );
+        env.put( "java.naming.ldap.version", "3" );
+        env.put( Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory" );
+        InitialDirContext ctx = new InitialDirContext( env );
+        Attributes attrs = ctx.getAttributes( "" );
+        assertTrue( attrs.get( "dc" ).get().equals( "aPache" ) );
+
+        Attributes user = new AttributesImpl( "cn", "Kate Bush", true );
+        Attribute oc = new AttributeImpl( "objectClass" );
+        oc.add( "top" );
+        oc.add( "person" );
+        oc.add( "organizationalPerson" );
+        oc.add( "inetOrgPerson" );
+        user.put( oc );
+        user.put( "sn", "Bush" );
+        user.put( "userPassword", "Aerial" );
+        ctx.createSubcontext( "cn=Kate Bush", user );
+
+        env.put( Context.SECURITY_AUTHENTICATION, "simple" );
+        env.put( Context.SECURITY_CREDENTIALS, "Aerial" );
+        env.put( Context.SECURITY_PRINCIPAL, "cn=Kate Bush,dc=aPache,dc=org" );
+
+        InitialDirContext userCtx = new InitialDirContext( env );
+        assertNotNull( userCtx );
+    }
+
+
+    /**
+     * Tests to make sure undefined attributes in filter assertions are pruned and do not
+     * result in exceptions.
+     */
+    public void testBogusAttributeInSearchFilter() throws Exception
+    {
+        SearchControls cons = new SearchControls();
+        NamingEnumeration<SearchResult> e = sysRoot.search( "", "(bogusAttribute=abc123)", cons );
+        assertNotNull( e );
+        assertEquals( e.getClass(), EmptyEnumeration.class );
+        
+        e = sysRoot.search( "", "(!(bogusAttribute=abc123))", cons );
+        assertNotNull( e );
+        assertFalse( e.hasMore() );
+        assertEquals( e.getClass(), EmptyEnumeration.class );
+        
+        e = sysRoot.search( "", "(|(bogusAttribute=abc123)(bogusAttribute=abc123))", cons );
+        assertNotNull( e );
+        assertFalse( e.hasMore() );
+        assertEquals( e.getClass(), EmptyEnumeration.class );
+        
+        e = sysRoot.search( "", "(|(bogusAttribute=abc123)(ou=abc123))", cons );
+        assertNotNull( e );
+        assertFalse( e.hasMore() );
+        assertFalse( e.getClass().equals( EmptyEnumeration.class ) );
+
+        e = sysRoot.search( "", "(OBJECTclass=*)", cons );
+        assertNotNull( e );
+        assertTrue( e.hasMore() );
+        assertFalse( e.getClass().equals( EmptyEnumeration.class ) );
+
+        e = sysRoot.search( "", "(objectclass=*)", cons );
+        assertNotNull( e );
+        assertFalse( e.getClass().equals( EmptyEnumeration.class ) );
+    }
+
+
+    public void testFailureWithUnsupportedControl() throws Exception
+    {
+        MutableControl unsupported = new MutableControl()
+        {
+            boolean isCritical = true;
+            private static final long serialVersionUID = 1L;
+
+
+            public String getType()
+            {
+                return "1.1.1.1";
+            }
+
+
+            public void setID( String oid )
+            {
+            }
+
+
+            public byte[] getValue()
+            {
+                return new byte[0];
+            }
+
+
+            public void setValue( byte[] value )
+            {
+            }
+
+
+            public boolean isCritical()
+            {
+                return isCritical;
+            }
+
+
+            public void setCritical( boolean isCritical )
+            {
+                this.isCritical = isCritical;
+            }
+
+
+            public String getID()
+            {
+                return "1.1.1.1";
+            }
+
+
+            public byte[] getEncodedValue()
+            {
+                return new byte[0];
+            }
+        };
+        final Hashtable env = new Hashtable();
+
+        env.put( Context.PROVIDER_URL, "ldap://localhost:" + port + "/ou=system" );
+        env.put( "java.naming.ldap.version", "3" );
+        env.put( Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory" );
+        env.put( Context.SECURITY_AUTHENTICATION, "simple" );
+        env.put( Context.SECURITY_CREDENTIALS, "secret" );
+        env.put( Context.SECURITY_PRINCIPAL, "uid=admin,ou=system" );
+        InitialLdapContext ctx = new InitialLdapContext( env, null );
+
+        Attributes user = new AttributesImpl( "cn", "Kate Bush", true );
+        Attribute oc = new AttributeImpl( "objectClass" );
+        oc.add( "top" );
+        oc.add( "person" );
+        oc.add( "organizationalPerson" );
+        oc.add( "inetOrgPerson" );
+        user.put( oc );
+        user.put( "sn", "Bush" );
+        user.put( "userPassword", "Aerial" );
+        ctx.setRequestControls( new MutableControl[]
+                {unsupported} );
+
+        try
+        {
+            ctx.createSubcontext( "cn=Kate Bush", user );
+        }
+        catch ( OperationNotSupportedException e )
+        {
+        }
+
+        unsupported.setCritical( false );
+        DirContext kate = ctx.createSubcontext( "cn=Kate Bush", user );
+        assertNotNull( kate );
+        assertTrue( ArrayUtils.isEquals( Asn1StringUtils.getBytesUtf8( "Aerial" ), kate.getAttributes( "" ).get(
+                "userPassword" ).get() ) );
+    }
+}
diff --git a/old_trunk/server-unit/src/test/java/org/apache/directory/server/ModifyAddTest.java b/old_trunk/server-unit/src/test/java/org/apache/directory/server/ModifyAddTest.java
new file mode 100644
index 0000000..7ff829d
--- /dev/null
+++ b/old_trunk/server-unit/src/test/java/org/apache/directory/server/ModifyAddTest.java
@@ -0,0 +1,658 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server;
+
+
+import java.util.Arrays;
+import java.util.Hashtable;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.NoPermissionException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.AttributeInUseException;
+import javax.naming.directory.AttributeModificationException;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.BasicAttribute;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.InvalidAttributeIdentifierException;
+import javax.naming.directory.InvalidAttributeValueException;
+import javax.naming.directory.ModificationItem;
+import javax.naming.directory.NoSuchAttributeException;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.InitialLdapContext;
+import javax.naming.ldap.LdapContext;
+
+import org.apache.directory.server.unit.AbstractServerTest;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.message.ModificationItemImpl;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+
+/**
+ * Testcase with different modify operations on a person entry. Each includes a
+ * single add op only. Created to demonstrate DIREVE-241 ("Adding an already
+ * existing attribute value with a modify operation does not cause an error.").
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class ModifyAddTest extends AbstractServerTest
+{
+    private LdapContext ctx = null;
+    private static final String RDN_TORI_AMOS = "cn=Tori Amos";
+    private static final String PERSON_DESCRIPTION = "an American singer-songwriter";
+    private static final String RDN_DEBBIE_HARRY = "cn=Debbie Harry";
+
+
+    /**
+     * Creation of required attributes of a person entry.
+     */
+    private Attributes getPersonAttributes( String sn, String cn )
+    {
+        Attributes attributes = new AttributesImpl();
+        Attribute attribute = new AttributeImpl( "objectClass" );
+        attribute.add( "top" );
+        attribute.add( "person" );
+        attribute.add( "organizationalperson" );
+        attribute.add( "inetorgperson" );
+        attributes.put( attribute );
+        attributes.put( "cn", cn );
+        attributes.put( "sn", sn );
+
+        return attributes;
+    }
+
+
+    /**
+     * Create context and a person entry.
+     */
+    @Before
+    protected void setUp() throws Exception
+    {
+        super.setUp();
+
+        Hashtable<String, Object> env = new Hashtable<String, Object>();
+        env.put( "java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory" );
+        env.put( "java.naming.provider.url", "ldap://localhost:" + port + "/ou=system" );
+        env.put( "java.naming.security.principal", "uid=admin,ou=system" );
+        env.put( "java.naming.security.credentials", "secret" );
+        env.put( "java.naming.security.authentication", "simple" );
+
+        ctx = new InitialLdapContext( env, null );
+        assertNotNull( ctx );
+
+        // Create Tori Amos with description
+        Attributes attributes = this.getPersonAttributes( "Amos", "Tori Amos" );
+        attributes.put( "description", "an American singer-songwriter" );
+        ctx.createSubcontext( RDN_TORI_AMOS, attributes );
+
+        // Create Debbie Harry ( I feel like being God when creating people as good looking as Blondie :)
+        attributes = getPersonAttributes( "Bush", "Kate Bush" );
+        ctx.createSubcontext( RDN_DEBBIE_HARRY, attributes );
+
+    }
+
+
+    /**
+     * Remove person entry and close context.
+     */
+    @After
+    protected void tearDown() throws Exception
+    {
+        ctx.unbind( RDN_TORI_AMOS );
+        ctx.destroySubcontext( RDN_DEBBIE_HARRY );
+        ctx.close();
+        ctx = null;
+        super.tearDown();
+    }
+
+
+    /**
+     * Add a new attribute to a person entry.
+     * 
+     * @throws NamingException
+     */
+    @Test
+    public void testAddNewAttributeValue() throws NamingException
+    {
+        // Add telephoneNumber attribute
+        String newValue = "1234567890";
+        Attributes attrs = new AttributesImpl( "telephoneNumber", newValue );
+        ctx.modifyAttributes( RDN_TORI_AMOS, DirContext.ADD_ATTRIBUTE, attrs );
+
+        // Verify, that attribute value is added
+        attrs = ctx.getAttributes( RDN_TORI_AMOS );
+        Attribute attr = attrs.get( "telephoneNumber" );
+        assertNotNull( attr );
+        assertTrue( attr.contains( newValue ) );
+        assertEquals( 1, attr.size() );
+    }
+
+
+    /**
+     * Add a new attribute with two values.
+     * 
+     * @throws NamingException
+     */
+    @Test
+    public void testAddNewAttributeValues() throws NamingException
+    {
+        // Add telephoneNumber attribute
+        String[] newValues =
+            { "1234567890", "999999999" };
+        Attribute attr = new AttributeImpl( "telephoneNumber" );
+        attr.add( newValues[0] );
+        attr.add( newValues[1] );
+        Attributes attrs = new AttributesImpl();
+        attrs.put( attr );
+        ctx.modifyAttributes( RDN_TORI_AMOS, DirContext.ADD_ATTRIBUTE, attrs );
+
+        // Verify, that attribute values are present
+        attrs = ctx.getAttributes( RDN_TORI_AMOS );
+        attr = attrs.get( "telephoneNumber" );
+        assertNotNull( attr );
+        assertTrue( attr.contains( newValues[0] ) );
+        assertTrue( attr.contains( newValues[1] ) );
+        assertEquals( newValues.length, attr.size() );
+    }
+
+
+    /**
+     * Add an additional value.
+     * 
+     * @throws NamingException
+     */
+    @Test
+    public void testAddAdditionalAttributeValue() throws NamingException
+    {
+        // A new description attribute value
+        String newValue = "A new description for this person";
+        assertFalse( newValue.equals( PERSON_DESCRIPTION ) );
+        Attributes attrs = new AttributesImpl( "description", newValue );
+
+        ctx.modifyAttributes( RDN_TORI_AMOS, DirContext.ADD_ATTRIBUTE, attrs );
+
+        // Verify, that attribute value is added
+        attrs = ctx.getAttributes( RDN_TORI_AMOS );
+        Attribute attr = attrs.get( "description" );
+        assertNotNull( attr );
+        assertTrue( attr.contains( newValue ) );
+        assertTrue( attr.contains( PERSON_DESCRIPTION ) );
+        assertEquals( 2, attr.size() );
+    }
+
+
+    /**
+     * Try to add an already existing attribute value.
+     * 
+     * Expected behaviour: Modify operation fails with an
+     * AttributeInUseException. Original LDAP Error code: 20 (Indicates that the
+     * attribute value specified in a modify or add operation already exists as
+     * a value for that attribute).
+     * 
+     * @throws NamingException
+     */
+    @Test
+    public void testAddExistingAttributeValue() throws NamingException
+    {
+        // Change description attribute
+        Attributes attrs = new AttributesImpl( "description", PERSON_DESCRIPTION );
+        
+        try
+        {
+            ctx.modifyAttributes( RDN_TORI_AMOS, DirContext.ADD_ATTRIBUTE, attrs );
+            fail( "Adding an already existing atribute value should fail." );
+        }
+        catch ( AttributeInUseException e )
+        {
+            // expected behaviour
+        }
+
+        // Verify, that attribute is still there, and is the only one
+        attrs = ctx.getAttributes( RDN_TORI_AMOS );
+        Attribute attr = attrs.get( "description" );
+        assertNotNull( attr );
+        assertTrue( attr.contains( PERSON_DESCRIPTION ) );
+        assertEquals( 1, attr.size() );
+    }
+
+    /**
+     * Try to add an already existing attribute value.
+     * 
+     * Expected behaviour: Modify operation fails with an
+     * AttributeInUseException. Original LDAP Error code: 20 (Indicates that the
+     * attribute value specified in a modify or add operation already exists as
+     * a value for that attribute).
+     * 
+     * Check for bug DIR_SERVER664
+     * 
+     * @throws NamingException
+     */
+    @Test
+    public void testAddExistingNthAttributesDirServer664() throws NamingException
+    {
+        // Change description attribute
+        Attributes attrs = new AttributesImpl( true );
+        attrs.put( new AttributeImpl( "telephoneNumber", "attr 1" ) );
+        attrs.put( new AttributeImpl( "telephoneNumber", "attr 2" ) );
+        attrs.put( new AttributeImpl( "telephoneNumber", "attr 3" ) );
+        attrs.put( new AttributeImpl( "telephoneNumber", "attr 4" ) );
+        attrs.put( new AttributeImpl( "telephoneNumber", "attr 5" ) );
+        attrs.put( new AttributeImpl( "telephoneNumber", "attr 6" ) );
+        attrs.put( new AttributeImpl( "telephoneNumber", "attr 7" ) );
+        attrs.put( new AttributeImpl( "telephoneNumber", "attr 8" ) );
+        attrs.put( new AttributeImpl( "telephoneNumber", "attr 9" ) );
+        attrs.put( new AttributeImpl( "telephoneNumber", "attr 10" ) );
+        attrs.put( new AttributeImpl( "telephoneNumber", "attr 11" ) );
+        attrs.put( new AttributeImpl( "telephoneNumber", "attr 12" ) );
+        attrs.put( new AttributeImpl( "telephoneNumber", "attr 13" ) );
+        attrs.put( new AttributeImpl( "telephoneNumber", "attr 14" ) );
+        
+        Attribute attr = new AttributeImpl( "description", PERSON_DESCRIPTION );
+
+        attrs.put( attr );
+        
+        try
+        {
+            ctx.modifyAttributes( RDN_TORI_AMOS, DirContext.ADD_ATTRIBUTE, attrs );
+            fail( "Adding an already existing atribute value should fail." );
+        }
+        catch ( AttributeInUseException e )
+        {
+            // expected behaviour
+        }
+
+        // Verify, that attribute is still there, and is the only one
+        attrs = ctx.getAttributes( RDN_TORI_AMOS );
+        attr = attrs.get( "description" );
+        assertNotNull( attr );
+        assertTrue( attr.contains( PERSON_DESCRIPTION ) );
+        assertEquals( 1, attr.size() );
+    }
+
+    /**
+     * Check for DIR_SERVER_643
+     * 
+     * @throws NamingException
+     */
+    @Test
+    public void testTwoDescriptionDirServer643() throws NamingException
+    {
+        // Change description attribute
+        Attributes attrs = new AttributesImpl( true );
+        Attribute attr = new AttributeImpl( "description", "a British singer-songwriter with an expressive four-octave voice" );
+        attr.add( "one of the most influential female artists of the twentieth century" );
+        attrs.put( attr );
+        
+        ctx.modifyAttributes( RDN_TORI_AMOS, DirContext.ADD_ATTRIBUTE, attrs );
+
+        // Verify, that attribute is still there, and is the only one
+        attrs = ctx.getAttributes( RDN_TORI_AMOS );
+        attr = attrs.get( "description" );
+        assertNotNull( attr );
+        assertEquals( 3, attr.size() );
+        assertTrue( attr.contains( "a British singer-songwriter with an expressive four-octave voice" ) );
+        assertTrue( attr.contains( "one of the most influential female artists of the twentieth century" ) );
+        assertTrue( attr.contains( PERSON_DESCRIPTION ) );
+    }
+
+    /**
+     * Try to add a duplicate attribute value to an entry, where this attribute
+     * is already present (objectclass in this case). Expected behaviour is that
+     * the modify operation causes an error (error code 20, "Attribute or value
+     * exists").
+     * 
+     * @throws NamingException
+     */
+    @Test
+    public void testAddDuplicateValueToExistingAttribute() throws NamingException
+    {
+        // modify object classes, add a new value twice
+        Attribute ocls = new AttributeImpl( "objectClass", "organizationalPerson" );
+        ModificationItemImpl[] modItems = new ModificationItemImpl[2];
+        modItems[0] = new ModificationItemImpl( DirContext.ADD_ATTRIBUTE, ocls );
+        modItems[1] = new ModificationItemImpl( DirContext.ADD_ATTRIBUTE, ocls );
+        try
+        {
+            ctx.modifyAttributes( RDN_TORI_AMOS, modItems );
+            fail( "Adding a duplicate attribute value should cause an error." );
+        }
+        catch ( AttributeInUseException ex )
+        {
+        }
+
+        // Check, whether attribute objectClass is unchanged
+        Attributes attrs = ctx.getAttributes( RDN_TORI_AMOS );
+        ocls = attrs.get( "objectClass" );
+        assertEquals( ocls.size(), 4 );
+        assertTrue( ocls.contains( "top" ) );
+        assertTrue( ocls.contains( "person" ) );
+    }
+
+
+    /**
+     * Try to add a duplicate attribute value to an entry, where this attribute
+     * is not present. Expected behaviour is that the modify operation causes an
+     * error (error code 20, "Attribute or value exists").
+     * 
+     * @throws NamingException
+     */
+    @Test
+    public void testAddDuplicateValueToNewAttribute() throws NamingException
+    {
+        // add the same description value twice
+        Attribute desc = new AttributeImpl( "description", "another description value besides songwriter" );
+        ModificationItemImpl[] modItems = new ModificationItemImpl[2];
+        modItems[0] = new ModificationItemImpl( DirContext.ADD_ATTRIBUTE, desc );
+        modItems[1] = new ModificationItemImpl( DirContext.ADD_ATTRIBUTE, desc );
+        try
+        {
+            ctx.modifyAttributes( RDN_TORI_AMOS, modItems );
+            fail( "Adding a duplicate attribute value should cause an error." );
+        }
+        catch ( AttributeInUseException ex )
+        {
+        }
+
+        // Check, whether attribute description is still not present
+        Attributes attrs = ctx.getAttributes( RDN_TORI_AMOS );
+        assertEquals( 1, attrs.get( "description" ).size() );
+    }
+
+
+    /**
+     * Create an entry with a bad attribute : this should fail.
+     * 
+     * @throws NamingException
+     */
+    @Test
+    public void testAddUnexistingAttribute() throws NamingException
+    {
+        Hashtable<String, Object> env = new Hashtable<String, Object>();
+        env.put( "java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory" );
+        env.put( "java.naming.provider.url", "ldap://localhost:" + port + "/ou=system" );
+        env.put( "java.naming.security.principal", "uid=admin,ou=system" );
+        env.put( "java.naming.security.credentials", "secret" );
+        env.put( "java.naming.security.authentication", "simple" );
+
+        ctx = new InitialLdapContext( env, null );
+        assertNotNull( ctx );
+
+        // Create a third person with a voice attribute
+        Attributes attributes = this.getPersonAttributes( "Jackson", "Michael Jackson" );
+        attributes.put( "voice", "He is bad ..." );
+
+        try
+        {
+            ctx.createSubcontext( "cn=Mickael Jackson", attributes );
+        }
+        catch ( InvalidAttributeIdentifierException iaie )
+        {
+            assertTrue( true );
+            return;
+        }
+
+        fail( "Should never reach this point" );
+    }
+
+
+    /**
+     * Modify the entry with a bad attribute : this should fail 
+     * 
+     * @throws NamingException
+     */
+    @Test
+    public void testSearchBadAttribute() throws NamingException
+    {
+        // Add a not existing attribute
+        String newValue = "unbelievable";
+        Attributes attrs = new AttributesImpl( "voice", newValue );
+
+        try
+        {
+            ctx.modifyAttributes( RDN_TORI_AMOS, DirContext.ADD_ATTRIBUTE, attrs );
+        }
+        catch ( NoSuchAttributeException nsae )
+        {
+            // We have a failure : the attribute is unknown in the schema
+            assertTrue( true );
+            return;
+        }
+
+        fail( "Cannot reach this point" );
+    }
+    
+    
+    /**
+     * Create a person entry and perform a modify op, in which
+     * we modify an attribute two times.
+     * 
+     * @throws NamingException 
+     */
+    @Test
+    public void testAttributeValueMultiMofificationDIRSERVER_636() throws NamingException {
+
+        // Create a person entry
+        Attributes attrs = getPersonAttributes("Bush", "Kate Bush");
+        String rdn = "cn=Kate Bush";
+        ctx.createSubcontext(rdn, attrs);
+
+        // Add a description with two values
+        String[] descriptions = {
+                "Kate Bush is a British singer-songwriter.",
+                "She has become one of the most influential female artists of the twentieth century." };
+        Attribute desc1 = new AttributeImpl("description");
+        desc1.add(descriptions[0]);
+        desc1.add(descriptions[1]);
+
+        ModificationItemImpl addModOp = new ModificationItemImpl(
+                DirContext.ADD_ATTRIBUTE, desc1);
+
+        Attribute desc2 = new AttributeImpl("description");
+        desc2.add(descriptions[1]);
+        ModificationItemImpl delModOp = new ModificationItemImpl(
+                DirContext.REMOVE_ATTRIBUTE, desc2);
+
+        ctx.modifyAttributes(rdn, new ModificationItemImpl[] { addModOp,
+                        delModOp });
+
+        SearchControls sctls = new SearchControls();
+        sctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
+        String filter = "(cn=*Bush)";
+        String base = "";
+
+        // Check entry
+        NamingEnumeration<SearchResult> enm = ctx.search(base, filter, sctls);
+        assertTrue(enm.hasMore());
+        
+        while (enm.hasMore()) {
+            SearchResult sr = enm.next();
+            attrs = sr.getAttributes();
+            Attribute desc = sr.getAttributes().get("description");
+            assertNotNull(desc);
+            assertEquals(1, desc.size());
+            assertTrue(desc.contains(descriptions[0]));
+        }
+
+        // Remove the person entry
+        ctx.destroySubcontext(rdn);
+    }
+
+    /**
+     * Try to add subschemaSubentry attribute to an entry
+     * 
+     * @throws NamingException 
+     */
+    @Test
+    public void testModifyOperationalAttributeAdd() throws NamingException
+    {
+        ModificationItem modifyOp = new ModificationItemImpl( DirContext.ADD_ATTRIBUTE, new BasicAttribute(
+            "subschemaSubentry", "cn=anotherSchema" ) );
+
+        try
+        {
+            ctx.modifyAttributes( RDN_DEBBIE_HARRY, new ModificationItem[]
+                { modifyOp } );
+
+            fail( "modification of entry should fail" );
+        }
+        catch ( InvalidAttributeValueException e )
+        {
+            // Expected result
+        }
+        catch ( NoPermissionException e )
+        {
+            // Expected result
+        }
+    }
+
+
+    /**
+     * Create a person entry and perform a modify op on an
+     * attribute which is part of the DN. This is not allowed.
+     * 
+     * A JIRA has been created for this bug : DIRSERVER_687
+     * 
+     * @throws NamingException 
+     */
+    @Test
+     public void testDNAttributeMemberMofificationDIRSERVER_687() throws NamingException {
+
+        // Create a person entry
+        Attributes attrs = getPersonAttributes("Bush", "Kate Bush");
+        String rdn = "cn=Kate Bush";
+        ctx.createSubcontext(rdn, attrs);
+
+        // Try to modify the cn attribute
+        Attribute desc1 = new AttributeImpl( "cn", "Georges Bush" );
+
+        ModificationItem addModOp = new ModificationItem(
+                DirContext.REPLACE_ATTRIBUTE, desc1);
+
+        try
+        {
+            ctx.modifyAttributes( rdn, new ModificationItem[] { addModOp } );
+            fail();
+        }
+        catch ( AttributeModificationException ame )
+        {
+            assertTrue( true );
+            // Remove the person entry
+            ctx.destroySubcontext(rdn);
+        }
+        catch ( NamingException ne ) 
+        {
+            assertTrue( true );
+            // Remove the person entry
+            ctx.destroySubcontext(rdn);
+        }
+    }
+    
+    /**
+     * Try to modify an entry adding invalid number of values for a single-valued atribute
+     * 
+     * @throws NamingException 
+     * @see <a href="http://issues.apache.org/jira/browse/DIRSERVER-614">DIRSERVER-614</a>
+     */
+    @Test
+    public void testModifyAddWithInvalidNumberOfAttributeValues() throws NamingException
+    {
+        Attributes attrs = new AttributesImpl();
+        Attribute ocls = new AttributeImpl( "objectClass" );
+        ocls.add( "top" );
+        ocls.add( "inetOrgPerson" );
+        attrs.put( ocls );
+        attrs.put( "cn", "Fiona Apple" );
+        attrs.put( "sn", "Apple" );
+        ctx.createSubcontext( "cn=Fiona Apple", attrs );
+        
+        // add two displayNames to an inetOrgPerson
+        attrs = new AttributesImpl();
+        Attribute displayName = new AttributeImpl( "displayName" );
+        displayName.add( "Fiona" );
+        displayName.add( "Fiona A." );
+        attrs.put( displayName );
+        
+        try
+        {
+            ctx.modifyAttributes( "cn=Fiona Apple", DirContext.ADD_ATTRIBUTE, attrs );
+            fail( "modification of entry should fail" );
+        }
+        catch ( InvalidAttributeValueException e )
+        {
+            
+        }
+    }
+
+
+    /**
+     * Add a new attribute to a person entry.
+     * 
+     * @throws NamingException
+     */
+    @Test
+    public void testAddNewBinaryAttributeValue() throws NamingException
+    {
+        // Add a binary attribute
+        byte[] newValue = new byte[]{0x00, 0x01, 0x02, 0x03};
+        Attributes attrs = new AttributesImpl( "userCertificate;binary", newValue );
+        ctx.modifyAttributes( RDN_TORI_AMOS, DirContext.ADD_ATTRIBUTE, attrs );
+
+        // Verify, that attribute value is added
+        attrs = ctx.getAttributes( RDN_TORI_AMOS );
+        Attribute attr = attrs.get( "userCertificate" );
+        assertNotNull( attr );
+        assertTrue( attr.contains( newValue ) );
+        byte[] certificate = (byte[])attr.get();
+        assertTrue( Arrays.equals( newValue, certificate ) );
+        assertEquals( 1, attr.size() );
+    }
+    
+    
+    /**
+     * Add a new ;binary attribute with bytes greater than 0x80
+     * to a person entry.
+     * Test for DIRSERVER-1146
+     *
+     * @throws NamingException
+     */
+    @Test
+    public void testAddNewBinaryAttributeValue0x80() throws NamingException
+    {
+        // Add a ;binary attribute with high-bytes
+        byte[] newValue = new byte[]{(byte)0x80, (byte)0x81, (byte)0x82, (byte)0x83};
+        Attributes attrs = new AttributesImpl( "userCertificate;binary", newValue );
+        ctx.modifyAttributes( RDN_TORI_AMOS, DirContext.ADD_ATTRIBUTE, attrs );
+        
+        // Verify, that attribute value is added
+        attrs = ctx.getAttributes( RDN_TORI_AMOS );
+        Attribute attr = attrs.get( "userCertificate" );
+        assertNotNull( attr );
+        assertTrue( attr.contains( newValue ) );
+        byte[] certificate = (byte[])attr.get();
+        assertTrue( Arrays.equals( newValue, certificate ) );
+        assertEquals( 1, attr.size() );
+    }
+}
diff --git a/old_trunk/server-unit/src/test/java/org/apache/directory/server/ModifyRdnTest.java b/old_trunk/server-unit/src/test/java/org/apache/directory/server/ModifyRdnTest.java
new file mode 100644
index 0000000..d0c0556
--- /dev/null
+++ b/old_trunk/server-unit/src/test/java/org/apache/directory/server/ModifyRdnTest.java
@@ -0,0 +1,1061 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server;
+
+
+import java.util.Hashtable;
+
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.NoPermissionException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.SchemaViolationException;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.InitialLdapContext;
+import javax.naming.ldap.LdapContext;
+
+import org.apache.directory.server.unit.AbstractServerTest;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+
+/**
+ * Testcase with different modify DN operations on a person entry.
+ * Originally created to demonstrate DIREVE-173.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class ModifyRdnTest extends AbstractServerTest
+{
+
+    private LdapContext ctx = null;
+
+
+    /**
+     * Create attributes for a person entry.
+     */
+    protected Attributes getPersonAttributes( String sn, String cn )
+    {
+        Attributes attributes = new AttributesImpl();
+        Attribute attribute = new AttributeImpl( "objectClass" );
+        attribute.add( "top" );
+        attribute.add( "person" );
+        attributes.put( attribute );
+        attributes.put( "cn", cn );
+        attributes.put( "sn", sn );
+        attributes.put( "description", cn + " is a person." );
+
+        return attributes;
+    }
+
+
+    /**
+     * Create attributes for a organizational unit entry.
+     */
+    protected Attributes getOrganizationalUnitAttributes( String ou )
+    {
+        Attributes attributes = new AttributesImpl();
+        Attribute attribute = new AttributeImpl( "objectClass" );
+        attribute.add( "top" );
+        attribute.add( "organizationalUnit" );
+        attributes.put( attribute );
+        attributes.put( "ou", ou );
+        attributes.put( "description", ou + " is an organizational unit." );
+
+        return attributes;
+    }
+
+
+    /**
+     * Create context
+     */
+    @Before
+    public void setUp() throws Exception
+    {
+        super.setUp();
+
+        Hashtable<String, Object> env = new Hashtable<String, Object>();
+        env.put( "java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory" );
+        env.put( "java.naming.provider.url", "ldap://localhost:" + port + "/ou=system" );
+        env.put( "java.naming.security.principal", "uid=admin,ou=system" );
+        env.put( "java.naming.security.credentials", "secret" );
+        env.put( "java.naming.security.authentication", "simple" );
+        ctx = new InitialLdapContext( env, null );
+        assertNotNull( ctx );
+    }
+
+
+    /**
+     * Close context
+     */
+    @After
+    public void tearDown() throws Exception
+    {
+        ctx.close();
+        ctx = null;
+
+        super.tearDown();
+    }
+
+
+    /**
+     * Just a little test to check wether opening the connection succeeds.
+     */
+    @Test
+    public void testSetUpTearDown()
+    {
+        assertNotNull( ctx );
+    }
+
+
+    /**
+     * Modify Rdn of an entry, delete its old rdn value.
+     * 
+     * @throws NamingException
+     */
+    @Test
+    public void testModifyRdnAndDeleteOld() throws NamingException
+    {
+        // Create a person, cn value is rdn
+        String oldCn = "Myra Ellen Amos";
+        String oldRdn = "cn=" + oldCn;
+        Attributes attributes = this.getPersonAttributes( "Amos", oldCn );
+        ctx.createSubcontext( oldRdn, attributes );
+
+        // modify Rdn
+        String newCn = "Tori Amos";
+        String newRdn = "cn=" + newCn;
+        ctx.addToEnvironment( "java.naming.ldap.deleteRDN", "true" );
+        ctx.rename( oldRdn, newRdn );
+
+        // Check, whether old Entry does not exists
+        try
+        {
+            ctx.lookup( oldRdn );
+            fail( "Entry must not exist" );
+        }
+        catch ( NameNotFoundException ignored )
+        {
+            // expected behaviour
+            assertTrue( true );
+        }
+
+        // Check, whether new Entry exists
+        DirContext tori = ( DirContext ) ctx.lookup( newRdn );
+        assertNotNull( tori );
+
+        // Check values of cn
+        Attribute cn = tori.getAttributes( "" ).get( "cn" );
+        assertTrue( cn.contains( newCn ) );
+        assertTrue( !cn.contains( oldCn ) ); // old value is gone
+        assertEquals( 1, cn.size() );
+
+        // Remove entry (use new rdn)
+        ctx.unbind( newRdn );
+    }
+
+
+    /**
+     * Modify Rdn of an entry, without deleting its old rdn value.
+     * 
+     * The JNDI property is set with 'False'
+     * 
+     * @throws NamingException
+     */
+    @Test
+    public void testModifyRdnAndDontDeleteOldFalse() throws NamingException
+    {
+        // Create a person, cn value is rdn
+        String oldCn = "Myra Ellen Amos";
+        String oldRdn = "cn=" + oldCn;
+        Attributes attributes = this.getPersonAttributes( "Amos", oldCn );
+        ctx.createSubcontext( oldRdn, attributes );
+
+        // modify Rdn
+        String newCn = "Tori Amos";
+        String newRdn = "cn=" + newCn;
+        ctx.addToEnvironment( "java.naming.ldap.deleteRDN", "False" );
+        ctx.rename( oldRdn, newRdn );
+
+        // Check, whether old Entry does not exists
+        try
+        {
+            ctx.lookup( oldRdn );
+            fail( "Entry must not exist" );
+        }
+        catch ( NameNotFoundException ignored )
+        {
+            // expected behaviour
+            assertTrue( true );
+        }
+
+        // Check, whether new Entry exists
+        DirContext tori = ( DirContext ) ctx.lookup( newRdn );
+        assertNotNull( tori );
+
+        // Check values of cn
+        Attribute cn = tori.getAttributes( "" ).get( "cn" );
+        assertTrue( cn.contains( newCn ) );
+        assertTrue( cn.contains( oldCn ) ); // old value is still there
+        assertEquals( 2, cn.size() );
+
+        // Remove entry (use new rdn)
+        ctx.unbind( newRdn );
+    }
+
+
+    /**
+     * Modify Rdn of an entry, keep its old rdn value.
+     * 
+     * @throws NamingException
+     */
+    @Test
+    public void testModifyRdnAndKeepOld() throws NamingException
+    {
+        // Create a person, cn value is rdn
+        String oldCn = "Myra Ellen Amos";
+        String oldRdn = "cn=" + oldCn;
+        Attributes attributes = this.getPersonAttributes( "Amos", oldCn );
+        ctx.createSubcontext( oldRdn, attributes );
+
+        // modify Rdn
+        String newCn = "Tori Amos";
+        String newRdn = "cn=" + newCn;
+        ctx.addToEnvironment( "java.naming.ldap.deleteRDN", "false" );
+        ctx.rename( oldRdn, newRdn );
+
+        // Check, whether old entry does not exist
+        try
+        {
+            ctx.lookup( oldRdn );
+            fail( "Entry must not exist" );
+        }
+        catch ( NameNotFoundException ignored )
+        {
+            // expected behaviour
+            assertTrue( true );
+        }
+
+        // Check, whether new entry exists
+        DirContext tori = ( DirContext ) ctx.lookup( newRdn );
+        assertNotNull( tori );
+
+        // Check values of cn
+        Attribute cn = tori.getAttributes( "" ).get( "cn" );
+        assertTrue( cn.contains( newCn ) );
+        assertTrue( cn.contains( oldCn ) ); // old value is still there
+        assertEquals( 2, cn.size() );
+
+        // Remove entry (use new rdn)
+        ctx.unbind( newRdn );
+    }
+
+
+    /**
+     * Modify Rdn of an entry, delete its old rdn value. Here, the rdn attribute
+     * cn has another value as well.
+     * 
+     * @throws NamingException
+     */
+    @Test
+    public void testModifyRdnAndDeleteOldVariant() throws NamingException
+    {
+        // Create a person, cn value is rdn
+        String oldCn = "Myra Ellen Amos";
+        String oldRdn = "cn=" + oldCn;
+        Attributes attributes = this.getPersonAttributes( "Amos", oldCn );
+
+        // add a second cn value
+        String alternateCn = "Myra E. Amos";
+        Attribute cn = attributes.get( "cn" );
+        cn.add( alternateCn );
+        assertEquals( 2, cn.size() );
+
+        ctx.createSubcontext( oldRdn, attributes );
+
+        // modify Rdn
+        String newCn = "Tori Amos";
+        String newRdn = "cn=" + newCn;
+        ctx.addToEnvironment( "java.naming.ldap.deleteRDN", "true" );
+        ctx.rename( oldRdn, newRdn );
+
+        // Check, whether old Entry does not exist anymore
+        try
+        {
+            ctx.lookup( oldRdn );
+            fail( "Entry must not exist" );
+        }
+        catch ( NameNotFoundException ignored )
+        {
+            // expected behaviour
+            assertTrue( true );
+        }
+
+        // Check, whether new Entry exists
+        DirContext tori = ( DirContext ) ctx.lookup( newRdn );
+        assertNotNull( tori );
+
+        // Check values of cn
+        cn = tori.getAttributes( "" ).get( "cn" );
+        assertTrue( cn.contains( newCn ) );
+        assertTrue( !cn.contains( oldCn ) ); // old value is gone
+        assertTrue( cn.contains( alternateCn ) ); // alternate value is still available
+        assertEquals( 2, cn.size() );
+
+        // Remove entry (use new rdn)
+        ctx.unbind( newRdn );
+    }
+
+
+    /**
+     * Modify DN of an entry, changing RDN from cn to sn.
+     * 
+     * @throws NamingException
+     */
+    @Test
+    public void testModifyRdnDifferentAttribute() throws NamingException
+    {
+        // Create a person, cn value is rdn
+        String cnVal = "Tori Amos";
+        String snVal = "Amos";
+        String oldRdn = "cn=" + cnVal;
+        Attributes attributes = this.getPersonAttributes( snVal, cnVal );
+        ctx.createSubcontext( oldRdn, attributes );
+
+        // modify Rdn from cn=... to sn=...
+        String newRdn = "sn=" + snVal;
+        ctx.addToEnvironment( "java.naming.ldap.deleteRDN", "false" );
+        ctx.rename( oldRdn, newRdn );
+
+        // Check, whether old Entry does not exists
+        try
+        {
+            ctx.lookup( oldRdn );
+            fail( "Entry must not exist" );
+        }
+        catch ( NameNotFoundException ignored )
+        {
+            // expected behaviour
+        }
+
+        // Check, whether new Entry exists
+        DirContext tori = ( DirContext ) ctx.lookup( newRdn );
+        assertNotNull( tori );
+
+        // Check values of cn and sn
+        // especially the number of cn and sn occurences
+        Attribute cn = tori.getAttributes( "" ).get( "cn" );
+        assertTrue( cn.contains( cnVal ) );
+        assertEquals( "Number of cn occurences", 1, cn.size() );
+        Attribute sn = tori.getAttributes( "" ).get( "sn" );
+        assertTrue( sn.contains( snVal ) );
+        assertEquals( "Number of sn occurences", 1, sn.size() );
+
+        // Remove entry (use new rdn)
+        ctx.unbind( newRdn );
+    }
+
+
+    /**
+     * Modify DN of an entry, changing RDN from cn to sn, 
+     * delete old RDn, must fail because cn can not be deleted.
+     * 
+     * @throws NamingException
+     */
+    @Test
+    public void testModifyRdnDifferentAttributeDeleteOldFails() throws NamingException
+    {
+        // Create a person, cn value is rdn
+        String cnVal = "Tori Amos";
+        String snVal = "Amos";
+        String oldRdn = "cn=" + cnVal;
+        Attributes attributes = this.getPersonAttributes( snVal, cnVal );
+        ctx.createSubcontext( oldRdn, attributes );
+
+        // modify Rdn from cn=... to sn=...
+        String newRdn = "sn=" + snVal;
+        ctx.addToEnvironment( "java.naming.ldap.deleteRDN", "true" );
+        try
+        {
+            ctx.rename( oldRdn, newRdn );
+            fail( "Rename must fail, mandatory attirbute cn can not be deleted." );
+        }
+        catch ( SchemaViolationException ignored )
+        {
+            // expected behaviour
+        }
+
+        // Remove entry (use old rdn)
+        ctx.unbind( oldRdn );
+    }
+
+
+    /**
+     * Test for DIRSERVER-1086.
+     * Modify Rdn of an entry that has a child entry, delete its old rdn value.
+     * Ensure that the tree is not broken.
+     *
+     * @throws NamingException
+     */
+    @Test
+    public void testModifyRdnAndDeleteOldWithChild() throws NamingException
+    {
+        // Create an organizational unit, ou value is rdn
+        String oldOu = "Writers";
+        String oldRdn = "ou=" + oldOu;
+        Attributes attributes = this.getOrganizationalUnitAttributes( oldOu );
+        DirContext createdCtx = ctx.createSubcontext( oldRdn, attributes );
+
+        // Create a child
+        String childCn = "Tori Amos";
+        String childRdn = "cn=" + childCn;
+        Attributes childAttributes = this.getPersonAttributes( "Amos", childCn );
+        createdCtx.createSubcontext( childRdn, childAttributes );
+
+        // modify Rdn
+        String newOu = "Singers";
+        String newRdn = "ou=" + newOu;
+        ctx.addToEnvironment( "java.naming.ldap.deleteRDN", "true" );
+        ctx.rename( oldRdn, newRdn );
+
+        // Check, whether old Entry does not exists
+        try
+        {
+            ctx.lookup( oldRdn );
+            fail( "Entry must not exist" );
+        }
+        catch ( NameNotFoundException ignored )
+        {
+            // expected behaviour
+            assertTrue( true );
+        }
+
+        // Check, whether new Entry exists
+        DirContext org = ( DirContext ) ctx.lookup( newRdn );
+        assertNotNull( org );
+
+        // Check values of ou
+        Attribute ou = org.getAttributes( "" ).get( "ou" );
+        assertTrue( ou.contains( newOu ) );
+        assertTrue( !ou.contains( oldOu ) ); // old value is gone
+        assertEquals( 1, ou.size() );
+
+        // Perform a search under renamed ou and check whether exactly one child entry exist
+        SearchControls searchControls = new SearchControls();
+        searchControls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+        searchControls.setReturningAttributes( new String[]
+            { "objectClass" } );
+        NamingEnumeration<SearchResult> results = org.search( "", "(objectClass=*)", searchControls );
+        assertTrue( results.hasMore() );
+        results.next();
+        assertTrue( !results.hasMore() );
+
+        // Check whether Tori exists
+        DirContext tori = ( DirContext ) org.lookup( childRdn );
+        assertNotNull( tori );
+
+        // Remove entry (use new rdn)
+        ctx.unbind( childRdn + "," + newRdn );
+        ctx.unbind( newRdn );
+    }
+
+
+    /**
+     * Test for DIRSERVER-1096.
+     * Modify the RDN of an entry with an escaped new RDN. 
+     * Ensure that the attribute itself contains the unescaped value.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void testModifyRdnWithEncodedNewRdn() throws Exception
+    {
+        // Create a person "cn=Tori Amos", cn value is rdn
+        String cnVal = "Tori Amos";
+        String snVal = "Amos";
+        String oldRdn = "cn=" + cnVal;
+        Attributes attributes = this.getPersonAttributes( snVal, cnVal );
+        ctx.createSubcontext( oldRdn, attributes );
+
+        // modify Rdn from cn=Tori Amos to cn=<a Umlaut>\+
+        String newCnEscapedVal = new String( new byte[]
+            { ( byte ) 0xC3, ( byte ) 0xA4, '\\', '+' }, "UTF-8" );
+        ctx.addToEnvironment( "java.naming.ldap.deleteRDN", "true" );
+        String newRdn = "cn=" + newCnEscapedVal;
+        ctx.rename( oldRdn, newRdn );
+
+        // Check, whether old Entry does not exists
+        try
+        {
+            ctx.lookup( oldRdn );
+            fail( "Entry must not exist" );
+        }
+        catch ( NameNotFoundException ignored )
+        {
+            // expected behaviour
+        }
+
+        // Check, whether new Entry exists
+        DirContext newCtx = ( DirContext ) ctx.lookup( newRdn );
+        assertNotNull( newCtx );
+
+        // Check that the DN contains the escaped value
+        assertEquals( "cn=" + newCnEscapedVal + "," + ctx.getNameInNamespace(), newCtx.getNameInNamespace() );
+
+        // Check that cn contains the unescaped value
+        Attribute cn = newCtx.getAttributes( "" ).get( "cn" );
+        assertEquals( "Number of cn occurences", 1, cn.size() );
+        assertTrue( cn.contains( "\\C3\\A4\\+" ) );
+
+        // Remove entry (use new rdn)
+        ctx.unbind( newRdn );
+    }
+
+
+    /**
+     * Test for DIRSERVER-1162 and DIRSERVER-1085.
+     * 
+     * Modify single valued RDN to a multi valued RDN.
+     * - Old Rdn: cn
+     * - New Rdn: cn+sn
+     * - Keep old Rdn
+     * - Attributes: cn, sn, description must exist 
+     * 
+     * @throws NamingException
+     */
+    @Test
+    public void testModifyMultiValuedRdnVariant1() throws NamingException
+    {
+        Attributes attributes = createPerson( "cn" );
+        String oldRdn = getRdn( attributes, "cn" );
+        String newRdn = getRdn( attributes, "cn", "sn" );
+
+        ctx.addToEnvironment( "java.naming.ldap.deleteRDN", "false" );
+        ctx.rename( oldRdn, newRdn );
+
+        // Check whether new Entry exists
+        DirContext newCtx = ( DirContext ) ctx.lookup( newRdn );
+        assertNotNull( newCtx );
+
+        // Check attributes
+        Attribute cnAttr = newCtx.getAttributes( "" ).get( "cn" );
+        assertEquals( 1, cnAttr.size() );
+        assertTrue( cnAttr.contains( "Tori Amos" ) );
+        Attribute snAttr = newCtx.getAttributes( "" ).get( "sn" );
+        assertEquals( 1, snAttr.size() );
+        assertTrue( snAttr.contains( "Amos" ) );
+        Attribute descriptionAttr = newCtx.getAttributes( "" ).get( "description" );
+        assertEquals( 1, descriptionAttr.size() );
+
+        // Remove entry (use new rdn)
+        ctx.unbind( newRdn );
+    }
+
+
+    /**
+     * Test for DIRSERVER-1162 and DIRSERVER-1085.
+     * 
+     * Modify single valued RDN to a multi valued RDN.
+     * - Old Rdn: cn
+     * - New Rdn: cn+sn
+     * - Delete old Rdn
+     * - Attributes: cn, sn, description must exist 
+     * 
+     * @throws NamingException
+     */
+    @Test
+    public void testModifyMultiValuedRdnVariant2() throws NamingException
+    {
+        Attributes attributes = createPerson( "cn" );
+        String oldRdn = getRdn( attributes, "cn" );
+        String newRdn = getRdn( attributes, "cn", "sn" );
+
+        ctx.addToEnvironment( "java.naming.ldap.deleteRDN", "true" );
+        ctx.rename( oldRdn, newRdn );
+
+        // Check whether new Entry exists
+        DirContext newCtx = ( DirContext ) ctx.lookup( newRdn );
+        assertNotNull( newCtx );
+
+        // Check attributes
+        Attribute cnAttr = newCtx.getAttributes( "" ).get( "cn" );
+        assertEquals( 1, cnAttr.size() );
+        assertTrue( cnAttr.contains( "Tori Amos" ) );
+        Attribute snAttr = newCtx.getAttributes( "" ).get( "sn" );
+        assertEquals( 1, snAttr.size() );
+        assertTrue( snAttr.contains( "Amos" ) );
+        Attribute descriptionAttr = newCtx.getAttributes( "" ).get( "description" );
+        assertEquals( 1, descriptionAttr.size() );
+
+        // Remove entry (use new rdn)
+        ctx.unbind( newRdn );
+    }
+
+
+    /**
+     * Test for DIRSERVER-1162 and DIRSERVER-1085.
+     * 
+     * Modify single valued RDN to a multi valued RDN.
+     * - Old Rdn: description
+     * - New Rdn: cn+sn
+     * - Keep old Rdn
+     * - Attributes: cn, sn, description must exist 
+     * 
+     * @throws NamingException
+     */
+    @Test
+    public void testModifyMultiValuedRdnVariant3() throws NamingException
+    {
+        Attributes attributes = createPerson( "description" );
+        String oldRdn = getRdn( attributes, "description" );
+        String newRdn = getRdn( attributes, "cn", "sn" );
+
+        ctx.addToEnvironment( "java.naming.ldap.deleteRDN", "false" );
+        ctx.rename( oldRdn, newRdn );
+
+        // Check whether new Entry exists
+        DirContext newCtx = ( DirContext ) ctx.lookup( newRdn );
+        assertNotNull( newCtx );
+
+        // Check attributes
+        Attribute cnAttr = newCtx.getAttributes( "" ).get( "cn" );
+        assertEquals( 1, cnAttr.size() );
+        assertTrue( cnAttr.contains( "Tori Amos" ) );
+        Attribute snAttr = newCtx.getAttributes( "" ).get( "sn" );
+        assertEquals( 1, snAttr.size() );
+        assertTrue( snAttr.contains( "Amos" ) );
+        Attribute descriptionAttr = newCtx.getAttributes( "" ).get( "description" );
+        assertEquals( 1, descriptionAttr.size() );
+
+        // Remove entry (use new rdn)
+        ctx.unbind( newRdn );
+    }
+
+
+    /**
+     * Test for DIRSERVER-1162 and DIRSERVER-1085.
+     * 
+     * Modify single valued RDN to a multi valued RDN.
+     * - Old Rdn: description
+     * - New Rdn: cn+sn
+     * - Delete old Rdn
+     * - Attributes: cn, sn must exist; descriptions must not exist 
+     * 
+     * @throws NamingException
+     */
+    @Test
+    public void testModifyMultiValuedRdnVariant4() throws NamingException
+    {
+        Attributes attributes = createPerson( "description" );
+        String oldRdn = getRdn( attributes, "description" );
+        String newRdn = getRdn( attributes, "cn", "sn" );
+
+        ctx.addToEnvironment( "java.naming.ldap.deleteRDN", "true" );
+        ctx.rename( oldRdn, newRdn );
+
+        // Check whether new Entry exists
+        DirContext newCtx = ( DirContext ) ctx.lookup( newRdn );
+        assertNotNull( newCtx );
+
+        // Check attributes
+        Attribute cnAttr = newCtx.getAttributes( "" ).get( "cn" );
+        assertEquals( 1, cnAttr.size() );
+        assertTrue( cnAttr.contains( "Tori Amos" ) );
+        Attribute snAttr = newCtx.getAttributes( "" ).get( "sn" );
+        assertEquals( 1, snAttr.size() );
+        assertTrue( snAttr.contains( "Amos" ) );
+        Attribute descriptionAttr = newCtx.getAttributes( "" ).get( "description" );
+        assertNull( descriptionAttr );
+
+        // Remove entry (use new rdn)
+        ctx.unbind( newRdn );
+    }
+
+
+    /**
+     * Test for DIRSERVER-1162 and DIRSERVER-1085.
+     * 
+     * Modify single valued RDN to a multi valued RDN.
+     * - Old Rdn: cn
+     * - New Rdn: sn+telephoneNumber
+     * - Keep old Rdn
+     * - Attributes: cn, sn, description, telephoneNumber must exist 
+     * 
+     * @throws NamingException
+     */
+    @Test
+    public void testModifyMultiValuedRdnVariant5() throws NamingException
+    {
+        Attributes attributes = createPerson( "cn" );
+        attributes.put( "telephoneNumber", "12345" );
+        String oldRdn = getRdn( attributes, "cn" );
+        String newRdn = getRdn( attributes, "sn", "telephoneNumber" );
+
+        ctx.addToEnvironment( "java.naming.ldap.deleteRDN", "false" );
+        ctx.rename( oldRdn, newRdn );
+
+        // Check whether new Entry exists
+        DirContext newCtx = ( DirContext ) ctx.lookup( newRdn );
+        assertNotNull( newCtx );
+
+        // Check attributes
+        Attribute cnAttr = newCtx.getAttributes( "" ).get( "cn" );
+        assertEquals( 1, cnAttr.size() );
+        assertTrue( cnAttr.contains( "Tori Amos" ) );
+        Attribute snAttr = newCtx.getAttributes( "" ).get( "sn" );
+        assertEquals( 1, snAttr.size() );
+        assertTrue( snAttr.contains( "Amos" ) );
+        Attribute descriptionAttr = newCtx.getAttributes( "" ).get( "description" );
+        assertEquals( 1, descriptionAttr.size() );
+        Attribute telephoneNumberAttr = newCtx.getAttributes( "" ).get( "telephoneNumber" );
+        assertEquals( 1, telephoneNumberAttr.size() );
+        assertTrue( telephoneNumberAttr.contains( "12345" ) );
+
+        // Remove entry (use new rdn)
+        ctx.unbind( newRdn );
+    }
+
+
+    /**
+     * Test for DIRSERVER-1162 and DIRSERVER-1085.
+     * 
+     * Modify single valued RDN to a multi valued RDN.
+     * - Old Rdn: cn
+     * - New Rdn: sn+telephoneNumber
+     * - Delete old Rdn
+     * - Must fail with schema violation, cn cannot be deleted
+     * 
+     * @throws NamingException
+     */
+    @Test
+    public void testModifyMultiValuedRdnVariant6() throws NamingException
+    {
+        Attributes attributes = createPerson( "cn" );
+        attributes.put( "telephoneNumber", "12345" );
+        String oldRdn = getRdn( attributes, "cn" );
+        String newRdn = getRdn( attributes, "sn", "telephoneNumber" );
+
+        ctx.addToEnvironment( "java.naming.ldap.deleteRDN", "true" );
+        try
+        {
+            ctx.rename( oldRdn, newRdn );
+            fail( "Rename must fail, cn can not be deleted from a person." );
+        }
+        catch ( SchemaViolationException ignored )
+        {
+            // expected behaviour
+        }
+
+        // Check that entry was not changed
+        try
+        {
+            ctx.lookup( newRdn );
+            fail( "Previous rename failed as expected, entry must not exist" );
+        }
+        catch ( NameNotFoundException ignored )
+        {
+            // expected behaviour
+        }
+
+        // Check that entry was not changed
+        DirContext oldCtx = ( DirContext ) ctx.lookup( oldRdn );
+        assertNotNull( oldCtx );
+        Attribute cnAttr = oldCtx.getAttributes( "" ).get( "cn" );
+        assertEquals( 1, cnAttr.size() );
+        assertTrue( cnAttr.contains( "Tori Amos" ) );
+        Attribute snAttr = oldCtx.getAttributes( "" ).get( "sn" );
+        assertEquals( 1, snAttr.size() );
+        assertTrue( snAttr.contains( "Amos" ) );
+        Attribute descriptionAttr = oldCtx.getAttributes( "" ).get( "description" );
+        assertEquals( 1, descriptionAttr.size() );
+
+        // Remove entry (use old rdn)
+        ctx.unbind( oldRdn );
+    }
+
+
+    /**
+     * Test for DIRSERVER-1162 and DIRSERVER-1085.
+     * 
+     * Modify multi valued RDN to a single valued RDN.
+     * - Old Rdn: cn+sn
+     * - New Rdn: cn
+     * - Keep old Rdn
+     * - Attributes: cn, sn, description must exist 
+     * 
+     * @throws NamingException
+     */
+    @Test
+    public void testModifyMultiValuedRdnVariant7() throws NamingException
+    {
+        Attributes attributes = createPerson( "cn", "sn" );
+        String oldRdn = getRdn( attributes, "cn", "sn" );
+        String newRdn = getRdn( attributes, "cn" );
+
+        ctx.addToEnvironment( "java.naming.ldap.deleteRDN", "false" );
+        ctx.rename( oldRdn, newRdn );
+
+        // Check whether new Entry exists
+        DirContext newCtx = ( DirContext ) ctx.lookup( newRdn );
+        assertNotNull( newCtx );
+
+        // Check attributes
+        Attribute cnAttr = newCtx.getAttributes( "" ).get( "cn" );
+        assertEquals( 1, cnAttr.size() );
+        assertTrue( cnAttr.contains( "Tori Amos" ) );
+        Attribute snAttr = newCtx.getAttributes( "" ).get( "sn" );
+        assertEquals( 1, snAttr.size() );
+        assertTrue( snAttr.contains( "Amos" ) );
+        Attribute descriptionAttr = newCtx.getAttributes( "" ).get( "description" );
+        assertEquals( 1, descriptionAttr.size() );
+
+        // Remove entry (use new rdn)
+        ctx.unbind( newRdn );
+    }
+
+
+    /**
+     * Test for DIRSERVER-1162 and DIRSERVER-1085.
+     * 
+     * Modify multi valued RDN to a single valued RDN.
+     * - Old Rdn: cn+sn
+     * - New Rdn: cn
+     * - Delete old Rdn
+     * - Must fail with schema violation, cn cannot be deleted
+     * 
+     * @throws NamingException
+     */
+    @Test
+    public void testModifyMultiValuedRdnVariant8() throws NamingException
+    {
+        Attributes attributes = createPerson( "cn", "sn" );
+        String oldRdn = getRdn( attributes, "cn", "sn" );
+        String newRdn = getRdn( attributes, "cn" );
+
+        ctx.addToEnvironment( "java.naming.ldap.deleteRDN", "true" );
+        try
+        {
+            ctx.rename( oldRdn, newRdn );
+            fail( "Rename must fail, cn can not be deleted from a person." );
+        }
+        catch ( SchemaViolationException ignored )
+        {
+            // expected behaviour
+        }
+
+        // Check that entry was not changed
+        try
+        {
+            ctx.lookup( newRdn );
+            fail( "Previous rename failed as expected, entry must not exist" );
+        }
+        catch ( NameNotFoundException ignored )
+        {
+            // expected behaviour
+        }
+
+        // Check that entry was not changed
+        DirContext oldCtx = ( DirContext ) ctx.lookup( oldRdn );
+        assertNotNull( oldCtx );
+        Attribute cnAttr = oldCtx.getAttributes( "" ).get( "cn" );
+        assertEquals( 1, cnAttr.size() );
+        assertTrue( cnAttr.contains( "Tori Amos" ) );
+        Attribute snAttr = oldCtx.getAttributes( "" ).get( "sn" );
+        assertEquals( 1, snAttr.size() );
+        assertTrue( snAttr.contains( "Amos" ) );
+        Attribute descriptionAttr = oldCtx.getAttributes( "" ).get( "description" );
+        assertEquals( 1, descriptionAttr.size() );
+
+        // Remove entry (use old rdn)
+        ctx.unbind( oldRdn );
+    }
+
+
+    /**
+     * Test for DIRSERVER-1162 and DIRSERVER-1085.
+     * 
+     * Tries to rename+deleteOldRdn an entry that has an operational attribute
+     * in its RDN. Must fail because an operational attribute can not be
+     * deleted.
+     * 
+     * @throws NamingException
+     */
+    @Test
+    public void testModifyRdnOperationalAttribute() throws NamingException
+    {
+        // create the entry
+        Attributes attributes = createPerson( "cn" );
+        String oldRdn = getRdn( attributes, "cn" );
+
+        // read createTimestamp
+        String createTimestamp = ( String ) ctx.getAttributes( oldRdn, new String[]
+            { "createTimestamp" } ).get( "createTimestamp" ).get();
+
+        // rename to createTimstamp=YYYYMMDDHHMMSSZ
+        String newRdn = "createTimestamp=" + createTimestamp;
+        ctx.addToEnvironment( "java.naming.ldap.deleteRDN", "false" );
+        ctx.rename( oldRdn, newRdn );
+
+        // rename back to old Rdn, enable deleteOldRdn, 
+        // must fail with NoPermisionException
+        ctx.addToEnvironment( "java.naming.ldap.deleteRDN", "true" );
+        try
+        {
+            ctx.rename( newRdn, oldRdn );
+            fail( "Rename must fail, operational attribute createTimestamp can not be deleted." );
+        }
+        catch ( NoPermissionException ignored )
+        {
+            // expected behaviour
+        }
+
+        // Remove entry (use new rdn)
+        ctx.unbind( newRdn );
+    }
+
+
+    /**
+     * Test for DIRSERVER-1096.
+     * Modify the RDN of an entry with an escaped new RDN. 
+     * Ensure that the attribute itself contains the unescaped value.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void testModifyRdnWithEscapedPoundNewRdn() throws Exception
+    {
+        // Create a person "cn=Tori Amos", cn value is rdn
+        String cnVal = "Tori Amos";
+        String snVal = "Amos";
+        String oldRdn = "cn=" + cnVal;
+        Attributes attributes = this.getPersonAttributes( snVal, cnVal );
+        ctx.createSubcontext( oldRdn, attributes );
+
+        // modify Rdn from cn=Tori Amos to cn=\#test\+
+        ctx.addToEnvironment( "java.naming.ldap.deleteRDN", "true" );
+        String newRdn = "cn=\\23test";
+        ctx.rename( oldRdn, newRdn );
+
+        // Check, whether old Entry does not exists
+        try
+        {
+            ctx.lookup( oldRdn );
+            fail( "Entry must not exist" );
+        }
+        catch ( NameNotFoundException ignored )
+        {
+            // expected behaviour
+        }
+
+        // Check, whether new Entry exists
+        DirContext newCtx = ( DirContext ) ctx.lookup( newRdn );
+        assertNotNull( newCtx );
+
+        // Check that the DN contains the escaped value
+        assertEquals( "cn=\\23test," + ctx.getNameInNamespace(), newCtx.getNameInNamespace() );
+
+        // Check that cn contains the unescaped value
+        Attribute cn = newCtx.getAttributes( "" ).get( "cn" );
+        assertEquals( "Number of cn occurences", 1, cn.size() );
+        assertTrue( cn.contains( "\\#test" ) );
+
+        // Remove entry (use new rdn)
+        ctx.unbind( newRdn );
+    }
+
+    
+    /**
+     * Test for DIRSERVER-1162 and DIRSERVER-1085.
+     * 
+     * Tries to rename+deleteOldRdn an entry that has the structural object class
+     * person in its RDN (objectClass=person,ou=system). Must fail because the 
+     * structural object class can not be deleted.
+     * 
+     * @throws NamingException
+     */
+    @Test
+    public void testModifyRdnObjectClassAttribute() throws NamingException
+    {
+        // create the entry
+        Attributes attributes = createPerson( "cn" );
+        String oldRdn = getRdn( attributes, "cn" );
+
+        // rename to objectClass=person
+        String newRdn = "objectClass=person";
+        ctx.addToEnvironment( "java.naming.ldap.deleteRDN", "false" );
+        ctx.rename( oldRdn, newRdn );
+
+        // rename back to old Rdn, enable deleteOldRdn, 
+        // must fail with NoPermisionException
+        ctx.addToEnvironment( "java.naming.ldap.deleteRDN", "true" );
+        try
+        {
+            ctx.rename( newRdn, oldRdn );
+            fail( "Rename must fail, structural objectClass person can not be deleted." );
+        }
+        catch ( SchemaViolationException ignored )
+        {
+            // expected behaviour
+        }
+
+        // Remove entry (use new rdn)
+        ctx.unbind( newRdn );
+    }
+
+
+    private String getRdn( Attributes attributes, String... rdnTypes ) throws NamingException
+    {
+        String rdn = "";
+        for ( String type : rdnTypes )
+        {
+            rdn += type + "=" + attributes.get( type ).get() + "+";
+        }
+        rdn = rdn.substring( 0, rdn.length() - 1 );
+        return rdn;
+    }
+
+
+    private Attributes createPerson( String... rdnTypes ) throws NamingException
+    {
+        Attributes attributes = new AttributesImpl();
+        Attribute attribute = new AttributeImpl( "objectClass" );
+        attribute.add( "top" );
+        attribute.add( "person" );
+        attributes.put( attribute );
+        attributes.put( "cn", "Tori Amos" );
+        attributes.put( "sn", "Amos" );
+        attributes.put( "description", "Tori Amos is a person." );
+
+        String rdn = getRdn( attributes, rdnTypes );
+
+        ctx.createSubcontext( rdn, attributes );
+
+        return attributes;
+    }
+
+}
diff --git a/old_trunk/server-unit/src/test/java/org/apache/directory/server/ModifyRemoveTest.java b/old_trunk/server-unit/src/test/java/org/apache/directory/server/ModifyRemoveTest.java
new file mode 100644
index 0000000..c17a7f0
--- /dev/null
+++ b/old_trunk/server-unit/src/test/java/org/apache/directory/server/ModifyRemoveTest.java
@@ -0,0 +1,581 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server;
+
+
+import java.util.Hashtable;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.InvalidAttributeIdentifierException;
+import javax.naming.directory.InvalidAttributeValueException;
+import javax.naming.directory.ModificationItem;
+import javax.naming.directory.NoSuchAttributeException;
+import javax.naming.directory.SchemaViolationException;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.InitialLdapContext;
+import javax.naming.ldap.LdapContext;
+
+import org.apache.directory.server.unit.AbstractServerTest;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.message.ModificationItemImpl;
+
+
+/**
+ * Testcase with different modify operations on a person entry. Each includes a
+ * single removal op only.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class ModifyRemoveTest extends AbstractServerTest
+{
+
+    private LdapContext ctx = null;
+
+    private static final String RDN = "cn=Tori Amos";
+
+
+    /**
+     * Creation of required attributes of a person entry.
+     */
+    protected Attributes getPersonAttributes( String sn, String cn )
+    {
+        Attributes attributes = new AttributesImpl();
+        Attribute attribute = new AttributeImpl( "objectClass" );
+        attribute.add( "top" );
+        attribute.add( "person" );
+        attributes.put( attribute );
+        attributes.put( "cn", cn );
+        attributes.put( "sn", sn );
+
+        return attributes;
+    }
+
+
+    /**
+     * Creation of required attributes of an inetOrgPerson entry.
+     */
+    protected Attributes getInetOrgPersonAttributes( String sn, String cn )
+    {
+        Attributes attrs = new AttributesImpl();
+        Attribute ocls = new AttributeImpl( "objectClass" );
+        ocls.add( "top" );
+        ocls.add( "person" );
+        ocls.add( "organizationalPerson" );
+        ocls.add( "inetOrgPerson" );
+        attrs.put( ocls );
+        attrs.put( "cn", cn );
+        attrs.put( "sn", sn );
+
+        return attrs;
+    }
+
+
+    /**
+     * Create context and a person entry.
+     */
+    public void setUp() throws Exception
+    {
+        super.setUp();
+
+        Hashtable<String,Object> env = new Hashtable<String,Object>();
+        env.put( "java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory" );
+        env.put( "java.naming.provider.url", "ldap://localhost:" + port + "/ou=system" );
+        env.put( "java.naming.security.principal", "uid=admin,ou=system" );
+        env.put( "java.naming.security.credentials", "secret" );
+        env.put( "java.naming.security.authentication", "simple" );
+
+        ctx = new InitialLdapContext( env, null );
+        assertNotNull( ctx );
+
+        // Create a person with description
+        Attributes attributes = this.getPersonAttributes( "Amos", "Tori Amos" );
+        attributes.put( "description", "an American singer-songwriter" );
+        ctx.createSubcontext( RDN, attributes );
+    }
+
+
+    /**
+     * Remove person entry and close context.
+     */
+    public void tearDown() throws Exception
+    {
+        ctx.unbind( RDN );
+        ctx.close();
+        ctx = null;
+        super.tearDown();
+    }
+
+
+    /**
+     * Just a little test to check wether opening the connection and creation of
+     * the person succeeds succeeds.
+     * 
+     * @throws NamingException 
+     */
+    public void testSetUpTearDown() throws NamingException
+    {
+        assertNotNull( ctx );
+        DirContext tori = ( DirContext ) ctx.lookup( RDN );
+        assertNotNull( tori );
+    }
+
+
+    /**
+     * Remove an attribute which does not exist in an attribute making sure 
+     * it does not remove other values in that attribute.  Tests if the 
+     * following JIRA issue is still valid:
+     * 
+     *    https://issues.apache.org/jira/browse/DIRSERVER-1149
+     * 
+     * @throws NamingException
+     */
+    public void testRemoveAttemptWithoutChange() throws NamingException
+    {
+        // Get the attributes and check the contents
+        Attributes tori = ctx.getAttributes( RDN );
+        assertNotNull( tori.get( "objectClass" ) );
+        assertNotNull( tori.get( "cn" ) );
+        assertEquals( 1, tori.get( "cn" ).size() );
+        assertEquals( "Tori Amos", tori.get( "cn" ).get() );
+        assertNotNull( tori.get( "sn" ) );
+        
+        // Test an add operation first
+        ModificationItem mod = new ModificationItem( DirContext.ADD_ATTRIBUTE, new AttributeImpl( "cn", "foo" ) );
+        ctx.modifyAttributes( RDN, new ModificationItem[] { mod } );
+        tori = ctx.getAttributes( RDN );
+        assertNotNull( tori.get( "objectClass" ) );
+        assertNotNull( tori.get( "cn" ) );
+        assertEquals( 2, tori.get( "cn" ).size() );
+        assertEquals( "Tori Amos", tori.get( "cn" ).get( 0 ) );
+        assertEquals( "foo", tori.get( "cn" ).get( 1 ) );
+        assertNotNull( tori.get( "sn" ) );
+        
+        // Now test remove of value ( bar ) that does not exist in cn
+        mod = new ModificationItem( DirContext.REMOVE_ATTRIBUTE, new AttributeImpl( "cn", "bar" ) );
+        ctx.modifyAttributes( RDN, new ModificationItem[] { mod } );
+        tori = ctx.getAttributes( RDN );
+        assertNotNull( tori.get( "objectClass" ) );
+        assertNotNull( tori.get( "cn" ) );
+        assertEquals( 2, tori.get( "cn" ).size() );
+        assertEquals( "Tori Amos", tori.get( "cn" ).get( 0 ) );
+        assertEquals( "foo", tori.get( "cn" ).get( 1 ) );
+        assertNotNull( tori.get( "sn" ) );
+    }
+
+
+    /**
+     * Remove an attribute, which is not required.
+     * 
+     * Expected result: After successful deletion, attribute is not present in
+     * entry.
+     * 
+     * @throws NamingException
+     */
+    public void testRemoveNotRequiredAttribute() throws NamingException
+    {
+        // Remove description Attribute
+        Attribute attr = new AttributeImpl( "description" );
+        Attributes attrs = new AttributesImpl();
+        attrs.put( attr );
+        ctx.modifyAttributes( RDN, DirContext.REMOVE_ATTRIBUTE, attrs );
+
+        // Verify, that attribute is deleted
+        attrs = ctx.getAttributes( RDN );
+        attr = attrs.get( "description" );
+        assertNull( attr );
+    }
+
+
+    /**
+     * Remove two not required attributes.
+     * 
+     * Expected result: After successful deletion, both attributes ar not
+     * present in entry.
+     * 
+     * @throws NamingException
+     */
+    public void testRemoveTwoNotRequiredAttributes() throws NamingException
+    {
+        // add telephoneNumber to entry
+        Attributes tn = new AttributesImpl( "telephoneNumber", "12345678" );
+        ctx.modifyAttributes( RDN, DirContext.ADD_ATTRIBUTE, tn );
+
+        // Remove description and telephoneNumber to Attribute
+        Attributes attrs = new AttributesImpl();
+        attrs.put( new AttributeImpl( "description" ) );
+        attrs.put( new AttributeImpl( "telephoneNumber" ) );
+        ctx.modifyAttributes( RDN, DirContext.REMOVE_ATTRIBUTE, attrs );
+
+        // Verify, that attributes are deleted
+        attrs = ctx.getAttributes( RDN );
+        assertNull( attrs.get( "description" ) );
+        assertNull( attrs.get( "telephoneNumber" ) );
+        assertNotNull( attrs.get( "cn" ) );
+        assertNotNull( attrs.get( "sn" ) );
+    }
+
+
+    /**
+     * Remove a required attribute. The sn attribute of the person entry is used
+     * here.
+     * 
+     * Expected Result: Deletion fails with NamingException (Schema Violation).
+     * 
+     * @throws NamingException
+     */
+    public void testRemoveRequiredAttribute() throws NamingException
+    {
+        // Remove sn attribute
+        Attribute attr = new AttributeImpl( "sn" );
+        Attributes attrs = new AttributesImpl();
+        attrs.put( attr );
+
+        try
+        {
+            ctx.modifyAttributes( RDN, DirContext.REMOVE_ATTRIBUTE, attrs );
+            fail( "Deletion of required attribute should fail." );
+        }
+        catch ( SchemaViolationException e )
+        {
+            // expected behaviour
+        }
+    }
+
+
+    /**
+     * Remove a required attribute from RDN.
+     * 
+     * Expected Result: Deletion fails with SchemaViolationException.
+     * 
+     * @throws NamingException
+     */
+    public void testRemovePartOfRdn() throws NamingException
+    {
+        // Remove sn attribute
+        Attribute attr = new AttributeImpl( "cn" );
+        Attributes attrs = new AttributesImpl();
+        attrs.put( attr );
+
+        try
+        {
+            ctx.modifyAttributes( RDN, DirContext.REMOVE_ATTRIBUTE, attrs );
+            fail( "Deletion of RDN attribute should fail." );
+        }
+        catch ( SchemaViolationException e )
+        {
+            // expected behaviour
+        }
+    }
+
+
+    /**
+     * Remove a not required attribute from RDN.
+     * 
+     * Expected Result: Deletion fails with SchemaViolationException.
+     * 
+     * @throws NamingException
+     */
+    public void testRemovePartOfRdnNotRequired() throws NamingException
+    {
+        // Change RDN to another attribute
+        String newRdn = "description=an American singer-songwriter";
+        ctx.addToEnvironment( "java.naming.ldap.deleteRDN", "false" );
+        ctx.rename( RDN, newRdn );
+
+        // Remove description, which is now RDN attribute
+        Attribute attr = new AttributeImpl( "description" );
+        Attributes attrs = new AttributesImpl();
+        attrs.put( attr );
+
+        try
+        {
+            ctx.modifyAttributes( newRdn, DirContext.REMOVE_ATTRIBUTE, attrs );
+            fail( "Deletion of RDN attribute should fail." );
+        }
+        catch ( SchemaViolationException e )
+        {
+            // expected behaviour
+        }
+
+        // Change RDN back to original
+        ctx.addToEnvironment( "java.naming.ldap.deleteRDN", "false" );
+        ctx.rename( newRdn, RDN );
+    }
+
+
+    /**
+     * Remove a an attribute which is not present on the entry, but in the
+     * schema.
+     * 
+     * Expected result: Deletion fails with NoSuchAttributeException
+     * 
+     * @throws NamingException
+     */
+    public void testRemoveAttributeNotPresent() throws NamingException
+    {
+        // Remove telephoneNumber Attribute
+        Attribute attr = new AttributeImpl( "telephoneNumber" );
+        Attributes attrs = new AttributesImpl();
+        attrs.put( attr );
+
+        try
+        {
+            ctx.modifyAttributes( RDN, DirContext.REMOVE_ATTRIBUTE, attrs );
+            fail( "Deletion of attribute, which is not present in the entry, should fail." );
+        }
+        catch ( NoSuchAttributeException e )
+        {
+            // expected behaviour
+        }
+    }
+
+
+    /**
+     * Remove a an attribute which is not present in the schema.
+     * 
+     * Expected result: Deletion fails with NoSuchAttributeException
+     * 
+     * @throws NamingException
+     */
+    public void testRemoveAttributeNotValid() throws NamingException
+    {
+        // Remove phantasy attribute
+        Attribute attr = new AttributeImpl( "XXX" );
+        Attributes attrs = new AttributesImpl();
+        attrs.put( attr );
+
+        try
+        {
+            ctx.modifyAttributes( RDN, DirContext.REMOVE_ATTRIBUTE, attrs );
+            fail( "Deletion of an invalid attribute should fail." );
+        }
+        catch ( NoSuchAttributeException e )
+        {
+            // expected behaviour
+        }
+        catch ( InvalidAttributeIdentifierException e )
+        {
+            // expected behaviour
+        }
+    }
+
+
+    /**
+     * Create a person entry and try to remove an attribute value
+     * 
+     * @throws NamingException 
+     */
+    public void testReplaceNonExistingAttribute() throws NamingException
+    {
+        // Create an entry
+        Attributes attrs = getInetOrgPersonAttributes( "Bush", "Kate Bush" );
+        attrs.put( "givenname", "Kate" );
+        String rdn = "cn=Kate Bush";
+        ctx.createSubcontext( rdn, attrs );
+
+        // replace attribute givenName with empty value (=> deletion)
+        Attribute attr = new AttributeImpl( "givenname" );
+        ModificationItemImpl item = new ModificationItemImpl( DirContext.REPLACE_ATTRIBUTE, attr );
+        ctx.modifyAttributes( rdn, new ModificationItemImpl[] { item } );
+
+        SearchControls sctls = new SearchControls();
+        sctls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+        String filter = "(cn=Kate Bush)";
+        String base = "";
+        NamingEnumeration enm = ctx.search( base, filter, sctls );
+        if ( enm.hasMore() )
+        {
+            SearchResult sr = ( SearchResult ) enm.next();
+            attrs = sr.getAttributes();
+            Attribute cn = sr.getAttributes().get( "cn" );
+            assertNotNull( cn );
+            assertTrue( cn.contains( "Kate Bush" ) );
+
+            // Check whether attribute has been removed
+            Attribute givenName = sr.getAttributes().get( "givenname" );
+            assertNull( givenName );
+        }
+        else
+        {
+            fail( "entry not found" );
+        }
+
+        ctx.destroySubcontext( rdn );
+    }
+
+
+    /**
+     * Create a person entry and try to remove an attribute value from the RDN
+     * by Replacement
+     * 
+     * @throws NamingException 
+     */
+    public void testReplaceRdnByEmptyValueAttribute() throws NamingException
+    {
+
+        // Create an entry
+        Attributes attrs = getPersonAttributes( "Bush", "Kate Bush" );
+        String rdn = "cn=Kate Bush";
+        ctx.createSubcontext( rdn, attrs );
+
+        // replace attribute cn with empty value (=> deletion)
+        Attribute attr = new AttributeImpl( "cn" );
+        ModificationItemImpl item = new ModificationItemImpl( DirContext.REPLACE_ATTRIBUTE, attr );
+
+        try
+        {
+            ctx.modifyAttributes( rdn, new ModificationItemImpl[]
+                { item } );
+            fail( "modify should fail" );
+        }
+        catch ( SchemaViolationException e )
+        {
+            // Expected behaviour
+        }
+
+        ctx.destroySubcontext( rdn );
+    }
+
+
+    /**
+     * Create a person entry and try to remove an attribute from the RDN
+     * 
+     * @throws NamingException 
+     */
+    public void testRemoveRdnAttribute() throws NamingException
+    {
+
+        // Create an entry
+        Attributes attrs = getPersonAttributes( "Bush", "Kate Bush" );
+        String rdn = "cn=Kate Bush";
+        ctx.createSubcontext( rdn, attrs );
+
+        // replace attribute cn with empty value (=> deletion)
+        Attribute attr = new AttributeImpl( "cn" );
+        ModificationItemImpl item = new ModificationItemImpl( DirContext.REMOVE_ATTRIBUTE, attr );
+
+        try
+        {
+            ctx.modifyAttributes( rdn, new ModificationItemImpl[]
+                { item } );
+            fail( "modify should fail" );
+        }
+        catch ( SchemaViolationException e )
+        {
+            // Expected behaviour
+        }
+
+        ctx.destroySubcontext( rdn );
+    }
+
+
+    /**
+     * Create a person entry and try to remove an attribute from the RDN
+     * 
+     * @throws NamingException 
+     */
+    public void testRemoveRdnAttributeValue() throws NamingException
+    {
+
+        // Create an entry
+        Attributes attrs = getPersonAttributes( "Bush", "Kate Bush" );
+        String rdn = "cn=Kate Bush";
+        ctx.createSubcontext( rdn, attrs );
+
+        // replace attribute cn with empty value (=> deletion)
+        Attribute attr = new AttributeImpl( "cn", "Kate Bush" );
+        ModificationItemImpl item = new ModificationItemImpl( DirContext.REMOVE_ATTRIBUTE, attr );
+
+        try
+        {
+            ctx.modifyAttributes( rdn, new ModificationItemImpl[]
+                { item } );
+            fail( "modify should fail" );
+        }
+        catch ( SchemaViolationException e )
+        {
+            // Expected behaviour
+        }
+
+        ctx.destroySubcontext( rdn );
+    }
+    
+    /**
+     * Create a person entry and try to remove objectClass attribute
+     * 
+     * @throws NamingException 
+     */
+    public void testDeleteOclAttrWithTopPersonOrganizationalpersonInetorgperson() throws NamingException {
+
+        // Create an entry
+        Attributes attrs = getInetOrgPersonAttributes("Bush", "Kate Bush");
+        String rdn = "cn=Kate Bush";
+        ctx.createSubcontext(rdn, attrs);
+
+        ModificationItemImpl delModOp = new ModificationItemImpl(DirContext.REMOVE_ATTRIBUTE, new AttributeImpl("objectclass", ""));
+
+        try {
+            ctx.modifyAttributes(rdn, new ModificationItemImpl[] { delModOp });
+            fail("deletion of objectclass should fail");
+        } catch (SchemaViolationException e) {
+            // expected
+        } catch (NoSuchAttributeException e) {
+            // expected
+        } catch (InvalidAttributeValueException e) {
+            // expected
+        } catch ( Exception e ) {
+            e.printStackTrace();
+        }
+
+        ctx.destroySubcontext(rdn);
+    }
+
+    /**
+     * Create a person entry and try to remove objectClass attribute. A variant
+     * which works.
+     * 
+     * @throws NamingException 
+     */
+    public void testDeleteOclAttrWithTopPersonOrganizationalpersonInetorgpersonVariant() throws NamingException {
+
+        // Create an entry
+        Attributes attrs = getInetOrgPersonAttributes("Bush", "Kate Bush");
+        String rdn = "cn=Kate Bush";
+        ctx.createSubcontext(rdn, attrs);
+
+        ModificationItemImpl delModOp = new ModificationItemImpl(DirContext.REMOVE_ATTRIBUTE, new AttributeImpl("objectclass"));
+
+        try {
+            ctx.modifyAttributes(rdn, new ModificationItemImpl[] { delModOp });
+            fail("deletion of objectclass should fail");
+        } catch (SchemaViolationException e) {
+            // expected
+        }
+
+        ctx.destroySubcontext(rdn);
+    }
+}
diff --git a/old_trunk/server-unit/src/test/java/org/apache/directory/server/ModifyReplaceITest.java b/old_trunk/server-unit/src/test/java/org/apache/directory/server/ModifyReplaceITest.java
new file mode 100644
index 0000000..1d8ffe2
--- /dev/null
+++ b/old_trunk/server-unit/src/test/java/org/apache/directory/server/ModifyReplaceITest.java
@@ -0,0 +1,158 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server;
+
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.BasicAttribute;
+import javax.naming.directory.BasicAttributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.ModificationItem;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+
+import org.apache.directory.server.unit.AbstractServerTest;
+
+
+/**
+ * Test case for all modify replace operations.
+ * 
+ * Testcase to demonstrate DIRSERVER-646 ("Replacing an unknown attribute with
+ * no values (deletion) causes an error").
+ */
+public class ModifyReplaceITest extends AbstractServerTest
+{
+    protected Attributes getPersonAttributes( String sn, String cn ) 
+    {
+        Attributes attrs = new BasicAttributes();
+        Attribute ocls = new BasicAttribute("objectClass");
+        ocls.add("top");
+        ocls.add("person");
+        attrs.put(ocls);
+        attrs.put("cn", cn);
+        attrs.put("sn", sn);
+
+        return attrs;
+    }
+
+    
+    /**
+     * Create a person entry and try to remove a not present attribute
+     */
+    public void testReplaceNotPresentAttribute() throws NamingException 
+    {
+        Attributes attrs = getPersonAttributes( "Bush", "Kate Bush" );
+        String rdn = "cn=Kate Bush";
+        sysRoot.createSubcontext( rdn, attrs );
+
+        Attribute attr = new BasicAttribute( "description" );
+        ModificationItem item = new ModificationItem( DirContext.REPLACE_ATTRIBUTE, attr );
+
+        sysRoot.modifyAttributes( rdn, new ModificationItem[] { item } );
+
+        SearchControls sctls = new SearchControls();
+        sctls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        String filter = "(sn=Bush)";
+        String base = "";
+
+        NamingEnumeration enm = sysRoot.search( base, filter, sctls );
+        while ( enm.hasMore() ) 
+        {
+            SearchResult sr = ( SearchResult ) enm.next();
+            attrs = sr.getAttributes();
+            Attribute cn = sr.getAttributes().get( "cn" );
+            assertNotNull( cn );
+            assertTrue( cn.contains("Kate Bush") );
+        }
+
+        sysRoot.destroySubcontext( rdn );
+    }
+
+    
+    /**
+     * Create a person entry and try to remove a non existing attribute
+     */
+    public void testReplaceNonExistingAttribute() throws NamingException 
+    {
+        Attributes attrs = getPersonAttributes( "Bush", "Kate Bush" );
+        String rdn = "cn=Kate Bush";
+        sysRoot.createSubcontext( rdn, attrs );
+
+        Attribute attr = new BasicAttribute( "numberOfOctaves" );
+        ModificationItem item = new ModificationItem( DirContext.REPLACE_ATTRIBUTE, attr );
+
+        sysRoot.modifyAttributes(rdn, new ModificationItem[] { item });
+
+        SearchControls sctls = new SearchControls();
+        sctls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        String filter = "(sn=Bush)";
+        String base = "";
+
+        NamingEnumeration enm = sysRoot.search( base, filter, sctls );
+        while ( enm.hasMore() ) 
+        {
+            SearchResult sr = ( SearchResult ) enm.next();
+            attrs = sr.getAttributes();
+            Attribute cn = sr.getAttributes().get( "cn" );
+            assertNotNull( cn );
+            assertTrue( cn.contains( "Kate Bush" ) );
+        }
+
+        sysRoot.destroySubcontext( rdn );
+    }
+
+
+    /**
+     * Create a person entry and try to remove a non existing attribute
+     */
+    public void testReplaceNonExistingAttributeManyMods() throws NamingException 
+    {
+        Attributes attrs = getPersonAttributes( "Bush", "Kate Bush" );
+        String rdn = "cn=Kate Bush";
+        sysRoot.createSubcontext( rdn, attrs );
+
+        Attribute attr = new BasicAttribute( "numberOfOctaves" );
+        ModificationItem item = new ModificationItem( DirContext.REPLACE_ATTRIBUTE, attr );
+        Attribute attr2 = new BasicAttribute( "description", "blah blah blah" );
+        ModificationItem item2 = new ModificationItem( DirContext.ADD_ATTRIBUTE, attr2 );
+
+        sysRoot.modifyAttributes(rdn, new ModificationItem[] { item, item2 });
+
+        SearchControls sctls = new SearchControls();
+        sctls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        String filter = "(sn=Bush)";
+        String base = "";
+
+        NamingEnumeration enm = sysRoot.search( base, filter, sctls );
+        while ( enm.hasMore() ) 
+        {
+            SearchResult sr = ( SearchResult ) enm.next();
+            attrs = sr.getAttributes();
+            Attribute cn = sr.getAttributes().get( "cn" );
+            assertNotNull( cn );
+            assertTrue( cn.contains( "Kate Bush" ) );
+        }
+
+        sysRoot.destroySubcontext( rdn );
+    }
+}
diff --git a/old_trunk/server-unit/src/test/java/org/apache/directory/server/NegationOperatorITest.java b/old_trunk/server-unit/src/test/java/org/apache/directory/server/NegationOperatorITest.java
new file mode 100644
index 0000000..a76c954
--- /dev/null
+++ b/old_trunk/server-unit/src/test/java/org/apache/directory/server/NegationOperatorITest.java
@@ -0,0 +1,216 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server;
+
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.LdapContext;
+
+import org.apache.directory.server.core.entry.DefaultServerEntry;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.partition.impl.btree.Index;
+import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmIndex;
+import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmPartition;
+import org.apache.directory.server.unit.AbstractServerTest;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.ldif.LdifEntry;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.util.DateUtils;
+
+
+/**
+ * A set of tests to make sure the negation operator is working 
+ * properly when included in search filters. Created in response
+ * to JIRA issue 
+ * <a href="https://issues.apache.org/jira/browse/DIRSERVER-951">DIRSERVER-951</a>.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 519077 $
+ */
+public class NegationOperatorITest extends AbstractServerTest
+{
+    private LdapContext ctx = null;
+    private List<LdifEntry> loadedEntries;
+
+
+    /**
+     * Create context and entries for tests.
+     */
+    public void setUp() throws Exception
+    {
+        super.setUp();
+        loadedEntries = super.loadTestLdif( true );
+        ctx = getWiredContext();
+        assertNotNull( ctx );
+        assertEquals( 5, loadedEntries.size() );
+    }
+
+
+    @Override
+    protected void configureDirectoryService() throws NamingException
+    {
+        if ( this.getName().indexOf( "Indexed" ) != -1 )
+        {
+            JdbmPartition system = new JdbmPartition();
+            system.setId( "system" );
+
+            // @TODO need to make this configurable for the system partition
+            system.setCacheSize( 500 );
+
+            system.setSuffix( "ou=system" );
+
+            // Add indexed attributes for system partition
+            Set<Index> indexedAttrs = new HashSet<Index>();
+            indexedAttrs.add( new JdbmIndex( SchemaConstants.OBJECT_CLASS_AT ) );
+            indexedAttrs.add( new JdbmIndex( SchemaConstants.OU_AT ) );
+            system.setIndexedAttributes( indexedAttrs );
+
+            // Add context entry for system partition
+            LdapDN systemDn = new LdapDN( "ou=system" );
+            ServerEntry systemEntry = new DefaultServerEntry( directoryService.getRegistries(), systemDn );
+            
+            systemEntry.put( "objectClass", "top", "organizationalUnit", "extensibleObject" ); 
+
+            systemEntry.put( SchemaConstants.CREATORS_NAME_AT, "uid=admin, ou=system" );
+            systemEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() );
+            systemEntry.put( "ou", "system" );
+            system.setContextEntry( systemEntry );
+
+            directoryService.setSystemPartition( system );
+        }
+    }
+
+    /**
+     * Closes context and destroys server.
+     */
+    public void tearDown() throws Exception
+    {
+        ctx.close();
+        ctx = null;
+        loadedEntries = null;
+        super.tearDown();
+    }
+    
+
+    /**
+     * Tests to make sure a negated search for actors without ou
+     * with value 'drama' returns those that do not have the attribute
+     * and do not have a 'drama' value for ou if the attribute still
+     * exists.  This test does not build an index on ou for the system
+     * partition.
+     */
+    public void testSearchNotDrama() throws Exception
+    {
+        // jack black has ou but not drama, and joe newbie has no ou what so ever
+        Set<SearchResult> results = getResults( "(!(ou=drama))" );
+        assertTrue( contains( "uid=jblack,ou=actors,ou=system", results ) );
+        assertTrue( contains( "uid=jnewbie,ou=actors,ou=system", results ) );
+        assertEquals( 2, results.size() );
+    }
+
+    
+    /**
+     * Tests to make sure a negated search for actors without ou
+     * with value 'drama' returns those that do not have the attribute
+     * and do not have a 'drama' value for ou if the attribute still
+     * exists.  This test DOES build an index on ou for the system
+     * partition and should have failed if the bug in DIRSERVER-951
+     * was present and reproducable.
+     */
+    public void testSearchNotDramaIndexed() throws Exception
+    {
+        // jack black has ou but not drama, and joe newbie has no ou what so ever
+        Set<SearchResult> results = getResults( "(!(ou=drama))" );
+        assertTrue( contains( "uid=jblack,ou=actors,ou=system", results ) );
+        assertTrue( contains( "uid=jnewbie,ou=actors,ou=system", results ) );
+        assertEquals( 2, results.size() );
+    }
+
+    
+    /**
+     * Tests to make sure a negated search for actors without ou
+     * with value 'drama' returns those that do not have the attribute
+     * and do not have a 'drama' value for ou if the attribute still
+     * exists.  This test does not build an index on ou for the system
+     * partition.
+     */
+    public void testSearchNotDramaNotNewbie() throws Exception
+    {
+        // jack black has ou but not drama, and joe newbie has no ou what so ever
+        Set<SearchResult> results = getResults( "(& (!(uid=jnewbie)) (!(ou=drama)) )" );
+        assertTrue( contains( "uid=jblack,ou=actors,ou=system", results ) );
+        assertFalse( contains( "uid=jnewbie,ou=actors,ou=system", results ) );
+        assertEquals( 1, results.size() );
+    }
+
+    
+    /**
+     * Tests to make sure a negated search for actors without ou
+     * with value 'drama' returns those that do not have the attribute
+     * and do not have a 'drama' value for ou if the attribute still
+     * exists.  This test DOES build an index on ou for the system
+     * partition and should have failed if the bug in DIRSERVER-951
+     * was present and reproducable.
+     */
+    public void testSearchNotDramaNotNewbieIndexed() throws Exception
+    {
+        // jack black has ou but not drama, and joe newbie has no ou what so ever
+        Set<SearchResult> results = getResults( "(& (!(uid=jnewbie)) (!(ou=drama)) )" );
+        assertTrue( contains( "uid=jblack,ou=actors,ou=system", results ) );
+        assertFalse( contains( "uid=jnewbie,ou=actors,ou=system", results ) );
+        assertEquals( 1, results.size() );
+    }
+
+    
+    boolean contains( String dn, Set<SearchResult> results )
+    {
+        for ( SearchResult result : results )
+        {
+            if ( result.getNameInNamespace().equals( dn ) )
+            {
+                return true;
+            }
+        }
+        
+        return false;
+    }
+    
+    
+    Set<SearchResult> getResults( String filter ) throws NamingException
+    {
+        Set<SearchResult> results = new HashSet<SearchResult>();
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+        NamingEnumeration<SearchResult> namingEnumeration = ctx.search( "ou=actors,ou=system", filter, controls );
+        while( namingEnumeration.hasMore() )
+        {
+            results.add( namingEnumeration.next() );
+        }
+        
+        return results;
+    }
+}
diff --git a/old_trunk/server-unit/src/test/java/org/apache/directory/server/PasswordPolicyServiceITest.java b/old_trunk/server-unit/src/test/java/org/apache/directory/server/PasswordPolicyServiceITest.java
new file mode 100644
index 0000000..cb62138
--- /dev/null
+++ b/old_trunk/server-unit/src/test/java/org/apache/directory/server/PasswordPolicyServiceITest.java
@@ -0,0 +1,304 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server;
+
+
+import org.apache.directory.server.core.entry.DefaultServerEntry;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.interceptor.Interceptor;
+import org.apache.directory.server.core.kerberos.PasswordPolicyInterceptor;
+import org.apache.directory.server.core.partition.Partition;
+import org.apache.directory.server.core.partition.impl.btree.Index;
+import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmIndex;
+import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmPartition;
+import org.apache.directory.server.unit.AbstractServerTest;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.InitialDirContext;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Set;
+
+
+/**
+ * An {@link AbstractServerTest} testing the (@link {@link PasswordPolicyInterceptor}.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class PasswordPolicyServiceITest extends AbstractServerTest
+{
+    private DirContext ctx;
+    private DirContext users;
+
+
+    /**
+     * Set up a partition for EXAMPLE.COM, add the {@link PasswordPolicyInterceptor}
+     * interceptor, and create a users subcontext.
+     */
+    public void setUp() throws Exception
+    {
+        super.setUp();
+        setAllowAnonymousAccess( false );
+
+        Attributes attrs;
+
+
+        Hashtable<String, String> env = new Hashtable<String, String>();
+        env.put( "java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory" );
+        env.put( "java.naming.provider.url", "ldap://localhost:" + port + "/dc=example,dc=com" );
+        env.put( "java.naming.security.principal", "uid=admin,ou=system" );
+        env.put( "java.naming.security.credentials", "secret" );
+        env.put( "java.naming.security.authentication", "simple" );
+        ctx = new InitialDirContext( env );
+
+        attrs = getOrgUnitAttributes( "users" );
+        users = ctx.createSubcontext( "ou=users", attrs );
+    }
+
+    protected void configureDirectoryService() throws NamingException
+    {
+        Set<Partition> partitions = new HashSet<Partition>();
+
+        // Add partition 'example'
+        JdbmPartition partition = new JdbmPartition();
+        partition.setId( "example" );
+        partition.setSuffix( "dc=example,dc=com" );
+
+        Set<Index> indexedAttrs = new HashSet<Index>();
+        indexedAttrs.add( new JdbmIndex( "ou" ) );
+        indexedAttrs.add( new JdbmIndex( "dc" ) );
+        indexedAttrs.add( new JdbmIndex( "objectClass" ) );
+        partition.setIndexedAttributes( indexedAttrs );
+
+        LdapDN exampleDn = new LdapDN( "dc=example,dc=com" );
+        ServerEntry serverEntry = new DefaultServerEntry( directoryService.getRegistries(), exampleDn );
+        serverEntry.put( "objectClass", "top", "domain" );
+        serverEntry.put( "dc", "example" );
+        
+        partition.setContextEntry( serverEntry );
+
+        partitions.add( partition );
+        directoryService.setPartitions( partitions );
+
+        List<Interceptor> list = directoryService.getInterceptors();
+
+        list.add( new PasswordPolicyInterceptor() );
+        directoryService.setInterceptors( list );
+    }
+
+
+    /**
+     * Tests that passwords that are too short are properly rejected. 
+     */
+    public void testLength()
+    {
+        Attributes attrs = getPersonAttributes( "Nelson", "Horatio Nelson", "hnelson", "HN1" );
+        try
+        {
+            users.createSubcontext( "uid=hnelson", attrs );
+            fail( "Shouldn't have gotten here." );
+        }
+        catch ( NamingException ne )
+        {
+            assertTrue( ne.getMessage().contains( "length too short" ) );
+            assertFalse( ne.getMessage().contains( "insufficient character mix" ) );
+            assertFalse( ne.getMessage().contains( "contains portions of username" ) );
+        }
+    }
+
+
+    /**
+     * Tests that passwords with insufficient character mix are properly rejected. 
+     */
+    public void testCharacterMix()
+    {
+        Attributes attrs = getPersonAttributes( "Nelson", "Horatio Nelson", "hnelson", "secret" );
+        try
+        {
+            users.createSubcontext( "uid=hnelson", attrs );
+            fail( "Shouldn't have gotten here." );
+        }
+        catch ( NamingException ne )
+        {
+            assertFalse( ne.getMessage().contains( "length too short" ) );
+            assertTrue( ne.getMessage().contains( "insufficient character mix" ) );
+            assertFalse( ne.getMessage().contains( "contains portions of username" ) );
+        }
+    }
+
+
+    /**
+     * Tests that passwords that contain substrings of the username are properly rejected. 
+     */
+    public void testContainsUsername()
+    {
+        Attributes attrs = getPersonAttributes( "Nelson", "Horatio Nelson", "hnelson", "A1nelson" );
+        try
+        {
+            users.createSubcontext( "uid=hnelson", attrs );
+            fail( "Shouldn't have gotten here." );
+        }
+        catch ( NamingException ne )
+        {
+            assertFalse( ne.getMessage().contains( "length too short" ) );
+            assertFalse( ne.getMessage().contains( "insufficient character mix" ) );
+            assertTrue( ne.getMessage().contains( "contains portions of username" ) );
+        }
+    }
+
+
+    /**
+     * Tests that passwords with insufficient character mix and that are too
+     * short are properly rejected. 
+     */
+    public void testCharacterMixAndLength()
+    {
+        Attributes attrs = getPersonAttributes( "Nelson", "Horatio Nelson", "hnelson", "hi" );
+        try
+        {
+            users.createSubcontext( "uid=hnelson", attrs );
+            fail( "Shouldn't have gotten here." );
+        }
+        catch ( NamingException ne )
+        {
+            assertTrue( ne.getMessage().contains( "length too short" ) );
+            assertTrue( ne.getMessage().contains( "insufficient character mix" ) );
+            assertFalse( ne.getMessage().contains( "contains portions of username" ) );
+        }
+    }
+
+
+    /**
+     * Tests that passwords that are too short and that contain substrings of
+     * the username are properly rejected.
+     */
+    public void testLengthAndContainsUsername()
+    {
+        Attributes attrs = getPersonAttributes( "Bush", "William Bush", "wbush", "bush1" );
+        try
+        {
+            users.createSubcontext( "uid=wbush", attrs );
+            fail( "Shouldn't have gotten here." );
+        }
+        catch ( NamingException ne )
+        {
+            assertTrue( ne.getMessage().contains( "length too short" ) );
+            assertFalse( ne.getMessage().contains( "insufficient character mix" ) );
+            assertTrue( ne.getMessage().contains( "contains portions of username" ) );
+        }
+    }
+
+
+    /**
+     * Tests that passwords with insufficient character mix and that contain substrings of
+     * the username are properly rejected.
+     */
+    public void testCharacterMixAndContainsUsername()
+    {
+        Attributes attrs = getPersonAttributes( "Nelson", "Horatio Nelson", "hnelson", "hnelson" );
+        try
+        {
+            users.createSubcontext( "uid=hnelson", attrs );
+            fail( "Shouldn't have gotten here." );
+        }
+        catch ( NamingException ne )
+        {
+            assertFalse( ne.getMessage().contains( "length too short" ) );
+            assertTrue( ne.getMessage().contains( "insufficient character mix" ) );
+            assertTrue( ne.getMessage().contains( "contains portions of username" ) );
+        }
+    }
+
+
+    /**
+     * Tests that passwords with insufficient character mix and that are too
+     * short and that contain substrings of the username are properly rejected.
+     */
+    public void testCharacterMixAndLengthAndContainsUsername()
+    {
+        Attributes attrs = getPersonAttributes( "Bush", "William Bush", "wbush", "bush" );
+        try
+        {
+            users.createSubcontext( "uid=wbush", attrs );
+            fail( "Shouldn't have gotten here." );
+        }
+        catch ( NamingException ne )
+        {
+            assertTrue( ne.getMessage().contains( "length too short" ) );
+            assertTrue( ne.getMessage().contains( "insufficient character mix" ) );
+            assertTrue( ne.getMessage().contains( "contains portions of username" ) );
+        }
+    }
+
+
+    /**
+     * Tear down.
+     */
+    public void tearDown() throws Exception
+    {
+        ctx.close();
+        ctx = null;
+        super.tearDown();
+    }
+
+
+    /**
+     * Convenience method for creating a person.
+     */
+    protected Attributes getPersonAttributes( String sn, String cn, String uid, String userPassword )
+    {
+        Attributes attrs = new AttributesImpl();
+        Attribute ocls = new AttributeImpl( "objectClass" );
+        ocls.add( "top" );
+        ocls.add( "person" ); // sn $ cn
+        ocls.add( "inetOrgPerson" ); // uid
+        attrs.put( ocls );
+        attrs.put( "cn", cn );
+        attrs.put( "sn", sn );
+        attrs.put( "uid", uid );
+        attrs.put( "userPassword", userPassword );
+
+        return attrs;
+    }
+
+
+    /**
+     * Convenience method for creating an organizational unit.
+     */
+    protected Attributes getOrgUnitAttributes( String ou )
+    {
+        Attributes attrs = new AttributesImpl();
+        Attribute ocls = new AttributeImpl( "objectClass" );
+        ocls.add( "top" );
+        ocls.add( "organizationalUnit" );
+        attrs.put( ocls );
+        attrs.put( "ou", ou );
+
+        return attrs;
+    }
+}
diff --git a/old_trunk/server-unit/src/test/java/org/apache/directory/server/PersistentSearchTest.java b/old_trunk/server-unit/src/test/java/org/apache/directory/server/PersistentSearchTest.java
new file mode 100644
index 0000000..8d04316
--- /dev/null
+++ b/old_trunk/server-unit/src/test/java/org/apache/directory/server/PersistentSearchTest.java
@@ -0,0 +1,795 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server;
+
+
+import java.util.ArrayList;
+import java.util.EventObject;
+import java.util.Hashtable;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.InitialDirContext;
+import javax.naming.directory.SearchResult;
+import javax.naming.event.EventContext;
+import javax.naming.event.EventDirContext;
+import javax.naming.event.NamespaceChangeListener;
+import javax.naming.event.NamingEvent;
+import javax.naming.event.NamingExceptionEvent;
+import javax.naming.event.ObjectChangeListener;
+import javax.naming.ldap.Control;
+import javax.naming.ldap.HasControls;
+import javax.naming.ldap.InitialLdapContext;
+import javax.naming.ldap.LdapContext;
+
+import org.apache.directory.server.unit.AbstractServerTest;
+import org.apache.directory.shared.ldap.codec.search.controls.ChangeType;
+import org.apache.directory.shared.ldap.codec.search.controls.EntryChangeControlCodec;
+import org.apache.directory.shared.ldap.codec.search.controls.EntryChangeControlDecoder;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.message.PersistentSearchControl;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * Testcase which tests the correct operation of the persistent search control.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class PersistentSearchTest extends AbstractServerTest
+{
+    public static final Logger log = LoggerFactory.getLogger( PersistentSearchTest.class );
+    public static final String PERSON_DESCRIPTION = "an American singer-songwriter";
+    public static final String RDN = "cn=Tori Amos";
+    private LdapContext ctx = null;
+
+
+    /**
+     * Creation of required attributes of a person entry.
+     */
+    protected Attributes getPersonAttributes( String sn, String cn )
+    {
+        Attributes attributes = new AttributesImpl();
+        Attribute attribute = new AttributeImpl( "objectClass" );
+        attribute.add( "top" );
+        attribute.add( "person" );
+        attributes.put( attribute );
+        attributes.put( "cn", cn );
+        attributes.put( "sn", sn );
+
+        return attributes;
+    }
+
+
+    /**
+     * Create context and a person entry.
+     */
+    @Before
+    public void setUp() throws Exception
+    {
+        super.setUp();
+
+        Hashtable<String, Object> env = new Hashtable<String, Object>();
+        env.put( "java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory" );
+        env.put( "java.naming.provider.url", "ldap://localhost:" + port + "/ou=system" );
+        env.put( "java.naming.security.principal", "uid=admin,ou=system" );
+        env.put( "java.naming.security.credentials", "secret" );
+        env.put( "java.naming.security.authentication", "simple" );
+
+        ctx = new InitialLdapContext( env, null );
+        assertNotNull( ctx );
+
+        // Create a person with description
+        Attributes attributes = this.getPersonAttributes( "Amos", "Tori Amos" );
+        attributes.put( "description", PERSON_DESCRIPTION );
+        ctx.createSubcontext( RDN, attributes );
+    }
+
+
+    /**
+     * Remove person entry and close context.
+     */
+    @After
+    public void tearDown() throws Exception
+    {
+        try
+        {
+            ctx.unbind( RDN );
+            ctx.close();
+            ctx = null;
+            super.tearDown();
+        }
+        catch ( Throwable t )
+        {
+        }
+    }
+
+
+    /**
+     * Shows correct notifications for modify(4) changes.
+     */
+    @Test
+    public void testPsearchModify() throws Exception
+    {
+        PSearchListener listener = new PSearchListener();
+        Thread t = new Thread( listener, "PSearchListener" );
+        t.start();
+
+        // let's wait until the listener thread started
+        while ( !listener.isReady )
+        {
+            Thread.sleep( 100 );
+        }
+        // Now we wait until the listener is registered (timing dependent crap)
+        Thread.sleep( 250 );
+
+        ctx.modifyAttributes( RDN, DirContext.REMOVE_ATTRIBUTE, 
+            new AttributesImpl( "description", PERSON_DESCRIPTION, true ) );
+        long start = System.currentTimeMillis();
+        while ( t.isAlive() )
+        {
+            Thread.sleep( 200 );
+            if ( System.currentTimeMillis() - start > 3000 )
+            {
+                break;
+            }
+        }
+
+        assertNotNull( listener.result );
+        assertEquals( RDN, listener.result.getName() );
+    }
+
+
+    /**
+     * Shows correct notifications for moddn(8) changes.
+     */
+    @Test
+    public void testPsearchModifyDn() throws Exception
+    {
+        PSearchListener listener = new PSearchListener();
+        Thread t = new Thread( listener );
+        t.start();
+
+        while ( !listener.isReady )
+        {
+            Thread.sleep( 100 );
+        }
+        Thread.sleep( 250 );
+
+        ctx.rename( RDN, "cn=Jack Black" );
+
+        long start = System.currentTimeMillis();
+        while ( t.isAlive() )
+        {
+            Thread.sleep( 100 );
+            if ( System.currentTimeMillis() - start > 3000 )
+            {
+                break;
+            }
+        }
+
+        assertNotNull( listener.result );
+        assertEquals( "cn=Jack Black", listener.result.getName() );
+    }
+
+
+    /**
+     * Shows correct notifications for delete(2) changes.
+     */
+    @Test
+    public void testPsearchDelete() throws Exception
+    {
+        PSearchListener listener = new PSearchListener();
+        Thread t = new Thread( listener );
+        t.start();
+
+        while ( !listener.isReady )
+        {
+            Thread.sleep( 100 );
+        }
+        Thread.sleep( 250 );
+
+        ctx.destroySubcontext( RDN );
+
+        long start = System.currentTimeMillis();
+        while ( t.isAlive() )
+        {
+            Thread.sleep( 100 );
+            if ( System.currentTimeMillis() - start > 3000 )
+            {
+                break;
+            }
+        }
+
+        assertNotNull( listener.result );
+        assertEquals( RDN, listener.result.getName() );
+    }
+
+
+    /**
+     * Shows correct notifications for add(1) changes.
+     */
+    @Test
+    public void testPsearchAdd() throws Exception
+    {
+        PSearchListener listener = new PSearchListener();
+        Thread t = new Thread( listener );
+        t.start();
+
+        while ( !listener.isReady )
+        {
+            Thread.sleep( 100 );
+        }
+        Thread.sleep( 250 );
+
+        ctx.createSubcontext( "cn=Jack Black", getPersonAttributes( "Black", "Jack Black" ) );
+
+        long start = System.currentTimeMillis();
+        while ( t.isAlive() )
+        {
+            Thread.sleep( 100 );
+            if ( System.currentTimeMillis() - start > 3000 )
+            {
+                break;
+            }
+        }
+
+        assertNotNull( listener.result );
+        assertEquals( "cn=Jack Black", listener.result.getName() );
+    }
+
+
+    /**
+     * Shows correct notifications for modify(4) changes with returned 
+     * EntryChangeControl.
+     */
+    @Test
+    public void testPsearchModifyWithEC() throws Exception
+    {
+        PersistentSearchControl control = new PersistentSearchControl();
+        control.setReturnECs( true );
+        PSearchListener listener = new PSearchListener( control );
+        Thread t = new Thread( listener, "PSearchListener" );
+        t.start();
+
+        while ( !listener.isReady )
+        {
+            Thread.sleep( 100 );
+        }
+        Thread.sleep( 250 );
+
+        ctx.modifyAttributes( RDN, DirContext.REMOVE_ATTRIBUTE, new AttributesImpl( "description", PERSON_DESCRIPTION,
+            true ) );
+        long start = System.currentTimeMillis();
+        while ( t.isAlive() )
+        {
+            Thread.sleep( 200 );
+            if ( System.currentTimeMillis() - start > 3000 )
+            {
+                break;
+            }
+        }
+
+        assertNotNull( listener.result );
+        assertEquals( RDN, listener.result.getName() );
+        assertEquals( listener.result.control.getChangeType(), ChangeType.MODIFY );
+    }
+
+
+    /**
+     * Shows correct notifications for moddn(8) changes with returned 
+     * EntryChangeControl.
+     */
+    @Test
+    public void testPsearchModifyDnWithEC() throws Exception
+    {
+        PersistentSearchControl control = new PersistentSearchControl();
+        control.setReturnECs( true );
+        PSearchListener listener = new PSearchListener( control );
+        Thread t = new Thread( listener );
+        t.start();
+
+        while ( !listener.isReady )
+        {
+            Thread.sleep( 100 );
+        }
+        Thread.sleep( 250 );
+
+        ctx.rename( RDN, "cn=Jack Black" );
+
+        long start = System.currentTimeMillis();
+        while ( t.isAlive() )
+        {
+            Thread.sleep( 100 );
+            if ( System.currentTimeMillis() - start > 3000 )
+            {
+                break;
+            }
+        }
+
+        assertNotNull( listener.result );
+        assertEquals( "cn=Jack Black", listener.result.getName() );
+        assertEquals( listener.result.control.getChangeType(), ChangeType.MODDN );
+        assertEquals( ( RDN + ",ou=system" ), listener.result.control.getPreviousDn().getUpName() );
+    }
+
+
+    /**
+     * Shows correct notifications for delete(2) changes with returned 
+     * EntryChangeControl.
+     */
+    @Test
+    public void testPsearchDeleteWithEC() throws Exception
+    {
+        PersistentSearchControl control = new PersistentSearchControl();
+        control.setReturnECs( true );
+        PSearchListener listener = new PSearchListener( control );
+        Thread t = new Thread( listener );
+        t.start();
+
+        while ( !listener.isReady )
+        {
+            Thread.sleep( 100 );
+        }
+        Thread.sleep( 250 );
+
+        ctx.destroySubcontext( RDN );
+
+        long start = System.currentTimeMillis();
+        while ( t.isAlive() )
+        {
+            Thread.sleep( 100 );
+            if ( System.currentTimeMillis() - start > 3000 )
+            {
+                break;
+            }
+        }
+
+        assertNotNull( listener.result );
+        assertEquals( RDN, listener.result.getName() );
+        assertEquals( listener.result.control.getChangeType(), ChangeType.DELETE );
+    }
+
+
+    /**
+     * Shows correct notifications for add(1) changes with returned 
+     * EntryChangeControl.
+     */
+    @Test
+    public void testPsearchAddWithEC() throws Exception
+    {
+        PersistentSearchControl control = new PersistentSearchControl();
+        control.setReturnECs( true );
+        PSearchListener listener = new PSearchListener( control );
+        Thread t = new Thread( listener );
+        t.start();
+
+        while ( !listener.isReady )
+        {
+            Thread.sleep( 100 );
+        }
+        Thread.sleep( 250 );
+
+        ctx.createSubcontext( "cn=Jack Black", getPersonAttributes( "Black", "Jack Black" ) );
+
+        long start = System.currentTimeMillis();
+        while ( t.isAlive() )
+        {
+            Thread.sleep( 100 );
+            if ( System.currentTimeMillis() - start > 3000 )
+            {
+                break;
+            }
+        }
+
+        assertNotNull( listener.result );
+        assertEquals( "cn=Jack Black", listener.result.getName() );
+        assertEquals( listener.result.control.getChangeType(), ChangeType.ADD );
+    }
+
+
+    /**
+     * Shows correct notifications for only add(1) and modify(4) registered changes with returned 
+     * EntryChangeControl.
+     */
+    @Test
+    public void testPsearchAddModifyEnabledWithEC() throws Exception
+    {
+        PersistentSearchControl control = new PersistentSearchControl();
+        control.setReturnECs( true );
+        control.setChangeTypes( ChangeType.ADD_VALUE );
+        control.enableNotification( ChangeType.MODIFY );
+        PSearchListener listener = new PSearchListener( control );
+        Thread t = new Thread( listener );
+        t.start();
+
+        while ( !listener.isReady )
+        {
+            Thread.sleep( 100 );
+        }
+        Thread.sleep( 250 );
+
+        ctx.createSubcontext( "cn=Jack Black", getPersonAttributes( "Black", "Jack Black" ) );
+
+        long start = System.currentTimeMillis();
+        while ( t.isAlive() )
+        {
+            Thread.sleep( 100 );
+            if ( System.currentTimeMillis() - start > 3000 )
+            {
+                break;
+            }
+        }
+
+        assertNotNull( listener.result );
+        assertEquals( "cn=Jack Black", listener.result.getName() );
+        assertEquals( listener.result.control.getChangeType(), ChangeType.ADD );
+        listener.result = null;
+        t = new Thread( listener );
+        t.start();
+
+        ctx.destroySubcontext( "cn=Jack Black" );
+
+        start = System.currentTimeMillis();
+        while ( t.isAlive() )
+        {
+            Thread.sleep( 100 );
+            if ( System.currentTimeMillis() - start > 3000 )
+            {
+                break;
+            }
+        }
+
+        assertNull( listener.result );
+
+        // thread is still waiting for notifications try a modify
+        ctx.modifyAttributes( RDN, DirContext.REMOVE_ATTRIBUTE, new AttributesImpl( "description", PERSON_DESCRIPTION,
+            true ) );
+        start = System.currentTimeMillis();
+        while ( t.isAlive() )
+        {
+            Thread.sleep( 200 );
+            if ( System.currentTimeMillis() - start > 3000 )
+            {
+                break;
+            }
+        }
+
+        assertNotNull( listener.result );
+        assertEquals( RDN, listener.result.getName() );
+        assertEquals( listener.result.control.getChangeType(), ChangeType.MODIFY );
+    }
+
+
+    /**
+     * Shows correct notifications for add(1) changes with returned 
+     * EntryChangeControl and changesOnly set to false so we return
+     * the first set of entries.
+     * 
+     * This test is commented out because it exhibits some producer
+     * consumer lockups (server and client being in same process)
+     * 
+     * PLUS ALL THIS GARBAGE IS TIME DEPENDENT!!!!!
+     */
+    //    public void testPsearchAddWithECAndFalseChangesOnly() throws Exception
+    //    {
+    //        PersistentSearchControl control = new PersistentSearchControl();
+    //        control.setReturnECs( true );
+    //        control.setChangesOnly( false );
+    //        PSearchListener listener = new PSearchListener( control );
+    //        Thread t = new Thread( listener );
+    //        t.start();
+    //        
+    //        Thread.sleep( 3000 );
+    //
+    //        assertEquals( 5, listener.count );
+    //        ctx.createSubcontext( "cn=Jack Black", getPersonAttributes( "Black", "Jack Black" ) );
+    //        
+    //        long start = System.currentTimeMillis();
+    //        while ( t.isAlive() )
+    //        {
+    //            Thread.sleep( 100 );
+    //            if ( System.currentTimeMillis() - start > 3000 )
+    //            {
+    //                break;
+    //            }
+    //        }
+    //        
+    //        assertEquals( 6, listener.count );
+    //        assertNotNull( listener.result );
+    //        assertEquals( "cn=Jack Black", listener.result.getName() );
+    //        assertEquals( listener.result.control.getChangeType(), ChangeType.ADD );
+    //    }
+
+    /**
+     * Shows notifications functioning with the JNDI notification API of the SUN
+     * provider.
+     */
+    @Test
+    public void testPsearchUsingJndiNotifications() throws Exception
+    {
+        Hashtable<String, Object> env = new Hashtable<String, Object>();
+        env.put( "java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory" );
+        env.put( "java.naming.provider.url", "ldap://localhost:" + port + "/ou=system" );
+        env.put( "java.naming.security.principal", "uid=admin,ou=system" );
+        env.put( "java.naming.security.credentials", "secret" );
+        env.put( "java.naming.security.authentication", "simple" );
+
+        JndiNotificationListener listener = new JndiNotificationListener();
+        InitialDirContext idc = new InitialDirContext( env );
+        EventDirContext edc = ( EventDirContext ) ( idc.lookup( "" ) );
+        edc.addNamingListener( "", EventContext.ONELEVEL_SCOPE, listener );
+
+        while ( listener.list.isEmpty() )
+        {
+            Thread.sleep( 250 );
+            String rdn = "cn=Jack Black";
+            ctx.createSubcontext( rdn, getPersonAttributes( "Black", "Jack Black" ) );
+            ctx.destroySubcontext( rdn );
+        }
+
+        if ( ! listener.hasError )
+        {
+            EventObject event = listener.list.get( 0 );
+            assertEquals( edc, event.getSource() );
+        }
+        else
+        {
+            throw new RuntimeException( "got naming exception while processing events", 
+                listener.exceptionEvent.getException() );
+        }
+    }
+
+
+    /**
+     * Shows notifications functioning with the JNDI notification API of the SUN
+     * provider.
+     *
+    @Test
+    public void testPsearchAbandon() throws Exception
+    {
+        PersistentSearchControl control = new PersistentSearchControl();
+        control.setReturnECs( true );
+        PSearchListener listener = new PSearchListener( control );
+        Thread t = new Thread( listener );
+        t.start();
+
+        while ( !listener.isReady )
+        {
+            Thread.sleep( 100 );
+        }
+        Thread.sleep( 250 );
+
+        ctx.createSubcontext( "cn=Jack Black", getPersonAttributes( "Black", "Jack Black" ) );
+
+        long start = System.currentTimeMillis();
+        while ( t.isAlive() )
+        {
+            Thread.sleep( 100 );
+            if ( System.currentTimeMillis() - start > 3000 )
+            {
+                break;
+            }
+        }
+
+        assertNotNull( listener.result );
+        assertEquals( "cn=Jack Black", listener.result.getName() );
+        assertEquals( listener.result.control.getChangeType(), ChangeType.ADD );
+        
+        listener = new PSearchListener( control );
+
+        t = new Thread( listener );
+        t.start();
+
+        ctx.destroySubcontext( "cn=Jack Black" );
+
+        start = System.currentTimeMillis();
+        while ( t.isAlive() )
+        {
+            Thread.sleep( 100 );
+            if ( System.currentTimeMillis() - start > 3000 )
+            {
+                break;
+            }
+        }
+
+        // there seems to be a race condition here
+        // assertNull( listener.result );
+        assertNotNull( listener.result );
+        assertEquals( "cn=Jack Black", listener.result.getName() );
+        assertEquals( ChangeType.DELETE, listener.result.control.getChangeType() );
+        listener.result = null;
+
+        // thread is still waiting for notifications try a modify
+        ctx.modifyAttributes( RDN, DirContext.REMOVE_ATTRIBUTE, new AttributesImpl( "description", PERSON_DESCRIPTION,
+            true ) );
+        start = System.currentTimeMillis();
+        while ( t.isAlive() )
+        {
+            Thread.sleep( 200 );
+            if ( System.currentTimeMillis() - start > 3000 )
+            {
+                break;
+            }
+        }
+
+        assertNull( listener.result );
+        //assertEquals( RDN, listener.result.getName() );
+        //assertEquals( listener.result.control.getChangeType(), ChangeType.MODIFY );
+    }*/
+
+    class JndiNotificationListener implements NamespaceChangeListener, ObjectChangeListener
+    {
+        boolean hasError = false;
+        ArrayList<EventObject> list = new ArrayList();
+        NamingExceptionEvent exceptionEvent = null;
+
+        public void objectAdded( NamingEvent evt )
+        {
+            list.add( 0, evt );
+        }
+
+
+        public void objectRemoved( NamingEvent evt )
+        {
+            list.add( 0, evt );
+        }
+
+
+        public void objectRenamed( NamingEvent evt )
+        {
+            list.add( 0, evt );
+        }
+
+
+        public void namingExceptionThrown( NamingExceptionEvent evt )
+        {
+            hasError = true;
+            exceptionEvent = evt;
+            list.add( 0, evt );
+        }
+
+
+        public void objectChanged( NamingEvent evt )
+        {
+            list.add( 0, evt );
+        }
+    }
+
+    class PSearchListener implements Runnable
+    {
+        boolean isReady = false;
+        PSearchNotification result;
+        int count = 0;
+        final PersistentSearchControl control;
+
+
+        PSearchListener()
+        {
+            control = new PersistentSearchControl();
+        }
+
+
+        PSearchListener(PersistentSearchControl control)
+        {
+            this.control = control;
+        }
+
+
+        public void run()
+        {
+            NamingEnumeration list = null;
+            control.setCritical( true );
+            Control[] ctxCtls = new Control[]
+                { control };
+
+            try
+            {
+                ctx.setRequestControls( ctxCtls );
+                isReady = true;
+                list = ctx.search( "", "objectClass=*", null );
+                EntryChangeControlCodec ecControl = null;
+
+                while ( list.hasMore() )
+                {
+                    Control[] controls = null;
+                    SearchResult sresult = ( SearchResult ) list.next();
+                    count++;
+                    if ( sresult instanceof HasControls )
+                    {
+                        controls = ( ( HasControls ) sresult ).getControls();
+                        if ( controls != null )
+                        {
+                            for ( int ii = 0; ii < controls.length; ii++ )
+                            {
+                                if ( controls[ii].getID().equals(
+                                    org.apache.directory.shared.ldap.message.EntryChangeControl.CONTROL_OID ) )
+                                {
+                                    EntryChangeControlDecoder decoder = new EntryChangeControlDecoder();
+                                    ecControl = ( EntryChangeControlCodec ) decoder.decode( controls[ii].getEncodedValue() );
+                                }
+                            }
+                        }
+                    }
+                    result = new PSearchNotification( sresult, ecControl );
+                    break;
+                }
+            }
+            catch ( Exception e )
+            {
+                e.printStackTrace();
+            }
+            finally
+            {
+                if ( list != null )
+                {
+                    try
+                    {
+                        list.close();
+                    }
+                    catch ( Exception e )
+                    {
+                        e.printStackTrace();
+                    }
+                }
+            }
+        }
+    }
+
+    class PSearchNotification extends SearchResult
+    {
+        private static final long serialVersionUID = 1L;
+        final EntryChangeControlCodec control;
+
+
+        public PSearchNotification(SearchResult result, EntryChangeControlCodec control)
+        {
+            super( result.getName(), result.getClassName(), result.getObject(), result.getAttributes(), result
+                .isRelative() );
+            this.control = control;
+        }
+
+
+        public String toString()
+        {
+            StringBuffer buf = new StringBuffer();
+            buf.append( "DN: " ).append( getName() ).append( "\n" );
+            if ( control != null )
+            {
+                buf.append( "    EntryChangeControl =\n" );
+                buf.append( "       changeType   : " ).append( control.getChangeType() ).append( "\n" );
+                buf.append( "       previousDN   : " ).append( control.getPreviousDn() ).append( "\n" );
+                buf.append( "       changeNumber : " ).append( control.getChangeNumber() ).append( "\n" );
+            }
+            return buf.toString();
+        }
+    }
+}
diff --git a/old_trunk/server-unit/src/test/java/org/apache/directory/server/ReferralITest.java b/old_trunk/server-unit/src/test/java/org/apache/directory/server/ReferralITest.java
new file mode 100644
index 0000000..cad18e1
--- /dev/null
+++ b/old_trunk/server-unit/src/test/java/org/apache/directory/server/ReferralITest.java
@@ -0,0 +1,558 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.
+ *
+ */
+package org.apache.directory.server;
+
+
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Set;
+
+import javax.naming.Context;
+import javax.naming.Name;
+import javax.naming.NameAlreadyBoundException;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.ReferralException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.InitialLdapContext;
+import javax.naming.ldap.LdapContext;
+
+import org.apache.directory.server.unit.AbstractServerTest;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+
+
+/**
+ * Tests to make sure the server is operating correctly when handling referrals.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 493916 $
+ */
+public class ReferralITest extends AbstractServerTest
+{
+    TestData td = new TestData();
+
+
+    /**
+     * Create attributes for a person entry.
+     */
+    protected Attributes getPersonAttributes( String sn, String cn )
+    {
+        Attributes attributes = new AttributesImpl();
+        Attribute attribute = new AttributeImpl( "objectClass" );
+        attribute.add( "top" );
+        attribute.add( "person" );
+        attributes.put( attribute );
+        attributes.put( "cn", cn );
+        attributes.put( "sn", sn );
+        attributes.put( "description", cn + " is a person." );
+        return attributes;
+    }
+
+
+    public void setUp() throws Exception
+    {
+        super.setUp();
+        addReferralEntries();
+        addCountries();
+        addCities();
+        addUsers();
+        addReferralEntries();
+    }
+
+
+    public void tearDown() throws Exception
+    {
+        if ( td.refEurop != null )
+        {
+            td.refEurop.close();
+        }
+
+        if ( td.refAmerica != null )
+        {
+            td.refAmerica.close();
+        }
+
+        td.ctx.close();
+
+        super.tearDown();
+    }
+
+
+    /**
+     * Get's the root "ou=system" using the SUN JNDI provider on the embedded ApacheDS instance
+     */
+    private LdapContext getSystemRoot() throws NamingException
+    {
+        Hashtable<String, Object> env = new Hashtable<String, Object>();
+        env.put( "java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory" );
+        env.put( "java.naming.provider.url", "ldap://localhost:" + port + "/ou=system" );
+        env.put( "java.naming.security.principal", "uid=admin,ou=system" );
+        env.put( "java.naming.security.credentials", "secret" );
+        env.put( "java.naming.security.authentication", "simple" );
+        LdapContext ctx = new InitialLdapContext( env, null );
+        assertNotNull( ctx );
+        return ctx;
+    }
+
+    private class TestData
+    {
+        LdapContext ctx;
+        Name ctxDn;
+        LdapContext refEurop;
+        LdapContext refAmerica;
+        List refs;
+    }
+
+
+    /**
+     * Create entries
+     * c=europ, ou=system
+     * and
+     * c=america, ou=system
+     */
+    private void addReferralEntries() throws NamingException
+    {
+        String europURL = "ldap://localhost:" + port + "/c=france,ou=system";
+        String americaURL = "ldap://localhost:" + port + "/c=usa,ou=system";
+
+        td.ctx = getSystemRoot();
+
+        // -------------------------------------------------------------------
+        // Adds a referral entry regardless of referral handling settings
+        // -------------------------------------------------------------------
+
+        // Add a referral entry for europ
+        Attributes europ = new AttributesImpl( "objectClass", "top", true );
+        europ.get( "objectClass" ).add( "referral" );
+        europ.get( "objectClass" ).add( "extensibleObject" );
+        europ.put( "ref", europURL );
+        europ.put( "c", "europ" );
+
+        // Add a referral entry for america
+        Attributes america = new AttributesImpl( "objectClass", "top", true );
+        america.get( "objectClass" ).add( "referral" );
+        america.get( "objectClass" ).add( "extensibleObject" );
+        america.put( "ref", americaURL );
+        america.put( "c", "america" );
+
+        // Just in case if server is a remote server destroy remaing referral
+        td.ctx.addToEnvironment( Context.REFERRAL, "ignore" );
+
+        try
+        {
+            td.refEurop = ( LdapContext ) td.ctx.createSubcontext( "c=europ", europ );
+            td.refAmerica = ( LdapContext ) td.ctx.createSubcontext( "c=america", america );
+        }
+        catch ( NameAlreadyBoundException e )
+        {
+        }
+    }
+
+    /**
+     * Create entries
+     * c=france, ou=system
+     * and
+     * c=usa, ou=system
+     */
+    private void addCountries() throws NamingException
+    {
+        td.ctx = getSystemRoot();
+        
+        // -------------------------------------------------------------------
+        // Adds a referral entry regardless of referral handling settings
+        // -------------------------------------------------------------------
+        
+        // Add a referral entry for europ
+        Attributes france = new AttributesImpl( "objectClass", "top", true );
+        france.get( "objectClass" ).add( "country" );
+        france.put( "c", "France" );
+        
+        // Add a referral entry for america
+        Attributes usa = new AttributesImpl( "objectClass", "top", true );
+        usa.get( "objectClass" ).add( "country" );
+        usa.put( "c", "USA" );
+        
+        try
+        {
+            td.ctx.createSubcontext( "c=france", france );
+            td.ctx.createSubcontext( "c=usa", usa );
+        }
+        catch ( NameAlreadyBoundException e )
+        {
+        }
+    }
+
+    /**
+     * Create cities
+     * l=paris, c=france, ou=system
+     * and
+     * l=jacksonville, c=usa, ou=system
+     */
+    private void addCities() throws NamingException
+    {
+        td.ctx = getSystemRoot();
+        
+        // -------------------------------------------------------------------
+        // Adds a referral entry regardless of referral handling settings
+        // -------------------------------------------------------------------
+        
+        // Add a referral entry for europ
+        Attributes paris = new AttributesImpl( "objectClass", "top", true );
+        paris.get( "objectClass" ).add( "locality" );
+        paris.put( "l", "Paris" );
+        
+        // Add a referral entry for america
+        Attributes jacksonville = new AttributesImpl( "objectClass", "top", true );
+        jacksonville.get( "objectClass" ).add( "locality" );
+        jacksonville.put( "l", "jacksonville" );
+        
+        try
+        {
+            td.ctx.createSubcontext( "l=paris, c=france", paris );
+            td.ctx.createSubcontext( "l=jacksonville, c=usa", jacksonville );
+        }
+        catch ( NameAlreadyBoundException e )
+        {
+        }
+    }
+
+    /**
+     * Create users
+     * cn=emmanuel lecharny, l=paris, c=france, ou=system
+     * and
+     * cn=alex karasulu, l=jacksonville, c=usa, ou=system
+     */
+    private void addUsers() throws NamingException
+    {
+        td.ctx = getSystemRoot();
+    
+        // -------------------------------------------------------------------
+        // Adds a referral entry regardless of referral handling settings
+        // -------------------------------------------------------------------
+    
+        // Add a referral entry for europ
+        Attributes emmanuel = new AttributesImpl( "objectClass", "top", true );
+        emmanuel.get( "objectClass" ).add( "person" );
+        emmanuel.get( "objectClass" ).add( "residentialperson" );
+        emmanuel.put( "cn", "emmanuel lecharny" );
+        emmanuel.put( "sn", "elecharny" );
+        emmanuel.put( "l", "Paris" );
+
+
+        // Add a referral entry for america
+        Attributes alex = new AttributesImpl( "objectClass", "top", true );
+        alex.get( "objectClass" ).add( "person" );
+        alex.get( "objectClass" ).add( "residentialperson" );
+        alex.put( "cn", "alex karasulu" );
+        alex.put( "sn", "akarasulu" );
+        alex.put( "l", "Jacksonville" );
+        
+        try
+        {
+            td.ctx.createSubcontext( "cn=emmanuel lecharny,l=paris,c=france", emmanuel );
+            td.ctx.createSubcontext( "cn=alex karasulu,l=jacksonville,c=usa", alex );
+        }
+        catch ( NameAlreadyBoundException e )
+        {
+        }
+    }
+        
+    /**
+     * Performs a search from a base and
+     * check that the expected result is found
+     */
+    private boolean exist( LdapContext ctx, String filter, String expected ) throws NamingException
+    {
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+
+        return exist( ctx, filter, expected, controls );
+    }
+
+    /**
+     * Performs a search from a base and
+     * check that the expected result is found
+     */
+    private boolean exist( LdapContext ctx, String filter, String expected,
+        SearchControls controls ) throws NamingException
+    {
+        NamingEnumeration ii = ctx.search( "", filter, controls );
+    
+        // collect all results
+        Set results = new HashSet();
+    
+        while ( ii.hasMore() )
+        {
+            SearchResult result = ( SearchResult ) ii.next();
+            results.add( result.getName() );
+        }
+    
+        if ( results.size() == 1 )
+        {
+            return results.contains( expected );
+        }
+        
+        return false;
+    }
+
+
+    /**
+     * Performs a single level search from a contect base and
+     * return the result as a Set, or throws an exception
+     */
+    private Set search( LdapContext ctx, String filter ) throws NamingException
+    {
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+
+        return search( ctx, filter, controls );
+    }
+
+    /**
+     * Performs a single level search from a contect base and
+     * return the result as a Set, or throws an exception
+     */
+    private Set search( LdapContext ctx, String filter, SearchControls controls ) throws NamingException
+    {
+        NamingEnumeration ii = ctx.search( "", filter, controls );
+
+        // collect all results
+        Set results = new HashSet();
+
+        while ( ii.hasMore() )
+        {
+            SearchResult result = ( SearchResult ) ii.next();
+            results.add( result.getName() );
+        }
+
+        return results;
+    }
+
+    //-------------------------------------------------------------------------
+    //
+    // Search operations
+    //
+    //-------------------------------------------------------------------------
+    /**
+     * Test of an search operation with a referral
+     *
+     * search for "cn=alex karasulu" on "c=america, ou=system"
+     * we should get a referral URL thrown, which point to
+     * "c=usa, ou=system", and ask for a subtree search
+     */
+    public void testSearchWithReferralThrow() throws Exception
+    {
+        td.refAmerica.addToEnvironment( Context.REFERRAL, "throw" );
+
+        try
+        {
+            search( td.refAmerica, "(cn=alex karasulu)" );
+            fail( "Should fail here throwing a ReferralException" );
+        }
+        catch ( ReferralException re )
+        {
+            String referral = (String)re.getReferralInfo();
+            assertEquals( "ldap://localhost:" + port + "/c=usa,ou=system??sub", referral );
+        }
+    }
+
+
+    /**
+     * Test of an search operation with a referral
+     *
+     * search for "cn=alex karasulu" on "c=america, ou=system"
+     * we should get a referral URL thrown, which point to
+     * "c=usa, ou=system", and ask for a subtree search
+     */
+    public void testSearchBaseWithReferralThrow() throws Exception
+    {
+        td.refAmerica.addToEnvironment( Context.REFERRAL, "throw" );
+
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.OBJECT_SCOPE );
+
+        try
+        {
+            search( td.refAmerica, "(cn=alex karasulu)", controls );
+            fail( "Should fail here throwing a ReferralException" );
+        }
+        catch ( ReferralException re )
+        {
+            String referral = (String)re.getReferralInfo();
+            assertEquals( "ldap://localhost:" + port + "/c=usa,ou=system??base", referral );
+        }
+    }
+
+    /**
+     * Test of an search operation with a referral
+     *
+     * search for "cn=alex karasulu" on "c=america, ou=system"
+     * we should get a referral URL thrown, which point to
+     * "c=usa, ou=system", and ask for a subtree search
+     */
+    public void testSearchOneLevelWithReferralThrow() throws Exception
+    {
+        td.refAmerica.addToEnvironment( Context.REFERRAL, "throw" );
+
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+
+        try
+        {
+            search( td.refAmerica, "(cn=alex karasulu)", controls );
+            fail( "Should fail here throwing a ReferralException" );
+        }
+        catch ( ReferralException re )
+        {
+            String referral = (String)re.getReferralInfo();
+            assertEquals( "ldap://localhost:" + port + "/c=usa,ou=system??one", referral );
+        }
+    }
+
+
+    /**
+     * Test of an search operation with a referral
+     *
+     * search for "cn=alex karasulu" on "c=america, ou=system"
+     * we should get a referral URL thrown, which point to
+     * "c=usa, ou=system", and ask for a subtree search
+     */
+    public void testSearchWithReferralContinuation() throws Exception
+    {
+        assertTrue( exist( td.ctx, "(cn=alex karasulu)",
+        "cn=alex karasulu,l=jacksonville,c=usa" ) );
+
+        td.refAmerica.addToEnvironment( Context.REFERRAL, "follow" );
+
+        assertTrue( exist( td.refAmerica, "(cn=alex karasulu)",
+            "ldap://localhost:" + port + "/cn=alex%20karasulu,l=jacksonville,c=usa,ou=system" ) );
+    }
+
+    /**
+     * Test of an search operation with a referral
+     *
+     * search for "cn=alex karasulu" on "c=america, ou=system"
+     * we should get a referral URL thrown, which point to
+     * "c=usa, ou=system", and ask for a subtree search
+     */
+    public void testSearchBaseWithReferralContinuation() throws Exception
+    {
+        td.refAmerica.addToEnvironment( Context.REFERRAL, "follow" );
+
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.OBJECT_SCOPE );
+
+        assertFalse( exist( td.refAmerica, "(cn=alex karasulu)",
+            "cn=alex karasulu,l=jacksonville,c=usa", controls ) );
+    }
+
+    /**
+     * Test of an search operation with a referral
+     *
+     * search for "cn=alex karasulu" on "c=america, ou=system"
+     * we should get a referral URL thrown, which point to
+     * "c=usa, ou=system", and ask for a subtree search
+     */
+    public void testSearchOneLevelWithReferralContinuation() throws Exception
+    {
+        td.refAmerica.addToEnvironment( Context.REFERRAL, "follow" );
+
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+
+        assertFalse( exist( td.refAmerica, "(cn=alex karasulu)",
+            "cn=alex karasulu,l=jacksonville,c=usa", controls ) );
+    }
+
+    //-------------------------------------------------------------------------
+    //
+    // Add operations
+    //
+    //-------------------------------------------------------------------------
+
+    /**
+     * Test of an add operation with a referral
+     * "cn=pierre-arnaud marcelot,l=paris" is added to "c=france,ou=system"
+     * we should get a referral URL thrown
+     */
+    public void testAddWithReferralThrow() throws Exception
+    {
+        // -------------------------------------------------------------------
+        // Attempt to add a normal entry below the referral parent. We should
+        // encounter referral errors with referral setting set to throw.
+        // -------------------------------------------------------------------
+
+        td.refEurop.addToEnvironment( Context.REFERRAL, "throw" );
+
+        Attributes userEntry = new AttributesImpl( "objectClass", "top", true );
+        userEntry.get( "objectClass" ).add( "person" );
+        userEntry.put( "sn", "marcelot" );
+        userEntry.put( "cn", "pierre-arnaud marcelot" );
+
+        try
+        {
+            td.refEurop.createSubcontext( "cn=pierre-arnaud marcelot,l=paris", userEntry );
+            fail( "Should fail here throwing a ReferralException" );
+        }
+        catch ( ReferralException re )
+        {
+            String referral = (String)re.getReferralInfo();
+            // @TODO : the returned LDAPURL must be escaped !!!
+            assertEquals( "ldap://localhost:" + port + "/cn=pierre-arnaud%20marcelot,l=paris,c=france,ou=system", referral );
+        }
+    }
+    
+
+    /**
+     * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
+     * for an add operation with the parent context being a referral.
+     *
+     * Test an add operation with a continuation
+     *
+     * "cn=pierre-arnaud marcelot,l=paris" is added to "c=europ,ou=system"
+     *
+     * The entry should be added to "l=paris,c=france,ou=system"
+     */
+    public void testAddWithReferralContinuation() throws Exception
+    {
+        // -------------------------------------------------------------------
+        // Attempt to add a normal entry below the referral parent. We should
+        // encounter referral errors with referral setting set to throw.
+        // -------------------------------------------------------------------
+
+        td.refEurop.addToEnvironment( Context.REFERRAL, "follow" );
+
+        Attributes userEntry = new AttributesImpl( "objectClass", "top", true );
+        userEntry.get( "objectClass" ).add( "person" );
+        userEntry.put( "sn", "marcelot" );
+        userEntry.put( "cn", "pierre-arnaud marcelot" );
+
+        td.refEurop.createSubcontext( "cn=pierre-arnaud marcelot,l=paris", userEntry );
+
+        assertTrue( exist( td.ctx, "(cn=pierre-arnaud marcelot)", "cn=pierre-arnaud marcelot,l=paris,c=france" ) );
+
+        td.refEurop.destroySubcontext( "cn=pierre-arnaud marcelot,l=paris" );
+    }
+}
diff --git a/old_trunk/server-unit/src/test/java/org/apache/directory/server/ReferralTest.java b/old_trunk/server-unit/src/test/java/org/apache/directory/server/ReferralTest.java
new file mode 100644
index 0000000..9c49922
--- /dev/null
+++ b/old_trunk/server-unit/src/test/java/org/apache/directory/server/ReferralTest.java
@@ -0,0 +1,931 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server;
+
+
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+
+import javax.naming.Context;
+import javax.naming.Name;
+import javax.naming.NameAlreadyBoundException;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.PartialResultException;
+import javax.naming.ReferralException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.InitialLdapContext;
+import javax.naming.ldap.LdapContext;
+
+import org.apache.directory.server.unit.AbstractServerTest;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.message.ModificationItemImpl;
+
+
+/**
+ * Tests to make sure the server is operating correctly when handling referrals.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class ReferralTest extends AbstractServerTest
+{
+    TestData td = new TestData();
+
+
+    /**
+     * Create attributes for a person entry.
+     */
+    protected Attributes getPersonAttributes( String sn, String cn )
+    {
+        Attributes attributes = new AttributesImpl();
+        Attribute attribute = new AttributeImpl( "objectClass" );
+        attribute.add( "top" );
+        attribute.add( "person" );
+        attributes.put( attribute );
+        attributes.put( "cn", cn );
+        attributes.put( "sn", sn );
+        attributes.put( "description", cn + " is a person." );
+        return attributes;
+    }
+
+
+    public void setUp() throws Exception
+    {
+        super.setUp();
+        addReferralEntry();
+    }
+
+
+    public void tearDown() throws Exception
+    {
+        if ( td.refCtx != null )
+        {
+            td.refCtx.close();
+            td.rootCtx.close();
+        }
+
+        super.tearDown();
+    }
+
+
+    /**
+     * Get's the root "ou=system" using the SUN JNDI provider on the embedded ApacheDS instance
+     */
+    private LdapContext getSystemRoot() throws NamingException
+    {
+        Hashtable<String, Object> env = new Hashtable<String, Object>();
+        env.put( "java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory" );
+        env.put( "java.naming.provider.url", "ldap://localhost:" + port + "/ou=system" );
+        env.put( "java.naming.security.principal", "uid=admin,ou=system" );
+        env.put( "java.naming.security.credentials", "secret" );
+        env.put( "java.naming.security.authentication", "simple" );
+        LdapContext ctx = new InitialLdapContext( env, null );
+        assertNotNull( ctx );
+        return ctx;
+    }
+
+    class TestData
+    {
+        LdapContext rootCtx;
+        Name ctxDn;
+        LdapContext refCtx;
+        List refs;
+    }
+
+
+    public void addReferralEntry() throws NamingException
+    {
+        String ref0 = "ldap://fermi:10389/ou=users,ou=system";
+        String ref1 = "ldap://hertz:10389/ou=users,dc=example,dc=com";
+        String ref2 = "ldap://maxwell:10389/ou=users,ou=system";
+        td.rootCtx = getSystemRoot();
+
+        // -------------------------------------------------------------------
+        // Adds a referral entry regardless of referral handling settings
+        // -------------------------------------------------------------------
+
+        // Add a referral entry ( should be fine with or without the control )
+        Attributes referral = new AttributesImpl( "objectClass", "top", true );
+        referral.get( "objectClass" ).add( "referral" );
+        referral.get( "objectClass" ).add( "extensibleObject" );
+        referral.put( "ref", ref0 );
+        referral.get( "ref" ).add( ref1 );
+        referral.get( "ref" ).add( ref2 );
+        referral.put( "ou", "users" );
+
+        // Just in case if server is a remote server destroy remaing referral
+        td.rootCtx.addToEnvironment( Context.REFERRAL, "ignore" );
+        try
+        {
+            td.rootCtx.destroySubcontext( "uid=akarasulu,ou=users" );
+        }
+        catch ( NameNotFoundException e )
+        {
+        }
+        try
+        {
+            td.rootCtx.destroySubcontext( "ou=users" );
+        }
+        catch ( NameNotFoundException e )
+        {
+        }
+        try
+        {
+            td.refCtx = ( LdapContext ) td.rootCtx.createSubcontext( "ou=users", referral );
+        }
+        catch ( NameAlreadyBoundException e )
+        {
+            td.refCtx = ( LdapContext ) td.rootCtx.lookup( "ou=users" );
+        }
+        referral = td.refCtx.getAttributes( "" );
+        assertTrue( referral.get( "ou" ).contains( "users" ) );
+        assertTrue( referral.get( "objectClass" ).contains( "referral" ) );
+    }
+
+
+    public void checkAncestorReferrals( ReferralException e ) throws Exception
+    {
+        assertEquals( "ldap://fermi:10389", e.getReferralInfo() );
+        assertTrue( e.skipReferral() );
+        assertEquals( "ldap://hertz:10389/cn=alex%20karasulu,ou=apache,ou=users,dc=example,dc=com", e.getReferralInfo() );
+        assertTrue( e.skipReferral() );
+        assertEquals( "ldap://maxwell:10389", e.getReferralInfo() );
+        assertFalse( e.skipReferral() );
+    }
+
+
+    public void checkParentReferrals( ReferralException e ) throws Exception
+    {
+        assertEquals( "ldap://fermi:10389", e.getReferralInfo() );
+        assertTrue( e.skipReferral() );
+        assertEquals( "ldap://hertz:10389/cn=alex%20karasulu,ou=users,dc=example,dc=com", e.getReferralInfo() );
+        assertTrue( e.skipReferral() );
+        assertEquals( "ldap://maxwell:10389", e.getReferralInfo() );
+        assertFalse( e.skipReferral() );
+    }
+
+
+    /**
+     * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
+     * for an add operation with the parent context being a referral.
+     * 
+     * @throws Exception if something goes wrong.
+     */
+    public void testAddWithReferralParent() throws Exception
+    {
+        // -------------------------------------------------------------------
+        // Attempt to add a normal entry below the referral parent. We should
+        // encounter referral errors with referral setting set to throw.
+        // -------------------------------------------------------------------
+
+        td.refCtx.addToEnvironment( Context.REFERRAL, "throw" );
+        Attributes userEntry = new AttributesImpl( "objectClass", "top", true );
+        userEntry.get( "objectClass" ).add( "person" );
+        userEntry.put( "sn", "karasulu" );
+        userEntry.put( "cn", "alex karasulu" );
+
+        try
+        {
+            td.refCtx.createSubcontext( "cn=alex karasulu", userEntry );
+            fail( "Should fail here throwing a ReferralException" );
+        }
+        catch ( ReferralException e )
+        {
+            checkParentReferrals( e );
+        }
+    }
+
+
+    /**
+     * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
+     * for an add operation with an ancestor context being a referral.
+     * 
+     * @throws Exception if something goes wrong.
+     */
+    public void testAddWithReferralAncestor() throws Exception
+    {
+        // -------------------------------------------------------------------
+        // Attempt to add a normal entry below the referral ancestor. We should
+        // encounter referral errors with referral setting set to throw.
+        // -------------------------------------------------------------------
+
+        td.refCtx.addToEnvironment( Context.REFERRAL, "throw" );
+        Attributes userEntry = new AttributesImpl( "objectClass", "top", true );
+        userEntry.get( "objectClass" ).add( "person" );
+        userEntry.put( "sn", "karasulu" );
+        userEntry.put( "cn", "alex karasulu" );
+
+        try
+        {
+            td.refCtx.createSubcontext( "cn=alex karasulu,ou=apache", userEntry );
+            fail( "Should fail here throwing a ReferralException" );
+        }
+        catch ( ReferralException e )
+        {
+            checkAncestorReferrals( e );
+        }
+    }
+
+
+    /**
+     * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
+     * for an delete operation with the parent context being a referral.
+     * 
+     * @throws Exception if something goes wrong.
+     */
+    public void testDeleteWithReferralParent() throws Exception
+    {
+        // -------------------------------------------------------------------
+        // Attempt to delete a non-existent entry below the referral parent. 
+        // We should encounter referral errors with referral setting set to 
+        // throw.
+        // -------------------------------------------------------------------
+
+        td.refCtx.addToEnvironment( Context.REFERRAL, "throw" );
+        try
+        {
+            td.refCtx.destroySubcontext( "cn=alex karasulu" );
+            fail( "Should fail here throwing a ReferralException" );
+        }
+        catch ( ReferralException e )
+        {
+            checkParentReferrals( e );
+        }
+    }
+
+
+    /**
+     * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
+     * for a delete operation with an ancestor context being a referral.
+     * 
+     * @throws Exception if something goes wrong.
+     */
+    public void testDeleteWithReferralAncestor() throws Exception
+    {
+        // -------------------------------------------------------------------
+        // Attempt to delete a non-existent entry below the referral ancestor. 
+        // We should encounter referral errors when referral setting is set to 
+        // throw.
+        // -------------------------------------------------------------------
+
+        td.refCtx.addToEnvironment( Context.REFERRAL, "throw" );
+        try
+        {
+            td.refCtx.destroySubcontext( "cn=alex karasulu,ou=apache" );
+            fail( "Should fail here throwing a ReferralException" );
+        }
+        catch ( ReferralException e )
+        {
+            checkAncestorReferrals( e );
+        }
+    }
+
+
+    /**
+     * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
+     * for a modify operation with the parent context being a referral.
+     * 
+     * @throws Exception if something goes wrong.
+     */
+    public void testModifyWithReferralParent() throws Exception
+    {
+        // -------------------------------------------------------------------
+        // Attempt to modify the attributes of an entry below the referral 
+        // parent.  We should encounter referral errors with referral setting 
+        // set to throw.
+        // -------------------------------------------------------------------
+
+        td.refCtx.addToEnvironment( Context.REFERRAL, "throw" );
+        try
+        {
+            td.refCtx.modifyAttributes( "cn=alex karasulu", DirContext.ADD_ATTRIBUTE, new AttributesImpl(
+                "description", "just some text", true ) );
+            fail( "Should fail here throwing a ReferralException" );
+        }
+        catch ( ReferralException e )
+        {
+            checkParentReferrals( e );
+        }
+    }
+
+
+    /**
+     * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
+     * for a modify operation with an ancestor context being a referral.
+     * 
+     * @throws Exception if something goes wrong.
+     */
+    public void testModifyWithReferralAncestor() throws Exception
+    {
+        // -------------------------------------------------------------------
+        // Attempt to modify the attributes of an entry below the referral 
+        // ancestor. We should encounter referral errors when referral setting 
+        // is set to throw.
+        // -------------------------------------------------------------------
+
+        td.refCtx.addToEnvironment( Context.REFERRAL, "throw" );
+        try
+        {
+            td.refCtx.modifyAttributes( "cn=alex karasulu,ou=apache", DirContext.ADD_ATTRIBUTE, new AttributesImpl(
+                "description", "just some text", true ) );
+            fail( "Should fail here throwing a ReferralException" );
+        }
+        catch ( ReferralException e )
+        {
+            checkAncestorReferrals( e );
+        }
+    }
+
+
+    /**
+     * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
+     * for a modify operation with the parent context being a referral.
+     * 
+     * @throws Exception if something goes wrong.
+     */
+    public void testModifyWithReferralParent2() throws Exception
+    {
+        // -------------------------------------------------------------------
+        // Attempt to modify the attributes of an entry below the referral 
+        // parent.  We should encounter referral errors with referral setting 
+        // set to throw.
+        // -------------------------------------------------------------------
+
+        td.refCtx.addToEnvironment( Context.REFERRAL, "throw" );
+        try
+        {
+            ModificationItemImpl[] mods = new ModificationItemImpl[]
+                { new ModificationItemImpl( DirContext.ADD_ATTRIBUTE, new AttributeImpl( "description", "just some text" ) ) };
+            td.refCtx.modifyAttributes( "cn=alex karasulu", mods );
+            fail( "Should fail here throwing a ReferralException" );
+        }
+        catch ( ReferralException e )
+        {
+            checkParentReferrals( e );
+        }
+    }
+
+
+    /**
+     * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
+     * for a modify operation with an ancestor context being a referral.
+     * 
+     * @throws Exception if something goes wrong.
+     */
+    public void testModifyWithReferralAncestor2() throws Exception
+    {
+        // -------------------------------------------------------------------
+        // Attempt to modify the attributes of an entry below the referral 
+        // ancestor. We should encounter referral errors when referral setting 
+        // is set to throw.
+        // -------------------------------------------------------------------
+
+        td.refCtx.addToEnvironment( Context.REFERRAL, "throw" );
+        try
+        {
+            ModificationItemImpl[] mods = new ModificationItemImpl[]
+                { new ModificationItemImpl( DirContext.ADD_ATTRIBUTE, new AttributeImpl( "description", "just some text" ) ) };
+            td.refCtx.modifyAttributes( "cn=alex karasulu,ou=apache", mods );
+            fail( "Should fail here throwing a ReferralException" );
+        }
+        catch ( ReferralException e )
+        {
+            checkAncestorReferrals( e );
+        }
+    }
+
+
+    /**
+     * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
+     * for a modify rdn interceptor operation (corresponds to a subset of the modify 
+     * dn operation) with the parent context being a referral.
+     * 
+     * @throws Exception if something goes wrong.
+     */
+    public void testModifyRdnWithReferralParent() throws Exception
+    {
+        // -------------------------------------------------------------------
+        // Attempt to modify the last component of the entry's name which 
+        // resides below an parent which is a referral. We should encounter 
+        // referral errors when referral setting is set to throw.
+        // -------------------------------------------------------------------
+
+        td.refCtx.addToEnvironment( Context.REFERRAL, "throw" );
+        try
+        {
+            td.refCtx.rename( "cn=alex karasulu", "cn=aok" );
+            fail( "Should fail here throwing a ReferralException" );
+        }
+        catch ( ReferralException e )
+        {
+            checkParentReferrals( e );
+        }
+    }
+
+
+    /**
+     * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
+     * for a modify rdn interceptor operation (corresponds to a subset of the modify 
+     * dn operation) with an ancestor context being a referral.
+     * 
+     * @throws Exception if something goes wrong.
+     */
+    public void testModifyRdnWithReferralAncestor() throws Exception
+    {
+        // -------------------------------------------------------------------
+        // Attempt to modify the last component of the entry's name which 
+        // resides below an ancestor which is a referral. We should encounter 
+        // referral errors when referral setting is set to throw.
+        // -------------------------------------------------------------------
+
+        td.refCtx.addToEnvironment( Context.REFERRAL, "throw" );
+        try
+        {
+            td.refCtx.rename( "cn=alex karasulu,ou=apache", "cn=aok,ou=apache" );
+            fail( "Should fail here throwing a ReferralException" );
+        }
+        catch ( ReferralException e )
+        {
+            checkAncestorReferrals( e );
+        }
+    }
+
+
+    /**
+     * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
+     * for a move interceptor operation (corresponds to a subset of the modify 
+     * dn operation) with the parent context being a referral.
+     * 
+     * @throws Exception if something goes wrong.
+     */
+    public void testMoveWithReferralParent() throws Exception
+    {
+        // -------------------------------------------------------------------
+        // Attempt to modify the last component of the entry's name which 
+        // resides below an parent which is a referral. We should encounter 
+        // referral errors when referral setting is set to throw.
+        // -------------------------------------------------------------------
+
+        td.refCtx.addToEnvironment( Context.REFERRAL, "throw" );
+        try
+        {
+            td.refCtx.rename( "cn=alex karasulu", "cn=alex karasulu,ou=groups" );
+            fail( "Should fail here throwing a ReferralException" );
+        }
+        catch ( PartialResultException e )
+        {
+        }
+        catch ( ReferralException e )
+        {
+            checkParentReferrals( e );
+        }
+    }
+
+
+    /**
+     * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
+     * for a move interceptor operation (corresponds to a subset of the modify 
+     * dn operation) with an ancestor context being a referral.
+     * 
+     * @throws Exception if something goes wrong.
+     */
+    public void testMoveWithReferralAncestor() throws Exception
+    {
+        // -------------------------------------------------------------------
+        // Attempt to modify the last component of the entry's name which 
+        // resides below an ancestor which is a referral. We should encounter 
+        // referral errors when referral setting is set to throw.
+        // -------------------------------------------------------------------
+
+        td.refCtx.addToEnvironment( Context.REFERRAL, "throw" );
+        try
+        {
+            td.refCtx.rename( "cn=alex karasulu,ou=apache", "cn=alex karasulu,ou=groups" );
+            fail( "Should fail here throwing a ReferralException" );
+        }
+        catch ( PartialResultException e )
+        {
+        }
+        catch ( ReferralException e )
+        {
+            checkAncestorReferrals( e );
+        }
+    }
+
+
+    /**
+     * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
+     * for a move interceptor operation (corresponds to a subset of the modify 
+     * dn operation) with the parent context being a referral.
+     * 
+     * @throws Exception if something goes wrong.
+     */
+    public void testMoveWithReferralParent2() throws Exception
+    {
+        // -------------------------------------------------------------------
+        // Attempt to modify the last component of the entry's name which 
+        // resides below an parent which is a referral. We should encounter 
+        // referral errors when referral setting is set to throw.
+        // -------------------------------------------------------------------
+
+        td.refCtx.addToEnvironment( Context.REFERRAL, "throw" );
+        try
+        {
+            td.refCtx.rename( "cn=alex karasulu", "cn=aok,ou=groups" );
+            fail( "Should fail here throwing a ReferralException" );
+        }
+        catch ( PartialResultException e )
+        {
+        }
+        catch ( ReferralException e )
+        {
+            checkParentReferrals( e );
+        }
+    }
+
+
+    /**
+     * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
+     * for a move interceptor operation (corresponds to a subset of the modify 
+     * dn operation) with an ancestor context being a referral.
+     * 
+     * @throws Exception if something goes wrong.
+     */
+    public void testMoveWithReferralAncestor2() throws Exception
+    {
+        // -------------------------------------------------------------------
+        // Attempt to modify the last component of the entry's name which 
+        // resides below an ancestor which is a referral. We should encounter 
+        // referral errors when referral setting is set to throw.
+        // -------------------------------------------------------------------
+
+        td.refCtx.addToEnvironment( Context.REFERRAL, "throw" );
+        try
+        {
+            td.refCtx.rename( "cn=alex karasulu,ou=apache", "cn=aok,ou=groups" );
+            fail( "Should fail here throwing a ReferralException" );
+        }
+        catch ( PartialResultException e )
+        {
+        }
+        catch ( ReferralException e )
+        {
+            checkAncestorReferrals( e );
+        }
+    }
+
+
+    /**
+     * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
+     * for a move interceptor operation (corresponds to a subset of the modify 
+     * dn operation) with the parent context being a referral.
+     * 
+     * @throws Exception if something goes wrong.
+     */
+    public void testMoveWithReferralParentDest() throws Exception
+    {
+        // -------------------------------------------------------------------
+        // Attempt to modify the last component of the entry's name which 
+        // resides below an parent which is a referral. We should encounter 
+        // referral errors when referral setting is set to throw.
+        // -------------------------------------------------------------------
+
+        createLocalUser();
+        td.rootCtx.addToEnvironment( Context.REFERRAL, "throw" );
+        try
+        {
+            td.rootCtx.rename( "cn=akarasulu", "cn=akarasulu,ou=users" );
+            fail( "Should fail here throwing a LdapNamingException with ResultCodeEnum = AFFECTSMULTIPLEDSAS" );
+        }
+        // this should have a result code of 71 for affectsMultipleDSAs
+        // however there is absolutely no way for us to tell if this is 
+        // the case because of JNDI exception resolution issues with LDAP
+        catch ( NamingException e )
+        {
+        }
+    }
+
+
+    /**
+     * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
+     * for a move interceptor operation (corresponds to a subset of the modify 
+     * dn operation) with an ancestor context being a referral.
+     * 
+     * @throws Exception if something goes wrong.
+     */
+    public void testMoveWithReferralAncestorDest() throws Exception
+    {
+        // -------------------------------------------------------------------
+        // Attempt to modify the last component of the entry's name which 
+        // resides below an ancestor which is a referral. We should encounter 
+        // referral errors when referral setting is set to throw.
+        // -------------------------------------------------------------------
+
+        createDeepLocalUser();
+        td.rootCtx.addToEnvironment( Context.REFERRAL, "throw" );
+        try
+        {
+            td.rootCtx.rename( "cn=akarasulu,ou=deep", "cn=akarasulu,ou=users" );
+            fail( "Should fail here throwing a LdapNamingException with ResultCodeEnum = AFFECTSMULTIPLEDSAS" );
+        }
+        // this should have a result code of 71 for affectsMultipleDSAs
+        // however there is absolutely no way for us to tell if this is 
+        // the case because of JNDI exception resolution issues with LDAP
+        catch ( NamingException e )
+        {
+        }
+    }
+
+
+    /**
+     * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
+     * for a move interceptor operation (corresponds to a subset of the modify 
+     * dn operation) with the parent context being a referral.
+     * 
+     * @throws Exception if something goes wrong.
+     */
+    public void testMoveWithReferralParent2Dest() throws Exception
+    {
+        // -------------------------------------------------------------------
+        // Attempt to modify the last component of the entry's name which 
+        // resides below an parent which is a referral. We should encounter 
+        // referral errors when referral setting is set to throw.
+        // -------------------------------------------------------------------
+
+        createLocalUser();
+        td.rootCtx.addToEnvironment( Context.REFERRAL, "throw" );
+        try
+        {
+            td.rootCtx.rename( "cn=akarasulu", "cn=aok,ou=users" );
+            fail( "Should fail here throwing a LdapNamingException with ResultCodeEnum = AFFECTSMULTIPLEDSAS" );
+        }
+        // this should have a result code of 71 for affectsMultipleDSAs
+        // however there is absolutely no way for us to tell if this is 
+        // the case because of JNDI exception resolution issues with LDAP
+        catch ( NamingException e )
+        {
+        }
+    }
+
+
+    /**
+     * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
+     * for a move interceptor operation (corresponds to a subset of the modify 
+     * dn operation) with an ancestor context being a referral.
+     * 
+     * @throws Exception if something goes wrong.
+     */
+    public void testMoveWithReferralAncestor2Dest() throws Exception
+    {
+        // -------------------------------------------------------------------
+        // Attempt to modify the last component of the entry's name which 
+        // resides below an ancestor which is a referral. We should encounter 
+        // referral errors when referral setting is set to throw.
+        // -------------------------------------------------------------------
+
+        createDeepLocalUser();
+        td.rootCtx.addToEnvironment( Context.REFERRAL, "throw" );
+        try
+        {
+            td.rootCtx.rename( "cn=akarasulu,ou=deep", "cn=aok,ou=users" );
+            fail( "Should fail here throwing a LdapNamingException with ResultCodeEnum = AFFECTSMULTIPLEDSAS" );
+        }
+        // this should have a result code of 71 for affectsMultipleDSAs
+        // however there is absolutely no way for us to tell if this is 
+        // the case because of JNDI exception resolution issues with LDAP
+        catch ( NamingException e )
+        {
+        }
+    }
+
+
+    public void createLocalUser() throws Exception
+    {
+        LdapContext userCtx = null;
+        Attributes referral = new AttributesImpl( "objectClass", "top", true );
+        referral.get( "objectClass" ).add( "person" );
+        referral.put( "cn", "akarasulu" );
+        referral.put( "sn", "karasulu" );
+
+        try
+        {
+            td.rootCtx.destroySubcontext( "uid=akarasulu" );
+        }
+        catch ( NameNotFoundException e )
+        {
+        }
+        try
+        {
+            userCtx = ( LdapContext ) td.rootCtx.createSubcontext( "cn=akarasulu", referral );
+        }
+        catch ( NameAlreadyBoundException e )
+        {
+            td.refCtx = ( LdapContext ) td.rootCtx.lookup( "cn=akarasulu" );
+        }
+        referral = userCtx.getAttributes( "" );
+        assertTrue( referral.get( "cn" ).contains( "akarasulu" ) );
+        assertTrue( referral.get( "sn" ).contains( "karasulu" ) );
+    }
+
+
+    public void createDeepLocalUser() throws Exception
+    {
+        LdapContext userCtx = null;
+        Attributes referral = new AttributesImpl( "objectClass", "top", true );
+        referral.get( "objectClass" ).add( "person" );
+        referral.put( "cn", "akarasulu" );
+        referral.put( "sn", "karasulu" );
+
+        try
+        {
+            td.rootCtx.destroySubcontext( "uid=akarasulu,ou=deep" );
+        }
+        catch ( NameNotFoundException e )
+        {
+        }
+        try
+        {
+            td.rootCtx.destroySubcontext( "ou=deep" );
+        }
+        catch ( NameNotFoundException e )
+        {
+        }
+        try
+        {
+            Attributes ouAttrs = new AttributesImpl( "objectClass", "top", true );
+            ouAttrs.get( "objectClass" ).add( "organizationalUnit" );
+            ouAttrs.put( "ou", "deep" );
+            td.rootCtx.createSubcontext( "ou=deep", ouAttrs );
+            userCtx = ( LdapContext ) td.rootCtx.createSubcontext( "cn=akarasulu,ou=deep", referral );
+        }
+        catch ( NameAlreadyBoundException e )
+        {
+            td.refCtx = ( LdapContext ) td.rootCtx.lookup( "cn=akarasulu,ou=deep" );
+        }
+        referral = userCtx.getAttributes( "" );
+        assertTrue( referral.get( "cn" ).contains( "akarasulu" ) );
+        assertTrue( referral.get( "sn" ).contains( "karasulu" ) );
+    }
+
+
+    public void testSearchBaseIsReferral() throws Exception
+    {
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        td.rootCtx.addToEnvironment( Context.REFERRAL, "throw" );
+        try
+        {
+            td.rootCtx.search( "ou=users", "(objectClass=*)", controls );
+            fail( "should never get here" );
+        }
+        catch ( ReferralException e )
+        {
+            assertEquals( "ldap://fermi:10389/ou=users,ou=system??sub", e.getReferralInfo() );
+            assertTrue( e.skipReferral() );
+            assertEquals( "ldap://hertz:10389/ou=users,dc=example,dc=com??sub", e.getReferralInfo() );
+            assertTrue( e.skipReferral() );
+            assertEquals( "ldap://maxwell:10389/ou=users,ou=system??sub", e.getReferralInfo() );
+            assertFalse( e.skipReferral() );
+        }
+    }
+
+
+    public void testSearchBaseParentIsReferral() throws Exception
+    {
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.OBJECT_SCOPE );
+        td.refCtx.addToEnvironment( Context.REFERRAL, "throw" );
+        try
+        {
+            td.refCtx.search( "cn=alex karasulu", "(objectClass=*)", controls );
+        }
+        catch ( ReferralException e )
+        {
+            assertEquals( "ldap://fermi:10389/cn=alex%20karasulu,ou=users,ou=system??base", e.getReferralInfo() );
+            assertTrue( e.skipReferral() );
+            assertEquals( "ldap://hertz:10389/cn=alex%20karasulu,ou=users,dc=example,dc=com??base", e.getReferralInfo() );
+            assertTrue( e.skipReferral() );
+            assertEquals( "ldap://maxwell:10389/cn=alex%20karasulu,ou=users,ou=system??base", e.getReferralInfo() );
+            assertFalse( e.skipReferral() );
+        }
+    }
+
+
+    public void testSearchBaseAncestorIsReferral() throws Exception
+    {
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.OBJECT_SCOPE );
+        td.refCtx.addToEnvironment( Context.REFERRAL, "throw" );
+        try
+        {
+            td.refCtx.search( "cn=alex karasulu,ou=apache", "(objectClass=*)", controls );
+        }
+        catch ( ReferralException e )
+        {
+            assertEquals( "ldap://fermi:10389/cn=alex%20karasulu,ou=apache,ou=users,ou=system??base", e.getReferralInfo() );
+            assertTrue( e.skipReferral() );
+            assertEquals( "ldap://hertz:10389/cn=alex%20karasulu,ou=apache,ou=users,dc=example,dc=com??base", e
+                .getReferralInfo() );
+            assertTrue( e.skipReferral() );
+            assertEquals( "ldap://maxwell:10389/cn=alex%20karasulu,ou=apache,ou=users,ou=system??base", e
+                .getReferralInfo() );
+            assertFalse( e.skipReferral() );
+        }
+    }
+
+
+    public void testSearchContinuations() throws Exception
+    {
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        NamingEnumeration list = td.rootCtx.search( "", "(objectClass=*)", controls );
+        Map results = new HashMap();
+        while ( list.hasMore() )
+        {
+            SearchResult result = ( SearchResult ) list.next();
+            results.put( result.getName(), result );
+        }
+
+        assertNotNull( results.get( "ou=users" ) );
+
+        // -------------------------------------------------------------------
+        // Now we will throw exceptions when searching for referrals 
+        // -------------------------------------------------------------------
+
+        td.rootCtx.addToEnvironment( Context.REFERRAL, "throw" );
+        list = td.rootCtx.search( "", "(objectClass=*)", controls );
+        results = new HashMap();
+
+        try
+        {
+            while ( list.hasMore() )
+            {
+                SearchResult result = ( SearchResult ) list.next();
+                results.put( result.getName(), result );
+            }
+        }
+        catch ( ReferralException e )
+        {
+            assertEquals( "ldap://fermi:10389/ou=users,ou=system??sub", e.getReferralInfo() );
+            assertTrue( e.skipReferral() );
+            assertEquals( "ldap://hertz:10389/ou=users,dc=example,dc=com??sub", e.getReferralInfo() );
+            assertTrue( e.skipReferral() );
+            assertEquals( "ldap://maxwell:10389/ou=users,ou=system??sub", e.getReferralInfo() );
+            assertFalse( e.skipReferral() );
+        }
+
+        assertNull( results.get( "ou=users" ) );
+
+        // try again but this time with single level scope
+
+        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+        list = td.rootCtx.search( "", "(objectClass=*)", controls );
+        results = new HashMap();
+
+        try
+        {
+            while ( list.hasMore() )
+            {
+                SearchResult result = ( SearchResult ) list.next();
+                results.put( result.getName(), result );
+            }
+        }
+        catch ( ReferralException e )
+        {
+            assertEquals( "ldap://fermi:10389/ou=users,ou=system??base", e.getReferralInfo() );
+            assertTrue( e.skipReferral() );
+            assertEquals( "ldap://hertz:10389/ou=users,dc=example,dc=com??base", e.getReferralInfo() );
+            assertTrue( e.skipReferral() );
+            assertEquals( "ldap://maxwell:10389/ou=users,ou=system??base", e.getReferralInfo() );
+            assertFalse( e.skipReferral() );
+        }
+
+        assertNull( results.get( "ou=users" ) );
+    }
+}
\ No newline at end of file
diff --git a/old_trunk/server-unit/src/test/java/org/apache/directory/server/SaslBindITest.java b/old_trunk/server-unit/src/test/java/org/apache/directory/server/SaslBindITest.java
new file mode 100644
index 0000000..42b57dd
--- /dev/null
+++ b/old_trunk/server-unit/src/test/java/org/apache/directory/server/SaslBindITest.java
@@ -0,0 +1,743 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server;
+
+
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Set;
+
+import javax.naming.Context;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.InitialDirContext;
+
+import org.apache.commons.lang.ArrayUtils;
+import org.apache.commons.net.SocketClient;
+import org.apache.directory.server.core.entry.DefaultServerEntry;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.partition.Partition;
+import org.apache.directory.server.core.partition.impl.btree.Index;
+import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmIndex;
+import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmPartition;
+import org.apache.directory.server.ldap.handlers.bind.ntlm.NtlmMechanismHandler;
+import org.apache.directory.server.ldap.handlers.bind.ntlm.NtlmProvider;
+import org.apache.directory.server.unit.AbstractServerTest;
+import org.apache.directory.shared.ldap.constants.SupportedSaslMechanisms;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.message.BindRequestImpl;
+import org.apache.directory.shared.ldap.message.BindResponse;
+import org.apache.directory.shared.ldap.message.MessageDecoder;
+import org.apache.directory.shared.ldap.message.MessageEncoder;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.message.spi.BinaryAttributeDetector;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.mina.common.IoSession;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * An {@link AbstractServerTest} testing SASL DIGEST-MD5 and CRAM-MD5
+ * authentication and security layer negotiation.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class SaslBindITest extends AbstractServerTest
+{
+    private DirContext ctx;
+    private BogusNtlmProvider provider;
+
+
+    /**
+     * Set up a partition for EXAMPLE.COM and add a user to
+     * test authentication with.
+     */
+    @Before
+    public void setUp() throws Exception
+    {
+        provider = new BogusNtlmProvider();
+        super.setUp();
+        setAllowAnonymousAccess( false );
+
+        Hashtable<String, String> env = new Hashtable<String, String>();
+        env.put( "java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory" );
+        env.put( "java.naming.provider.url", "ldap://localhost:" + port + "/dc=example,dc=com" );
+        env.put( "java.naming.security.principal", "uid=admin,ou=system" );
+        env.put( "java.naming.security.credentials", "secret" );
+        env.put( "java.naming.security.authentication", "simple" );
+        ctx = new InitialDirContext( env );
+
+        Attributes attrs = new AttributesImpl( true );
+        attrs = getOrgUnitAttributes( "users" );
+        DirContext users = ctx.createSubcontext( "ou=users", attrs );
+
+        attrs = getPersonAttributes( "Nelson", "Horatio Nelson", "hnelson", "secret" );
+        users.createSubcontext( "uid=hnelson", attrs );
+    }
+
+
+    @Override
+    protected void configureDirectoryService() throws NamingException
+    {
+        Set<Partition> partitions = new HashSet<Partition>();
+        JdbmPartition partition = new JdbmPartition();
+        partition.setId( "example" );
+        partition.setSuffix( "dc=example,dc=com" );
+
+        Set<Index> indexedAttrs = new HashSet<Index>();
+        indexedAttrs.add( new JdbmIndex( "ou" ) );
+        indexedAttrs.add( new JdbmIndex( "dc" ) );
+        indexedAttrs.add( new JdbmIndex( "objectClass" ) );
+        partition.setIndexedAttributes( indexedAttrs );
+
+        LdapDN exampleDn = new LdapDN( "dc=example,dc=com" );
+        ServerEntry serverEntry = new DefaultServerEntry( directoryService.getRegistries(), exampleDn );
+        serverEntry.put( "objectClass", "top", "domain" );
+        serverEntry.put( "dc", "example" );
+
+        partition.setContextEntry( serverEntry );
+
+
+        partitions.add( partition );
+        directoryService.setPartitions( partitions );
+    }
+
+
+    @Override
+    protected void configureLdapServer()
+    {
+        ldapServer.setSaslHost( "localhost" );
+        
+        NtlmMechanismHandler ntlmMechanismHandler = new NtlmMechanismHandler();
+        ntlmMechanismHandler.setNtlmProvider( provider  );
+        
+        ldapServer.removeSaslMechanismHandler( SupportedSaslMechanisms.NTLM );
+        ldapServer.addSaslMechanismHandler( SupportedSaslMechanisms.NTLM, ntlmMechanismHandler );
+        ldapServer.removeSaslMechanismHandler( SupportedSaslMechanisms.GSS_SPNEGO );
+        ldapServer.addSaslMechanismHandler( SupportedSaslMechanisms.GSS_SPNEGO, ntlmMechanismHandler );
+    }
+
+    
+    /**
+     * Tear down.
+     */
+    @After
+    public void tearDown() throws Exception
+    {
+        ctx.close();
+        ctx = null;
+        super.tearDown();
+    }
+
+
+    /**
+     * Convenience method for creating a person.
+     */
+    protected Attributes getPersonAttributes( String sn, String cn, String uid, String userPassword )
+    {
+        Attributes attrs = new AttributesImpl();
+        Attribute ocls = new AttributeImpl( "objectClass" );
+        ocls.add( "top" );
+        ocls.add( "person" ); // sn $ cn
+        ocls.add( "inetOrgPerson" ); // uid
+        attrs.put( ocls );
+        attrs.put( "cn", cn );
+        attrs.put( "sn", sn );
+        attrs.put( "uid", uid );
+        attrs.put( "userPassword", userPassword );
+
+        return attrs;
+    }
+
+
+    /**
+     * Convenience method for creating an organizational unit.
+     */
+    protected Attributes getOrgUnitAttributes( String ou )
+    {
+        Attributes attrs = new AttributesImpl();
+        Attribute ocls = new AttributeImpl( "objectClass" );
+        ocls.add( "top" );
+        ocls.add( "organizationalUnit" );
+        attrs.put( ocls );
+        attrs.put( "ou", ou );
+
+        return attrs;
+    }
+
+
+    /**
+     * Tests to make sure the server properly returns the supportedSASLMechanisms.
+     */
+    @Test
+    public void testSupportedSASLMechanisms()
+    {
+        try
+        {
+            DirContext context = new InitialDirContext();
+
+            Attributes attrs = context.getAttributes( "ldap://localhost:" + port, new String[]
+                { "supportedSASLMechanisms" } );
+
+            NamingEnumeration<? extends Attribute> answer = attrs.getAll();
+            Attribute result = answer.next();
+            assertTrue( result.size() == 6 );
+            assertTrue( result.contains( SupportedSaslMechanisms.GSSAPI ) );
+            assertTrue( result.contains( SupportedSaslMechanisms.DIGEST_MD5 ) );
+            assertTrue( result.contains( SupportedSaslMechanisms.CRAM_MD5 ) );
+            assertTrue( result.contains( SupportedSaslMechanisms.NTLM ) );
+            assertTrue( result.contains( SupportedSaslMechanisms.PLAIN ) );
+            assertTrue( result.contains( SupportedSaslMechanisms.GSS_SPNEGO ) );
+        }
+        catch ( NamingException e )
+        {
+            fail( "Should not have caught exception." );
+        }
+    }
+
+
+    /**
+     * Tests to make sure we still have anonymous access to the RootDSE.  The
+     * configuration for this testcase MUST disable anonymous access.
+     */
+    @Test
+    public void testAnonymousRootDSE()
+    {
+        try
+        {
+            Hashtable<String, String> env = new Hashtable<String, String>();
+            env.put( Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory" );
+            env.put( Context.PROVIDER_URL, "ldap://localhost:" + port );
+
+            DirContext context = new InitialDirContext( env );
+
+            String[] attrIDs =
+                { "vendorName" };
+
+            Attributes attrs = context.getAttributes( "", attrIDs );
+
+            String vendorName = null;
+
+            if ( attrs.get( "vendorName" ) != null )
+            {
+                vendorName = ( String ) attrs.get( "vendorName" ).get();
+            }
+
+            assertEquals( "Apache Software Foundation", vendorName );
+        }
+        catch ( NamingException e )
+        {
+            fail( "Should not have caught exception." );
+        }
+    }
+
+
+    /**
+     * Tests to make sure binds below the RootDSE require authentication.
+     */
+    @Test
+    public void testAnonymousBelowRootDSE()
+    {
+        try
+        {
+            Hashtable<String, String> env = new Hashtable<String, String>();
+            env.put( Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory" );
+            env.put( Context.PROVIDER_URL, "ldap://localhost:" + port );
+
+            DirContext context = new InitialDirContext( env );
+
+            String[] attrIDs =
+                { "vendorName" };
+
+            context.getAttributes( "dc=example,dc=com", attrIDs );
+
+            fail( "Should not have gotten here." );
+        }
+        catch ( NamingException e )
+        {
+            assertTrue( e.getMessage().contains( "Anonymous binds have been disabled!" ) );
+        }
+    }
+
+
+    /**
+     * Tests to make sure SIMPLE binds below the RootDSE work.
+     */
+    @Test
+    public void testSimpleBindBelowRootDSE()
+    {
+        try
+        {
+            Hashtable<String, String> env = new Hashtable<String, String>();
+            env.put( Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory" );
+            env.put( Context.PROVIDER_URL, "ldap://localhost:" + port );
+
+            env.put( Context.SECURITY_AUTHENTICATION, "simple" );
+            env.put( Context.SECURITY_PRINCIPAL, "uid=hnelson,ou=users,dc=example,dc=com" );
+            env.put( Context.SECURITY_CREDENTIALS, "secret" );
+
+            DirContext context = new InitialDirContext( env );
+
+            String[] attrIDs =
+                { "uid" };
+
+            Attributes attrs = context.getAttributes( "uid=hnelson,ou=users,dc=example,dc=com", attrIDs );
+
+            String uid = null;
+
+            if ( attrs.get( "uid" ) != null )
+            {
+                uid = ( String ) attrs.get( "uid" ).get();
+            }
+
+            assertEquals( uid, "hnelson" );
+        }
+        catch ( NamingException e )
+        {
+            fail( "Should not have caught exception." );
+        }
+    }
+
+
+    /**
+     * Tests to make sure SIMPLE binds below the RootDSE fail if the password is bad.
+     */
+    @Test
+    public void testSimpleBindBadPassword()
+    {
+        try
+        {
+            Hashtable<String, String> env = new Hashtable<String, String>();
+            env.put( Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory" );
+            env.put( Context.PROVIDER_URL, "ldap://localhost:" + port );
+
+            env.put( Context.SECURITY_AUTHENTICATION, "simple" );
+            env.put( Context.SECURITY_PRINCIPAL, "uid=hnelson,ou=users,dc=example,dc=com" );
+            env.put( Context.SECURITY_CREDENTIALS, "badsecret" );
+
+            DirContext context = new InitialDirContext( env );
+
+            String[] attrIDs =
+                { "uid" };
+
+            context.getAttributes( "uid=hnelson,ou=users,dc=example,dc=com", attrIDs );
+
+            fail( "Should not have gotten here." );
+        }
+        catch ( NamingException e )
+        {
+            assertTrue( e.getMessage().contains( "Bind failed" ) );
+        }
+    }
+
+
+    /**
+     * Tests to make sure SIMPLE binds below the RootDSE fail if the password is bad.
+     */
+    @Test
+    public void testSimpleBindUnauthenticated()
+    {
+        try
+        {
+            Hashtable<String, String> env = new Hashtable<String, String>();
+            env.put( Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory" );
+            env.put( Context.PROVIDER_URL, "ldap://localhost:" + port );
+
+            env.put( Context.SECURITY_AUTHENTICATION, "simple" );
+            env.put( Context.SECURITY_PRINCIPAL, "uid=admin,ou=system" );
+            env.put( Context.SECURITY_CREDENTIALS, "" );
+
+            DirContext context = new InitialDirContext( env );
+
+            String[] attrIDs =
+                { "uid" };
+
+            context.getAttributes( "uid=hnelson,ou=users,dc=example,dc=com", attrIDs );
+
+            fail( "Should not have gotten here." );
+        }
+        catch ( NamingException e )
+        {
+            assertTrue( e.getMessage().contains( "Bind failed" ) );
+        }
+    }
+
+
+    /**
+     * Tests to make sure DIGEST-MD5 binds below the RootDSE work.
+     */
+    @Test
+    public void testSaslDigestMd5Bind() throws Exception
+    {
+        Hashtable<String, String> env = new Hashtable<String, String>();
+        env.put( Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory" );
+        env.put( Context.PROVIDER_URL, "ldap://localhost:" + port );
+
+        env.put( Context.SECURITY_AUTHENTICATION, "DIGEST-MD5" );
+        env.put( Context.SECURITY_PRINCIPAL, "hnelson" );
+        env.put( Context.SECURITY_CREDENTIALS, "secret" );
+
+        // Specify realm
+        env.put( "java.naming.security.sasl.realm", "example.com" );
+
+        // Request privacy protection
+        env.put( "javax.security.sasl.qop", "auth-conf" );
+
+        DirContext context = new InitialDirContext( env );
+
+        String[] attrIDs =
+            { "uid" };
+
+        Attributes attrs = context.getAttributes( "uid=hnelson,ou=users,dc=example,dc=com", attrIDs );
+
+        String uid = null;
+
+        if ( attrs.get( "uid" ) != null )
+        {
+            uid = ( String ) attrs.get( "uid" ).get();
+        }
+
+        assertEquals( uid, "hnelson" );
+    }
+
+
+    /**
+     * Tests to make sure DIGEST-MD5 binds below the RootDSE fail if the realm is bad.
+     */
+    @Test
+    public void testSaslDigestMd5BindBadRealm()
+    {
+        try
+        {
+            Hashtable<String, String> env = new Hashtable<String, String>();
+            env.put( Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory" );
+            env.put( Context.PROVIDER_URL, "ldap://localhost:" + port );
+
+            env.put( Context.SECURITY_AUTHENTICATION, "DIGEST-MD5" );
+            env.put( Context.SECURITY_PRINCIPAL, "hnelson" );
+            env.put( Context.SECURITY_CREDENTIALS, "secret" );
+
+            // Bad realm
+            env.put( "java.naming.security.sasl.realm", "badrealm.com" );
+
+            // Request privacy protection
+            env.put( "javax.security.sasl.qop", "auth-conf" );
+
+            DirContext context = new InitialDirContext( env );
+
+            String[] attrIDs =
+                { "uid" };
+
+            context.getAttributes( "uid=hnelson,ou=users,dc=example,dc=com", attrIDs );
+
+            fail( "Should have thrown exception." );
+        }
+        catch ( NamingException e )
+        {
+            assertTrue( e.getMessage().contains( "Nonexistent realm" ) );
+        }
+    }
+
+
+    /**
+     * Tests to make sure DIGEST-MD5 binds below the RootDSE fail if the password is bad.
+     */
+    @Test
+    public void testSaslDigestMd5BindBadPassword()
+    {
+        try
+        {
+            Hashtable<String, String> env = new Hashtable<String, String>();
+            env.put( Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory" );
+            env.put( Context.PROVIDER_URL, "ldap://localhost:" + port );
+
+            env.put( Context.SECURITY_AUTHENTICATION, "DIGEST-MD5" );
+            env.put( Context.SECURITY_PRINCIPAL, "hnelson" );
+            env.put( Context.SECURITY_CREDENTIALS, "badsecret" );
+
+            DirContext context = new InitialDirContext( env );
+
+            String[] attrIDs =
+                { "uid" };
+
+            context.getAttributes( "uid=hnelson,ou=users,dc=example,dc=com", attrIDs );
+
+            fail( "Should have thrown exception." );
+        }
+        catch ( NamingException e )
+        {
+            assertTrue( e.getMessage().contains( "digest response format violation" ) );
+        }
+    }
+
+
+    /**
+     * Tests to make sure CRAM-MD5 binds below the RootDSE work.
+     */
+    @Test
+    public void testSaslCramMd5Bind()
+    {
+        try
+        {
+            Hashtable<String, String> env = new Hashtable<String, String>();
+            env.put( Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory" );
+            env.put( Context.PROVIDER_URL, "ldap://localhost:" + port );
+
+            env.put( Context.SECURITY_AUTHENTICATION, "CRAM-MD5" );
+            env.put( Context.SECURITY_PRINCIPAL, "hnelson" );
+            env.put( Context.SECURITY_CREDENTIALS, "secret" );
+
+            DirContext context = new InitialDirContext( env );
+
+            String[] attrIDs =
+                { "uid" };
+
+            Attributes attrs = context.getAttributes( "uid=hnelson,ou=users,dc=example,dc=com", attrIDs );
+
+            String uid = null;
+
+            if ( attrs.get( "uid" ) != null )
+            {
+                uid = ( String ) attrs.get( "uid" ).get();
+            }
+
+            assertEquals( uid, "hnelson" );
+        }
+        catch ( NamingException e )
+        {
+            fail( "Should not have caught exception." );
+        }
+    }
+
+
+    /**
+     * Tests to make sure CRAM-MD5 binds below the RootDSE fail if the password is bad.
+     */
+    @Test
+    public void testSaslCramMd5BindBadPassword()
+    {
+        try
+        {
+            Hashtable<String, String> env = new Hashtable<String, String>();
+            env.put( Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory" );
+            env.put( Context.PROVIDER_URL, "ldap://localhost:" + port );
+
+            env.put( Context.SECURITY_AUTHENTICATION, "CRAM-MD5" );
+            env.put( Context.SECURITY_PRINCIPAL, "hnelson" );
+            env.put( Context.SECURITY_CREDENTIALS, "badsecret" );
+
+            DirContext context = new InitialDirContext( env );
+
+            String[] attrIDs =
+                { "uid" };
+
+            context.getAttributes( "uid=hnelson,ou=users,dc=example,dc=com", attrIDs );
+
+            fail( "Should have thrown exception." );
+        }
+        catch ( NamingException e )
+        {
+            assertTrue( e.getMessage().contains( "Invalid response" ) );
+        }
+    }
+
+
+    /**
+     * Tests that the plumbing for NTLM bind works.
+     */
+    @Test
+    public void testNtlmBind() throws Exception
+    {
+        NtlmSaslBindClient client = new NtlmSaslBindClient( SupportedSaslMechanisms.NTLM );
+        BindResponse type2response = client.bindType1( "type1_test".getBytes() );
+        assertEquals( 1, type2response.getMessageId() );
+        assertEquals( ResultCodeEnum.SASL_BIND_IN_PROGRESS, type2response.getLdapResult().getResultCode() );
+        assertTrue( ArrayUtils.isEquals( "type1_test".getBytes(), provider.getType1Response() ) );
+        assertTrue( ArrayUtils.isEquals( "challenge".getBytes(), type2response.getServerSaslCreds() ) );
+        
+        BindResponse finalResponse = client.bindType3( "type3_test".getBytes() );
+        assertEquals( 2, finalResponse.getMessageId() );
+        assertEquals( ResultCodeEnum.SUCCESS, finalResponse.getLdapResult().getResultCode() );
+        assertTrue( ArrayUtils.isEquals( "type3_test".getBytes(), provider.getType3Response() ) );
+    }
+
+
+    /**
+     * Tests that the plumbing for NTLM bind works.
+     */
+    @Test
+    public void testGssSpnegoBind() throws Exception
+    {
+        NtlmSaslBindClient client = new NtlmSaslBindClient( SupportedSaslMechanisms.GSS_SPNEGO );
+        BindResponse type2response = client.bindType1( "type1_test".getBytes() );
+        assertEquals( 1, type2response.getMessageId() );
+        assertEquals( ResultCodeEnum.SASL_BIND_IN_PROGRESS, type2response.getLdapResult().getResultCode() );
+        assertTrue( ArrayUtils.isEquals( "type1_test".getBytes(), provider.getType1Response() ) );
+        assertTrue( ArrayUtils.isEquals( "challenge".getBytes(), type2response.getServerSaslCreds() ) );
+        
+        BindResponse finalResponse = client.bindType3( "type3_test".getBytes() );
+        assertEquals( 2, finalResponse.getMessageId() );
+        assertEquals( ResultCodeEnum.SUCCESS, finalResponse.getLdapResult().getResultCode() );
+        assertTrue( ArrayUtils.isEquals( "type3_test".getBytes(), provider.getType3Response() ) );
+    }
+
+
+    class BogusNtlmProvider implements NtlmProvider
+    {
+        private byte[] type1response;
+        private byte[] type3response;
+        
+        
+        public boolean authenticate( IoSession session, byte[] type3response ) throws Exception
+        {
+            this.type3response = type3response;
+            return true;
+        }
+
+
+        public byte[] generateChallenge( IoSession session, byte[] type1reponse ) throws Exception
+        {
+            this.type1response = type1reponse;
+            return "challenge".getBytes();
+        }
+        
+        
+        public byte[] getType1Response()
+        {
+            return type1response;
+        }
+        
+        
+        public byte[] getType3Response()
+        {
+            return type3response;
+        }
+    }
+
+
+    class NtlmSaslBindClient extends SocketClient
+    {
+        private final Logger LOG = LoggerFactory.getLogger( NtlmSaslBindClient.class );
+        
+        private final String mechanism;
+        
+        
+        NtlmSaslBindClient( String mechanism ) throws Exception
+        {
+            this.mechanism = mechanism;
+            setDefaultPort( port );
+            connect( "localhost", port );
+            setTcpNoDelay( false );
+            
+            LOG.debug( "isConnected() = {}", _isConnected_ );
+            LOG.debug( "LocalPort     = {}", getLocalPort() );
+            LOG.debug( "LocalAddress  = {}", getLocalAddress() );
+            LOG.debug( "RemotePort    = {}", getRemotePort() );
+            LOG.debug( "RemoteAddress = {}", getRemoteAddress() );
+        }
+
+        
+        BindResponse bindType1( byte[] type1response ) throws Exception
+        {
+            if ( ! isConnected() )
+            {
+                throw new IllegalStateException( "Client is not connected." );
+            }
+            
+            // Setup the bind request
+            BindRequestImpl request = new BindRequestImpl( 1 ) ;
+            request.setName( new LdapDN( "uid=admin,ou=system" ) ) ;
+            request.setSimple( false ) ;
+            request.setCredentials( type1response ) ;
+            request.setSaslMechanism( mechanism );
+            request.setVersion3( true ) ;
+            
+            // Setup the ASN1 Enoder and Decoder
+            MessageEncoder encoder = new MessageEncoder();
+            MessageDecoder decoder = new MessageDecoder( new BinaryAttributeDetector() {
+                public boolean isBinary( String attributeId )
+                {
+                    return false;
+                }
+            } );
+     
+            // Send encoded request to server
+            encoder.encodeBlocking( null, _output_, request );
+            _output_.flush();
+            
+            while ( _input_.available() <= 0 )
+            {
+                Thread.sleep( 100 );
+            }
+            
+            // Retrieve the response back from server to my last request.
+            return ( BindResponse ) decoder.decode( null, _input_ );
+        }
+        
+        
+        BindResponse bindType3( byte[] type3response ) throws Exception
+        {
+            if ( ! isConnected() )
+            {
+                throw new IllegalStateException( "Client is not connected." );
+            }
+            
+            // Setup the bind request
+            BindRequestImpl request = new BindRequestImpl( 2 ) ;
+            request.setName( new LdapDN( "uid=admin,ou=system" ) ) ;
+            request.setSimple( false ) ;
+            request.setCredentials( type3response ) ;
+            request.setSaslMechanism( mechanism );
+            request.setVersion3( true ) ;
+            
+            // Setup the ASN1 Enoder and Decoder
+            MessageEncoder encoder = new MessageEncoder();
+            MessageDecoder decoder = new MessageDecoder( new BinaryAttributeDetector() {
+                public boolean isBinary( String attributeId )
+                {
+                    return false;
+                }
+            } );
+     
+            // Send encoded request to server
+            encoder.encodeBlocking( null, _output_, request );
+            
+            _output_.flush();
+            
+            while ( _input_.available() <= 0 )
+            {
+                Thread.sleep( 100 );
+            }
+            
+            // Retrieve the response back from server to my last request.
+            return ( BindResponse ) decoder.decode( null, _input_ );
+        }
+    }
+}
diff --git a/old_trunk/server-unit/src/test/java/org/apache/directory/server/SchemaSearchITest.java b/old_trunk/server-unit/src/test/java/org/apache/directory/server/SchemaSearchITest.java
new file mode 100644
index 0000000..e903d0f
--- /dev/null
+++ b/old_trunk/server-unit/src/test/java/org/apache/directory/server/SchemaSearchITest.java
@@ -0,0 +1,197 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server;
+
+
+import java.util.Hashtable;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.InitialLdapContext;
+import javax.naming.ldap.LdapContext;
+
+import org.apache.directory.server.unit.AbstractServerTest;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+
+
+/**
+ * Testcase with different search operations on the cn=schema entry. 
+ * Created to demonstrate DIRSERVER-1055
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 569048 $
+ */
+public class SchemaSearchITest extends AbstractServerTest
+{
+    private LdapContext ctx = null;
+    private static final String DN = "cn=schema";
+    private static final String FILTER = "(objectclass=subschema)";
+
+
+    protected void checkForAttributes( Attributes attrs, String[] attrNames )
+    {
+        for ( int i = 0; i < attrNames.length; i++ )
+        {
+            String attrName = attrNames[i];
+
+            assertNotNull( "Check if attr " + attrName + " is present", attrs.get( attrNames[i] ) );
+        }
+    }
+
+
+    /**
+     * Create context and a person entry.
+     */
+    public void setUp() throws Exception
+    {
+        super.setUp();
+
+        Hashtable<String, String> env = new Hashtable<String, String>();
+        env.put( "java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory" );
+        env.put( "java.naming.provider.url", "ldap://localhost:" + port );
+        env.put( "java.naming.security.principal", "uid=admin,ou=system" );
+        env.put( "java.naming.security.credentials", "secret" );
+        env.put( "java.naming.security.authentication", "simple" );
+
+        ctx = new InitialLdapContext( env, null );
+        assertNotNull( ctx );
+    }
+
+
+    /**
+     * Remove person entry and close context.
+     */
+    public void tearDown() throws Exception
+    {
+        ctx.close();
+
+        ctx = null;
+        super.tearDown();
+    }
+
+
+    /**
+     * Check if modifyTimestamp and createTimestamp are present in the search result,
+     * if they are requested.
+     */
+    public void testRequestOperationalAttributes() throws NamingException
+    {
+        SearchControls ctls = new SearchControls();
+
+        String[] attrNames =
+            { "creatorsName", "createTimestamp", "modifiersName", "modifyTimestamp" };
+
+        ctls.setSearchScope( SearchControls.OBJECT_SCOPE );
+        ctls.setReturningAttributes( attrNames );
+
+        NamingEnumeration result = ctx.search( DN, FILTER, ctls );
+
+        if ( result.hasMore() )
+        {
+            SearchResult entry = ( SearchResult ) result.next();
+            checkForAttributes( entry.getAttributes(), attrNames );
+        }
+        else
+        {
+            fail( "entry " + DN + " not found" );
+        }
+
+        result.close();
+    }
+
+
+    /**
+     * Check if modifyTimestamp and createTimestamp are present in the search result,
+     * if + is requested.
+     */
+    public void testRequestAllOperationalAttributes() throws NamingException
+    {
+        SearchControls ctls = new SearchControls();
+
+        ctls.setSearchScope( SearchControls.OBJECT_SCOPE );
+        ctls.setReturningAttributes( new String[]
+            { "+" } );
+
+        NamingEnumeration result = ctx.search( DN, FILTER, ctls );
+
+        if ( result.hasMore() )
+        {
+            SearchResult entry = ( SearchResult ) result.next();
+            String[] attrNames =
+                { "creatorsName", "createTimestamp", "modifiersName", "modifyTimestamp" };
+            checkForAttributes( entry.getAttributes(), attrNames );
+        }
+        else
+        {
+            fail( "entry " + DN + " not found" );
+        }
+
+        result.close();
+    }
+
+    
+    /**
+     * Test case for DIRSERVER-1083: Search on an custom attribute added to 
+     * the dynamic schema fails when no result is found. 
+     */
+    public void testSearchingNewSchemaElements() throws Exception
+    {
+        // load the bogus active directory schema from the issue
+        super.loadLdif( getClass().getResourceAsStream( "active-directory.ldif" ), false );
+        
+        // create an entry with the schema objectClass personActiveDirectory
+        AttributesImpl person = new AttributesImpl( "objectClass", "top", true );
+        person.get( "objectClass" ).add( "person" );
+        person.get( "objectClass" ).add( "personActiveDirectory" );
+        person.put( "cn", "foobar" );
+        person.put( "sn", "bar" );
+        person.put( "pwdLastSet", "3" );
+        person.put( "SourceAD", "blah" );
+        person.put( "useraccountcontrol", "7" );
+        person.put( "sAMAccountName", "foobar" );
+        ctx.createSubcontext( "cn=foobar,ou=system", person );
+        
+        // Confirm creation with a lookup
+        Attributes read = ctx.getAttributes( "cn=foobar,ou=system" );
+        assertNotNull( read );
+        assertEquals( "3", read.get( "pwdLastSet" ).get() );
+        
+        // Now search for foobar with pwdLastSet value of 3
+        SearchControls searchControls = new SearchControls();
+        searchControls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+        NamingEnumeration results = ctx.search( "ou=system", "(pwdLastSet=3)", searchControls );
+        assertTrue( results.hasMore() );
+        SearchResult result = ( SearchResult ) results.next();
+        assertNotNull( result );
+        assertEquals( "cn=foobar", result.getName() );
+        Attributes attributes = result.getAttributes();
+        assertEquals( "3", attributes.get( "pwdLastSet" ).get() );
+        results.close();
+        
+        // Now search with bogus value for pwdLastSet
+        results = ctx.search( "ou=system", "(pwdLastSet=300)", searchControls );
+        assertFalse( results.hasMore() );
+        results.close();
+    }
+}
diff --git a/old_trunk/server-unit/src/test/java/org/apache/directory/server/SearchITest.java b/old_trunk/server-unit/src/test/java/org/apache/directory/server/SearchITest.java
new file mode 100644
index 0000000..dff822a
--- /dev/null
+++ b/old_trunk/server-unit/src/test/java/org/apache/directory/server/SearchITest.java
@@ -0,0 +1,1266 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server;
+
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Set;
+
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.Control;
+import javax.naming.ldap.InitialLdapContext;
+import javax.naming.ldap.LdapContext;
+
+import org.apache.directory.server.core.subtree.SubentryInterceptor;
+import org.apache.directory.server.unit.AbstractServerTest;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.message.SubentriesControl;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+
+/**
+ * Testcase with different modify operations on a person entry. Each includes a
+ * single add op only. Created to demonstrate DIREVE-241 ("Adding an already
+ * existing attribute value with a modify operation does not cause an error.").
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class SearchITest extends AbstractServerTest
+{
+    private LdapContext ctx;
+    private static final String RDN = "cn=Tori Amos";
+    private static final String RDN2 = "cn=Rolling-Stones";
+    private static final String HEATHER_RDN = "cn=Heather Graham";
+    private static final String FILTER = "(objectclass=*)";
+
+
+    private static final byte[] JPEG = new byte[]
+        {
+            (byte)0xff, (byte)0xd8, (byte)0xff, (byte)0xe0, 0x00, 0x10, 0x4a, 0x46, 
+            0x49, 0x46, 0x00, 0x01, 0x01, 0x01, 0x00, 0x48,
+            0x00, 0x48, 0x00, 0x00, (byte)0xff, (byte)0xe1, 0x00, 0x16, 
+            0x45, 0x78, 0x69, 0x66, 0x00, 0x00, 0x4d, 0x4d,
+            0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 
+            0x00, 0x00, 0x00, 0x00, (byte)0xff, (byte)0xfe, 0x00, 0x17,
+            0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 
+            0x77, 0x69, 0x74, 0x68, 0x20, 0x54, 0x68, 0x65,
+            0x20, 0x47, 0x49, 0x4d, 0x50, (byte)0xff, (byte)0xdb, 0x00, 
+            0x43, 0x00, 0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a,
+            0x10, 0x0e, 0x0d, 0x0e, 0x12, 0x11, 0x10, 0x13, 
+            0x18, 0x28, 0x1a, 0x18, 0x16, 0x16, 0x18, 0x31,
+            0x23, 0x25, 0x1d, 0x28, 0x3a, 0x33, 0x3d, 0x3c, 
+            0x39, 0x33, 0x38, 0x37, 0x40, 0x48, 0x5c, 0x4e,
+            0x40, 0x44, 0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 
+            0x51, 0x57, 0x5f, 0x62, 0x67, 0x68, 0x67, 0x3e,
+            0x4d, 0x71, 0x79, 0x70, 0x64, 0x78, 0x5c, 0x65, 
+            0x67, 0x63, (byte)0xff, (byte)0xdb, 0x00, 0x43, 0x01, 0x11,
+            0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a, 0x1a, 
+            0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63, 0x63,
+            0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 
+            0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+            0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 
+            0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+            0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 
+            0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, (byte)0xff,
+            (byte)0xc0, 0x00, 0x11, 0x08, 0x00, 0x01, 0x00, 0x01, 
+            0x03, 0x01, 0x22, 0x00, 0x02, 0x11, 0x01, 0x03,
+            0x11, 0x01, (byte)0xff, (byte)0xc4, 0x00, 0x15, 0x00, 0x01, 
+            0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+            0x05, (byte)0xff, (byte)0xc4, 0x00, 0x14, 0x10, 0x01, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, (byte)0xff,
+            (byte)0xc4, 0x00, 0x15, 0x01, 0x01, 0x01, 0x00, 0x00, 
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x05, 0x06, (byte)0xff, (byte)0xc4, 
+            0x00, 0x14, 0x11, 0x01, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+            0x00, 0x00, 0x00, 0x00, (byte)0xff, (byte)0xda, 0x00, 0x0c,
+            0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 
+            0x3f, 0x00, (byte)0x8a, 0x00, (byte)0xb5, (byte)0xe3, (byte)0xff, (byte)0xd9,
+        };
+                                            
+
+
+    /**
+     * Creation of required attributes of a person entry.
+     */
+    private Attributes getPersonAttributes( String sn, String cn )
+    {
+        Attributes attributes = new AttributesImpl();
+        Attribute attribute = new AttributeImpl( "objectClass" );
+        attribute.add( "top" );
+        attribute.add( "person" );
+        attribute.add( "organizationalPerson" );
+        attribute.add( "inetOrgPerson" );
+        attributes.put( attribute );
+        attributes.put( "cn", cn );
+        attributes.put( "sn", sn );
+        attributes.put( "jpegPhoto", JPEG );
+
+        return attributes;
+    }
+
+
+    private void checkForAttributes( Attributes attrs, String[] attrNames )
+    {
+        for ( String attrName : attrNames )
+        {
+            assertNotNull( "Check if attr " + attrName + " is present", attrs.get( attrName ) );
+        }
+    }
+
+
+    /**
+     * Create context and a person entry.
+     */
+    @Before
+    public void setUp() throws Exception
+    {
+        super.setUp();
+
+        Hashtable<String, String> env = new Hashtable<String, String>();
+        env.put( "java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory" );
+        env.put( "java.naming.provider.url", "ldap://localhost:" + port + "/ou=system" );
+        env.put( "java.naming.security.principal", "uid=admin,ou=system" );
+        env.put( "java.naming.security.credentials", "secret" );
+        env.put( "java.naming.security.authentication", "simple" );
+
+        ctx = new InitialLdapContext( env, null );
+        assertNotNull( ctx );
+
+        // Create a person with description
+        Attributes attributes = this.getPersonAttributes( "Amos", "Tori Amos" );
+        attributes.put( "description", "an American singer-songwriter" );
+        ctx.createSubcontext( RDN, attributes );
+
+        // Create a second person with description
+        attributes = this.getPersonAttributes( "Jagger", "Rolling-Stones" );
+        attributes.put( "description", "an English singer-songwriter" );
+        ctx.createSubcontext( RDN2, attributes );
+        
+        // Create entry for Heather Graham
+        Attributes heather = new AttributesImpl();
+        Attribute ocls = new AttributeImpl( "objectClass" );
+        ocls.add( "top" );
+        ocls.add( "person" );
+        heather.put( ocls );
+        heather.put( "cn", "Heather Nova" );
+        heather.put( "sn", "Nova" );
+
+        ctx.createSubcontext( HEATHER_RDN, heather );
+    }
+
+
+    /**
+     * Remove person entry and close context.
+     */
+    @After
+    public void tearDown() throws Exception
+    {
+        try
+        {
+            ctx.unbind( RDN );
+        }
+        catch ( Exception e )
+        {
+            // Do nothing
+        }
+        finally
+        {
+            ctx.close();
+        }
+            
+        ctx = null;
+        super.tearDown();
+    }
+    
+    
+    /**
+     * Performs a single level search from ou=system base and 
+     * returns the set of DNs found.
+     */
+    private Set<String> search( String filter ) throws NamingException
+    {
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+        NamingEnumeration<SearchResult> ii = ctx.search( "", filter, controls );
+        
+        // collect all results 
+        HashSet<String> results = new HashSet<String>();
+        while ( ii.hasMore() )
+        {
+            SearchResult result = ii.next();
+            results.add( result.getName() );
+        }
+        
+        return results;
+    }
+
+    
+    @Test
+    public void testDirserver635() throws NamingException
+    {
+        nbTests = 26;
+        
+        // create additional entry
+        Attributes attributes = this.getPersonAttributes( "Bush", "Kate Bush" );
+        ctx.createSubcontext( "cn=Kate Bush", attributes );
+
+        // -------------------------------------------------------------------
+        Set<String> results = search( "(|(cn=Kate*)(cn=Tori*))" );
+        assertEquals( "returned size of results", 2, results.size() );
+        assertTrue( "contains cn=Tori Amos", results.contains( "cn=Tori Amos" ) );
+        assertTrue( "contains cn=Kate Bush", results.contains( "cn=Kate Bush" ) );
+
+        // -------------------------------------------------------------------
+        results = search( "(|(cn=*Amos)(cn=Kate*))" );
+        assertEquals( "returned size of results", 2, results.size() );
+        assertTrue( "contains cn=Tori Amos", results.contains( "cn=Tori Amos" ) );
+        assertTrue( "contains cn=Kate Bush", results.contains( "cn=Kate Bush" ) );
+
+        // -------------------------------------------------------------------
+        results = search( "(|(cn=Kate Bush)(cn=Tori*))" );
+        assertEquals( "returned size of results", 2, results.size() );
+        assertTrue( "contains cn=Tori Amos", results.contains( "cn=Tori Amos" ) );
+        assertTrue( "contains cn=Kate Bush", results.contains( "cn=Kate Bush" ) );
+
+        // -------------------------------------------------------------------
+        results = search( "(|(cn=*Amos))" );
+        assertEquals( "returned size of results", 1, results.size() );
+        assertTrue( "contains cn=Tori Amos", results.contains( "cn=Tori Amos" ) );
+    }
+
+    
+    /**
+     * Search operation with a base DN which contains a BER encoded value.
+     */
+    /*
+    public void testSearchBEREncodedBase() throws NamingException
+    {
+        // create additional entry
+        Attributes attributes = this.getPersonAttributes( "Ferry", "Bryan Ferry" );
+        ctx.createSubcontext( "sn=Ferry", attributes );
+
+        SearchControls sctls = new SearchControls();
+        sctls.setSearchScope( SearchControls.OBJECT_SCOPE );
+        String FILTER = "(cn=Bryan Ferry)";
+
+        // sn=Ferry with BEROctetString values
+        String base = "2.5.4.4=#4665727279";
+
+        try
+        {
+            // Check entry
+            NamingEnumeration enm = ctx.search( base, FILTER, sctls );
+            assertTrue( enm.hasMore() );
+            while ( enm.hasMore() )
+            {
+                SearchResult sr = ( SearchResult ) enm.next();
+                Attributes attrs = sr.getAttributes();
+                Attribute sn = attrs.get( "sn" );
+                assertNotNull( sn );
+                assertTrue( sn.contains( "Ferry" ) );
+            }
+        }
+        catch ( Exception e )
+        {
+            fail( e.getMessage() );
+        }
+    }*/
+
+    
+    /**
+     * Search operation with a base DN which contains a BER encoded value.
+     */
+    @Test
+    public void testSearchWithBackslashEscapedBase() throws NamingException
+    {
+        // create additional entry
+        Attributes attributes = this.getPersonAttributes( "Ferry", "Bryan Ferry" );
+        ctx.createSubcontext( "sn=Ferry", attributes );
+
+        SearchControls sctls = new SearchControls();
+        sctls.setSearchScope( SearchControls.OBJECT_SCOPE );
+        String filter = "(cn=Bryan Ferry)";
+
+        // sn=Ferry with BEROctetString values
+        String base = "sn=\\46\\65\\72\\72\\79";
+
+        try
+        {
+            // Check entry
+            NamingEnumeration<SearchResult> enm = ctx.search( base, filter, sctls );
+            assertTrue( enm.hasMore() );
+            while ( enm.hasMore() )
+            {
+                SearchResult sr = enm.next();
+                Attributes attrs = sr.getAttributes();
+                Attribute sn = attrs.get( "sn" );
+                assertNotNull( sn );
+                assertTrue( sn.contains( "Ferry" ) );
+            }
+        }
+        catch ( Exception e )
+        {
+            fail( e.getMessage() );
+        }
+    }
+
+    
+    /**
+     * Add a new attribute to a person entry.
+     * 
+     * @throws NamingException
+     */
+    @Test
+    public void testSearchValue() throws NamingException
+    {
+        // Setting up search controls for compare op
+        SearchControls ctls = new SearchControls();
+        ctls.setReturningAttributes( new String[]
+            { "*" } ); // no attributes
+        ctls.setSearchScope( SearchControls.OBJECT_SCOPE );
+
+        // Search for all entries
+        NamingEnumeration<SearchResult> results = ctx.search( RDN, "(cn=*)", ctls );
+        assertTrue( results.hasMore() );
+
+        results = ctx.search( RDN2, "(cn=*)", ctls );
+        assertTrue( results.hasMore() );
+
+        // Search for all entries ending by Amos
+        results = ctx.search( RDN, "(cn=*Amos)", ctls );
+        assertTrue( results.hasMore() );
+
+        results = ctx.search( RDN2, "(cn=*Amos)", ctls );
+        assertFalse( results.hasMore() );
+
+        // Search for all entries ending by amos
+        results = ctx.search( RDN, "(cn=*amos)", ctls );
+        assertTrue( results.hasMore() );
+
+        results = ctx.search( RDN2, "(cn=*amos)", ctls );
+        assertFalse( results.hasMore() );
+
+        // Search for all entries starting by Tori
+        results = ctx.search( RDN, "(cn=Tori*)", ctls );
+        assertTrue( results.hasMore() );
+
+        results = ctx.search( RDN2, "(cn=Tori*)", ctls );
+        assertFalse( results.hasMore() );
+
+        // Search for all entries starting by tori
+        results = ctx.search( RDN, "(cn=tori*)", ctls );
+        assertTrue( results.hasMore() );
+
+        results = ctx.search( RDN2, "(cn=tori*)", ctls );
+        assertFalse( results.hasMore() );
+
+        // Search for all entries containing ori
+        results = ctx.search( RDN, "(cn=*ori*)", ctls );
+        assertTrue( results.hasMore() );
+
+        results = ctx.search( RDN2, "(cn=*ori*)", ctls );
+        assertFalse( results.hasMore() );
+
+        // Search for all entries containing o and i
+        results = ctx.search( RDN, "(cn=*o*i*)", ctls );
+        assertTrue( results.hasMore() );
+
+        results = ctx.search( RDN2, "(cn=*o*i*)", ctls );
+        assertTrue( results.hasMore() );
+
+        // Search for all entries containing o, space and o
+        results = ctx.search( RDN, "(cn=*o* *o*)", ctls );
+        assertTrue( results.hasMore() );
+
+        results = ctx.search( RDN2, "(cn=*o* *o*)", ctls );
+        assertFalse( results.hasMore() );
+
+        results = ctx.search( RDN2, "(cn=*o*-*o*)", ctls );
+        assertTrue( results.hasMore() );
+
+        // Search for all entries starting by To and containing A
+        results = ctx.search( RDN, "(cn=To*A*)", ctls );
+        assertTrue( results.hasMore() );
+
+        results = ctx.search( RDN2, "(cn=To*A*)", ctls );
+        assertFalse( results.hasMore() );
+
+        // Search for all entries ending by os and containing ri
+        results = ctx.search( RDN, "(cn=*ri*os)", ctls );
+        assertTrue( results.hasMore() );
+
+        results = ctx.search( RDN2, "(cn=*ri*os)", ctls );
+        assertFalse( results.hasMore() );
+    }
+    
+    
+    /**
+     * Search operation with a base DN with quotes
+     *
+    public void testSearchWithQuotesInBase() throws NamingException {
+
+        SearchControls sctls = new SearchControls();
+        sctls.setSearchScope(SearchControls.OBJECT_SCOPE);
+        String filter = "(cn=Tori Amos)";
+
+        // sn="Tori Amos" (with quotes)
+        String base = "cn=\"Tori Amos\"";
+
+        try {
+            // Check entry
+            NamingEnumeration<SearchResult> enm = ctx.search( base, filter, sctls );
+            assertTrue( enm.hasMore() );
+            
+            while ( enm.hasMore() ) {
+                SearchResult sr = enm.next();
+                Attributes attrs = sr.getAttributes();
+                Attribute sn = attrs.get("sn");
+                assertNotNull(sn);
+                assertTrue( sn.contains( "Amos" ) );
+            }
+        } catch (Exception e) {
+            fail( e.getMessage() );
+        }
+    }
+ 
+    
+    /**
+     * Tests for <a href="http://issues.apache.org/jira/browse/DIRSERVER-645">
+     * DIRSERVER-645<\a>: Wrong search FILTER evaluation with AND
+     * operator and undefined operands.
+     */
+    @Test
+    public void testUndefinedAvaInBranchFilters() throws Exception
+    {
+        // create additional entry
+        Attributes attributes = this.getPersonAttributes( "Bush", "Kate Bush" );
+        ctx.createSubcontext( "cn=Kate Bush", attributes );
+
+        // -------------------------------------------------------------------
+        Set<String> results = search( "(|(sn=Bush)(numberOfOctaves=4))" );
+        assertEquals( "returned size of results", 1, results.size() );
+        assertTrue( "contains cn=Kate Bush", results.contains( "cn=Kate Bush" ) );
+
+        // if numberOfOctaves is undefined then this whole FILTER is undefined
+        results = search( "(&(sn=Bush)(numberOfOctaves=4))" );
+        assertEquals( "returned size of results", 0, results.size() );
+    }
+    
+    
+    @Test
+    public void testSearchSchema() throws Exception
+    {
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.OBJECT_SCOPE );
+        controls.setReturningAttributes( new String[] { "objectClasses" } );
+        
+        Hashtable<String, Object> env = new Hashtable<String, Object>();
+        env.put( "java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory" );
+        env.put( "java.naming.provider.url", "ldap://localhost:" + port );
+        env.put( "java.naming.security.principal", "uid=admin,ou=system" );
+        env.put( "java.naming.security.credentials", "secret" );
+        env.put( "java.naming.security.authentication", "simple" );
+
+        ctx = new InitialLdapContext( env, null );
+        assertNotNull( ctx );
+
+        NamingEnumeration<SearchResult> results = ctx.search( "cn=schema", "objectClass=subschema", controls );
+        assertTrue( results.hasMore() );
+        SearchResult result = results.next();
+        assertNotNull( result );
+        assertFalse( results.hasMore() );
+        
+        NamingEnumeration<? extends Attribute> attrs = result.getAttributes().getAll();
+        
+        while ( attrs.hasMoreElements() )
+        {
+            Attribute attr = ( Attribute ) attrs.next();
+            String ID = attr.getID();
+            assertEquals( "objectClasses", ID );
+        }
+        
+        assertNotNull( result.getAttributes().get( "objectClasses" ) );
+        assertEquals( 1, result.getAttributes().size() );
+    }
+    
+    
+    /**
+     * Creates an access control subentry under ou=system whose subtree covers
+     * the entire naming context.
+     *
+     * @param cn the common name and rdn for the subentry
+     * @param subtree the subtreeSpecification for the subentry
+     * @param aciItem the prescriptive ACI attribute value
+     * @throws NamingException if there is a problem creating the subentry
+     */
+    private void createAccessControlSubentry( String cn, String subtree, String aciItem ) throws NamingException
+    {
+        DirContext adminCtx = ctx;
+
+        // modify ou=system to be an AP for an A/C AA if it is not already
+        Attributes ap = adminCtx.getAttributes( "", new String[] { "administrativeRole" } );
+        Attribute administrativeRole = ap.get( "administrativeRole" );
+        if ( administrativeRole == null || !administrativeRole.contains( SubentryInterceptor.AC_AREA ) )
+        {
+            Attributes changes = new AttributesImpl( "administrativeRole", SubentryInterceptor.AC_AREA, true );
+            adminCtx.modifyAttributes( "", DirContext.ADD_ATTRIBUTE, changes );
+        }
+
+        // now add the A/C subentry below ou=system
+        Attributes subentry = new AttributesImpl( "cn", cn, true );
+        Attribute objectClass = new AttributeImpl( "objectClass" );
+        subentry.put( objectClass );
+        objectClass.add( "top" );
+        objectClass.add( SchemaConstants.SUBENTRY_OC );
+        objectClass.add( "accessControlSubentry" );
+        subentry.put( "subtreeSpecification", subtree );
+        subentry.put( "prescriptiveACI", aciItem );
+        adminCtx.createSubcontext( "cn=" + cn, subentry );
+    }
+    
+
+    /**
+     * Test case to demonstrate DIRSERVER-705 ("object class top missing in search
+     * result, if scope is base and attribute objectClass is requested explicitly").
+     */
+    @Test
+    public void testAddWithObjectclasses() throws NamingException
+    {
+
+        // Create entry
+        Attributes heather = new AttributesImpl();
+        Attribute ocls = new AttributeImpl( "objectClass" );
+        ocls.add( "top" );
+        ocls.add( "person" );
+        heather.put( ocls );
+        heather.put( "cn", "Heather Nova" );
+        heather.put( "sn", "Nova" );
+        String rdn = "cn=Heather Nova";
+        ctx.createSubcontext( rdn, heather );
+
+        SearchControls ctls = new SearchControls();
+        ctls.setSearchScope( SearchControls.OBJECT_SCOPE );
+        ctls.setReturningAttributes( new String[]
+            { "objectclass" } );
+        String filter = "(objectclass=*)";
+
+        NamingEnumeration<SearchResult> result = ctx.search( rdn, filter, ctls );
+        if ( result.hasMore() )
+        {
+            SearchResult entry = result.next();
+            Attributes heatherReloaded = entry.getAttributes();
+            Attribute loadedOcls = heatherReloaded.get( "objectClass" );
+            assertNotNull( loadedOcls );
+            assertTrue( loadedOcls.contains( "person" ) );
+            assertTrue( loadedOcls.contains( "top" ) );
+        }
+        else
+        {
+            fail( "entry " + rdn + " not found" );
+        }
+
+        ctx.destroySubcontext( rdn );
+    }
+
+
+    /**
+     * Test case to demonstrate DIRSERVER-705 ("object class top missing in search
+     * result, if scope is base and attribute objectClass is requested explicitly").
+     */
+    @Test
+    public void testAddWithMissingObjectclasses() throws NamingException
+    {
+
+        // Create entry
+        Attributes kate = new AttributesImpl();
+        kate.put( "objectClass", "organizationalperson" );
+        kate.put( "cn", "Kate Bush" );
+        kate.put( "sn", "Bush" );
+        String rdn = "cn=Kate Bush";
+        ctx.createSubcontext( rdn, kate );
+
+        SearchControls ctls = new SearchControls();
+        ctls.setSearchScope( SearchControls.OBJECT_SCOPE );
+        ctls.setReturningAttributes( new String[]
+            { "objectclass" } );
+        String filter = "(objectclass=*)";
+
+        NamingEnumeration<SearchResult> result = ctx.search( rdn, filter, ctls );
+        if ( result.hasMore() )
+        {
+            SearchResult entry = result.next();
+            Attributes kateReloaded = entry.getAttributes();
+            Attribute loadedOcls = kateReloaded.get( "objectClass" );
+            assertNotNull( loadedOcls );
+            assertTrue( loadedOcls.contains( "top" ) );
+            assertTrue( loadedOcls.contains( "person" ) );
+            assertTrue( loadedOcls.contains( "organizationalPerson" ) );
+
+        }
+        else
+        {
+            fail( "entry " + rdn + " not found" );
+        }
+
+        ctx.destroySubcontext( rdn );
+    }
+
+
+    @Test
+    public void testSubentryControl() throws Exception
+    {
+        // create a real access control subentry
+        createAccessControlSubentry( "anyBodyAdd", "{}", 
+            "{ " + "identificationTag \"addAci\", " + "precedence 14, "
+            + "authenticationLevel none, " + "itemOrUserFirst userFirst: { " + "userClasses { allUsers }, "
+            + "userPermissions { { " + "protectedItems {entry, allUserAttributeTypesAndValues}, "
+            + "grantsAndDenials { grantAdd, grantBrowse } } } } }"
+        );
+        
+        // prepare the subentry control to make the subentry visible
+        SubentriesControl control = new SubentriesControl();
+        control.setVisibility( true );
+        Control[] reqControls = new Control[] { control };
+        SearchControls searchControls = new SearchControls();
+        searchControls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+        
+        ctx.setRequestControls( reqControls );
+        NamingEnumeration<SearchResult> enm = ctx.search( "", "(objectClass=*)", searchControls );
+        Set<String> results = new HashSet<String>();
+        while ( enm.hasMore() )
+        {
+            SearchResult result = enm.next();
+            results.add( result.getName() );
+        }
+        
+        assertEquals( "expected results size of", 1, results.size() );
+        assertTrue( results.contains( "cn=anyBodyAdd" ) );
+    }
+
+    
+    /**
+     * Create a person entry with multivalued RDN and check its content. This
+     * testcase was created to demonstrate DIRSERVER-628.
+     */
+    @Test
+    public void testMultiValuedRdnContent() throws NamingException
+    {
+        Attributes attrs = getPersonAttributes( "Bush", "Kate Bush" );
+        String rdn = "cn=Kate Bush+sn=Bush";
+        ctx.createSubcontext( rdn, attrs );
+
+        SearchControls sctls = new SearchControls();
+        sctls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        String filter = "(sn=Bush)";
+        String base = "";
+
+        NamingEnumeration<SearchResult> enm = ctx.search( base, filter, sctls );
+        while ( enm.hasMore() )
+        {
+            SearchResult sr = enm.next();
+            attrs = sr.getAttributes();
+            Attribute cn = sr.getAttributes().get( "cn" );
+            assertNotNull( cn );
+            assertTrue( cn.contains( "Kate Bush" ) );
+            Attribute sn = sr.getAttributes().get( "sn" );
+            assertNotNull( sn );
+            assertTrue( sn.contains( "Bush" ) );
+        }
+
+        ctx.destroySubcontext( rdn );
+    }
+
+
+    /**
+     * Create a person entry with multivalued RDN and check its name.
+     */
+    @Test
+    public void testMultiValuedRdnName() throws NamingException
+    {
+        Attributes attrs = getPersonAttributes( "Bush", "Kate Bush" );
+        String rdn = "cn=Kate Bush+sn=Bush";
+        DirContext entry = ctx.createSubcontext( rdn, attrs );
+        String nameInNamespace = entry.getNameInNamespace();
+
+        SearchControls sctls = new SearchControls();
+        sctls.setSearchScope( SearchControls.OBJECT_SCOPE );
+        String filter = "(sn=Bush)";
+        String base = rdn;
+
+        NamingEnumeration<SearchResult> enm = ctx.search( base, filter, sctls );
+        if ( enm.hasMore() )
+        {
+            SearchResult sr = enm.next();
+            assertNotNull( sr );
+            assertEquals( "Name in namespace", nameInNamespace, sr.getNameInNamespace() );
+        }
+        else
+        {
+            fail( "Entry not found:" + nameInNamespace );
+        }
+
+        ctx.destroySubcontext( rdn );
+    }
+    
+    
+    @Test
+    public void testSearchJpeg() throws NamingException
+    {
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+        NamingEnumeration<SearchResult> res = ctx.search( "", "(cn=Tori*)", controls );
+        
+        // collect all results 
+        while ( res.hasMore() )
+        {
+            SearchResult result = res.next();
+
+            Attributes attrs = result.getAttributes();
+            
+            NamingEnumeration<? extends Attribute> all = attrs.getAll();
+                
+            while ( all.hasMoreElements() )
+            {
+                Attribute attr = all.next();
+                
+                if ( "jpegPhoto".equalsIgnoreCase( attr.getID() ) )
+                {
+                    byte[] jpegVal = (byte[])attr.get();
+                    
+                    assertTrue( Arrays.equals( jpegVal, JPEG ) );
+                }
+            }
+        }
+    }
+    
+    
+    @Test
+    public void testSearchOID() throws NamingException
+    {
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+        NamingEnumeration<SearchResult> res = ctx.search( "", "(2.5.4.3=Tori*)", controls );
+        
+        // ensure that the entry "cn=Tori Amos" was found
+        assertTrue( res.hasMore() );
+
+        SearchResult result = ( SearchResult ) res.next();
+
+        // ensure that result is not null
+        assertNotNull( result );
+        
+        String rdn = result.getName();
+        
+        // ensure that the entry "cn=Tori Amos" was found
+        assertEquals( "cn=Tori Amos", rdn );
+        
+        // ensure that no other value was found
+        assertFalse( res.hasMore() );
+    }
+
+    
+    @Test
+    public void testSearchAttrCN() throws NamingException
+    {
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+        controls.setReturningAttributes( new String[]{"cn"} );
+        
+        NamingEnumeration<SearchResult> res = ctx.search( "", "(commonName=Tori*)", controls );
+        
+        assertTrue( res.hasMore() );
+        
+        SearchResult result = res.next();
+
+        // ensure that result is not null
+        assertNotNull( result );
+        
+        Attributes attrs = result.getAttributes();
+        
+        // ensure the one and only attribute is "cn"
+        assertEquals( 1, attrs.size() );
+        assertNotNull( attrs.get( "cn" ) );
+        assertEquals( 1, attrs.get( "cn" ).size() );
+        assertEquals( "Tori Amos", ( String ) attrs.get("cn").get() );
+    }
+
+    
+    @Test
+    public void testSearchAttrName() throws NamingException
+    {
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+        controls.setReturningAttributes( new String[]{"name"} );
+        
+        NamingEnumeration<SearchResult> res = ctx.search( "", "(commonName=Tori*)", controls );
+        
+        assertTrue( res.hasMore() );
+        
+        SearchResult result = res.next();
+        
+        // ensure that result is not null
+        assertNotNull( result );
+        
+        Attributes attrs = result.getAttributes();
+        
+        // ensure that "cn" and "sn" are returned
+        assertEquals( 2, attrs.size() );
+        assertNotNull( attrs.get( "cn" ) );
+        assertEquals( 1, attrs.get("cn").size() );
+        assertEquals( "Tori Amos", ( String ) attrs.get( "cn" ).get() );
+        assertNotNull( attrs.get( "sn" ) );
+        assertEquals( 1, attrs.get( "sn" ).size() );
+        assertEquals( "Amos", ( String ) attrs.get( "sn" ).get() );
+    }
+
+    
+    @Test
+    public void testSearchAttrCommonName() throws NamingException
+    {
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+        controls.setReturningAttributes( new String[] { "commonName" } );
+        
+        NamingEnumeration<SearchResult> res = ctx.search( "", "(commonName=Tori*)", controls );
+        
+        assertTrue( res.hasMore() );
+        
+
+        SearchResult result = res.next();
+        
+        // ensure that result is not null
+        assertNotNull( result );
+        
+        Attributes attrs = result.getAttributes();
+        
+        // requested attribute was "commonName", but ADS returns "cn". 
+        //       Other servers do the following:
+        //       - OpenLDAP: also return "cn"
+        //       - Siemens DirX: return "commonName"
+        //       - Sun Directory 5.2: return "commonName"
+        // ensure the one and only attribute is "cn"
+        assertEquals( 1, attrs.size() );
+        assertNotNull( attrs.get("cn") );
+        assertEquals( 1, attrs.get("cn").size() );
+        assertEquals( "Tori Amos", (String)attrs.get("cn").get() );
+    }
+
+    
+    @Test
+    public void testSearchAttrOID() throws NamingException
+    {
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+        controls.setReturningAttributes( new String[]{"2.5.4.3"} );
+        
+        NamingEnumeration<SearchResult> res = ctx.search( "", "(commonName=Tori*)", controls );
+        
+        assertTrue( res.hasMore() );
+        
+        SearchResult result = res.next();
+        
+        // ensure that result is not null
+        assertNotNull( result );
+        
+        Attributes attrs = result.getAttributes();
+        
+        // requested attribute was "2.5.4.3", but ADS returns "cn". 
+        //       Other servers do the following:
+        //       - OpenLDAP: also return "cn"
+        //       - Siemens DirX: also return "cn"
+        //       - Sun Directory 5.2: return "2.5.4.3"
+        // ensure the one and only attribute is "cn"
+        assertEquals( 1, attrs.size() );
+        assertNotNull( attrs.get("cn") );
+        assertEquals( 1, attrs.get("cn").size() );
+        assertEquals( "Tori Amos", (String)attrs.get("cn").get() );
+    }
+    
+    
+    @Test
+    public void testSearchAttrC_L() throws NamingException
+    {
+        // create administrative area
+        Attributes aaAttrs = new AttributesImpl();
+        Attribute aaObjectClass = new AttributeImpl( "objectClass" );
+        aaObjectClass.add( "top" );
+        aaObjectClass.add( "organizationalUnit" );
+        aaObjectClass.add( "extensibleObject" );
+        aaAttrs.put( aaObjectClass );
+        aaAttrs.put( "ou", "Collective Area" );
+        aaAttrs.put( "administrativeRole", "collectiveAttributeSpecificArea" );
+        DirContext aaCtx = ctx.createSubcontext( "ou=Collective Area", aaAttrs );
+        
+        // create subentry
+        Attributes subentry = new AttributesImpl();
+        Attribute objectClass = new AttributeImpl( "objectClass" );
+        objectClass.add( "top" );
+        objectClass.add( SchemaConstants.SUBENTRY_OC );
+        objectClass.add( "collectiveAttributeSubentry" );
+        subentry.put( objectClass );
+        subentry.put( "c-l", "Munich" );
+        subentry.put( "cn", "Collective Subentry" );
+        subentry.put( "subtreeSpecification", "{ }" );
+        aaCtx.createSubcontext( "cn=Collective Subentry", subentry );
+        
+        // create real enty
+        Attributes attributes = this.getPersonAttributes( "Bush", "Kate Bush" );
+        aaCtx.createSubcontext( "cn=Kate Bush", attributes );
+        
+        // search
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+        controls.setReturningAttributes( new String[]{"c-l" } );
+        
+        NamingEnumeration<SearchResult> res = aaCtx.search( "", "(cn=Kate Bush)", controls );
+        
+        assertTrue( res.hasMore() );
+        
+        SearchResult result = res.next();
+        
+        // ensure that result is not null
+        assertNotNull( result );
+        
+        Attributes attrs = result.getAttributes();
+        
+        // ensure the one and only attribute is "c-l"
+        assertEquals( 1, attrs.size() );
+        assertNotNull( attrs.get("c-l") );
+        assertEquals( 1, attrs.get("c-l").size() );
+        assertEquals( "Munich", (String)attrs.get("c-l").get() );
+    }
+
+    @Test
+    public void testSearchUsersAttrs() throws NamingException
+    {
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+        controls.setReturningAttributes( new String[]{"*"} );
+        
+        NamingEnumeration<SearchResult> res = ctx.search( "", "(commonName=Tori Amos)", controls );
+        
+        assertTrue( res.hasMore() );
+        
+        SearchResult result = res.next();
+        
+        // ensure that result is not null
+        assertNotNull( result );
+        
+        Attributes attrs = result.getAttributes();
+        
+        // ensure that all user attributes are returned
+        assertEquals( 5, attrs.size() );
+        assertNotNull( attrs.get( "cn" ) );
+        assertNotNull( attrs.get( "sn" ) );
+        assertNotNull( attrs.get( "objectClass" ) );
+        assertNotNull( attrs.get( "jpegPhoto" ) );
+        assertNotNull( attrs.get( "description" ) );
+        assertNull( attrs.get( "createtimestamp" ) );
+        assertNull( attrs.get( "creatorsname" ) );
+    }
+
+
+    @Test
+    public void testSearchOperationalAttrs() throws NamingException
+    {
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+        controls.setReturningAttributes( new String[]{"+"} );
+        
+        NamingEnumeration<SearchResult> res = ctx.search( "", "(commonName=Tori Amos)", controls );
+        
+        assertTrue( res.hasMore() );
+        
+        SearchResult result = res.next();
+        
+        // ensure that result is not null
+        assertNotNull( result );
+        
+        Attributes attrs = result.getAttributes();
+        
+        // ensure that all operational attributes are returned
+        // and no user attributes
+        assertEquals( 2, attrs.size() );
+        assertNull( attrs.get( "cn" ) );
+        assertNull( attrs.get( "sn" ) );
+        assertNull( attrs.get( "objectClass" ) );
+        assertNull( attrs.get( "jpegPhoto" ) );
+        assertNull( attrs.get( "description" ) );
+        assertNotNull( attrs.get( "createtimestamp" ) );
+        assertNotNull( attrs.get( "creatorsname" ) );
+    }
+    
+
+    @Test
+    public void testSearchAllAttrs() throws NamingException
+    {
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+        controls.setReturningAttributes( new String[]{"+", "*"} );
+        
+        NamingEnumeration<SearchResult> res = ctx.search( "", "(commonName=Tori Amos)", controls );
+        
+        assertTrue( res.hasMore() );
+        
+        SearchResult result = ( SearchResult ) res.next();
+        
+        // ensure that result is not null
+        assertNotNull( result );
+        
+        Attributes attrs = result.getAttributes();
+        
+        // ensure that all user attributes are returned
+        assertEquals( 7, attrs.size() );
+        assertNotNull( attrs.get( "cn" ) );
+        assertNotNull( attrs.get( "sn" ) );
+        assertNotNull( attrs.get( "objectClass" ) );
+        assertNotNull( attrs.get( "jpegPhoto" ) );
+        assertNotNull( attrs.get( "description" ) );
+        assertNotNull( attrs.get( "createtimestamp" ) );
+        assertNotNull( attrs.get( "creatorsname" ) );
+    }
+
+
+    @Test
+    public void testSearchBadDN() throws NamingException
+    {
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+        
+        try
+        {
+            ctx.search( "cn=admin", "(objectClass=*)", controls );
+        }
+        catch ( NameNotFoundException nnfe )
+        {
+            assertTrue( true );
+        }
+    }
+    
+
+    @Test
+    public void testSearchInvalidDN() throws NamingException, Exception
+    {
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+        
+        try
+        {
+            ctx.search( "myBadDN", "(objectClass=*)", controls );
+            fail();
+        }
+        catch ( NamingException ne )
+        {
+            assertTrue( true );
+        }
+    }
+    
+
+    /**
+     * Check if operational attributes are present, if "+" is requested.
+     */
+    @Test
+    public void testSearchOperationalAttributes() throws NamingException
+    {
+        SearchControls ctls = new SearchControls();
+
+        ctls.setSearchScope( SearchControls.OBJECT_SCOPE );
+        ctls.setReturningAttributes( new String[]
+            { "+" } );
+
+        NamingEnumeration<SearchResult> result = ctx.search( HEATHER_RDN, FILTER, ctls );
+
+        if ( result.hasMore() )
+        {
+            SearchResult entry = result.next();
+
+            String[] opAttrNames =
+                { "creatorsName", "createTimestamp" };
+
+            checkForAttributes( entry.getAttributes(), opAttrNames );
+        }
+        else
+        {
+            fail( "entry " + HEATHER_RDN + " not found" );
+        }
+
+        result.close();
+    }
+
+
+    /**
+     * Check if user attributes are present, if "*" is requested.
+     */
+    @Test
+    public void testSearchUserAttributes() throws NamingException
+    {
+        SearchControls ctls = new SearchControls();
+
+        ctls.setSearchScope( SearchControls.OBJECT_SCOPE );
+        ctls.setReturningAttributes( new String[]
+            { "*" } );
+
+        NamingEnumeration<SearchResult> result = ctx.search( HEATHER_RDN, FILTER, ctls );
+
+        if ( result.hasMore() )
+        {
+            SearchResult entry = result.next();
+
+            String[] userAttrNames =
+                { "objectClass", "sn", "cn" };
+
+            checkForAttributes( entry.getAttributes(), userAttrNames );
+        }
+        else
+        {
+            fail( "entry " + HEATHER_RDN + " not found" );
+        }
+
+        result.close();
+    }
+    
+    
+    /**
+     * Check if user and operational attributes are present, if both "*" and "+" are requested.
+     */
+    @Test
+    public void testSearchOperationalAndUserAttributes() throws NamingException
+    {
+        SearchControls ctls = new SearchControls();
+ 
+        ctls.setSearchScope( SearchControls.OBJECT_SCOPE );
+        ctls.setReturningAttributes( new String[]
+            { "+", "*" } );
+
+        String[] userAttrNames =
+            { "objectClass", "sn", "cn" };
+
+        String[] opAttrNames =
+            { "creatorsName", "createTimestamp" };
+
+        NamingEnumeration<SearchResult> result = ctx.search( HEATHER_RDN, FILTER, ctls );
+
+        if ( result.hasMore() )
+        {
+            SearchResult entry = result.next();
+            Attributes attrs = entry.getAttributes();
+
+            assertNotNull( attrs );
+
+            checkForAttributes( attrs, userAttrNames );
+            checkForAttributes( attrs, opAttrNames );
+        }
+        else
+        {
+            fail( "entry " + HEATHER_RDN + " not found" );
+        }
+
+        result.close();
+
+        ctls.setReturningAttributes( new String[]
+            { "*", "+" } );
+
+        result = ctx.search( HEATHER_RDN, FILTER, ctls );
+
+        if ( result.hasMore() )
+        {
+            SearchResult entry = ( SearchResult ) result.next();
+            Attributes attrs = entry.getAttributes();
+
+            assertNotNull( attrs );
+            
+            checkForAttributes( attrs, userAttrNames );
+            checkForAttributes( attrs, opAttrNames );
+        }
+        else
+        {
+            fail( "entry " + HEATHER_RDN + " not found" );
+        }
+
+        result.close();
+    }
+    
+    
+    /**
+     * Test for DIRSERVER-1183.
+     * 
+     * @see https://issues.apache.org/jira/browse/DIRSERVER-1183
+     * @throws Exception
+     */
+    @Test
+    public void testDIRSERVER_1183() throws Exception
+    {
+    	Attributes attrs = new AttributesImpl( "objectClass", "inetOrgPerson", true );
+    	attrs.get( "objectClass" ).add( "organizationalPerson" );
+    	attrs.get( "objectClass" ).add( "person" );
+    	attrs.put( "givenName", "Jim" );
+    	attrs.put( "sn", "Bean" );
+    	attrs.put( "cn", "\"Jim, Bean\"" );
+    	
+    	ctx.createSubcontext( "cn=\"Jim, Bean\"", attrs );
+    }
+
+    
+    /**
+     * Test for DIRSERVER-1180 where search hangs when an invalid a substring 
+     * expression missing an any field is used in a filter: i.e. (cn=**).
+     * 
+     * @see https://issues.apache.org/jira/browse/DIRSERVER-1180
+     */
+    @Test
+    public void testMissingAnyInSubstring_DIRSERVER_1180() throws Exception
+    {
+        Attributes attrs = new AttributesImpl( "objectClass", "inetOrgPerson", true );
+        attrs.get( "objectClass" ).add( "organizationalPerson" );
+        attrs.get( "objectClass" ).add( "person" );
+        attrs.put( "givenName", "Jim" );
+        attrs.put( "sn", "Bean" );
+        attrs.put( "cn", "jimbean" );
+        
+        ctx.createSubcontext( "cn=jimbean", attrs );
+        
+        try
+        {
+            ctx.search( "", "(cn=**)", new SearchControls() );
+            fail();
+        }
+        catch ( Exception e )
+        {
+            assertTrue( true );
+        }
+    }
+}
diff --git a/old_trunk/server-unit/src/test/java/org/apache/directory/server/StoredProcedureExecutionITest.java b/old_trunk/server-unit/src/test/java/org/apache/directory/server/StoredProcedureExecutionITest.java
new file mode 100644
index 0000000..f6abf4b
--- /dev/null
+++ b/old_trunk/server-unit/src/test/java/org/apache/directory/server/StoredProcedureExecutionITest.java
@@ -0,0 +1,179 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server;
+
+
+import org.apache.directory.server.ldap.ExtendedOperationHandler;
+import org.apache.directory.server.ldap.handlers.extended.StoredProcedureExtendedOperationHandler;
+import org.apache.directory.server.unit.AbstractServerTest;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.schema.DeepTrimToLowerNormalizer;
+import org.apache.directory.shared.ldap.schema.OidNormalizer;
+import org.apache.directory.shared.ldap.sp.JavaStoredProcUtils;
+import org.apache.directory.shared.ldap.sp.LdapContextParameter;
+import org.junit.Before;
+import org.junit.Test;
+
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+import javax.naming.directory.Attributes;
+import javax.naming.ldap.InitialLdapContext;
+import javax.naming.ldap.LdapContext;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.Set;
+
+
+/**
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev:$
+ */
+public class StoredProcedureExecutionITest extends AbstractServerTest
+{
+    private LdapContext ctx;
+    private LdapContext spCtx;
+    private Map<String, OidNormalizer> oids;
+
+    
+    @Before public void setUp() throws Exception
+    {
+        super.setUp();
+        
+        Hashtable<String, Object> env = new Hashtable<String, Object>();
+        env.put( "java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory" );
+        env.put( "java.naming.provider.url", "ldap://localhost:" + port + "/ou=system" );
+        env.put( "java.naming.security.principal", "uid=admin,ou=system" );
+        env.put( "java.naming.security.credentials", "secret" );
+        env.put( "java.naming.security.authentication", "simple" );
+        ctx = new InitialLdapContext( env, null );
+        
+        Attributes spContainer = new AttributesImpl( "objectClass", "top", true );
+        spContainer.get( "objectClass" ).add( "organizationalUnit" );
+        spContainer.put( "ou", "Stored Procedures" );
+        spCtx = ( LdapContext ) ctx.createSubcontext( "ou=Stored Procedures", spContainer );
+        assertNotNull( spCtx );
+        
+        // Initialize OIDs maps for normalization
+        oids = new HashMap<String, OidNormalizer>();
+
+        oids.put( "ou", new OidNormalizer( "ou", new DeepTrimToLowerNormalizer() ) );
+        oids.put( "organizationalUnitName", new OidNormalizer( "ou", new DeepTrimToLowerNormalizer() ) );
+        oids.put( "2.5.4.11", new OidNormalizer( "ou", new DeepTrimToLowerNormalizer() ) );
+    }
+
+
+    @Override
+    protected void configureLdapServer()
+    {
+        /////////////////////////////////////////////////////////
+        // Enable the Stored Procedure Extended Operation Handler
+        /////////////////////////////////////////////////////////
+        Set<ExtendedOperationHandler> handlers = new HashSet<ExtendedOperationHandler>( ldapServer.getExtendedOperationHandlers() );
+        handlers.add( new StoredProcedureExtendedOperationHandler() );
+        ldapServer.setExtendedOperationHandlers( handlers );
+    }
+
+    public void tearDown() throws Exception
+    {
+        ctx.close();
+        ctx = null;
+        
+        super.tearDown();
+    }
+    
+
+    public void testExecuteProcedureWithReturnValue() throws NamingException
+    {
+        String procedureName = HelloWorldProcedure.class.getName() + ":sayHello";
+        
+        JavaStoredProcUtils.loadStoredProcedureClass( spCtx, HelloWorldProcedure.class );
+        
+        Object response = JavaStoredProcUtils.callStoredProcedure( ctx, procedureName, new Object[] { } );
+        
+        assertEquals( "Hello World!", response );
+    }
+    
+
+    public void testExecuteProcedureWithParametersAndReturnValue() throws NamingException
+    {
+        String procedureName = HelloWorldProcedure.class.getName() + ":sayHelloTo";
+        
+        JavaStoredProcUtils.loadStoredProcedureClass( spCtx, HelloWorldProcedure.class );
+        
+        Object response = JavaStoredProcUtils.callStoredProcedure( ctx, procedureName, new Object[] { "Ersin" } );
+        
+        assertEquals( "Hello Ersin!", response );
+    }
+    
+    
+    @Test public void testSPDeleteSubtree() throws NamingException
+    {
+        String ldif =
+            "version: 1\n" +
+            "\n" +
+            "dn: ou=People,ou=system\n" +
+            "ou: People\n" +
+            "objectclass: organizationalUnit\n" +
+            "objectclass: top\n" +
+            "\n" + 
+            "dn: cn=John,ou=People,ou=system\n" +
+            "objectclass: person\n" +
+            "objectclass: top\n" +
+            "sn: John\n" +
+            "cn: John\n" +
+            "\n" +
+            "dn: cn=Jane,ou=People,ou=system\n" +
+            "objectclass: person\n" +
+            "objectclass: top\n" +
+            "sn: Jane\n" +
+            "cn: Jane\n";
+        
+        injectEntries( ldif );
+        
+        JavaStoredProcUtils.loadStoredProcedureClass( spCtx, DITUtilitiesSP.class );
+        
+        LdapDN people = new LdapDN( "ou=People" );
+        people = LdapDN.normalize(  people, oids );
+        
+        String spName = DITUtilitiesSP.class.getName() + ":deleteSubtree";
+        Object[] params = new Object[] { new LdapContextParameter( "ou=system" ),
+                                         people };
+        
+        
+        JavaStoredProcUtils.callStoredProcedure( ctx, spName, params );
+        
+        try
+        {
+            sysRoot.lookup( "cn=Jane,ou=People" );
+            sysRoot.lookup( "cn=John,ou=People" );
+            sysRoot.lookup( "ou=People" );
+            fail( "We should not have come here." );
+        }
+        catch ( NameNotFoundException e )
+        {
+            // Expected
+        }
+    }
+     
+}
diff --git a/old_trunk/server-unit/src/test/java/org/apache/directory/server/UnknownExtendedOperationTest.java b/old_trunk/server-unit/src/test/java/org/apache/directory/server/UnknownExtendedOperationTest.java
new file mode 100644
index 0000000..efc8e99
--- /dev/null
+++ b/old_trunk/server-unit/src/test/java/org/apache/directory/server/UnknownExtendedOperationTest.java
@@ -0,0 +1,124 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server;
+
+
+import java.util.Hashtable;
+
+import javax.naming.CommunicationException;
+import javax.naming.NamingException;
+import javax.naming.ldap.ExtendedRequest;
+import javax.naming.ldap.ExtendedResponse;
+import javax.naming.ldap.InitialLdapContext;
+import javax.naming.ldap.LdapContext;
+
+import org.apache.directory.server.unit.AbstractServerTest;
+
+
+/**
+ * Check the behaviour of the server for an unknown extended operation. Created
+ * to demonstrate DIREVE-256 ("Extended operation causes client to hang.").
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class UnknownExtendedOperationTest extends AbstractServerTest
+{
+    private LdapContext ctx = null;
+
+
+    /**
+     * Create context.
+     */
+    public void setUp() throws Exception
+    {
+        super.setUp();
+
+        Hashtable<String, Object> env = new Hashtable<String, Object>();
+        env.put( "java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory" );
+        env.put( "java.naming.provider.url", "ldap://localhost:" + port + "/ou=system" );
+        env.put( "java.naming.security.principal", "uid=admin,ou=system" );
+        env.put( "java.naming.security.credentials", "secret" );
+        env.put( "java.naming.security.authentication", "simple" );
+
+        ctx = new InitialLdapContext( env, null );
+        assertNotNull( ctx );
+    }
+
+
+    /**
+     * Close context.
+     */
+    public void tearDown() throws Exception
+    {
+        ctx.close();
+        ctx = null;
+        super.tearDown();
+    }
+
+
+    /**
+     * Calls an extended exception, which does not exist. Expected behaviour is
+     * a CommunicationException.
+     * 
+     * @throws NamingException 
+     */
+    public void testUnknownExtendedOperation() throws NamingException
+    {
+        try
+        {
+            ctx.extendedOperation( new UnknownExtendedOperationRequest() );
+            fail( "Calling an unknown extended operation should fail." );
+        }
+        catch ( CommunicationException ce )
+        {
+            // expected behaviour
+        }
+    }
+
+    /**
+     * Class for the request of an extended operation which does not exist.
+     */
+    private class UnknownExtendedOperationRequest implements ExtendedRequest
+    {
+
+        private static final long serialVersionUID = 1L;
+
+
+        public String getID()
+        {
+            return "1.1"; // Never an OID for an extended operation
+        }
+
+
+        public byte[] getEncodedValue()
+        {
+            return null;
+        }
+
+
+        public ExtendedResponse createExtendedResponse( String id, byte[] berValue, int offset, int length )
+            throws NamingException
+        {
+            return null;
+        }
+    }
+
+}
diff --git a/old_trunk/server-unit/src/test/java/org/apache/directory/server/jndi/ServerContextFactoryTest.java b/old_trunk/server-unit/src/test/java/org/apache/directory/server/jndi/ServerContextFactoryTest.java
new file mode 100644
index 0000000..c91807a
--- /dev/null
+++ b/old_trunk/server-unit/src/test/java/org/apache/directory/server/jndi/ServerContextFactoryTest.java
@@ -0,0 +1,235 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.jndi;
+
+
+import org.apache.directory.server.core.entry.DefaultServerEntry;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.partition.Partition;
+import org.apache.directory.server.core.partition.impl.btree.Index;
+import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmIndex;
+import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmPartition;
+import org.apache.directory.server.unit.AbstractServerTest;
+import org.apache.directory.shared.ldap.name.LdapDN;
+
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+
+import java.util.HashSet;
+import java.util.Set;
+
+
+/**
+ * Tests to see if we can fire up the Eve directory server via JNDI.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class ServerContextFactoryTest extends AbstractServerTest
+{
+    public ServerContextFactoryTest() throws NamingException
+    {
+    }
+
+    private DirContext ctx = null;
+
+
+    public void setUp() throws Exception
+    {
+        super.setUp();
+        Set<Index> indexedAttrs;
+        Set<Partition> partitions = new HashSet<Partition>();
+
+        // Add partition 'testing'
+        JdbmPartition partition = new JdbmPartition();
+        partition.setId( "testing" );
+        partition.setSuffix( "ou=testing" );
+
+        indexedAttrs = new HashSet<Index>();
+        indexedAttrs.add( new JdbmIndex( "ou" ) );
+        indexedAttrs.add( new JdbmIndex( "objectClass" ) );
+        partition.setIndexedAttributes( indexedAttrs );
+
+        ServerEntry serverEntry = new DefaultServerEntry( directoryService.getRegistries(), new LdapDN( "ou=testing" ) );
+        serverEntry.put( "objectClass", "top", "organizationalUnit", "extensibleObject" );
+        serverEntry.put( "ou", "testing" );
+        partition.setContextEntry( serverEntry );
+
+        partitions.add( partition );
+
+        // Add partition 'example'
+        partition = new JdbmPartition();
+        partition.setId( "example" );
+        partition.setSuffix( "dc=example" );
+
+        indexedAttrs = new HashSet<Index>();
+        indexedAttrs.add( new JdbmIndex( "ou" ) );
+        indexedAttrs.add( new JdbmIndex( "dc" ) );
+        indexedAttrs.add( new JdbmIndex( "objectClass" ) );
+        partition.setIndexedAttributes( indexedAttrs );
+
+        serverEntry = new DefaultServerEntry( directoryService.getRegistries(), new LdapDN( "dc=example" ) );
+        serverEntry.put( "objectClass", "top", "organizationalUnit", "extensibleObject" );
+        serverEntry.put( "dc", "example" );
+        partition.setContextEntry( serverEntry );
+
+        partitions.add( partition );
+
+        // Add partition 'MixedCase'
+        partition = new JdbmPartition();
+        partition.setId( "mixedcase" );
+        partition.setSuffix( "dc=MixedCase" );
+
+        indexedAttrs = new HashSet<Index>();
+        indexedAttrs.add( new JdbmIndex( "dc" ) );
+        indexedAttrs.add( new JdbmIndex( "objectClass" ) );
+        partition.setIndexedAttributes( indexedAttrs );
+
+        serverEntry = new DefaultServerEntry( directoryService.getRegistries(), new LdapDN( "dc=MixedCase" ) );
+        serverEntry.put( "objectClass", "top", "organizationalUnit", "extensibleObject" );
+        serverEntry.put( "dc", "MixedCase" );
+        partition.setContextEntry( serverEntry );
+
+        partitions.add( partition );
+
+        directoryService.setPartitions( partitions );
+
+        super.setUp();
+    }
+
+
+    /**
+     * Makes sure the system context has the right attributes and values.
+     *
+     * @throws NamingException if there are failures
+     */
+    public void testSystemContext() throws NamingException
+    {
+        assertNotNull( sysRoot );
+
+        Attributes attributes = sysRoot.getAttributes( "" );
+
+        assertNotNull( attributes );
+
+        assertEquals( "system", attributes.get( "ou" ).get() );
+
+        Attribute attribute = attributes.get( "objectClass" );
+
+        assertNotNull( attribute );
+
+        assertTrue( attribute.contains( "top" ) );
+
+        assertTrue( attribute.contains( "organizationalUnit" ) );
+    }
+
+
+    /**
+     * Tests to make sure tearDown is working correctly.
+     *
+     * @throws NamingException if there are failures
+     */
+    public void testSetupTeardown() throws NamingException
+    {
+        assertNotNull( sysRoot );
+        Attributes attributes = sysRoot.getAttributes( "" );
+        assertNotNull( attributes );
+        assertEquals( "system", attributes.get( "ou" ).get() );
+        Attribute attribute = attributes.get( "objectClass" );
+        assertNotNull( attribute );
+        assertTrue( attribute.contains( "top" ) );
+        assertTrue( attribute.contains( "organizationalUnit" ) );
+    }
+
+
+    /*
+    public void testAppPartitionExample() throws NamingException
+    {
+        Hashtable<String,Object> env = new Hashtable<String,Object>();
+
+        env.put( Context.PROVIDER_URL, "dc=example" );
+        env.put( DirectoryService.JNDI_KEY, directoryService );
+        env.put( Context.SECURITY_PRINCIPAL, "uid=admin,ou=system" );
+        env.put( Context.SECURITY_CREDENTIALS, "secret" );
+        env.put( Context.SECURITY_AUTHENTICATION, "simple" );
+        env.put( Context.INITIAL_CONTEXT_FACTORY, "org.apache.directory.server.core.jndi.CoreContextFactory" );
+
+        InitialContext initialContext = new InitialContext( env );
+        DirContext appRoot = ( DirContext ) initialContext.lookup( "" );
+        assertNotNull( appRoot );
+        Attributes attributes = appRoot.getAttributes( "" );
+        assertNotNull( attributes );
+        assertEquals( "example", attributes.get( "dc" ).get() );
+        Attribute attribute = attributes.get( "objectClass" );
+        assertNotNull( attribute );
+        assertTrue( attribute.contains( "top" ) );
+        assertTrue( attribute.contains( "domain" ) );
+    }
+
+
+    public void testAppPartitionTesting() throws NamingException
+    {
+        Hashtable<String,Object> env = new Hashtable<String,Object>();
+
+        env.put( Context.PROVIDER_URL, "ou=testing" );
+        env.put( DirectoryService.JNDI_KEY, directoryService );
+        env.put( Context.SECURITY_PRINCIPAL, "uid=admin,ou=system" );
+        env.put( Context.SECURITY_CREDENTIALS, "secret" );
+        env.put( Context.SECURITY_AUTHENTICATION, "simple" );
+        env.put( Context.INITIAL_CONTEXT_FACTORY, "org.apache.directory.server.core.jndi.CoreContextFactory" );
+
+        InitialContext initialContext = new InitialContext( env );
+        DirContext appRoot = ( DirContext ) initialContext.lookup( "" );
+        assertNotNull( appRoot );
+        Attributes attributes = appRoot.getAttributes( "" );
+        assertNotNull( attributes );
+        assertEquals( "testing", attributes.get( "ou" ).get() );
+        Attribute attribute = attributes.get( "objectClass" );
+        assertNotNull( attribute );
+        assertTrue( attribute.contains( "top" ) );
+        assertTrue( attribute.contains( "organizationalUnit" ) );
+    }
+
+
+    public void testAppPartitionMixedCase() throws NamingException
+    {
+        Hashtable<String,Object> env = new Hashtable<String,Object>();
+
+        env.put( Context.PROVIDER_URL, "dc=MixedCase" );
+        env.put( DirectoryService.JNDI_KEY, directoryService );
+        env.put( Context.SECURITY_PRINCIPAL, "uid=admin,ou=system" );
+        env.put( Context.SECURITY_CREDENTIALS, "secret" );
+        env.put( Context.SECURITY_AUTHENTICATION, "simple" );
+        env.put( Context.INITIAL_CONTEXT_FACTORY, "org.apache.directory.server.core.jndi.CoreContextFactory" );
+
+        InitialContext initialContext = new InitialContext( env );
+        DirContext appRoot = ( DirContext ) initialContext.lookup( "" );
+        assertNotNull( appRoot );
+        Attributes attributes = appRoot.getAttributes( "" );
+        assertNotNull( attributes );
+        assertEquals( "MixedCase", attributes.get( "dc" ).get() );
+        Attribute attribute = attributes.get( "objectClass" );
+        assertNotNull( attribute );
+        assertTrue( attribute.contains( "top" ) );
+        assertTrue( attribute.contains( "domain" ) );
+    }
+    */
+}
diff --git a/old_trunk/server-unit/src/test/java/org/apache/directory/server/ssl/ConfidentialityRequiredITest.java b/old_trunk/server-unit/src/test/java/org/apache/directory/server/ssl/ConfidentialityRequiredITest.java
new file mode 100644
index 0000000..af848be
--- /dev/null
+++ b/old_trunk/server-unit/src/test/java/org/apache/directory/server/ssl/ConfidentialityRequiredITest.java
@@ -0,0 +1,276 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.ssl;
+
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.security.KeyStore;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateFactory;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Set;
+
+import javax.naming.AuthenticationNotSupportedException;
+import javax.naming.Context;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingEnumeration;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.ModificationItem;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.InitialLdapContext;
+import javax.naming.ldap.LdapContext;
+import javax.naming.ldap.StartTlsRequest;
+import javax.naming.ldap.StartTlsResponse;
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.SSLSession;
+
+import org.apache.directory.server.unit.AbstractServerTest;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * Test case to verify proper operation of confidentiality requirements as 
+ * specified in https://issues.apache.org/jira/browse/DIRSERVER-1189.  
+ * 
+ * Starts up the server binds via SUN JNDI provider to perform various 
+ * operations on entries which should be rejected when a TLS secured 
+ * connection is not established.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 639006 $
+ */
+public class ConfidentialityRequiredITest extends AbstractServerTest 
+{
+    private static final Logger LOG = LoggerFactory.getLogger( ConfidentialityRequiredITest.class );
+    private File ksFile;
+    
+    
+    /**
+     * Sets up the key store and installs the self signed certificate for the 
+     * server (created on first startup) which is to be used by the StartTLS 
+     * JDNDI client that will connect.  The key store is created from scratch
+     * programmatically and whipped on each run.  The certificate is acquired 
+     * by pulling down the bytes for administrator's userCertificate from 
+     * uid=admin,ou=system.  We use sysRoot direct context instead of one over
+     * the wire since the server is configured to prevent connections without
+     * TLS secured connections.
+     */
+    public void setUp() throws Exception
+    {
+    	super.setUp();
+    	
+    	if ( ksFile != null && ksFile.exists() )
+    	{
+    		ksFile.delete();
+    	}
+    	
+    	ksFile = File.createTempFile( "testStore", "ks" );
+
+        Attributes adminEntry = sysRoot.getAttributes( "uid=admin" );
+    	Attribute userCertificateAttr = adminEntry.get( "userCertificate" );
+    	
+    	assertNotNull( userCertificateAttr );
+    	byte[] userCertificate = ( byte[] ) userCertificateAttr.get();
+    	assertNotNull( userCertificate );
+    	ByteArrayInputStream in = new ByteArrayInputStream( userCertificate );
+    	
+    	CertificateFactory factory = CertificateFactory.getInstance( "X.509" );
+    	Certificate cert = factory.generateCertificate( in );
+    	KeyStore ks = KeyStore.getInstance( KeyStore.getDefaultType() );
+    	ks.load( null, null );
+    	ks.setCertificateEntry( "apacheds", cert );
+    	ks.store( new FileOutputStream( ksFile ), "changeit".toCharArray() );
+    	LOG.debug( "Keystore file installed: {}", ksFile.getAbsolutePath() );
+    }
+    
+    
+    /**
+     * Just deletes the generated key store file.
+     */
+    public void tearDown() throws Exception
+    {
+    	if ( ksFile != null && ksFile.exists() )
+    	{
+    		ksFile.delete();
+    	}
+    	
+    	LOG.debug( "Keystore file deleted: {}", ksFile.getAbsolutePath() );
+    	super.tearDown();
+    }
+    
+
+    /**
+     * Setup confidentiality to be required.
+     */
+    protected void configureLdapServer()
+    {
+    	super.configureLdapServer();
+    	ldapServer.setConfidentialityRequired( true );
+    }
+
+    
+    private LdapContext getSecuredContext() throws Exception
+    {
+    	System.setProperty ( "javax.net.ssl.trustStore", ksFile.getAbsolutePath() );
+    	System.setProperty ( "javax.net.ssl.keyStore", ksFile.getAbsolutePath() );
+    	System.setProperty ( "javax.net.ssl.keyStorePassword", "changeit" );
+    	LOG.debug( "testStartTls() test starting ... " );
+    	
+    	// Set up environment for creating initial context
+    	Hashtable<String, Object> env = new Hashtable<String,Object>();
+        env.put( Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory" );
+        
+        // Must use the name of the server that is found in its certificate?
+        env.put( Context.PROVIDER_URL, "ldap://localhost:" + port );
+
+        // Create initial context
+        LOG.debug( "About to get initial context" );
+        LdapContext ctx = new InitialLdapContext( env, null );
+
+        // Start TLS
+        LOG.debug( "About send startTls extended operation" );
+        StartTlsResponse tls = ( StartTlsResponse ) ctx.extendedOperation( new StartTlsRequest() );
+        LOG.debug( "Extended operation issued" );
+        tls.setHostnameVerifier( new HostnameVerifier() {
+            public boolean verify( String hostname, SSLSession session )
+            {
+                return true;
+            } 
+        } );
+        LOG.debug( "TLS negotion about to begin" );
+        tls.negotiate();
+        return ctx;
+    }
+    
+
+    /**
+     * Checks to make sure insecure binds fail while secure binds succeed.
+     */
+    public void testConfidentiality() throws Exception
+    {
+    	// -------------------------------------------------------------------
+    	// Unsecured bind should fail
+    	// -------------------------------------------------------------------
+
+    	try
+    	{
+    		getWiredContext();
+    		fail( "Should not get here due to violation of confidentiality requirements" );
+    	}
+    	catch( AuthenticationNotSupportedException e )
+    	{
+    	}
+    	
+    	// -------------------------------------------------------------------
+    	// get anonymous connection with StartTLS (no bind request sent)
+    	// -------------------------------------------------------------------
+
+    	LdapContext ctx = getSecuredContext();
+    	assertNotNull( ctx );
+    	
+    	// -------------------------------------------------------------------
+    	// upgrade connection via bind request (same physical connection - TLS)
+    	// -------------------------------------------------------------------
+
+    	ctx.addToEnvironment( Context.SECURITY_PRINCIPAL, "uid=admin,ou=system" );
+    	ctx.addToEnvironment( Context.SECURITY_CREDENTIALS, "secret" );
+    	ctx.addToEnvironment( Context.SECURITY_AUTHENTICATION, "simple" );
+    	ctx.reconnect( null );
+    	
+    	// -------------------------------------------------------------------
+    	// do a search and confirm
+    	// -------------------------------------------------------------------
+
+    	NamingEnumeration<SearchResult> results = ctx.search( "ou=system", "(objectClass=*)", new SearchControls() );
+    	Set<String> names = new HashSet<String>();
+    	while( results.hasMore() )
+    	{
+    		names.add( results.next().getName() );
+    	}
+    	results.close();
+    	assertTrue( names.contains( "prefNodeName=sysPrefRoot" ) );
+    	assertTrue( names.contains( "ou=users" ) );
+    	assertTrue( names.contains( "ou=configuration" ) );
+    	assertTrue( names.contains( "uid=admin" ) );
+    	assertTrue( names.contains( "ou=groups" ) );
+    	
+    	// -------------------------------------------------------------------
+    	// do add and confirm
+    	// -------------------------------------------------------------------
+
+    	AttributesImpl attrs = new AttributesImpl( "objectClass", "person", true );
+    	attrs.put( "sn", "foo" );
+    	attrs.put( "cn", "foo bar" );
+    	ctx.createSubcontext( "cn=foo bar,ou=system", attrs );
+    	assertNotNull( ctx.lookup( "cn=foo bar,ou=system" ) );
+    	
+    	// -------------------------------------------------------------------
+    	// do modify and confirm
+    	// -------------------------------------------------------------------
+
+    	ModificationItem[] mods = new ModificationItem[] {
+    			new ModificationItem( DirContext.ADD_ATTRIBUTE, new AttributeImpl( "cn", "fbar" ) )
+    	};
+    	ctx.modifyAttributes( "cn=foo bar,ou=system", mods );
+    	Attributes reread = ( Attributes ) ctx.getAttributes( "cn=foo bar,ou=system" );
+    	assertTrue( reread.get( "cn" ).contains( "fbar" ) );
+    	
+    	// -------------------------------------------------------------------
+    	// do rename and confirm 
+    	// -------------------------------------------------------------------
+
+    	ctx.rename( "cn=foo bar,ou=system", "cn=fbar,ou=system" );
+    	try
+    	{
+    		ctx.getAttributes( "cn=foo bar,ou=system" );
+    		fail( "old name of renamed entry should not be found" );
+    	}
+    	catch ( NameNotFoundException e )
+    	{
+    	}
+    	reread = ( Attributes ) ctx.getAttributes( "cn=fbar,ou=system" );
+    	assertTrue( reread.get( "cn" ).contains( "fbar" ) );
+    	
+    	// -------------------------------------------------------------------
+    	// do delete and confirm
+    	// -------------------------------------------------------------------
+
+    	ctx.destroySubcontext( "cn=fbar,ou=system" );
+    	try
+    	{
+    		ctx.getAttributes( "cn=fbar,ou=system" );
+    		fail( "deleted entry should not be found" );
+    	}
+    	catch ( NameNotFoundException e )
+    	{
+    	}
+    	
+    	ctx.close();
+    }
+}
diff --git a/old_trunk/server-unit/src/test/java/org/apache/directory/server/ssl/LdapsITest.java b/old_trunk/server-unit/src/test/java/org/apache/directory/server/ssl/LdapsITest.java
new file mode 100644
index 0000000..3e9779a
--- /dev/null
+++ b/old_trunk/server-unit/src/test/java/org/apache/directory/server/ssl/LdapsITest.java
@@ -0,0 +1,111 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.ssl;
+
+
+import org.apache.directory.server.ssl.support.SSLSocketFactory;
+import org.apache.directory.server.unit.AbstractServerTest;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.InitialDirContext;
+import java.util.Hashtable;
+
+
+/**
+ * Test case to verify DIREVE-216.  Starts up the server binds via SUN JNDI provider
+ * to perform add modify operations on entries.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class LdapsITest extends AbstractServerTest
+{
+    private static final String RDN = "cn=The Person";
+
+    private DirContext ctx;
+
+
+    /**
+     * Create an entry for a person.
+     */
+    public void setUp() throws Exception
+    {
+        super.setUp();
+
+//        int ldapsPort = AvailablePortFinder.getNextAvailable( 8192 );
+//
+//        LdapServer ldapsServer = new LdapServer( socketAcceptor, directoryService );
+
+
+        Hashtable<String, String> env = new Hashtable<String, String>();
+        env.put( "java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory" );
+        env.put( "java.naming.provider.url", "ldap://localhost:" + port + "/ou=system" );
+        env.put( "java.naming.ldap.factory.socket", SSLSocketFactory.class.getName() );
+        env.put( "java.naming.security.principal", "uid=admin,ou=system" );
+        env.put( "java.naming.security.credentials", "secret" );
+        env.put( "java.naming.security.authentication", "simple" );
+        ctx = new InitialDirContext( env );
+    }
+
+
+    @Override
+    protected void configureLdapServer()
+    {
+        ldapServer.setEnableLdaps( true );
+    }
+
+    /**
+     * Remove the person.
+     */
+    public void tearDown() throws Exception
+    {
+        ctx.unbind( RDN );
+        ctx.close();
+        ctx = null;
+        super.tearDown();
+    }
+
+
+    /**
+     * Just a little test to check if the connection is made successfully.
+     * 
+     * @throws NamingException cannot create person
+     */
+    public void testSetUpTearDown() throws NamingException
+    {
+        // Create a person
+        Attributes attributes = new AttributesImpl( true );
+        Attribute attribute = new AttributeImpl( "objectClass" );
+        attribute.add( "top" );
+        attribute.add( "person" );
+        attributes.put( attribute );
+        attributes.put( "cn", "The Person" );
+        attributes.put( "sn", "Person" );
+        attributes.put( "description", "this is a person" );
+        DirContext person = ctx.createSubcontext( RDN, attributes );
+
+        assertNotNull( person );
+    }
+}
diff --git a/old_trunk/server-unit/src/test/java/org/apache/directory/server/ssl/StartTlsITest.java b/old_trunk/server-unit/src/test/java/org/apache/directory/server/ssl/StartTlsITest.java
new file mode 100644
index 0000000..6eb6116
--- /dev/null
+++ b/old_trunk/server-unit/src/test/java/org/apache/directory/server/ssl/StartTlsITest.java
@@ -0,0 +1,223 @@
+/*
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *   or more contributor license agreements.  See the NOTICE file
+ *   distributed with this work for additional information
+ *   regarding copyright ownership.  The ASF licenses this file
+ *   to you 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.
+ *
+ */
+package org.apache.directory.server.ssl;
+
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.security.KeyStore;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateFactory;
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.List;
+
+import javax.naming.Context;
+import javax.naming.NamingEnumeration;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.InitialLdapContext;
+import javax.naming.ldap.LdapContext;
+import javax.naming.ldap.StartTlsRequest;
+import javax.naming.ldap.StartTlsResponse;
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.SSLSession;
+
+import org.apache.directory.server.unit.AbstractServerTest;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * Test case for StartTls.  
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class StartTlsITest extends AbstractServerTest
+{
+    private static final Logger LOG = LoggerFactory.getLogger( StartTlsITest.class );
+    private static final int CONNECT_ITERATIONS = 10;
+    private static final boolean VERBOSE = false;
+    private File ksFile;
+    
+    
+    /**
+     * Sets up the key store and installs the self signed certificate for the 
+     * server (created on first startup) which is to be used by the StartTLS 
+     * JDNDI client that will connect.  The key store is created from scratch
+     * programmatically and whipped on each run.  The certificate is acquired 
+     * by pulling down the bytes for administrator's userCertificate from 
+     * uid=admin,ou=system.
+     */
+    public void setUp() throws Exception
+    {
+    	super.setUp();
+    	
+    	if ( ksFile != null && ksFile.exists() )
+    	{
+    		ksFile.delete();
+    	}
+    	
+    	ksFile = File.createTempFile( "testStore", "ks" );
+
+    	Hashtable<String, Object> env = new Hashtable<String,Object>();
+        env.put( Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory" );
+        env.put( "java.naming.security.principal", "uid=admin,ou=system" );
+        env.put( "java.naming.security.credentials", "secret" );
+        env.put( "java.naming.security.authentication", "simple" );
+        
+        // Must use the name of the server that is found in its certificate?
+        env.put( Context.PROVIDER_URL, "ldap://localhost:" + port + "/ou=system" );
+        LdapContext ctx = new InitialLdapContext( env, null );
+
+        Attributes adminEntry = ctx.getAttributes( "uid=admin" );
+    	Attribute userCertificateAttr = adminEntry.get( "userCertificate" );
+    	
+    	assertNotNull( userCertificateAttr );
+    	byte[] userCertificate = ( byte[] ) userCertificateAttr.get();
+    	assertNotNull( userCertificate );
+    	ByteArrayInputStream in = new ByteArrayInputStream( userCertificate );
+    	
+    	CertificateFactory factory = CertificateFactory.getInstance( "X.509" );
+    	Certificate cert = factory.generateCertificate( in );
+    	KeyStore ks = KeyStore.getInstance( KeyStore.getDefaultType() );
+    	ks.load( null, null );
+    	ks.setCertificateEntry( "apacheds", cert );
+    	ks.store( new FileOutputStream( ksFile ), "changeit".toCharArray() );
+    	
+    	ctx.close();
+    	ctx = null;
+    	env = null;
+    }
+    
+    
+    /**
+     * Just deletes the generated key store file.
+     */
+    public void tearDown() throws Exception
+    {
+    	if ( ksFile != null && ksFile.exists() )
+    	{
+    		ksFile.delete();
+    	}
+    	
+    	super.tearDown();
+    }
+    
+    
+    private void search( int ii, LdapContext securedContext ) throws Exception
+    {
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+    	
+    	if ( VERBOSE )
+    	{
+    		System.out.println( "Searching on " + ii + "-th iteration:" );
+    	}
+    	
+    	List<String> results = new ArrayList<String>();
+        NamingEnumeration<SearchResult> ne = securedContext.search( "ou=system", "(objectClass=*)", controls );
+        while ( ne.hasMore() )
+        {
+        	String dn = ne.next().getNameInNamespace();
+        	results.add( dn );
+        	
+        	if ( VERBOSE )
+        	{
+        		System.out.println( "\tSearch Result = " + dn );
+        	}
+        }
+        ne.close();
+        
+    	assertEquals( "ou=system", results.get( 0 ) );
+    	assertEquals( "uid=admin,ou=system", results.get( 1 ) );
+    	assertEquals( "ou=users,ou=system", results.get( 2 ) );
+    	assertEquals( "ou=groups,ou=system", results.get( 3 ) );
+    	assertEquals( "cn=Administrators,ou=groups,ou=system", results.get( 4 ) );
+    	assertEquals( "ou=configuration,ou=system", results.get( 5 ) );
+    	assertEquals( "ou=partitions,ou=configuration,ou=system", results.get( 6 ) );
+    	assertEquals( "ou=services,ou=configuration,ou=system", results.get( 7 ) );
+    	assertEquals( "ou=interceptors,ou=configuration,ou=system", results.get( 8 ) );
+    	assertEquals( "prefNodeName=sysPrefRoot,ou=system", results.get( 9 ) );
+    }
+    
+    
+    /**
+     * Tests StartTLS by creating a JNDI connection using the generated key 
+     * store with the installed self signed certificate.  It then searches 
+     * the server and verifies the presence of the expected entries and closes
+     * the connection.  This process repeats for a number of iterations.  
+     * Modify the CONNECT_ITERATIONS constant to change the number of 
+     * iterations.  Modify the VERBOSE constant to print out information while
+     * performing searches.
+     */
+    public void testStartTls() throws Exception
+    {
+    	for ( int ii = 0; ii < CONNECT_ITERATIONS; ii++ )
+    	{
+        	if ( VERBOSE )
+        	{
+        		System.out.println( "Performing " + ii + "-th iteration to connect via StartTLS." );
+        	}
+
+        	System.setProperty ( "javax.net.ssl.trustStore", ksFile.getAbsolutePath() );
+	    	System.setProperty ( "javax.net.ssl.keyStore", ksFile.getAbsolutePath() );
+	    	System.setProperty ( "javax.net.ssl.keyStorePassword", "changeit" );
+	    	LOG.debug( "testStartTls() test starting ... " );
+	    	
+	    	// Set up environment for creating initial context
+	    	Hashtable<String, Object> env = new Hashtable<String,Object>();
+	        env.put( Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory" );
+	        env.put( "java.naming.security.principal", "uid=admin,ou=system" );
+	        env.put( "java.naming.security.credentials", "secret" );
+	        env.put( "java.naming.security.authentication", "simple" );
+	        
+	        // Must use the name of the server that is found in its certificate?
+	        env.put( Context.PROVIDER_URL, "ldap://localhost:" + port );
+	
+	        // Create initial context
+	        LOG.debug( "About to get initial context" );
+	        LdapContext ctx = new InitialLdapContext( env, null );
+	
+	        // Start TLS
+	        LOG.debug( "About send startTls extended operation" );
+	        StartTlsResponse tls = ( StartTlsResponse ) ctx.extendedOperation( new StartTlsRequest() );
+	        LOG.debug( "Extended operation issued" );
+	        tls.setHostnameVerifier( new HostnameVerifier() {
+	            public boolean verify( String hostname, SSLSession session )
+	            {
+	                return true;
+	            } 
+	        } );
+	        LOG.debug( "TLS negotion about to begin" );
+	        tls.negotiate();
+
+	        search( ii, ctx );
+	        
+	        tls.close();
+	        ctx.close();
+    	}
+    }
+}
diff --git a/old_trunk/server-unit/src/test/java/org/apache/directory/server/ssl/support/BogusSSLContextFactory.java b/old_trunk/server-unit/src/test/java/org/apache/directory/server/ssl/support/BogusSSLContextFactory.java
new file mode 100644
index 0000000..caffecd
--- /dev/null
+++ b/old_trunk/server-unit/src/test/java/org/apache/directory/server/ssl/support/BogusSSLContextFactory.java
@@ -0,0 +1,160 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.ssl.support;
+
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+
+
+/**
+ * Factory to create a bougus SSLContext.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class BogusSSLContextFactory
+{
+
+    /**
+     * Protocol to use.
+     */
+    private static final String PROTOCOL = "TLS";
+
+    /**
+     * Bougus Server certificate keystore file name.
+     */
+    private static final String BOGUS_KEYSTORE = "/bogus.cert";
+
+    // NOTE: The keystore was generated using keytool:
+    //   keytool -genkey -alias bogus -keysize 512 -validity 3650
+    //           -keyalg RSA -dname "CN=bogus.com, OU=XXX CA,
+    //               O=Bogus Inc, L=Stockholm, S=Stockholm, C=SE"
+    //           -keypass boguspw -storepass boguspw -keystore bogus.cert
+
+    /**
+     * Bougus keystore password.
+     */
+    private static final char[] BOGUS_PW =
+        { 'b', 'o', 'g', 'u', 's', 'p', 'w' };
+
+    private static SSLContext serverInstance = null;
+
+    private static SSLContext clientInstance = null;
+
+
+    /**
+     * Get SSLContext singleton.
+     *
+     * @return SSLContext
+     * @throws java.security.GeneralSecurityException
+     *
+     */
+    public static SSLContext getInstance( boolean server ) throws GeneralSecurityException
+    {
+        SSLContext retInstance = null;
+        if ( server )
+        {
+            if ( serverInstance == null )
+            {
+                synchronized ( BogusSSLContextFactory.class )
+                {
+                    if ( serverInstance == null )
+                    {
+                        try
+                        {
+                            serverInstance = createBougusServerSSLContext();
+                        }
+                        catch ( Exception ioe )
+                        {
+                            throw new GeneralSecurityException( "Can't create Server SSLContext:" + ioe );
+                        }
+                    }
+                }
+            }
+            retInstance = serverInstance;
+        }
+        else
+        {
+            if ( clientInstance == null )
+            {
+                synchronized ( BogusSSLContextFactory.class )
+                {
+                    if ( clientInstance == null )
+                    {
+                        clientInstance = createBougusClientSSLContext();
+                    }
+                }
+            }
+            retInstance = clientInstance;
+        }
+        return retInstance;
+    }
+
+
+    private static SSLContext createBougusServerSSLContext() throws GeneralSecurityException, IOException
+    {
+        // Create keystore
+        KeyStore ks = KeyStore.getInstance( "JKS" );
+        InputStream in = null;
+        try
+        {
+            in = BogusSSLContextFactory.class.getResourceAsStream( BOGUS_KEYSTORE );
+            ks.load( in, BOGUS_PW );
+        }
+        finally
+        {
+            if ( in != null )
+            {
+                try
+                {
+                    in.close();
+                }
+                catch ( IOException ignored )
+                {
+                }
+            }
+        }
+
+        // Set up key manager factory to use our key store
+        KeyManagerFactory kmf = KeyManagerFactory.getInstance( "SunX509" );
+        kmf.init( ks, BOGUS_PW );
+
+        // Initialize the SSLContext to work with our key managers.
+        SSLContext sslContext = SSLContext.getInstance( PROTOCOL );
+        sslContext.init( kmf.getKeyManagers(), BogusTrustManagerFactory.X509_MANAGERS, null );
+
+        return sslContext;
+    }
+
+
+    private static SSLContext createBougusClientSSLContext() throws GeneralSecurityException
+    {
+        SSLContext context = SSLContext.getInstance( PROTOCOL );
+        context.init( null, BogusTrustManagerFactory.X509_MANAGERS, null );
+        return context;
+    }
+
+}
diff --git a/old_trunk/server-unit/src/test/java/org/apache/directory/server/ssl/support/BogusTrustManagerFactory.java b/old_trunk/server-unit/src/test/java/org/apache/directory/server/ssl/support/BogusTrustManagerFactory.java
new file mode 100644
index 0000000..b27a9c8
--- /dev/null
+++ b/old_trunk/server-unit/src/test/java/org/apache/directory/server/ssl/support/BogusTrustManagerFactory.java
@@ -0,0 +1,88 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.ssl.support;
+
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+
+import javax.net.ssl.ManagerFactoryParameters;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactorySpi;
+import javax.net.ssl.X509TrustManager;
+
+
+/**
+ * Bogus trust manager factory. Creates BogusX509TrustManager
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+class BogusTrustManagerFactory extends TrustManagerFactorySpi
+{
+
+    static final X509TrustManager X509 = new X509TrustManager()
+    {
+        public void checkClientTrusted( X509Certificate[] x509Certificates, String s ) throws CertificateException
+        {
+        }
+
+
+        public void checkServerTrusted( X509Certificate[] x509Certificates, String s ) throws CertificateException
+        {
+        }
+
+
+        public X509Certificate[] getAcceptedIssuers()
+        {
+            return new X509Certificate[0];
+        }
+    };
+
+    static final TrustManager[] X509_MANAGERS = new TrustManager[]
+        { X509 };
+
+
+    public BogusTrustManagerFactory()
+    {
+    }
+
+
+    protected TrustManager[] engineGetTrustManagers()
+    {
+        return X509_MANAGERS;
+    }
+
+
+    protected void engineInit( KeyStore keystore ) throws KeyStoreException
+    {
+        // noop
+    }
+
+
+    protected void engineInit( ManagerFactoryParameters managerFactoryParameters )
+        throws InvalidAlgorithmParameterException
+    {
+        // noop
+    }
+}
diff --git a/old_trunk/server-unit/src/test/java/org/apache/directory/server/ssl/support/SSLSocketFactory.java b/old_trunk/server-unit/src/test/java/org/apache/directory/server/ssl/support/SSLSocketFactory.java
new file mode 100644
index 0000000..391663d
--- /dev/null
+++ b/old_trunk/server-unit/src/test/java/org/apache/directory/server/ssl/support/SSLSocketFactory.java
@@ -0,0 +1,150 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.ssl.support;
+
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.UnknownHostException;
+import java.security.GeneralSecurityException;
+
+import javax.net.SocketFactory;
+
+
+/**
+ * Simple Socket factory to create sockets with or without SSL enabled.
+ * If SSL enabled a "bougus" SSL Context is used (suitable for test purposes)
+ * 
+ * @version $Rev$, $Date$
+ */
+public class SSLSocketFactory extends SocketFactory
+{
+    private static boolean sslEnabled = true;
+
+    private static javax.net.ssl.SSLSocketFactory sslFactory = null;
+
+    private static javax.net.SocketFactory factory = null;
+
+
+    public static SocketFactory getDefault()
+    {
+        return new SSLSocketFactory();
+    }
+
+
+    public SSLSocketFactory()
+    {
+        super();
+    }
+
+
+    public Socket createSocket( String arg1, int arg2 ) throws IOException, UnknownHostException
+    {
+        if ( isSslEnabled() )
+        {
+            return getSSLFactory().createSocket( arg1, arg2 );
+        }
+        else
+        {
+            return new Socket( arg1, arg2 );
+        }
+    }
+
+
+    public Socket createSocket( String arg1, int arg2, InetAddress arg3, int arg4 ) throws IOException,
+        UnknownHostException
+    {
+        if ( isSslEnabled() )
+        {
+            return getSSLFactory().createSocket( arg1, arg2, arg3, arg4 );
+        }
+        else
+        {
+            return new Socket( arg1, arg2, arg3, arg4 );
+        }
+    }
+
+
+    public Socket createSocket( InetAddress arg1, int arg2 ) throws IOException
+    {
+        if ( isSslEnabled() )
+        {
+            return getSSLFactory().createSocket( arg1, arg2 );
+        }
+        else
+        {
+            return new Socket( arg1, arg2 );
+        }
+    }
+
+
+    public Socket createSocket( InetAddress arg1, int arg2, InetAddress arg3, int arg4 ) throws IOException
+    {
+        if ( isSslEnabled() )
+        {
+            return getSSLFactory().createSocket( arg1, arg2, arg3, arg4 );
+        }
+        else
+        {
+            return new Socket( arg1, arg2, arg3, arg4 );
+        }
+    }
+
+
+    public static javax.net.SocketFactory getSocketFactory()
+    {
+        if ( factory == null )
+        {
+            factory = new SSLSocketFactory();
+        }
+        return factory;
+    }
+
+
+    private javax.net.ssl.SSLSocketFactory getSSLFactory()
+    {
+        if ( sslFactory == null )
+        {
+            try
+            {
+                sslFactory = BogusSSLContextFactory.getInstance( false ).getSocketFactory();
+            }
+            catch ( GeneralSecurityException e )
+            {
+                throw new RuntimeException( "could not create SSL socket", e );
+            }
+        }
+        return sslFactory;
+    }
+
+
+    public static boolean isSslEnabled()
+    {
+        return sslEnabled;
+    }
+
+
+    public static void setSslEnabled( boolean newSslEnabled )
+    {
+        sslEnabled = newSslEnabled;
+    }
+
+}
diff --git a/old_trunk/server-unit/src/test/resources/bogus.cert b/old_trunk/server-unit/src/test/resources/bogus.cert
new file mode 100644
index 0000000..d34502d
--- /dev/null
+++ b/old_trunk/server-unit/src/test/resources/bogus.cert
Binary files differ
diff --git a/old_trunk/server-unit/src/test/resources/log4j.properties b/old_trunk/server-unit/src/test/resources/log4j.properties
new file mode 100644
index 0000000..facb3e6
--- /dev/null
+++ b/old_trunk/server-unit/src/test/resources/log4j.properties
@@ -0,0 +1,21 @@
+#############################################################################
+#    Licensed to the Apache Software Foundation (ASF) under one or more
+#    contributor license agreements.  See the NOTICE file distributed with
+#    this work for additional information regarding copyright ownership.
+#    The ASF licenses this file to You 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.
+#############################################################################
+log4j.rootCategory=OFF, stdout
+
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=[%d{HH:mm:ss}] %p [%c] - %m%n
diff --git a/old_trunk/server-unit/src/test/resources/org/apache/directory/server/DIRSERVER951ITest.ldif b/old_trunk/server-unit/src/test/resources/org/apache/directory/server/DIRSERVER951ITest.ldif
new file mode 100644
index 0000000..9c4e9f9
--- /dev/null
+++ b/old_trunk/server-unit/src/test/resources/org/apache/directory/server/DIRSERVER951ITest.ldif
@@ -0,0 +1,45 @@
+#
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you 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.
+#
+#   EXAMPLE.COM is reserved for testing according to this RFC:
+#
+#   http://www.rfc-editor.org/rfc/rfc2606.txt
+#
+
+dn: ou=test,ou=system
+objectClass: top
+objectClass: organizationalUnit
+ou: test
+
+dn: uid=test1,ou=test,ou=system
+objectClass: top
+objectClass: account
+uid: test1
+ou: test1
+
+dn: uid=test2,ou=test,ou=system
+objectClass: top
+objectClass: account
+uid: test2
+ou: test2
+
+dn: uid=testNoOU,ou=test,ou=system
+objectClass: top
+objectClass: account
+uid: testNoOU
+
diff --git a/old_trunk/server-unit/src/test/resources/org/apache/directory/server/NegationOperatorITest.ldif b/old_trunk/server-unit/src/test/resources/org/apache/directory/server/NegationOperatorITest.ldif
new file mode 100644
index 0000000..7be384b
--- /dev/null
+++ b/old_trunk/server-unit/src/test/resources/org/apache/directory/server/NegationOperatorITest.ldif
@@ -0,0 +1,69 @@
+#
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you 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.
+#
+#   EXAMPLE.COM is reserved for testing according to this RFC:
+#
+#   http://www.rfc-editor.org/rfc/rfc2606.txt
+#
+
+dn: ou=actors,ou=system
+objectClass: top
+objectClass: organizationalUnit
+ou: actors
+
+dn: uid=jblack,ou=actors,ou=system
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: uidObject
+uid: jblack
+ou: comedy
+ou: adventure 
+cn: Jack Black
+sn: Black 
+
+dn: uid=bpitt,ou=actors,ou=system
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: uidObject
+uid: bpitt
+ou: drama
+ou: adventure
+cn: Brad Pitt
+sn: Pitt
+
+dn: uid=gcloony,ou=actors,ou=system
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: uidObject
+uid: gcloony
+ou: drama
+cn: Goerge Cloony
+sn: Cloony
+
+dn: uid=jnewbie,ou=actors,ou=system
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: uidObject
+uid: jnewbie
+cn: Joe Newbie
+sn: Newbie
+
diff --git a/old_trunk/server-unit/src/test/resources/org/apache/directory/server/active-directory.ldif b/old_trunk/server-unit/src/test/resources/org/apache/directory/server/active-directory.ldif
new file mode 100644
index 0000000..dd63b92
--- /dev/null
+++ b/old_trunk/server-unit/src/test/resources/org/apache/directory/server/active-directory.ldif
@@ -0,0 +1,129 @@
+# Generated by Apache Directory Studio on October 5, 2007 2:13:05 PM
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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.  
+
+
+# SCHEMA 'ACTIVE-DIRECTORY'
+dn: cn=active-directory, ou=schema
+objectclass: metaSchema
+objectclass: top
+cn: active-directory
+m-dependencies: core
+
+dn: ou=attributeTypes, cn=active-directory, ou=schema
+objectclass: organizationalUnit
+objectclass: top
+ou: attributeTypes
+
+dn: m-oid=1.1, ou=attributeTypes, cn=active-directory, ou=schema
+objectclass: metaAttributeType
+objectclass: metaTop
+objectclass: top
+m-oid: 1.1
+m-name: sAMAccountName
+m-syntax: 1.3.6.1.4.1.1466.115.121.1.15
+
+dn: m-oid=1.2, ou=attributeTypes, cn=active-directory, ou=schema
+objectclass: metaAttributeType
+objectclass: metaTop
+objectclass: top
+m-oid: 1.2
+m-name: pwdLastSet
+m-equality: integerMatch
+m-ordering: integerMatch
+m-syntax: 1.3.6.1.4.1.1466.115.121.1.27
+
+dn: m-oid=1.4, ou=attributeTypes, cn=active-directory, ou=schema
+objectclass: metaAttributeType
+objectclass: metaTop
+objectclass: top
+m-oid: 1.4
+m-name: useraccountcontrol
+m-syntax: 1.3.6.1.4.1.1466.115.121.1.27
+
+dn: m-oid=1.5, ou=attributeTypes, cn=active-directory, ou=schema
+objectclass: metaAttributeType
+objectclass: metaTop
+objectclass: top
+m-oid: 1.5
+m-name: SourceAD
+m-syntax: 1.3.6.1.4.1.1466.115.121.1.15
+m-length: 0
+
+dn: ou=comparators, cn=active-directory, ou=schema
+objectclass: organizationalUnit
+objectclass: top
+ou: comparators
+
+dn: ou=ditContentRules, cn=active-directory, ou=schema
+objectclass: organizationalUnit
+objectclass: top
+ou: ditContentRules
+
+dn: ou=ditStructureRules, cn=active-directory, ou=schema
+objectclass: organizationalUnit
+objectclass: top
+ou: ditStructureRules
+
+dn: ou=matchingRules, cn=active-directory, ou=schema
+objectclass: organizationalUnit
+objectclass: top
+ou: matchingRules
+
+dn: ou=matchingRuleUse, cn=active-directory, ou=schema
+objectclass: organizationalUnit
+objectclass: top
+ou: matchingRuleUse
+
+dn: ou=nameForms, cn=active-directory, ou=schema
+objectclass: organizationalUnit
+objectclass: top
+ou: nameForms
+
+dn: ou=normalizers, cn=active-directory, ou=schema
+objectclass: organizationalUnit
+objectclass: top
+ou: normalizers
+
+dn: ou=objectClasses, cn=active-directory, ou=schema
+objectclass: organizationalUnit
+objectclass: top
+ou: objectClasses
+
+dn: m-oid=1.3, ou=objectClasses, cn=active-directory, ou=schema
+objectclass: metaObjectClass
+objectclass: metaTop
+objectclass: top
+m-oid: 1.3
+m-name: personActiveDirectory
+m-supObjectClass: person
+m-must: pwdLastSet
+m-must: sAMAccountName
+m-must: useraccountcontrol
+m-must: SourceAD
+
+dn: ou=syntaxCheckers, cn=active-directory, ou=schema
+objectclass: organizationalUnit
+objectclass: top
+ou: syntaxCheckers
+
+dn: ou=syntaxes, cn=active-directory, ou=schema
+objectclass: organizationalUnit
+objectclass: top
+ou: syntaxes
+
diff --git a/old_trunk/server-xml/pom.xml b/old_trunk/server-xml/pom.xml
new file mode 100644
index 0000000..2b976f5
--- /dev/null
+++ b/old_trunk/server-xml/pom.xml
@@ -0,0 +1,114 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you 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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.directory.server</groupId>
+    <artifactId>apacheds-parent</artifactId>
+    <version>1.5.4-SNAPSHOT</version>
+  </parent>
+  <artifactId>apacheds-server-xml</artifactId>
+  <name>ApacheDS Server XML File</name>
+  <packaging>jar</packaging>
+  <description>
+    A single authoritative server.xml file.
+  </description>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-xbean-spring</artifactId>
+      <version>${pom.version}</version>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-protocol-changepw</artifactId>
+      <version>${pom.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-protocol-kerberos</artifactId>
+      <version>${pom.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-protocol-dns</artifactId>
+      <version>${pom.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-protocol-ldap</artifactId>
+      <version>${pom.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-protocol-ntp</artifactId>
+      <version>${pom.version}</version>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-bootstrap-partition</artifactId>
+      <version>${pom.version}</version>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-server-jndi</artifactId>
+      <version>${pom.version}</version>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.xbean</groupId>
+      <artifactId>xbean-spring</artifactId>
+      <version>3.2</version>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-core</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-beans</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-context</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+</project>
diff --git a/old_trunk/server-xml/src/main/resources/server.xml b/old_trunk/server-xml/src/main/resources/server.xml
new file mode 100644
index 0000000..6bb21c0
--- /dev/null
+++ b/old_trunk/server-xml/src/main/resources/server.xml
@@ -0,0 +1,264 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you 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.
+-->
+
+
+<spring:beans xmlns:spring="http://xbean.apache.org/schemas/spring/1.0" 
+			  xmlns:s="http://www.springframework.org/schema/beans"
+			  xmlns="http://apacheds.org/config/1.0">
+
+  <defaultDirectoryService id="directoryService" instanceId="default"
+                           workingDirectory="example.com"
+                           allowAnonymousAccess="true"
+                           accessControlEnabled="false"
+                           denormalizeOpAttrsEnabled="false">
+    <systemPartition>
+      <!-- use the following partitionConfiguration to override defaults for -->
+      <!-- the system partition                                              -->
+      <jdbmPartition id="system" cacheSize="100" suffix="ou=system" optimizerEnabled="true" syncOnWrite="true">
+        <indexedAttributes>
+          <jdbmIndex attributeId="1.3.6.1.4.1.18060.0.4.1.2.1" cacheSize="100"/>
+          <jdbmIndex attributeId="1.3.6.1.4.1.18060.0.4.1.2.2" cacheSize="100"/>
+          <jdbmIndex attributeId="1.3.6.1.4.1.18060.0.4.1.2.3" cacheSize="100"/>
+          <jdbmIndex attributeId="1.3.6.1.4.1.18060.0.4.1.2.4" cacheSize="100"/>
+          <jdbmIndex attributeId="1.3.6.1.4.1.18060.0.4.1.2.5" cacheSize="10"/>
+          <jdbmIndex attributeId="1.3.6.1.4.1.18060.0.4.1.2.6" cacheSize="10"/>
+          <jdbmIndex attributeId="1.3.6.1.4.1.18060.0.4.1.2.7" cacheSize="10"/>
+          <jdbmIndex attributeId="ou" cacheSize="100"/>
+          <jdbmIndex attributeId="uid" cacheSize="100"/>
+          <jdbmIndex attributeId="objectClass" cacheSize="100"/>
+        </indexedAttributes>
+        <contextEntry>#systemContextEntry</contextEntry>
+        <!-- contextEntry>
+          <s:value>
+            objectClass: top
+            objectClass: organizationalUnit
+            objectClass: extensibleObject
+            ou: system
+          </s:value>
+        </contextEntry -->
+      </jdbmPartition>
+    </systemPartition>
+
+    <partitions>
+      <!-- NOTE: when specifying new partitions you need not include those   -->
+      <!-- attributes below with OID's which are the system indices, if left -->
+      <!-- out they will be automatically configured for you with defaults.  -->
+      <jdbmPartition id="example" cacheSize="100" suffix="dc=example,dc=com" optimizerEnabled="true"
+                     syncOnWrite="true">
+        <indexedAttributes>
+          <jdbmIndex attributeId="1.3.6.1.4.1.18060.0.4.1.2.1" cacheSize="100"/>
+          <jdbmIndex attributeId="1.3.6.1.4.1.18060.0.4.1.2.2" cacheSize="100"/>
+          <jdbmIndex attributeId="1.3.6.1.4.1.18060.0.4.1.2.3" cacheSize="100"/>
+          <jdbmIndex attributeId="1.3.6.1.4.1.18060.0.4.1.2.4" cacheSize="100"/>
+          <jdbmIndex attributeId="1.3.6.1.4.1.18060.0.4.1.2.5" cacheSize="10"/>
+          <jdbmIndex attributeId="1.3.6.1.4.1.18060.0.4.1.2.6" cacheSize="10"/>
+          <jdbmIndex attributeId="1.3.6.1.4.1.18060.0.4.1.2.7" cacheSize="10"/>
+          <jdbmIndex attributeId="dc" cacheSize="100"/>
+          <jdbmIndex attributeId="ou" cacheSize="100"/>
+          <jdbmIndex attributeId="krb5PrincipalName" cacheSize="100"/>
+          <jdbmIndex attributeId="uid" cacheSize="100"/>
+          <jdbmIndex attributeId="objectClass" cacheSize="100"/>
+        </indexedAttributes>
+        <contextEntry>#exampleContextEntry</contextEntry>
+        <!-- contextEntry>
+          <s:value>
+            objectClass: top
+            objectClass: domain
+            objectClass: extensibleObject
+            dc: example
+          </s:value>
+        </contextEntry -->
+      </jdbmPartition>
+    </partitions>
+
+    <interceptors>
+      <normalizationInterceptor/>
+      <authenticationInterceptor/>
+      <referralInterceptor/>
+      <aciAuthorizationInterceptor/>
+      <defaultAuthorizationInterceptor/>
+      <exceptionInterceptor/>
+      <operationalAttributeInterceptor/>
+
+      <!-- Uncomment to enable the password policy interceptor
+      <passwordPolicyInterceptor/>
+      <keyDerivationInterceptor/>
+      -->
+
+      <schemaInterceptor/>
+      <subentryInterceptor/>
+      <collectiveAttributeInterceptor/>
+      <eventInterceptor/>
+      <triggerInterceptor/>
+
+      <!-- Uncomment to enable replication interceptor
+      <replicationInterceptor>
+        <configuration>
+          <replicationConfiguration serverPort="10390" peerReplicas="instance_b@localhost:10392">
+            <replicaId>
+              <replicaId id="instance_a"/>
+            </replicaId>
+          </replicationConfiguration>
+        </configuration>
+      </replicationInterceptor>
+      -->
+    </interceptors>
+  </defaultDirectoryService>
+
+  <standardThreadPool id="standardThreadPool" maxThreads="8"/>
+  <datagramAcceptor id="datagramAcceptor" logicExecutor="#standardThreadPool"/>
+  <socketAcceptor id="socketAcceptor" logicExecutor="#standardThreadPool"/>
+
+<!--  missing  atou=users,dc=example,dc=com
+  <changePasswordServer ipPort="60464">
+    <directoryService>#directoryService</directoryService>
+    <datagramAcceptor>#datagramAcceptor</datagramAcceptor>
+    <socketAcceptor>#socketAcceptor</socketAcceptor>
+  </changePasswordServer>
+-->
+<!--  missing atou=users,dc=example,dc=com
+  <kdcServer ipPort="60088">
+    <directoryService>#directoryService</directoryService>
+    <datagramAcceptor>#datagramAcceptor</datagramAcceptor>
+    <socketAcceptor>#socketAcceptor</socketAcceptor>
+  </kdcServer>
+-->
+  <ntpServer ipPort="60123">
+    <datagramAcceptor>#datagramAcceptor</datagramAcceptor>
+    <socketAcceptor>#socketAcceptor</socketAcceptor>
+  </ntpServer>
+<!--  missing atou=users,dc=example,dc=com
+  <dnsServer ipPort="8053">
+    <directoryService>#directoryService</directoryService>
+    <datagramAcceptor>#datagramAcceptor</datagramAcceptor>
+    <socketAcceptor>#socketAcceptor</socketAcceptor>
+  </dnsServer>
+-->
+
+  <ldapServer id="ldapsServer"
+              enabled="true"
+              ipPort="10636"
+              enableLdaps="true">
+    <directoryService>#directoryService</directoryService>
+    <socketAcceptor>#socketAcceptor</socketAcceptor>
+  </ldapServer>
+
+
+  <ldapServer id="ldapServer"
+              ipPort="10389"
+              allowAnonymousAccess="false"
+              saslHost="ldap.example.com"
+              saslPrincipal="ldap/ldap.example.com@EXAMPLE.COM"
+              searchBaseDn="ou=users,ou=system"
+              maxTimeLimit="15000"
+              maxSizeLimit="1000">
+
+    <directoryService>#directoryService</directoryService>
+    <socketAcceptor>#socketAcceptor</socketAcceptor>
+
+    <!-- The list of supported authentication mechanisms.                   -->
+    <saslMechanismHandlers>
+      <simpleMechanismHandler mech-name="SIMPLE"/>
+      <cramMd5MechanismHandler mech-name="CRAM-MD5" directoryService="#directoryService"/>
+      <digestMd5MechanismHandler mech-name="DIGEST-MD5" directoryService="#directoryService"/>
+      <gssapiMechanismHandler mech-name="GSSAPI" directoryService="#directoryService"/>
+      <ntlmMechanismHandler mech-name="NTLM" ntlmProviderFqcn="com.foo.Bar"/>
+      <ntlmMechanismHandler mech-name="GSS-SPNEGO" ntlmProviderFqcn="com.foo.Bar"/>
+    </saslMechanismHandlers>
+
+    <!-- The desired quality-of-protection, used by DIGEST-MD5 and GSSAPI.  -->
+    <saslQop>
+      <s:value>auth</s:value>
+      <s:value>auth-int</s:value>
+      <s:value>auth-conf</s:value>
+    </saslQop>
+
+    <!-- The realms serviced by this SASL host, used by DIGEST-MD5 and GSSAPI. -->
+    <saslRealms>
+      <s:value>example.com</s:value>
+      <s:value>apache.org</s:value>
+    </saslRealms>
+
+    <!-- the collection of extended operation handlers to install           -->
+    <extendedOperationHandlers>
+      <startTlsHandler/>
+      <gracefulShutdownHandler/>
+      <launchDiagnosticUiHandler/>
+      <!-- The Stored Procedure Extended Operation is not stable yet and it may cause security risks.-->
+      <!--storedProcedureExtendedOperationHandler/-->
+    </extendedOperationHandlers>
+  </ldapServer>
+
+
+  <apacheDS id="apacheDS"
+            synchPeriodMillis="15000"
+            allowAnonymousAccess="false">
+
+    <directoryService>#directoryService</directoryService>
+    <ldapServer>#ldapServer</ldapServer>
+    <ldapsServer>#ldapsServer</ldapsServer>
+  </apacheDS>
+
+  <spring:bean id="systemContextEntry" 
+	class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
+    <spring:property name="targetObject"><spring:ref local='directoryService'/></spring:property>
+    <spring:property name="targetMethod"><spring:value>newEntry</spring:value></spring:property>
+    <spring:property name="arguments">
+      <spring:list>
+        <spring:value xmlns="http://www.springframework.org/schema/beans">
+          objectClass: top
+          objectClass: organizationalUnit
+          objectClass: extensibleObject
+          ou: system
+        </spring:value>
+        <spring:value>ou=system</spring:value>
+      </spring:list>
+    </spring:property>
+  </spring:bean>
+  
+  <spring:bean id="exampleContextEntry" 
+	class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
+    <spring:property name="targetObject"><spring:ref local='directoryService'/></spring:property>
+    <spring:property name="targetMethod"><spring:value>newEntry</spring:value></spring:property>
+    <spring:property name="arguments">
+      <spring:list>
+        <spring:value xmlns="http://www.springframework.org/schema/beans">
+          objectClass: top
+          objectClass: domain
+          objectClass: extensibleObject
+          dc: example
+        </spring:value>
+        <spring:value>dc=example,dc=com</spring:value>
+      </spring:list>
+    </spring:property>
+  </spring:bean>
+
+  <!-- another bean I didn't convert -->
+  <spring:bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
+    <spring:property name="customEditors">
+      <spring:map>
+        <spring:entry key="javax.naming.directory.Attributes">
+          <spring:bean class="org.apache.directory.server.core.configuration.AttributesPropertyEditor"/>
+        </spring:entry>
+      </spring:map>
+    </spring:property>
+  </spring:bean>
+</spring:beans>
diff --git a/old_trunk/server-xml/src/site/site.xml b/old_trunk/server-xml/src/site/site.xml
new file mode 100644
index 0000000..9ec7c16
--- /dev/null
+++ b/old_trunk/server-xml/src/site/site.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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.
+-->
+<!--
+  @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+-->
+<project name="${project.name}">
+    <bannerLeft>
+        <name>${project.name}</name>
+    </bannerLeft>
+    <publishDate position="navigation-bottom" format="dd-MM-yyyy HH:mm" />
+    <body>
+        <menu name="Parent">
+            <item name="Apache Directory ApacheDS" href="../index.html" />
+        </menu>
+        <menu ref="reports" />
+    </body>
+</project>
diff --git a/old_trunk/server-xml/src/test/java/org/apache/directory/server/SpringServerTest.java b/old_trunk/server-xml/src/test/java/org/apache/directory/server/SpringServerTest.java
new file mode 100644
index 0000000..ad96d27
--- /dev/null
+++ b/old_trunk/server-xml/src/test/java/org/apache/directory/server/SpringServerTest.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+package org.apache.directory.server;
+
+import org.apache.directory.server.configuration.ApacheDS;
+import org.apache.xbean.spring.context.FileSystemXmlApplicationContext;
+import org.junit.Test;
+import org.springframework.context.ApplicationContext;
+
+import java.io.File;
+import java.net.URL;
+
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class SpringServerTest
+{
+    private String providerURL = "dc=example,dc=com";
+
+    @Test
+    public void testSpringServerStartup() throws Exception {
+        ClassLoader classLoader = this.getClass().getClassLoader();
+        URL configURL = classLoader.getResource( "server.xml" );
+
+        File configF = new File( configURL.toURI() );
+        ApplicationContext factory = new FileSystemXmlApplicationContext( configF.toURI().toURL().toString() );
+        ApacheDS apacheDS = ( ApacheDS ) factory.getBean( "apacheDS" );
+        File workingDirFile = new File( configF.getParentFile(), "work" );
+        apacheDS.getDirectoryService().setWorkingDirectory( workingDirFile );
+    }
+}
diff --git a/old_trunk/server-xml/src/test/resources/log4j.properties b/old_trunk/server-xml/src/test/resources/log4j.properties
new file mode 100644
index 0000000..b05742f
--- /dev/null
+++ b/old_trunk/server-xml/src/test/resources/log4j.properties
@@ -0,0 +1,22 @@
+#############################################################################
+#    Licensed to the Apache Software Foundation (ASF) under one or more
+#    contributor license agreements.  See the NOTICE file distributed with
+#    this work for additional information regarding copyright ownership.
+#    The ASF licenses this file to You 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.
+#############################################################################
+log4j.rootCategory=ERROR, stdout
+
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=[%d{HH:mm:ss}] %p [%c] - %m%n
+
diff --git a/old_trunk/src/main/appended-resources/supplemental-models.xml b/old_trunk/src/main/appended-resources/supplemental-models.xml
new file mode 100644
index 0000000..6b60065
--- /dev/null
+++ b/old_trunk/src/main/appended-resources/supplemental-models.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you 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.
+-->
+
+<supplementalDataModels>
+  <supplement>
+    <project>
+      <groupId>jdbm</groupId>
+      <artifactId>jdbm</artifactId>
+      <name>JDBM</name>
+      <organization>
+        <name>JDBM</name>
+        <url>http://jdbm.sourceforge.net/</url>
+      </organization>
+      <licenses>
+        <license>
+          <name>JDBM License v1.00 (BSD)</name>
+          <url>http://jdbm.cvs.sourceforge.net/jdbm/jdbm/LICENSE.txt?revision=1.1</url>
+        </license>
+      </licenses>
+    </project>
+  </supplement>
+  <supplement>
+    <project>
+      <groupId>antlr</groupId>
+      <artifactId>antlr</artifactId>
+      <name>ANTLR Parser Generator</name>
+      <organization>
+        <name>ANTLR</name>
+        <url>http://www.antlr.org/</url>
+      </organization>
+      <licenses>
+        <license>
+          <name>ANTLR 2 License (public domain)</name>
+          <url>http://www.antlr2.org/license.html</url>
+        </license>
+      </licenses>
+    </project>
+  </supplement>
+
+</supplementalDataModels>
diff --git a/old_trunk/utils/pom.xml b/old_trunk/utils/pom.xml
new file mode 100644
index 0000000..670e5fb
--- /dev/null
+++ b/old_trunk/utils/pom.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you 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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.directory.server</groupId>
+    <artifactId>apacheds-parent</artifactId>
+    <version>1.5.4-SNAPSHOT</version>
+  </parent>
+  <artifactId>apacheds-utils</artifactId>
+  <name>ApacheDS Utils</name>
+  <packaging>jar</packaging>
+
+  <description>
+    Contains utility classes for ApacheDS.
+  </description>
+
+  <dependencies>
+    <dependency>
+      <groupId>${pom.groupId}</groupId>
+      <artifactId>apacheds-core-constants</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>${pom.groupId}</groupId>
+      <artifactId>apacheds-core-shared</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>${pom.groupId}</groupId>
+      <artifactId>apacheds-core-entry</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
+  </dependencies>
+</project>
+
diff --git a/old_trunk/utils/src/main/java/org/apache/directory/server/utils/AttributesFactory.java b/old_trunk/utils/src/main/java/org/apache/directory/server/utils/AttributesFactory.java
new file mode 100644
index 0000000..a3c8fbf
--- /dev/null
+++ b/old_trunk/utils/src/main/java/org/apache/directory/server/utils/AttributesFactory.java
@@ -0,0 +1,464 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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. 
+ *  
+ */
+package org.apache.directory.server.utils; 
+
+
+import java.util.Comparator; 
+
+import javax.naming.NamingException;
+
+import org.apache.directory.server.constants.MetaSchemaConstants;
+import org.apache.directory.server.core.entry.DefaultServerAttribute;
+import org.apache.directory.server.core.entry.DefaultServerEntry;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.schema.bootstrap.Schema;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.schema.DITContentRule;
+import org.apache.directory.shared.ldap.schema.DITStructureRule;
+import org.apache.directory.shared.ldap.schema.MatchingRule;
+import org.apache.directory.shared.ldap.schema.MatchingRuleUse;
+import org.apache.directory.shared.ldap.schema.NameForm;
+import org.apache.directory.shared.ldap.schema.Normalizer;
+import org.apache.directory.shared.ldap.schema.ObjectClass;
+import org.apache.directory.shared.ldap.schema.SchemaObject;
+import org.apache.directory.shared.ldap.schema.Syntax;
+import org.apache.directory.shared.ldap.schema.syntax.SyntaxChecker;
+import org.apache.directory.shared.ldap.util.DateUtils;
+
+
+/**
+ * A factory that generates an entry using the meta schema for schema 
+ * elements.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class AttributesFactory
+{
+    public ServerEntry getAttributes( SchemaObject obj, Schema schema, Registries registries ) throws NamingException
+    {
+        if ( obj instanceof Syntax )
+        {
+            return getAttributes( ( Syntax ) obj, schema, registries );
+        }
+        else if ( obj instanceof MatchingRule )
+        {
+            return getAttributes( ( MatchingRule ) obj, schema, registries );
+        }
+        else if ( obj instanceof AttributeType )
+        {
+            return getAttributes( ( AttributeType ) obj, schema, registries );
+        }
+        else if ( obj instanceof ObjectClass )
+        {
+            return getAttributes( ( ObjectClass ) obj, schema, registries );
+        }
+        else if ( obj instanceof MatchingRuleUse )
+        {
+            return getAttributes( ( MatchingRuleUse ) obj, schema, registries );
+        }
+        else if ( obj instanceof DITStructureRule )
+        {
+            return getAttributes( ( DITStructureRule ) obj, schema, registries );
+        }
+        else if ( obj instanceof DITContentRule )
+        {
+            return getAttributes( ( DITContentRule ) obj, schema, registries );
+        }
+        else if ( obj instanceof NameForm )
+        {
+            return getAttributes( ( NameForm ) obj, schema, registries );
+        }
+        
+        throw new IllegalArgumentException( "Unknown SchemaObject type: " + obj.getClass() );
+    }
+    
+    
+    public ServerEntry getAttributes( Schema schema, Registries registries ) throws NamingException
+    {
+        ServerEntry entry = new DefaultServerEntry( registries );
+
+        entry.put( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC, MetaSchemaConstants.META_SCHEMA_OC );
+        entry.put( SchemaConstants.CN_AT, schema.getSchemaName() );
+        entry.put( SchemaConstants.CREATORS_NAME_AT, schema.getOwner() );
+        entry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() );
+        
+        if ( schema.isDisabled() )
+        {
+            entry.put( MetaSchemaConstants.M_DISABLED_AT, "TRUE" );
+        }
+        
+        String[] dependencies = schema.getDependencies();
+        
+        if ( dependencies != null && dependencies.length > 0 )
+        {
+            EntryAttribute attr = new DefaultServerAttribute( registries.getAttributeTypeRegistry().lookup( MetaSchemaConstants.M_DEPENDENCIES_AT ) );
+            
+            for ( String dependency:dependencies )
+            {
+                attr.add( dependency );
+            }
+            
+            entry.put( attr );
+        }
+        
+        return entry;
+    }
+    
+    
+    public ServerEntry getAttributes( SyntaxChecker syntaxChecker, Schema schema, Registries registries )
+    {
+        ServerEntry entry = new DefaultServerEntry( registries );
+
+        entry.put( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC, MetaSchemaConstants.META_SYNTAX_CHECKER_OC );
+        entry.put( MetaSchemaConstants.M_OID_AT, syntaxChecker.getSyntaxOid() );
+        entry.put( MetaSchemaConstants.M_FQCN_AT, syntaxChecker.getClass().getName() );
+        entry.put( SchemaConstants.CREATORS_NAME_AT, schema.getOwner() );
+        entry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() );
+        
+        return entry;
+    }
+
+    
+    public ServerEntry getAttributes( Syntax syntax, Schema schema, Registries registries ) throws NamingException
+    {
+        ServerEntry entry = new DefaultServerEntry( registries );
+
+        entry.put( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC, MetaSchemaConstants.META_SYNTAX_OC );
+        entry.put( MetaSchemaConstants.X_HUMAN_READABLE_AT, getBoolean( syntax.isHumanReadable() ) );
+        entry.put( SchemaConstants.CREATORS_NAME_AT, schema.getOwner() );
+        entry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() );
+        injectCommon( syntax, entry, registries );
+        
+        return entry;
+    }
+
+    
+    public ServerEntry getAttributes( String oid, Normalizer normalizer, Schema schema, Registries registries )
+    {
+        ServerEntry entry = new DefaultServerEntry( registries );
+
+        entry.put( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC, MetaSchemaConstants.META_NORMALIZER_OC );
+        entry.put( MetaSchemaConstants.M_OID_AT, oid );
+        entry.put( MetaSchemaConstants.M_FQCN_AT, normalizer.getClass().getName() );
+        entry.put( SchemaConstants.CREATORS_NAME_AT, schema.getOwner() );
+        entry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() );
+        return entry;
+    }
+
+    
+    public ServerEntry getAttributes( String oid, Comparator comparator, Schema schema, Registries registries )
+    {
+        ServerEntry entry = new DefaultServerEntry( registries );
+
+        entry.put( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC, MetaSchemaConstants.META_COMPARATOR_OC );
+        entry.put( MetaSchemaConstants.M_OID_AT, oid );
+        entry.put( MetaSchemaConstants.M_FQCN_AT, comparator.getClass().getName() );
+        entry.put( SchemaConstants.CREATORS_NAME_AT, schema.getOwner() );
+        entry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() );
+        return entry;
+    }
+
+
+    /**
+     * 
+     * @param matchingRule
+     * @return Attributes
+     * @throws NamingException
+     */
+    public ServerEntry getAttributes( MatchingRule matchingRule, Schema schema, Registries registries ) throws NamingException
+    {
+        ServerEntry entry = new DefaultServerEntry( registries );
+
+        entry.put( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC, MetaSchemaConstants.META_MATCHING_RULE_OC );
+        entry.put( MetaSchemaConstants.M_SYNTAX_AT, matchingRule.getSyntax().getOid() );
+        entry.put( SchemaConstants.CREATORS_NAME_AT, schema.getOwner() );
+        entry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() );
+        injectCommon( matchingRule, entry, registries );
+        return entry;
+    }
+
+    
+    public ServerEntry getAttributes( MatchingRuleUse matchingRuleUse, Schema schema, Registries registries )
+    {
+        ServerEntry entry = new DefaultServerEntry( registries );
+
+        entry.put( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC, "" );
+        entry.put( SchemaConstants.CREATORS_NAME_AT, schema.getOwner() );
+        entry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() );
+        return entry;
+    }
+
+    
+    public ServerEntry getAttributes( DITStructureRule dITStructureRule, Schema schema, Registries registries )
+    {
+        ServerEntry entry = new DefaultServerEntry( registries );
+
+        entry.put( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC, "" );
+        entry.put( SchemaConstants.CREATORS_NAME_AT, schema.getOwner() );
+        entry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() );
+        return entry;
+    }
+
+    
+    public ServerEntry getAttributes( DITContentRule dITContentRule, Schema schema, Registries registries )
+    {
+        ServerEntry entry = new DefaultServerEntry( registries );
+
+        entry.put( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC, "" );
+        entry.put( SchemaConstants.CREATORS_NAME_AT, schema.getOwner() );
+        entry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() );
+        return entry;
+    }
+
+    
+    public ServerEntry getAttributes( NameForm nameForm, Schema schema, Registries registries )
+    {
+        ServerEntry entry = new DefaultServerEntry( registries );
+
+        entry.put( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC, "" );
+        entry.put( SchemaConstants.CREATORS_NAME_AT, schema.getOwner() );
+        entry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() );
+        return entry;
+    }
+
+
+    /**
+     * <pre>
+     *    objectclass ( 1.3.6.1.4.1.18060.0.4.0.3.3
+     *       NAME 'metaAttributeType'
+     *       DESC 'meta definition of the AttributeType object'
+     *       SUP metaTop
+     *       STRUCTURAL
+     *       MUST ( m-name $ m-syntax )
+     *       MAY ( m-supAttributeType $ m-obsolete $ m-equality $ m-ordering $ 
+     *             m-substr $ m-singleValue $ m-collective $ m-noUserModification $ 
+     *             m-usage $ m-extensionAttributeType )
+     *    )
+     * </pre>
+     * 
+     * @param attributeType
+     * @return Attributes
+     * @throws NamingException
+     */
+    public ServerEntry getAttributes( AttributeType attributeType, Schema schema, Registries registries ) throws NamingException
+    {
+        ServerEntry entry = new DefaultServerEntry( registries );
+
+        entry.put( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC, MetaSchemaConstants.META_ATTRIBUTE_TYPE_OC );
+        entry.put( MetaSchemaConstants.M_SYNTAX_AT, attributeType.getSyntax().getOid() );
+        entry.put( MetaSchemaConstants.M_COLLECTIVE_AT, getBoolean( attributeType.isCollective() ) );
+        entry.put( MetaSchemaConstants.M_NO_USER_MODIFICATION_AT, getBoolean( ! attributeType.isCanUserModify() ) );
+        entry.put( MetaSchemaConstants.M_SINGLE_VALUE_AT, getBoolean( attributeType.isSingleValue() ) );
+        entry.put( MetaSchemaConstants.M_USAGE_AT, attributeType.getUsage().toString() );
+        entry.put( SchemaConstants.CREATORS_NAME_AT, schema.getOwner() );
+        entry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() );
+
+        injectCommon( attributeType, entry, registries );
+        
+        AttributeType superior = attributeType.getSuperior();
+        
+        if ( superior != null )
+        {
+            // use name if we can for clarity
+            String sup = superior.getName();
+            
+            if ( sup == null )
+            {
+                sup = superior.getOid();
+            }
+            
+            entry.put( MetaSchemaConstants.M_SUP_ATTRIBUTE_TYPE_AT, sup );
+        }
+        
+        if ( attributeType.getEquality() != null )
+        {
+            String equality = attributeType.getEquality().getName();
+            
+            if ( equality == null )
+            {
+                equality = attributeType.getEquality().getOid();
+            }
+            
+            entry.put( MetaSchemaConstants.M_EQUALITY_AT, equality );
+        }
+
+        if ( attributeType.getSubstr() != null )
+        {
+            String substr = attributeType.getSubstr().getName();
+            
+            if ( substr == null )
+            {
+                substr = attributeType.getSubstr().getOid();
+            }
+            
+            entry.put( MetaSchemaConstants.M_SUBSTR_AT, substr );
+        }
+
+        if ( attributeType.getOrdering() != null )
+        {
+            String ordering = attributeType.getOrdering().getName();
+            
+            if ( ordering == null )
+            {
+                ordering = attributeType.getOrdering().getOid();
+            }
+            
+            entry.put( MetaSchemaConstants.M_ORDERING_AT, ordering );
+        }
+
+        return entry;
+    }
+
+    
+    /**
+     * Creates the attributes of an entry representing an objectClass.
+     * 
+     * <pre>
+     *  objectclass ( 1.3.6.1.4.1.18060.0.4.0.3.2
+     *      NAME 'metaObjectClass'
+     *      DESC 'meta definition of the objectclass object'
+     *      SUP metaTop
+     *      STRUCTURAL
+     *      MUST m-oid
+     *      MAY ( m-name $ m-obsolete $ m-supObjectClass $ m-typeObjectClass $ m-must $ 
+     *            m-may $ m-extensionObjectClass )
+     *  )
+     * </pre>
+     * 
+     * @param objectClass the objectClass to produce a meta schema entry for
+     * @return the attributes of the metaSchema entry representing the objectClass
+     * @throws NamingException if there are any problems
+     */
+    public ServerEntry getAttributes( ObjectClass objectClass, Schema schema, Registries registries ) throws NamingException
+    {
+        ServerEntry entry = new DefaultServerEntry( registries );
+
+        entry.put( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC, MetaSchemaConstants.META_OBJECT_CLASS_OC );
+        entry.put( MetaSchemaConstants.M_TYPE_OBJECT_CLASS_AT, objectClass.getType().toString() );
+        entry.put( SchemaConstants.CREATORS_NAME_AT, schema.getOwner() );
+        entry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() );
+        
+        injectCommon( objectClass, entry, registries );
+
+        // handle the superior objectClasses 
+        if ( objectClass.getSuperClasses() != null && objectClass.getSuperClasses().length != 0 )
+        {
+            EntryAttribute attr = new DefaultServerAttribute( registries.getAttributeTypeRegistry().lookup( MetaSchemaConstants.M_SUP_OBJECT_CLASS_AT ) );
+            ObjectClass[] superClasses = objectClass.getSuperClasses();
+            
+            for ( ObjectClass superClass:superClasses )
+            {
+                attr.add( getNameOrNumericoid( superClass ) ); 
+            }
+            
+            entry.put( attr );
+        }
+
+        // add the must list
+        if ( objectClass.getMustList() != null && objectClass.getMustList().length != 0 )
+        {
+            EntryAttribute attr = new DefaultServerAttribute( registries.getAttributeTypeRegistry().lookup( MetaSchemaConstants.M_MUST_AT ) );
+            AttributeType[] mustList = objectClass.getMustList();
+
+            for ( AttributeType attributeType:mustList )
+            {
+                attr.add( getNameOrNumericoid( attributeType ) );
+            }
+            
+            entry.put( attr );
+        }
+        
+        // add the may list
+        if ( objectClass.getMayList() != null && objectClass.getMayList().length != 0 )
+        {
+            EntryAttribute attr = new DefaultServerAttribute( registries.getAttributeTypeRegistry().lookup( MetaSchemaConstants.M_MAY_AT ) );
+            AttributeType[] mayList = objectClass.getMayList();
+
+            for ( AttributeType attributeType:mayList )
+            {
+                attr.add( getNameOrNumericoid( attributeType ) );
+            }
+            
+            entry.put( attr );
+        }
+        
+        return entry;
+    }
+
+    
+    private final String getNameOrNumericoid( SchemaObject object )
+    {
+        // first try to use user friendly name if we can
+        if ( object.getName() != null )
+        {
+            return object.getName();
+        }
+        
+        return object.getOid();
+    }
+    
+    
+    private final void injectCommon( SchemaObject object, ServerEntry entry, Registries registries ) throws NamingException
+    {
+        injectNames( object.getNamesRef(), entry, registries );
+        entry.put( MetaSchemaConstants.M_OBSOLETE_AT, getBoolean( object.isObsolete() ) );
+        entry.put( MetaSchemaConstants.M_OID_AT, object.getOid() );
+        
+        if ( object.getDescription() != null )
+        {
+            entry.put( MetaSchemaConstants.M_DESCRIPTION_AT, object.getDescription() );
+        }
+    }
+    
+    
+    private final void injectNames( String[] names, ServerEntry entry, Registries registries ) throws NamingException
+    {
+        if ( names == null || names.length == 0 )
+        {
+            return;
+        }
+        
+        EntryAttribute attr = new DefaultServerAttribute( registries.getAttributeTypeRegistry().lookup( MetaSchemaConstants.M_NAME_AT ) );
+
+        for ( String name:names )
+        {
+            attr.add( name );
+        }
+        
+        entry.put( attr );
+    }
+
+    
+    private final String getBoolean( boolean value )
+    {
+        if ( value ) 
+        {
+            return "TRUE";
+        }
+        else
+        {
+            return "FALSE";
+        }
+    }
+}
diff --git a/old_trunk/utils/src/site/site.xml b/old_trunk/utils/src/site/site.xml
new file mode 100644
index 0000000..9ec7c16
--- /dev/null
+++ b/old_trunk/utils/src/site/site.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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.
+-->
+<!--
+  @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+-->
+<project name="${project.name}">
+    <bannerLeft>
+        <name>${project.name}</name>
+    </bannerLeft>
+    <publishDate position="navigation-bottom" format="dd-MM-yyyy HH:mm" />
+    <body>
+        <menu name="Parent">
+            <item name="Apache Directory ApacheDS" href="../index.html" />
+        </menu>
+        <menu ref="reports" />
+    </body>
+</project>
diff --git a/old_trunk/xbean-spring/pom.xml b/old_trunk/xbean-spring/pom.xml
new file mode 100644
index 0000000..75dd1e8
--- /dev/null
+++ b/old_trunk/xbean-spring/pom.xml
@@ -0,0 +1,165 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you 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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+  <parent>
+    <artifactId>apacheds-parent</artifactId>
+    <groupId>org.apache.directory.server</groupId>
+    <version>1.5.4-SNAPSHOT</version>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>org.apache.directory.server</groupId>
+  <artifactId>apacheds-xbean-spring</artifactId>
+  <name>ApacheDS Xbean Spring</name>
+
+  <url>http://maven.apache.org</url>
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-protocol-shared</artifactId>
+      <version>${pom.version}</version>
+      <classifier>sources</classifier>
+      <scope>provided</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-protocol-changepw</artifactId>
+      <version>${pom.version}</version>
+      <classifier>sources</classifier>
+      <scope>provided</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-jdbm-store</artifactId>
+      <version>${pom.version}</version>
+      <classifier>sources</classifier>
+      <scope>provided</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-protocol-ntp</artifactId>
+      <version>${pom.version}</version>
+      <classifier>sources</classifier>
+      <scope>provided</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-protocol-kerberos</artifactId>
+      <version>${pom.version}</version>
+      <classifier>sources</classifier>
+      <scope>provided</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-protocol-ldap</artifactId>
+      <version>${pom.version}</version>
+      <classifier>sources</classifier>
+      <scope>provided</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-protocol-dns</artifactId>
+      <version>${pom.version}</version>
+      <classifier>sources</classifier>
+      <scope>provided</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-core</artifactId>
+      <version>${pom.version}</version>
+      <classifier>sources</classifier>
+      <scope>provided</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-server-jndi</artifactId>
+      <version>${pom.version}</version>
+      <classifier>sources</classifier>
+      <scope>provided</scope>
+    </dependency>
+
+    <!-- replication interceptor xbean config metadata -->
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>mitosis</artifactId>
+      <version>${pom.version}</version>
+      <classifier>sources</classifier>
+      <scope>provided</scope>
+    </dependency>
+
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.xbean</groupId>
+        <artifactId>maven-xbean-plugin</artifactId>
+        <executions>
+          <execution>
+            <configuration>
+              <namespace>http://apacheds.org/config/1.0</namespace>
+              <schema>target/xbean/${pom.artifactId}.xsd</schema>
+            </configuration>
+            <goals>
+              <goal>mapping</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+
+      <!--  lets ensure that the XSD gets deployed  -->
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>build-helper-maven-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>attach-artifacts</id>
+            <phase>package</phase>
+            <goals>
+              <goal>attach-artifact</goal>
+            </goals>
+            <configuration>
+              <artifacts>
+                <artifact>
+                  <file>${basedir}/target/xbean/${pom.artifactId}.xsd</file>
+                  <type>xsd</type>
+                </artifact>
+                <artifact>
+                  <file>${basedir}/target/xbean/${pom.artifactId}.xsd.html</file>
+                  <type>xsd.html</type>
+                </artifact>
+              </artifacts>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/old_trunk/xbean-spring/src/site/site.xml b/old_trunk/xbean-spring/src/site/site.xml
new file mode 100644
index 0000000..9ec7c16
--- /dev/null
+++ b/old_trunk/xbean-spring/src/site/site.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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.
+-->
+<!--
+  @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+-->
+<project name="${project.name}">
+    <bannerLeft>
+        <name>${project.name}</name>
+    </bannerLeft>
+    <publishDate position="navigation-bottom" format="dd-MM-yyyy HH:mm" />
+    <body>
+        <menu name="Parent">
+            <item name="Apache Directory ApacheDS" href="../index.html" />
+        </menu>
+        <menu ref="reports" />
+    </body>
+</project>